escafandra |
29-12-2008 16:34:57 |
Pasar un HICON a un TIcon o a un archivo
Determinadas APIs como ExtractAssociatedIcon, devuelven un manejador HICON. Cuando queremos cargar la
imagen del Handle hIcon en un componente TIcon, la forma más directa es:
Código:
Icon->Handle = hIcon;
Pero, lamentablemente esto no funciona del todo bien en todas las versiones de Builder y Delphi. Si lo que pretendemos es conseguir un icono de 16 colores, entonces no tendremos problemas. Pero si queremos manejar mas colores nos los truncará a un máximo de 16 (4 bits). Esto ocurre con seguridad en versiones de Builder y Delphi inferiores a la 6, incluida ésta. En las versiones del 2009 está resuelto este problema.
Para solventar el inconveniente he escrito una función que consigue un bloque de memoria con una imagen de un archivo.ico a partir de un Handle HICON de Windows. Este Buffer puede usarse para salvar directamente el icono en un archivo.ico o para pasar la imagen a un TIcon mediante un TMemoryStream. El código permite elegir el número de bits de color (bitCount) con el que trabajaremos, este será de 1, 4, 8, 24, ó 32.
Pasamos al código.
En primer lugar publico un archivo cabecera con las definiciones y estructuras necesarias. Veréis que alinea al compilador a Word dato muy importante. Este archivo lo he denominado Iconos.h:
Código:
#ifndef iconosH
#define iconosH
#pragma pack( push )
#pragma pack( 2 )
typedef struct
{
BYTE bWidth; // Width of the image
BYTE bHeight; // Height of the image (times 2)
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // how many bytes in this resource?
WORD nID; // the ID
} MEMICONDIRENTRY, *LPMEMICONDIRENTRY, GRPICONDIRENTRY, *LPGRPICONDIRENTRY;
typedef struct
{
WORD idReserved; // Reserved
WORD idType; // resource type (1 for icons)
WORD idCount; // how many images?
MEMICONDIRENTRY idEntries[1]; // the entries for each image
} MEMICONDIR, *LPMEMICONDIR, GRPICONDIR, *LPGRPICONDIR;
// These next two structs represent how the icon information is stored
// in an ICO file.
typedef struct
{
BYTE bWidth; // Width of the image
BYTE bHeight; // Height of the image (times 2)
BYTE bColorCount; // Number of colors in image (0 if >=8bpp)
BYTE bReserved; // Reserved
WORD wPlanes; // Color Planes
WORD wBitCount; // Bits per pixel
DWORD dwBytesInRes; // how many bytes in this resource?
DWORD dwImageOffset; // where in the file is this image
} ICONDIRENTRY, *LPICONDIRENTRY;
typedef struct
{
WORD idReserved; // Reserved
WORD idType; // resource type (1 for icons)
WORD idCount; // how many images?
ICONDIRENTRY idEntries[1]; // the entries for each image
} ICONDIR, *LPICONDIR;
typedef struct
{
BITMAPINFOHEADER icHeader; // DIB header
RGBQUAD icColors[1]; // Color table
BYTE icXOR[1]; // DIB bits for XOR mask
BYTE icAND[1]; // DIB bits for AND mask
} ICONIMAGE, *LPICONIMAGE;
#pragma pack( pop )
//---------------------------------------------------------------------------
#endif
A continuación coloco una versión de la función que localiza la memoria necesaria con la API VirtualAlloc, esto obliga a liberar el bloque después de usar la función. La función devuelve un puntero al mismo bloque:
Código:
# include "Iconos.h"
//---------------------------------------------------------------------------
long
NColors(WORD bitCount)
{
if (bitCount == 1 || bitCount == 4 || bitCount == 8)
return 1 << bitCount;
else if (bitCount >= 24)
return 0;
return -1;
}
//---------------------------------------------------------------------------
// Devuelve una imagen de archivo.ico para poder guardar en disco....
// Reserva la memoria necesaria devolviendo en Size el Tamaño
// Requiere liberar luego la memoria con VirtualFree(Mem, 0, MEM_RELEASE);
void* hIconToMem(HICON hIcon, int BitCountPerPixel, int* Size)
{
// Localizo la información del icono y su tamaño en un fichero.ico
HDC hDC = ::GetDC(NULL); // ScreenDC
ICONINFO IconInfo;
BITMAP bmpAND={0};
BITMAP bmpXOR={0};
bool I = GetIconInfo(hIcon, &IconInfo);
GetObject(IconInfo.hbmMask, sizeof(BITMAP), &bmpAND);
GetObject(IconInfo.hbmColor, sizeof(BITMAP), &bmpXOR);
if(BitCountPerPixel==0) BitCountPerPixel=bmpXOR.bmPlanes*bmpXOR.bmBitsPixel;
int RawDataSizeAND=((((bmpAND.bmWidth*bmpAND.bmBitsPixel)+31) & ~31) >> 3)*bmpAND.bmHeight;
int RawDataSizeXOR=((((bmpXOR.bmWidth*BitCountPerPixel)+31) & ~31) >> 3)*bmpXOR.bmHeight;
int RawDataSize = RawDataSizeAND + RawDataSizeXOR;
int PalSize=(BitCountPerPixel>8 ? 0 :1 << BitCountPerPixel)<<2; // RGBQUAD 4
int AllSize= sizeof(ICONDIR)+sizeof(BITMAPINFOHEADER)+PalSize+RawDataSizeAND+RawDataSizeXOR;
int Width = bmpAND.bmWidth;
int Height = bmpAND.bmHeight;
// Reservo memoria para el fichero
BYTE* FileIconMem = (BYTE*)VirtualAlloc(0, AllSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
ICONDIR* IconDir = (ICONDIR*)FileIconMem;
// Obtengo los Bits de cada parte Mask y Color
BITMAPINFO bmiAND;
BITMAPINFO *bmiICON = (BITMAPINFO*)(FileIconMem + sizeof(ICONDIR));
LPVOID lpBitsXOR = (BITMAPINFO*)(FileIconMem + sizeof(ICONDIR) + sizeof(BITMAPINFOHEADER) + PalSize);
LPVOID lpBitsAND = (BITMAPINFO*)((BYTE*)lpBitsXOR + RawDataSizeXOR);
// Preparo la cabecera del Icon
IconDir->idReserved = 0;
IconDir->idType = 1;
IconDir->idCount = 1;
IconDir->idEntries[0].bWidth = Width;
IconDir->idEntries[0].bHeight = Height;
IconDir->idEntries[0].bColorCount = NColors(BitCountPerPixel);
IconDir->idEntries[0].bReserved = 0;
IconDir->idEntries[0].wPlanes = 0; // Color Planes
IconDir->idEntries[0].wBitCount = 0;//BitCountPerPixel; // Bits per pixel
IconDir->idEntries[0].dwBytesInRes = AllSize-sizeof(ICONDIR); // How many bytes in this resource?
IconDir->idEntries[0].dwImageOffset = sizeof(ICONDIR); // Where in the file is this image?
LPICONIMAGE IconImage = (LPICONIMAGE)(bmiICON);
memset(IconImage, 0, sizeof(BITMAPINFOHEADER));
IconImage->icHeader.biSize = sizeof(BITMAPINFOHEADER);
IconImage->icHeader.biWidth = Width;
IconImage->icHeader.biHeight = Height;
IconImage->icHeader.biPlanes = 1;
IconImage->icHeader.biBitCount = BitCountPerPixel;
IconImage->icHeader.biSizeImage = RawDataSize;
// Preparo BITMAPINFOHEADER para la Mascara (bmiAND)
memcpy(&bmiAND, bmiICON, sizeof(BITMAPINFOHEADER));
bmiAND.bmiHeader.biSizeImage = RawDataSizeAND;
bmiAND.bmiHeader.biBitCount = 1;
bmiAND.bmiHeader.biClrUsed = 1;
bmiAND.bmiHeader.biClrImportant = 1;
// Recupero los bits de cada hBitmap del icono
GetDIBits(hDC, IconInfo.hbmColor, 0, Height, lpBitsXOR, bmiICON, DIB_RGB_COLORS);
GetDIBits(hDC, IconInfo.hbmMask, 0, Height, lpBitsAND, &bmiAND, DIB_RGB_COLORS);
// Sumo las dos alturas de ambos bitmaps del icono
IconImage->icHeader.biHeight = Height*2;
ReleaseDC(0, hDC);
// Salida
*Size = AllSize;
return FileIconMem;
}
Para ilustrar el uso voy a colocar dos ejemplos prácticos. En el primero guardo un hIcon en un archivo y lo vuelvo a abrir para ver lo que sucede:
Código:
TIcon *Icon = new TIcon;
Icon->LoadFromFile("MI_ICONO.ICO");
// Convertimos el HICON a imagen en memoria del .ico
int Size;
void* FileIconMem = hIconToMem(Icon->Handle, 32, &Size);
HANDLE hFile=CreateFile("FilePath.ico", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwWritten;
WriteFile(hFile, FileIconMem, Size, &dwWritten, 0);
CloseHandle(hFile);
VirtualFree(FileIconMem, 0, MEM_RELEASE);
// Lo recuperamos para ver que ha pasado
Icon->LoadFromFile("FilePath.ico");
Image1->Picture->Icon->Assign(Icon);
Ahora vamos a pasar la Imagen de un hIcon a un TIcon. Para ello utilizamos un TMemoryStream como paso intermedio. Creo que su uso es bastante intuitivo:
Código:
// Creo un MemoryStream
TMemoryStream* Stream = new TMemoryStream;
// Leo el mapa del hIcon deseado
int Size;
void* FileIconMem = hIconToMem(hIcon, 32, &Size);
// lo paso al MemoryStream
Stream->Write(FileIconMem, Size);
Stream->Position = 0;
// Libero el mapa
VirtualFree(FileIconMem, 0, MEM_RELEASE);
// Creo el icono con la imagen del hIcon
TIcon *Icon = new TIcon;
Stream->Position = 0; // Para situar le puntero de lectura al principio del Stream
Icon->LoadFromStream(Stream); // Cargo la imagen del Stream
El truco es un poco largo, pero seguro que a alguien le merece la pena usarlo.
Saludos.
|