1 /* giffunc.c - General functions for the GIF library.
2    Copyright (C) 1997-2021 Eddie Kohler, ekohler@gmail.com
3    This file is part of the LCDF GIF library.
4 
5    The LCDF GIF library is free software. It is distributed under the GNU
6    General Public License, version 2; you can copy, distribute, or alter it at
7    will, as long as this notice is kept intact and this source code is made
8    available. There is no warranty, express or implied. */
9 
10 #if HAVE_CONFIG_H
11 # include <config.h>
12 #endif
13 #include <lcdfgif/gif.h>
14 #include <string.h>
15 #include <stdarg.h>
16 #ifdef __cplusplus
17 extern "C" {
18 #endif
19 
20 
21 Gif_Stream *
Gif_NewStream(void)22 Gif_NewStream(void)
23 {
24   Gif_Stream *gfs = Gif_New(Gif_Stream);
25   if (!gfs)
26     return 0;
27   gfs->images = 0;
28   gfs->nimages = gfs->imagescap = 0;
29   gfs->global = 0;
30   gfs->background = 256;
31   gfs->screen_width = gfs->screen_height = 0;
32   gfs->loopcount = -1;
33   gfs->end_comment = 0;
34   gfs->end_extension_list = 0;
35   gfs->errors = 0;
36   gfs->user_flags = 0;
37   gfs->refcount = 0;
38   gfs->landmark = 0;
39   return gfs;
40 }
41 
42 
43 Gif_Image *
Gif_NewImage(void)44 Gif_NewImage(void)
45 {
46   Gif_Image *gfi = Gif_New(Gif_Image);
47   if (!gfi)
48     return 0;
49   gfi->width = gfi->height = 0;
50   gfi->img = 0;
51   gfi->image_data = 0;
52   gfi->left = gfi->top = 0;
53   gfi->delay = 0;
54   gfi->disposal = GIF_DISPOSAL_NONE;
55   gfi->interlace = 0;
56   gfi->local = 0;
57   gfi->transparent = -1;
58   gfi->user_flags = 0;
59   gfi->identifier = 0;
60   gfi->comment = 0;
61   gfi->extension_list = 0;
62   gfi->free_image_data = Gif_Free;
63   gfi->compressed_len = 0;
64   gfi->compressed_errors = 0;
65   gfi->compressed = 0;
66   gfi->free_compressed = 0;
67   gfi->user_data = 0;
68   gfi->free_user_data = 0;
69   gfi->refcount = 0;
70   return gfi;
71 }
72 
73 
74 Gif_Colormap *
Gif_NewColormap(void)75 Gif_NewColormap(void)
76 {
77   Gif_Colormap *gfcm = Gif_New(Gif_Colormap);
78   if (!gfcm)
79     return 0;
80   gfcm->ncol = 0;
81   gfcm->capacity = 0;
82   gfcm->col = 0;
83   gfcm->refcount = 0;
84   gfcm->user_flags = 0;
85   return gfcm;
86 }
87 
88 
89 Gif_Colormap *
Gif_NewFullColormap(int count,int capacity)90 Gif_NewFullColormap(int count, int capacity)
91 {
92   Gif_Colormap *gfcm = Gif_New(Gif_Colormap);
93   if (!gfcm || capacity <= 0 || count < 0) {
94     Gif_Delete(gfcm);
95     return 0;
96   }
97   if (count > capacity)
98     capacity = count;
99   gfcm->ncol = count;
100   gfcm->capacity = capacity;
101   gfcm->col = Gif_NewArray(Gif_Color, capacity);
102   gfcm->refcount = 0;
103   gfcm->user_flags = 0;
104   if (!gfcm->col) {
105     Gif_Delete(gfcm);
106     return 0;
107   } else
108     return gfcm;
109 }
110 
111 
112 Gif_Comment *
Gif_NewComment(void)113 Gif_NewComment(void)
114 {
115   Gif_Comment *gfcom = Gif_New(Gif_Comment);
116   if (!gfcom)
117     return 0;
118   gfcom->str = 0;
119   gfcom->len = 0;
120   gfcom->count = gfcom->cap = 0;
121   return gfcom;
122 }
123 
124 
125 Gif_Extension *
Gif_NewExtension(int kind,const char * appname,int applength)126 Gif_NewExtension(int kind, const char* appname, int applength)
127 {
128     Gif_Extension *gfex = Gif_New(Gif_Extension);
129     if (!gfex)
130         return 0;
131     gfex->kind = kind;
132     if (appname) {
133         gfex->appname = (char*) Gif_NewArray(char, applength + 1);
134         if (!gfex->appname) {
135             Gif_Delete(gfex);
136             return 0;
137         }
138         memcpy(gfex->appname, appname, applength);
139         gfex->appname[applength] = 0;
140         gfex->applength = applength;
141     } else {
142         gfex->appname = 0;
143         gfex->applength = 0;
144     }
145     gfex->data = 0;
146     gfex->stream = 0;
147     gfex->image = 0;
148     gfex->next = 0;
149     gfex->free_data = 0;
150     gfex->packetized = 0;
151     return gfex;
152 }
153 
154 Gif_Extension*
Gif_CopyExtension(Gif_Extension * src)155 Gif_CopyExtension(Gif_Extension* src)
156 {
157     Gif_Extension* dst = Gif_NewExtension(src->kind, src->appname, src->applength);
158     if (!dst)
159         return NULL;
160     if (!src->data || !src->free_data) {
161         dst->data = src->data;
162         dst->length = src->length;
163     } else {
164         dst->data = Gif_NewArray(uint8_t, src->length);
165         if (!dst->data) {
166             Gif_DeleteExtension(dst);
167             return NULL;
168         }
169         memcpy(dst->data, src->data, src->length);
170         dst->length = src->length;
171         dst->free_data = Gif_Free;
172     }
173     dst->packetized = src->packetized;
174     return dst;
175 }
176 
177 
178 char *
Gif_CopyString(const char * s)179 Gif_CopyString(const char *s)
180 {
181   int l;
182   char *copy;
183   if (!s)
184     return 0;
185   l = strlen(s);
186   copy = Gif_NewArray(char, l + 1);
187   if (!copy)
188     return 0;
189   memcpy(copy, s, l + 1);
190   return copy;
191 }
192 
193 
194 int
Gif_AddImage(Gif_Stream * gfs,Gif_Image * gfi)195 Gif_AddImage(Gif_Stream *gfs, Gif_Image *gfi)
196 {
197   if (gfs->nimages >= gfs->imagescap) {
198     if (gfs->imagescap)
199       gfs->imagescap *= 2;
200     else
201       gfs->imagescap = 2;
202     Gif_ReArray(gfs->images, Gif_Image *, gfs->imagescap);
203     if (!gfs->images)
204       return 0;
205   }
206   gfs->images[gfs->nimages] = gfi;
207   gfs->nimages++;
208   gfi->refcount++;
209   return 1;
210 }
211 
212 
213 void
Gif_RemoveImage(Gif_Stream * gfs,int inum)214 Gif_RemoveImage(Gif_Stream *gfs, int inum)
215 {
216   int j;
217   if (inum < 0 || inum >= gfs->nimages)
218     return;
219   Gif_DeleteImage(gfs->images[inum]);
220   for (j = inum; j < gfs->nimages - 1; j++)
221     gfs->images[j] = gfs->images[j+1];
222   gfs->nimages--;
223 }
224 
225 
226 int
Gif_ImageColorBound(const Gif_Image * gfi)227 Gif_ImageColorBound(const Gif_Image* gfi)
228 {
229     if (gfi->compressed && gfi->compressed[0] > 0 && gfi->compressed[0] < 8)
230         return 1 << gfi->compressed[0];
231     else
232         return 256;
233 }
234 
235 
236 int
Gif_AddCommentTake(Gif_Comment * gfcom,char * x,int xlen)237 Gif_AddCommentTake(Gif_Comment *gfcom, char *x, int xlen)
238 {
239   if (gfcom->count >= gfcom->cap) {
240     if (gfcom->cap)
241       gfcom->cap *= 2;
242     else
243       gfcom->cap = 2;
244     Gif_ReArray(gfcom->str, char *, gfcom->cap);
245     Gif_ReArray(gfcom->len, int, gfcom->cap);
246     if (!gfcom->str || !gfcom->len)
247       return 0;
248   }
249   if (xlen < 0)
250     xlen = strlen(x);
251   gfcom->str[ gfcom->count ] = x;
252   gfcom->len[ gfcom->count ] = xlen;
253   gfcom->count++;
254   return 1;
255 }
256 
257 
258 int
Gif_AddComment(Gif_Comment * gfcom,const char * x,int xlen)259 Gif_AddComment(Gif_Comment *gfcom, const char *x, int xlen)
260 {
261   char *new_x;
262   if (xlen < 0)
263     xlen = strlen(x);
264   new_x = Gif_NewArray(char, xlen);
265   if (!new_x)
266     return 0;
267   memcpy(new_x, x, xlen);
268   if (Gif_AddCommentTake(gfcom, new_x, xlen) == 0) {
269     Gif_DeleteArray(new_x);
270     return 0;
271   } else
272     return 1;
273 }
274 
275 
276 int
Gif_AddExtension(Gif_Stream * gfs,Gif_Image * gfi,Gif_Extension * gfex)277 Gif_AddExtension(Gif_Stream* gfs, Gif_Image* gfi, Gif_Extension* gfex)
278 {
279     Gif_Extension **pprev;
280     if (gfex->stream || gfex->image)
281         return 0;
282     pprev = gfi ? &gfi->extension_list : &gfs->end_extension_list;
283     while (*pprev)
284         pprev = &(*pprev)->next;
285     *pprev = gfex;
286     gfex->stream = gfs;
287     gfex->image = gfi;
288     gfex->next = 0;
289     return 1;
290 }
291 
292 
293 int
Gif_ImageNumber(Gif_Stream * gfs,Gif_Image * gfi)294 Gif_ImageNumber(Gif_Stream *gfs, Gif_Image *gfi)
295 {
296     int i;
297     if (gfs && gfi)
298         for (i = 0; i != gfs->nimages; ++i)
299             if (gfs->images[i] == gfi)
300                 return i;
301     return -1;
302 }
303 
304 
305 void
Gif_CalculateScreenSize(Gif_Stream * gfs,int force)306 Gif_CalculateScreenSize(Gif_Stream *gfs, int force)
307 {
308   int i;
309   int screen_width = 0;
310   int screen_height = 0;
311 
312   for (i = 0; i < gfs->nimages; i++) {
313     Gif_Image *gfi = gfs->images[i];
314     /* 17.Dec.1999 - I find this old behavior annoying. */
315     /* if (gfi->left != 0 || gfi->top != 0) continue; */
316     if (screen_width < gfi->left + gfi->width)
317       screen_width = gfi->left + gfi->width;
318     if (screen_height < gfi->top + gfi->height)
319       screen_height = gfi->top + gfi->height;
320   }
321 
322   /* Only use the default 640x480 screen size if we are being forced to create
323      a new screen size or there's no screen size currently. */
324   if (screen_width == 0 && (gfs->screen_width == 0 || force))
325     screen_width = 640;
326   if (screen_height == 0 && (gfs->screen_height == 0 || force))
327     screen_height = 480;
328 
329   if (gfs->screen_width < screen_width || force)
330     gfs->screen_width = screen_width;
331   if (gfs->screen_height < screen_height || force)
332     gfs->screen_height = screen_height;
333 }
334 
335 
336 Gif_Stream *
Gif_CopyStreamSkeleton(Gif_Stream * gfs)337 Gif_CopyStreamSkeleton(Gif_Stream *gfs)
338 {
339   Gif_Stream *ngfs = Gif_NewStream();
340   if (!ngfs)
341     return 0;
342   ngfs->global = Gif_CopyColormap(gfs->global);
343   ngfs->background = gfs->background;
344   ngfs->screen_width = gfs->screen_width;
345   ngfs->screen_height = gfs->screen_height;
346   ngfs->loopcount = gfs->loopcount;
347   if (gfs->global && !ngfs->global) {
348     Gif_DeleteStream(ngfs);
349     return 0;
350   } else
351     return ngfs;
352 }
353 
354 
355 Gif_Stream *
Gif_CopyStreamImages(Gif_Stream * gfs)356 Gif_CopyStreamImages(Gif_Stream *gfs)
357 {
358   Gif_Stream *ngfs = Gif_CopyStreamSkeleton(gfs);
359   int i;
360   if (!ngfs)
361     return 0;
362   for (i = 0; i < gfs->nimages; i++) {
363     Gif_Image *gfi = Gif_CopyImage(gfs->images[i]);
364     if (!gfi || !Gif_AddImage(ngfs, gfi)) {
365       Gif_DeleteStream(ngfs);
366       return 0;
367     }
368   }
369   return ngfs;
370 }
371 
372 
373 Gif_Colormap *
Gif_CopyColormap(Gif_Colormap * src)374 Gif_CopyColormap(Gif_Colormap *src)
375 {
376   Gif_Colormap *dest;
377   if (!src)
378     return 0;
379 
380   dest = Gif_NewFullColormap(src->ncol, src->capacity);
381   if (!dest)
382     return 0;
383 
384   memcpy(dest->col, src->col, sizeof(src->col[0]) * src->ncol);
385   return dest;
386 }
387 
388 
389 Gif_Image *
Gif_CopyImage(Gif_Image * src)390 Gif_CopyImage(Gif_Image *src)
391 {
392   Gif_Image *dest;
393   uint8_t *data;
394   int i;
395   if (!src)
396     return 0;
397 
398   dest = Gif_NewImage();
399   if (!dest)
400     return 0;
401 
402   dest->identifier = Gif_CopyString(src->identifier);
403   if (!dest->identifier && src->identifier)
404       goto failure;
405   if (src->comment) {
406       dest->comment = Gif_NewComment();
407       if (!dest->comment)
408         goto failure;
409       for (i = 0; i < src->comment->count; i++)
410         if (!Gif_AddComment(dest->comment, src->comment->str[i],
411                             src->comment->len[i]))
412           goto failure;
413   }
414   if (src->extension_list) {
415       Gif_Extension* gfex = src->extension_list;
416       while (gfex) {
417           Gif_Extension* dstex = Gif_CopyExtension(gfex);
418           if (!dstex)
419               goto failure;
420           Gif_AddExtension(NULL, dest, dstex);
421           gfex = gfex->next;
422       }
423   }
424 
425   dest->local = Gif_CopyColormap(src->local);
426   if (!dest->local && src->local)
427     goto failure;
428   dest->transparent = src->transparent;
429 
430   dest->delay = src->delay;
431   dest->disposal = src->disposal;
432   dest->left = src->left;
433   dest->top = src->top;
434 
435   dest->width = src->width;
436   dest->height = src->height;
437 
438   dest->interlace = src->interlace;
439   if (src->img) {
440     dest->img = Gif_NewArray(uint8_t *, dest->height + 1);
441     dest->image_data = Gif_NewArray(uint8_t, (size_t) dest->width * (size_t) dest->height);
442     dest->free_image_data = Gif_Free;
443     if (!dest->img || !dest->image_data)
444       goto failure;
445     for (i = 0, data = dest->image_data; i < dest->height; i++) {
446       memcpy(data, src->img[i], dest->width);
447       dest->img[i] = data;
448       data += dest->width;
449     }
450     dest->img[dest->height] = 0;
451   }
452   if (src->compressed) {
453     if (src->free_compressed == 0)
454       dest->compressed = src->compressed;
455     else {
456       dest->compressed = Gif_NewArray(uint8_t, src->compressed_len);
457       dest->free_compressed = Gif_Free;
458       memcpy(dest->compressed, src->compressed, src->compressed_len);
459     }
460     dest->compressed_len = src->compressed_len;
461     dest->compressed_errors = src->compressed_errors;
462   }
463 
464   return dest;
465 
466  failure:
467   Gif_DeleteImage(dest);
468   return 0;
469 }
470 
471 
Gif_MakeImageEmpty(Gif_Image * gfi)472 void Gif_MakeImageEmpty(Gif_Image* gfi) {
473     Gif_ReleaseUncompressedImage(gfi);
474     Gif_ReleaseCompressedImage(gfi);
475     gfi->width = gfi->height = 1;
476     gfi->transparent = 0;
477     Gif_CreateUncompressedImage(gfi, 0);
478     gfi->img[0][0] = 0;
479 }
480 
481 
482 /** DELETION **/
483 
484 typedef struct Gif_DeletionHook {
485   int kind;
486   Gif_DeletionHookFunc func;
487   void *callback_data;
488   struct Gif_DeletionHook *next;
489 } Gif_DeletionHook;
490 
491 static Gif_DeletionHook *all_hooks;
492 
493 void
Gif_DeleteStream(Gif_Stream * gfs)494 Gif_DeleteStream(Gif_Stream *gfs)
495 {
496   Gif_DeletionHook *hook;
497   int i;
498   if (!gfs || --gfs->refcount > 0)
499     return;
500 
501   for (i = 0; i < gfs->nimages; i++)
502     Gif_DeleteImage(gfs->images[i]);
503   Gif_DeleteArray(gfs->images);
504 
505   Gif_DeleteColormap(gfs->global);
506 
507   Gif_DeleteComment(gfs->end_comment);
508   while (gfs->end_extension_list)
509       Gif_DeleteExtension(gfs->end_extension_list);
510 
511   for (hook = all_hooks; hook; hook = hook->next)
512     if (hook->kind == GIF_T_STREAM)
513       (*hook->func)(GIF_T_STREAM, gfs, hook->callback_data);
514   Gif_Delete(gfs);
515 }
516 
517 
518 void
Gif_DeleteImage(Gif_Image * gfi)519 Gif_DeleteImage(Gif_Image *gfi)
520 {
521   Gif_DeletionHook *hook;
522   if (!gfi || --gfi->refcount > 0)
523     return;
524 
525   for (hook = all_hooks; hook; hook = hook->next)
526     if (hook->kind == GIF_T_IMAGE)
527       (*hook->func)(GIF_T_IMAGE, gfi, hook->callback_data);
528 
529   Gif_DeleteArray(gfi->identifier);
530   Gif_DeleteComment(gfi->comment);
531   while (gfi->extension_list)
532       Gif_DeleteExtension(gfi->extension_list);
533   Gif_DeleteColormap(gfi->local);
534   if (gfi->image_data && gfi->free_image_data)
535     (*gfi->free_image_data)((void *)gfi->image_data);
536   Gif_DeleteArray(gfi->img);
537   if (gfi->compressed && gfi->free_compressed)
538     (*gfi->free_compressed)((void *)gfi->compressed);
539   if (gfi->user_data && gfi->free_user_data)
540     (*gfi->free_user_data)(gfi->user_data);
541   Gif_Delete(gfi);
542 }
543 
544 
545 void
Gif_DeleteColormap(Gif_Colormap * gfcm)546 Gif_DeleteColormap(Gif_Colormap *gfcm)
547 {
548   Gif_DeletionHook *hook;
549   if (!gfcm || --gfcm->refcount > 0)
550     return;
551 
552   for (hook = all_hooks; hook; hook = hook->next)
553     if (hook->kind == GIF_T_COLORMAP)
554       (*hook->func)(GIF_T_COLORMAP, gfcm, hook->callback_data);
555 
556   Gif_DeleteArray(gfcm->col);
557   Gif_Delete(gfcm);
558 }
559 
560 
561 void
Gif_DeleteComment(Gif_Comment * gfcom)562 Gif_DeleteComment(Gif_Comment *gfcom)
563 {
564   int i;
565   if (!gfcom)
566     return;
567   for (i = 0; i < gfcom->count; i++)
568     Gif_DeleteArray(gfcom->str[i]);
569   Gif_DeleteArray(gfcom->str);
570   Gif_DeleteArray(gfcom->len);
571   Gif_Delete(gfcom);
572 }
573 
574 
575 void
Gif_DeleteExtension(Gif_Extension * gfex)576 Gif_DeleteExtension(Gif_Extension *gfex)
577 {
578   if (!gfex)
579     return;
580   if (gfex->data && gfex->free_data)
581     (*gfex->free_data)(gfex->data);
582   Gif_DeleteArray(gfex->appname);
583   if (gfex->stream || gfex->image) {
584       Gif_Extension** pprev;
585       if (gfex->image)
586           pprev = &gfex->image->extension_list;
587       else
588           pprev = &gfex->stream->end_extension_list;
589       while (*pprev && *pprev != gfex)
590           pprev = &(*pprev)->next;
591       if (*pprev)
592           *pprev = gfex->next;
593   }
594   Gif_Delete(gfex);
595 }
596 
597 
598 /** DELETION HOOKS **/
599 
600 int
Gif_AddDeletionHook(int kind,void (* func)(int,void *,void *),void * cb)601 Gif_AddDeletionHook(int kind, void (*func)(int, void *, void *), void *cb)
602 {
603   Gif_DeletionHook *hook = Gif_New(Gif_DeletionHook);
604   if (!hook)
605     return 0;
606   Gif_RemoveDeletionHook(kind, func, cb);
607   hook->kind = kind;
608   hook->func = func;
609   hook->callback_data = cb;
610   hook->next = all_hooks;
611   all_hooks = hook;
612   return 1;
613 }
614 
615 void
Gif_RemoveDeletionHook(int kind,void (* func)(int,void *,void *),void * cb)616 Gif_RemoveDeletionHook(int kind, void (*func)(int, void *, void *), void *cb)
617 {
618   Gif_DeletionHook *hook = all_hooks, *prev = 0;
619   while (hook) {
620     if (hook->kind == kind && hook->func == func
621         && hook->callback_data == cb) {
622       if (prev)
623         prev->next = hook->next;
624       else
625         all_hooks = hook->next;
626       Gif_Delete(hook);
627       return;
628     }
629     prev = hook;
630     hook = hook->next;
631   }
632 }
633 
634 
635 int
Gif_ColorEq(Gif_Color * c1,Gif_Color * c2)636 Gif_ColorEq(Gif_Color *c1, Gif_Color *c2)
637 {
638   return GIF_COLOREQ(c1, c2);
639 }
640 
641 
642 int
Gif_FindColor(Gif_Colormap * gfcm,Gif_Color * c)643 Gif_FindColor(Gif_Colormap *gfcm, Gif_Color *c)
644 {
645   int i;
646   for (i = 0; i < gfcm->ncol; i++)
647     if (GIF_COLOREQ(&gfcm->col[i], c))
648       return i;
649   return -1;
650 }
651 
652 
653 int
Gif_AddColor(Gif_Colormap * gfcm,Gif_Color * c,int look_from)654 Gif_AddColor(Gif_Colormap *gfcm, Gif_Color *c, int look_from)
655 {
656   int i;
657   if (look_from >= 0)
658     for (i = look_from; i < gfcm->ncol; i++)
659       if (GIF_COLOREQ(&gfcm->col[i], c))
660         return i;
661   if (gfcm->ncol >= gfcm->capacity) {
662     gfcm->capacity *= 2;
663     Gif_ReArray(gfcm->col, Gif_Color, gfcm->capacity);
664     if (gfcm->col == 0)
665       return -1;
666   }
667   i = gfcm->ncol;
668   gfcm->ncol++;
669   gfcm->col[i] = *c;
670   return i;
671 }
672 
673 
674 Gif_Image *
Gif_GetImage(Gif_Stream * gfs,int imagenumber)675 Gif_GetImage(Gif_Stream *gfs, int imagenumber)
676 {
677   if (imagenumber >= 0 && imagenumber < gfs->nimages)
678     return gfs->images[imagenumber];
679   else
680     return 0;
681 }
682 
683 
684 Gif_Image *
Gif_GetNamedImage(Gif_Stream * gfs,const char * name)685 Gif_GetNamedImage(Gif_Stream *gfs, const char *name)
686 {
687   int i;
688 
689   if (!name)
690     return gfs->nimages ? gfs->images[0] : 0;
691 
692   for (i = 0; i < gfs->nimages; i++)
693     if (gfs->images[i]->identifier &&
694         strcmp(gfs->images[i]->identifier, name) == 0)
695       return gfs->images[i];
696 
697   return 0;
698 }
699 
700 
701 void
Gif_ReleaseCompressedImage(Gif_Image * gfi)702 Gif_ReleaseCompressedImage(Gif_Image *gfi)
703 {
704   if (gfi->compressed && gfi->free_compressed)
705     (*gfi->free_compressed)(gfi->compressed);
706   gfi->compressed = 0;
707   gfi->compressed_len = 0;
708   gfi->compressed_errors = 0;
709   gfi->free_compressed = 0;
710 }
711 
712 void
Gif_ReleaseUncompressedImage(Gif_Image * gfi)713 Gif_ReleaseUncompressedImage(Gif_Image *gfi)
714 {
715   Gif_DeleteArray(gfi->img);
716   if (gfi->image_data && gfi->free_image_data)
717     (*gfi->free_image_data)(gfi->image_data);
718   gfi->img = 0;
719   gfi->image_data = 0;
720   gfi->free_image_data = 0;
721 }
722 
723 
724 int
Gif_ClipImage(Gif_Image * gfi,int left,int top,int width,int height)725 Gif_ClipImage(Gif_Image *gfi, int left, int top, int width, int height)
726 {
727   int new_width = gfi->width, new_height = gfi->height;
728   int y;
729 
730   if (!gfi->img)
731     return 0;
732 
733   if (gfi->left < left) {
734     int shift = left - gfi->left;
735     for (y = 0; y < gfi->height; y++)
736       gfi->img[y] += shift;
737     gfi->left += shift;
738     new_width -= shift;
739   }
740 
741   if (gfi->top < top) {
742     int shift = top - gfi->top;
743     for (y = gfi->height - 1; y >= shift; y++)
744       gfi->img[y - shift] = gfi->img[y];
745     gfi->top += shift;
746     new_height -= shift;
747   }
748 
749   if (gfi->left + new_width >= width)
750     new_width = width - gfi->left;
751 
752   if (gfi->top + new_height >= height)
753     new_height = height - gfi->top;
754 
755   if (new_width < 0)
756     new_width = 0;
757   if (new_height < 0)
758     new_height = 0;
759   gfi->width = new_width;
760   gfi->height = new_height;
761   return 1;
762 }
763 
764 
765 int
Gif_InterlaceLine(int line,int height)766 Gif_InterlaceLine(int line, int height)
767 {
768   height--;
769   if (line > height / 2)
770     return line * 2 - ( height       | 1);
771   else if (line > height / 4)
772     return line * 4 - ((height & ~1) | 2);
773   else if (line > height / 8)
774     return line * 8 - ((height & ~3) | 4);
775   else
776     return line * 8;
777 }
778 
779 
780 int
Gif_SetUncompressedImage(Gif_Image * gfi,uint8_t * image_data,void (* free_data)(void *),int data_interlaced)781 Gif_SetUncompressedImage(Gif_Image *gfi, uint8_t *image_data,
782                          void (*free_data)(void *), int data_interlaced)
783 {
784   /* NB does not affect compressed image (and must not) */
785   unsigned i;
786   unsigned width = gfi->width;
787   unsigned height = gfi->height;
788   uint8_t **img;
789 
790   Gif_ReleaseUncompressedImage(gfi);
791   if (!image_data)
792     return 0;
793 
794   img = Gif_NewArray(uint8_t *, height + 1);
795   if (!img)
796     return 0;
797 
798   if (data_interlaced)
799     for (i = 0; i < height; i++)
800       img[ Gif_InterlaceLine(i, height) ] = image_data + width * i;
801   else
802     for (i = 0; i < height; i++)
803       img[i] = image_data + width * i;
804   img[height] = 0;
805 
806   gfi->img = img;
807   gfi->image_data = image_data;
808   gfi->free_image_data = free_data;
809   return 1;
810 }
811 
812 int
Gif_CreateUncompressedImage(Gif_Image * gfi,int data_interlaced)813 Gif_CreateUncompressedImage(Gif_Image *gfi, int data_interlaced)
814 {
815     size_t sz = (size_t) gfi->width * (size_t) gfi->height;
816     uint8_t *data = Gif_NewArray(uint8_t, sz ? sz : 1);
817     return Gif_SetUncompressedImage(gfi, data, Gif_Free, data_interlaced);
818 }
819 
820 void
Gif_InitCompressInfo(Gif_CompressInfo * gcinfo)821 Gif_InitCompressInfo(Gif_CompressInfo *gcinfo)
822 {
823     gcinfo->flags = 0;
824     gcinfo->loss = 0;
825 }
826 
827 
828 void
Gif_Debug(char * x,...)829 Gif_Debug(char *x, ...)
830 {
831     va_list val;
832     va_start(val, x);
833     vfprintf(stderr, x, val);
834     va_end(val);
835 }
836 
837 
838 #if !GIF_ALLOCATOR_DEFINED
Gif_Realloc(void * p,size_t s,size_t n,const char * file,int line)839 void* Gif_Realloc(void* p, size_t s, size_t n, const char* file, int line) {
840     (void) file, (void) line;
841     if (s == 0 || n == 0)
842         Gif_Free(p);
843     else if (s == 1 || n == 1 || s <= ((size_t) -1) / n)
844         return realloc(p, s * n);
845     return (void*) 0;
846 }
847 
848 #undef Gif_Free
Gif_Free(void * p)849 void Gif_Free(void* p) {
850     free(p);
851 }
852 #endif
853 
854 #ifdef __cplusplus
855 }
856 #endif
857