]> Creatis software - clitk.git/blob - utilities/CxImage/ximaico.cpp
cosmetic
[clitk.git] / utilities / CxImage / ximaico.cpp
1 /*
2  * File:        ximaico.cpp
3  * Purpose:     Platform Independent ICON Image Class Loader and Writer (MS version)
4  * 07/Aug/2001 Davide Pizzolato - www.xdp.it
5  * CxImage version 6.0.0 02/Feb/2008
6  */
7
8 #include "ximaico.h"
9
10 #if CXIMAGE_SUPPORT_ICO
11
12 ////////////////////////////////////////////////////////////////////////////////
13 #if CXIMAGE_SUPPORT_DECODE
14 ////////////////////////////////////////////////////////////////////////////////
15 bool CxImageICO::Decode(CxFile *hFile)
16 {
17         if (hFile==NULL) return false;
18
19         DWORD off = hFile->Tell(); //<yuandi>
20         int     page=info.nFrame;       //internal icon structure indexes
21
22         // read the first part of the header
23         ICONHEADER icon_header;
24         hFile->Read(&icon_header,sizeof(ICONHEADER),1);
25
26         icon_header.idType = ntohs(icon_header.idType);
27         icon_header.idCount = ntohs(icon_header.idCount);
28
29         // check if it's an icon or a cursor
30         if ((icon_header.idReserved == 0) && ((icon_header.idType == 1)||(icon_header.idType == 2))) {
31
32                 info.nNumFrames = icon_header.idCount;
33
34                 // load the icon descriptions
35                 ICONDIRENTRY *icon_list = (ICONDIRENTRY *)malloc(icon_header.idCount * sizeof(ICONDIRENTRY));
36                 int c;
37                 for (c = 0; c < icon_header.idCount; c++) {
38                         hFile->Read(icon_list + c, sizeof(ICONDIRENTRY), 1);
39
40                         icon_list[c].wPlanes = ntohs(icon_list[c].wPlanes);
41                         icon_list[c].wBitCount = ntohs(icon_list[c].wBitCount);
42                         icon_list[c].dwBytesInRes = ntohl(icon_list[c].dwBytesInRes);
43                         icon_list[c].dwImageOffset = ntohl(icon_list[c].dwImageOffset);
44                 }
45                 
46                 if ((page>=0)&&(page<icon_header.idCount)){
47
48                         if (info.nEscape == -1) {
49                                 // Return output dimensions only
50                                 head.biWidth = icon_list[page].bWidth;
51                                 head.biHeight = icon_list[page].bHeight;
52 #if CXIMAGE_SUPPORT_PNG
53                                 if (head.biWidth==0 && head.biHeight==0)
54                                 {       // Vista icon support
55                                         hFile->Seek(off + icon_list[page].dwImageOffset, SEEK_SET);
56                                         CxImage png;
57                                         png.SetEscape(-1);
58                                         if (png.Decode(hFile,CXIMAGE_FORMAT_PNG)){
59                                                 Transfer(png);
60                                                 info.nNumFrames = icon_header.idCount;
61                                         }
62                                 }
63 #endif //CXIMAGE_SUPPORT_PNG
64                                 free(icon_list);
65                                 info.dwType = CXIMAGE_FORMAT_ICO;
66                                 return true;
67                         }
68
69                         // get the bit count for the colors in the icon <CoreyRLucier>
70                         BITMAPINFOHEADER bih;
71                         hFile->Seek(off + icon_list[page].dwImageOffset, SEEK_SET);
72
73                         if (icon_list[page].bWidth==0 && icon_list[page].bHeight==0)
74                         {       // Vista icon support
75 #if CXIMAGE_SUPPORT_PNG
76                                 CxImage png;
77                                 if (png.Decode(hFile,CXIMAGE_FORMAT_PNG)){
78                                         Transfer(png);
79                                         info.nNumFrames = icon_header.idCount;
80                                 }
81                                 SetType(CXIMAGE_FORMAT_ICO);
82 #endif //CXIMAGE_SUPPORT_PNG
83                         }
84                         else
85                         {       // standard icon
86                                 hFile->Read(&bih,sizeof(BITMAPINFOHEADER),1);
87
88                                 bihtoh(&bih);
89
90                                 c = bih.biBitCount;
91
92                                 // allocate memory for one icon
93                                 Create(icon_list[page].bWidth,icon_list[page].bHeight, c, CXIMAGE_FORMAT_ICO);  //image creation
94
95                                 // read the palette
96                                 RGBQUAD pal[256];
97                                 if (bih.biClrUsed)
98                                         hFile->Read(pal,bih.biClrUsed*sizeof(RGBQUAD), 1);
99                                 else
100                                         hFile->Read(pal,head.biClrUsed*sizeof(RGBQUAD), 1);
101
102                                 SetPalette(pal,head.biClrUsed); //palette assign
103
104                                 //read the icon
105                                 if (c<=24){
106                                         hFile->Read(info.pImage, head.biSizeImage, 1);
107                                 } else { // 32 bit icon
108                                         BYTE* buf=(BYTE*)malloc(4*head.biHeight*head.biWidth);
109                                         BYTE* src = buf;
110                                         hFile->Read(buf, 4*head.biHeight*head.biWidth, 1);
111 #if CXIMAGE_SUPPORT_ALPHA
112                                         if (!AlphaIsValid()) AlphaCreate();
113 #endif //CXIMAGE_SUPPORT_ALPHA
114                                         for (long y = 0; y < head.biHeight; y++) {
115                                                 BYTE* dst = GetBits(y);
116                                                 for(long x=0;x<head.biWidth;x++){
117                                                         *dst++=src[0];
118                                                         *dst++=src[1];
119                                                         *dst++=src[2];
120 #if CXIMAGE_SUPPORT_ALPHA
121                                                         AlphaSet(x,y,src[3]);
122 #endif //CXIMAGE_SUPPORT_ALPHA
123                                                         src+=4;
124                                                 }
125                                         }
126                                         free(buf);
127                                 }
128                                 // apply the AND and XOR masks
129                                 int maskwdt = ((head.biWidth+31) / 32) * 4;     //line width of AND mask (always 1 Bpp)
130                                 int masksize = head.biHeight * maskwdt;                         //size of mask
131                                 BYTE *mask = (BYTE *)malloc(masksize);
132                                 if (hFile->Read(mask, masksize, 1)){
133
134                                         bool bGoodMask=false;
135                                         for (int im=0;im<masksize;im++){
136                                                 if (mask[im]!=255){
137                                                         bGoodMask=true;
138                                                         break;
139                                                 }
140                                         }
141
142                                         if (bGoodMask){
143 #if CXIMAGE_SUPPORT_ALPHA
144                                                 bool bNeedAlpha = false;
145                                                 if (!AlphaIsValid()){
146                                                         AlphaCreate();
147                                                 } else { 
148                                                         bNeedAlpha=true; //32bit icon
149                                                 }
150                                                 int x,y;
151                                                 for (y = 0; y < head.biHeight; y++) {
152                                                         for (x = 0; x < head.biWidth; x++) {
153                                                                 if (((mask[y*maskwdt+(x>>3)]>>(7-x%8))&0x01)){
154                                                                         AlphaSet(x,y,0);
155                                                                         bNeedAlpha=true;
156                                                                 }
157                                                         }
158                                                 }
159                                                 if (!bNeedAlpha) AlphaDelete();
160 #endif //CXIMAGE_SUPPORT_ALPHA
161
162                                                 //check if there is only one transparent color
163                                                 RGBQUAD cc,ct;
164                                                 long* pcc = (long*)&cc;
165                                                 long* pct = (long*)&ct;
166                                                 int nTransColors=0;
167                                                 int nTransIndex=0;
168                                                 for (y = 0; y < head.biHeight; y++){
169                                                         for (x = 0; x < head.biWidth; x++){
170                                                                 if (((mask[y*maskwdt+(x>>3)] >> (7-x%8)) & 0x01)){
171                                                                         cc = GetPixelColor(x,y,false);
172                                                                         if (nTransColors==0){
173                                                                                 nTransIndex = GetPixelIndex(x,y);
174                                                                                 nTransColors++;
175                                                                                 ct = cc;
176                                                                         } else {
177                                                                                 if (*pct!=*pcc){
178                                                                                         nTransColors++;
179                                                                                 }
180                                                                         }
181                                                                 }
182                                                         }
183                                                 }
184                                                 if (nTransColors==1){
185                                                         SetTransColor(ct);
186                                                         SetTransIndex(nTransIndex);
187 #if CXIMAGE_SUPPORT_ALPHA
188                                                         AlphaDelete(); //because we have a unique transparent color in the image
189 #endif //CXIMAGE_SUPPORT_ALPHA
190                                                 }
191
192                                                 // <vho> - Transparency support w/o Alpha support
193                                                 if (c <= 8){ // only for icons with less than 256 colors (XP icons need alpha).
194                                                           
195                                                         // find a color index, which is not used in the image
196                                                         // it is almost sure to find one, bcs. nobody uses all possible colors for an icon
197
198                                                         BYTE colorsUsed[256];
199                                                         memset(colorsUsed, 0, sizeof(colorsUsed));
200
201                                                         for (y = 0; y < head.biHeight; y++){
202                                                                 for (x = 0; x < head.biWidth; x++){
203                                                                         colorsUsed[BlindGetPixelIndex(x,y)] = 1;
204                                                                 }
205                                                         }
206
207                                                         int iTransIdx = -1;
208                                                         for (x = (int)(head.biClrUsed-1); x>=0 ; x--){
209                                                                 if (colorsUsed[x] == 0){
210                                                                         iTransIdx = x; // this one is not in use. we may use it as transparent color
211                                                                         break;
212                                                                 }
213                                                         }
214
215                                                         // Go thru image and set unused color as transparent index if needed
216                                                         if (iTransIdx >= 0){
217                                                                 bool bNeedTrans = false;
218                                                                 for (y = 0; y < head.biHeight; y++){
219                                                                         for (x = 0; x < head.biWidth; x++){
220                                                                                 // AND mask (Each Byte represents 8 Pixels)
221                                                                                 if (((mask[y*maskwdt+(x>>3)] >> (7-x%8)) & 0x01)){
222                                                                                         // AND mask is set (!=0). This is a transparent part
223                                                                                         SetPixelIndex(x, y, (BYTE)iTransIdx);
224                                                                                         bNeedTrans = true;
225                                                                                 }
226                                                                         }
227                                                                 }
228                                                                 // set transparent index if needed
229                                                                 if (bNeedTrans) SetTransIndex(iTransIdx);
230 #if CXIMAGE_SUPPORT_ALPHA
231                                                                 AlphaDelete(); //because we have a transparent color in the palette
232 #endif //CXIMAGE_SUPPORT_ALPHA
233                                                         }
234                                                 }
235                                         } else {
236                                                 SetTransIndex(0); //empty mask, set black as transparent color
237                                                 Negative();
238                                         }
239                                 } 
240                                 free(mask);
241                         }
242                         free(icon_list);
243                         // icon has been loaded successfully!
244                         return true;
245                 }
246                 free(icon_list);
247         }
248         return false;
249 }
250 ////////////////////////////////////////////////////////////////////////////////
251 #endif //CXIMAGE_SUPPORT_DECODE
252 ////////////////////////////////////////////////////////////////////////////////
253 #if CXIMAGE_SUPPORT_ENCODE
254 ////////////////////////////////////////////////////////////////////////////////
255 // Thanks to <Alas>
256 bool CxImageICO::Encode(CxFile * hFile, CxImage ** pImages, int nPageCount)
257 {
258   cx_try
259   {
260         if (hFile==NULL) cx_throw("invalid file pointer");
261         if (pImages==NULL || nPageCount<=0) cx_throw("multipage ICO, no images!");
262
263         int i;
264         for (i=0; i<nPageCount; i++){
265                 if (pImages[i]==NULL)
266                         cx_throw("Bad image pointer");
267                 if (!(pImages[i]->IsValid()))
268                         cx_throw("Empty image");
269         }
270
271         CxImageICO ghost;
272         for (i=0; i<nPageCount; i++){   //write headers
273                 ghost.Ghost(pImages[i]);
274                 ghost.info.nNumFrames = nPageCount;
275                 if (i==0) {
276                         if (!ghost.Encode(hFile,false,nPageCount))
277                                 cx_throw("Error writing ICO file header");
278                 }
279                 if (!ghost.Encode(hFile,true,nPageCount)) 
280                         cx_throw("Error saving ICO image header");
281         }
282         for (i=0; i<nPageCount; i++){   //write bodies
283                 ghost.Ghost(pImages[i]);
284                 ghost.info.nNumFrames = nPageCount;
285                 if (!ghost.Encode(hFile,true,i)) 
286                         cx_throw("Error saving ICO body");
287         }
288
289   } cx_catch {
290           if (strcmp(message,"")) strncpy(info.szLastError,message,255);
291           return false;
292   }
293         return true;
294 }
295 ////////////////////////////////////////////////////////////////////////////////
296 bool CxImageICO::Encode(CxFile * hFile, bool bAppend, int nPageCount)
297 {
298         if (EncodeSafeCheck(hFile)) return false;
299
300 #if CXIMAGE_SUPPORT_PNG == 0
301         //check format limits
302         if ((head.biWidth>255)||(head.biHeight>255)){
303                 strcpy(info.szLastError,"Can't save this image as icon");
304                 return false;
305         }
306 #endif
307
308         //prepare the palette struct
309         RGBQUAD* pal=GetPalette();
310         if (head.biBitCount<=8 && pal==NULL) return false;
311
312         int maskwdt=((head.biWidth+31)/32)*4; //mask line width
313         int masksize=head.biHeight * maskwdt; //size of mask
314         int bitcount=head.biBitCount;
315         int imagesize=head.biSizeImage;
316 #if CXIMAGE_SUPPORT_ALPHA
317         if (AlphaIsValid() && head.biClrUsed==0){
318                 bitcount=32;
319                 imagesize=4*head.biHeight*head.biWidth;
320         }
321 #endif
322
323         //fill the icon headers
324         int nPages = nPageCount;
325         if (nPages<1) nPages = 1;
326
327         ICONHEADER icon_header={0,1,nPages};
328
329         if (!bAppend)
330                 m_dwImageOffset = sizeof(ICONHEADER) + nPages * sizeof(ICONDIRENTRY);
331
332         DWORD dwBytesInRes = sizeof(BITMAPINFOHEADER)+head.biClrUsed*sizeof(RGBQUAD)+imagesize+masksize;
333
334         ICONDIRENTRY icon_list={
335                 (BYTE)head.biWidth,
336                 (BYTE)head.biHeight,
337                 (BYTE)head.biClrUsed,
338                 0, 0,
339                 (WORD)bitcount,
340                 dwBytesInRes,
341                 m_dwImageOffset
342         };
343
344         BITMAPINFOHEADER bi={
345                 sizeof(BITMAPINFOHEADER),
346                 head.biWidth,
347                 2*head.biHeight,
348                 1,
349                 (WORD)bitcount,
350                 0, imagesize,
351                 0, 0, 0, 0
352         };
353
354 #if CXIMAGE_SUPPORT_PNG // Vista icon support
355         CxImage png(*this);
356         CxMemFile memfile;
357         if (head.biWidth>255 || head.biHeight>255){
358                 icon_list.bWidth = icon_list.bHeight = 0;
359                 memfile.Open();
360                 png.Encode(&memfile,CXIMAGE_FORMAT_PNG);
361                 icon_list.dwBytesInRes = dwBytesInRes = memfile.Size();
362         }
363 #endif //CXIMAGE_SUPPORT_PNG
364
365         if (!bAppend){
366                 icon_header.idType = ntohs(icon_header.idType);
367                 icon_header.idCount = ntohs(icon_header.idCount);
368                 hFile->Write(&icon_header,sizeof(ICONHEADER),1);        //write the file header
369                 icon_header.idType = ntohs(icon_header.idType);
370                 icon_header.idCount = ntohs(icon_header.idCount);
371         }
372
373
374         if ((bAppend && nPageCount==info.nNumFrames) || (!bAppend && nPageCount==0)){
375                 icon_list.wPlanes = ntohs(icon_list.wPlanes);
376                 icon_list.wBitCount = ntohs(icon_list.wBitCount);
377                 icon_list.dwBytesInRes = ntohl(icon_list.dwBytesInRes);
378                 icon_list.dwImageOffset = ntohl(icon_list.dwImageOffset);
379                 hFile->Write(&icon_list,sizeof(ICONDIRENTRY),1);        //write the image entry
380                 icon_list.wPlanes = ntohs(icon_list.wPlanes);
381                 icon_list.wBitCount = ntohs(icon_list.wBitCount);
382                 icon_list.dwBytesInRes = ntohl(icon_list.dwBytesInRes);
383                 icon_list.dwImageOffset = ntohl(icon_list.dwImageOffset);
384
385                 m_dwImageOffset += dwBytesInRes;                        //update offset for next header
386         }
387
388         if ((bAppend && nPageCount<info.nNumFrames) || (!bAppend && nPageCount==0))
389         {
390 #if CXIMAGE_SUPPORT_PNG
391                 if (icon_list.bWidth==0 && icon_list.bHeight==0) {      // Vista icon support
392                         hFile->Write(memfile.GetBuffer(false),dwBytesInRes,1);
393                 } else
394 #endif //CXIMAGE_SUPPORT_PNG
395                 {       // standard icon
396                         bihtoh(&bi);
397                         hFile->Write(&bi,sizeof(BITMAPINFOHEADER),1);                   //write the image header
398                         bihtoh(&bi);
399
400                         bool bTransparent = info.nBkgndIndex >= 0;
401                         RGBQUAD ct = GetTransColor();
402                         if (pal){
403                                 if (bTransparent) SetPaletteColor((BYTE)info.nBkgndIndex,0,0,0,0);
404                                 hFile->Write(pal,head.biClrUsed*sizeof(RGBQUAD),1); //write palette
405                                 if (bTransparent) SetPaletteColor((BYTE)info.nBkgndIndex,ct);
406                         }
407
408 #if CXIMAGE_SUPPORT_ALPHA
409                         if (AlphaIsValid() && head.biClrUsed==0){
410                                 BYTE* buf=(BYTE*)malloc(imagesize);
411                                 BYTE* dst = buf;
412                                 for (long y = 0; y < head.biHeight; y++) {
413                                         BYTE* src = GetBits(y);
414                                         for(long x=0;x<head.biWidth;x++){
415                                                 *dst++=*src++;
416                                                 *dst++=*src++;
417                                                 *dst++=*src++;
418                                                 *dst++=AlphaGet(x,y);
419                                         }
420                                 }
421                                 hFile->Write(buf,imagesize, 1);
422                                 free(buf);
423                         } else {
424                                 hFile->Write(info.pImage,imagesize,1);  //write image
425                         }
426 #else
427                         hFile->Write(info.pImage,imagesize,1);  //write image
428 #endif
429
430                         //save transparency mask
431                         BYTE* mask=(BYTE*)calloc(masksize,1);   //create empty AND/XOR masks
432                         if (!mask) return false;
433
434                         //prepare the variables to build the mask
435                         BYTE* iDst;
436                         int pos,i;
437                         RGBQUAD c={0,0,0,0};
438                         long* pc = (long*)&c;
439                         long* pct= (long*)&ct;
440 #if CXIMAGE_SUPPORT_ALPHA
441                         bool bAlphaPaletteIsValid = AlphaPaletteIsValid();
442                         bool bAlphaIsValid = AlphaIsValid();
443 #endif
444                         //build the mask
445                         for (int y = 0; y < head.biHeight; y++) {
446                                 for (int x = 0; x < head.biWidth; x++) {
447                                         i=0;
448 #if CXIMAGE_SUPPORT_ALPHA
449                                         if (bAlphaIsValid && AlphaGet(x,y)==0) i=1;
450                                         if (bAlphaPaletteIsValid && BlindGetPixelColor(x,y).rgbReserved==0) i=1;
451 #endif
452                                         c=GetPixelColor(x,y,false);
453                                         if (bTransparent && *pc==*pct) i=1;
454                                         iDst = mask + y*maskwdt + (x>>3);
455                                         pos = 7-x%8;
456                                         *iDst &= ~(0x01<<pos);
457                                         *iDst |= ((i & 0x01)<<pos);
458                                 }
459                         }
460                         //write AND/XOR masks
461                         hFile->Write(mask,masksize,1);
462                         free(mask);
463                 }
464         }
465
466         return true;
467 }
468 ////////////////////////////////////////////////////////////////////////////////
469 #endif // CXIMAGE_SUPPORT_ENCODE
470 ////////////////////////////////////////////////////////////////////////////////
471 #endif // CXIMAGE_SUPPORT_ICO
472