Ver Mensaje Individual
  #10  
Antiguo 04-01-2010
Avatar de escafandra
[escafandra] escafandra is offline
Miembro Premium
 
Registrado: nov 2007
Posts: 2.210
Reputación: 22
escafandra Tiene un aura espectacularescafandra Tiene un aura espectacular
Vista la demanda para guardar correctamente un Icono dado por su Handle (HICON) desde delphi, publico una implementación en delphi:

Estructuras necesarias:
Código Delphi [-]
// These next two structs represent how the icon information is stored
// in an ICO file.
{$ALIGN 1}
//{$ALIGN ON}
type 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}

La primera función auxiliar, NColors, consigue el número de colores según el valor de BitCountPerPixel. Observar que para valores de 24 y 32 el resultado es cero:

Código Delphi [-]
//---------------------------------------------------------------------------
// Convierte bitCount a número de colores
function NColors(bitCount: WORD): integer;
begin
  Result:= -1;
  if (bitCount = 1) or (bitCount = 4) or (bitCount = 8) then
    Result:= 1 shl bitCount
  else if (bitCount >= 24) then
    Result:= 0;
end;
Ahora la función hIconToMem que se encargará de conseguir una imagen en memoria de un fichero.ico a partir de un Handle HICON. El código está lo suficientemente comentado:

Código Delphi [-]
//---------------------------------------------------------------------------
// Devuelve un puntero con la imagen de archivo.ico para poder guardar en disco.
// Devuelve el tamaño de la memoria de archivo de un Icono en Size
// Es necesario liberar el puntero devuelto tras usarlo: usar VirtualFree
function hIconToMem(hIcon: Thandle; BitCountPerPixel: integer; var Size: integer): Pointer;
var
  hDC: Thandle;
  IconInfo: TICONINFO;
  bmpAND, bmpXOR: BITMAP;
  FileIconMem: Pointer;
  RawDataSizeAND, RawDataSizeXOR, RawDataSize, PalSize, AllSize, Width, Height: integer;
  IconDir: PICONDIR;
  bmiAND: BITMAPINFO;
  bmiICON: PBITMAPINFO;
  lpBitsXOR, lpBitsAND: Pointer;
  IconImage: PICONIMAGE;

begin
  // Localizo la información del icono y su tamaño en un fichero.ico
  hDC:= GetDC(0);    // ScreenDC
  ZeroMemory(@bmpAND, sizeof(BITMAP));
  ZeroMemory(@bmpXOR, sizeof(BITMAP));
  GetIconInfo(hIcon, IconInfo);
  GetObject(IconInfo.hbmMask, sizeof(BITMAP), @bmpAND);
  GetObject(IconInfo.hbmColor, sizeof(BITMAP), @bmpXOR);
  if(BitCountPerPixel=0) then BitCountPerPixel:= bmpXOR.bmPlanes*bmpXOR.bmBitsPixel;
  RawDataSizeAND:= ((((bmpAND.bmWidth*bmpAND.bmBitsPixel)+31) and not 31) shr 3)*bmpAND.bmHeight;
  RawDataSizeXOR:= ((((bmpXOR.bmWidth*BitCountPerPixel)+31) and not 31) shr 3)*bmpXOR.bmHeight;
  RawDataSize:= RawDataSizeAND + RawDataSizeXOR;
  PalSize:=0; if(BitCountPerPixel<=8) then PalSize:= (1 shl BitCountPerPixel) shl 2; // RGBQUAD 4
  AllSize:= sizeof(TICONDIR)+sizeof(BITMAPINFOHEADER)+PalSize+RawDataSizeAND+RawDataSizeXOR;
  Width  := bmpAND.bmWidth;
  Height := bmpAND.bmHeight;

  // Reservo memoria para el fichero
  FileIconMem:= VirtualAlloc(0, AllSize, MEM_COMMIT, PAGE_READWRITE);
  IconDir:=     PICONDIR(FileIconMem);

  // Obtengo los Bits de cada parte Mask y Color
  bmiICON:=   PBITMAPINFO(DWORD(FileIconMem) + sizeof(TICONDIR));
  lpBitsXOR:= PBITMAPINFO(DWORD(FileIconMem) + sizeof(TICONDIR) + sizeof(BITMAPINFOHEADER) + PalSize);
  lpBitsAND:= PBITMAPINFO(DWORD(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(TICONDIR);    // How many bytes in this resource?
  IconDir.idEntries[0].dwImageOffset:= sizeof(TICONDIR);   // Where in the file is this image?
  IconImage:= PICONIMAGE(bmiICON);
  ZeroMemory(IconImage, 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)
  CopyMemory(@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;

  // Salida
  Size:= AllSize;
  Result:= FileIconMem;
end;

Un ejemplo para guardar en un archivo nuestro icono dado por su Handle:

Código Delphi [-]
function HIconToFile(hIcon: THandle; FileName: String): Boolean;
var
  FileIconMem: Pointer;
  Size: integer;
  hFile: Cardinal;
begin
  Result:= false;
  FileIconMem:= hIconToMem(hIcon, 32, Size);
  hFile := CreateFile(PCHAR(FileName), GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_FLAG_WRITE_THROUGH, 0);
  if(_lwrite(hFile, FileIconMem, Size) = Size) then
      Result:= true;
  CloseHandle(hFile);
  VirtualFree(FileIconMem, 0, MEM_RELEASE);
end;

Más información aquí.

Saludos.

Última edición por escafandra fecha: 04-01-2010 a las 18:15:28.
Responder Con Cita