1 /* -*- mode: c; c-basic-offset: 2 -*- */
2 /* ungifwrt.c - Functions to write unGIFs -- GIFs with run-length compression,
3    not LZW compression. Idea due to Hutchinson Avenue Software Corporation
4    <http://www.hasc.com>.
5    Copyright (C) 1997-2021 Eddie Kohler, ekohler@gmail.com
6    This file is part of the LCDF GIF library.
7 
8    The LCDF GIF library is free software. It is distributed under the GNU
9    General Public License, version 2; you can copy, distribute, or alter it at
10    will, as long as this notice is kept intact and this source code is made
11    available. There is no warranty, express or implied. */
12 
13 #if HAVE_CONFIG_H
14 # include <config.h>
15 #elif !defined(__cplusplus) && !defined(inline)
16 /* Assume we don't have inline by default */
17 # define inline
18 #endif
19 #include <lcdfgif/gif.h>
20 #include <stdarg.h>
21 #include <string.h>
22 #include <limits.h>
23 #ifdef __cplusplus
24 extern "C" {
25 #endif
26 
27 #define WRITE_BUFFER_SIZE	255
28 
29 struct Gif_Writer {
30   FILE *f;
31   uint8_t *v;
32   uint32_t pos;
33   uint32_t cap;
34   Gif_CompressInfo gcinfo;
35   int global_size;
36   int local_size;
37   int errors;
38   int cleared;
39   Gif_Code* rle_next;
40   void (*byte_putter)(uint8_t, struct Gif_Writer *);
41   void (*block_putter)(const uint8_t *, size_t, struct Gif_Writer *);
42 };
43 
44 
45 #define gifputbyte(b, grr)	((*grr->byte_putter)(b, grr))
46 #define gifputblock(b, l, grr)	((*grr->block_putter)(b, l, grr))
47 
48 static inline void
gifputunsigned(uint16_t uns,Gif_Writer * grr)49 gifputunsigned(uint16_t uns, Gif_Writer *grr)
50 {
51   gifputbyte(uns & 0xFF, grr);
52   gifputbyte(uns >> 8, grr);
53 }
54 
55 
56 static void
file_byte_putter(uint8_t b,Gif_Writer * grr)57 file_byte_putter(uint8_t b, Gif_Writer *grr)
58 {
59   fputc(b, grr->f);
60 }
61 
62 static void
file_block_putter(const uint8_t * block,size_t size,Gif_Writer * grr)63 file_block_putter(const uint8_t *block, size_t size, Gif_Writer *grr)
64 {
65   if (fwrite(block, 1, size, grr->f) != size)
66     grr->errors = 1;
67 }
68 
69 
70 static void
memory_byte_putter(uint8_t b,Gif_Writer * grr)71 memory_byte_putter(uint8_t b, Gif_Writer *grr)
72 {
73   if (grr->pos >= grr->cap) {
74     grr->cap = (grr->cap ? grr->cap * 2 : 1024);
75     Gif_ReArray(grr->v, uint8_t, grr->cap);
76   }
77   if (grr->v) {
78     grr->v[grr->pos] = b;
79     grr->pos++;
80   }
81 }
82 
83 static void
memory_block_putter(const uint8_t * data,size_t len,Gif_Writer * grr)84 memory_block_putter(const uint8_t *data, size_t len, Gif_Writer *grr)
85 {
86   while (grr->pos + len >= grr->cap) {
87     grr->cap = (grr->cap ? grr->cap * 2 : 1024);
88     Gif_ReArray(grr->v, uint8_t, grr->cap);
89   }
90   if (grr->v) {
91     memcpy(grr->v + grr->pos, data, len);
92     grr->pos += len;
93   }
94 }
95 
96 
97 static int
gif_writer_init(Gif_Writer * grr,FILE * f,const Gif_CompressInfo * gcinfo)98 gif_writer_init(Gif_Writer* grr, FILE* f, const Gif_CompressInfo* gcinfo)
99 {
100   grr->f = f;
101   grr->v = NULL;
102   grr->pos = grr->cap = 0;
103   if (gcinfo)
104     grr->gcinfo = *gcinfo;
105   else
106     Gif_InitCompressInfo(&grr->gcinfo);
107   grr->errors = 0;
108   grr->cleared = 0;
109   /* 27.Jul.2001: Must allocate GIF_MAX_CODE + 1 because we assign to
110      rle_next[GIF_MAX_CODE]! Thanks, Jeff Brown <jabrown@ipn.caida.org>, for
111      supplying the buggy files. */
112   grr->rle_next = Gif_NewArray(Gif_Code, GIF_MAX_CODE + 1);
113   if (f) {
114     grr->byte_putter = file_byte_putter;
115     grr->block_putter = file_block_putter;
116   } else {
117     grr->byte_putter = memory_byte_putter;
118     grr->block_putter = memory_block_putter;
119   }
120   return grr->rle_next != 0;
121 }
122 
123 static void
gif_writer_cleanup(Gif_Writer * grr)124 gif_writer_cleanup(Gif_Writer* grr)
125 {
126   Gif_DeleteArray(grr->v);
127   Gif_DeleteArray(grr->rle_next);
128 }
129 
130 
131 static inline const uint8_t *
gif_imageline(Gif_Image * gfi,unsigned pos)132 gif_imageline(Gif_Image *gfi, unsigned pos)
133 {
134   unsigned y, x;
135   if (gfi->width == 0)
136     return NULL;
137   y = pos / gfi->width;
138   x = pos - y * gfi->width;
139   if (y == (unsigned) gfi->height)
140     return NULL;
141   else if (!gfi->interlace)
142     return gfi->img[y] + x;
143   else
144     return gfi->img[Gif_InterlaceLine(y, gfi->height)] + x;
145 }
146 
147 static inline unsigned
gif_line_endpos(Gif_Image * gfi,unsigned pos)148 gif_line_endpos(Gif_Image *gfi, unsigned pos)
149 {
150   unsigned y = pos / gfi->width;
151   return (y + 1) * gfi->width;
152 }
153 
154 /* Write GIFs compressed with run-length encoding, an idea from code by
155    Hutchinson Avenue Software Corporation <http://www.hasc.com> found in
156    Thomas Boutell's gd library <http://www.boutell.com>. */
157 
158 static int
write_compressed_data(Gif_Image * gfi,int min_code_bits,Gif_Writer * grr)159 write_compressed_data(Gif_Image *gfi,
160 		      int min_code_bits, Gif_Writer *grr)
161 {
162   uint8_t stack_buffer[512 - 24];
163   uint8_t *buf = stack_buffer;
164   unsigned bufpos = 0;
165   unsigned bufcap = sizeof(stack_buffer) * 8;
166 
167   unsigned pos;
168   unsigned clear_bufpos, clear_pos;
169   unsigned line_endpos;
170   const uint8_t *imageline;
171 
172   unsigned run;
173 #ifndef GIF_NO_COMPRESSION
174 #define RUN_EWMA_SHIFT 4
175 #define RUN_EWMA_SCALE 19
176 #define RUN_INV_THRESH ((unsigned) (1 << RUN_EWMA_SCALE) / 3000)
177   unsigned run_ewma;
178   Gif_Code* rle_next = grr->rle_next;
179 #endif
180 
181   Gif_Code next_code = 0;
182   Gif_Code output_code;
183   uint8_t suffix;
184 
185   int cur_code_bits;
186 
187   /* Here we go! */
188   gifputbyte(min_code_bits, grr);
189 #define CLEAR_CODE      ((Gif_Code) (1 << min_code_bits))
190 #define EOI_CODE        ((Gif_Code) (CLEAR_CODE + 1))
191 #define CUR_BUMP_CODE   (1 << cur_code_bits)
192   grr->cleared = 0;
193 
194   cur_code_bits = min_code_bits + 1;
195   /* next_code set by first runthrough of output clear_code */
196   GIF_DEBUG(("clear(%d) eoi(%d) bits(%d) ", CLEAR_CODE, EOI_CODE, cur_code_bits));
197 
198   output_code = CLEAR_CODE;
199   /* Because output_code is clear_code, we'll initialize next_code, et al.
200      below. */
201 
202   pos = clear_pos = clear_bufpos = 0;
203   line_endpos = gfi->width;
204   imageline = gif_imageline(gfi, pos);
205 
206   while (1) {
207 
208     /*****
209      * Output 'output_code' to the memory buffer. */
210     if (bufpos + 32 >= bufcap) {
211       unsigned ncap = bufcap * 2 + (24 << 3);
212       uint8_t *nbuf = Gif_NewArray(uint8_t, ncap >> 3);
213       if (!nbuf)
214         goto error;
215       memcpy(nbuf, buf, bufcap >> 3);
216       if (buf != stack_buffer)
217         Gif_DeleteArray(buf);
218       buf = nbuf;
219       bufcap = ncap;
220     }
221 
222     {
223       unsigned endpos = bufpos + cur_code_bits;
224       do {
225         if (bufpos & 7)
226           buf[bufpos >> 3] |= output_code << (bufpos & 7);
227         else if (bufpos & 0x7FF)
228           buf[bufpos >> 3] = output_code >> (bufpos - endpos + cur_code_bits);
229         else {
230           buf[bufpos >> 3] = 255;
231           endpos += 8;
232         }
233 
234         bufpos += 8 - (bufpos & 7);
235       } while (bufpos < endpos);
236       bufpos = endpos;
237     }
238 
239 
240     /*****
241      * Handle special codes. */
242 
243     if (output_code == CLEAR_CODE) {
244       /* Clear data and prepare gfc */
245       cur_code_bits = min_code_bits + 1;
246       next_code = EOI_CODE + 1;
247 #ifndef GIF_NO_COMPRESSION
248       {
249         Gif_Code c;
250         for (c = 0; c < CLEAR_CODE; ++c)
251           rle_next[c] = CLEAR_CODE;
252       }
253       run_ewma = 1 << RUN_EWMA_SCALE;
254 #endif
255       run = 0;
256       clear_pos = clear_bufpos = 0;
257 
258       GIF_DEBUG(("clear "));
259 
260     } else if (output_code == EOI_CODE)
261       break;
262 
263     else {
264       if (next_code > CUR_BUMP_CODE && cur_code_bits < GIF_MAX_CODE_BITS)
265         /* bump up compression size */
266         ++cur_code_bits;
267 
268 #ifndef GIF_NO_COMPRESSION
269       /* Adjust current run length average. */
270       run = (run << RUN_EWMA_SCALE) + (1 << (RUN_EWMA_SHIFT - 1));
271       if (run < run_ewma)
272         run_ewma -= (run_ewma - run) >> RUN_EWMA_SHIFT;
273       else
274         run_ewma += (run - run_ewma) >> RUN_EWMA_SHIFT;
275 #else
276       if (cur_code_bits != min_code_bits) {
277         /* never bump up compression size -- keep cur_code_bits small by
278            generating clear_codes */
279         output_code = CLEAR_CODE;
280         continue;
281       }
282 #endif
283 
284       /* Reset run length. */
285       run = 0;
286     }
287 
288 
289     /*****
290      * Find the next code to output. */
291 
292     /* If height is 0 -- no more pixels to write -- we output work_node next
293        time around. */
294     if (imageline) {
295       output_code = suffix = *imageline;
296 
297       while (1) {
298         imageline++;
299         pos++;
300         if (pos == line_endpos) {
301           imageline = gif_imageline(gfi, pos);
302           line_endpos += gfi->width;
303         }
304         run++;
305 #ifndef GIF_NO_COMPRESSION
306         if (!imageline || *imageline != suffix || rle_next[output_code] == CLEAR_CODE)
307           break;
308         output_code = rle_next[output_code];
309 #else
310         break;
311 #endif
312       }
313 
314       /* Output the current code. */
315       if (next_code < GIF_MAX_CODE) {
316 #ifndef GIF_NO_COMPRESSION
317         if (imageline && *imageline == suffix) {
318           rle_next[output_code] = next_code;
319           rle_next[next_code] = CLEAR_CODE;
320         }
321 #endif
322         next_code++;
323       } else
324         next_code = GIF_MAX_CODE + 1; /* to match "> CUR_BUMP_CODE" above */
325 
326       /* Check whether to clear table. */
327       if (next_code > 4094) {
328         int do_clear = grr->gcinfo.flags & GIF_WRITE_EAGER_CLEAR;
329 
330 #ifndef GIF_NO_COMPRESSION
331         if (!do_clear) {
332           unsigned pixels_left = gfi->width * gfi->height - pos;
333           if (pixels_left) {
334             /* Always clear if run_ewma gets small relative to
335                min_code_bits. Otherwise, clear if #images/run is smaller
336                than an empirical threshold, meaning it will take more than
337                3000 or so average runs to complete the image. */
338             if (run_ewma < ((36U << RUN_EWMA_SCALE) / min_code_bits)
339                 || pixels_left > UINT_MAX / RUN_INV_THRESH
340                 || run_ewma < pixels_left * RUN_INV_THRESH)
341               do_clear = 1;
342           }
343         }
344 #else
345         do_clear = 1;
346 #endif
347 
348         if ((do_clear || run < 7) && !clear_pos) {
349           clear_pos = pos - run;
350           clear_bufpos = bufpos;
351         } else if (!do_clear && run > 50)
352           clear_pos = clear_bufpos = 0;
353 
354         if (do_clear) {
355           GIF_DEBUG(("rewind %u pixels/%d bits ", pos - clear_pos, bufpos + cur_code_bits - clear_bufpos));
356           output_code = CLEAR_CODE;
357           pos = clear_pos;
358           imageline = gif_imageline(gfi, pos);
359           line_endpos = gif_line_endpos(gfi, pos);
360           bufpos = clear_bufpos;
361           buf[bufpos >> 3] &= (1 << (bufpos & 7)) - 1;
362           grr->cleared = 1;
363         }
364       }
365     } else
366       output_code = EOI_CODE;
367   }
368 
369   /* Output memory buffer to stream. */
370   bufpos = (bufpos + 7) >> 3;
371   buf[(bufpos - 1) & 0xFFFFFF00] = (bufpos - 1) & 0xFF;
372   buf[bufpos] = 0;
373   gifputblock(buf, bufpos + 1, grr);
374 
375   if (buf != stack_buffer)
376     Gif_DeleteArray(buf);
377   return 1;
378 
379  error:
380   if (buf != stack_buffer)
381     Gif_DeleteArray(buf);
382   return 0;
383 }
384 
385 
386 static int
calculate_min_code_bits(Gif_Image * gfi,const Gif_Writer * grr)387 calculate_min_code_bits(Gif_Image *gfi, const Gif_Writer *grr)
388 {
389   int colors_used = -1, min_code_bits, i;
390 
391   if (grr->gcinfo.flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE) {
392     /* calculate m_c_b based on colormap */
393     if (grr->local_size > 0)
394       colors_used = grr->local_size;
395     else if (grr->global_size > 0)
396       colors_used = grr->global_size;
397 
398   } else if (gfi->img) {
399     /* calculate m_c_b from uncompressed data */
400     int x, y, width = gfi->width, height = gfi->height;
401     colors_used = 0;
402     for (y = 0; y < height && colors_used < 128; y++) {
403       uint8_t *data = gfi->img[y];
404       for (x = width; x > 0; x--, data++)
405 	if (*data > colors_used)
406 	  colors_used = *data;
407     }
408     colors_used++;
409 
410   } else if (gfi->compressed) {
411     /* take m_c_b from compressed image */
412     colors_used = 1 << gfi->compressed[0];
413 
414   } else {
415     /* should never happen */
416     colors_used = 256;
417   }
418 
419   min_code_bits = 2;		/* min_code_bits of 1 isn't allowed */
420   i = 4;
421   while (i < colors_used) {
422     min_code_bits++;
423     i *= 2;
424   }
425 
426   return min_code_bits;
427 }
428 
429 
430 static int get_color_table_size(const Gif_Stream *gfs, Gif_Image *gfi,
431 				Gif_Writer *grr);
432 
433 static void
save_compression_result(Gif_Image * gfi,Gif_Writer * grr,int ok)434 save_compression_result(Gif_Image *gfi, Gif_Writer *grr, int ok)
435 {
436   if (!(grr->gcinfo.flags & GIF_WRITE_SHRINK)
437       || (ok && (!gfi->compressed || gfi->compressed_len > grr->pos))) {
438     if (gfi->compressed)
439       (*gfi->free_compressed)((void *) gfi->compressed);
440     if (ok) {
441       gfi->compressed_len = grr->pos;
442       gfi->compressed_errors = 0;
443       gfi->compressed = grr->v;
444       gfi->free_compressed = Gif_Free;
445       grr->v = 0;
446       grr->cap = 0;
447     } else
448       gfi->compressed = 0;
449   }
450   grr->pos = 0;
451 }
452 
453 int
Gif_FullCompressImage(Gif_Stream * gfs,Gif_Image * gfi,const Gif_CompressInfo * gcinfo)454 Gif_FullCompressImage(Gif_Stream *gfs, Gif_Image *gfi,
455 		      const Gif_CompressInfo *gcinfo)
456 {
457   int ok = 0;
458   uint8_t min_code_bits;
459   Gif_Writer grr;
460 
461   if (!gif_writer_init(&grr, NULL, gcinfo)) {
462     if (!(grr.gcinfo.flags & GIF_WRITE_SHRINK))
463       Gif_ReleaseCompressedImage(gfi);
464     goto done;
465   }
466 
467   grr.global_size = get_color_table_size(gfs, 0, &grr);
468   grr.local_size = get_color_table_size(gfs, gfi, &grr);
469 
470   min_code_bits = calculate_min_code_bits(gfi, &grr);
471   ok = write_compressed_data(gfi, min_code_bits, &grr);
472   save_compression_result(gfi, &grr, ok);
473 
474   if ((grr.gcinfo.flags & (GIF_WRITE_OPTIMIZE | GIF_WRITE_EAGER_CLEAR))
475       == GIF_WRITE_OPTIMIZE
476       && grr.cleared && ok) {
477     grr.gcinfo.flags |= GIF_WRITE_EAGER_CLEAR | GIF_WRITE_SHRINK;
478     if (write_compressed_data(gfi, min_code_bits, &grr))
479       save_compression_result(gfi, &grr, 1);
480   }
481 
482  done:
483   gif_writer_cleanup(&grr);
484   return ok;
485 }
486 
487 
488 static int
get_color_table_size(const Gif_Stream * gfs,Gif_Image * gfi,Gif_Writer * grr)489 get_color_table_size(const Gif_Stream *gfs, Gif_Image *gfi, Gif_Writer *grr)
490 {
491   Gif_Colormap *gfcm = (gfi ? gfi->local : gfs->global);
492   int ncol, totalcol, i;
493 
494   if (!gfcm || gfcm->ncol <= 0)
495     return 0;
496 
497   /* Make sure ncol is reasonable */
498   ncol = gfcm->ncol;
499 
500   /* Possibly bump up 'ncol' based on 'transparent' values, if
501      careful_min_code_bits */
502   if (grr->gcinfo.flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE) {
503     if (gfi && gfi->transparent >= ncol)
504       ncol = gfi->transparent + 1;
505     else if (!gfi)
506       for (i = 0; i < gfs->nimages; i++)
507 	if (gfs->images[i]->transparent >= ncol)
508 	  ncol = gfs->images[i]->transparent + 1;
509   }
510 
511   /* Make sure the colormap is a power of two entries! */
512   /* GIF format doesn't allow a colormap with only 1 entry. */
513   if (ncol > 256)
514     ncol = 256;
515   for (totalcol = 2; totalcol < ncol; totalcol *= 2)
516     /* nada */;
517 
518   return totalcol;
519 }
520 
521 static void
write_color_table(Gif_Colormap * gfcm,int totalcol,Gif_Writer * grr)522 write_color_table(Gif_Colormap *gfcm, int totalcol, Gif_Writer *grr)
523 {
524   Gif_Color *c = gfcm->col;
525   int i, ncol = gfcm->ncol;
526 
527   for (i = 0; i < ncol && i < totalcol; i++, c++) {
528     gifputbyte(c->gfc_red, grr);
529     gifputbyte(c->gfc_green, grr);
530     gifputbyte(c->gfc_blue, grr);
531   }
532 
533   /* Pad out colormap with black. */
534   for (; i < totalcol; i++) {
535     gifputbyte(0, grr);
536     gifputbyte(0, grr);
537     gifputbyte(0, grr);
538   }
539 }
540 
541 
542 static int
write_image(Gif_Stream * gfs,Gif_Image * gfi,Gif_Writer * grr)543 write_image(Gif_Stream *gfs, Gif_Image *gfi, Gif_Writer *grr)
544 {
545   uint8_t min_code_bits, packed = 0;
546   grr->local_size = get_color_table_size(gfs, gfi, grr);
547 
548   gifputbyte(',', grr);
549   gifputunsigned(gfi->left, grr);
550   gifputunsigned(gfi->top, grr);
551   gifputunsigned(gfi->width, grr);
552   gifputunsigned(gfi->height, grr);
553 
554   if (grr->local_size > 0) {
555     int size = 2;
556     packed |= 0x80;
557     while (size < grr->local_size)
558       size *= 2, packed++;
559   }
560 
561   if (gfi->interlace) packed |= 0x40;
562   gifputbyte(packed, grr);
563 
564   if (grr->local_size > 0)
565     write_color_table(gfi->local, grr->local_size, grr);
566 
567   /* calculate min_code_bits here (because calculation may involve
568      recompression, if GIF_WRITE_CAREFUL_MIN_CODE_SIZE is true) */
569   min_code_bits = calculate_min_code_bits(gfi, grr);
570 
571   /* use existing compressed data if it exists. This will tend to whip
572      people's asses who uncompress an image, keep the compressed data around,
573      but modify the uncompressed data anyway. That sucks. */
574   if (gfi->compressed
575       && (!(grr->gcinfo.flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE)
576           || gfi->compressed[0] == min_code_bits)) {
577     uint8_t *compressed = gfi->compressed;
578     uint32_t compressed_len = gfi->compressed_len;
579     while (compressed_len > 0) {
580       uint16_t amt = (compressed_len > 0x7000 ? 0x7000 : compressed_len);
581       gifputblock(compressed, amt, grr);
582       compressed += amt;
583       compressed_len -= amt;
584     }
585 
586   } else if (!gfi->img) {
587     Gif_UncompressImage(gfs, gfi);
588     write_compressed_data(gfi, min_code_bits, grr);
589     Gif_ReleaseUncompressedImage(gfi);
590 
591   } else
592     write_compressed_data(gfi, min_code_bits, grr);
593 
594   return 1;
595 }
596 
597 
598 static void
write_logical_screen_descriptor(Gif_Stream * gfs,Gif_Writer * grr)599 write_logical_screen_descriptor(Gif_Stream *gfs, Gif_Writer *grr)
600 {
601   uint8_t packed = 0x70;		/* high resolution colors */
602   grr->global_size = get_color_table_size(gfs, 0, grr);
603 
604   Gif_CalculateScreenSize(gfs, 0);
605   gifputunsigned(gfs->screen_width, grr);
606   gifputunsigned(gfs->screen_height, grr);
607 
608   if (grr->global_size > 0) {
609     uint16_t size = 2;
610     packed |= 0x80;
611     while (size < grr->global_size)
612       size *= 2, packed++;
613   }
614 
615   gifputbyte(packed, grr);
616   if (gfs->background < grr->global_size)
617     gifputbyte(gfs->background, grr);
618   else
619     gifputbyte(255, grr);
620   gifputbyte(0, grr);		/* no aspect ratio information */
621 
622   if (grr->global_size > 0)
623     write_color_table(gfs->global, grr->global_size, grr);
624 }
625 
626 
627 /* extension byte table:
628    0x01 plain text extension
629    0xCE name*
630    0xF9 graphic control extension
631    0xFE comment extension
632    0xFF application extension
633    */
634 
635 static void
write_graphic_control_extension(Gif_Image * gfi,Gif_Writer * grr)636 write_graphic_control_extension(Gif_Image *gfi, Gif_Writer *grr)
637 {
638   uint8_t packed = 0;
639   gifputbyte('!', grr);
640   gifputbyte(0xF9, grr);
641   gifputbyte(4, grr);
642   if (gfi->transparent >= 0) packed |= 0x01;
643   packed |= (gfi->disposal & 0x07) << 2;
644   gifputbyte(packed, grr);
645   gifputunsigned(gfi->delay, grr);
646   gifputbyte((uint8_t)gfi->transparent, grr);
647   gifputbyte(0, grr);
648 }
649 
650 
651 static void
blast_data(const uint8_t * data,int len,Gif_Writer * grr)652 blast_data(const uint8_t *data, int len, Gif_Writer *grr)
653 {
654   while (len > 0) {
655     int s = len > 255 ? 255 : len;
656     gifputbyte(s, grr);
657     gifputblock(data, s, grr);
658     data += s;
659     len -= s;
660   }
661   gifputbyte(0, grr);
662 }
663 
664 
665 static void
write_name_extension(char * id,Gif_Writer * grr)666 write_name_extension(char *id, Gif_Writer *grr)
667 {
668   gifputbyte('!', grr);
669   gifputbyte(0xCE, grr);
670   blast_data((uint8_t *)id, strlen(id), grr);
671 }
672 
673 
674 static void
write_comment_extensions(Gif_Comment * gfcom,Gif_Writer * grr)675 write_comment_extensions(Gif_Comment *gfcom, Gif_Writer *grr)
676 {
677   int i;
678   for (i = 0; i < gfcom->count; i++) {
679     gifputbyte('!', grr);
680     gifputbyte(0xFE, grr);
681     blast_data((const uint8_t *)gfcom->str[i], gfcom->len[i], grr);
682   }
683 }
684 
685 
686 static void
write_netscape_loop_extension(uint16_t value,Gif_Writer * grr)687 write_netscape_loop_extension(uint16_t value, Gif_Writer *grr)
688 {
689   gifputblock((const uint8_t *)"!\xFF\x0BNETSCAPE2.0\x03\x01", 16, grr);
690   gifputunsigned(value, grr);
691   gifputbyte(0, grr);
692 }
693 
694 
695 static void
write_generic_extension(Gif_Extension * gfex,Gif_Writer * grr)696 write_generic_extension(Gif_Extension *gfex, Gif_Writer *grr)
697 {
698   uint32_t pos = 0;
699   if (gfex->kind < 0) return;	/* ignore our private extensions */
700 
701   gifputbyte('!', grr);
702   gifputbyte(gfex->kind, grr);
703   if (gfex->kind == 255) {	/* an application extension */
704     if (gfex->applength) {
705       gifputbyte(gfex->applength, grr);
706       gifputblock((const uint8_t*) gfex->appname, gfex->applength, grr);
707     }
708   }
709   if (gfex->packetized)
710     gifputblock(gfex->data, gfex->length, grr);
711   else {
712     while (pos + 255 < gfex->length) {
713       gifputbyte(255, grr);
714       gifputblock(gfex->data + pos, 255, grr);
715       pos += 255;
716     }
717     if (pos < gfex->length) {
718       uint32_t len = gfex->length - pos;
719       gifputbyte(len, grr);
720       gifputblock(gfex->data + pos, len, grr);
721     }
722   }
723   gifputbyte(0, grr);
724 }
725 
726 static int
write_gif(Gif_Stream * gfs,Gif_Writer * grr)727 write_gif(Gif_Stream *gfs, Gif_Writer *grr)
728 {
729   Gif_Extension* gfex;
730   int ok = 0;
731   int i;
732 
733   {
734     uint8_t isgif89a = 0;
735     if (gfs->end_comment || gfs->end_extension_list || gfs->loopcount > -1)
736       isgif89a = 1;
737     for (i = 0; i < gfs->nimages && !isgif89a; i++) {
738       Gif_Image* gfi = gfs->images[i];
739       if (gfi->identifier || gfi->transparent != -1 || gfi->disposal
740           || gfi->delay || gfi->comment || gfi->extension_list)
741         isgif89a = 1;
742     }
743     if (isgif89a)
744       gifputblock((const uint8_t *)"GIF89a", 6, grr);
745     else
746       gifputblock((const uint8_t *)"GIF87a", 6, grr);
747   }
748 
749   write_logical_screen_descriptor(gfs, grr);
750 
751   if (gfs->loopcount > -1)
752     write_netscape_loop_extension(gfs->loopcount, grr);
753 
754   for (i = 0; i < gfs->nimages; i++)
755     if (!Gif_IncrementalWriteImage(grr, gfs, gfs->images[i]))
756       goto done;
757 
758   for (gfex = gfs->end_extension_list; gfex; gfex = gfex->next)
759     write_generic_extension(gfex, grr);
760   if (gfs->end_comment)
761     write_comment_extensions(gfs->end_comment, grr);
762 
763   gifputbyte(';', grr);
764   ok = 1;
765 
766  done:
767   return ok;
768 }
769 
770 
771 int
Gif_FullWriteFile(Gif_Stream * gfs,const Gif_CompressInfo * gcinfo,FILE * f)772 Gif_FullWriteFile(Gif_Stream *gfs, const Gif_CompressInfo *gcinfo,
773 		  FILE *f)
774 {
775   Gif_Writer grr;
776   int ok = gif_writer_init(&grr, f, gcinfo)
777            && write_gif(gfs, &grr);
778   gif_writer_cleanup(&grr);
779   return ok;
780 }
781 
782 
783 Gif_Writer*
Gif_IncrementalWriteFileInit(Gif_Stream * gfs,const Gif_CompressInfo * gcinfo,FILE * f)784 Gif_IncrementalWriteFileInit(Gif_Stream* gfs, const Gif_CompressInfo* gcinfo,
785                              FILE *f)
786 {
787     Gif_Writer* grr = Gif_New(Gif_Writer);
788     if (!grr || !gif_writer_init(grr, f, gcinfo)) {
789         Gif_Delete(grr);
790         return NULL;
791     }
792     gifputblock((const uint8_t *)"GIF89a", 6, grr);
793     write_logical_screen_descriptor(gfs, grr);
794     if (gfs->loopcount > -1)
795         write_netscape_loop_extension(gfs->loopcount, grr);
796     return grr;
797 }
798 
799 int
Gif_IncrementalWriteImage(Gif_Writer * grr,Gif_Stream * gfs,Gif_Image * gfi)800 Gif_IncrementalWriteImage(Gif_Writer* grr, Gif_Stream* gfs, Gif_Image* gfi)
801 {
802     Gif_Extension *gfex;
803     for (gfex = gfi->extension_list; gfex; gfex = gfex->next)
804         write_generic_extension(gfex, grr);
805     if (gfi->comment)
806         write_comment_extensions(gfi->comment, grr);
807     if (gfi->identifier)
808         write_name_extension(gfi->identifier, grr);
809     if (gfi->transparent != -1 || gfi->disposal || gfi->delay)
810         write_graphic_control_extension(gfi, grr);
811     return write_image(gfs, gfi, grr);
812 }
813 
814 int
Gif_IncrementalWriteComplete(Gif_Writer * grr,Gif_Stream * gfs)815 Gif_IncrementalWriteComplete(Gif_Writer* grr, Gif_Stream* gfs)
816 {
817     Gif_Extension* gfex;
818     for (gfex = gfs->end_extension_list; gfex; gfex = gfex->next)
819         write_generic_extension(gfex, grr);
820     if (gfs->end_comment)
821         write_comment_extensions(gfs->end_comment, grr);
822     gifputbyte(';', grr);
823     gif_writer_cleanup(grr);
824     Gif_Delete(grr);
825     return 1;
826 }
827 
828 
829 #undef Gif_CompressImage
830 #undef Gif_WriteFile
831 
832 int
Gif_CompressImage(Gif_Stream * gfs,Gif_Image * gfi)833 Gif_CompressImage(Gif_Stream *gfs, Gif_Image *gfi)
834 {
835   return Gif_FullCompressImage(gfs, gfi, 0);
836 }
837 
838 int
Gif_WriteFile(Gif_Stream * gfs,FILE * f)839 Gif_WriteFile(Gif_Stream *gfs, FILE *f)
840 {
841   return Gif_FullWriteFile(gfs, 0, f);
842 }
843 
844 
845 #ifdef __cplusplus
846 }
847 #endif
848