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