1 /*
2 / gaiagraphics_gif.c
3 /
4 / GIF auxiliary helpers
5 /
6 / version 1.0, 2010 July 20
7 /
8 / Author: Sandro Furieri a.furieri@lqt.it
9 /
10 / Copyright (C) 2010 Alessandro Furieri
11 /
12 / This program is free software: you can redistribute it and/or modify
13 / it under the terms of the GNU Lesser General Public License as published by
14 / the Free Software Foundation, either version 3 of the License, or
15 / (at your option) any later version.
16 /
17 / This program is distributed in the hope that it will be useful,
18 / but WITHOUT ANY WARRANTY; without even the implied warranty of
19 / MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 / GNU Lesser General Public License for more details.
21 /
22 / You should have received a copy of the GNU Lesser General Public License
23 / along with this program. If not, see <http://www.gnu.org/licenses/>.
24 /
25 */
26
27 #include <stdio.h>
28 #include <math.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include "gaiagraphics.h"
33 #include "gaiagraphics_internals.h"
34
35 /*
36 /
37 / DISCLAIMER:
38 / all the following code merely is an 'ad hoc' adaption
39 / deriving from the original GD lib code
40 / which in turn is based on he following:
41 /
42 ** Code drawn from ppmtogif.c, from the pbmplus package
43 **
44 ** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>. A
45 ** Lempel-Zim compression based on "compress".
46 **
47 ** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
48 **
49 ** Copyright (C) 1989 by Jef Poskanzer.
50 **
51 ** Permission to use, copy, modify, and distribute this software and its
52 ** documentation for any purpose and without fee is hereby granted, provided
53 ** that the above copyright notice appear in all copies and that both that
54 ** copyright notice and this permission notice appear in supporting
55 ** documentation. This software is provided "as is" without express or
56 ** implied warranty.
57 **
58 ** The Graphics Interchange Format(c) is the Copyright property of
59 ** CompuServe Incorporated. GIF(sm) is a Service Mark property of
60 ** CompuServe Incorporated.
61 /
62 */
63
64 typedef int code_int;
65
66 #ifdef SIGNED_COMPARE_SLOW
67 typedef unsigned long int count_int;
68 typedef unsigned short int count_short;
69 #else /*SIGNED_COMPARE_SLOW */
70 typedef long int count_int;
71 #endif /*SIGNED_COMPARE_SLOW */
72
73 #define GIFBITS 12
74
75 #define maxbits GIFBITS
76
77 #define maxmaxcode ((code_int)1 << GIFBITS)
78
79 #define HSIZE 5003
80 #define hsize HSIZE
81
82 #ifdef COMPATIBLE
83 #define MAXCODE(n_bits) ((code_int) 1 << (n_bits) - 1)
84 #else /*COMPATIBLE*/
85 #define MAXCODE(n_bits) (((code_int) 1 << (n_bits)) - 1)
86 #endif /*COMPATIBLE*/
87 #define HashTabOf(i) ctx->htab[i]
88 #define CodeTabOf(i) ctx->codetab[i]
89 typedef struct
90 {
91 int Width, Height;
92 int curx, cury;
93 long CountDown;
94 int Pass;
95 int Interlace;
96 int n_bits;
97 code_int maxcode;
98 count_int htab[HSIZE];
99 unsigned short codetab[HSIZE];
100 code_int free_ent;
101 int clear_flg;
102 int offset;
103 long int in_count;
104 long int out_count;
105 int g_init_bits;
106 xgdIOCtx *g_outfile;
107 int ClearCode;
108 int EOFCode;
109 unsigned long cur_accum;
110 int cur_bits;
111 int a_count;
112 char accum[256];
113 } GifCtx;
114
115 #define MAXCOLORMAPSIZE 256
116
117 #define TRUE 1
118 #define FALSE 0
119
120 #define CM_RED 0
121 #define CM_GREEN 1
122 #define CM_BLUE 2
123
124 #define MAX_LWZ_BITS 12
125
126 #define INTERLACE 0x40
127 #define LOCALCOLORMAP 0x80
128
129 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
130 #define ReadOK(file,buffer,len) (xgdGetBuf(buffer, len, file) > 0)
131 #define LM_to_uint(a,b) (((b)<<8)|(a))
132
133 #define STACK_SIZE ((1<<(MAX_LWZ_BITS))*2)
134
135 typedef struct
136 {
137 unsigned char buf[280];
138 int curbit, lastbit, done, last_byte;
139 } CODE_STATIC_DATA;
140
141 typedef struct
142 {
143 int fresh;
144 int code_size, set_code_size;
145 int max_code, max_code_size;
146 int firstcode, oldcode;
147 int clear_code, end_code;
148 int table[2][(1 << MAX_LWZ_BITS)];
149 int stack[STACK_SIZE], *sp;
150 CODE_STATIC_DATA scd;
151 } LZW_STATIC_DATA;
152
153 static int
GetDataBlock_(xgdIOCtx * fd,unsigned char * buf,int * ZeroDataBlockP)154 GetDataBlock_ (xgdIOCtx * fd, unsigned char *buf, int *ZeroDataBlockP)
155 {
156 unsigned char count;
157 if (!ReadOK (fd, &count, 1))
158 {
159 return -1;
160 }
161 *ZeroDataBlockP = count == 0;
162 if ((count != 0) && (!ReadOK (fd, buf, count)))
163 {
164 return -1;
165 }
166 return count;
167 }
168
169 static int
GetDataBlock(xgdIOCtx * fd,unsigned char * buf,int * ZeroDataBlockP)170 GetDataBlock (xgdIOCtx * fd, unsigned char *buf, int *ZeroDataBlockP)
171 {
172 int rv;
173 rv = GetDataBlock_ (fd, buf, ZeroDataBlockP);
174 return (rv);
175 }
176
177 static int
ReadColorMap(xgdIOCtx * fd,int number,unsigned char (* buffer)[256])178 ReadColorMap (xgdIOCtx * fd, int number, unsigned char (*buffer)[256])
179 {
180 int i;
181 unsigned char rgb[3];
182 for (i = 0; i < number; ++i)
183 {
184 if (!ReadOK (fd, rgb, sizeof (rgb)))
185 {
186 return TRUE;
187 }
188 buffer[CM_RED][i] = rgb[0];
189 buffer[CM_GREEN][i] = rgb[1];
190 buffer[CM_BLUE][i] = rgb[2];
191 }
192 return FALSE;
193 }
194
195 static int
GetCode_(xgdIOCtx * fd,CODE_STATIC_DATA * scd,int code_size,int flag,int * ZeroDataBlockP)196 GetCode_ (xgdIOCtx * fd, CODE_STATIC_DATA * scd, int code_size, int flag,
197 int *ZeroDataBlockP)
198 {
199 int i, j, ret;
200 unsigned char count;
201 if (flag)
202 {
203 scd->curbit = 0;
204 scd->lastbit = 0;
205 scd->last_byte = 0;
206 scd->done = FALSE;
207 return 0;
208 }
209 if ((scd->curbit + code_size) >= scd->lastbit)
210 {
211 if (scd->done)
212 {
213 if (scd->curbit >= scd->lastbit)
214 {
215 /* Oh well */
216 }
217 return -1;
218 }
219 scd->buf[0] = scd->buf[scd->last_byte - 2];
220 scd->buf[1] = scd->buf[scd->last_byte - 1];
221 if ((count = GetDataBlock (fd, &scd->buf[2], ZeroDataBlockP)) <= 0)
222 scd->done = TRUE;
223 scd->last_byte = 2 + count;
224 scd->curbit = (scd->curbit - scd->lastbit) + 16;
225 scd->lastbit = (2 + count) * 8;
226 }
227 ret = 0;
228 for (i = scd->curbit, j = 0; j < code_size; ++i, ++j)
229 ret |= ((scd->buf[i / 8] & (1 << (i % 8))) != 0) << j;
230 scd->curbit += code_size;
231 return ret;
232 }
233
234 static int
GetCode(xgdIOCtx * fd,CODE_STATIC_DATA * scd,int code_size,int flag,int * ZeroDataBlockP)235 GetCode (xgdIOCtx * fd, CODE_STATIC_DATA * scd, int code_size, int flag,
236 int *ZeroDataBlockP)
237 {
238 int rv;
239 rv = GetCode_ (fd, scd, code_size, flag, ZeroDataBlockP);
240 return (rv);
241 }
242
243 static int
LWZReadByte_(xgdIOCtx * fd,LZW_STATIC_DATA * sd,char flag,int input_code_size,int * ZeroDataBlockP)244 LWZReadByte_ (xgdIOCtx * fd, LZW_STATIC_DATA * sd, char flag,
245 int input_code_size, int *ZeroDataBlockP)
246 {
247 int code, incode, i;
248 if (flag)
249 {
250 sd->set_code_size = input_code_size;
251 sd->code_size = sd->set_code_size + 1;
252 sd->clear_code = 1 << sd->set_code_size;
253 sd->end_code = sd->clear_code + 1;
254 sd->max_code_size = 2 * sd->clear_code;
255 sd->max_code = sd->clear_code + 2;
256 GetCode (fd, &sd->scd, 0, TRUE, ZeroDataBlockP);
257 sd->fresh = TRUE;
258 for (i = 0; i < sd->clear_code; ++i)
259 {
260 sd->table[0][i] = 0;
261 sd->table[1][i] = i;
262 }
263 for (; i < (1 << MAX_LWZ_BITS); ++i)
264 sd->table[0][i] = sd->table[1][0] = 0;
265 sd->sp = sd->stack;
266 return 0;
267 }
268 else if (sd->fresh)
269 {
270 sd->fresh = FALSE;
271 do
272 {
273 sd->firstcode = sd->oldcode =
274 GetCode (fd, &sd->scd, sd->code_size, FALSE,
275 ZeroDataBlockP);
276 }
277 while (sd->firstcode == sd->clear_code);
278 return sd->firstcode;
279 }
280 if (sd->sp > sd->stack)
281 return *--sd->sp;
282 while ((code =
283 GetCode (fd, &sd->scd, sd->code_size, FALSE, ZeroDataBlockP)) >= 0)
284 {
285 if (code == sd->clear_code)
286 {
287 for (i = 0; i < sd->clear_code; ++i)
288 {
289 sd->table[0][i] = 0;
290 sd->table[1][i] = i;
291 }
292 for (; i < (1 << MAX_LWZ_BITS); ++i)
293 sd->table[0][i] = sd->table[1][i] = 0;
294 sd->code_size = sd->set_code_size + 1;
295 sd->max_code_size = 2 * sd->clear_code;
296 sd->max_code = sd->clear_code + 2;
297 sd->sp = sd->stack;
298 sd->firstcode = sd->oldcode =
299 GetCode (fd, &sd->scd, sd->code_size, FALSE,
300 ZeroDataBlockP);
301 return sd->firstcode;
302 }
303 else if (code == sd->end_code)
304 {
305 int count;
306 unsigned char buf[260];
307 if (*ZeroDataBlockP)
308 return -2;
309 while ((count = GetDataBlock (fd, buf, ZeroDataBlockP)) > 0)
310 ;
311 if (count != 0)
312 return -2;
313 }
314 incode = code;
315 if (sd->sp == (sd->stack + STACK_SIZE))
316 {
317 return -1;
318 }
319 if (code >= sd->max_code)
320 {
321 *sd->sp++ = sd->firstcode;
322 code = sd->oldcode;
323 }
324 while (code >= sd->clear_code)
325 {
326 if (sd->sp == (sd->stack + STACK_SIZE))
327 {
328 return -1;
329 }
330 *sd->sp++ = sd->table[1][code];
331 if (code == sd->table[0][code])
332 {
333 /* Oh well */
334 }
335 code = sd->table[0][code];
336 }
337 *sd->sp++ = sd->firstcode = sd->table[1][code];
338 if ((code = sd->max_code) < (1 << MAX_LWZ_BITS))
339 {
340 sd->table[0][code] = sd->oldcode;
341 sd->table[1][code] = sd->firstcode;
342 ++sd->max_code;
343 if ((sd->max_code >= sd->max_code_size) &&
344 (sd->max_code_size < (1 << MAX_LWZ_BITS)))
345 {
346 sd->max_code_size *= 2;
347 ++sd->code_size;
348 }
349 }
350 sd->oldcode = incode;
351 if (sd->sp > sd->stack)
352 return *--sd->sp;
353 }
354 return code;
355 }
356
357 static int
LWZReadByte(xgdIOCtx * fd,LZW_STATIC_DATA * sd,char flag,int input_code_size,int * ZeroDataBlockP)358 LWZReadByte (xgdIOCtx * fd, LZW_STATIC_DATA * sd, char flag,
359 int input_code_size, int *ZeroDataBlockP)
360 {
361 int rv;
362 rv = LWZReadByte_ (fd, sd, flag, input_code_size, ZeroDataBlockP);
363 return (rv);
364 }
365
366 static void
ReadImage(gGraphImagePtr img,xgdIOCtx * fd,unsigned char (* cmap)[256],int interlace,int * ZeroDataBlockP)367 ReadImage (gGraphImagePtr img, xgdIOCtx * fd,
368 unsigned char (*cmap)[256], int interlace, int *ZeroDataBlockP)
369 {
370 unsigned char c;
371 int v;
372 int xpos = 0, ypos = 0, pass = 0;
373 int i;
374 int red[256];
375 int green[256];
376 int blue[256];
377 unsigned char *p_out;
378 int len = img->width;
379 int height = img->height;
380 LZW_STATIC_DATA sd;
381 if (!ReadOK (fd, &c, 1))
382 {
383 return;
384 }
385 if (c > MAX_LWZ_BITS)
386 {
387 return;
388 }
389 for (i = 0; (i < 256); i++)
390 {
391 red[i] = cmap[CM_RED][i];
392 green[i] = cmap[CM_GREEN][i];
393 blue[i] = cmap[CM_BLUE][i];
394 }
395 if (LWZReadByte (fd, &sd, TRUE, c, ZeroDataBlockP) < 0)
396 {
397 return;
398 }
399 while ((v = LWZReadByte (fd, &sd, FALSE, c, ZeroDataBlockP)) >= 0)
400 {
401 if (v >= 256)
402 {
403 v = 0;
404 }
405 p_out =
406 img->pixels + (ypos * img->scanline_width) +
407 (xpos * img->pixel_size);
408 if (img->pixel_format == GG_PIXEL_PALETTE)
409 {
410 /* the output image is expected to be PALETTE-based */
411 *p_out++ = v;
412 if ((v + 1) > img->max_palette)
413 img->max_palette = v + 1;
414 img->palette_red[v] = red[v];
415 img->palette_green[v] = green[v];
416 img->palette_blue[v] = blue[v];
417 }
418 ++xpos;
419 if (xpos == len)
420 {
421 xpos = 0;
422 if (interlace)
423 {
424 switch (pass)
425 {
426 case 0:
427 case 1:
428 ypos += 8;
429 break;
430 case 2:
431 ypos += 4;
432 break;
433 case 3:
434 ypos += 2;
435 break;
436 }
437 if (ypos >= height)
438 {
439 ++pass;
440 switch (pass)
441 {
442 case 1:
443 ypos = 4;
444 break;
445 case 2:
446 ypos = 2;
447 break;
448 case 3:
449 ypos = 1;
450 break;
451 default:
452 goto fini;
453 }
454 }
455 }
456 else
457 {
458 ++ypos;
459 }
460 }
461 if (ypos >= height)
462 break;
463 }
464 fini:
465 if (LWZReadByte (fd, &sd, FALSE, c, ZeroDataBlockP) >= 0)
466 {
467 /* Ignore extra */
468 }
469 }
470
471 static int
DoExtension(xgdIOCtx * fd,int label,int * Transparent,int * ZeroDataBlockP)472 DoExtension (xgdIOCtx * fd, int label, int *Transparent, int *ZeroDataBlockP)
473 {
474 unsigned char buf[256];
475 switch (label)
476 {
477 case 0xf9:
478 memset (buf, 0, 4);
479 (void) GetDataBlock (fd, (unsigned char *) buf, ZeroDataBlockP);
480 if ((buf[0] & 0x1) != 0)
481 *Transparent = buf[3];
482 while (GetDataBlock (fd, (unsigned char *) buf, ZeroDataBlockP) > 0);
483 return FALSE;
484 default:
485 break;
486 }
487 while (GetDataBlock (fd, (unsigned char *) buf, ZeroDataBlockP) > 0)
488 ;
489 return FALSE;
490 }
491
492 static void
BumpPixel(GifCtx * ctx)493 BumpPixel (GifCtx * ctx)
494 {
495 ++(ctx->curx);
496 if (ctx->curx == ctx->Width)
497 {
498 ctx->curx = 0;
499 if (!ctx->Interlace)
500 ++(ctx->cury);
501 else
502 {
503 switch (ctx->Pass)
504 {
505 case 0:
506 ctx->cury += 8;
507 if (ctx->cury >= ctx->Height)
508 {
509 ++(ctx->Pass);
510 ctx->cury = 4;
511 }
512 break;
513 case 1:
514 ctx->cury += 8;
515 if (ctx->cury >= ctx->Height)
516 {
517 ++(ctx->Pass);
518 ctx->cury = 2;
519 }
520 break;
521 case 2:
522 ctx->cury += 4;
523 if (ctx->cury >= ctx->Height)
524 {
525 ++(ctx->Pass);
526 ctx->cury = 1;
527 }
528 break;
529
530 case 3:
531 ctx->cury += 2;
532 break;
533 }
534 }
535 }
536 }
537
538 static int
GIFNextPixel(gGraphImagePtr img,GifCtx * ctx)539 GIFNextPixel (gGraphImagePtr img, GifCtx * ctx)
540 {
541 int pixel = 0;
542 unsigned char *p_in;
543 if (ctx->CountDown == 0)
544 return EOF;
545 --(ctx->CountDown);
546
547 p_in =
548 img->pixels + (ctx->cury * img->scanline_width) +
549 (ctx->curx * img->pixel_size);
550 if (img->pixel_format == GG_PIXEL_GRAYSCALE
551 || img->pixel_format == GG_PIXEL_PALETTE)
552 {
553 /* the input image is expected to be GRAYSCALE or PALETTE anyway */
554 pixel = *p_in;
555 }
556 BumpPixel (ctx);
557 return pixel;
558 }
559
560 static void
xgdPutC(const unsigned char c,xgdIOCtx * ctx)561 xgdPutC (const unsigned char c, xgdIOCtx * ctx)
562 {
563 (ctx->putC) (ctx, c);
564 }
565
566 static void
char_init(GifCtx * ctx)567 char_init (GifCtx * ctx)
568 {
569 ctx->a_count = 0;
570 }
571
572 static void
cl_hash(register count_int chsize,GifCtx * ctx)573 cl_hash (register count_int chsize, GifCtx * ctx)
574 {
575 register count_int *htab_p = ctx->htab + chsize;
576 register long i;
577 register long m1 = -1;
578 i = chsize - 16;
579 do
580 {
581 *(htab_p - 16) = m1;
582 *(htab_p - 15) = m1;
583 *(htab_p - 14) = m1;
584 *(htab_p - 13) = m1;
585 *(htab_p - 12) = m1;
586 *(htab_p - 11) = m1;
587 *(htab_p - 10) = m1;
588 *(htab_p - 9) = m1;
589 *(htab_p - 8) = m1;
590 *(htab_p - 7) = m1;
591 *(htab_p - 6) = m1;
592 *(htab_p - 5) = m1;
593 *(htab_p - 4) = m1;
594 *(htab_p - 3) = m1;
595 *(htab_p - 2) = m1;
596 *(htab_p - 1) = m1;
597 htab_p -= 16;
598 }
599 while ((i -= 16) >= 0);
600 for (i += 16; i > 0; --i)
601 *--htab_p = m1;
602 }
603
604 static void
flush_char(GifCtx * ctx)605 flush_char (GifCtx * ctx)
606 {
607 if (ctx->a_count > 0)
608 {
609 xgdPutC (ctx->a_count, ctx->g_outfile);
610 xgdPutBuf (ctx->accum, ctx->a_count, ctx->g_outfile);
611 ctx->a_count = 0;
612 }
613 }
614
615 static void
char_out(int c,GifCtx * ctx)616 char_out (int c, GifCtx * ctx)
617 {
618 ctx->accum[ctx->a_count++] = c;
619 if (ctx->a_count >= 254)
620 flush_char (ctx);
621 }
622
623 static unsigned long masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F,
624 0x001F, 0x003F, 0x007F, 0x00FF,
625 0x01FF, 0x03FF, 0x07FF, 0x0FFF,
626 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF
627 };
628
629 static void
output(code_int code,GifCtx * ctx)630 output (code_int code, GifCtx * ctx)
631 {
632 ctx->cur_accum &= masks[ctx->cur_bits];
633 if (ctx->cur_bits > 0)
634 ctx->cur_accum |= ((long) code << ctx->cur_bits);
635 else
636 ctx->cur_accum = code;
637 ctx->cur_bits += ctx->n_bits;
638 while (ctx->cur_bits >= 8)
639 {
640 char_out ((unsigned int) (ctx->cur_accum & 0xff), ctx);
641 ctx->cur_accum >>= 8;
642 ctx->cur_bits -= 8;
643 }
644 if (ctx->free_ent > ctx->maxcode || ctx->clear_flg)
645 {
646
647 if (ctx->clear_flg)
648 {
649
650 ctx->maxcode = MAXCODE (ctx->n_bits = ctx->g_init_bits);
651 ctx->clear_flg = 0;
652
653 }
654 else
655 {
656
657 ++(ctx->n_bits);
658 if (ctx->n_bits == maxbits)
659 ctx->maxcode = maxmaxcode;
660 else
661 ctx->maxcode = MAXCODE (ctx->n_bits);
662 }
663 }
664 if (code == ctx->EOFCode)
665 {
666 while (ctx->cur_bits > 0)
667 {
668 char_out ((unsigned int) (ctx->cur_accum & 0xff), ctx);
669 ctx->cur_accum >>= 8;
670 ctx->cur_bits -= 8;
671 }
672 flush_char (ctx);
673 }
674 }
675
676 static void
cl_block(GifCtx * ctx)677 cl_block (GifCtx * ctx)
678 {
679 cl_hash ((count_int) hsize, ctx);
680 ctx->free_ent = ctx->ClearCode + 2;
681 ctx->clear_flg = 1;
682 output ((code_int) ctx->ClearCode, ctx);
683 }
684
685 static int
gifPutWord(int w,xgdIOCtx * out)686 gifPutWord (int w, xgdIOCtx * out)
687 {
688 xgdPutC (w & 0xFF, out);
689 xgdPutC ((w >> 8) & 0xFF, out);
690 return 0;
691 }
692
693 static void
GIFcompress(int init_bits,xgdIOCtxPtr outfile,gGraphImagePtr img,GifCtx * ctx)694 GIFcompress (int init_bits, xgdIOCtxPtr outfile, gGraphImagePtr img,
695 GifCtx * ctx)
696 {
697 /*
698 * compress stdin to stdout
699 *
700 * Algorithm: use open addressing double hashing (no chaining) on the
701 * prefix code / next character combination. We do a variant of Knuth's
702 * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
703 * secondary probe. Here, the modular division first probe is gives way
704 * to a faster exclusive-or manipulation. Also do block compression with
705 * an adaptive reset, whereby the code table is cleared when the compression
706 * ratio decreases, but after the table fills. The variable-length output
707 * codes are re-sized at this point, and a special CLEAR code is generated
708 * for the decompressor. Late addition: construct the table according to
709 * file size for noticeable speed improvement on small files. Please direct
710 * questions about this implementation to ames!jaw.
711 */
712 register long fcode;
713 register code_int i /* = 0 */ ;
714 register int c;
715 register code_int ent;
716 register code_int disp;
717 register code_int hsize_reg;
718 register int hshift;
719 ctx->g_init_bits = init_bits;
720 ctx->g_outfile = outfile;
721 ctx->offset = 0;
722 ctx->out_count = 0;
723 ctx->clear_flg = 0;
724 ctx->in_count = 1;
725 ctx->maxcode = MAXCODE (ctx->n_bits = ctx->g_init_bits);
726 ctx->ClearCode = (1 << (init_bits - 1));
727 ctx->EOFCode = ctx->ClearCode + 1;
728 ctx->free_ent = ctx->ClearCode + 2;
729 char_init (ctx);
730 ent = GIFNextPixel (img, ctx);
731 hshift = 0;
732 for (fcode = (long) hsize; fcode < 65536L; fcode *= 2L)
733 ++hshift;
734 hshift = 8 - hshift;
735 hsize_reg = hsize;
736 cl_hash ((count_int) hsize_reg, ctx);
737 output ((code_int) ctx->ClearCode, ctx);
738 #ifdef SIGNED_COMPARE_SLOW
739 while ((c = GIFNextPixel (img)) != (unsigned) EOF)
740 {
741 #else /*SIGNED_COMPARE_SLOW */
742 while ((c = GIFNextPixel (img, ctx)) != EOF)
743 { /* } */
744 #endif /*SIGNED_COMPARE_SLOW */
745 ++(ctx->in_count);
746 fcode = (long) (((long) c << maxbits) + ent);
747 i = (((code_int) c << hshift) ^ ent);
748 if (HashTabOf (i) == fcode)
749 {
750 ent = CodeTabOf (i);
751 continue;
752 }
753 else if ((long) HashTabOf (i) < 0)
754 goto nomatch;
755 disp = hsize_reg - i;
756 if (i == 0)
757 disp = 1;
758 probe:
759 if ((i -= disp) < 0)
760 i += hsize_reg;
761 if (HashTabOf (i) == fcode)
762 {
763 ent = CodeTabOf (i);
764 continue;
765 }
766 if ((long) HashTabOf (i) > 0)
767 goto probe;
768 nomatch:
769 output ((code_int) ent, ctx);
770 ++(ctx->out_count);
771 ent = c;
772 #ifdef SIGNED_COMPARE_SLOW
773 if ((unsigned) ctx->free_ent < (unsigned) maxmaxcode)
774 {
775 #else /*SIGNED_COMPARE_SLOW */
776 if (ctx->free_ent < maxmaxcode)
777 {
778 #endif /*SIGNED_COMPARE_SLOW */
779 CodeTabOf (i) = ctx->free_ent++;
780 HashTabOf (i) = fcode;
781 }
782 else
783 cl_block (ctx);
784 }
785 output ((code_int) ent, ctx);
786 ++(ctx->out_count);
787 output ((code_int) ctx->EOFCode, ctx);
788 }
789
790 static int
791 GIFEncode (xgdIOCtxPtr fp, int GInterlace,
792 int Background, int Transparent, int BitsPerPixel, int *Red,
793 int *Green, int *Blue, gGraphImagePtr img)
794 {
795 int B;
796 int RWidth, RHeight;
797 int LeftOfs, TopOfs;
798 int Resolution;
799 int ColorMapSize;
800 int InitCodeSize;
801 int i;
802 int GWidth = img->width;
803 int GHeight = img->height;
804 GifCtx ctx;
805 ctx.Interlace = GInterlace;
806 ctx.in_count = 1;
807 memset (&ctx, 0, sizeof (ctx));
808 ColorMapSize = 1 << BitsPerPixel;
809 RWidth = ctx.Width = GWidth;
810 RHeight = ctx.Height = GHeight;
811 LeftOfs = TopOfs = 0;
812 Resolution = BitsPerPixel;
813 ctx.CountDown = (long) ctx.Width * (long) ctx.Height;
814 ctx.Pass = 0;
815 if (BitsPerPixel <= 1)
816 InitCodeSize = 2;
817 else
818 InitCodeSize = BitsPerPixel;
819 ctx.curx = ctx.cury = 0;
820 xgdPutBuf (Transparent < 0 ? "GIF87a" : "GIF89a", 6, fp);
821 gifPutWord (RWidth, fp);
822 gifPutWord (RHeight, fp);
823 B = 0x80;
824 B |= (Resolution - 1) << 5;
825 B |= (BitsPerPixel - 1);
826 xgdPutC (B, fp);
827 xgdPutC (Background, fp);
828 xgdPutC (0, fp);
829 for (i = 0; i < ColorMapSize; ++i)
830 {
831 xgdPutC (Red[i], fp);
832 xgdPutC (Green[i], fp);
833 xgdPutC (Blue[i], fp);
834 }
835 if (Transparent >= 0)
836 {
837 xgdPutC ('!', fp);
838 xgdPutC (0xf9, fp);
839 xgdPutC (4, fp);
840 xgdPutC (1, fp);
841 xgdPutC (0, fp);
842 xgdPutC (0, fp);
843 xgdPutC ((unsigned char) Transparent, fp);
844 xgdPutC (0, fp);
845 }
846 xgdPutC (',', fp);
847 gifPutWord (LeftOfs, fp);
848 gifPutWord (TopOfs, fp);
849 gifPutWord (ctx.Width, fp);
850 gifPutWord (ctx.Height, fp);
851 if (ctx.Interlace)
852 xgdPutC (0x40, fp);
853 else
854 xgdPutC (0x00, fp);
855 xgdPutC (InitCodeSize, fp);
856 GIFcompress (InitCodeSize + 1, fp, img, &ctx);
857 xgdPutC (0, fp);
858 xgdPutC (';', fp);
859 return GGRAPH_OK;
860 }
861
862 static int
863 colorstobpp (int colors)
864 {
865 int bpp = 0;
866
867 if (colors <= 2)
868 bpp = 1;
869 else if (colors <= 4)
870 bpp = 2;
871 else if (colors <= 8)
872 bpp = 3;
873 else if (colors <= 16)
874 bpp = 4;
875 else if (colors <= 32)
876 bpp = 5;
877 else if (colors <= 64)
878 bpp = 6;
879 else if (colors <= 128)
880 bpp = 7;
881 else if (colors <= 256)
882 bpp = 8;
883 return bpp;
884 }
885
886 static gGraphImageInfosPtr
887 xgdImageInfosFromGifCtx (xgdIOCtxPtr fd, int *errcode)
888 {
889 int BitPixel;
890 unsigned char buf[16];
891 int imw, imh, screen_width, screen_height;
892 int bitPixel;
893 gGraphImageInfosPtr infos = NULL;
894 if (!ReadOK (fd, buf, 6))
895 {
896 *errcode = GGRAPH_GIF_CODEC_ERROR;
897 return NULL;
898 }
899 if (strncmp ((char *) buf, "GIF", 3) != 0)
900 {
901 *errcode = GGRAPH_GIF_CODEC_ERROR;
902 return NULL;
903 }
904 if (memcmp ((char *) buf + 3, "87a", 3) == 0
905 || memcmp ((char *) buf + 3, "89a", 3) == 0)
906 ;
907 else
908 {
909 *errcode = GGRAPH_GIF_CODEC_ERROR;
910 return NULL;
911 }
912 if (!ReadOK (fd, buf, 7))
913 {
914 *errcode = GGRAPH_GIF_CODEC_ERROR;
915 return NULL;
916 }
917 BitPixel = 2 << (buf[4] & 0x07);
918 screen_width = imw = LM_to_uint (buf[0], buf[1]);
919 screen_height = imh = LM_to_uint (buf[2], buf[3]);
920 bitPixel = colorstobpp (BitPixel);
921 if (!
922 (infos =
923 gg_image_infos_create (GG_PIXEL_PALETTE, screen_width, screen_height,
924 bitPixel, 1, GGRAPH_SAMPLE_UINT, NULL, NULL)))
925 {
926 *errcode = GGRAPH_INSUFFICIENT_MEMORY;
927 return NULL;
928 }
929 infos->compression = GGRAPH_TIFF_COMPRESSION_LZW;
930 if (!infos)
931 {
932 *errcode = GGRAPH_GIF_CODEC_ERROR;
933 return NULL;
934 }
935 return infos;
936 }
937
938 static gGraphImagePtr
939 xgdImageCreateFromGifCtx (xgdIOCtxPtr fd, int *errcode)
940 {
941 int BitPixel;
942 int Transparent = (-1);
943 unsigned char buf[16];
944 unsigned char c;
945 unsigned char ColorMap[3][MAXCOLORMAPSIZE];
946 unsigned char localColorMap[3][MAXCOLORMAPSIZE];
947 int imw, imh, screen_width, screen_height;
948 int useGlobalColormap;
949 int bitPixel;
950 int ZeroDataBlock = FALSE;
951 int haveGlobalColormap;
952 gGraphImagePtr img = NULL;
953 if (!ReadOK (fd, buf, 6))
954 {
955 *errcode = GGRAPH_GIF_CODEC_ERROR;
956 return NULL;
957 }
958 if (strncmp ((char *) buf, "GIF", 3) != 0)
959 {
960 *errcode = GGRAPH_GIF_CODEC_ERROR;
961 return NULL;
962 }
963 if (memcmp ((char *) buf + 3, "87a", 3) == 0
964 || memcmp ((char *) buf + 3, "89a", 3) == 0)
965 ;
966 else
967 {
968 *errcode = GGRAPH_GIF_CODEC_ERROR;
969 return NULL;
970 }
971 if (!ReadOK (fd, buf, 7))
972 {
973 *errcode = GGRAPH_GIF_CODEC_ERROR;
974 return NULL;
975 }
976 BitPixel = 2 << (buf[4] & 0x07);
977 screen_width = imw = LM_to_uint (buf[0], buf[1]);
978 screen_height = imh = LM_to_uint (buf[2], buf[3]);
979 haveGlobalColormap = BitSet (buf[4], LOCALCOLORMAP); /* Global Colormap */
980 if (haveGlobalColormap)
981 {
982 if (ReadColorMap (fd, BitPixel, ColorMap))
983 {
984 *errcode = GGRAPH_GIF_CODEC_ERROR;
985 return NULL;
986 }
987 }
988 for (;;)
989 {
990 int top, left;
991 int width, height;
992 if (!ReadOK (fd, &c, 1))
993 {
994 *errcode = GGRAPH_GIF_CODEC_ERROR;
995 return NULL;
996 }
997 if (c == ';')
998 {
999 goto terminated;
1000 }
1001
1002 if (c == '!')
1003 {
1004 if (!ReadOK (fd, &c, 1))
1005 {
1006 *errcode = GGRAPH_GIF_CODEC_ERROR;
1007 return NULL;
1008 }
1009 DoExtension (fd, c, &Transparent, &ZeroDataBlock);
1010 continue;
1011 }
1012
1013 if (c != ',')
1014 {
1015 continue;
1016 }
1017 if (!ReadOK (fd, buf, 9))
1018 {
1019 *errcode = GGRAPH_GIF_CODEC_ERROR;
1020 return NULL;
1021 }
1022 useGlobalColormap = !BitSet (buf[8], LOCALCOLORMAP);
1023 bitPixel = 1 << ((buf[8] & 0x07) + 1);
1024 left = LM_to_uint (buf[0], buf[1]);
1025 top = LM_to_uint (buf[2], buf[3]);
1026 width = LM_to_uint (buf[4], buf[5]);
1027 height = LM_to_uint (buf[6], buf[7]);
1028 if (left + width > screen_width || top + height > screen_height)
1029 {
1030 *errcode = GGRAPH_GIF_CODEC_ERROR;
1031 return NULL;
1032 }
1033 if (!
1034 (img =
1035 gg_image_create (GG_PIXEL_PALETTE, width, height, bitPixel, 1,
1036 GGRAPH_SAMPLE_UINT, NULL, NULL)))
1037 {
1038 *errcode = GGRAPH_INSUFFICIENT_MEMORY;
1039 return NULL;
1040 }
1041 if (!useGlobalColormap)
1042 {
1043 if (ReadColorMap (fd, bitPixel, localColorMap))
1044 {
1045 gg_image_destroy (img);
1046 *errcode = GGRAPH_GIF_CODEC_ERROR;
1047 return NULL;
1048 }
1049 ReadImage (img, fd, localColorMap,
1050 BitSet (buf[8], INTERLACE), &ZeroDataBlock);
1051 }
1052 else
1053 {
1054 if (!haveGlobalColormap)
1055 {
1056 gg_image_destroy (img);
1057 *errcode = GGRAPH_GIF_CODEC_ERROR;
1058 return NULL;
1059 }
1060 ReadImage (img, fd,
1061 ColorMap,
1062 BitSet (buf[8], INTERLACE), &ZeroDataBlock);
1063 }
1064 }
1065 terminated:
1066 if (!img)
1067 {
1068 *errcode = GGRAPH_GIF_CODEC_ERROR;
1069 return NULL;
1070 }
1071 return img;
1072 }
1073
1074 static int
1075 xgdImageGifCtx (gGraphImagePtr img, xgdIOCtxPtr out, int is_transparent)
1076 {
1077 int BitsPerPixel;
1078 int Red[256];
1079 int Green[256];
1080 int Blue[256];
1081 int i, colors;
1082 int transparent_idx = -1;
1083 if (img->pixel_format == GG_PIXEL_GRAYSCALE)
1084 {
1085 /* generating a GRAYSCALE palette */
1086 colors = 256;
1087 for (i = 0; i < 256; i++)
1088 {
1089 Red[i] = i;
1090 Green[i] = i;
1091 Blue[i] = i;
1092 }
1093 }
1094 else
1095 {
1096 /* copying the PALETTE from image */
1097 colors = 0;
1098 for (i = 0; i < img->max_palette; ++i)
1099 {
1100 Red[i] = img->palette_red[i];
1101 Green[i] = img->palette_green[i];
1102 Blue[i] = img->palette_blue[i];
1103 colors++;
1104 }
1105 }
1106 BitsPerPixel = colorstobpp (colors);
1107 if (is_transparent)
1108 {
1109 /* identifying the Transparent Color Index */
1110 for (i = 0; i < colors; ++i)
1111 {
1112 if (Red[i] == img->transparent_red
1113 && Green[i] == img->transparent_green
1114 && Blue[i] == img->transparent_blue)
1115 transparent_idx = i;
1116 }
1117 }
1118 return GIFEncode (out, 0, 0, transparent_idx, BitsPerPixel, Red, Green,
1119 Blue, img);
1120 }
1121
1122 GGRAPH_PRIVATE int
1123 gg_image_to_gif (const gGraphImagePtr img, void **mem_buf, int *mem_buf_size,
1124 FILE * file, int dest_type, int is_transparent)
1125 {
1126 /* compressing an image as GIF */
1127 int ret;
1128 void *rv;
1129 int size;
1130 xgdIOCtx *out;
1131
1132 /* checkings args for validity */
1133 if (dest_type == GG_TARGET_IS_FILE)
1134 {
1135 if (!file)
1136 return GGRAPH_ERROR;
1137 }
1138 else
1139 {
1140 if (!mem_buf || !mem_buf_size)
1141 return GGRAPH_ERROR;
1142 *mem_buf = NULL;
1143 *mem_buf_size = 0;
1144 }
1145
1146 if (dest_type == GG_TARGET_IS_FILE)
1147 out = xgdNewDynamicCtx (0, file, dest_type);
1148 else
1149 out = xgdNewDynamicCtx (2048, NULL, dest_type);
1150 ret = xgdImageGifCtx (img, out, is_transparent);
1151 if (dest_type == GG_TARGET_IS_FILE)
1152 {
1153 out->xgd_free (out);
1154 return ret;
1155 }
1156
1157 if (ret == GGRAPH_OK)
1158 rv = xgdDPExtractData (out, &size);
1159 out->xgd_free (out);
1160 *mem_buf = rv;
1161 *mem_buf_size = size;
1162 return ret;
1163 }
1164
1165 GGRAPH_PRIVATE int
1166 gg_image_from_gif (int size, const void *data, int source_type,
1167 gGraphImagePtr * image_handle)
1168 {
1169 /* uncompressing a GIF */
1170 int errcode = GGRAPH_OK;
1171 gGraphImagePtr img;
1172 xgdIOCtx *in =
1173 xgdNewDynamicCtxEx (size, data, XGD_CTX_DONT_FREE, source_type);
1174 img = xgdImageCreateFromGifCtx (in, &errcode);
1175 in->xgd_free (in);
1176 *image_handle = img;
1177 return errcode;
1178 }
1179
1180 GGRAPH_PRIVATE int
1181 gg_image_infos_from_gif (int size, const void *data, int source_type,
1182 gGraphImageInfosPtr * infos_handle)
1183 {
1184 /* image infos from GIF */
1185 int errcode = GGRAPH_OK;
1186 gGraphImageInfosPtr infos;
1187 xgdIOCtx *in =
1188 xgdNewDynamicCtxEx (size, data, XGD_CTX_DONT_FREE, source_type);
1189 infos = xgdImageInfosFromGifCtx (in, &errcode);
1190 in->xgd_free (in);
1191 *infos_handle = infos;
1192 return errcode;
1193 }
1194