PDA

Ver la Versión Completa : Extraer un icono con el index


SaraTorres
05-03-2020, 14:55:15
Saludos chicos

Tengo 1 icono con imágenes de diferentes tamaños (256x256, 128x128, 64x64...)

¿Cómo puedo extraer cada imagen a png (index) que hay dentro de 1 icono?

Se los agradezco de antemano.

Neftali [Germán.Estévez]
06-03-2020, 09:08:24
Busca ejemplos sobre PrivateExtractIcons.
En este caso te permite extraer iconos del tamaño especificado sea cual sea el que le especifiques. El icono extraído lo obtendrás a partir del existente en el fichero que mejor se ajusta.

Si el fichero contiene un 64x64 y lo pides ese tamaño te dará ese, si le pides un 72x72 y el mayor que hay en el fichero es un 64x64, te lo generará a partir de ese.


Si no encuentras dímelo, creo que tengo por ahí algunas pruebas antiguas.

escafandra
07-03-2020, 16:31:46
Es posible que de este hilo (https://clubdelphi.com/foros/showthread.php?t=61651&highlight=Resource+por+codigo) saques ideas.


Saludos.

escafandra
07-03-2020, 23:19:52
Quizás esto te aclare un poco más las cosas. Te presento una función que extrae un icono determinado de un archivo de icono que porta más de una imagen.
Quizás lo apetecible sea hacerlo con la API ExtractIcon (https://docs.microsoft.com/es-es/windows/win32/api/shellapi/nf-shellapi-extracticona) o incluso con ExtractIconEx (https://docs.microsoft.com/windows/desktop/api/shellapi/nf-shellapi-extracticonexa) pero ninguna de las dos acierta con el numero exacto de imágenes que tiene un archivo ico. Así que he escrito una función a bajo nivel que los busca, identifica uno por su índice y lo guarda en solitario en otro archivo ico.



Este es el código y las estructuras necesarias:



type

{$ALIGN 1}
TICONDIRENTRY = record
bWidth: BYTE; // Width of the image
bHeight: BYTE; // Height of the image (times 2)
bColorCount: BYTE; // Number of colors in image (0 if >=8bpp)
bReserved: BYTE; // Reserved
wPlanes: WORD; // Color Planes
wBitCount: WORD; // Bits per pixel
dwBytesInRes: DWORD; // how many bytes in this resource?
dwImageOffset: DWORD; // where in the file is this image
end; PICONDIRENTRY = ^TICONDIRENTRY;

type TICONDIR = record
idReserved: WORD; // Reserved
idType: WORD; // resource type (1 for icons)
idCount: WORD; // how many images?
idEntries: array[0..0] of TICONDIRENTRY; // the entries for each image
end; PICONDIR = ^TICONDIR;

type tagICONIMAGE= record
icHeader: BITMAPINFOHEADER; // DIB header
icColors: array[0..0] of RGBQUAD; // Color table
icXORarray: array[0..0] of BYTE; // DIB bits for XOR mask
icANDarray: array[0..0] of BYTE; // DIB bits for AND mask
end; PICONIMAGE = ^tagICONIMAGE;
{$ALIGN OFF}




function ExtractIconFromFile(SourceFileName, DestFileName: String; Index: integer): integer;
var
Stream, SDest: TMemoryStream;
IconDir, IconDirDest: PICONDIR;
AllSize: integer;
SImage, DImage: PBYTE;
begin
Stream:= TMemoryStream.Create;
SDest:= TMemoryStream.Create;
Stream.LoadFromFile(SourceFileName);
IconDir:= Stream.Memory;
Result:= IconDir.idCount;
if Index < Result then
begin
AllSize:= sizeof(ICONDIR) + IconDir.idEntries[index].dwBytesInRes;
SDest.SetSize(AllSize);
IconDirDest:= SDest.Memory;
IconDirDest.idReserved:= 0;
IconDirDest.idType:= 1;
IconDirDest.idCount:= 1;
IconDirDest.idEntries[0]:= IconDir.idEntries[index];
IconDirDest.idEntries[0].dwImageOffset:= sizeof(TICONDIR) + sizeof(TICONDIRENTRY);
SImage:= Stream.Memory;
inc(SImage, IconDir.idEntries[index].dwImageOffset);
DImage:= SDest.Memory;
inc(DImage, IconDirDest.idEntries[0].dwImageOffset);
CopyMemory(DImage, SImage, IconDir.idEntries[index].dwBytesInRes);
SDest.SaveToFile(DestFileName);
end;
Stream.Free;
SDest.Free;
end;



La función devuelve el número de iconos encontrado, si el índice es menor que ese número, guarda ese icono en un archivo. Si el indice es -1 o el no hay nombre de archivo de salida, simplemente devuelve el número de iconos encontrado.



Ejemplo de uso:

var
IconCount: integer;
begin
IconCount:= ExtractIconFromFile('icon.ico', 'p2.ico', 2);
end;





Saludos.

escafandra
08-03-2020, 00:02:26
Y para continuar con la respuesta, te muestro cómo guardar la imagen de un icono localizado por su índice en un archivo png utilizando GDI+ Flat API. El código esta escrito y compilado con delphi7



type

{$ALIGN 1}
TICONDIRENTRY = record
bWidth: BYTE; // Width of the image
bHeight: BYTE; // Height of the image (times 2)
bColorCount: BYTE; // Number of colors in image (0 if >=8bpp)
bReserved: BYTE; // Reserved
wPlanes: WORD; // Color Planes
wBitCount: WORD; // Bits per pixel
dwBytesInRes: DWORD; // how many bytes in this resource?
dwImageOffset: DWORD; // where in the file is this image
end; PICONDIRENTRY = ^TICONDIRENTRY;

type TICONDIR = record
idReserved: WORD; // Reserved
idType: WORD; // resource type (1 for icons)
idCount: WORD; // how many images?
idEntries: array[0..0] of TICONDIRENTRY; // the entries for each image
end; PICONDIR = ^TICONDIR;

type tagICONIMAGE= record
icHeader: BITMAPINFOHEADER; // DIB header
icColors: array[0..0] of RGBQUAD; // Color table
icXORarray: array[0..0] of BYTE; // DIB bits for XOR mask
icANDarray: array[0..0] of BYTE; // DIB bits for AND mask
end; PICONIMAGE = ^tagICONIMAGE;
{$ALIGN OFF}



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 GdipCreateBitmapFromHICON(Icon: HICON; var GBitmap: THANDLE): Cardinal; stdcall external 'gdiplus';
function GdipCreateBitmapFromHBITMAP(hbm: HBITMAP; hpal: HPALETTE; var GBitmap: THANDLE): Cardinal; 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';





// 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;

//---------------------------------------------------------------------------
// Guarda un HBITMAP en un archivo con un formato gráfico determinado
procedure SaveIconToFile(Icon: HICON; FileName, Format: PWCHAR; Quality: ULONG=100);
var
gdiplusToken: DWORD;
GdiPlusStartupInput: array[0..2] of int64;

Clsid: TCLSID;
EP: TEncoderParameters;
GBitmap: THANDLE;
begin
// Inicializamos GDI+.
GdiPlusStartupInput[0]:= 1; GdiPlusStartupInput[1]:= 0;
if GdiplusStartup(gdiplusToken, @GdiPlusStartupInput, nil) <> 0 then exit;

GetEncoderClsid(Format, Clsid);
GetEncoderParameters(@EP, @Quality);

GdipCreateBitmapFromHICON(Icon, GBitmap);

GdipSaveImageToFile(GBitmap, FileName, Clsid, @EP);
GdipDisposeImage(GBitmap);

// Shutdown GDI+
GdiplusShutdown(gdiplusToken);
end;



// Encuentra una imagen de icono y la guarda como png
function IconToPng(IconFileName, PngFileName: String; index: integer): integer;
var
Stream, SDest: TMemoryStream;
IconDir, IconDirDest: PICONDIR;
AllSize: integer;
SImage, DImage: PBYTE;
Icon: TIcon;
begin
Stream:= TMemoryStream.Create;
SDest:= TMemoryStream.Create;
Stream.LoadFromFile(IconFileName);
IconDir:= Stream.Memory;
Result:= IconDir.idCount;
if (Index >= 0) and (Index < Result) and (PngFileName <> '') then
begin
AllSize:= sizeof(ICONDIR) + IconDir.idEntries[index].dwBytesInRes;
SDest.SetSize(AllSize);
IconDirDest:= SDest.Memory;
IconDirDest.idReserved:= 0;
IconDirDest.idType:= 1;
IconDirDest.idCount:= 1;
IconDirDest.idEntries[0]:= IconDir.idEntries[index];
IconDirDest.idEntries[0].dwImageOffset:= sizeof(TICONDIR) + sizeof(TICONDIRENTRY);
SImage:= Stream.Memory;
inc(SImage, IconDir.idEntries[index].dwImageOffset);
DImage:= SDest.Memory;
inc(DImage, IconDirDest.idEntries[0].dwImageOffset);
CopyMemory(DImage, SImage, IconDir.idEntries[index].dwBytesInRes);
Icon:= TIcon.Create;
SDest.Position:= 0;
Icon.LoadFromStream(SDest);
SaveIconToFile(Icon.Handle, PWChar(WideString(PngFileName)), 'image/png');
Icon.Free;
end;
Stream.Free;
SDest.Free;
end;



En realidad la función SaveIconToFile es mucho más potente pues puede guardar el icono en cualquiera de los formatos que admite GDI+ (BMP, GIF, JPEG, PNG, TIFF, WMF, EMF e ICON).



Saludos.