Ir al contenido



Foto

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


  • Por favor identifícate para responder
7 respuestas en este tema

#1 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 06 marzo 2012 - 04:32

Aunque ya se ha publicado código en el foro para realizar las rotaciones de una imagen, quiero añadir esta eficiente forma de conseguirlo usando la API GDI plus. Para compatibilizar con versiones antiguas de delphi y Builder, voy a usar la GDI+ flat API.

El corazón del sistema está en la API GdipRotateWorldTransform y para conseguir el punto del centro de rotación, la API GdipTranslateWorldTransform.

La función que presento, rota una imagen sobre un punto de rotación elegido y un determinado número de grados. Recibe como parámetro la imagen en un HDC (Canvas.Handle) y devuelve un HBITMAP.

Como de costumbre daré las versiones delphi y C/C++ del mismo código:
 

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  GdipRotateWorldTransform(graphics: Pointer; angle: Single; order: Cardinal): Cardinal; stdcall external 'gdiplus';
  8. function  GdipTranslateWorldTransform(graphics: Pointer; sx, sy: Single; order: Cardinal): Cardinal; stdcall external 'gdiplus';
  9. function  GdipDrawImage(graphics: Pointer; image: THANDLE; sx, sy: Single): Cardinal; stdcall external 'gdiplus';
  10. function  GdipDeleteGraphics(graphics: Pointer): Cardinal; stdcall external 'gdiplus';
  11.  
  12.  
  13. function RotateDC(DC: HDC; x, y, Angle: Single): HBITMAP;
  14. var
  15.   Bitmap: HBITMAP;
  16.   GBitmap: THANDLE;
  17.   Graphics: Pointer;
  18.   BitmapData: TagBITMAP;
  19.   Rect: TRect;
  20. begin
  21.   Bitmap:= GetCurrentObject(DC, OBJ_BITMAP);
  22.   GetObject(Bitmap, sizeof(TagBITMAP), @BitmapData);
  23.   Rect.Left:= 0; Rect.Right:= BitmapData.bmWidth; Rect.Top:= 0; Rect.Bottom:= BitmapData.bmHeight;
  24.   GdipCreateBitmapFromHBITMAP(Bitmap, 0, GBitmap);
  25.   FillRect(DC, Rect, 0);
  26.   GdipCreateFromHDC(DC, Graphics);
  27.   GdipTranslateWorldTransform(Graphics, -x, -y, 0);
  28.   GdipRotateWorldTransform(Graphics, Angle, 1);
  29.   GdipTranslateWorldTransform(Graphics, x, y, 1);
  30.   GdipDrawImage(Graphics, GBitmap, 0, 0);
  31.   GdipDisposeImage(GBitmap);
  32.   GdipDeleteGraphics(Graphics);
  33.   Result:= GetCurrentObject(DC, OBJ_BITMAP);
  34. end;

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.  
  10.   RotateDC(Image1.Picture.Bitmap.Canvas.Handle, Image1.Picture.Width/2, Image1.Picture.Height/2, 45);
  11.   Image1.Invalidate;
  12.  
  13.   // Shutdown GDI+
  14.   GdiplusShutdown(gdiplusToken);
  15. end;

Espero que sea de utilidad.


Saludos.
  • 0

#2 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 06 marzo 2012 - 04:35

Ahora le toca a C/C++.

La explicación es como la versión de delphi. La función rota una imagen sobre un punto de rotación elegido y un determinado número de grados. Recibe como parámetro la imagen en un HDC (Canvas.Handle) y devuelve un HBITMAP.


cpp
  1. #pragma link "Gdiplus.lib" // poner la ruta dentro de las libreriras
  2. //---------------------------------------------------------------------------
  3.  
  4.  
  5. // GDI+ Flat API...
  6. extern "C" {
  7. DWORD WINAPI GdiplusStartup(ULONG_PTR *hToken, void *input, void *Output);
  8. void  WINAPI GdiplusShutdown(ULONG_PTR token);
  9. DWORD WINAPI GdipCreateBitmapFromHBITMAP(HBITMAP hBmp, HPALETTE hPal, HANDLE* GBitmap);
  10. DWORD WINAPI GdipCreateHBITMAPFromBitmap(HANDLE Bmp, HBITMAP* hBmp, DWORD BKColor);
  11. DWORD WINAPI GdipCreateFromHDC(HDC hDC, HANDLE* hGraphics);
  12. DWORD WINAPI GdipDisposeImage(HANDLE hImage);
  13. DWORD WINAPI GdipTranslateWorldTransform(HANDLE hGraphics, float sx, float sy, DWORD Order);
  14. DWORD WINAPI GdipRotateWorldTransform(HANDLE hGraphics, float Angle, DWORD Order);
  15. DWORD WINAPI GdipDrawImage(HANDLE hGraphics, HANDLE hImage, float sx, float sy);
  16. DWORD WINAPI GdipDeleteGraphics(HANDLE hGraphics);
  17.  
  18. DWORD WINAPI GdipImageRotateFlip(HANDLE hImage, DWORD rfType);
  19. }
  20.  
  21.  
  22. HBITMAP RotateDC(HDC hDC, float x, float y, float Angle)
  23. {
  24.   HBITMAP Bitmap;
  25.   HANDLE GBitmap;
  26.   HANDLE Graphics;
  27.   BITMAP BitmapData;
  28.   RECT  Rect;
  29.  
  30.   Bitmap = GetCurrentObject(hDC, OBJ_BITMAP);
  31.   GetObject(Bitmap, sizeof(BITMAP), &BitmapData);
  32.   Rect.left = 0; Rect.right = BitmapData.bmWidth; Rect.top = 0; Rect.bottom = BitmapData.bmHeight;
  33.   GdipCreateBitmapFromHBITMAP(Bitmap, 0, &GBitmap);
  34.   FillRect(hDC, &Rect, 0);
  35.   GdipCreateFromHDC(hDC, &Graphics);
  36.   GdipTranslateWorldTransform(Graphics, -x, -y, 0);
  37.   GdipRotateWorldTransform(Graphics, Angle, 1);
  38.   GdipTranslateWorldTransform(Graphics, x, y, 1);
  39.   GdipDrawImage(Graphics, GBitmap, 0, 0);
  40.   GdipDisposeImage(GBitmap);
  41.   GdipDeleteGraphics(Graphics);
  42.   return GetCurrentObject(hDC, OBJ_BITMAP);
  43. }


Ejemplo de uso:

cpp
  1.   // Initialize GDI+.
  2.   __int64 gdiplusStartupInput[2];
  3.   ULONG_PTR  gdiplusToken;
  4.   gdiplusStartupInput[0] = 1; gdiplusStartupInput[1] = 0;
  5.   if(GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL)) return;
  6.  
  7.   RotateDC(Image1->Picture->Bitmap->Canvas->Handle, Image1->Picture->Width/2, Image1->Picture->Height/2, 45);
  8.   Image1->Invalidate();
  9.  
  10.   //shutdown GDI+
  11.   GdiplusShutdown(gdiplusToken);


Espero que sea de utilidad.


Saludos.
  • 0

#3 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.404 mensajes
  • LocationRepública Dominicana

Escrito 14 julio 2018 - 02:10

Hola amigo Escafandra, ahora mismo estoy en necesidad de rotar imágenes en mi aplicación, antes de utilizar el wrapper IGDI+ de Mitov, me gustaría utilizar tus funciones, tengo una duda, al hacer las rotaciones de la imagen, ¿la misma se actualiza en el archivo en sí?, ó es sólo de manera de visualización?.

 

Saludos.


  • 0

#4 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.404 mensajes
  • LocationRepública Dominicana

Escrito 14 julio 2018 - 03:44

Acabo de probarlo y no me funcionó, la imagen está en un TcxImage de DevExpress, sólo queda en blanco y no rota, ¿ó hay algo que estoy omitiendo?


  • 0

#5 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 14 julio 2018 - 05:25

Hola amigo Escafandra, ahora mismo estoy en necesidad de rotar imágenes en mi aplicación, antes de utilizar el wrapper IGDI+ de Mitov, me gustaría utilizar tus funciones, tengo una duda, al hacer las rotaciones de la imagen, ¿la misma se actualiza en el archivo en sí?, ó es sólo de manera de visualización?.

 

Saludos.

 

La función rota el HBITMAP que tiene un hDC (Canvas.Handle) y lo devuelve, aunque no es necesario hacer nada con él para verlo rotado.

 

Acabo de probarlo y no me funcionó, la imagen está en un TcxImage de DevExpress, sólo queda en blanco y no rota, ¿ó hay algo que estoy omitiendo?

 

No he probado TcxImage por lo que no se que decirte al respecto. Lo importante es pasar como parámetro el hDC (handle to a device context) que en delphi el Canvas.Handle que se está visualizando. Aquí es donde puede estar el fallo. No conviene rotar de nuevo esa imagen ya rotada porque va perdiendo calidad, en su lugar, es mejor guardar una copia del original y rotarla los grados añadidos que queramos. De esta forma obtenemos un efecto de rotación continua y sin perdida de calidad de la imagen, como en el ejemplo visual que muestro y que es el truco de este efecto de la clase TPelota que publiqué hace un par de años:

 

fb7c1f23b98a923450e28bc8cc62f33bo.gif

 

 

 

Saludos.


  • 0

#6 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.404 mensajes
  • LocationRepública Dominicana

Escrito 14 julio 2018 - 06:08

Vale, gracias por la aclaración amigo escafandra, en lo que el hacha iba y venía estuve hurgando la Unidad cxGraphic de DevExpress (Que es lo que estoy utilizando), para mi sorpresa encontré que tiene otra unidad que maneja GDIPlus, dentro de la Unidad cxGraphic hay una clase TAlphaBitmap si mal no recuerdo, en ella hay un método Rotate que hace uso de la función RotateFlip de GDIPlus, y me ha funcionado perfecto..

 

Saludos.


  • 0

#7 escafandra

escafandra

    Advanced Member

  • Moderadores
  • PipPipPip
  • 3.837 mensajes
  • LocationMadrid - España

Escrito 15 julio 2018 - 02:35

GdipImageRotateFlip está diseñada para rotaciones con ángulo múltiplo de 90 y especulares. Aquí expuse la forma de usarla en el caso de sistemas que no la aporten: FlipImage. En el caso de querer rotaciones más precisas, debes usar GdipRotateWorldTransform, que es la que se describe en este tema.

 

Por lo que entiendo, con un RotateFip tienes resuelto el problema  (y)

 

Saludos.


  • 2

#8 enecumene

enecumene

    Webmaster

  • Administrador
  • 7.404 mensajes
  • LocationRepública Dominicana

Escrito 15 julio 2018 - 08:16

Rayos, no he buscado bien..gracias por recordar el tema de FlipImage.


  • 0