Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Trucos (https://www.clubdelphi.com/foros/forumdisplay.php?f=52)
-   -   Pasar un HICON a un TIcon o a un archivo (https://www.clubdelphi.com/foros/showthread.php?t=80895)

escafandra 29-12-2008 17: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.


La franja horaria es GMT +2. Ahora son las 20:28:38.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi