This post is related to one simple task – decode ICO file format from C++ manually (I needed this for one of my projects where i wanted to display fav icons related to web sites and decoder has to be part of my cross-platform framework). As a result you can download small c++ code below (ico.cpp). The description of ICO format you can find here – http://www.daubnet.com/en/file-format-ico. Image data is stored uncompressed so we don’t need to implement some smart decompression algorithms – just read some headers, get data and apply bit mask to fill alpha channel.
As solution i wanted a function like this:
bool IcoDecoder::decode(unsigned char* buffer,///< input buffer data
int size,///< size of buffer
unsigned int& width,///< output – width
unsigned int& height,///< output – height
std::vector<unsigned char>&image///< output – image data
)
and this function should extract the largest possible image from multiresolution icon.
We need some header structs (i found them somewhere over internet). I skip them – you can find everything in source code below. As first step we check is it really ICO header and get number of images inside:
LPICONDIR icoDir = (LPICONDIR)buffer;
int iconsCount = icoDir->idCount;
if (icoDir->idReserved!=0) return false;
if (icoDir->idType!=1) return false;
if (iconsCount==0) return false;
if (iconsCount>20) return false;
Get offset of the largest icon within file:
unsigned char* cursor = buffer;
cursor += 6;
ICONDIRENTRY* dirEntry=(ICONDIRENTRY*)(cursor);
int maxSize=0;
int offset=0;
for(int i=0; i<iconsCount; i++)
{
int w=dirEntry->bWidth;
int h=dirEntry->bHeight;
int colorCount=dirEntry->bColorCount;
int bitCount=dirEntry->wBitCount;
if(w*h>maxSize)// we choose icon with max resolution
{
width=w;
height=h;
offset=dirEntry->dwImageOffset;
maxSize = w * h;
}
dirEntry++;
}
if (offset==0) return false;
Get image bit count and bit mask existance flag
cursor=buffer;
cursor+=offset;
ICONIMAGE* icon = (ICONIMAGE*)(cursor);
int realBitsCount=(int)icon->icHeader.biBitCount;
bool hasAndMask=(height!=icon->icHeader.biHeight);
cursor+=40;
int numBytes=width*height*4;
In case of 32 bit data (realBitsCount == 32) we just copy data to output with optional vertical flip:
int shift;
int shift2;
for (int x=0; x<width; x++)
for (int y=0; y<height; y++)
{
shift=4*(x+y*width);
shift2=4*(x+(height–y–1)*width);
image[shift]=cursor[shift2+2];
image[shift+1]=cursor[shift2+1];
image[shift+2]=cursor[shift2];
image[shift+3]=cursor[shift2+3];
}
If color count is limited to 256 or 16 we take colors from color table. Here is the part for 256 color:
unsigned char* colors=(unsigned char*)cursor;
cursor+=256*4;
int shift;
int shift2;
int index;
for (int x=0; x<width; x++)
for (int y=0; y<height; y++)
{
shift=4*(x+y*width);
shift2=(x+(height–y–1)*width);
index=4*cursor[shift2];
image[shift]=colors[index+2];
image[shift+1]=colors[index+1];
image[shift+2]=colors[index];
image[shift+3]=255;
}
And final step is optional bit mask (this is not applicable only to icons with 32bit color).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
int shift; int shift2; unsigned char bit; int mask; int boundary = width * realBitsCount; //!!! 32 bit boundary (http://www.daubnet.com/en/file-format-ico) while (boundary % 32 != 0) boundary++; cursor += boundary * height / 8; boundary = width; while (boundary % 32 != 0) boundary++; for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { shift = 4 * (x + y * width) + 3; bit = 7 - (x % 8); shift2 = (x + (height - y - 1) * boundary) / 8; mask = (0x01 & ((unsigned char)cursor[shift2] >> bit)); image[shift] *= 1 - mask; } |
And that all. See complete file to enjoy how compact the decoding is. Feel free to use it. I have tested this on a lot of icons but i cannot guarantee anything. Also i skipped some range checks and stuff to make module more robust – but you are free to modify it as you like.
UPDATE 14.08.2014: Code was updated to rev2 to support 1-bit encoding. Also contains fix for bit mask application.
SOURCE CODE: download ico.cpp (last version – rev2 15.08.2014)
PS. Please post in comments info about any icons which fail to decode using code above.