3 * Purpose: Platform Independent ICON Image Class Loader and Writer (MS version)
\r
4 * 07/Aug/2001 Davide Pizzolato - www.xdp.it
\r
5 * CxImage version 6.0.0 02/Feb/2008
\r
10 #if CXIMAGE_SUPPORT_ICO
\r
12 ////////////////////////////////////////////////////////////////////////////////
\r
13 #if CXIMAGE_SUPPORT_DECODE
\r
14 ////////////////////////////////////////////////////////////////////////////////
\r
15 bool CxImageICO::Decode(CxFile *hFile)
\r
17 if (hFile==NULL) return false;
\r
19 DWORD off = hFile->Tell(); //<yuandi>
\r
20 int page=info.nFrame; //internal icon structure indexes
\r
22 // read the first part of the header
\r
23 ICONHEADER icon_header;
\r
24 hFile->Read(&icon_header,sizeof(ICONHEADER),1);
\r
26 icon_header.idType = ntohs(icon_header.idType);
\r
27 icon_header.idCount = ntohs(icon_header.idCount);
\r
29 // check if it's an icon or a cursor
\r
30 if ((icon_header.idReserved == 0) && ((icon_header.idType == 1)||(icon_header.idType == 2))) {
\r
32 info.nNumFrames = icon_header.idCount;
\r
34 // load the icon descriptions
\r
35 ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header.idCount * sizeof(ICONDIRENTRY));
\r
37 for (c = 0; c < icon_header.idCount; c++) {
\r
38 hFile->Read(icon_list + c, sizeof(ICONDIRENTRY), 1);
\r
40 icon_list[c].wPlanes = ntohs(icon_list[c].wPlanes);
\r
41 icon_list[c].wBitCount = ntohs(icon_list[c].wBitCount);
\r
42 icon_list[c].dwBytesInRes = ntohl(icon_list[c].dwBytesInRes);
\r
43 icon_list[c].dwImageOffset = ntohl(icon_list[c].dwImageOffset);
\r
46 if ((page>=0)&&(page<icon_header.idCount)){
\r
48 if (info.nEscape == -1) {
\r
49 // Return output dimensions only
\r
50 head.biWidth = icon_list[page].bWidth;
\r
51 head.biHeight = icon_list[page].bHeight;
\r
52 #if CXIMAGE_SUPPORT_PNG
\r
53 if (head.biWidth==0 && head.biHeight==0)
\r
54 { // Vista icon support
\r
55 hFile->Seek(off + icon_list[page].dwImageOffset, SEEK_SET);
\r
58 if (png.Decode(hFile,CXIMAGE_FORMAT_PNG)){
\r
60 info.nNumFrames = icon_header.idCount;
\r
63 #endif //CXIMAGE_SUPPORT_PNG
\r
65 info.dwType = CXIMAGE_FORMAT_ICO;
\r
69 // get the bit count for the colors in the icon <CoreyRLucier>
\r
70 BITMAPINFOHEADER bih;
\r
71 hFile->Seek(off + icon_list[page].dwImageOffset, SEEK_SET);
\r
73 if (icon_list[page].bWidth==0 && icon_list[page].bHeight==0)
\r
74 { // Vista icon support
\r
75 #if CXIMAGE_SUPPORT_PNG
\r
77 if (png.Decode(hFile,CXIMAGE_FORMAT_PNG)){
\r
79 info.nNumFrames = icon_header.idCount;
\r
81 SetType(CXIMAGE_FORMAT_ICO);
\r
82 #endif //CXIMAGE_SUPPORT_PNG
\r
86 hFile->Read(&bih,sizeof(BITMAPINFOHEADER),1);
\r
92 // allocate memory for one icon
\r
93 Create(icon_list[page].bWidth,icon_list[page].bHeight, c, CXIMAGE_FORMAT_ICO); //image creation
\r
98 hFile->Read(pal,bih.biClrUsed*sizeof(RGBQUAD), 1);
\r
100 hFile->Read(pal,head.biClrUsed*sizeof(RGBQUAD), 1);
\r
102 SetPalette(pal,head.biClrUsed); //palette assign
\r
106 hFile->Read(info.pImage, head.biSizeImage, 1);
\r
107 } else { // 32 bit icon
\r
108 BYTE* buf=(BYTE*)malloc(4*head.biHeight*head.biWidth);
\r
110 hFile->Read(buf, 4*head.biHeight*head.biWidth, 1);
\r
111 #if CXIMAGE_SUPPORT_ALPHA
\r
112 if (!AlphaIsValid()) AlphaCreate();
\r
113 #endif //CXIMAGE_SUPPORT_ALPHA
\r
114 for (long y = 0; y < head.biHeight; y++) {
\r
115 BYTE* dst = GetBits(y);
\r
116 for(long x=0;x<head.biWidth;x++){
\r
120 #if CXIMAGE_SUPPORT_ALPHA
\r
121 AlphaSet(x,y,src[3]);
\r
122 #endif //CXIMAGE_SUPPORT_ALPHA
\r
128 // apply the AND and XOR masks
\r
129 int maskwdt = ((head.biWidth+31) / 32) * 4; //line width of AND mask (always 1 Bpp)
\r
130 int masksize = head.biHeight * maskwdt; //size of mask
\r
131 BYTE *mask = (BYTE *)malloc(masksize);
\r
132 if (hFile->Read(mask, masksize, 1)){
\r
134 bool bGoodMask=false;
\r
135 for (int im=0;im<masksize;im++){
\r
136 if (mask[im]!=255){
\r
143 #if CXIMAGE_SUPPORT_ALPHA
\r
144 bool bNeedAlpha = false;
\r
145 if (!AlphaIsValid()){
\r
148 bNeedAlpha=true; //32bit icon
\r
151 for (y = 0; y < head.biHeight; y++) {
\r
152 for (x = 0; x < head.biWidth; x++) {
\r
153 if (((mask[y*maskwdt+(x>>3)]>>(7-x%8))&0x01)){
\r
159 if (!bNeedAlpha) AlphaDelete();
\r
160 #endif //CXIMAGE_SUPPORT_ALPHA
\r
162 //check if there is only one transparent color
\r
164 long* pcc = (long*)&cc;
\r
165 long* pct = (long*)&ct;
\r
166 int nTransColors=0;
\r
168 for (y = 0; y < head.biHeight; y++){
\r
169 for (x = 0; x < head.biWidth; x++){
\r
170 if (((mask[y*maskwdt+(x>>3)] >> (7-x%8)) & 0x01)){
\r
171 cc = GetPixelColor(x,y,false);
\r
172 if (nTransColors==0){
\r
173 nTransIndex = GetPixelIndex(x,y);
\r
184 if (nTransColors==1){
\r
186 SetTransIndex(nTransIndex);
\r
187 #if CXIMAGE_SUPPORT_ALPHA
\r
188 AlphaDelete(); //because we have a unique transparent color in the image
\r
189 #endif //CXIMAGE_SUPPORT_ALPHA
\r
192 // <vho> - Transparency support w/o Alpha support
\r
193 if (c <= 8){ // only for icons with less than 256 colors (XP icons need alpha).
\r
195 // find a color index, which is not used in the image
\r
196 // it is almost sure to find one, bcs. nobody uses all possible colors for an icon
\r
198 BYTE colorsUsed[256];
\r
199 memset(colorsUsed, 0, sizeof(colorsUsed));
\r
201 for (y = 0; y < head.biHeight; y++){
\r
202 for (x = 0; x < head.biWidth; x++){
\r
203 colorsUsed[BlindGetPixelIndex(x,y)] = 1;
\r
207 int iTransIdx = -1;
\r
208 for (x = (int)(head.biClrUsed-1); x>=0 ; x--){
\r
209 if (colorsUsed[x] == 0){
\r
210 iTransIdx = x; // this one is not in use. we may use it as transparent color
\r
215 // Go thru image and set unused color as transparent index if needed
\r
216 if (iTransIdx >= 0){
\r
217 bool bNeedTrans = false;
\r
218 for (y = 0; y < head.biHeight; y++){
\r
219 for (x = 0; x < head.biWidth; x++){
\r
220 // AND mask (Each Byte represents 8 Pixels)
\r
221 if (((mask[y*maskwdt+(x>>3)] >> (7-x%8)) & 0x01)){
\r
222 // AND mask is set (!=0). This is a transparent part
\r
223 SetPixelIndex(x, y, (BYTE)iTransIdx);
\r
228 // set transparent index if needed
\r
229 if (bNeedTrans) SetTransIndex(iTransIdx);
\r
230 #if CXIMAGE_SUPPORT_ALPHA
\r
231 AlphaDelete(); //because we have a transparent color in the palette
\r
232 #endif //CXIMAGE_SUPPORT_ALPHA
\r
236 SetTransIndex(0); //empty mask, set black as transparent color
\r
243 // icon has been loaded successfully!
\r
250 ////////////////////////////////////////////////////////////////////////////////
\r
251 #endif //CXIMAGE_SUPPORT_DECODE
\r
252 ////////////////////////////////////////////////////////////////////////////////
\r
253 #if CXIMAGE_SUPPORT_ENCODE
\r
254 ////////////////////////////////////////////////////////////////////////////////
\r
255 // Thanks to <Alas>
\r
256 bool CxImageICO::Encode(CxFile * hFile, CxImage ** pImages, int nPageCount)
\r
260 if (hFile==NULL) cx_throw("invalid file pointer");
\r
261 if (pImages==NULL || nPageCount<=0) cx_throw("multipage ICO, no images!");
\r
264 for (i=0; i<nPageCount; i++){
\r
265 if (pImages[i]==NULL)
\r
266 cx_throw("Bad image pointer");
\r
267 if (!(pImages[i]->IsValid()))
\r
268 cx_throw("Empty image");
\r
272 for (i=0; i<nPageCount; i++){ //write headers
\r
273 ghost.Ghost(pImages[i]);
\r
274 ghost.info.nNumFrames = nPageCount;
\r
276 if (!ghost.Encode(hFile,false,nPageCount))
\r
277 cx_throw("Error writing ICO file header");
\r
279 if (!ghost.Encode(hFile,true,nPageCount))
\r
280 cx_throw("Error saving ICO image header");
\r
282 for (i=0; i<nPageCount; i++){ //write bodies
\r
283 ghost.Ghost(pImages[i]);
\r
284 ghost.info.nNumFrames = nPageCount;
\r
285 if (!ghost.Encode(hFile,true,i))
\r
286 cx_throw("Error saving ICO body");
\r
290 if (strcmp(message,"")) strncpy(info.szLastError,message,255);
\r
295 ////////////////////////////////////////////////////////////////////////////////
\r
296 bool CxImageICO::Encode(CxFile * hFile, bool bAppend, int nPageCount)
\r
298 if (EncodeSafeCheck(hFile)) return false;
\r
300 #if CXIMAGE_SUPPORT_PNG == 0
\r
301 //check format limits
\r
302 if ((head.biWidth>255)||(head.biHeight>255)){
\r
303 strcpy(info.szLastError,"Can't save this image as icon");
\r
308 //prepare the palette struct
\r
309 RGBQUAD* pal=GetPalette();
\r
310 if (head.biBitCount<=8 && pal==NULL) return false;
\r
312 int maskwdt=((head.biWidth+31)/32)*4; //mask line width
\r
313 int masksize=head.biHeight * maskwdt; //size of mask
\r
314 int bitcount=head.biBitCount;
\r
315 int imagesize=head.biSizeImage;
\r
316 #if CXIMAGE_SUPPORT_ALPHA
\r
317 if (AlphaIsValid() && head.biClrUsed==0){
\r
319 imagesize=4*head.biHeight*head.biWidth;
\r
323 //fill the icon headers
\r
324 int nPages = nPageCount;
\r
325 if (nPages<1) nPages = 1;
\r
327 ICONHEADER icon_header={0,1,nPages};
\r
330 m_dwImageOffset = sizeof(ICONHEADER) + nPages * sizeof(ICONDIRENTRY);
\r
332 DWORD dwBytesInRes = sizeof(BITMAPINFOHEADER)+head.biClrUsed*sizeof(RGBQUAD)+imagesize+masksize;
\r
334 ICONDIRENTRY icon_list={
\r
335 (BYTE)head.biWidth,
\r
336 (BYTE)head.biHeight,
\r
337 (BYTE)head.biClrUsed,
\r
344 BITMAPINFOHEADER bi={
\r
345 sizeof(BITMAPINFOHEADER),
\r
354 #if CXIMAGE_SUPPORT_PNG // Vista icon support
\r
355 CxImage png(*this);
\r
357 if (head.biWidth>255 || head.biHeight>255){
\r
358 icon_list.bWidth = icon_list.bHeight = 0;
\r
360 png.Encode(&memfile,CXIMAGE_FORMAT_PNG);
\r
361 icon_list.dwBytesInRes = dwBytesInRes = memfile.Size();
\r
363 #endif //CXIMAGE_SUPPORT_PNG
\r
366 icon_header.idType = ntohs(icon_header.idType);
\r
367 icon_header.idCount = ntohs(icon_header.idCount);
\r
368 hFile->Write(&icon_header,sizeof(ICONHEADER),1); //write the file header
\r
369 icon_header.idType = ntohs(icon_header.idType);
\r
370 icon_header.idCount = ntohs(icon_header.idCount);
\r
374 if ((bAppend && nPageCount==info.nNumFrames) || (!bAppend && nPageCount==0)){
\r
375 icon_list.wPlanes = ntohs(icon_list.wPlanes);
\r
376 icon_list.wBitCount = ntohs(icon_list.wBitCount);
\r
377 icon_list.dwBytesInRes = ntohl(icon_list.dwBytesInRes);
\r
378 icon_list.dwImageOffset = ntohl(icon_list.dwImageOffset);
\r
379 hFile->Write(&icon_list,sizeof(ICONDIRENTRY),1); //write the image entry
\r
380 icon_list.wPlanes = ntohs(icon_list.wPlanes);
\r
381 icon_list.wBitCount = ntohs(icon_list.wBitCount);
\r
382 icon_list.dwBytesInRes = ntohl(icon_list.dwBytesInRes);
\r
383 icon_list.dwImageOffset = ntohl(icon_list.dwImageOffset);
\r
385 m_dwImageOffset += dwBytesInRes; //update offset for next header
\r
388 if ((bAppend && nPageCount<info.nNumFrames) || (!bAppend && nPageCount==0))
\r
390 #if CXIMAGE_SUPPORT_PNG
\r
391 if (icon_list.bWidth==0 && icon_list.bHeight==0) { // Vista icon support
\r
392 hFile->Write(memfile.GetBuffer(false),dwBytesInRes,1);
\r
394 #endif //CXIMAGE_SUPPORT_PNG
\r
397 hFile->Write(&bi,sizeof(BITMAPINFOHEADER),1); //write the image header
\r
400 bool bTransparent = info.nBkgndIndex >= 0;
\r
401 RGBQUAD ct = GetTransColor();
\r
403 if (bTransparent) SetPaletteColor((BYTE)info.nBkgndIndex,0,0,0,0);
\r
404 hFile->Write(pal,head.biClrUsed*sizeof(RGBQUAD),1); //write palette
\r
405 if (bTransparent) SetPaletteColor((BYTE)info.nBkgndIndex,ct);
\r
408 #if CXIMAGE_SUPPORT_ALPHA
\r
409 if (AlphaIsValid() && head.biClrUsed==0){
\r
410 BYTE* buf=(BYTE*)malloc(imagesize);
\r
412 for (long y = 0; y < head.biHeight; y++) {
\r
413 BYTE* src = GetBits(y);
\r
414 for(long x=0;x<head.biWidth;x++){
\r
418 *dst++=AlphaGet(x,y);
\r
421 hFile->Write(buf,imagesize, 1);
\r
424 hFile->Write(info.pImage,imagesize,1); //write image
\r
427 hFile->Write(info.pImage,imagesize,1); //write image
\r
430 //save transparency mask
\r
431 BYTE* mask=(BYTE*)calloc(masksize,1); //create empty AND/XOR masks
\r
432 if (!mask) return false;
\r
434 //prepare the variables to build the mask
\r
437 RGBQUAD c={0,0,0,0};
\r
438 long* pc = (long*)&c;
\r
439 long* pct= (long*)&ct;
\r
440 #if CXIMAGE_SUPPORT_ALPHA
\r
441 bool bAlphaPaletteIsValid = AlphaPaletteIsValid();
\r
442 bool bAlphaIsValid = AlphaIsValid();
\r
445 for (int y = 0; y < head.biHeight; y++) {
\r
446 for (int x = 0; x < head.biWidth; x++) {
\r
448 #if CXIMAGE_SUPPORT_ALPHA
\r
449 if (bAlphaIsValid && AlphaGet(x,y)==0) i=1;
\r
450 if (bAlphaPaletteIsValid && BlindGetPixelColor(x,y).rgbReserved==0) i=1;
\r
452 c=GetPixelColor(x,y,false);
\r
453 if (bTransparent && *pc==*pct) i=1;
\r
454 iDst = mask + y*maskwdt + (x>>3);
\r
456 *iDst &= ~(0x01<<pos);
\r
457 *iDst |= ((i & 0x01)<<pos);
\r
460 //write AND/XOR masks
\r
461 hFile->Write(mask,masksize,1);
\r
468 ////////////////////////////////////////////////////////////////////////////////
\r
469 #endif // CXIMAGE_SUPPORT_ENCODE
\r
470 ////////////////////////////////////////////////////////////////////////////////
\r
471 #endif // CXIMAGE_SUPPORT_ICO
\r