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:
uses Windows; type TCLSID = TGUID; PCLSID = ^TCLSID; TImageCodecInfo = packed record Clsid: TCLSID; FormatID: TGUID; CodecName: PWCHAR; DllName: PWCHAR; FormatDescription: PWCHAR; FilenameExtension: PWCHAR; MimeType: PWCHAR; Flags: DWORD; Version: DWORD; SigCount: DWORD; SigSize: DWORD; SigPattern: PBYTE; SigMask: PBYTE; end; PImageCodecInfo = ^TImageCodecInfo; TEncoderParameter = packed record Guid: TGUID; NumberOfValues: ULONG; Type_: ULONG; Value: Pointer; end; PEncoderParameter = ^TEncoderParameter; TEncoderParameters = packed record Count : UINT; Parameter : array[0..0] of TEncoderParameter; end; PEncoderParameters = ^TEncoderParameters; function wcscmp(wstr1, wstr2: PWCHAR): Integer; cdecl external 'crtdll'; // GDI+ Flat API... function GdiplusStartup(var GdiToken: DWORD; Startup, Output: PBYTE): Cardinal; stdcall external 'gdiplus'; procedure GdiplusShutdown(GdiToken: DWORD); stdcall external 'gdiplus'; function GdipCreateBitmapFromHBITMAP(hbm: HBITMAP; hpal: HPALETTE; var GBitmap: THANDLE): Cardinal; stdcall external 'gdiplus'; function GdipCreateHBITMAPFromBitmap(GBitmap: THANDLE; var hBitmap: HBITMAP; BKColor: DWORD): DWORD; stdcall external 'gdiplus'; function GdipGetImageEncodersSize(var numEncoders: DWORD; var size: DWORD): Cardinal; stdcall external 'gdiplus'; function GdipGetImageEncoders(numEncoders, size: DWORD; encoders: PImageCodecInfo): Cardinal; stdcall external 'gdiplus'; function GdipDisposeImage(image: THANDLE): Cardinal; stdcall external 'gdiplus'; function GdipCreateBitmapFromFile(lpFileName: PWCHAR; var GBitmap: THANDLE): DWORD; stdcall external 'gdiplus'; function GdipSaveImageToFile(image: THANDLE; FileName: PWCHAR; var clsidEncoder: TCLSID; encoderParams: Pointer): Cardinal; stdcall external 'gdiplus'; // Calidad de imagen y factor de compresión // Quality = 100 es la maxima calidad. procedure GetEncoderParameters(EP: PEncoderParameters; Quality: PULONG); const EncoderQuality: TGUID = '{1d5be4b5-fa4a-452d-9cdd-5db35105e7eb}'; begin EP.Count:= 1; EP.Parameter[0].Guid:= EncoderQuality; EP.Parameter[0].Type_:= 4; //Gdiplus::EncoderParameterValueTypeLong; EP.Parameter[0].NumberOfValues:= 1; EP.Parameter[0].Value:= Quality; end; // Obtener el CLSID para la codificación de un formato gráfico function GetEncoderClsid(Format: PWCHAR; var Clsid: TCLSID): boolean; var i, N, Size: Cardinal; ICInfo: array of TImageCodecInfo; begin i:= 0; N:= 0; Size:= 0; GdipGetImageEncodersSize(N, Size); if Size > 0 then begin SetLength(ICInfo, Size); GdipGetImageEncoders(N, Size, @ICInfo[0]); while (i<N) and (wcscmp(ICInfo[i].MimeType, Format)<>0) do inc(i); if i<N then Clsid:= ICInfo[i].Clsid; end; Result:= boolean(i<N); end; //--------------------------------------------------------------------------- // Crea un HBITMAP desde un fichero usando GDI+ function CreateHBITMAPFromFileW(FileName: PWCHAR): HBITMAP; var GBitmap: THANDLE; begin Result:= 0; GBitmap:= 0; GdipCreateBitmapFromFile(FileName, GBitmap); GdipCreateHBITMAPFromBitmap(GBitmap, Result, 0); GdipDisposeImage(GBitmap); end; //--------------------------------------------------------------------------- // Guarda un HBITMAP en un archivo con un formato gráfico determinado // Calidad de 0 a 100 procedure SaveBitmapToFile(Bitmap: HBITMAP; FileName, Format: PWCHAR; Quality: ULONG=100); var Clsid: TCLSID; EP: TEncoderParameters; GBitmap: THANDLE; begin GetEncoderClsid(Format, Clsid); GetEncoderParameters(@EP, @Quality); GdipCreateBitmapFromHBITMAP(Bitmap, 0, GBitmap); GdipSaveImageToFile(GBitmap, FileName, Clsid, @EP); GdipDisposeImage(GBitmap); 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:
var gdiplusToken: DWORD; GdiPlusStartupInput: array[0..2] of int64; Bmp: HBITMAP; begin // Inicializamos GDI+. GdiPlusStartupInput[0]:= 1; GdiPlusStartupInput[1]:= 0; if GdiplusStartup(gdiplusToken, @GdiPlusStartupInput, nil) <> 0 then exit; Bmp:= CreateHBITMAPFromFileW('Imagen.jpg'); // Un formato cualquiera de los soportados... if Bmp <> 0 then begin SaveBitmapToFile(Bmp, '_Imagen.bmp', 'image/bmp'); SaveBitmapToFile(Bmp, '_Imagen.jpg', 'image/jpeg'); SaveBitmapToFile(Bmp, '_Imagen.png', 'image/png'); SaveBitmapToFile(Bmp, '_Imagen.tif', 'image/tiff'); SaveBitmapToFile(Bmp, '_Imagen.gif', 'image/gif'); end; // Shutdown GDI+ GdiplusShutdown(gdiplusToken); end.
Espero que este sencillo truco os sea de ayuda y utilidad.
Saludos.
Edito para ajustar las etiquetas de código