nguồn Vấn đề:
Thực tế, mà biểu tượng là một tập tin biểu tượng đa kích thước không quan trọng trong trường hợp này. Tiêu đề thông tin bitmap của biểu tượng được đọc nội bộ theo một cách khác. Biểu tượng của bạn là biểu tượng tệp định dạng PNG và biểu tượng không có cấu trúc tiêu đề thông tin bitmap. Lý do, tại sao bạn nhận được ngoại lệ Out of system resources
, là do các thủ tục được sử dụng nội bộ được mong đợi từ biểu tượng có cấu trúc TBitmapInfoHeader
và sau đó cố gắng tạo một bitmap tạm thời dựa trên thông tin tiêu đề này. Đối với biểu tượng của bạn được đọc như thế này:

Nếu bạn xem xét kỹ hơn về các giá trị tiêu đề, bạn tính toán rằng hệ thống sẽ cố gắng để tạo ra một bitmap đó sẽ là kích thước 169.478.669 * 218.103.808 pixel tại 21.060 B mỗi pixel, điều gì sẽ cần phải có ít nhất 778.5 EB (exabytes)
bộ nhớ miễn phí :-)
Cách giải quyết:
Đó là tất nhiên không thể (tại thời điểm này :-) và ha trang chỉ vì các biểu tượng định dạng tệp PNG không có tiêu đề bitmap này, nhưng thay vào đó chứa trực tiếp hình ảnh PNG trên vị trí đó. Những gì bạn có thể làm để giải quyết vấn đề này là kiểm tra, nếu có PNG signature
trên 8 byte đầu tiên của dữ liệu hình ảnh, thực tế sẽ kiểm tra xem có hình ảnh PNG hay không và xử lý dưới dạng hình ảnh PNG, nếu không hãy thử thêm biểu tượng theo cách thông thường thông qua đối tượng TIcon
.
Trong mã sau, hàm ImageListAddIconEx
lặp lại tất cả các biểu tượng trong tệp biểu tượng và khi có một biểu tượng khớp với thứ nguyên danh sách hình ảnh được xử lý. Trước tiên, quá trình xử lý sẽ kiểm tra 8 byte đó nếu có hình ảnh PNG trên vị trí bù đắp dữ liệu và nếu có, nó sẽ thêm hình ảnh PNG này vào danh sách hình ảnh. Nếu không, biểu tượng sẽ được thêm theo cách thông thường thông qua đối tượng TIcon
. Hàm này trả về chỉ số của các biểu tượng được thêm vào trong danh sách hình ảnh nếu thành công, -1 khác:
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;
Và việc sử dụng:
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;
Kết luận:
Tôi muốn nếu Microsoft khuyến cáo định dạng biểu tượng PNG để sử dụng (được hỗ trợ từ Windows Vista), bạn có thể cập nhật thủ tục ReadIcon
trong Graphics.pas
để tính đến điều này.
cái gì đó để đọc:
Có bao nhiêu hình ảnh khác nằm trong danh sách hình ảnh trước khi thêm biểu tượng này? –
@Ken, bạn có thể mô phỏng điều này ngay cả khi danh sách hình ảnh trống. Tôi đoán đó là vì biểu tượng được thêm vào là biểu tượng nhiều kích thước. – TLama
@TLama - Thật vậy. Hình ảnh đầu tiên có thể được thêm vào mà không có vấn đề gì nếu được trích xuất. –