問題來源:
事實上,該圖標是一個多尺寸圖標文件並不在這種情況下,無所謂。圖標的位圖信息頭在內部以不同於應該的方式讀取。您的圖標是PNG格式的文件圖標,並且它們沒有位圖信息標題結構。之所以出現Out of system resources
例外,是因爲內部使用的程序期望圖標具有TBitmapInfoHeader
結構,然後嘗試根據此標頭信息創建臨時位圖。爲了您的圖標,它是這樣寫的:
如果你看一看上的標頭值越接近,你算算,該系統將嘗試創建它的尺寸就達到169478669個* 218103808個像素的位圖每像素21060 B,就需要有空閒內存至少778.5 EB (exabytes)
:-)什麼
解決方法:
這當然是不可能的(此時:-)和HA只是因爲PNG文件格式圖標沒有這個位圖標題,而是直接包含該位置上的PNG圖像。你可以做的解決方法是檢查圖像數據的前8個字節是否存在PNG signature
,它實際上檢查是否存在PNG圖像,如果是,則將其視爲PNG圖像,否則嘗試添加圖標以通常的方式通過TIcon
對象。
在下面的代碼中,ImageListAddIconEx
函數會迭代圖標文件中的所有圖標,並且當存在與處理圖像列表尺寸相匹配的圖標時。如果在數據偏移位置上存在PNG圖像,則處理首先檢查這8個字節,如果是,則將該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
過程就可以考慮到這一點。
東西可以讀:
多少其他圖像在圖像列表中添加該圖標之前? – 2013-03-02 02:14:15
@Ken,即使圖像列表爲空,也可以模擬這個。我想這是因爲添加的圖標是多尺寸的圖標。 – TLama 2013-03-02 02:19:43
@TLama - 確實。如果提取第一個圖像可以添加沒有問題。 – 2013-03-02 02:24:06