1 /* SaveGIF.c */
2 
3 /* GIF compressor code
4  * (K) All Rites Reversed - Copy What You Like (see file Copying)
5  *
6  * Authors:
7  *      Peter Hartley       <pdh@chaos.org.uk>
8  *
9  * History:
10  *      18-Aug-95 pdh Took BSD compress(1) to pieces to provide a C version
11  *                    of SWI Squash_Compress (cf 3PRM v4 p102)
12  *      10-Dec-95 pdh 8 bit GIF algorithm
13  *      13-Dec-95 pdh Adapted even more for 4 bit and interlaced GIFs
14  *      14-Dec-95 *** Release 1.00
15  *      03-Apr-96 pdh Add compression of multiple sprites into Netscape 2
16  *                    animated GIF format
17  *      03-Apr-96 pdh Added allowance for LH wastage in sprites
18  *      04-Apr-96 pdh Add bLoop, csDelay
19  *      04-Apr-96 *** Release 2.00
20  *      21-May-96 pdh Fix csDelay so it actually works
21  *      21-May-96 *** Release 2.01
22  *      11-Aug-96 pdh Allow for 2-bit and 1-bit GIFs
23  *      11-Aug-96 pdh Reduce #colours in dest if not all used in src
24  *      11-Aug-96 *** Release 2.02
25  *      23-Aug-96 pdh Modularise, remove all RiscOS specific bits
26  *      23-Aug-96 pdh Changed-rectangle support
27  *      25-Aug-96 pdh Write GIF straight to a file
28  *      25-Aug-96 *** Release 3.00
29  *      01-Sep-96 pdh Add 'one frame per file' option (-split)
30  *      01-Sep-96 *** Release 3.01
31  *      25-Sep-96 pdh Change loop count to 0x7FFF (from 0)
32  *      26-Oct-96 pdh Groovy new transparency optimisation
33  *      27-Oct-96 *** Release 4beta1
34  *      29-Oct-96 *** Release 4beta2
35  *      07-Nov-96 *** Release 4
36  *      17-Nov-96 pdh Fixed disposal of last frame of looped animation
37  *      15-Dec-96 *** Release 5beta1
38  *      01-Jan-97 pdh Rewrote transparency optimiser again
39  *      27-Jan-97 *** Release 5beta2
40  *      29-Jan-97 *** Release 5beta3
41  *      02-Feb-97 pdh Modified handling of bForceTrans (cannonspr bug)
42  *      02-Feb-97 pdh Call to Anim_Percent
43  *      03-Feb-97 *** Release 5
44  *      07-Feb-97 *** Release 5.01
45  *      02-Mar-97 pdh Frob a lot for new anim library
46  *      07-Apr-97 *** Release 6beta1
47  *      20-May-97 pdh Fix bug with -split
48  *      20-May-97 *** Release 6beta2
49  *      27-May-97 pdh Remove bForceTrans/bUseTrans
50  *      01-Jun-97 pdh Split up some big allocate() buffers
51  *      21-Aug-97 pdh Reinstate transparent borders on first frame (see below)
52  *      24-Aug-97 *** Release 6
53  *      27-Sep-97 pdh Add bForceDelay field to anim_GIFflags
54  *      27-Sep-97 *** Release 6.01
55  *      07-Nov-97 pdh Changed transparent borders behaviour again (see below)
56  *      08-Nov-97 *** Release 6.02
57  *      21-Feb-98 *** Release 6.03
58  *      07-Jun-98 *** Release 6.04
59  *      21-Aug-98 *** Release 6.05
60  *      19-Feb-99 *** Release 6.07
61  *      26-Mar-00 pdh Fix for 1-pixel-wide input GIFs
62  *      26-Mar-00 *** Release 6.10
63  *
64  *
65  * GIF sizes
66  * ---------
67  *
68  * Since at least version 2.02, InterGif has optimised out any transparent
69  * border on the first (or only) frame of transparent GIFs. It does this by
70  * setting the size in the Logical Screen Descriptor to the size of the whole
71  * GIF, and the size in the first Frame Descriptor to the smaller rectangle
72  * which bounds the first frame. This is all completely as per GIF spec, and
73  * is what happens for the second and subsequent frames of animated GIFs
74  * anyway.
75  *      However, some programs which read GIFs (usually those which either
76  * don't understand animations, or don't understand transparency) incorrectly
77  * ignore the LSD size and use the FD size. These programs include ChangeFSI,
78  * Claris HomePage, and early versions of Fresco (before 1.60). This is a
79  * problem as it can lead to Web authors specifying the wrong width= and
80  * height= attributes in Web pages. All versions of Netscape and MSIE use
81  * the LSD size (at least for GIF89's).
82  *      Early versions of Creator only set the FD size and not the LSD size;
83  * such images look wrong in MSIE. Netscape cheats! and uses only the FD size
84  * for GIF87 images and (correctly) the LSD size for GIF89 images. As of
85  * version 1.63, this is Fresco's behaviour too.
86  *
87  *      Newsflash, InterGif 6.02... Netscape Communicator (Netscape 4) gets it
88  * wrong if an image is (a) transparent (b) interlaced and (c) has a border
89  * optimised out on the first frame. The symptom is that black, non-transparent
90  * lines appear every fourth pixel down "transparent" areas of the image. This
91  * is *unquestionably* a bug in Communicator rather than InterGif (especially
92  * as Netscape 3 gets it right), but, powerless as I am in the face of Netscape
93  * Corporation, I've stopped InterGif from optimising out the border if an
94  * interlaced GIF is being made.
95  *      This means that such GIFs end up being compressed less optimally than
96  * they might. If this is a problem (and it may not be, as interlaced GIFs
97  * usually end up compressed less well than non-interlaced ones anyway) you can
98  * use the new -trim option to *remove*, rather than just avoid compressing,
99  * the transparent border. When using -trim, InterGif's output will *not* be
100  * the same size in pixels as the input image (it is in all other cases).
101  *
102  */
103 
104 #include <string.h>
105 #include <stdio.h>
106 #include <stdlib.h>
107 #include <stddef.h>
108 
109 #include "animlib.h"
110 #include "gifencode.h"
111 #include "frame.h"
112 #include "count.h"
113 #include "utils.h"
114 #include "workspace.h"
115 
116 #if 0
117 #define debugf printf
118 #define DEBUG 1
119 #else
120 #define debugf 1?0:printf
121 #define DEBUG 0
122 #endif
123 
124 
125 /*--------------------*
126  * Parts of GIF files *
127  *--------------------*/
128 
129 static char GIFheader[6] = { 'G','I','F','8','7','a' };
130 #define gifheader_VERSION 4
131 
132 static char GIFscreen[7];
133 #define gifscreen_XLO 0
134 #define gifscreen_XHI 1
135 #define gifscreen_YLO 2
136 #define gifscreen_YHI 3
137 #define gifscreen_FLAGS 4
138 
139 static char GIFtrans[8] = { '!', (char)0xF9, 4, 1, 0, 0, 0, 0 };
140 #define giftrans_FLAGS 3
141 #define giftrans_DELAYLO 4
142 #define giftrans_DELAYHI 5
143 #define giftrans_PIXEL 6
144 
145 static char GIFimage[10] = { ',', 0 };
146 #define gifimage_XOFFLO 1
147 #define gifimage_XOFFHI 2
148 #define gifimage_YOFFLO 3
149 #define gifimage_YOFFHI 4
150 #define gifimage_XLO 5
151 #define gifimage_XHI 6
152 #define gifimage_YLO 7
153 #define gifimage_YHI 8
154 #define gifimage_FLAGS 9
155 
156 static char GIFloop[19] = {
157     '!', (char)0xFF, 11,
158     'N', 'E', 'T', 'S', 'C', 'A', 'P', 'E', '2', '.', '0',
159     3, 1,
160     0, 0, /* 2 byte loop count == 0 (infinite) */
161     0
162 };
163 
164 
165 /*--------------------*
166  * Forward references *
167  *--------------------*/
168 
169 static BOOL GIF__GetFrame( anim a, int i, pixel *image, pixel *mask,
170                            anim_GIFflags flags );
171 
172 static int GIFCompressFrame( anim a, pixel *img, int bpp,
173                              char *linebuf, BOOL bInterlace, void *pTo,
174                              rect *pRect );
175 
176 
177 /*---------------------------------------------------------------------------*
178  * Anim_SaveGIF                                                              *
179  * Send a GIF to the given output stream                                     *
180  *---------------------------------------------------------------------------*/
181 
Anim_SaveGIF(anim a,anim_GIFflags flags,FILE * f,int nFrom,int nTo)182 BOOL Anim_SaveGIF( anim a, anim_GIFflags flags, FILE *f, int nFrom, int nTo )
183 {
184     char *to;
185     unsigned int sizeafter;
186     int nBytes;
187     int x, y;
188     unsigned int *GCT;
189     int nGCTcol;
190     char *ptr;
191     char *linebuf;
192     int i;
193     rect r,r2;
194     rect lastrect;
195     BOOL bFrameTrans;
196     int nFrameTransPixel;
197     int disposal;
198     int lastdisposal;
199     BOOL ok = TRUE;
200     pixel *imgbuffer[3];
201     pixel *maskbuffer[3];
202     int prevframe = 0, thisframe = 1, nextframe = 2;
203     palette palGlobal = a->pFrames->pal;        /* first frame's palette */
204     int nFrames = nTo - nFrom + 1;
205     int nTotalCol;
206 
207     x = a->nWidth;
208     y = a->nHeight;
209     nBytes = x*y;
210 
211     for ( i=0; i<3; i++ )
212     {
213         imgbuffer[i] = Anim_Allocate( nBytes );
214         maskbuffer[i] = Anim_Allocate( nBytes );
215         if ( !imgbuffer[i] || !maskbuffer[i] )
216             ok = FALSE;
217     }
218 
219     to =      Anim_Allocate( nBytes*2 + 1024 ); /* max poss compressed size */
220     linebuf = Anim_Allocate( x );
221 
222     if ( !to || !ok || !linebuf )
223     {
224         Anim_Free( &to );
225         for ( i=0; i<3; i++ )
226         {
227             Anim_Free( &imgbuffer[i] );
228             Anim_Free( &maskbuffer[i] );
229         }
230         Anim_Free( &linebuf );
231         Anim_NoMemory( "gifcomp" );
232         return FALSE;
233     }
234 
235     /* Write header */
236 
237     GIFheader[gifheader_VERSION] = ( a->pFrames[0].pMaskData
238                                      || nFrames>0 ) ? '9' : '7';
239     fwrite( GIFheader, 1, 6, f );
240 
241     /* Write Logical Screen Descriptor ... */
242 
243     nGCTcol = palGlobal->nColours;
244     GCT = palGlobal->pColours;
245 
246     nTotalCol = 1 << MinBpp( nGCTcol );
247 
248     debugf( "amg: nGCTcol=%d nTotalCol=%d choosing %dbpp\n",
249             nGCTcol, nTotalCol, MinBpp(nTotalCol) );
250 
251     GIFscreen[gifscreen_XLO] = x & 0xFF;
252     GIFscreen[gifscreen_XHI] = x >> 8;
253     GIFscreen[gifscreen_YLO] = y & 0xFF;
254     GIFscreen[gifscreen_YHI] = y >> 8;
255     GIFscreen[gifscreen_FLAGS] = 0xF0 + MinBpp(nTotalCol) - 1;
256     fwrite( GIFscreen, 1, 7, f );
257 
258     /* ... including palette */
259 
260     ptr = to;
261     memset( to, 0, nGCTcol*3 );
262     for ( i=0; i < nGCTcol; i++ )
263     {
264         *ptr++ = GCT[i]>>8;
265         *ptr++ = GCT[i]>>16;
266         *ptr++ = GCT[i]>>24;
267     }
268 
269     fwrite( to, 1, nGCTcol*3, f );
270 
271     for ( i = nGCTcol; i < nTotalCol; i++ )
272     {
273         fwrite( "ig\x6A", 1, 3, f );    /* 6.10 in BCD... */
274     }
275 
276     if ( nFrames > 1 && flags.bLoop )
277     {
278         /* Write Netscape extension */
279         fwrite( GIFloop, 1, 19, f );
280     }
281 
282     /* Write the frames */
283 
284     lastdisposal = 0;
285 
286     Workspace_Claim(0);
287 
288     for ( i = nFrom; i <= nTo && ok; i++ )
289     {
290         /* Trans5 algorithm */
291         frame fr = a->pFrames + i;
292         int next = i+1;
293 
294         if ( next > nTo )
295             next = nFrom;
296 
297         if ( i )
298             Anim_Percent( i*100 / nFrames );
299 
300         if ( i == nFrom )
301         {
302             debugf( "amg: opening frame %d as thisframe\n", i );
303 
304             if ( !GIF__GetFrame( a, i, imgbuffer[thisframe],
305                                  maskbuffer[thisframe], flags ) )
306             {
307                 ok = FALSE;
308                 break;
309             }
310         }
311 
312         bFrameTrans = FALSE;
313         nFrameTransPixel = -1;
314 
315         /* Must be done FIRST to get the arnie case right */
316         if ( ( nFrames > 1 )
317               && ( i < nTo || flags.bLoop ) )
318         {
319             debugf( "amg: opening frame %d as nextframe\n", next );
320 
321             if ( !GIF__GetFrame( a, next, imgbuffer[nextframe],
322                                  maskbuffer[nextframe], flags ) )
323             {
324                 ok = FALSE;
325                 break;
326             }
327 
328             if ( fr->pal == a->pFrames[next].pal )
329             {
330                 /* Find bounding box of all pixels which are solid in thisframe
331                  * but transparent in nextframe */
332 
333                 BitMaskFindTransRect( a, maskbuffer[thisframe],
334                                       maskbuffer[nextframe], &r2 );
335 
336                 debugf( "amg: transrect is (%d,%d)-(%d,%d)\n",
337                         r2.xoff, r2.yoff, r2.xoff+r2.xsize, r2.yoff+r2.ysize );
338 
339                 if ( r2.xsize == 0 )
340                     disposal = 1;
341                 else
342                     disposal = 2;
343             }
344             else
345             {
346                 debugf( "amg: palettes incompatible, compressing whole frame\n" );
347                 disposal = 2;
348                 r2.xsize = 0;
349                 r2.ysize = 0;
350             }
351         }
352         else
353         {
354             disposal = 1;
355             r2.xsize = 0;
356             r2.ysize = 0;
357         }
358 
359         if ( i > nFrom
360              && fr->pal == a->pFrames[i-1].pal )
361         {
362             debugf( "amg: opening frame %d as prevframe\n", i-1 );
363 
364             if ( !GIF__GetFrame( a, i-1, imgbuffer[prevframe],
365                                  maskbuffer[prevframe], flags ) )
366             {
367                 ok = FALSE;
368                 break;
369             }
370 
371             if ( lastdisposal == 2 )
372                 BitMaskClearRectangle( a, maskbuffer[prevframe], &lastrect );
373 
374             ChangedRect( a, imgbuffer[thisframe], maskbuffer[thisframe],
375                             imgbuffer[prevframe], maskbuffer[prevframe], &r );
376 
377             debugf( "amg: changed rect is (%d,%d)-(%d,%d)\n",
378                     r.xoff, r.yoff, r.xoff+r.xsize, r.yoff+r.ysize );
379 
380             if ( BitMaskAnyTransparent( a, maskbuffer[thisframe], &r ) )
381             {
382                 bFrameTrans = TRUE;
383                 BitMaskEqualPixels( a, imgbuffer[thisframe],
384                                        maskbuffer[thisframe],
385                                        imgbuffer[prevframe],
386                                        maskbuffer[prevframe] );
387                 nFrameTransPixel =
388                     BitMaskFindTransPixel( a, imgbuffer[thisframe],
389                                            maskbuffer[thisframe], TRUE );
390             }
391             else
392             {
393                 nFrameTransPixel =
394                     BitMaskFindTransPixel( a, imgbuffer[thisframe],
395                                            maskbuffer[thisframe], FALSE );
396 
397                 if ( nFrameTransPixel != -1 )
398                 {
399                     bFrameTrans = TRUE;
400                     BitMaskEqualPixels( a, imgbuffer[thisframe],
401                                            maskbuffer[thisframe],
402                                            imgbuffer[prevframe],
403                                            maskbuffer[prevframe] );
404                 }
405             }
406 
407             if ( bFrameTrans )
408             {
409                 BitMaskToPixMask( a, imgbuffer[thisframe],
410                                   maskbuffer[thisframe], nFrameTransPixel );
411                 PixMaskOptimiseRectangle( a, imgbuffer[thisframe],
412                                              maskbuffer[thisframe],
413                                              imgbuffer[prevframe],
414                                              maskbuffer[prevframe],
415                                           nFrameTransPixel, &r );
416             }
417         }
418         else
419         {
420             /* First frame, or incompatible palette */
421             r.xoff =
422                 r.yoff = 0;
423             r.xsize = x;
424             r.ysize = y;
425 
426             if ( BitMaskAnyTransparent( a, maskbuffer[thisframe], &r ) )
427             {
428                 bFrameTrans = TRUE;
429                 nFrameTransPixel =
430                     BitMaskFindTransPixel( a, imgbuffer[thisframe],
431                                            maskbuffer[thisframe], TRUE );
432 
433                 /* pdh: this stops InterGif from optimising out transparent
434                  * transparent borders on the first frame of interlaced GIFs.
435                  * See "GIF sizes" above for more information.
436                  */
437                 if ( i>nFrom || !flags.bInterlace )
438                     BitMaskTrimTransparentBorders( a, maskbuffer[thisframe],
439                                                    &r );
440 
441                 BitMaskToPixMask( a, imgbuffer[thisframe],
442                                   maskbuffer[thisframe], nFrameTransPixel );
443             }
444         }
445 
446 
447         Rect_Union( &r, &r2 );
448 
449         /* r is now the rectangle of this frame that wants compressing */
450 
451         if ( r.xsize == 0 )
452             r.xsize = 1;
453         if ( r.ysize == 0 )
454             r.ysize = 1;
455 
456         if ( fr->pal == palGlobal )
457         {
458             ptr = to;
459             if ( bFrameTrans || nFrames > 1 )
460             {
461                 /* Write Graphic Format Extension */
462 
463                 int csDelay = fr->csDelay;
464 
465                 if ( flags.bForceDelay )
466                     csDelay = flags.nDefaultDelay;
467 
468                 GIFtrans[giftrans_FLAGS] = (disposal << 2)
469                                             | (bFrameTrans ? 1 : 0);
470                 GIFtrans[giftrans_DELAYLO] = csDelay & 0xFF;
471                 GIFtrans[giftrans_DELAYHI] = csDelay >> 8;
472                 GIFtrans[giftrans_PIXEL] = nFrameTransPixel;
473                 memcpy( ptr, GIFtrans, 8 );
474                 ptr += 8;
475             }
476 
477             /* Write frame data */
478 
479             GIFimage[gifimage_XOFFLO] = r.xoff & 0xFF;
480             GIFimage[gifimage_XOFFHI] = r.xoff >> 8;
481             GIFimage[gifimage_YOFFLO] = r.yoff & 0xFF;
482             GIFimage[gifimage_YOFFHI] = r.yoff >> 8;
483             GIFimage[gifimage_XLO] = r.xsize & 0xFF;
484             GIFimage[gifimage_XHI] = r.xsize >> 8;
485             GIFimage[gifimage_YLO] = r.ysize & 0xFF;
486             GIFimage[gifimage_YHI] = r.ysize >> 8;
487             GIFimage[gifimage_FLAGS] = (flags.bInterlace && i==nFrom) ? 0x40
488                                                                       : 0;
489             memcpy( ptr, GIFimage, 10 );
490             ptr += 10;
491 
492             debugf( "%03d: %dx%d at (%d,%d) dis=%d",
493                     i, r.xsize, r.ysize, r.xoff, r.yoff, disposal );
494 
495             if ( bFrameTrans )
496                 debugf( " trans=%d", nFrameTransPixel );
497 
498             sizeafter = GIFCompressFrame( a, imgbuffer[thisframe],
499                                           MinBpp(nTotalCol),
500                                           linebuf, flags.bInterlace && i==nFrom,
501                                           ptr, &r );
502             ptr += sizeafter;
503 
504             debugf( " size=%4d\n", sizeafter );
505 
506             fwrite( to, 1, ptr-to, f );
507         }
508         else
509         {
510             Anim_SetError( "Please choose a palette-reduction option when using animations with more than 256 colours" );
511             ok = FALSE;
512         }
513 
514         lastrect = r;
515         lastdisposal = disposal;
516 
517         debugf( "Finished writing frame\n" );
518 
519         thisframe = (thisframe+1) % 3;
520         prevframe = (prevframe+1) % 3;
521         nextframe = (nextframe+1) % 3;
522 
523         /* if !ok then fall out */
524     }
525 
526     Workspace_Release();
527 
528     fwrite( ";", 1,1, f );
529 
530     Anim_Free( &to );
531     for ( i=0; i<3; i++ )
532     {
533         Anim_Free( &imgbuffer[i] );
534         Anim_Free( &maskbuffer[i] );
535     }
536     Anim_Free( &linebuf );
537     return ok;
538 }
539 
540 
541 /*---------------------------------------------------------------------------*
542  * GIF__GetFrame()                                                           *
543  * Decompresses a frame into the image and mask buffers, setting the mask as *
544  * appropriate depending on the flags.                                       *
545  *---------------------------------------------------------------------------*/
546 
GIF__GetFrame(anim a,int i,pixel * image,pixel * mask,anim_GIFflags flags)547 static BOOL GIF__GetFrame( anim a, int i, pixel *image, pixel *mask,
548                            anim_GIFflags flags )
549 {
550     frame f = a->pFrames + i;
551     unsigned int nBytes = a->nWidth * a->nHeight;
552     BOOL ok;
553 
554     if ( f->pMaskData )
555         ok = Anim_Decompress( f->pMaskData, f->nMaskSize, nBytes, mask );
556     else
557     {
558         memset( mask, 1, nBytes );     /* all solid */
559         ok = TRUE;
560     }
561 
562     if ( ok )
563         ok = Anim_Decompress( f->pImageData, f->nImageSize, nBytes, image );
564 
565     return ok;
566 }
567 
568 
569 /*---------------------------------------------------------------------------*
570  * Compressing one frame into a GIF image block                              *
571  * Uses GIFLZW compression -- the compression routine (in giflzw.c) takes    *
572  * a "fetcher" function (and handle) to supply it with its source bytestream *
573  * The fetcher function is getnextline, and the handle is a GIF_linedata*    *
574  *---------------------------------------------------------------------------*/
575 
576 typedef struct {
577     BOOL bDone;         /* Finished yet? */
578     BOOL bInterlace;    /* Interlaced? (affects the order we supply lines) */
579     int pass;           /* Interlacing pass */
580     unsigned int ypos;           /* Next line to give */
581     char *buffer;       /* Line buffer */
582     anim a;             /* Animation */
583     pixel *img;         /* And frame */
584     rect r;             /* Rectangle of frame to compress */
585 } GIF_linedata;
586 
587 
588 /* Data for interlacing */
589 
590 #define GIFPASSES 4
591 static const char starts[GIFPASSES]  = { 0,4,2,1 };    /* starts[0] not used */
592 static const char offsets[GIFPASSES] = { 8,8,4,2 };
593 
594 
595 /* Supply another lump of data (one line of bitmap) to the compressor */
596 
597 #if DEBUG
debugstream(const char * p,int n)598 static void debugstream( const char *p, int n )
599 {
600     int j=0;
601     while ( n-- )
602     {
603         debugf( j ? " %d" : "[%d", (int)*p );
604         p++;
605         j=1;
606     }
607     debugf( "]" );
608 }
609 #else
610 #define debugstream(p,n) /* skip */
611 #endif
612 
getnextline(void * handle,int * pSize)613 static unsigned char *getnextline( void *handle, int *pSize )
614 {
615     GIF_linedata *pLine = (GIF_linedata*) handle;
616     int w = pLine->a->nWidth;
617     pixel *pImage = pLine->img;
618     int offset = (pLine->ypos + pLine->r.yoff)*w;
619 
620     pImage += offset;
621 
622     if ( pLine->bDone )
623     {
624         *pSize = 0;
625         return NULL;
626     }
627 
628     if ( pLine->bInterlace )
629     {
630         pLine->ypos += offsets[pLine->pass];
631         while ( pLine->ypos >= pLine->r.ysize )
632         {
633             pLine->pass++;
634             if ( pLine->pass == GIFPASSES )
635             {
636                 pLine->bDone = TRUE;
637                 break;
638             }
639             pLine->ypos = starts[pLine->pass];
640         }
641     }
642     else
643     {
644         pLine->ypos++;
645         if ( pLine->ypos == pLine->r.ysize )
646             pLine->bDone = TRUE;
647     }
648 
649     *pSize = pLine->r.xsize;
650 
651 #if 0
652     if ( pLine->ypos == 1 )
653     {
654         debugf( "Sending " );
655         debugstream( pImage + pLine->r.xoff, 10 );
656     }
657 #endif
658 
659     return (unsigned char*)(pImage + pLine->r.xoff);
660 }
661 
662 /* Compress one frame */
663 
GIFCompressFrame(anim a,pixel * img,int bpp,char * linebuf,BOOL bInterlace,void * pTo,rect * r)664 static int GIFCompressFrame( anim a, pixel *img, int bpp,
665                              char *linebuf, BOOL bInterlace, void *pTo,
666                              rect *r )
667 {
668     GIF_linedata line;
669     int n_bits;
670 
671     line.bDone = FALSE;
672     line.bInterlace = bInterlace;
673     line.pass = 0;
674     line.ypos = 0;
675     line.buffer = linebuf;
676     line.a = a;
677     line.img = img;
678     line.r = *r;
679 
680     n_bits = bpp+1;
681     if ( n_bits == 2 )
682         n_bits = 3;     /* avoid degenerate case */
683 
684     debugf( "compressing, n_bits=%d\n", n_bits );
685 
686     return LZWCompress( n_bits, pTo, &getnextline, (void*)&line, NULL );
687 }
688