]> Creatis software - clitk.git/blob - utilities/CxImage/ximaico.cpp
0cb3b060f2895fb7420fedcb6b2bbb1568540b70
[clitk.git] / utilities / CxImage / ximaico.cpp
1 /*\r
2  * File:        ximaico.cpp\r
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
6  */\r
7 \r
8 #include "ximaico.h"\r
9 \r
10 #if CXIMAGE_SUPPORT_ICO\r
11 \r
12 ////////////////////////////////////////////////////////////////////////////////\r
13 #if CXIMAGE_SUPPORT_DECODE\r
14 ////////////////////////////////////////////////////////////////////////////////\r
15 bool CxImageICO::Decode(CxFile *hFile)\r
16 {\r
17         if (hFile==NULL) return false;\r
18 \r
19         DWORD off = hFile->Tell(); //<yuandi>\r
20         int     page=info.nFrame;       //internal icon structure indexes\r
21 \r
22         // read the first part of the header\r
23         ICONHEADER icon_header;\r
24         hFile->Read(&icon_header,sizeof(ICONHEADER),1);\r
25 \r
26         icon_header.idType = ntohs(icon_header.idType);\r
27         icon_header.idCount = ntohs(icon_header.idCount);\r
28 \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
31 \r
32                 info.nNumFrames = icon_header.idCount;\r
33 \r
34                 // load the icon descriptions\r
35                 ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header.idCount * sizeof(ICONDIRENTRY));\r
36                 int c;\r
37                 for (c = 0; c < icon_header.idCount; c++) {\r
38                         hFile->Read(icon_list + c, sizeof(ICONDIRENTRY), 1);\r
39 \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
44                 }\r
45                 \r
46                 if ((page>=0)&&(page<icon_header.idCount)){\r
47 \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
56                                         CxImage png;\r
57                                         png.SetEscape(-1);\r
58                                         if (png.Decode(hFile,CXIMAGE_FORMAT_PNG)){\r
59                                                 Transfer(png);\r
60                                                 info.nNumFrames = icon_header.idCount;\r
61                                         }\r
62                                 }\r
63 #endif //CXIMAGE_SUPPORT_PNG\r
64                                 free(icon_list);\r
65                                 info.dwType = CXIMAGE_FORMAT_ICO;\r
66                                 return true;\r
67                         }\r
68 \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
72 \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
76                                 CxImage png;\r
77                                 if (png.Decode(hFile,CXIMAGE_FORMAT_PNG)){\r
78                                         Transfer(png);\r
79                                         info.nNumFrames = icon_header.idCount;\r
80                                 }\r
81                                 SetType(CXIMAGE_FORMAT_ICO);\r
82 #endif //CXIMAGE_SUPPORT_PNG\r
83                         }\r
84                         else\r
85                         {       // standard icon\r
86                                 hFile->Read(&bih,sizeof(BITMAPINFOHEADER),1);\r
87 \r
88                                 bihtoh(&bih);\r
89 \r
90                                 c = bih.biBitCount;\r
91 \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
94 \r
95                                 // read the palette\r
96                                 RGBQUAD pal[256];\r
97                                 if (bih.biClrUsed)\r
98                                         hFile->Read(pal,bih.biClrUsed*sizeof(RGBQUAD), 1);\r
99                                 else\r
100                                         hFile->Read(pal,head.biClrUsed*sizeof(RGBQUAD), 1);\r
101 \r
102                                 SetPalette(pal,head.biClrUsed); //palette assign\r
103 \r
104                                 //read the icon\r
105                                 if (c<=24){\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
109                                         BYTE* src = buf;\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
117                                                         *dst++=src[0];\r
118                                                         *dst++=src[1];\r
119                                                         *dst++=src[2];\r
120 #if CXIMAGE_SUPPORT_ALPHA\r
121                                                         AlphaSet(x,y,src[3]);\r
122 #endif //CXIMAGE_SUPPORT_ALPHA\r
123                                                         src+=4;\r
124                                                 }\r
125                                         }\r
126                                         free(buf);\r
127                                 }\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
133 \r
134                                         bool bGoodMask=false;\r
135                                         for (int im=0;im<masksize;im++){\r
136                                                 if (mask[im]!=255){\r
137                                                         bGoodMask=true;\r
138                                                         break;\r
139                                                 }\r
140                                         }\r
141 \r
142                                         if (bGoodMask){\r
143 #if CXIMAGE_SUPPORT_ALPHA\r
144                                                 bool bNeedAlpha = false;\r
145                                                 if (!AlphaIsValid()){\r
146                                                         AlphaCreate();\r
147                                                 } else { \r
148                                                         bNeedAlpha=true; //32bit icon\r
149                                                 }\r
150                                                 int x,y;\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
154                                                                         AlphaSet(x,y,0);\r
155                                                                         bNeedAlpha=true;\r
156                                                                 }\r
157                                                         }\r
158                                                 }\r
159                                                 if (!bNeedAlpha) AlphaDelete();\r
160 #endif //CXIMAGE_SUPPORT_ALPHA\r
161 \r
162                                                 //check if there is only one transparent color\r
163                                                 RGBQUAD cc,ct;\r
164                                                 long* pcc = (long*)&cc;\r
165                                                 long* pct = (long*)&ct;\r
166                                                 int nTransColors=0;\r
167                                                 int nTransIndex=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
174                                                                                 nTransColors++;\r
175                                                                                 ct = cc;\r
176                                                                         } else {\r
177                                                                                 if (*pct!=*pcc){\r
178                                                                                         nTransColors++;\r
179                                                                                 }\r
180                                                                         }\r
181                                                                 }\r
182                                                         }\r
183                                                 }\r
184                                                 if (nTransColors==1){\r
185                                                         SetTransColor(ct);\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
190                                                 }\r
191 \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
194                                                           \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
197 \r
198                                                         BYTE colorsUsed[256];\r
199                                                         memset(colorsUsed, 0, sizeof(colorsUsed));\r
200 \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
204                                                                 }\r
205                                                         }\r
206 \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
211                                                                         break;\r
212                                                                 }\r
213                                                         }\r
214 \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
224                                                                                         bNeedTrans = true;\r
225                                                                                 }\r
226                                                                         }\r
227                                                                 }\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
233                                                         }\r
234                                                 }\r
235                                         } else {\r
236                                                 SetTransIndex(0); //empty mask, set black as transparent color\r
237                                                 Negative();\r
238                                         }\r
239                                 } \r
240                                 free(mask);\r
241                         }\r
242                         free(icon_list);\r
243                         // icon has been loaded successfully!\r
244                         return true;\r
245                 }\r
246                 free(icon_list);\r
247         }\r
248         return false;\r
249 }\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
257 {\r
258   cx_try\r
259   {\r
260         if (hFile==NULL) cx_throw("invalid file pointer");\r
261         if (pImages==NULL || nPageCount<=0) cx_throw("multipage ICO, no images!");\r
262 \r
263         int i;\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
269         }\r
270 \r
271         CxImageICO ghost;\r
272         for (i=0; i<nPageCount; i++){   //write headers\r
273                 ghost.Ghost(pImages[i]);\r
274                 ghost.info.nNumFrames = nPageCount;\r
275                 if (i==0) {\r
276                         if (!ghost.Encode(hFile,false,nPageCount))\r
277                                 cx_throw("Error writing ICO file header");\r
278                 }\r
279                 if (!ghost.Encode(hFile,true,nPageCount)) \r
280                         cx_throw("Error saving ICO image header");\r
281         }\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
287         }\r
288 \r
289   } cx_catch {\r
290           if (strcmp(message,"")) strncpy(info.szLastError,message,255);\r
291           return false;\r
292   }\r
293         return true;\r
294 }\r
295 ////////////////////////////////////////////////////////////////////////////////\r
296 bool CxImageICO::Encode(CxFile * hFile, bool bAppend, int nPageCount)\r
297 {\r
298         if (EncodeSafeCheck(hFile)) return false;\r
299 \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
304                 return false;\r
305         }\r
306 #endif\r
307 \r
308         //prepare the palette struct\r
309         RGBQUAD* pal=GetPalette();\r
310         if (head.biBitCount<=8 && pal==NULL) return false;\r
311 \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
318                 bitcount=32;\r
319                 imagesize=4*head.biHeight*head.biWidth;\r
320         }\r
321 #endif\r
322 \r
323         //fill the icon headers\r
324         int nPages = nPageCount;\r
325         if (nPages<1) nPages = 1;\r
326 \r
327         ICONHEADER icon_header={0,1,nPages};\r
328 \r
329         if (!bAppend)\r
330                 m_dwImageOffset = sizeof(ICONHEADER) + nPages * sizeof(ICONDIRENTRY);\r
331 \r
332         DWORD dwBytesInRes = sizeof(BITMAPINFOHEADER)+head.biClrUsed*sizeof(RGBQUAD)+imagesize+masksize;\r
333 \r
334         ICONDIRENTRY icon_list={\r
335                 (BYTE)head.biWidth,\r
336                 (BYTE)head.biHeight,\r
337                 (BYTE)head.biClrUsed,\r
338                 0, 0,\r
339                 (WORD)bitcount,\r
340                 dwBytesInRes,\r
341                 m_dwImageOffset\r
342         };\r
343 \r
344         BITMAPINFOHEADER bi={\r
345                 sizeof(BITMAPINFOHEADER),\r
346                 head.biWidth,\r
347                 2*head.biHeight,\r
348                 1,\r
349                 (WORD)bitcount,\r
350                 0, imagesize,\r
351                 0, 0, 0, 0\r
352         };\r
353 \r
354 #if CXIMAGE_SUPPORT_PNG // Vista icon support\r
355         CxImage png(*this);\r
356         CxMemFile memfile;\r
357         if (head.biWidth>255 || head.biHeight>255){\r
358                 icon_list.bWidth = icon_list.bHeight = 0;\r
359                 memfile.Open();\r
360                 png.Encode(&memfile,CXIMAGE_FORMAT_PNG);\r
361                 icon_list.dwBytesInRes = dwBytesInRes = memfile.Size();\r
362         }\r
363 #endif //CXIMAGE_SUPPORT_PNG\r
364 \r
365         if (!bAppend){\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
371         }\r
372 \r
373 \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
384 \r
385                 m_dwImageOffset += dwBytesInRes;                        //update offset for next header\r
386         }\r
387 \r
388         if ((bAppend && nPageCount<info.nNumFrames) || (!bAppend && nPageCount==0))\r
389         {\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
393                 } else\r
394 #endif //CXIMAGE_SUPPORT_PNG\r
395                 {       // standard icon\r
396                         bihtoh(&bi);\r
397                         hFile->Write(&bi,sizeof(BITMAPINFOHEADER),1);                   //write the image header\r
398                         bihtoh(&bi);\r
399 \r
400                         bool bTransparent = info.nBkgndIndex >= 0;\r
401                         RGBQUAD ct = GetTransColor();\r
402                         if (pal){\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
406                         }\r
407 \r
408 #if CXIMAGE_SUPPORT_ALPHA\r
409                         if (AlphaIsValid() && head.biClrUsed==0){\r
410                                 BYTE* buf=(BYTE*)malloc(imagesize);\r
411                                 BYTE* dst = buf;\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
415                                                 *dst++=*src++;\r
416                                                 *dst++=*src++;\r
417                                                 *dst++=*src++;\r
418                                                 *dst++=AlphaGet(x,y);\r
419                                         }\r
420                                 }\r
421                                 hFile->Write(buf,imagesize, 1);\r
422                                 free(buf);\r
423                         } else {\r
424                                 hFile->Write(info.pImage,imagesize,1);  //write image\r
425                         }\r
426 #else\r
427                         hFile->Write(info.pImage,imagesize,1);  //write image\r
428 #endif\r
429 \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
433 \r
434                         //prepare the variables to build the mask\r
435                         BYTE* iDst;\r
436                         int pos,i;\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
443 #endif\r
444                         //build the mask\r
445                         for (int y = 0; y < head.biHeight; y++) {\r
446                                 for (int x = 0; x < head.biWidth; x++) {\r
447                                         i=0;\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
451 #endif\r
452                                         c=GetPixelColor(x,y,false);\r
453                                         if (bTransparent && *pc==*pct) i=1;\r
454                                         iDst = mask + y*maskwdt + (x>>3);\r
455                                         pos = 7-x%8;\r
456                                         *iDst &= ~(0x01<<pos);\r
457                                         *iDst |= ((i & 0x01)<<pos);\r
458                                 }\r
459                         }\r
460                         //write AND/XOR masks\r
461                         hFile->Write(mask,masksize,1);\r
462                         free(mask);\r
463                 }\r
464         }\r
465 \r
466         return true;\r
467 }\r
468 ////////////////////////////////////////////////////////////////////////////////\r
469 #endif // CXIMAGE_SUPPORT_ENCODE\r
470 ////////////////////////////////////////////////////////////////////////////////\r
471 #endif // CXIMAGE_SUPPORT_ICO\r
472 \r