2008. március 3., hétfő

Read and write icon files


Problem/Question/Abstract:

How to read and write icon files

Answer:

{ icon. pas}

unit Icons;

interface

uses
  windows, sysutils;

type
  PByte = ^Byte;
  PBitmapInfo = ^BitmapInfo;

  {These first two structs represent how the icon information is stored when it is
  bound into a EXE or DLL file. Structure members are WORD aligned and the last
  member of the structure is the ID instead of the imageoffset.}

type
  PMEMICONDIRENTRY = ^TMEMICONDIRENTRY;
  TMEMICONDIRENTRY = packed 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?}
    nID: WORD; {The ID}
  end;

type
  PMEMICONDIR = ^TMEMICONDIR;
  TMEMICONDIR = packed record
    idReserved: WORD; {Reserved}
    idType: WORD; {Resource type (1 for icons) }
    idCount: WORD; {How many images?}
    idEntries: array[0..10] of TMEMICONDIRENTRY; {The entries for each image}
  end;

  {These next two structs represent how the icon information is stored in an ICO file.}

type
  PICONDIRENTRY = ^TICONDIRENTRY;
  TICONDIRENTRY = packed 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;

type
  PICONDIR = ^TICONDIR;
  TICONDIR = packed 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;

  {The following two structs are for the use of this program in manipulating icons.
  They are more closely tied to the operation of this program than the structures
  listed above. One of the main differences is that they provide a pointer to the
  DIB information of the masks.}

type
  PICONIMAGE = ^TICONIMAGE;
  TICONIMAGE = packed record
    Width, Height, Colors: UINT; {Width, Height and bpp}
    lpBits: Pointer; {ptr to DIB bits}
    dwNumBytes: DWORD; {How many bytes?}
    pBmpInfo: PBitmapInfo;
  end;

  {
    TICONIMAGE = packed record
      Width, Height, Colors: UINT;  {Width, Height and bpp}
  lpBits: pointer; {ptr to DIB bits}
  dwNumBytes: DWORD; {How many bytes?}
  lpbi: PBITMAPINFO; {ptr to header}
  lpXOR: LPBYTE; {ptr to XOR image bits}
  lpAND: LPBYTE; {ptr to AND image bits}
end;
}

type
  PICONRESOURCE = ^TICONRESOURCE;
  TICONRESOURCE = packed record
    nNumImages: UINT; {How many images?}
    IconImages: array[0..10] of TICONIMAGE; {Image entries}
  end;

  {
    TICONRESOURCE = packed record
      bHasChanged: BOOL;  {Has image changed?}
  szOriginalICOFileName: array[0..MAX_PATH] of Char; {Original name}
  szOriginalDLLFileName: array[0..MAX_PATH] of Char; {Original name}
  nNumImages: UINT; {How many images?}
  IconImages: array[0..0] of ICONIMAGE; {Image entries}
end;
}

type
  TPageInfo = packed record
    Width: Byte;
    Height: Byte;
    ColorQuantity: Integer;
    Reserved: DWORD;
    PageSize: DWORD;
    PageOffSet: DWORD;
  end;

type
  TPageDataHeader = packed record
    PageHeadSize: DWORD;
    XSize: DWORD;
    YSize: DWORD;
    SpeDataPerPixSize: Integer;
    ColorDataPerPixSize: Integer;
    Reserved: DWORD;
    DataAreaSize: DWORD;
    ReservedArray: array[0..15] of Char;
  end;

type
  TIcoFileHeader = packed record
    FileFlag: array[0..3] of Byte;
    PageQuartity: Integer;
    PageInfo: TPageInfo;
  end;

  {function WriteIconToFile(Bitmap: hBitmap; Icon: hIcon; szFileName: string): Boolean; overload;}
function ExtractIconFromFile(ResFileName: string; IcoFileName: string; nIndex:
  string): Boolean;
function WriteIconResourceToFile(hFile: hwnd; lpIR: PICONRESOURCE): Boolean;

implementation

function WriteICOHeader(hFile: HWND; nNumEntries: UINT): Boolean;
type
  TFIcoHeader = record
    wReserved: WORD;
    wType: WORD;
    wNumEntries: WORD;
  end;
var
  IcoHeader: TFIcoHeader;
  {Output: WORD;}
  dwBytesWritten: DWORD;
begin
  Result := False;
  IcoHeader.wReserved := 0;
  IcoHeader.wType := 1;
  IcoHeader.wNumEntries := WORD(nNumEntries);
  if not WriteFile(hFile, IcoHeader, SizeOf(IcoHeader), dwBytesWritten, nil) then
  begin
    MessageBox(0, pchar(SysErrorMessage(GetLastError)), 'info', MB_OK);
    exit;
  end;
  if dwBytesWritten <> SizeOf(IcoHeader) then
    exit;
  {
  Output := 0;
  {Write 'reserved' WORD}
  if not WriteFile(hFile, Output, SizeOf(WORD), dwBytesWritten, nil) then
    exit;
  {Did we write a WORD?}
  if dwBytesWritten <> SizeOf(WORD) then
    exit;
  {Write 'type' WORD (1) }
  Output := 1;
  if not WriteFile(hFile, Output, SizeOf(WORD), dwBytesWritten, nil) then
    exit;
  if dwBytesWritten <> SizeOf(WORD) then
    exit;
  {Write Number of Entries}
  Output := WORD(nNumEntries);
  if not WriteFile(hFile, Output, SizeOf(WORD), dwBytesWritten, nil) then
    exit;
  if dwBytesWritten <> SizeOf(WORD) then
    exit;
  }
    Result := True;
end;

function CalculateImageOffset(lpIR: PICONRESOURCE; nIndex: UINT): DWORD;
var
  dwSize: DWORD;
  i: Integer;
begin
  {Calculate the ICO header size}
  dwSize := 3 * sizeof(WORD);
  {Add the ICONDIRENTRY's}
  inc(dwSize, lpIR.nNumImages * sizeof(TICONDIRENTRY));
  {Add the sizes of the previous images}
  for i := 0 to nIndex - 1 do
    inc(dwSize, lpIR.IconImages[i].dwNumBytes);
  {We're there - return the number}
  Result := dwSize;
end;

function WriteIconResourceToFile(hFile: hwnd; lpIR: PICONRESOURCE): Boolean;
var
  i: UINT;
  dwBytesWritten: DWORD;
  ide: TICONDIRENTRY;
  dwTemp: DWORD;
begin
  {Open the file}
  Result := False;
  {Write the ICONDIRENTRY's}
  for i := 0 to lpIR^.nNumImages - 1 do
  begin
    {Convert internal format to ICONDIRENTRY}
    ide.bWidth := lpIR^.IconImages[i].Width;
    ide.bHeight := lpIR^.IconImages[i].Height;
    ide.bReserved := 0;
    ide.wPlanes := lpIR^.IconImages[i].pBmpInfo.bmiHeader.biPlanes;
    ide.wBitCount := lpIR^.IconImages[i].pBmpInfo.bmiHeader.biBitCount;
    if ide.wPlanes * ide.wBitCount >= 8 then
      ide.bColorCount := 0
    else
      ide.bColorCount := 1 shl (ide.wPlanes * ide.wBitCount);
    ide.dwBytesInRes := lpIR^.IconImages[i].dwNumBytes;
    ide.dwImageOffset := CalculateImageOffset(lpIR, i);
    {Write the ICONDIRENTRY out to disk}
    if not WriteFile(hFile, ide, sizeof(TICONDIRENTRY), dwBytesWritten, nil) then
      exit;
    {Did we write a full ICONDIRENTRY ?}
    if dwBytesWritten <> sizeof(TICONDIRENTRY) then
      exit;
  end;
  {Write the image bits for each image}
  for i := 0 to lpIR^.nNumImages - 1 do
  begin
    dwTemp := lpIR^.IconImages[i].pBmpInfo^.bmiHeader.biSizeImage;
    {Set the sizeimage member to zero}
    lpIR^.IconImages[i].pBmpInfo^.bmiHeader.biSizeImage := 0;
    {Write the image bits to file}
    if not WriteFile(hFile, lpIR^.IconImages[i].lpBits^,
      lpIR^.IconImages[i].dwNumBytes,
      dwBytesWritten, nil) then
      exit;
    if dwBytesWritten <> lpIR^.IconImages[i].dwNumBytes then
      exit;
    {Set it back}
    lpIR^.IconImages[i].pBmpInfo^.bmiHeader.biSizeImage := dwTemp;
  end;
  Result := True;
end;

function AWriteIconToFile(bitmap: hBitmap; Icon: hIcon; szFileName: string): Boolean;
var
  fh: file of Byte;
  IconInfo: _ICONINFO;
  PageInfo: TPageInfo;
  PageDataHeader: TPageDataHeader;
  IcoFileHeader: TIcoFileHeader;
  BitsInfo: tagBITMAPINFO;
  p: Pointer;
  PageDataSize: Integer;
begin
  Result := False;
  GetIconInfo(Icon, IconInfo);
  AssignFile(fh, szFileName);
  FileMode := 1;
  Reset(fh);
  GetDIBits(0, Icon, 0, 32, nil, BitsInfo, DIB_PAL_COLORS);
  GetDIBits(0, Icon, 0, 32, p, BitsInfo, DIB_PAL_COLORS);
  PageDataSize := SizeOf(PageDataHeader) + BitsInfo.bmiHeader.biBitCount;
  PageInfo.Width := 32;
  PageInfo.Height := 32;
  PageInfo.ColorQuantity := 65535;
  Pageinfo.Reserved := 0;
  PageInfo.PageSize := PageDataSize;
  PageInfo.PageOffSet := SizeOf(IcoFileHeader);
  IcoFileHeader.FileFlag[0] := 0;
  IcoFileHeader.FileFlag[1] := 0;
  IcoFileHeader.FileFlag[2] := 1;
  IcoFileHeader.FileFlag[3] := 0;
  IcoFileHeader.PageQuartity := 1;
  IcoFileHeader.PageInfo := PageInfo;
  FillChar(PageDataHeader, SizeOf(PageDataHeader), 0);
  PageDataHeader.XSize := 32;
  PageDataHeader.YSize := 32;
  PageDataHeader.SpeDataPerPixSize := 0;
  PageDataHeader.ColorDataPerPixSize := 32;
  PageDataHeader.PageHeadSize := SizeOf(PageDataHeader);
  PageDataHeader.Reserved := 0;
  PageDataHeader.DataAreaSize := BitsInfo.bmiHeader.biBitCount;
  BlockWrite(fh, IcoFileHeader, SizeOf(IcoFileHeader));
  BlockWrite(fh, PageDataHeader, SizeOf(PageDataHeader));
  BlockWrite(fh, p, BitsInfo.bmiHeader.biBitCount);
  CloseFile(fh);
end;

function AdjustIconImagePointers(lpImage: PICONIMAGE): Bool;
begin
  if lpImage = nil then
  begin
    Result := False;
    exit;
  end;
  lpImage.pBmpInfo := PBitMapInfo(lpImage^.lpBits);
  lpImage.Width := lpImage^.pBmpInfo^.bmiHeader.biWidth;
  lpImage.Height := (lpImage^.pBmpInfo^.bmiHeader.biHeight) div 2;
  lpImage.Colors := lpImage^.pBmpInfo^.bmiHeader.biPlanes *
    lpImage^.pBmpInfo^.bmiHeader.biBitCount;
  Result := true;
end;

function ExtractIconFromFile(ResFileName: string; IcoFileName: string; nIndex:
  string): Boolean;
var
  h: HMODULE;
  lpMemIcon: PMEMICONDIR;
  lpIR: TICONRESOURCE;
  src: HRSRC;
  Global: HGLOBAL;
  i: Integer;
  hFile: HWND;
begin
  Result := False;
  hFile := CreateFile(pchar(IcoFileName), GENERIC_WRITE, 0, nil, CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL, 0);
  if hFile = INVALID_HANDLE_VALUE then
    exit; {Error Create File}
  h := LoadLibraryEx(pchar(ResFileName), 0, LOAD_LIBRARY_AS_DATAFILE);
  if h = 0 then
    exit;
  try
    src := FindResource(h, pchar(nIndex), RT_GROUP_ICON);
    if src = 0 then
      Src := FindResource(h, Pointer(StrToInt(nIndex)), RT_GROUP_ICON);
    if src <> 0 then
    begin
      Global := LoadResource(h, src);
      if Global <> 0 then
      begin
        lpMemIcon := LockResource(Global);
        if Global <> 0 then
        begin
          {lpIR := @IR;}
          try
            lpIR.nNumImages := lpMemIcon.idCount;
            {Write the header}
            for i := 0 to lpMemIcon^.idCount - 1 do
            begin
              src := FindResource(h, MakeIntResource(lpMemIcon^.idEntries[i].nID),
                RT_ICON);
              if src <> 0 then
              begin
                Global := LoadResource(h, src);
                if Global <> 0 then
                begin
                  lpIR.IconImages[i].dwNumBytes := SizeofResource(h, src);
                  GetMem(lpIR.IconImages[i].lpBits, lpIR.IconImages[i].dwNumBytes);
                  CopyMemory(lpIR.IconImages[i].lpBits, LockResource(Global),
                    lpIR.IconImages[i].dwNumBytes);
                  if not AdjustIconImagePointers(@(lpIR.IconImages[i])) then
                    exit;
                end;
              end;
            end;
            if WriteICOHeader(hFile, lpIR.nNumImages) then {No Error Write File}
              if WriteIconResourceToFile(hFile, @lpIR) then
                Result := True;
          finally
            for i := 0 to lpIR.nNumImages - 1 do
              if assigned(lpIR.IconImages[i].lpBits) then
                FreeMem(lpIR.IconImages[i].lpBits);
          end;
        end;
      end;
    end;
  finally
    FreeLibrary(h);
  end;
  CloseHandle(hFile);
end;

end.

Nincsenek megjegyzések:

Megjegyzés küldése