]> Creatis software - clitk.git/blob - utilities/CxImage/ximagif.cpp
Merge branch 'master' of git.creatis.insa-lyon.fr:clitk
[clitk.git] / utilities / CxImage / ximagif.cpp
1 /*\r
2  * File:        ximagif.cpp\r
3  * Purpose:     Platform Independent GIF Image Class Loader and Writer\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 "ximagif.h"\r
9 \r
10 #if CXIMAGE_SUPPORT_GIF\r
11 \r
12 #include "ximaiter.h"\r
13 \r
14 #if defined (_WIN32_WCE)\r
15         #define assert(s)\r
16 #else\r
17         #include <assert.h>\r
18 #endif\r
19 \r
20 ////////////////////////////////////////////////////////////////////////////////\r
21 #if CXIMAGE_SUPPORT_DECODE\r
22 ////////////////////////////////////////////////////////////////////////////////\r
23 bool CxImageGIF::Decode(CxFile *fp)\r
24 {\r
25         /* AD - for transparency */\r
26         struct_dscgif dscgif;\r
27         struct_image image;\r
28         struct_TabCol TabCol;\r
29 \r
30         if (fp == NULL) return false;\r
31 \r
32         fp->Read(&dscgif,/*sizeof(dscgif)*/13,1);\r
33         //if (strncmp(dscgif.header,"GIF8",3)!=0) {\r
34         if (strncmp(dscgif.header,"GIF8",4)!=0) return FALSE;\r
35 \r
36         // Avoid Byte order problem with Mac <AMSN>\r
37         dscgif.scrheight = ntohs(dscgif.scrheight);\r
38         dscgif.scrwidth = ntohs(dscgif.scrwidth);\r
39 \r
40         if (info.nEscape == -1) {\r
41                 // Return output dimensions only\r
42                 head.biWidth = dscgif.scrwidth;\r
43                 head.biHeight = dscgif.scrheight;\r
44                 info.dwType = CXIMAGE_FORMAT_GIF;\r
45                 return true;\r
46         }\r
47 \r
48         /* AD - for interlace */\r
49         TabCol.sogct = (short)(1 << ((dscgif.pflds & 0x07)+1));\r
50         TabCol.colres = (short)(((dscgif.pflds & 0x70) >> 4) + 1);\r
51 \r
52         // assume that the image is a truecolor-gif if\r
53         // 1) no global color map found\r
54         // 2) (image.w, image.h) of the 1st image != (dscgif.scrwidth, dscgif.scrheight)\r
55         long bTrueColor=0;\r
56         CxImage* imaRGB=NULL;\r
57 \r
58         // Global colour map?\r
59         if (dscgif.pflds & 0x80)\r
60                 fp->Read(TabCol.paleta,sizeof(struct rgb_color)*TabCol.sogct,1);\r
61         else \r
62                 bTrueColor++;   //first chance for a truecolor gif\r
63 \r
64         long first_transparent_index = 0;\r
65 \r
66         int iImage = 0;\r
67         info.nNumFrames=get_num_frames(fp,&TabCol,&dscgif);\r
68 \r
69         if ((info.nFrame<0)||(info.nFrame>=info.nNumFrames)) return false;\r
70 \r
71         //it cannot be a true color GIF with only one frame\r
72         if (info.nNumFrames == 1)\r
73                 bTrueColor=0;\r
74 \r
75         char ch;\r
76         bool bPreviousWasNull = true;\r
77         int  prevdispmeth = 0;\r
78         CxImage *previousFrame = NULL;\r
79 \r
80         for (BOOL bContinue = TRUE; bContinue; )\r
81         {\r
82                 if (fp->Read(&ch, sizeof(ch), 1) != 1) {break;}\r
83 \r
84                 if (info.nEscape > 0) return false; // <vho> - cancel decoding\r
85                 if (bPreviousWasNull || ch==0)\r
86                 {\r
87                         switch (ch)\r
88                         {\r
89                         case '!': // extension\r
90                                 {\r
91                                 bContinue = DecodeExtension(fp);\r
92                                 break;\r
93                                 }\r
94                         case ',': // image\r
95                                 {\r
96                                 assert(sizeof(image) == 9);\r
97                                 fp->Read(&image,sizeof(image),1);\r
98                                 //avoid byte order problems with Solaris <candan> <AMSN>\r
99                                 image.l = ntohs(image.l);\r
100                                 image.t = ntohs(image.t);\r
101                                 image.w = ntohs(image.w);\r
102                                 image.h = ntohs(image.h);\r
103 \r
104                                 if (((image.l + image.w) > dscgif.scrwidth)||((image.t + image.h) > dscgif.scrheight))\r
105                                         break;\r
106 \r
107                                 // check if it could be a truecolor gif\r
108                                 if ((iImage==0) && (image.w != dscgif.scrwidth) && (image.h != dscgif.scrheight))\r
109                                         bTrueColor++;\r
110 \r
111                                 rgb_color  locpal[256];                         //Local Palette \r
112                                 rgb_color* pcurpal = TabCol.paleta;     //Current Palette \r
113                                 short palcount = TabCol.sogct;          //Current Palette color count  \r
114 \r
115                                 // Local colour map?\r
116                                 if (image.pf & 0x80) {\r
117                                         palcount = (short)(1 << ((image.pf & 0x07) +1));\r
118                                         assert(3 == sizeof(struct rgb_color));\r
119                                         fp->Read(locpal,sizeof(struct rgb_color)*palcount,1);\r
120                                         pcurpal = locpal;\r
121                                 }\r
122 \r
123                                 int bpp; //<DP> select the correct bit per pixel value\r
124                                 if              (palcount <= 2)  bpp = 1;\r
125                                 else if (palcount <= 16) bpp = 4;\r
126                                 else                                     bpp = 8;\r
127 \r
128                                 CxImageGIF backimage;\r
129                                 backimage.CopyInfo(*this);\r
130                                 if (iImage==0){\r
131                                         //first frame: build image background\r
132                                         backimage.Create(dscgif.scrwidth, dscgif.scrheight, bpp, CXIMAGE_FORMAT_GIF);\r
133                                         first_transparent_index = info.nBkgndIndex;\r
134                                         backimage.Clear((BYTE)gifgce.transpcolindex);\r
135                                         previousFrame = new CxImage(backimage);\r
136                                         previousFrame->SetRetreiveAllFrames(false);\r
137                                 } else {\r
138                                 //generic frame: handle disposal method from previous one\r
139                                 /*Values :  0 -   No disposal specified. The decoder is\r
140                                                                   not required to take any action.\r
141                                                         1 -   Do not dispose. The graphic is to be left\r
142                                                                   in place.\r
143                                                         2 -   Restore to background color. The area used by the\r
144                                                                   graphic must be restored to the background color.\r
145                                                         3 -   Restore to previous. The decoder is required to\r
146                                                                   restore the area overwritten by the graphic with\r
147                                                                   what was there prior to rendering the graphic.\r
148                                 */\r
149                                 /*      backimage.Copy(*this);\r
150                                         if (prevdispmeth==2){\r
151                                                 backimage.Clear((BYTE)first_transparent_index);\r
152                                         }*/\r
153                                         if (prevdispmeth==2){\r
154                                                 backimage.Copy(*this,false,false,false);\r
155                                                 backimage.Clear((BYTE)first_transparent_index);\r
156                                         } else if (prevdispmeth==3) {\r
157                                                 backimage.Copy(*this,false,false,false);\r
158                                                 backimage.Create(previousFrame->GetWidth(),\r
159                                                         previousFrame->GetHeight(),\r
160                                                         previousFrame->GetBpp(),CXIMAGE_FORMAT_GIF);\r
161                                                 memcpy(backimage.GetDIB(),previousFrame->GetDIB(),\r
162                                                         backimage.GetSize());\r
163                                                 backimage.AlphaSet(*previousFrame);\r
164                                         } else {\r
165                                                 backimage.Copy(*this);\r
166                                         }\r
167                                 }\r
168 \r
169                                 //active frame\r
170                                 Create(image.w, image.h, bpp, CXIMAGE_FORMAT_GIF);\r
171 \r
172                                 if ((image.pf & 0x80) || (dscgif.pflds & 0x80)) {\r
173                                         unsigned char r[256], g[256], b[256];\r
174                                         int i, has_white = 0;\r
175 \r
176                                         for (i=0; i < palcount; i++) {\r
177                                                 r[i] = pcurpal[i].r;\r
178                                                 g[i] = pcurpal[i].g;\r
179                                                 b[i] = pcurpal[i].b;\r
180                                                 if (RGB(r[i],g[i],b[i]) == 0xFFFFFF) has_white = 1;\r
181                                         }\r
182 \r
183                                         // Force transparency colour white...\r
184                                         //if (0) if (info.nBkgndIndex >= 0)\r
185                                         //      r[info.nBkgndIndex] = g[info.nBkgndIndex] = b[info.nBkgndIndex] = 255;\r
186                                         // Fill in with white // AD\r
187                                         if (info.nBkgndIndex >= 0) {\r
188                                                 while (i < 256) {\r
189                                                         has_white = 1;\r
190                                                         r[i] = g[i] = b[i] = 255;\r
191                                                         i++;\r
192                                                 }\r
193                                         }\r
194 \r
195                                         // Force last colour to white...   // AD\r
196                                         //if ((info.nBkgndIndex >= 0) && !has_white) {\r
197                                         //      r[255] = g[255] = b[255] = 255;\r
198                                         //}\r
199 \r
200                                         SetPalette((info.nBkgndIndex >= 0 ? 256 : palcount), r, g, b);\r
201                                 }\r
202 \r
203                                 CImageIterator* iter = new CImageIterator(this);\r
204                                 iter->Upset();\r
205                                 int badcode=0;\r
206                                 ibf = GIFBUFTAM+1;\r
207 \r
208                                 interlaced = image.pf & 0x40;\r
209                                 iheight = image.h;\r
210                                 istep = 8;\r
211                                 iypos = 0;\r
212                                 ipass = 0;\r
213 \r
214                                 long pos_start = fp->Tell();\r
215                                 //if (interlaced) log << "Interlaced" << endl;\r
216                                 decoder(fp, iter, image.w, badcode);\r
217                                 delete iter;\r
218 \r
219                                 if (info.nEscape) return false; // <vho> - cancel decoding\r
220 \r
221                                 if (bTrueColor<2 ){ //standard GIF: mix frame with background\r
222                                         backimage.GifMix(*this,image);\r
223                                         backimage.SetTransIndex(first_transparent_index);\r
224                                         backimage.SetPalette(GetPalette());\r
225                                         Transfer(backimage,false);\r
226                                 } else { //it's a truecolor gif!\r
227                                         //force full image decoding\r
228                                         info.nFrame=info.nNumFrames-1;\r
229                                         //build the RGB image\r
230                                         if (imaRGB==NULL) imaRGB = new CxImage(dscgif.scrwidth,dscgif.scrheight,24,CXIMAGE_FORMAT_GIF);\r
231                                         //copy the partial image into the full RGB image\r
232                                         for(long y=0;y<image.h;y++){\r
233                                                 for (long x=0;x<image.w;x++){\r
234                                                         imaRGB->SetPixelColor(x+image.l,dscgif.scrheight-1-image.t-y,GetPixelColor(x,image.h-y-1));\r
235                                                 }\r
236                                         }\r
237                                 }\r
238 \r
239                                 prevdispmeth = (gifgce.flags >> 2) & 0x7;\r
240 \r
241                                 //restore the correct position in the file for the next image\r
242                                 if (badcode){\r
243                                         seek_next_image(fp,pos_start);\r
244                                 } else {\r
245                                         fp->Seek(-(ibfmax - ibf - 1), SEEK_CUR);\r
246                                 }\r
247 \r
248                                 if (info.bGetAllFrames && imaRGB == NULL) {\r
249                                         if (iImage == 0) {\r
250                                                 DestroyFrames();\r
251                                                 ppFrames = new CxImage*[info.nNumFrames];\r
252                                                 for(int frameIdx = 0; frameIdx < info.nNumFrames; frameIdx++){\r
253                                                         ppFrames[frameIdx] = NULL;\r
254                                                 }\r
255                                         }\r
256                                         ppFrames[iImage] = new CxImage(*this);\r
257                                         ppFrames[iImage]->SetRetreiveAllFrames(false);\r
258                                 }\r
259                                 if (prevdispmeth <= 1) {\r
260                                         delete previousFrame;\r
261                                         previousFrame = new CxImage(*this);\r
262                                         previousFrame->SetRetreiveAllFrames(false);\r
263                                 }\r
264 \r
265                                 if (info.nFrame==iImage) bContinue=false; else iImage++;\r
266 \r
267                                 break;\r
268                                 }\r
269                         case ';': //terminator\r
270                                 bContinue=false;\r
271                                 break;\r
272                         default:\r
273                                 bPreviousWasNull = (ch==0);\r
274                                 break;\r
275                         }\r
276                 }\r
277         }\r
278 \r
279         if (bTrueColor>=2 && imaRGB){\r
280                 if (gifgce.flags & 0x1){\r
281                         imaRGB->SetTransColor(GetPaletteColor((BYTE)info.nBkgndIndex));\r
282                         imaRGB->SetTransIndex(0);\r
283                 }\r
284                 Transfer(*imaRGB);\r
285         }\r
286         delete imaRGB;\r
287 \r
288         delete previousFrame;\r
289 \r
290         return true;\r
291 \r
292 }\r
293 ////////////////////////////////////////////////////////////////////////////////\r
294 bool CxImageGIF::DecodeExtension(CxFile *fp)\r
295 {\r
296         bool bContinue;\r
297         unsigned char count;\r
298         unsigned char fc;\r
299 \r
300         bContinue = (1 == fp->Read(&fc, sizeof(fc), 1));\r
301         if (bContinue) {\r
302                 /* AD - for transparency */\r
303                 if (fc == 0xF9) {\r
304                         bContinue = (1 == fp->Read(&count, sizeof(count), 1));\r
305                         if (bContinue) {\r
306                                 assert(sizeof(gifgce) == 4);\r
307                                 bContinue = (count == fp->Read(&gifgce, 1, sizeof(gifgce)));\r
308                                 gifgce.delaytime = ntohs(gifgce.delaytime); // Avoid Byte order problem with Mac <AMSN>\r
309                                 if (bContinue) {\r
310                                         info.nBkgndIndex  = (gifgce.flags & 0x1) ? gifgce.transpcolindex : -1;\r
311                                         info.dwFrameDelay = gifgce.delaytime;\r
312                                         SetDisposalMethod((gifgce.flags >> 2) & 0x7);\r
313                 }       }       }\r
314 \r
315                 if (fc == 0xFE) { //<DP> Comment block\r
316                         bContinue = (1 == fp->Read(&count, sizeof(count), 1));\r
317                         if (bContinue) {\r
318                                 bContinue = (1 == fp->Read(m_comment, count, 1));\r
319                                 m_comment[count]='\0';\r
320                 }       }\r
321 \r
322                 if (fc == 0xFF) { //<DP> Application Extension block\r
323                         bContinue = (1 == fp->Read(&count, sizeof(count), 1));\r
324                         if (bContinue) {\r
325                                 bContinue = (count==11);\r
326                                 if (bContinue){\r
327                                         char AppID[11];\r
328                                         bContinue = (1 == fp->Read(AppID, count, 1));\r
329                                         if (bContinue) {\r
330                                                 bContinue = (1 == fp->Read(&count, sizeof(count), 1));\r
331                                                 if (bContinue) {\r
332                                                         BYTE* dati = (BYTE*)malloc(count);\r
333                                                         bContinue = (dati!=NULL);\r
334                                                         if (bContinue){\r
335                                                                 bContinue = (1 == fp->Read(dati, count, 1));\r
336                                                                 if (count>2){\r
337                                                                         m_loops = dati[1]+256*dati[2];\r
338                                                                 }\r
339                                                         }\r
340                                                         free(dati);\r
341                 }       }       }       }       }\r
342 \r
343                 while (bContinue && fp->Read(&count, sizeof(count), 1) && count) {\r
344                         //log << "Skipping " << count << " bytes" << endl;\r
345                         fp->Seek(count, SEEK_CUR);\r
346                 }\r
347         }\r
348         return bContinue;\r
349 \r
350 }\r
351 ////////////////////////////////////////////////////////////////////////////////\r
352 #endif //CXIMAGE_SUPPORT_DECODE\r
353 ////////////////////////////////////////////////////////////////////////////////\r
354 \r
355 //   - This external (machine specific) function is expected to return\r
356 // either the next BYTE from the GIF file, or a negative error number.\r
357 int CxImageGIF::get_byte(CxFile* file)\r
358 {\r
359         if (ibf>=GIFBUFTAM){\r
360                 // FW 06/02/98 >>>\r
361                 ibfmax = (int)file->Read( buf , 1 , GIFBUFTAM) ;\r
362                 if( ibfmax < GIFBUFTAM ) buf[ ibfmax ] = 255 ;\r
363                 // FW 06/02/98 <<<\r
364                 ibf = 0;\r
365         }\r
366         if (ibf>=ibfmax) return -1; //<DP> avoid overflows\r
367         return buf[ibf++];\r
368 }\r
369 ////////////////////////////////////////////////////////////////////////////////\r
370 /*   - This function takes a full line of pixels (one BYTE per pixel) and\r
371  * displays them (or does whatever your program wants with them...).  It\r
372  * should return zero, or negative if an error or some other event occurs\r
373  * which would require aborting the decode process...  Note that the length\r
374  * passed will almost always be equal to the line length passed to the\r
375  * decoder function, with the sole exception occurring when an ending code\r
376  * occurs in an odd place in the GIF file...  In any case, linelen will be\r
377  * equal to the number of pixels passed...\r
378 */\r
379 int CxImageGIF::out_line(CImageIterator* iter, unsigned char *pixels, int linelen)\r
380 {\r
381         if (iter == NULL || pixels == NULL)\r
382                 return -1;\r
383 \r
384         //<DP> for 1 & 4 bpp images, the pixels are compressed\r
385         if (head.biBitCount < 8){\r
386                 for(long x=0;x<head.biWidth;x++){\r
387                         BYTE pos;\r
388                         BYTE* iDst= pixels + (x*head.biBitCount >> 3);\r
389                         if (head.biBitCount==4){\r
390                                 pos = (BYTE)(4*(1-x%2));\r
391                                 *iDst &= ~(0x0F<<pos);\r
392                                 *iDst |= ((pixels[x] & 0x0F)<<pos);\r
393                         } else if (head.biBitCount==1){\r
394                                 pos = (BYTE)(7-x%8);\r
395                                 *iDst &= ~(0x01<<pos);\r
396                                 *iDst |= ((pixels[x] & 0x01)<<pos);\r
397                         }\r
398                 }\r
399         }\r
400 \r
401         /* AD - for interlace */\r
402         if (interlaced) {\r
403                 iter->SetY(iheight-iypos-1);\r
404                 iter->SetRow(pixels, linelen);\r
405 \r
406                 if ((iypos += istep) >= iheight) {\r
407                         do {\r
408                                 if (ipass++ > 0) istep /= 2;\r
409                                 iypos = istep / 2;\r
410                         }\r
411                         while (iypos > iheight);\r
412                 }\r
413                 return 0;\r
414         } else {\r
415                 if (iter->ItOK()) {\r
416                         iter->SetRow(pixels, linelen);\r
417                         (void)iter->PrevRow();\r
418                         return 0;\r
419                 } else {\r
420                         //       puts("chafeo");\r
421                         return -1;\r
422                 }\r
423         }\r
424 }\r
425 ////////////////////////////////////////////////////////////////////////////////\r
426 #if CXIMAGE_SUPPORT_ENCODE\r
427 ////////////////////////////////////////////////////////////////////////////////\r
428 // SaveFile - writes GIF87a gif file\r
429 // Randy Spann 6/15/97\r
430 // R.Spann@ConnRiver.net\r
431 bool CxImageGIF::Encode(CxFile * fp)\r
432 {\r
433         if (EncodeSafeCheck(fp)) return false;\r
434 \r
435         if(head.biBitCount > 8) {\r
436                 //strcpy(info.szLastError,"GIF Images must be 8 bit or less");\r
437                 //return FALSE;\r
438                 return EncodeRGB(fp);\r
439         }\r
440 \r
441         if ( GetNumFrames()>1 && ppFrames ) {\r
442                 return Encode(fp, ppFrames, GetNumFrames() );\r
443         }\r
444 \r
445         EncodeHeader(fp);\r
446 \r
447         EncodeExtension(fp);\r
448 \r
449         EncodeComment(fp);\r
450 \r
451         EncodeBody(fp);\r
452 \r
453         fp->PutC(';'); // Write the GIF file terminator\r
454 \r
455         return true; // done!\r
456 }\r
457 ////////////////////////////////////////////////////////////////////////////////\r
458 bool CxImageGIF::Encode(CxFile * fp, CxImage ** pImages, int pagecount, bool bLocalColorMap, bool bLocalDispMeth)\r
459 {\r
460   cx_try {\r
461         if (fp==NULL) cx_throw("invalid file pointer");\r
462         if (pImages==NULL || pagecount<=0 || pImages[0]==NULL) cx_throw("multipage GIF, no images!");\r
463 \r
464         int i;\r
465         for (i=0; i<pagecount; i++){\r
466                 if (pImages[i]==NULL)\r
467                         cx_throw("Bad image pointer");\r
468                 if (!(pImages[i]->IsValid()))\r
469                         cx_throw("Empty image");\r
470                 if (pImages[i]->GetNumColors()==0)\r
471                         cx_throw("CxImageGIF::Encode cannot create animated GIFs with a true color frame. Use DecreaseBpp before");\r
472         }\r
473 \r
474         CxImageGIF ghost;\r
475 \r
476         //write the first image\r
477         ghost.Ghost(pImages[0]);\r
478         ghost.EncodeHeader(fp);\r
479 \r
480         if (m_loops!=1){\r
481                 ghost.SetLoops(max(0,m_loops-1));\r
482                 ghost.EncodeLoopExtension(fp);\r
483         }\r
484 \r
485         if (bLocalDispMeth) {\r
486                 ghost.EncodeExtension(fp);\r
487         } else {\r
488                 BYTE dm = ghost.GetDisposalMethod();\r
489                 ghost.SetDisposalMethod(GetDisposalMethod());\r
490                 ghost.EncodeExtension(fp);\r
491                 ghost.SetDisposalMethod(dm);\r
492         }\r
493 \r
494         EncodeComment(fp);\r
495 \r
496         ghost.EncodeBody(fp);\r
497 \r
498         for (i=1; i<pagecount; i++){\r
499                 ghost.Ghost(pImages[i]);\r
500 \r
501                 if (bLocalDispMeth) {\r
502                         ghost.EncodeExtension(fp);\r
503                 } else {\r
504                         BYTE dm = ghost.GetDisposalMethod();\r
505                         ghost.SetDisposalMethod(GetDisposalMethod());\r
506                         ghost.EncodeExtension(fp);\r
507                         ghost.SetDisposalMethod(dm);\r
508                 }\r
509 \r
510                 ghost.EncodeBody(fp,bLocalColorMap);\r
511         }\r
512 \r
513         fp->PutC(';'); // Write the GIF file terminator\r
514 \r
515   } cx_catch {\r
516           if (strcmp(message,"")) strncpy(info.szLastError,message,255);\r
517           return false;\r
518   }\r
519         return true;\r
520 }\r
521 ////////////////////////////////////////////////////////////////////////////////\r
522 void CxImageGIF::EncodeHeader(CxFile *fp)\r
523 {\r
524         fp->Write("GIF89a",1,6);           //GIF Header\r
525 \r
526         Putword(head.biWidth,fp);                          //Logical screen descriptor\r
527         Putword(head.biHeight,fp);\r
528 \r
529         BYTE Flags;\r
530         if (head.biClrUsed==0){\r
531                 Flags=0x11;\r
532         } else {\r
533                 Flags = 0x80;\r
534                 Flags |=(head.biBitCount - 1) << 5;\r
535                 Flags |=(head.biBitCount - 1);\r
536         }\r
537 \r
538         fp->PutC(Flags); //GIF "packed fields"\r
539         fp->PutC(0);     //GIF "BackGround"\r
540         fp->PutC(0);     //GIF "pixel aspect ratio"\r
541 \r
542         if (head.biClrUsed!=0){\r
543                 RGBQUAD* pPal = GetPalette();\r
544                 for(DWORD i=0; i<head.biClrUsed; ++i) \r
545                 {\r
546                         fp->PutC(pPal[i].rgbRed);\r
547                         fp->PutC(pPal[i].rgbGreen);\r
548                         fp->PutC(pPal[i].rgbBlue);\r
549                 }\r
550         }\r
551 }\r
552 ////////////////////////////////////////////////////////////////////////////////\r
553 void CxImageGIF::EncodeExtension(CxFile *fp)\r
554 {\r
555         // TRK BEGIN : transparency\r
556         fp->PutC('!');\r
557         fp->PutC(TRANSPARENCY_CODE);\r
558 \r
559         gifgce.flags = 0;\r
560         gifgce.flags |= ((info.nBkgndIndex != -1) ? 1 : 0);\r
561         gifgce.flags |= ((GetDisposalMethod() & 0x7) << 2);\r
562         gifgce.delaytime = (WORD)info.dwFrameDelay;\r
563         gifgce.transpcolindex = (BYTE)info.nBkgndIndex;    \r
564 \r
565         //Invert byte order in case we use a byte order arch, then set it back <AMSN>\r
566         gifgce.delaytime = ntohs(gifgce.delaytime);\r
567         fp->PutC(sizeof(gifgce));\r
568         fp->Write(&gifgce, sizeof(gifgce), 1);\r
569         gifgce.delaytime = ntohs(gifgce.delaytime);\r
570 \r
571         fp->PutC(0);\r
572         // TRK END\r
573 }\r
574 ////////////////////////////////////////////////////////////////////////////////\r
575 void CxImageGIF::EncodeLoopExtension(CxFile *fp)\r
576 {\r
577         fp->PutC('!');          //byte  1  : 33 (hex 0x21) GIF Extension code\r
578         fp->PutC(255);          //byte  2  : 255 (hex 0xFF) Application Extension Label\r
579         fp->PutC(11);           //byte  3  : 11 (hex (0x0B) Length of Application Block (eleven bytes of data to follow)\r
580         fp->Write("NETSCAPE2.0",11,1);\r
581         fp->PutC(3);                    //byte 15  : 3 (hex 0x03) Length of Data Sub-Block (three bytes of data to follow)\r
582         fp->PutC(1);                    //byte 16  : 1 (hex 0x01)\r
583         Putword(m_loops,fp); //bytes 17 to 18 : 0 to 65535, an unsigned integer in lo-hi byte format. \r
584                                                 //This indicate the number of iterations the loop should be executed.\r
585         fp->PutC(0);                    //bytes 19       : 0 (hex 0x00) a Data Sub-block Terminator. \r
586 }\r
587 ////////////////////////////////////////////////////////////////////////////////\r
588 void CxImageGIF::EncodeBody(CxFile *fp, bool bLocalColorMap)\r
589 {\r
590         curx = 0;\r
591         cury = head.biHeight - 1;       //because we read the image bottom to top\r
592         CountDown = (long)head.biWidth * (long)head.biHeight;\r
593 \r
594         fp->PutC(',');\r
595 \r
596         Putword(info.xOffset,fp);\r
597         Putword(info.yOffset,fp);\r
598         Putword(head.biWidth,fp);\r
599         Putword(head.biHeight,fp);\r
600 \r
601         BYTE Flags=0x00; //non-interlaced (0x40 = interlaced) (0x80 = LocalColorMap)\r
602         if (bLocalColorMap)     { Flags|=0x80; Flags|=head.biBitCount-1; }\r
603         fp->PutC(Flags);\r
604 \r
605         if (bLocalColorMap){\r
606                 Flags|=0x87;\r
607                 RGBQUAD* pPal = GetPalette();\r
608                 for(DWORD i=0; i<head.biClrUsed; ++i) \r
609                 {\r
610                         fp->PutC(pPal[i].rgbRed);\r
611                         fp->PutC(pPal[i].rgbGreen);\r
612                         fp->PutC(pPal[i].rgbBlue);\r
613                 }\r
614         }\r
615 \r
616         int InitCodeSize = head.biBitCount <=1 ? 2 : head.biBitCount;\r
617          // Write out the initial code size\r
618         fp->PutC((BYTE)InitCodeSize);\r
619 \r
620          // Go and actually compress the data\r
621         switch (GetCodecOption(CXIMAGE_FORMAT_GIF))\r
622         {\r
623         case 1: //uncompressed\r
624                 compressNONE(InitCodeSize+1, fp);\r
625                 break;\r
626         case 2: //RLE\r
627                 compressRLE(InitCodeSize+1, fp);\r
628                 break;\r
629         default: //LZW\r
630                 compressLZW(InitCodeSize+1, fp);\r
631         }\r
632 \r
633          // Write out a Zero-length packet (to end the series)\r
634         fp->PutC(0);\r
635 }\r
636 ////////////////////////////////////////////////////////////////////////////////\r
637 void CxImageGIF::EncodeComment(CxFile *fp)\r
638 {\r
639         unsigned long n = (unsigned long) strlen(m_comment);\r
640         if (n>255) n=255;\r
641         if (n) {\r
642                 fp->PutC('!');  //extension code:\r
643                 fp->PutC(254);  //comment extension\r
644                 fp->PutC((BYTE)n);      //size of comment\r
645                 fp->Write(m_comment,n,1);\r
646                 fp->PutC(0);    //block terminator\r
647         }\r
648 }\r
649 ////////////////////////////////////////////////////////////////////////////////\r
650 bool CxImageGIF::EncodeRGB(CxFile *fp)\r
651 {\r
652         EncodeHeader(fp);\r
653 \r
654 //      EncodeLoopExtension(fp);\r
655 \r
656         EncodeComment(fp);\r
657 \r
658         unsigned long w,h;\r
659         w=h=0;\r
660         const long cellw = 17;\r
661         const long cellh = 15;\r
662         CxImageGIF tmp;\r
663         for (long y=0;y<head.biHeight;y+=cellh){\r
664                 for (long x=0;x<head.biWidth;x+=cellw){\r
665                         if ((head.biWidth -x)<cellw) w=head.biWidth -x; else w=cellw;\r
666                         if ((head.biHeight-y)<cellh) h=head.biHeight-y; else h=cellh;\r
667 \r
668                         if (w!=tmp.GetWidth() || h!=tmp.GetHeight()) tmp.Create(w,h,8);\r
669 \r
670                         if (IsTransparent()){\r
671                                 tmp.SetTransIndex(0);\r
672                                 tmp.SetPaletteColor(0,GetTransColor());\r
673                         }\r
674 \r
675                         BYTE i;\r
676                         for (unsigned long j=0;j<h;j++){\r
677                                 for (unsigned long k=0;k<w;k++){\r
678                                         i=(BYTE)(1+k+cellw*j);\r
679                                         tmp.SetPaletteColor(i,GetPixelColor(x+k,head.biHeight-y-h+j));\r
680                                         tmp.SetPixelIndex(k,j,tmp.GetNearestIndex(tmp.GetPaletteColor(i)));\r
681                                 }\r
682                         }\r
683 \r
684                         tmp.SetOffset(x,y);\r
685                         tmp.EncodeExtension(fp);\r
686                         tmp.EncodeBody(fp,true);\r
687                 }\r
688         }\r
689 \r
690         fp->PutC(';'); // Write the GIF file terminator\r
691 \r
692         return true; // done!\r
693 }\r
694 ////////////////////////////////////////////////////////////////////////////////\r
695 #endif // CXIMAGE_SUPPORT_ENCODE\r
696 ////////////////////////////////////////////////////////////////////////////////\r
697 // Return the next pixel from the image\r
698 // <DP> fix for 1 & 4 bpp images\r
699 int CxImageGIF::GifNextPixel( )\r
700 {\r
701         if( CountDown == 0 ) return EOF;\r
702         --CountDown;\r
703         int r = GetPixelIndex(curx,cury);\r
704         // Bump the current X position\r
705         ++curx;\r
706         if( curx == head.biWidth ){\r
707                 curx = 0;\r
708                 cury--;              //bottom to top\r
709         }\r
710         return r;\r
711 }\r
712 ////////////////////////////////////////////////////////////////////////////////\r
713 void CxImageGIF::Putword(int w, CxFile *fp )\r
714 {\r
715         fp->PutC((BYTE)(w & 0xff));\r
716         fp->PutC((BYTE)((w >> 8) & 0xff));\r
717 }\r
718 ////////////////////////////////////////////////////////////////////////////////\r
719 void CxImageGIF::compressNONE( int init_bits, CxFile* outfile)\r
720 {\r
721         register long c;\r
722         register long ent;\r
723 \r
724         // g_init_bits - initial number of bits\r
725         // g_outfile   - pointer to output file\r
726         g_init_bits = init_bits;\r
727         g_outfile = outfile;\r
728 \r
729          // Set up the necessary values\r
730         cur_accum = cur_bits = clear_flg = 0;\r
731         maxcode = (short)MAXCODE(n_bits = g_init_bits);\r
732         code_int maxmaxcode = (code_int)1 << MAXBITSCODES;\r
733 \r
734         ClearCode = (1 << (init_bits - 1));\r
735         EOFCode = ClearCode + 1;\r
736         free_ent = (short)(ClearCode + 2);\r
737 \r
738         a_count=0;\r
739         ent = GifNextPixel( );\r
740 \r
741         output( (code_int)ClearCode );\r
742 \r
743         while ( ent != EOF ) {    \r
744                 c = GifNextPixel();\r
745 \r
746                 output ( (code_int) ent );\r
747                 ent = c;\r
748                 if ( free_ent < maxmaxcode ) {  \r
749                         free_ent++;\r
750                 } else {\r
751                         free_ent=(short)(ClearCode+2);\r
752                         clear_flg=1;\r
753                         output((code_int)ClearCode);\r
754                 }\r
755         }\r
756          // Put out the final code.\r
757         output( (code_int) EOFCode );\r
758 }\r
759 ////////////////////////////////////////////////////////////////////////////////\r
760 \r
761 /***************************************************************************\r
762  *\r
763  *  GIFCOMPR.C       -     LZW GIF Image compression routines\r
764  *\r
765  ***************************************************************************/\r
766 \r
767 void CxImageGIF::compressLZW( int init_bits, CxFile* outfile)\r
768 {\r
769         register long fcode;\r
770         register long c;\r
771         register long ent;\r
772         register long hshift;\r
773         register long disp;\r
774         register long i;\r
775 \r
776         // g_init_bits - initial number of bits\r
777         // g_outfile   - pointer to output file\r
778         g_init_bits = init_bits;\r
779         g_outfile = outfile;\r
780 \r
781          // Set up the necessary values\r
782         cur_accum = cur_bits = clear_flg = 0;\r
783         maxcode = (short)MAXCODE(n_bits = g_init_bits);\r
784         code_int maxmaxcode = (code_int)1 << MAXBITSCODES;\r
785 \r
786         ClearCode = (1 << (init_bits - 1));\r
787         EOFCode = ClearCode + 1;\r
788         free_ent = (short)(ClearCode + 2);\r
789 \r
790         a_count=0;\r
791         ent = GifNextPixel( );\r
792 \r
793         hshift = 0;\r
794         for ( fcode = (long) HSIZE;  fcode < 65536L; fcode *= 2L )      ++hshift;\r
795         hshift = 8 - hshift;                /* set hash code range bound */\r
796         cl_hash((long)HSIZE);        /* clear hash table */\r
797         output( (code_int)ClearCode );\r
798 \r
799         while ( (c = GifNextPixel( )) != EOF ) {    \r
800 \r
801                 fcode = (long) (((long) c << MAXBITSCODES) + ent);\r
802                 i = (((code_int)c << hshift) ^ ent);    /* xor hashing */\r
803 \r
804                 if ( HashTabOf (i) == fcode ) {\r
805                         ent = CodeTabOf (i);\r
806                         continue;\r
807                 } else if ( (long)HashTabOf (i) < 0 )      /* empty slot */\r
808                         goto nomatch;\r
809                 disp = HSIZE - i;           /* secondary hash (after G. Knott) */\r
810                 if ( i == 0 )   disp = 1;\r
811 probe:\r
812                 if ( (i -= disp) < 0 )  i += HSIZE;\r
813                 if ( HashTabOf (i) == fcode ) { ent = CodeTabOf (i); continue; }\r
814                 if ( (long)HashTabOf (i) > 0 )  goto probe;\r
815 nomatch:\r
816                 output ( (code_int) ent );\r
817                 ent = c;\r
818                 if ( free_ent < maxmaxcode ) {  \r
819                         CodeTabOf (i) = free_ent++; /* code -> hashtable */\r
820                         HashTabOf (i) = fcode;\r
821                 } else {\r
822                         cl_hash((long)HSIZE);\r
823                         free_ent=(short)(ClearCode+2);\r
824                         clear_flg=1;\r
825                         output((code_int)ClearCode);\r
826                 }\r
827         }\r
828          // Put out the final code.\r
829         output( (code_int)ent );\r
830         output( (code_int) EOFCode );\r
831 }\r
832 ////////////////////////////////////////////////////////////////////////////////\r
833 \r
834 static const unsigned long code_mask[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,\r
835                                                                   0x001F, 0x003F, 0x007F, 0x00FF,\r
836                                                                   0x01FF, 0x03FF, 0x07FF, 0x0FFF,\r
837                                                                   0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };\r
838 \r
839 ////////////////////////////////////////////////////////////////////////////////\r
840 void CxImageGIF::output( code_int  code)\r
841 {\r
842         cur_accum &= code_mask[ cur_bits ];\r
843 \r
844         if( cur_bits > 0 )\r
845                 cur_accum |= ((long)code << cur_bits);\r
846         else\r
847                 cur_accum = code;\r
848 \r
849         cur_bits += n_bits;\r
850 \r
851         while( cur_bits >= 8 ) {\r
852                 char_out( (unsigned int)(cur_accum & 0xff) );\r
853                 cur_accum >>= 8;\r
854                 cur_bits -= 8;\r
855         }\r
856 \r
857         /*\r
858          * If the next entry is going to be too big for the code size,\r
859          * then increase it, if possible.\r
860          */\r
861 \r
862         if ( free_ent > maxcode || clear_flg ) {\r
863                 if( clear_flg ) {\r
864                         maxcode = (short)MAXCODE(n_bits = g_init_bits);\r
865                         clear_flg = 0;\r
866                 } else {\r
867                         ++n_bits;\r
868                         if ( n_bits == MAXBITSCODES )\r
869                                 maxcode = (code_int)1 << MAXBITSCODES; /* should NEVER generate this code */\r
870                         else\r
871                                 maxcode = (short)MAXCODE(n_bits);\r
872                 }\r
873         }\r
874         \r
875         if( code == EOFCode ) {\r
876                  // At EOF, write the rest of the buffer.\r
877                 while( cur_bits > 0 ) {\r
878                         char_out( (unsigned int)(cur_accum & 0xff) );\r
879                         cur_accum >>= 8;\r
880                         cur_bits -= 8;\r
881                 }\r
882         \r
883                 flush_char();\r
884                 g_outfile->Flush();\r
885 \r
886                 if(g_outfile->Error()) strcpy(info.szLastError,"Write Error in GIF file");\r
887         }\r
888 }\r
889 ////////////////////////////////////////////////////////////////////////////////\r
890 \r
891 void CxImageGIF::cl_hash(long hsize)\r
892 \r
893 {\r
894         register long *htab_p = htab+hsize;\r
895 \r
896         register long i;\r
897         register long m1 = -1L;\r
898 \r
899         i = hsize - 16;\r
900 \r
901         do {\r
902                 *(htab_p-16)=m1;\r
903                 *(htab_p-15)=m1;\r
904                 *(htab_p-14)=m1;\r
905                 *(htab_p-13)=m1;\r
906                 *(htab_p-12)=m1;\r
907                 *(htab_p-11)=m1;\r
908                 *(htab_p-10)=m1;\r
909                 *(htab_p-9)=m1;\r
910                 *(htab_p-8)=m1;\r
911                 *(htab_p-7)=m1;\r
912                 *(htab_p-6)=m1;\r
913                 *(htab_p-5)=m1;\r
914                 *(htab_p-4)=m1;\r
915                 *(htab_p-3)=m1;\r
916                 *(htab_p-2)=m1;\r
917                 *(htab_p-1)=m1;\r
918                 \r
919                 htab_p-=16;\r
920         } while ((i-=16) >=0);\r
921 \r
922         for (i+=16;i>0;--i)\r
923                 *--htab_p=m1;\r
924 }\r
925 \r
926 /*******************************************************************************\r
927 *   GIF specific\r
928 *******************************************************************************/\r
929 \r
930 void CxImageGIF::char_out(int c)\r
931 {\r
932         accum[a_count++]=(char)c;\r
933         if (a_count >=254)\r
934                 flush_char();\r
935 }\r
936 \r
937 void CxImageGIF::flush_char()\r
938 {\r
939         if (a_count > 0) {\r
940                 g_outfile->PutC((BYTE)a_count);\r
941                 g_outfile->Write(accum,1,a_count);\r
942                 a_count=0;\r
943         }\r
944 }\r
945 \r
946 /*******************************************************************************\r
947 *   GIF decoder\r
948 *******************************************************************************/\r
949 /* DECODE.C - An LZW decoder for GIF\r
950  * Copyright (C) 1987, by Steven A. Bennett\r
951  * Copyright (C) 1994, C++ version by Alejandro Aguilar Sierra\r
952 *\r
953  * Permission is given by the author to freely redistribute and include\r
954  * this code in any program as long as this credit is given where due.\r
955  *\r
956  * In accordance with the above, I want to credit Steve Wilhite who wrote\r
957  * the code which this is heavily inspired by...\r
958  *\r
959  * GIF and 'Graphics Interchange Format' are trademarks (tm) of\r
960  * Compuserve, Incorporated, an H&R Block Company.\r
961  *\r
962  * Release Notes: This file contains a decoder routine for GIF images\r
963  * which is similar, structurally, to the original routine by Steve Wilhite.\r
964  * It is, however, somewhat noticably faster in most cases.\r
965  *\r
966  */\r
967 \r
968 ////////////////////////////////////////////////////////////////////////////////\r
969 \r
970 short CxImageGIF::init_exp(short size)\r
971 {\r
972         curr_size = (short)(size + 1);\r
973         top_slot = (short)(1 << curr_size);\r
974         clear = (short)(1 << size);\r
975         ending = (short)(clear + 1);\r
976         slot = newcodes = (short)(ending + 1);\r
977         navail_bytes = nbits_left = 0;\r
978 \r
979         memset(stack,0,MAX_CODES + 1);\r
980         memset(prefix,0,MAX_CODES + 1);\r
981         memset(suffix,0,MAX_CODES + 1);\r
982         return(0);\r
983 }\r
984 ////////////////////////////////////////////////////////////////////////////////\r
985 \r
986 /* get_next_code()\r
987  * - gets the next code from the GIF file.  Returns the code, or else\r
988  * a negative number in case of file errors...\r
989  */\r
990 short CxImageGIF::get_next_code(CxFile* file)\r
991 {\r
992         short i, x;\r
993         DWORD ret;\r
994 \r
995         if (nbits_left == 0) {\r
996                 if (navail_bytes <= 0) {\r
997                         /* Out of bytes in current block, so read next block */\r
998                         pbytes = byte_buff;\r
999                         if ((navail_bytes = (short)get_byte(file)) < 0)\r
1000                                 return(navail_bytes);\r
1001                         else if (navail_bytes) {\r
1002                                 for (i = 0; i < navail_bytes; ++i) {\r
1003                                         if ((x = (short)get_byte(file)) < 0) return(x);\r
1004                                         byte_buff[i] = (BYTE)x;\r
1005                                 }\r
1006                         }\r
1007                 }\r
1008                 b1 = *pbytes++;\r
1009                 nbits_left = 8;\r
1010                 --navail_bytes;\r
1011         }\r
1012 \r
1013         if (navail_bytes<0) return ending; // prevent deadlocks (thanks to Mike Melnikov)\r
1014 \r
1015         ret = b1 >> (8 - nbits_left);\r
1016         while (curr_size > nbits_left){\r
1017                 if (navail_bytes <= 0){\r
1018                         /* Out of bytes in current block, so read next block*/\r
1019                         pbytes = byte_buff;\r
1020                         if ((navail_bytes = (short)get_byte(file)) < 0)\r
1021                                 return(navail_bytes);\r
1022                         else if (navail_bytes){\r
1023                                 for (i = 0; i < navail_bytes; ++i){\r
1024                                         if ((x = (short)get_byte(file)) < 0) return(x);\r
1025                                         byte_buff[i] = (BYTE)x;\r
1026                                 }\r
1027                         }\r
1028                 }\r
1029                 b1 = *pbytes++;\r
1030                 ret |= b1 << nbits_left;\r
1031                 nbits_left += 8;\r
1032                 --navail_bytes;\r
1033         }\r
1034         nbits_left = (short)(nbits_left-curr_size);\r
1035         ret &= code_mask[curr_size];\r
1036         return((short)(ret));\r
1037 }\r
1038 ////////////////////////////////////////////////////////////////////////////////\r
1039 \r
1040 /* short decoder(linewidth)\r
1041  *    short linewidth;               * Pixels per line of image *\r
1042  *\r
1043  * - This function decodes an LZW image, according to the method used\r
1044  * in the GIF spec.  Every *linewidth* "characters" (ie. pixels) decoded\r
1045  * will generate a call to out_line(), which is a user specific function\r
1046  * to display a line of pixels.  The function gets it's codes from\r
1047  * get_next_code() which is responsible for reading blocks of data and\r
1048  * seperating them into the proper size codes.  Finally, get_byte() is\r
1049  * the global routine to read the next BYTE from the GIF file.\r
1050  *\r
1051  * It is generally a good idea to have linewidth correspond to the actual\r
1052  * width of a line (as specified in the Image header) to make your own\r
1053  * code a bit simpler, but it isn't absolutely necessary.\r
1054  *\r
1055  * Returns: 0 if successful, else negative.  (See ERRS.H)\r
1056  *\r
1057  */\r
1058 /* bad_code_count is incremented each time an out of range code is read.\r
1059  * When this value is non-zero after a decode, your GIF file is probably\r
1060  * corrupt in some way...\r
1061  */\r
1062 short CxImageGIF::decoder(CxFile* file, CImageIterator* iter, short linewidth, int &bad_code_count)\r
1063 {\r
1064         register BYTE *sp, *bufptr;\r
1065         BYTE *buf;\r
1066         register short code, fc, oc, bufcnt;\r
1067         short c, size, ret;\r
1068 \r
1069         /* Initialize for decoding a new image... */\r
1070         bad_code_count = 0;\r
1071         if ((size = (short)get_byte(file)) < 0) return(size);\r
1072         if (size < 2 || 9 < size)                               return(BAD_CODE_SIZE);\r
1073         // out_line = outline;\r
1074         init_exp(size);\r
1075         //printf("L %d %x\n",linewidth,size);\r
1076 \r
1077         /* Initialize in case they forgot to put in a clear code.\r
1078          * (This shouldn't happen, but we'll try and decode it anyway...)\r
1079          */\r
1080         oc = fc = 0;\r
1081 \r
1082    /* Allocate space for the decode buffer */\r
1083         if ((buf = new BYTE[linewidth + 1]) == NULL) return(OUT_OF_MEMORY);\r
1084 \r
1085    /* Set up the stack pointer and decode buffer pointer */\r
1086         sp = stack;\r
1087         bufptr = buf;\r
1088         bufcnt = linewidth;\r
1089 \r
1090    /* This is the main loop.  For each code we get we pass through the\r
1091         * linked list of prefix codes, pushing the corresponding "character" for\r
1092         * each code onto the stack.  When the list reaches a single "character"\r
1093         * we push that on the stack too, and then start unstacking each\r
1094     * character for output in the correct order.  Special handling is\r
1095         * included for the clear code, and the whole thing ends when we get\r
1096     * an ending code.\r
1097     */\r
1098         while ((c = get_next_code(file)) != ending) {\r
1099                 /* If we had a file error, return without completing the decode*/\r
1100                 if (c < 0){\r
1101                         delete [] buf;\r
1102                         return(0);\r
1103                 }\r
1104                 /* If the code is a clear code, reinitialize all necessary items.*/\r
1105                 if (c == clear){\r
1106                         curr_size = (short)(size + 1);\r
1107                         slot = newcodes;\r
1108                         top_slot = (short)(1 << curr_size);\r
1109 \r
1110                         /* Continue reading codes until we get a non-clear code\r
1111                         * (Another unlikely, but possible case...)\r
1112                         */\r
1113                         while ((c = get_next_code(file)) == clear);\r
1114 \r
1115                         /* If we get an ending code immediately after a clear code\r
1116                         * (Yet another unlikely case), then break out of the loop.\r
1117                         */\r
1118                         if (c == ending) break;\r
1119 \r
1120                         /* Finally, if the code is beyond the range of already set codes,\r
1121                         * (This one had better NOT happen...  I have no idea what will\r
1122                         * result from this, but I doubt it will look good...) then set it\r
1123                         * to color zero.\r
1124                         */\r
1125                         if (c >= slot) c = 0;\r
1126                         oc = fc = c;\r
1127 \r
1128                         /* And let us not forget to put the char into the buffer... And\r
1129                         * if, on the off chance, we were exactly one pixel from the end\r
1130                         * of the line, we have to send the buffer to the out_line()\r
1131                         * routine...\r
1132                         */\r
1133                         *bufptr++ = (BYTE)c;\r
1134                         if (--bufcnt == 0) {\r
1135                                 if (iter) {\r
1136                                         if ((ret = (short)out_line(iter, buf, linewidth)) < 0) {\r
1137                                                 delete [] buf;\r
1138                                                 return(ret);\r
1139                                         }\r
1140                                 }\r
1141                                 bufptr = buf;\r
1142                                 bufcnt = linewidth;\r
1143             }\r
1144                 } else {\r
1145                         /* In this case, it's not a clear code or an ending code, so\r
1146                         * it must be a code code...  So we can now decode the code into\r
1147                         * a stack of character codes. (Clear as mud, right?)\r
1148                         */\r
1149                         code = c;\r
1150 \r
1151                         /* Here we go again with one of those off chances...  If, on the\r
1152                         * off chance, the code we got is beyond the range of those already\r
1153                         * set up (Another thing which had better NOT happen...) we trick\r
1154                         * the decoder into thinking it actually got the last code read.\r
1155                         * (Hmmn... I'm not sure why this works...  But it does...)\r
1156                         */\r
1157                         if (code >= slot && sp<(stack+MAX_CODES-1)) {\r
1158                                 if (code > slot)\r
1159                                         ++bad_code_count;\r
1160                                 code = oc;\r
1161                                 *sp++ = (BYTE)fc;\r
1162             }\r
1163 \r
1164                         /* Here we scan back along the linked list of prefixes, pushing\r
1165                         * helpless characters (ie. suffixes) onto the stack as we do so.\r
1166                         */\r
1167                         while (code >= newcodes && sp<(stack+MAX_CODES-1)) {\r
1168                                 *sp++ = suffix[code];\r
1169                                 code = prefix[code];\r
1170             }\r
1171 \r
1172                         /* Push the last character on the stack, and set up the new\r
1173                         * prefix and suffix, and if the required slot number is greater\r
1174                         * than that allowed by the current bit size, increase the bit\r
1175                         * size.  (NOTE - If we are all full, we *don't* save the new\r
1176                         * suffix and prefix...  I'm not certain if this is correct...\r
1177                         * it might be more proper to overwrite the last code...\r
1178                         */\r
1179                         *sp++ = (BYTE)code;\r
1180                         if (slot < top_slot){\r
1181                                 suffix[slot] = (BYTE)(fc = (BYTE)code);\r
1182                                 prefix[slot++] = oc;\r
1183                                 oc = c;\r
1184             }\r
1185                         if (slot >= top_slot){\r
1186                                 if (curr_size < 12) {\r
1187                                         top_slot <<= 1;\r
1188                                         ++curr_size;\r
1189                                 }\r
1190                         }\r
1191 \r
1192                         /* Now that we've pushed the decoded string (in reverse order)\r
1193                         * onto the stack, lets pop it off and put it into our decode\r
1194                         * buffer...  And when the decode buffer is full, write another\r
1195                         * line...\r
1196                         */\r
1197                         while (sp > stack) {\r
1198                                 *bufptr++ = *(--sp);\r
1199                                 if (--bufcnt == 0) {\r
1200                                         if (iter) {\r
1201                                                 if ((ret = (short)out_line(iter, buf, linewidth)) < 0) {\r
1202                                                         delete [] buf;\r
1203                                                         return(ret);\r
1204                                                 }\r
1205                                         }\r
1206                                         bufptr = buf;\r
1207                                         bufcnt = linewidth;\r
1208                                 }\r
1209                         }\r
1210                 }\r
1211         }\r
1212         ret = 0;\r
1213         if (bufcnt != linewidth && iter)\r
1214                 ret = (short)out_line(iter, buf, (linewidth - bufcnt));\r
1215         delete [] buf;\r
1216         return(ret);\r
1217 }\r
1218 ////////////////////////////////////////////////////////////////////////////////\r
1219 int CxImageGIF::get_num_frames(CxFile *fp,struct_TabCol* TabColSrc,struct_dscgif* dscgif)\r
1220 {\r
1221         struct_image image;\r
1222 \r
1223         long pos=fp->Tell();\r
1224         int nframes=0;\r
1225 \r
1226         struct_TabCol TempTabCol;\r
1227         memcpy(&TempTabCol,TabColSrc,sizeof(struct_TabCol));\r
1228 \r
1229         char ch;\r
1230         bool bPreviousWasNull = true;\r
1231 \r
1232         for (BOOL bContinue = TRUE; bContinue; )\r
1233         {\r
1234                 if (fp->Read(&ch, sizeof(ch), 1) != 1) {break;}\r
1235 \r
1236                 if (bPreviousWasNull || ch==0)\r
1237                 {\r
1238                         switch (ch)\r
1239                         {\r
1240                         case '!': // extension\r
1241                                 {\r
1242                                 DecodeExtension(fp);\r
1243                                 break;\r
1244                                 }\r
1245                         case ',': // image\r
1246                                 {\r
1247 \r
1248                                 assert(sizeof(image) == 9);\r
1249                                 //log << "Image header" << endl;\r
1250                                 fp->Read(&image,sizeof(image),1);\r
1251 \r
1252                                 //avoid byte order problems with Solaris <candan> <AMSN>\r
1253                                 image.l = ntohs(image.l);\r
1254                                 image.t = ntohs(image.t);\r
1255                                 image.w = ntohs(image.w);\r
1256                                 image.h = ntohs(image.h);\r
1257 \r
1258                                 // in case of images with empty screen descriptor, give a last chance\r
1259                                 if (dscgif->scrwidth==0 && dscgif->scrheight==0){\r
1260                                         dscgif->scrwidth = image.w;\r
1261                                         dscgif->scrheight = image.h;\r
1262                                 }\r
1263 \r
1264                                 if (((image.l + image.w) > dscgif->scrwidth)||((image.t + image.h) > dscgif->scrheight))\r
1265                                         break;\r
1266 \r
1267                                 nframes++;\r
1268 \r
1269                                 // Local colour map?\r
1270                                 if (image.pf & 0x80) {\r
1271                                         TempTabCol.sogct = (short)(1 << ((image.pf & 0x07) +1));\r
1272                                         assert(3 == sizeof(struct rgb_color));\r
1273                                         fp->Read(TempTabCol.paleta,sizeof(struct rgb_color)*TempTabCol.sogct,1);\r
1274                                         //log << "Local colour map" << endl;\r
1275                                 }\r
1276 \r
1277                                 int badcode=0;\r
1278                                 ibf = GIFBUFTAM+1;\r
1279 \r
1280                                 interlaced = image.pf & 0x40;\r
1281                                 iheight = image.h;\r
1282                                 istep = 8;\r
1283                                 iypos = 0;\r
1284                                 ipass = 0;\r
1285 \r
1286                                 long pos_start = fp->Tell();\r
1287 \r
1288                                 //if (interlaced) log << "Interlaced" << endl;\r
1289                                 decoder(fp, 0, image.w, badcode);\r
1290 \r
1291                                 if (badcode){\r
1292                                         seek_next_image(fp,pos_start);\r
1293                                 } else {\r
1294                                         fp->Seek(-(ibfmax - ibf - 1), SEEK_CUR);\r
1295                                 }\r
1296                 \r
1297                                 break;\r
1298                                 }\r
1299                         case ';': //terminator\r
1300                                 bContinue=false;\r
1301                                 break;\r
1302                         default:\r
1303                                 bPreviousWasNull = (ch==0);\r
1304                                 break;\r
1305                         }\r
1306                 }\r
1307         }\r
1308 \r
1309         fp->Seek(pos,SEEK_SET);\r
1310         return nframes;\r
1311 }\r
1312 ////////////////////////////////////////////////////////////////////////////////\r
1313 long CxImageGIF::seek_next_image(CxFile* fp, long position)\r
1314 {\r
1315         fp->Seek(position, SEEK_SET);\r
1316         char ch1,ch2;\r
1317         ch1=ch2=0;\r
1318         while(fp->Read(&ch2,sizeof(char),1)>0){\r
1319                 if (ch1 == 0 && ch2 == ','){\r
1320                         fp->Seek(-1,SEEK_CUR);\r
1321                         return fp->Tell();\r
1322                 } else {\r
1323                         ch1 = ch2;\r
1324                 }\r
1325         }\r
1326         return -1;\r
1327 }\r
1328 ////////////////////////////////////////////////////////////////////////////////\r
1329 void CxImageGIF::SetLoops(int loops)\r
1330 {       m_loops=loops; }\r
1331 ////////////////////////////////////////////////////////////////////////////////\r
1332 long CxImageGIF::GetLoops()\r
1333 {       return m_loops; }\r
1334 ////////////////////////////////////////////////////////////////////////////////\r
1335 void CxImageGIF::SetComment(const char* sz_comment_in)\r
1336 {       if (sz_comment_in) strncpy(m_comment,sz_comment_in,255); }\r
1337 ////////////////////////////////////////////////////////////////////////////////\r
1338 void CxImageGIF::GetComment(char* sz_comment_out)\r
1339 {       if (sz_comment_out) strncpy(sz_comment_out,m_comment,255); }\r
1340 ////////////////////////////////////////////////////////////////////////////////\r
1341 void CxImageGIF::GifMix(CxImage & imgsrc2, struct_image & imgdesc)\r
1342 {\r
1343         long ymin = max(0,(long)(GetHeight()-imgdesc.t - imgdesc.h));\r
1344         long ymax = GetHeight()-imgdesc.t;\r
1345         long xmin = imgdesc.l;\r
1346         long xmax = min(GetWidth(), (DWORD)(imgdesc.l + imgdesc.w));\r
1347 \r
1348         long ibg2= imgsrc2.GetTransIndex();\r
1349     BYTE i2;\r
1350 \r
1351         for(long y = ymin; y < ymax; y++){\r
1352                 for(long x = xmin; x < xmax; x++){\r
1353                         i2 = imgsrc2.GetPixelIndex(x-xmin,y-ymin);\r
1354                         if(i2!=ibg2) SetPixelIndex(x,y,i2);\r
1355                 }\r
1356         }\r
1357 }\r
1358 ////////////////////////////////////////////////////////////////////////////////\r
1359 /*-----------------------------------------------------------------------\r
1360  *\r
1361  * miGIF Compression - mouse and ivo's GIF-compatible compression\r
1362  *\r
1363  *          -run length encoding compression routines-\r
1364  *\r
1365  * Copyright (C) 1998 Hutchison Avenue Software Corporation\r
1366  *               http://www.hasc.com\r
1367  *               info@hasc.com\r
1368  *\r
1369  * Permission to use, copy, modify, and distribute this software and its\r
1370  * documentation for any purpose and without fee is hereby granted, provided\r
1371  * that the above copyright notice appear in all copies and that both that\r
1372  * copyright notice and this permission notice appear in supporting\r
1373  * documentation.  This software is provided "AS IS." The Hutchison Avenue \r
1374  * Software Corporation disclaims all warranties, either express or implied, \r
1375  * including but not limited to implied warranties of merchantability and \r
1376  * fitness for a particular purpose, with respect to this code and accompanying\r
1377  * documentation. \r
1378  * \r
1379  * The miGIF compression routines do not, strictly speaking, generate files \r
1380  * conforming to the GIF spec, since the image data is not LZW-compressed \r
1381  * (this is the point: in order to avoid transgression of the Unisys patent \r
1382  * on the LZW algorithm.)  However, miGIF generates data streams that any \r
1383  * reasonably sane LZW decompresser will decompress to what we want.\r
1384  *\r
1385  * miGIF compression uses run length encoding. It compresses horizontal runs \r
1386  * of pixels of the same color. This type of compression gives good results\r
1387  * on images with many runs, for example images with lines, text and solid \r
1388  * shapes on a solid-colored background. It gives little or no compression \r
1389  * on images with few runs, for example digital or scanned photos.\r
1390  *\r
1391  *                               der Mouse\r
1392  *                      mouse@rodents.montreal.qc.ca\r
1393  *            7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B\r
1394  *\r
1395  *                             ivo@hasc.com\r
1396  *\r
1397  * The Graphics Interchange Format(c) is the Copyright property of\r
1398  * CompuServe Incorporated.  GIF(sm) is a Service Mark property of\r
1399  * CompuServe Incorporated.\r
1400  *\r
1401  */\r
1402 ////////////////////////////////////////////////////////////////////////////////\r
1403 void CxImageGIF::rle_clear(struct_RLE* rle)\r
1404 {\r
1405         rle->out_bits = rle->out_bits_init;\r
1406         rle->out_bump = rle->out_bump_init;\r
1407         rle->out_clear = rle->out_clear_init;\r
1408         rle->out_count = 0;\r
1409         rle->rl_table_max = 0;\r
1410         rle->just_cleared = 1;\r
1411 }\r
1412 ////////////////////////////////////////////////////////////////////////////////\r
1413 void CxImageGIF::rle_flush(struct_RLE* rle)\r
1414 {\r
1415         if (rle->rl_count == 1){\r
1416                 rle_output_plain(rle->rl_pixel,rle);\r
1417                 rle->rl_count = 0;\r
1418                 return;\r
1419         }\r
1420         if (rle->just_cleared){\r
1421                 rle_flush_fromclear(rle->rl_count,rle);\r
1422         } else if ((rle->rl_table_max < 2) || (rle->rl_table_pixel != rle->rl_pixel)) {\r
1423                 rle_flush_clearorrep(rle->rl_count,rle);\r
1424         } else {\r
1425                 rle_flush_withtable(rle->rl_count,rle);\r
1426         }\r
1427         rle->rl_count = 0;\r
1428 }\r
1429 ////////////////////////////////////////////////////////////////////////////////\r
1430 void CxImageGIF::rle_output_plain(int c,struct_RLE* rle)\r
1431 {\r
1432         rle->just_cleared = 0;\r
1433         rle_output(c,rle);\r
1434         rle->out_count++;\r
1435         if (rle->out_count >= rle->out_bump){\r
1436                 rle->out_bits ++;\r
1437                 rle->out_bump += 1 << (rle->out_bits - 1);\r
1438         }\r
1439         if (rle->out_count >= rle->out_clear){\r
1440                 rle_output(rle->code_clear,rle);\r
1441                 rle_clear(rle);\r
1442         }\r
1443 }\r
1444 ////////////////////////////////////////////////////////////////////////////////\r
1445 void CxImageGIF::rle_flush_fromclear(int count,struct_RLE* rle)\r
1446 {\r
1447         int n;\r
1448 \r
1449         rle->out_clear = rle->max_ocodes;\r
1450         rle->rl_table_pixel = rle->rl_pixel;\r
1451         n = 1;\r
1452         while (count > 0){\r
1453                 if (n == 1){\r
1454                         rle->rl_table_max = 1;\r
1455                         rle_output_plain(rle->rl_pixel,rle);\r
1456                         count --;\r
1457                 } else if (count >= n){\r
1458                         rle->rl_table_max = n;\r
1459                         rle_output_plain(rle->rl_basecode+n-2,rle);\r
1460                         count -= n;\r
1461                 } else if (count == 1){\r
1462                         rle->rl_table_max ++;\r
1463                         rle_output_plain(rle->rl_pixel,rle);\r
1464                         count = 0;\r
1465                 } else {\r
1466                         rle->rl_table_max ++;\r
1467                         rle_output_plain(rle->rl_basecode+count-2,rle);\r
1468                         count = 0;\r
1469                 }\r
1470                 if (rle->out_count == 0) n = 1; else n ++;\r
1471         }\r
1472         rle_reset_out_clear(rle);\r
1473 }\r
1474 ////////////////////////////////////////////////////////////////////////////////\r
1475 void CxImageGIF::rle_reset_out_clear(struct_RLE* rle)\r
1476 {\r
1477         rle->out_clear = rle->out_clear_init;\r
1478         if (rle->out_count >= rle->out_clear){\r
1479                 rle_output(rle->code_clear,rle);\r
1480                 rle_clear(rle);\r
1481         }\r
1482 }\r
1483 ////////////////////////////////////////////////////////////////////////////////\r
1484 void CxImageGIF::rle_flush_withtable(int count, struct_RLE* rle)\r
1485 {\r
1486         int repmax;\r
1487         int repleft;\r
1488         int leftover;\r
1489 \r
1490         repmax = count / rle->rl_table_max;\r
1491         leftover = count % rle->rl_table_max;\r
1492         repleft = (leftover ? 1 : 0);\r
1493         if (rle->out_count+repmax+repleft > rle->max_ocodes){\r
1494                 repmax = rle->max_ocodes - rle->out_count;\r
1495                 leftover = count - (repmax * rle->rl_table_max);\r
1496                 repleft = 1 + rle_compute_triangle_count(leftover,rle->max_ocodes);\r
1497         }\r
1498         if (1+rle_compute_triangle_count(count,rle->max_ocodes) < (unsigned int)(repmax+repleft)){\r
1499                 rle_output(rle->code_clear,rle);\r
1500                 rle_clear(rle);\r
1501                 rle_flush_fromclear(count,rle);\r
1502                 return;\r
1503         }\r
1504         rle->out_clear = rle->max_ocodes;\r
1505         for (;repmax>0;repmax--) rle_output_plain(rle->rl_basecode+rle->rl_table_max-2,rle);\r
1506         if (leftover){\r
1507                 if (rle->just_cleared){\r
1508                         rle_flush_fromclear(leftover,rle);\r
1509                 } else if (leftover == 1){\r
1510                         rle_output_plain(rle->rl_pixel,rle);\r
1511                 } else {\r
1512                         rle_output_plain(rle->rl_basecode+leftover-2,rle);\r
1513                 }\r
1514         }\r
1515         rle_reset_out_clear(rle);\r
1516 }\r
1517 ////////////////////////////////////////////////////////////////////////////////\r
1518 unsigned int CxImageGIF::rle_compute_triangle_count(unsigned int count, unsigned int nrepcodes)\r
1519 {\r
1520         unsigned int perrep;\r
1521         unsigned int cost;\r
1522 \r
1523         cost = 0;\r
1524         perrep = (nrepcodes * (nrepcodes+1)) / 2;\r
1525         while (count >= perrep){\r
1526                 cost += nrepcodes;\r
1527                 count -= perrep;\r
1528         }\r
1529         if (count > 0){\r
1530                 unsigned int n;\r
1531                 n = rle_isqrt(count);\r
1532                 while ((n*(n+1)) >= 2*count) n --;\r
1533                 while ((n*(n+1)) < 2*count) n ++;\r
1534                 cost += n;\r
1535         }\r
1536         return(cost);\r
1537 }\r
1538 ////////////////////////////////////////////////////////////////////////////////\r
1539 unsigned int CxImageGIF::rle_isqrt(unsigned int x)\r
1540 {\r
1541         unsigned int r;\r
1542         unsigned int v;\r
1543 \r
1544         if (x < 2) return(x);\r
1545         for (v=x,r=1;v;v>>=2,r<<=1) ;\r
1546         for( ;; )\r
1547         {\r
1548                 v = ((x / r) + r) / 2;\r
1549                 if ((v == r) || (v == r+1)) return(r);\r
1550                 r = v;\r
1551         }\r
1552 }\r
1553 ////////////////////////////////////////////////////////////////////////////////\r
1554 void CxImageGIF::rle_flush_clearorrep(int count, struct_RLE* rle)\r
1555 {\r
1556         int withclr;\r
1557         withclr = 1 + rle_compute_triangle_count(count,rle->max_ocodes);\r
1558         if (withclr < count) {\r
1559                 rle_output(rle->code_clear,rle);\r
1560                 rle_clear(rle);\r
1561                 rle_flush_fromclear(count,rle);\r
1562         } else {\r
1563                 for (;count>0;count--) rle_output_plain(rle->rl_pixel,rle);\r
1564         }\r
1565 }\r
1566 ////////////////////////////////////////////////////////////////////////////////\r
1567 void CxImageGIF::rle_write_block(struct_RLE* rle)\r
1568 {\r
1569         g_outfile->PutC((BYTE)rle->oblen);\r
1570         g_outfile->Write(rle->oblock,1,rle->oblen);\r
1571         rle->oblen = 0;\r
1572 }\r
1573 ////////////////////////////////////////////////////////////////////////////////\r
1574 void CxImageGIF::rle_block_out(unsigned char c, struct_RLE* rle)\r
1575 {\r
1576         rle->oblock[rle->oblen++] = c;\r
1577         if (rle->oblen >= 255) rle_write_block(rle);\r
1578 }\r
1579 ////////////////////////////////////////////////////////////////////////////////\r
1580 void CxImageGIF::rle_block_flush(struct_RLE* rle)\r
1581 {\r
1582         if (rle->oblen > 0) rle_write_block(rle);\r
1583 }\r
1584 ////////////////////////////////////////////////////////////////////////////////\r
1585 void CxImageGIF::rle_output(int val, struct_RLE* rle)\r
1586 {\r
1587         rle->obuf |= val << rle->obits;\r
1588         rle->obits += rle->out_bits;\r
1589         while (rle->obits >= 8){\r
1590                 rle_block_out((unsigned char)(rle->obuf&0xff),rle);\r
1591                 rle->obuf >>= 8;\r
1592                 rle->obits -= 8;\r
1593         }\r
1594 }\r
1595 ////////////////////////////////////////////////////////////////////////////////\r
1596 void CxImageGIF::rle_output_flush(struct_RLE* rle)\r
1597 {\r
1598          if (rle->obits > 0) rle_block_out((unsigned char)(rle->obuf),rle);\r
1599          rle_block_flush(rle);\r
1600 }\r
1601 ////////////////////////////////////////////////////////////////////////////////\r
1602 void CxImageGIF::compressRLE( int init_bits, CxFile* outfile)\r
1603 {\r
1604         g_init_bits = init_bits;\r
1605         g_outfile = outfile;\r
1606 \r
1607         struct_RLE rle;\r
1608         rle.code_clear = 1 << (init_bits - 1);\r
1609         rle.code_eof = rle.code_clear + 1;\r
1610         rle.rl_basecode = rle.code_eof + 1;\r
1611         rle.out_bump_init = (1 << (init_bits - 1)) - 1;\r
1612         rle.out_clear_init = (init_bits <= 3) ? 9 : (rle.out_bump_init-1);\r
1613         rle.out_bits_init = init_bits;\r
1614         rle.max_ocodes = (1 << MAXBITSCODES) - ((1 << (rle.out_bits_init - 1)) + 3);\r
1615         rle.rl_count = 0;\r
1616         rle_clear(&rle);\r
1617         rle.obuf = 0;\r
1618         rle.obits = 0;\r
1619         rle.oblen = 0;\r
1620 \r
1621         rle_output(rle.code_clear,&rle);\r
1622 \r
1623         int c;\r
1624         for( ;; )\r
1625         {\r
1626                 c = GifNextPixel();\r
1627                 if ((rle.rl_count > 0) && (c != rle.rl_pixel)) rle_flush(&rle);\r
1628                 if (c == EOF) break;\r
1629                 if (rle.rl_pixel == c){\r
1630                         rle.rl_count++;\r
1631                 } else {\r
1632                         rle.rl_pixel = c;\r
1633                         rle.rl_count = 1;\r
1634                 }\r
1635         }\r
1636         rle_output(rle.code_eof,&rle);\r
1637         rle_output_flush(&rle);\r
1638 }\r
1639 ////////////////////////////////////////////////////////////////////////////////\r
1640 #endif // CXIMAGE_SUPPORT_GIF\r