blob: 4523ccf3d9832735930acd9ba9fc3d677fa67f2b [file] [log] [blame]
/*
* File: ximaico.cpp
* Purpose: Platform Independent ICON Image Class Loader and Writer (MS version)
* 07/Aug/2001 Davide Pizzolato - www.xdp.it
* CxImage version 5.99c 17/Oct/2004
*/
#include "ximaico.h"
#if CXIMAGE_SUPPORT_ICO
////////////////////////////////////////////////////////////////////////////////
bool CxImageICO::Decode(CxFile *hFile)
{
if (hFile==NULL) return false;
DWORD off = hFile->Tell(); //<yuandi>
int page=info.nFrame; //internal icon structure indexes
// read the first part of the header
ICONHEADER icon_header;
hFile->Read(&icon_header,sizeof(ICONHEADER),1);
// check if it's an icon or a cursor
if ((icon_header.idReserved == 0) && ((icon_header.idType == 1)||(icon_header.idType == 2))) {
info.nNumFrames = icon_header.idCount;
// load the icon descriptions
ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header.idCount * sizeof(ICONDIRENTRY));
int c;
for (c = 0; c < icon_header.idCount; c++)
hFile->Read(icon_list + c, sizeof(ICONDIRENTRY), 1);
if ((page>=0)&&(page<icon_header.idCount)){
// get the bit count for the colors in the icon <CoreyRLucier>
BITMAPINFOHEADER bih;
hFile->Seek(off + icon_list[page].dwImageOffset, SEEK_SET);
hFile->Read(&bih,sizeof(BITMAPINFOHEADER),1);
c = bih.biBitCount;
// allocate memory for one icon
Create(icon_list[page].bWidth,icon_list[page].bHeight, c, CXIMAGE_FORMAT_ICO); //image creation
// read the palette
RGBQUAD pal[256];
hFile->Read(pal,head.biClrUsed*sizeof(RGBQUAD), 1);
SetPalette(pal,head.biClrUsed); //palette assign
//read the icon
if (c<=24){
hFile->Read(info.pImage, head.biSizeImage, 1);
} else { // 32 bit icon
BYTE* dst = info.pImage;
BYTE* buf=(BYTE*)malloc(4*head.biHeight*head.biWidth);
BYTE* src = buf;
hFile->Read(buf, 4*head.biHeight*head.biWidth, 1);
#if CXIMAGE_SUPPORT_ALPHA
if (!AlphaIsValid()) AlphaCreate();
#endif //CXIMAGE_SUPPORT_ALPHA
for (long y = 0; y < head.biHeight; y++) {
for(long x=0;x<head.biWidth;x++){
*dst++=src[0];
*dst++=src[1];
*dst++=src[2];
#if CXIMAGE_SUPPORT_ALPHA
AlphaSet(x,y,src[3]);
#endif //CXIMAGE_SUPPORT_ALPHA
src+=4;
}
}
free(buf);
}
// apply the AND and XOR masks
int maskwdt = ((head.biWidth+31) / 32) * 4; //line width of AND mask (always 1 Bpp)
int masksize = head.biHeight * maskwdt; //size of mask
BYTE *mask = (BYTE *)malloc(masksize);
if (hFile->Read(mask, masksize, 1)){
bool bGoodMask=false;
for (int im=0;im<masksize;im++){
if (mask[im]!=255){
bGoodMask=true;
break;
}
}
if (bGoodMask){
#if CXIMAGE_SUPPORT_ALPHA
bool bNeedAlpha = false;
if (!AlphaIsValid()){
AlphaCreate();
} else {
bNeedAlpha=true; //32bit icon
}
for (int y = 0; y < head.biHeight; y++) {
for (int x = 0; x < head.biWidth; x++) {
if (((mask[y*maskwdt+(x>>3)]>>(7-x%8))&0x01)){
AlphaSet(x,y,0);
bNeedAlpha=true;
}
}
}
if (!bNeedAlpha) AlphaDelete();
#endif //CXIMAGE_SUPPORT_ALPHA
if (c==24){ //check if there is only one transparent color
RGBQUAD cc,ct;
long* pcc = (long*)&cc;
long* pct = (long*)&ct;
int nTransColors=0;
for (int y = 0; y < head.biHeight; y++){
for (int x = 0; x < head.biWidth; x++){
if (((mask[y*maskwdt+(x>>3)] >> (7-x%8)) & 0x01)){
cc = GetPixelColor(x,y,false);
if (nTransColors==0){
nTransColors++;
ct = cc;
} else {
if (*pct!=*pcc){
nTransColors++;
}
}
}
}
}
if (nTransColors==1){
SetTransColor(ct);
SetTransIndex(0);
#if CXIMAGE_SUPPORT_ALPHA
AlphaDelete(); //because we have a unique transparent color in the image
#endif //CXIMAGE_SUPPORT_ALPHA
}
}
// <vho> - Transparency support w/o Alpha support
if (c <= 8){ // only for icons with less than 256 colors (XP icons need alpha).
// find a color index, which is not used in the image
// it is almost sure to find one, bcs. nobody uses all possible colors for an icon
BYTE colorsUsed[256];
memset(colorsUsed, 0, sizeof(colorsUsed));
for (int y = 0; y < head.biHeight; y++){
for (int x = 0; x < head.biWidth; x++){
colorsUsed[GetPixelIndex(x,y)] = 1;
}
}
int iTransIdx = -1;
for (int x = 0; x < (int)head.biClrUsed; x++){
if (colorsUsed[x] == 0){
iTransIdx = x; // this one is not in use. we may use it as transparent color
break;
}
}
// Go thru image and set unused color as transparent index if needed
if (iTransIdx >= 0){
bool bNeedTrans = false;
for (int y = 0; y < head.biHeight; y++){
for (int x = 0; x < head.biWidth; x++){
// AND mask (Each Byte represents 8 Pixels)
if (((mask[y*maskwdt+(x>>3)] >> (7-x%8)) & 0x01)){
// AND mask is set (!=0). This is a transparent part
SetPixelIndex(x, y, iTransIdx);
bNeedTrans = true;
}
}
}
// set transparent index if needed
if (bNeedTrans) SetTransIndex(iTransIdx);
#if CXIMAGE_SUPPORT_ALPHA
AlphaDelete(); //because we have a transparent color in the palette
#endif //CXIMAGE_SUPPORT_ALPHA
}
}
} else {
SetTransIndex(0); //empty mask, set black as transparent color
Negative();
}
}
free(mask);
free(icon_list);
// icon has been loaded successfully!
return true;
}
free(icon_list);
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
#if CXIMAGE_SUPPORT_ENCODE
////////////////////////////////////////////////////////////////////////////////
bool CxImageICO::Encode(CxFile * hFile)
{
if (EncodeSafeCheck(hFile)) return false;
//check format limits
if ((head.biWidth>255)||(head.biHeight>255)){
strcpy(info.szLastError,"Can't save this image as icon");
return false;
}
//prepare the palette struct
RGBQUAD* pal=GetPalette();
if (head.biBitCount<=8 && pal==NULL) return false;
int maskwdt=((head.biWidth+31)/32)*4; //mask line width
int masksize=head.biHeight * maskwdt; //size of mask
int bitcount=head.biBitCount;
int imagesize=head.biSizeImage;
#if CXIMAGE_SUPPORT_ALPHA
if (AlphaIsValid() && head.biClrUsed==0){
bitcount=32;
imagesize=4*head.biHeight*head.biWidth;
}
#endif
//fill the icon headers
ICONHEADER icon_header={0,1,1};
ICONDIRENTRY icon_list={(BYTE)head.biWidth,(BYTE)head.biHeight,(BYTE)head.biClrUsed ,0,0,(WORD)bitcount,
sizeof(BITMAPINFOHEADER)+head.biClrUsed*sizeof(RGBQUAD)+
imagesize+masksize,
sizeof(ICONHEADER)+sizeof(ICONDIRENTRY)};
BITMAPINFOHEADER bi={sizeof(BITMAPINFOHEADER),head.biWidth,2*head.biHeight,1,(WORD)bitcount,
0,imagesize,0,0,0,0};
hFile->Write(&icon_header,sizeof(ICONHEADER),1); //write the headers
hFile->Write(&icon_list,sizeof(ICONDIRENTRY),1);
hFile->Write(&bi,sizeof(BITMAPINFOHEADER),1);
if (pal) hFile->Write(pal,head.biClrUsed*sizeof(RGBQUAD),1); //write palette
#if CXIMAGE_SUPPORT_ALPHA
if (AlphaIsValid() && head.biClrUsed==0){
BYTE* src = info.pImage;
BYTE* buf=(BYTE*)malloc(imagesize);
BYTE* dst = buf;
for (long y = 0; y < head.biHeight; y++) {
for(long x=0;x<head.biWidth;x++){
*dst++=*src++;
*dst++=*src++;
*dst++=*src++;
*dst++=AlphaGet(x,y);
}
}
hFile->Write(buf,imagesize, 1);
free(buf);
} else {
hFile->Write(info.pImage,imagesize,1); //write image
}
#else
hFile->Write(info.pImage,imagesize,1); //write image
#endif
//save transparency mask
BYTE* mask=(BYTE*)calloc(masksize,1); //create empty AND/XOR masks
if (!mask) return false;
//prepare the variables to build the mask
BYTE* iDst;
int pos,i;
RGBQUAD c={0,0,0,0};
RGBQUAD ct = GetTransColor();
long* pc = (long*)&c;
long* pct= (long*)&ct;
bool bTransparent = info.nBkgndIndex != -1;
#if CXIMAGE_SUPPORT_ALPHA
bool bAlphaPaletteIsValid = AlphaPaletteIsValid();
bool bAlphaIsValid = AlphaIsValid();
#endif
//build the mask
for (int y = 0; y < head.biHeight; y++) {
for (int x = 0; x < head.biWidth; x++) {
i=0;
#if CXIMAGE_SUPPORT_ALPHA
if (bAlphaIsValid && AlphaGet(x,y)==0) i=1;
if (bAlphaPaletteIsValid && GetPixelColor(x,y).rgbReserved==0) i=1;
#endif
c=GetPixelColor(x,y,false);
if (bTransparent && *pc==*pct) i=1;
iDst = mask + y*maskwdt + (x>>3);
pos = 7-x%8;
*iDst &= ~(0x01<<pos);
*iDst |= ((i & 0x01)<<pos);
}
}
//write AND/XOR masks
hFile->Write(mask,masksize,1);
free(mask);
return true;
}
////////////////////////////////////////////////////////////////////////////////
#endif // CXIMAGE_SUPPORT_ENCODE
////////////////////////////////////////////////////////////////////////////////
#endif // CXIMAGE_SUPPORT_ICO