~ubuntu-branches/ubuntu/gutsy/amsn/gutsy

« back to all changes in this revision

Viewing changes to utils/TkCximage/src/CxImage/ximagif.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Theodore Karkoulis
  • Date: 2006-01-04 15:26:02 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060104152602-ipe1yg00rl3nlklv
Tags: 0.95-1
New Upstream Release (closes: #345052, #278575).

Show diffs side-by-side

added added

removed removed

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