En esta ocasión os voy a mostrar una pequeña aplicación de consola que extrae y escribe en disco cada imagen de un archivo multiframe. El formato de escritura será cualquiera de los que soporta GDI+, por defecto es jpg y la calidad de compresión será configurable de 1 a 100, siendo 100 por defecto. Los nombres de los archivos escritos serán como el del original añadiendo un número que corresponde al indice del frame extraído.
La sintaxis:
GrExtract FileName [format graphic Ext] [%Quality]
Esta es la unidad corazón del sistema:
unit GDI_Extract; //------------------------------------------------------------------------------ // GDI_Extract V 1.0 // escafandra 2018 // Clase para extraer archivos de un archivo grafico multipágina // Usa GDI+ interface 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 Extract(FileName, _format: PWCHAR; Quality: ULONG = 100): boolean; function wcscmp(wstr1, wstr2: PWCHAR): Integer; cdecl external 'crtdll'; function wsprintfW(lpOut, lpFmt: PWCHAR): Integer; cdecl; varargs; external 'User32.dll' name 'wsprintfW'; function AttachConsole(dwProcessId: DWORD): BOOL; stdcall external 'Kernel32.dll'; // GDI+ Flat API... function GdiplusStartup(var GdiToken: DWORD; Startup, Output: PBYTE): Cardinal; stdcall external 'gdiplus'; procedure GdiplusShutdown(GdiToken: DWORD); stdcall external 'gdiplus'; function GdipLoadImageFromFile(lpFileName: PWideChar; var hImage: THANDLE): DWORD; stdcall external 'gdiplus'; function GdipImageGetFrameDimensionsList(hImage: THANDLE; dimensionIDs: PGUID; Count: Integer): DWORD; stdcall external 'gdiplus'; function GdipImageGetFrameCount(hImage: THANDLE; lpDimensionID: PGUID; out Count: UINT): DWORD; stdcall external 'gdiplus'; function GdipImageSelectActiveFrame(hImage: THANDLE; DimensionID: PGUID; frameIndex: Integer): 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 GdipSaveImageToFile(image: THANDLE; FileName: PWCHAR; var clsidEncoder: TCLSID; encoderParams: Pointer): Cardinal; stdcall external 'gdiplus'; implementation // 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; function Extract(FileName, _format: PWCHAR; Quality: ULONG = 100): boolean; var gdiplusToken: DWORD; GdiPlusStartupInput: array[0..2] of int64; hGdipImage: THANDLE; Clsid: TCLSID; EP: TEncoderParameters; dimensionID: TGUID; Frames: UINT; Index: integer; Format: Array [0..80] of WCHAR; lpFile: Array [0..MAX_PATH] of WCHAR; Ext: PWCHAR; begin Result:= false; // Inicializamos GDI+. GdiPlusStartupInput[0]:= 1; GdiPlusStartupInput[1]:= 0; if GdiplusStartup(gdiplusToken, @GdiPlusStartupInput, nil) <> 0 then exit; if lstrcmpiW(_format, 'jpg') = 0 then wsprintfW(@Format, 'image/jpeg') else if lstrcmpiW(_format, 'tif') = 0 then wsprintfW(@Format, 'image/tiff') else wsprintfW(@Format, 'image/%s', _format); lstrcpyW(@lpFile, FileName); Ext:= @lpFile[lstrlenW(@lpFile)-4]; if GdipLoadImageFromFile(FileName, hGdipImage) <> 0 then exit; if hGdipImage = 0 then exit; GetEncoderClsid(@Format, Clsid); GetEncoderParameters(@EP, @Quality); if hGdipImage <> 0 then begin GdipImageGetFrameDimensionsList(hGdipImage, @dimensionID, 1); GdipImageGetFrameCount(hGdipImage, @dimensionID, Frames); for Index:= 0 to Frames-1 do begin GdipImageSelectActiveFrame(hGdipImage, @dimensionID, Index); wsprintfW(Ext, '%d.%s', Index, _format); GdipSaveImageToFile(hGdipImage, @lpFile, Clsid, @EP); end; end; GdipDisposeImage(hGdipImage); GdiplusShutdown(gdiplusToken); Result:= true; end; end.
Y esta es la aplicación de consola que sirve de interface:
program Gr_Extract; uses Windows, ShellAPI, GDI_Extract; function _wtoi(wstr: PWCHAR): Integer; cdecl external 'crtdll'; type AWCHAR = Array [0..10] of PWCHAR; PAWCHAR = ^AWCHAR; var Argc: integer; Argv: PAWCHAR; Help: ShortString; F: THANDLE; begin // Buscando los parámetros pasados Argv:= PAWCHAR(CommandLineToArgvW(GetCommandLineW, Argc)); // Si no hay parámetros, muestro ayuda if Argc <= 1 then begin Help:= #10+#10+'GrExtract FileName [format graphic Ext] [%Quality]' +#10 + #0; AttachConsole(DWORD(-1)); F:= CreateFile('CONOUT$', GENERIC_READ or GENERIC_WRITE, FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0); WriteConsole(F, @Help[1], lstrlen(@Help[1]), PDWORD(nil)^, nil); CloseHandle(F); FreeConsole; end else if Argc = 2 then Extract(Argv[1], 'jpg') else if Argc = 3 then Extract(Argv[1], Argv[2]) else if Argc > 3 then Extract(Argv[1], Argv[2], _wtoi(Argv[3])); end.
Si la aplicación se ejecuta en una consola mostrará los errores de sintaxis en ella. En caso contrario será mudo. Para realizar la extracción de los frames de un archivo Gif o Tif, soltadlo sobre el ejecutable o usad la consola para pasar más parámetros.
Saludos.