gpt4 book ai didi

delphi - 为什么加载PNG图像格式图标会导致 "Out of system resources"异常?

转载 作者:行者123 更新时间:2023-12-03 14:43:26 26 4
gpt4 key购买 nike

我有一个特定的图标文件,它由 PNG 压缩图像组成,当我尝试加载它并添加到 TImageList 时,出现 系统资源不足 异常被提出。

图标文件在这里:https://www.dropbox.com/s/toll6jhlwv3cpq0/icon.ico?m

以下代码适用于常见类型的图标,但不适用于 PNG 图像图标:

procedure TForm1.Button1Click(Sender: TObject);
var
Icon: TIcon;
begin
try
Icon := TIcon.Create;
Icon.LoadFromFile('icon.ico');
ImageList1.AddIcon(Icon);
Caption := IntToStr(ImageList1.Count);
finally
Icon.Free;
end;
end;

为什么 PNG 图像图标格式无法加载并出现系统资源不足异常?如何将这种图标添加到图像列表中?

最佳答案

问题来源:

事实上,图标是多尺寸图标文件在这种情况下并不重要。图标的位图信息 header 在内部以与应有的方式不同的方式读取。您的图标是 PNG 格式的文件图标,并且这些图标没有位图信息 header 结构。之所以出现 Out of system resources 异常,是因为内部使用的程序期望图标具有 TBitmapInfoHeader结构,然后尝试根据此 header 信息创建临时位图。对于您的图标,其读法如下:

enter image description here

如果您仔细查看标题值,您会发现系统将尝试创建一个大小为 169478669 * 218103808 像素、每像素 21060 B 的位图,至少需要 778.5 EB (exabytes)可用内存:-)

解决方法:

这当然是不可能的(此时:-),并且发生的原因只是因为 PNG 文件格式图标没有此位图 header ,而是在该位置直接包含 PNG 图像。解决此问题的方法是检查是否有 PNG signature在图像数据的前 8 个字节上,它实际上检查是否有 PNG 图像,如果有,则将其视为 PNG 图像,否则尝试通过 TIcon 对象以常见方式添加图标.

在下面的代码中,ImageListAddIconEx 函数会迭代图标文件中的所有图标,当有一个图标与图像列表尺寸匹配时,就会对其进行处理。处理首先检查这 8 个字节,如果数据偏移位置上有 PNG 图像,如果有,则将此 PNG 图像添加到图像列表中。如果没有,则通过 TIcon 对象以常见方式添加图标。如果成功,该函数返回添加的图标在图像列表中的索引,否则返回-1:

uses
PNGImage;

type
TIconDirEntry = packed record
bWidth: Byte; // image width, in pixels
bHeight: Byte; // image height, in pixels
bColorCount: Byte; // number of colors in the image (0 if >= 8bpp)
bReserved: Byte; // reserved (must be 0)
wPlanes: Word; // color planes
wBitCount: Word; // bits per pixel
dwBytesInRes: DWORD; // image data size
dwImageOffset: DWORD; // image data offset
end;

TIconDir = packed record
idReserved: Word; // reserved (must be 0)
idType: Word; // resource type (1 for icons)
idCount: Word; // image count
idEntries: array[0..255] of TIconDirEntry;
end;
PIconDir = ^TIconDir;

function ImageListAddIconEx(AImageList: TCustomImageList;
AIconStream: TMemoryStream): Integer;
var
I: Integer;
Data: PByte;
Icon: TIcon;
IconHeader: PIconDir;
Bitmap: TBitmap;
PNGImage: TPNGImage;
PNGStream: TMemoryStream;
const
PNGSignature: array[0..7] of Byte = ($89, $50, $4E, $47, $0D, $0A, $1A, $0A);
begin
// initialize result to -1
Result := -1;
// point to the icon header
IconHeader := AIconStream.Memory;
// iterate all the icons in the icon file
for I := 0 to IconHeader.idCount - 1 do
begin
// if the icon dimensions matches to the image list, then...
if (IconHeader.idEntries[I].bWidth = AImageList.Width) and
(IconHeader.idEntries[I].bHeight = AImageList.Height) then
begin
// point to the stream beginning
Data := AIconStream.Memory;
// point with the Data pointer to the current icon image data
Inc(Data, IconHeader.idEntries[I].dwImageOffset);
// check if the first 8 bytes are PNG image signature; if so, then...
if CompareMem(Data, @PNGSignature[0], 8) then
begin
Bitmap := TBitmap.Create;
try
PNGImage := TPNGImage.Create;
try
PNGStream := TMemoryStream.Create;
try
// set the icon stream position to the current icon data offset
AIconStream.Position := IconHeader.idEntries[I].dwImageOffset;
// copy the whole PNG image from icon data to a temporary stream
PNGStream.CopyFrom(AIconStream,
IconHeader.idEntries[I].dwBytesInRes);
// reset the temporary stream position to the beginning
PNGStream.Position := 0;
// load the temporary stream data to a temporary TPNGImage object
PNGImage.LoadFromStream(PNGStream);
finally
PNGStream.Free;
end;
// assign temporary TPNGImage object to a temporary TBitmap object
Bitmap.Assign(PNGImage);
finally
PNGImage.Free;
end;
// to properly add the bitmap to the image list set the AlphaFormat
// to afIgnored, see e.g. http://stackoverflow.com/a/4618630/960757
// if you don't have TBitmap.AlphaFormat property available, simply
// comment out the following line
Bitmap.AlphaFormat := afIgnored;
// and finally add the temporary TBitmap object to the image list
Result := AImageList.Add(Bitmap, nil);
finally
Bitmap.Free;
end;
end
// the icon is not PNG type icon, so load it to a TIcon object
else
begin
// reset the position of the input stream
AIconStream.Position := 0;
// load the icon and add it to the image list in a common way
Icon := TIcon.Create;
try
Icon.LoadFromStream(AIconStream);
Result := AImageList.AddIcon(Icon);
finally
Icon.Free;
end;
end;
// break the loop to exit the function
Break;
end;
end;
end;

以及用法:

procedure TForm1.Button1Click(Sender: TObject);
var
Index: Integer;
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile('d:\Icon.ico');
Index := ImageListAddIconEx(ImageList1, Stream);
if (Index <> -1) then
ImageList1.Draw(Canvas, 8, 8, Index);
finally
Stream.Free;
end;
end;

结论:

我想说,如果 Microsoft 推荐使用 PNG 图标格式(自 Windows Vista 起支持),那么更新 Graphics.pas 中的 ReadIcon 过程就可以了考虑到这一点。

需要阅读的内容:

关于delphi - 为什么加载PNG图像格式图标会导致 "Out of system resources"异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15169907/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com