加载多帧图标

6
有没有人知道一个可以读取多帧图标的类?在网上搜索并没有找到相关信息。
我尝试使用Alan Peter Stotz的IconTools 2.0,它可以正确地将图标加载到列表中,但8位和4位图标的位深度返回值为0。32位和24位图标帧的位深度返回值是正确的。
查看图标时,图标本身显示正确...只有提到的位深度是错误的。
编辑#2 根据TLama的评论,这里是一些未经测试的代码:
function NumberOfIcons ( AFileName: string ): integer;
var
  iNumberOfIcons: Integer;
begin

  iNumberOfIcons := ExtractIcon ( hInstance, PChar ( AFilename ), UINT ( -1 ) );
  Result := iNumberOfIcons;

end;

function ExtractAnIcon ( AFilename: string; AIndex: integer ): TBitmap;
var
  icoHandle: HIcon;
  iBitmap: TBitmap;
  iIcon: TIcon;
  iNumberOfIcons, i: Integer;
begin

  Result := nil;

  iBitmap := TBitMap.Create;

  iIcon := TIcon.Create;
  try

    // Get the number of Icons
    iNumberOfIcons := ExtractIcon ( hInstance, PChar ( AFilename ), UINT ( -1 ) );

    // Extract the icon frame
    icoHandle := ExtractIcon ( hInstance, PChar ( AFileName ), AIndex );
    iIcon.Handle := icoHandle;
    iBitmap.Width := iIcon.Width;
    iBitmap.Height := iIcon.Height;
    // Draw the icon on your bitmap
    DrawIcon ( iBitmap.Canvas.Handle, 0, 0, iIcon.Handle );    
    Result := iBitmap;

  finally
    iIcon.Free;
  end;

end;

function PixelFormatToBitDepth ( APixelFormat: TPixelFormat ): integer;
// Convert TPixelFormat to integer
begin

  Result := -1;
  case APixelFormat of
    pf32Bit:
      Result := 32;
    pf24bit:
      Result := 24;
    pf8bit:
      Result := 8;
    pf4Bit:
      Result := 4;
    pf1bit:
      Result := 1;
  end;

end;

我是否走在正确的轨道上?在我的测试中,我现在得到了1个图标,但是NumberOfIcons函数返回1吗?

编辑#3 根据帮助文件,“如果文件是.ico文件,则ExtractIcon的返回值为1。”那么有什么方法可以用来获取ico文件中的图标数量?


你是打算进一步编辑它们还是只想在程序中使用它们?如果是后者,那么将它们链接为资源并使用资源API获取正确版本的图标会更容易。 - David Heffernan
@TLama。这是一个位图编辑器http://www.omuller.com/。我发布的代码现在可以获取图标框架,但我无法获取.ico文件中的图标数量。我不需要资源文件,只需要.ico文件。使用ExtractIcon返回图标帧数有问题吗? - Bill
@user539484。代码正在测试中,我发布的代码是我遇到问题的代码... NumberOfIcons始终返回1而不是文件中图标的数量。 - Bill
1
我已经阅读了 Graphics.pas 中读取 .ico 文件的代码。那非常全面。而来自微软的主要文档来源在这里:http://msdn.microsoft.com/en-us/library/ms997538.aspx - David Heffernan
1
@user539484 是的,我知道那段代码是做什么的。但它提供了一些有用的思路,可以实现所需的功能。因此,提供了实际文档的链接。阅读 Graphics.pas 中的 Delphi 代码可能会更容易阅读抽象的 MS 文档,反之亦然。这肯定是我的经验。 - David Heffernan
显示剩余7条评论
1个回答

5

以下是一个小代码示例:

uses ShellApi;

type
  TICONDIRENTRY = packed record
    bWidth: Byte;          // Width, in pixels, of the image
    bHeight: Byte;         // Height, in pixels, of the image
    bColorCount: Byte;     // Number of colors in image (0 if >=8bpp)
    bReserved: Byte;       // Reserved ( must be 0)
    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;

  TICONDIR = packed record
    idReserved: Word; // Reserved (must be 0)
    idType: Word;     // Resource Type (1 for icons)
    idCount: Word;    // How many images?
    idEntries: array [0..255] of TICONDIRENTRY;
  end;
  PICONDIR=^TICONDIR;

function GetIconsCount(const FileName: string): Word;
var
  Stream: TMemoryStream;
  IconDir: PICONDIR;
begin
  Result := 0;
  if ExtractIcon(hInstance, PChar(FileName), UINT(-1)) <> 0 then
  try
    Stream := TMemoryStream.Create;
    try
      Stream.LoadFromFile(FileName);
      IconDir := Stream.Memory;
      if IconDir.idType = 1 then
        Result := IconDir.idCount;
    finally
      Stream.Free;
    end;
  except
    // do not raise exceptions
  end;
end;

function ExtractIcons(const FileName: string; IconList: TList): Boolean;
var
  Stream: TMemoryStream;
  NewIconStream: TMemoryStream;
  IconDir: PICONDIR;
  NewIconDir: PICONDIR;
  Icon: TIcon;
  I: Integer;
begin
  Result := False;
  if ExtractIcon(hInstance, PChar(FileName), UINT(-1)) <> 0 then
  try
    Stream := TMemoryStream.Create;
    try
      Stream.LoadFromFile(FileName);
      IconDir := Stream.Memory;
      for I := 0 to IconDir.idCount-1 do
      begin
        NewIconStream := TMemoryStream.Create;
        try
          NewIconStream.Size := SizeOf(Word) * 3 + SizeOf(TICONDIRENTRY);
          NewIconStream.Position:= SizeOf(Word) * 3 + SizeOf(TICONDIRENTRY);

          NewIconDir := NewIconStream.memory;
          NewIconDir.idCount := 1;
          NewIconDir.idType := IconDir.idType;
          NewIconDir.idReserved := IconDir.idReserved;
          NewIconDir.idEntries[0] := IconDir.idEntries[I];
          NewIconDir.idEntries[0].dwImageOffset := NewIconStream.Size;

          Stream.Position := IconDir.idEntries[I].dwImageOffset;
          NewIconStream.CopyFrom(Stream, IconDir.idEntries[I].dwBytesInRes);
          NewIconStream.Position := 0;
          Icon := TIcon.Create;
          Icon.LoadFromStream(NewIconStream);
          IconList.Add(Icon);
        finally
          NewIconStream.Free;
        end;
        IconList.Add(Icon);
      end;
      Result := True;
    finally
      Stream.Free;
    end;
  except
    // do not raise exceptions
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  FileName: string;
  Icon: TIcon;
  List: TList;
  I: Integer;
begin
  FileName := 'c:\myicon.ico';
  List := TList.Create;
  try
    if ExtractIcons(FileName, List) then
    for I := 0 to List.Count - 1 do
    begin
      Icon := TIcon(List.Items[I]);
      DrawIcon(Form1.Canvas.Handle, 10, I * 40, Icon.Handle);
      Icon.Free;
    end;
  finally
    List.Free;
  end;
end;

2
@Kobik......谢谢你......我似乎也在http://www.tkweb.eu/en/delphicomp/kicon.html上取得了一些成功。 - Bill
2
@Bill,顺便说一下,KIcon也使用了上述相同的方法,即TKIcon.LoadFromStream->FIconCount := IH.idCount; - kobik
1
嘿,有趣,从未想过在描述的情况下可以使用ExtractIcon进行处理。 - OnTheFly

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接