2013-03-02 9 views
5

Ho un file di icona specifico, che è composto da immagini compresse PNG e quando provo a caricarlo e aggiungerlo a TImageList, viene sollevata l'eccezione Out of system resources.Perché il caricamento di un'icona di formato immagine PNG causa l'eccezione "Fuori dalle risorse di sistema"?

Il file icona è qui: https://www.dropbox.com/s/toll6jhlwv3cpq0/icon.ico?m

Ecco il codice, che funziona con tipo comune di icone, ma non riesce con PNG immagine icone:

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; 

Perché il formato PNG immagine dell'icona falliscono caricare con l'eccezione Out of system resources? Come aggiungere questo tipo di icona a un elenco di immagini?

+0

Quante altre immagini sono nell'elenco delle immagini prima di aggiungere questa icona? –

+1

@Ken, è possibile simularlo anche se l'elenco delle immagini è vuoto. Immagino sia perché l'icona che viene aggiunta è un'icona a più dimensioni. – TLama

+0

@TLama - Infatti. La prima immagine può essere aggiunta senza problemi se estratta. –

risposta

10

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:

enter image description here

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:

+0

C'è un modo per verificare se un TIcon è un multiicon? – Casady

+0

ExtractIconEx() è l'unica possibilità di ottenere l'icona piccola? Il problema è che, a differenza di questo esempio, il mio codice reale ha TMemoryStream dove si trovano i dati dell'icona, quindi non posso usare ExtractIconEx() senza salvare prima TMemoryStream, che sarebbe molto lento con migliaia di icone. – Casady

+0

Ho comunque provato il tuo codice, e ottengo Out of System Resources anche se Aggiungi IconSmall. Deve essere qualcos'altro che è problematico con questa icona. – Casady

Problemi correlati