Az ICO állomány szerkezetében a legelső helyen egy fejléc található, amely meghatározza az állományban található képek számát.
TIconHeader = record
idReserved: Word;
idType: Word;
idCount: Word;
end;
Az idReserved értéke mindig 0, az idType értéke pedig mindig 1. Az állományban található ikonok számát az idCount mezőből olvashatjuk ki.
A fejlécet a TIconEntry struktúra követi. A struktúrából annyi példány következik egymás után, ahány kép található az ICO állományban. Ez a struktúra tulajdonképpen a kép fejlécének is tekinthető. Mezőiből megtudhatjuk a kép méretét képpontokban és bájtokban, valamint magának a képadatnak az elhelyezkedését az állományon belül.
TIconEntry = record
bWidth: Byte;
bHeight: Byte;
bColorCount: Byte;
bReserved: Byte;
wPlanes: Word;
wBitCount: Word;
dwBytesInRes: Longint;
dwImageOffset: Longint;
end;
A kép szélességét és magasságát, képpontokban mérve, a bWidth és bHeight mezőkből tudhatjuk meg. Az bColorCount értéke adja meg a képen használt színek számát. A bReserved, wPlanes, és wBitCount mezők értéke nem használt. A feldolgozáshoz nélkülözhetetlen két adat, a kép mérete bájtokban, valamint a képet tartalmazó első bájt helye az állományon belül. Ezeket a dwBytesInRes és dwImageOffset mezőkből olvashatjuk ki.
A fenti információk elegendőek arra, hogy az állományon belül meghatározzuk a keresett kép helyét, és azt kimásoljuk onnan. Az állományba található képek DIB formátumban vannak tárolva.
A feldolgozás során létrehozunk egy ideiglenes ICO állományt, amelybe csak a keresett képet mentjük el. A kép feldolgozását ezután az operációs rendszerre bízzuk.
A feldolgozásához létrehozzuk a GetImageFromIcon függvényt, melynek paraméterként a betöltendő ICO állomány nevét és a keresett kép sorszámát kell megadnunk. A képek indexelését 0-val kezdjük.
procedure GetImageFromIcon(ICOFile: String; Index: Byte);
Az ICO fájl megnyitását és feldolgozását TMemoryStream típusú változók segítségével végezzük el.
A teljes állomány tartalmát a Stream változóba töltjük be.
A beolvasott állomány fejlécét az IconHeader változóba mentjük el.
Read(IconHeader,SizeOf(TIconHeader));
A fejléc után, a megadott sorszámú TIconEntry rekordot kell beolvasnunk.
if Index<IconHeader.idCount then begin
for i:=0 to Index do
Read(IconEntry,SizeOf(TIconEntry));
A kiolvasott struktúrából meg tudjuk határozni a kép helyét és méretét. A megtalált képet az ImageData változóba mentjük el.
Seek(IconEntry.dwImageOffset,soFromBeginning);
ImageData.SetSize(IconEntry.dwBytesInRes);
ImageData.CopyFrom(Stream,IconEntry.dwBytesInRes);
Létrehozunk egy temp.ico nevű állományt, amelybe elmentjük az ImageData változóban található képet. Az új állomány tartalmát a NewIcon (TMemoryStream) változóban állítjuk össze.
A létrejövő állományba először a fejlécet kell elmentenünk, melynek idCount mezőjét 1-re állítjuk be (csak egy képet helyezünk el benne).
A fejléc után az IconEntry tartalmát kell elmentenünk. Mentés előtt azonban módosítanunk kell a kép fizikai helyére vonatkozó adatot.
IconEntry.dwImageOffset:=SizeOf(TIconHeader)+SizeOf(TIconEntry);
A két fejlécet a Write metódus segítségével írhatjuk be a TMemoryStream-be.
NewIcon.Write(IconHeader,SizeOf(TIconHeader));
NewIcon.Write(IconEntry,SizeOf(TIconEntry));
Magát a képet a CopyFrom metódus segítségével másolhatjuk át az ImageData változóból.
NewIcon.CopyFrom(ImageData,IconEntry.dwBytesInRes);
Végül a NewIcon tartalmát elmentve létrejön egy új ICO állomány, amely már csak a keresett képet tartalmazza.
NewIcon.SaveToFile('temp.ico');
Ezt hagyományos módon fel tudjuk használni, például egy TImage komponens segítségével.