Jump to content


Photo

[MULTILENGUAJE] Redimensionar una imagen con GDI+ flat API.


  • Please log in to reply
3 replies to this topic

#1 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 12 May 2013 - 06:22 AM

En ocasiones podemos plantearnos redimensionar una imagen para lo que podemos usar la propiedad Stretch de un TImage o dibujar con la API StretchBlt que dibuja redimensionando. El problema es que este método no da muy buena calidad de imagen y puede resultar poco apetecible, al menos en versiones antiguas de delphi y Builder o si trabajamos con la API directamente.

Usando GDI plus podemos conseguir resultados mucho mas aparentes, así que he escrito dos funciones simples, una para dibujar un bitmap al tamaño deseado y otra para cambiar el tamaño de un bitmap de forma que éste puede ser volcado a una imagen o guardado en un archivo con sus nuevas dimensiones.

Estas son las funciones GDI flat usadas:

delphi
  1. // GDI+ Flat API...
  2. function  GdiplusStartup(var GdiToken: DWORD; Startup, Output: PBYTE): Cardinal; stdcall external 'gdiplus';
  3. procedure GdiplusShutdown(GdiToken: DWORD); stdcall external 'gdiplus';
  4. function  GdipCreateBitmapFromHBITMAP(hbm: HBITMAP; hpal: HPALETTE; var GBitmap: THANDLE): Cardinal; stdcall external 'gdiplus';
  5. function  GdipDisposeImage(image: THANDLE): Cardinal; stdcall external 'gdiplus';
  6. function  GdipCreateFromHDC(DC: HDC; var Graphics: Pointer): Cardinal; stdcall external 'gdiplus';
  7. function  GdipDeleteGraphics(graphics: Pointer): Cardinal; stdcall external 'gdiplus';
  8. function  GdipDrawImageRect(graphics: Pointer; Image: THANDLE; x, y, w, h: Single): Cardinal; stdcall external 'gdiplus';

La primera función, DrawImageRect:

delphi
  1. //---------------------------------------------------------------------------
  2. // Dibuja un Bitmap en un hDC ajustando su tamaño para que quepa entero
  3. // en las coordenadas dadas
  4. procedure DrawImageRect(DC: HDC; Bitmap: HBITMAP; X, Y, W, H: integer);
  5. var
  6.   Graphics: Pointer;
  7.   GBitmap:  THANDLE;
  8. begin
  9.   GdipCreateBitmapFromHBITMAP(Bitmap, 0, GBitmap);
  10.   GdipCreateFromHDC(DC, Graphics);
  11.   GdipDrawImageRect(Graphics, GBitmap, X, Y, W, H);
  12.   GdipDisposeImage(GBitmap);
  13.   GdipDeleteGraphics(Graphics);
  14. end;

Un ejemplo de uso:

delphi
  1.  
  2. var
  3.   gdiplusToken: DWORD;
  4.   GdiPlusStartupInput: array[0..1] of int64;
  5. begin
  6.   // Inicializamos GDI+.
  7.   GdiPlusStartupInput[0]:= 1; GdiPlusStartupInput[1]:= 0;
  8.   if GdiplusStartup(gdiplusToken, @GdiPlusStartupInput, nil) <> 0 then exit;
  9.  
  10.   DrawImageRect(Image2.Picture.Bitmap.Canvas.Handle, Image1.Picture.Bitmap.Handle, 20, 20, 200, 150);
  11.   Image2.Invalidate;
  12.  
  13.   // Shutdown GDI+
  14.   GdiplusShutdown(gdiplusToken);
  15. end;

La segunda función CreateResizeBitmap:

delphi
  1. //-----------------------------------------------------------------------------
  2. // Crea y devuelve un Bitmap con unas nuevas dimensiones y la imagen ajustada a estas
  3. function CreateResizeBitmap(Bitmap: HBITMAP; H, W: integer): HBITMAP;
  4. var
  5.   Graphics: Pointer;
  6.   GBitmap: THANDLE;
  7.   DC, dcMem: HDC;
  8.   bmOld: HBITMAP;
  9. begin
  10.   DC:= GetDC(0);
  11.   Result:= CreateCompatibleBitmap(DC, W, H);
  12.   dcMem:= CreateCompatibleDC(DC);
  13.   bmOld:= SelectObject(dcMem, Result);
  14.  
  15.   GdipCreateFromHDC(dcMem, Graphics);
  16.   GdipCreateBitmapFromHBITMAP(Bitmap, 0, GBitmap);
  17.   GdipDrawImageRect(Graphics, GBitmap, 0, 0, H, W);
  18.   GdipDisposeImage(GBitmap);
  19.   GdipDeleteGraphics(Graphics);
  20.  
  21.   SelectObject(dcMem, bmOld);
  22.   DeleteDC(dcMem);
  23.   ReleaseDC(0, DC);
  24. end;

Como creamos un Bitmap nuevo, somos responsables de borrar ese Handle con DeleteObject. Si lo asignamos a un TBitmap la VCL se encargará de hacerlo al destruirlo pero en otro caso somos responsables de su destrucción.

Un ejemplo de uso:

delphi
  1. var
  2.   gdiplusToken: DWORD;
  3.   GdiPlusStartupInput: array[0..1] of int64;
  4. begin
  5.   // Inicializamos GDI+.
  6.   GdiPlusStartupInput[0]:= 1; GdiPlusStartupInput[1]:= 0;
  7.   if GdiplusStartup(gdiplusToken, @GdiPlusStartupInput, nil) <> 0 then exit;
  8.  
  9.   Image1.Picture.Bitmap.Handle:= CreateResizeBitmap(Image1.Picture.Bitmap.Handle, 100, 100); 
  10.   Image1.Invalidate;
  11.  
  12.   // Shutdown GDI+
  13.   GdiplusShutdown(gdiplusToken);
  14. end;

Podremos guardar el HBITMAP con la VCL o bien usando GDI+ como en ejemplo de este hilo.

Finalmente añadir que debemos inicializar GDI+ antes de usar las funciones y cerrarlo después. Esto se puede incluir en las funciones para automatizarlo pero si vamos a hacer uso intensivo de GDI+ no tiene sentido abrir y cerrar cada vez, por ese motivo no lo he incluido en las funciones individuales. Podemos inicializar GDI+ al inicio del programa o de la sección que lo usa y cerrar al terminar.


Saludos.
  • 1

#2 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4111 posts
  • LocationMadrid - España

Posted 12 May 2013 - 07:49 AM

Ahora voy a publicar las funciones equivalentes en C++ tambien usando GDI+ flat:


cpp
  1. #pragma link "Gdiplus.lib"
  2.  
  3. extern "C" {
  4. DWORD WINAPI GdiplusStartup(ULONG_PTR *hToken, void *input, void *Output);
  5. void  WINAPI GdiplusShutdown(ULONG_PTR token);
  6. DWORD WINAPI GdipCreateBitmapFromHBITMAP(HBITMAP hBmp, HPALETTE hPal, HANDLE* GBitmap);
  7. DWORD WINAPI GdipCreateFromHDC(HDC hDC, HANDLE* hGraphics);
  8. DWORD WINAPI GdipDisposeImage(HANDLE hImage);
  9. DWORD WINAPI GdipDeleteGraphics(HANDLE hGraphics);
  10. DWORD WINAPI GdipGetDC(HANDLE hGraphics, HDC* hdc);
  11. DWORD WINAPI GdipDrawImageRect(HANDLE hGraphics, HANDLE himage, float x, float y, float width, float height);
  12. }
  13.  
  14. //-----------------------------------------------------------------------------
  15. // Devuelve un Bitmap con unas nuevas dimensiones y la imagen ajustada a estas
  16. HBITMAP CreateResizeBitmap(HBITMAP hBitmap, int W, int H)
  17. {
  18.   HANDLE Graphics;
  19.   HANDLE GBitmap;
  20.   HDC hDC = GetDC(0);
  21.   HDC hdcMem = CreateCompatibleDC(0);
  22.   HBITMAP Result = CreateCompatibleBitmap(hDC, W, H);
  23.   HBITMAP hbmOld = SelectObject(hdcMem, Result);
  24.  
  25.   GdipCreateFromHDC(hdcMem, &Graphics);
  26.   GdipCreateBitmapFromHBITMAP(hBitmap, 0, &GBitmap);
  27.   GdipDrawImageRect(Graphics, GBitmap, 0, 0, W, H);
  28.   GdipDisposeImage(GBitmap);
  29.   GdipDeleteGraphics(Graphics);
  30.  
  31.   //Result = GetCurrentObject(hdcMem, OBJ_BITMAP);
  32.   SelectObject(hdcMem, hbmOld);
  33.   DeleteDC(hdcMem);
  34.   ReleaseDC(0, hDC);
  35.   return Result;
  36. }
  37.  
  38. //---------------------------------------------------------------------------
  39. // Dibuja un Bitmap en un hDC ajustando su tamaño para que quepa entero
  40. // en las coordenadas dadas
  41. void DrawImageRect(HDC hDC, HBITMAP hBitmap, int X, int Y, int W, int H)
  42. {
  43.   HANDLE Graphics;
  44.   HANDLE GBitmap;
  45.  
  46.   GdipCreateBitmapFromHBITMAP(hBitmap, 0, &GBitmap);
  47.   GdipCreateFromHDC(hDC, &Graphics);
  48.   GdipDrawImageRect(Graphics, GBitmap, X, Y, W, H);
  49.   GdipDisposeImage(GBitmap);
  50.   GdipDeleteGraphics(Graphics);
  51. }


Usando DrawImageRect:

cpp
  1. // Initialize GDI+.
  2. __int64 gdiplusStartupInput[3];
  3. ULONG_PTR  gdiplusToken;
  4. gdiplusStartupInput[0] = 1; gdiplusStartupInput[1] = 0;
  5. if(GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL)) return;
  6.  
  7. DrawImageRect(GetDC(0), Image2->Picture->Bitmap->Handle, 0, 0, 100, 100);
  8. Image2->Invalidate();
  9.  
  10.  
  11. //shutdown GDI+
  12. GdiplusShutdown(gdiplusToken);


Usando CreateResizeBitmap:

cpp
  1. // Initialize GDI+.
  2. __int64 gdiplusStartupInput[3];
  3. ULONG_PTR  gdiplusToken;
  4. gdiplusStartupInput[0] = 1; gdiplusStartupInput[1] = 0;
  5.  
  6. Image1->Picture->Bitmap->Handle = CreateResizeBitmap(Image1->Picture->Bitmap->Handle, 100, 100);
  7. Image1->Invalidate();
  8.  
  9. // Shutdown GDI+
  10. GdiplusShutdown(gdiplusToken);


Los comentarios de la versión delphi son válidos para la versión Builder.


Saludos.
  • 0

#3 ELKurgan

ELKurgan

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 566 posts
  • LocationEspaña

Posted 13 May 2013 - 01:02 AM

Muchas gracias por el aporte, maestro.

(y)
  • 0

#4 poliburro

poliburro

    Advanced Member

  • Administrador
  • 4945 posts
  • LocationMéxico

Posted 13 May 2013 - 07:21 AM

Amigo mio, muy muy interesante aporte. Muchas gracias por compartirnos parte de tu conocimiento.

Saludos
  • 0




IP.Board spam blocked by CleanTalk.