fonte Problema:
Il fatto, che l'icona è un file di icona a più dimensioni non importa in questo caso. L'intestazione delle informazioni bitmap dell'icona viene letta internamente in modo diverso da come dovrebbe essere. L'icona è l'icona del file di formato PNG e quelli non hanno una struttura di intestazione di informazioni bitmap. Il motivo per cui si sta verificando l'eccezione Out of system resources
è perché le procedure utilizzate internamente si aspettano dall'icona di avere una struttura TBitmapInfoHeader
e quindi tenta di creare una bitmap temporanea basata su queste informazioni di intestazione. Per la vostra icona è stato letto in questo modo:
Se si dà un'occhiata più da vicino sui valori di intestazione, si calcola che il sistema avrebbe cercato di creare una bitmap che sarebbe in formato 169478669 * 218103808 pixel a 21060 B per pixel, che cosa avrebbe bisogno di avere almeno 778.5 EB (exabytes)
di memoria libera :-)
Soluzione:
questo è ovviamente impossibile (in questo momento :-) e ha ppens solo perché le icone del formato del file PNG non hanno questa intestazione bitmap, ma invece contengono direttamente un'immagine PNG su quella posizione. Quello che puoi fare per ovviare a questo è controllare, se c'è il PNG signature
sui primi 8 byte dei dati dell'immagine, che in realtà controlla se c'è un'immagine PNG e se è così, trattala come un'immagine PNG, altrimenti prova ad aggiungere l'icona in modo comune attraverso l'oggetto TIcon
.
Nel codice seguente, la funzione ImageListAddIconEx
itera tutte le icone nel file di icona e quando ce n'è una che corrisponde alle dimensioni dell'elenco di immagini viene elaborata. L'elaborazione prima controlla quegli 8 byte se c'è un'immagine PNG sulla posizione di spostamento dei dati e, in caso affermativo, aggiunge questa immagine PNG all'elenco delle immagini. In caso contrario, l'icona viene aggiunta in modo comune tramite l'oggetto TIcon
. Questa funzione restituisce indice dell'icona aggiunto nell'elenco delle immagini, se riuscirà, -1 altrimenti:
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;
E l'utilizzo:
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;
Conclusione:
mi piacerebbe Se Microsoft consiglia di utilizzare il formato dell'icona PNG (supportato da Windows Vista), sarebbe opportuno aggiornare la procedura ReadIcon
in Graphics.pas
per tenerne conto.
qualcosa da leggere:
Quante altre immagini sono nell'elenco delle immagini prima di aggiungere questa icona? –
@Ken, è possibile simularlo anche se l'elenco delle immagini è vuoto. Immagino sia perché l'icona che viene aggiunta è un'icona a più dimensioni. – TLama
@TLama - Infatti. La prima immagine può essere aggiunta senza problemi se estratta. –