Ir al contenido


Foto

[MULTILENGUAJE] Como guardar archivos gráficos en varios formatos.


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

#1 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 17 junio 2013 - 10:47

Ya he publicado varias cosas en delphi usando GDIplus, entre otras cosas cómo leer gráficos en varios formatos. En esta ocasión voy a publicar un truco básico y complementario del anterior, como guardar archivos gráficos en varios formatos. GDIplus soporta BMP, GIF, JPEG, PNG, TIFF, WMF, EMF e ICON, sin embargo sólo puede codificar BMP, GIF, JPEG, PNG y TIFF.

En este truco vamos a ver como leer un formato y convertirlo a un HBITMAP para luego ser capaces de guardarlo en un fichero eligiendo la calidad de compresión, en los formatos en los que es posible.

La función con la que elegimos el codificador (encoder) es GetEncoderClsid, esta función recibe una cadela como parémetro que define el encoder. dicha cadena estará compuesta de la siguiente forma y en minúsculas: image/xxx, siendo xxx la extensión del archivo en cuyo formato queremos salvar el fichero, salvo en dos excepciones:
  jpg ->  image/jpeg
  tif  ->  image/tiff

Todas las cadenas van a ser unicode. Vamos con el código, primero en delphi:


delphi
  1. uses
  2.   Windows;
  3.  
  4. type
  5. TCLSID = TGUID;
  6. PCLSID = ^TCLSID;
  7. TImageCodecInfo = packed record
  8.   Clsid:            TCLSID;
  9.   FormatID:          TGUID;
  10.   CodecName:        PWCHAR;
  11.   DllName:          PWCHAR;
  12.   FormatDescription: PWCHAR;
  13.   FilenameExtension: PWCHAR;
  14.   MimeType:          PWCHAR;
  15.   Flags:            DWORD;
  16.   Version:          DWORD;
  17.   SigCount:          DWORD;
  18.   SigSize:          DWORD;
  19.   SigPattern:        PBYTE;
  20.   SigMask:          PBYTE;
  21. end;
  22. PImageCodecInfo = ^TImageCodecInfo;
  23.  
  24. TEncoderParameter = packed record
  25.   Guid:          TGUID;
  26.   NumberOfValues: ULONG;
  27.   Type_:          ULONG;
  28.   Value:          Pointer;
  29. end;
  30. PEncoderParameter = ^TEncoderParameter;
  31.  
  32. TEncoderParameters = packed record
  33.   Count    : UINT;
  34.   Parameter : array[0..0] of TEncoderParameter;
  35. end;
  36. PEncoderParameters = ^TEncoderParameters;
  37.  
  38.  
  39. function  wcscmp(wstr1, wstr2: PWCHAR): Integer; cdecl external 'crtdll';
  40.  
  41. // GDI+ Flat API...
  42. function  GdiplusStartup(var GdiToken: DWORD; Startup, Output: PBYTE): Cardinal; stdcall external 'gdiplus';
  43. procedure GdiplusShutdown(GdiToken: DWORD); stdcall external 'gdiplus';
  44. function  GdipCreateBitmapFromHBITMAP(hbm: HBITMAP; hpal: HPALETTE; var GBitmap: THANDLE): Cardinal; stdcall external 'gdiplus';
  45. function  GdipCreateHBITMAPFromBitmap(GBitmap: THANDLE; var hBitmap: HBITMAP; BKColor: DWORD): DWORD; stdcall external 'gdiplus';
  46. function  GdipGetImageEncodersSize(var numEncoders: DWORD; var size: DWORD): Cardinal; stdcall external 'gdiplus';
  47. function  GdipGetImageEncoders(numEncoders, size: DWORD; encoders: PImageCodecInfo): Cardinal; stdcall external 'gdiplus';
  48. function  GdipDisposeImage(image: THANDLE): Cardinal; stdcall external 'gdiplus';
  49. function  GdipCreateBitmapFromFile(lpFileName: PWCHAR; var GBitmap: THANDLE): DWORD; stdcall external 'gdiplus';
  50. function  GdipSaveImageToFile(image: THANDLE; FileName: PWCHAR; var clsidEncoder: TCLSID; encoderParams: Pointer):
  51.  
  52. Cardinal; stdcall external 'gdiplus';
  53.  
  54.  
  55. // Calidad de imagen y factor de compresión
  56. // Quality = 100 es la maxima calidad.
  57. procedure GetEncoderParameters(EP: PEncoderParameters; Quality: PULONG);
  58. const
  59.   EncoderQuality: TGUID = '{1d5be4b5-fa4a-452d-9cdd-5db35105e7eb}';
  60. begin
  61.   EP.Count:= 1;
  62.   EP.Parameter[0].Guid:= EncoderQuality;
  63.   EP.Parameter[0].Type_:= 4; //Gdiplus::EncoderParameterValueTypeLong;
  64.   EP.Parameter[0].NumberOfValues:= 1;
  65.   EP.Parameter[0].Value:= Quality;
  66. end;
  67.  
  68. // Obtener el CLSID para la codificación de un formato gráfico
  69. function GetEncoderClsid(Format: PWCHAR; var Clsid: TCLSID): boolean;
  70. var
  71.   i, N, Size: Cardinal;
  72.   ICInfo: array of TImageCodecInfo;
  73. begin
  74.   i:= 0; N:= 0; Size:= 0;
  75.   GdipGetImageEncodersSize(N, Size);
  76.   if Size > 0 then
  77.   begin
  78.     SetLength(ICInfo, Size);
  79.     GdipGetImageEncoders(N, Size, @ICInfo[0]);
  80.     while (i<N) and (wcscmp(ICInfo[i].MimeType, Format)<>0) do inc(i);
  81.     if i<N then Clsid:= ICInfo[i].Clsid;
  82.   end;
  83.   Result:= boolean(i<N);
  84. end;
  85.  
  86. //---------------------------------------------------------------------------
  87. // Crea un HBITMAP desde un fichero usando GDI+
  88. function CreateHBITMAPFromFileW(FileName: PWCHAR): HBITMAP;
  89. var
  90.   GBitmap: THANDLE;
  91. begin
  92.   Result:= 0;
  93.   GBitmap:= 0;
  94.   GdipCreateBitmapFromFile(FileName, GBitmap);
  95.   GdipCreateHBITMAPFromBitmap(GBitmap, Result, 0);
  96.   GdipDisposeImage(GBitmap);
  97. end;
  98.  
  99. //---------------------------------------------------------------------------
  100. // Guarda un HBITMAP en un archivo con un formato gráfico determinado
  101. // Calidad de 0 a 100
  102. procedure SaveBitmapToFile(Bitmap: HBITMAP; FileName, Format: PWCHAR; Quality: ULONG=100);
  103. var
  104.   Clsid: TCLSID;
  105.   EP: TEncoderParameters;
  106.   GBitmap: THANDLE;
  107. begin
  108.   GetEncoderClsid(Format, Clsid);
  109.   GetEncoderParameters(@EP, @Quality);
  110.   GdipCreateBitmapFromHBITMAP(Bitmap, 0, GBitmap);
  111.  
  112.   GdipSaveImageToFile(GBitmap, FileName, Clsid, @EP);
  113.   GdipDisposeImage(GBitmap);
  114. end;

CreateHBITMAPFromFile y CreateHBITMAPFromFileW eras una función conocida de otros trucos, la segunda es versión unicode.

SaveBitmapToFile tiene poca descripción salvo que debemos pasar el mombre del archivo con extensión, el formato gráfico y la calidad de compresión.

Un ejemplo de uso:






delphi
  1. var
  2.   gdiplusToken: DWORD;
  3.   GdiPlusStartupInput: array[0..2] of int64;
  4.   Bmp: HBITMAP;
  5. begin
  6.   // Inicializamos GDI+.
  7.   GdiPlusStartupInput[0]:= 1; GdiPlusStartupInput[1]:= 0;
  8.   if GdiplusStartup(gdiplusToken, @GdiPlusStartupInput, nil) <> 0 then exit;
  9.  
  10.   Bmp:= CreateHBITMAPFromFileW('Imagen.jpg');  // Un formato cualquiera de los soportados...
  11.   if Bmp <> 0 then
  12.   begin
  13.     SaveBitmapToFile(Bmp, '_Imagen.bmp', 'image/bmp');
  14.     SaveBitmapToFile(Bmp, '_Imagen.jpg', 'image/jpeg');
  15.     SaveBitmapToFile(Bmp, '_Imagen.png', 'image/png');
  16.     SaveBitmapToFile(Bmp, '_Imagen.tif', 'image/tiff');
  17.     SaveBitmapToFile(Bmp, '_Imagen.gif', 'image/gif');
  18.   end;
  19.  
  20. // Shutdown GDI+
  21.   GdiplusShutdown(gdiplusToken);
  22. end.

Espero que este sencillo truco os sea de ayuda y utilidad.


Saludos.

Edito para ajustar las etiquetas de código


  • 0

#2 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 17 junio 2013 - 11:16

Ahora el código equivalente en C/C++ también con API plana:


cpp
  1. #include <windows.h>
  2. #pragma link "gdiplus.lib"
  3.  
  4. struct EncoderParameter
  5. {
  6.   GUID    Guid;              // GUID of the parameter
  7.   ULONG  NumberOfValues;    // Number of the parameter values
  8.   ULONG  Type;              // Value type, like ValueTypeLONG  etc.
  9.   VOID*  Value;              // A pointer to the parameter values
  10. };
  11.  
  12. struct EncoderParameters
  13. {
  14.   UINT Count;                  // Number of parameters in this struct
  15.   EncoderParameter Parameter[1]; // Parameter values
  16. };
  17.  
  18. struct ImageCodecInfo
  19. {
  20.   CLSID Clsid;
  21.   GUID FormatID;
  22.   PWCHAR CodecName;
  23.   PWCHAR DllName;
  24.   PWCHAR FormatDescription;
  25.   PWCHAR FilenameExtension;
  26.   PWCHAR MimeType;
  27.   DWORD Flags;
  28.   DWORD Version;
  29.   DWORD SigCount;
  30.   DWORD SigSize;
  31.   PBYTE SigPattern;
  32.   PBYTE SigMask;
  33. };
  34.  
  35.  
  36. extern "C"{
  37. DWORD WINAPI GdiplusStartup(ULONG_PTR *hToken, void *input, void *Output);
  38. void  WINAPI GdiplusShutdown(ULONG_PTR token);
  39. DWORD WINAPI GdipCreateBitmapFromFile(WCHAR* filename, HANDLE* bitmap);
  40. DWORD WINAPI GdipCreateHBITMAPFromBitmap(HANDLE bitmap, HBITMAP* hBitmap, DWORD BKColor);
  41. DWORD WINAPI GdipCreateBitmapFromHBITMAP(HBITMAP hBmp, HPALETTE hPal, HANDLE* GBitmap);
  42. DWORD WINAPI GdipGetImageEncodersSize(UINT *numEncoders, UINT *size);
  43. DWORD WINAPI GdipGetImageEncoders(UINT numEncoders,  UINT size, ImageCodecInfo *encoders);
  44. DWORD WINAPI GdipSaveImageToFile(HANDLE himage, PWCHAR FileName, CLSID &clsidEncoder, void *encoderParams);
  45. DWORD WINAPI GdipDisposeImage(HANDLE himage);
  46. }
  47.  
  48.  
  49. // Inicializamos GDI+
  50. ULONG_PTR  gdiplusToken;
  51. char GdiPlusStartupInput[16] = {1,0};
  52.  
  53. void GetEncoderParameters(EncoderParameters *EP, PULONG Quality)
  54. {
  55.   // Calidad de imagen y dfactor de compresión
  56.   // 100 = maxima calidad
  57.   // _GUID _EncoderQuality: Adaptado de GdiplusImaging.h para evitar error en Link
  58.   _GUID _EncoderQuality = {0x1d5be4b5,0xfa4a,0x452d,0x9c,0xdd,0x5d,0xb3,0x51,0x05,0xe7,0xeb};
  59.   EP->Count = 1;
  60.   EP->Parameter[0].Guid = _EncoderQuality;
  61.   EP->Parameter[0].Type = 4;  //Gdiplus::EncoderParameterValueTypeLong; //GdiplusEnums.h
  62.   EP->Parameter[0].NumberOfValues = 1;
  63.   EP->Parameter[0].Value = Quality;
  64. }
  65.  
  66. bool GetEncoderClsid(const WCHAR* Format, CLSID* Clsid)
  67. {
  68.   UINT i=0, N=0, Size=0;
  69.  
  70.   GdipGetImageEncodersSize(&N, &Size);
  71.   if(!Size) return false;
  72.  
  73.   ImageCodecInfo* ICInfo = new ImageCodecInfo[Size];
  74.   GdipGetImageEncoders(N, Size, ICInfo);
  75.  
  76.   while (i<N && wcscmp(ICInfo[i].MimeType, Format)) i++;
  77.   if(i<N) *Clsid = ICInfo[i].Clsid;
  78.   delete [] ICInfo;
  79.  
  80.   return (i<N);
  81. }
  82.  
  83. //---------------------------------------------------------------------------
  84. // Crea un HBITMAP desde un fichero usando GDI+
  85. HBITMAP CreateHBITMAPFromFileW(WCHAR *FileName)
  86. {
  87.   HBITMAP hBitmap = 0;
  88.   HANDLE GBitmap = 0;
  89.   GdipCreateBitmapFromFile(FileName, &GBitmap);
  90.   GdipCreateHBITMAPFromBitmap(GBitmap, &hBitmap, 0);
  91.   GdipDisposeImage(GBitmap);
  92.   return hBitmap;
  93. }
  94.  
  95. //---------------------------------------------------------------------------
  96. void SaveBitmapToFile(HBITMAP hBitmap, WCHAR* FileName, PWCHAR Format, ULONG Quality = 100)
  97. {
  98.   CLSID Clsid;
  99.   EncoderParameters EP;
  100.   HANDLE GBitmap;
  101.  
  102.   GetEncoderClsid(Format, &Clsid);
  103.   GetEncoderParameters(&EP, &Quality);
  104.   GdipCreateBitmapFromHBITMAP(hBitmap, 0, &GBitmap);
  105.  
  106.   GdipSaveImageToFile(GBitmap, FileName, Clsid, &EP);
  107.   GdipDisposeImage(GBitmap);
  108. }



y un ejemplo de uso:


cpp
  1. // Inicializamos GDI+
  2. ULONG_PTR  gdiplusToken;
  3. char GdiPlusStartupInput[16] = {1,0};
  4. GdiplusStartup(&gdiplusToken, GdiPlusStartupInput, NULL);
  5.  
  6. // Leemos y obtememos un HBITMAP:
  7.   HBITMAP hBitmap = CreateHBITMAPFromFileW(L"Imagen.jpg");
  8.  
  9. // Escribimos... 
  10.   if(hBitmap){
  11.     SaveBitmapToFile(hBitmap, L"_Imagen.bmp", L"image/bmp");
  12.     SaveBitmapToFile(hBitmap, L"_Imagen.jpg", L"image/jpeg");
  13.     SaveBitmapToFile(hBitmap, L"_Imagen.png", L"image/png");
  14.     SaveBitmapToFile(hBitmap, L"_Imagen.tif", L"image/tiff");
  15.     SaveBitmapToFile(hBitmap, L"_Imagen.gif", L"image/gif");
  16.   }
  17.  
  18.  
  19. // Cerramos GDI+
  20. GdiplusShutdown(gdiplusToken);

 


Saludos.

Edito para ajustar las etiquetas de código
  • 0

#3 poliburro

poliburro

    Advanced Member

  • Administrador
  • 4.945 mensajes
  • LocationMéxico

Escrito 17 junio 2013 - 11:43

(y)  (y) Gracias amigo :D
  • 0

#4 ELKurgan

ELKurgan

    Advanced Member

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

Escrito 17 junio 2013 - 11:12

Gracias por el aporte, maestro

(y)
  • 0

#5 seoane

seoane

    Advanced Member

  • Administrador
  • 1.259 mensajes
  • LocationEspaña

Escrito 17 junio 2013 - 11:51

Muy interesante!  (y)
  • 0




IP.Board spam blocked by CleanTalk.