]> git.imager.perl.org - imager.git/blame - GIF/imgif.c
0.84_01 development release
[imager.git] / GIF / imgif.c
CommitLineData
ec6d8908 1#include "imgif.h"
02d1d628 2#include <gif_lib.h>
55f2fb90 3#ifdef _MSC_VER
10461f9a
TC
4#include <io.h>
5#else
6#include <unistd.h>
7#endif
8#include <errno.h>
ec6d8908 9#include <string.h>
4922fb3a 10#include <stdlib.h>
8d14daab 11#include <stdio.h>
5b0d044f 12
a3923855
TC
13/*
14=head1 NAME
15
4922fb3a 16imgif.c - read and write gif files for Imager
a3923855
TC
17
18=head1 SYNOPSIS
19
20 i_img *img;
21 i_img *imgs[count];
22 int fd;
23 int *colour_table,
24 int colours;
25 int max_colours; // number of bits per colour
26 int pixdev; // how much noise to add
27 i_color fixed[N]; // fixed palette entries
28 int fixedlen; // number of fixed colours
29 int success; // non-zero on success
3bb1c1f3 30 char *data; // a GIF file in memory
a3923855
TC
31 int length; // how big data is
32 int reader(char *, char *, int, int);
33 int writer(char *, char *, int);
34 char *userdata; // user's data, whatever it is
35 i_quantize quant;
36 i_gif_opts opts;
37
38 img = i_readgif(fd, &colour_table, &colours);
39 success = i_writegif(img, fd, max_colours, pixdev, fixedlen, fixed);
40 success = i_writegifmc(img, fd, max_colours);
41 img = i_readgif_scalar(data, length, &colour_table, &colours);
42 img = i_readgif_callback(cb, userdata, &colour_table, &colours);
43 success = i_writegif_gen(&quant, fd, imgs, count, &opts);
44 success = i_writegif_callback(&quant, writer, userdata, maxlength,
45 imgs, count, &opts);
46
47=head1 DESCRIPTION
48
49This source file provides the C level interface to reading and writing
50GIF files for Imager.
51
52This has been tested with giflib 3 and 4, though you lose the callback
53functionality with giflib3.
54
55=head1 REFERENCE
56
57=over
58
59=cut
60*/
02d1d628 61
c48818b1 62static char const *gif_error_msg(int code);
faa9b3e7 63static void gif_push_error(void);
c48818b1 64
02d1d628
AMH
65/* Make some variables global, so we could access them faster: */
66
67static int
2ff8ed30
AMH
68 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
69 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
7828ca98 70
02d1d628 71
7828ca98
AMH
72
73static
74void
2ff8ed30 75i_colortable_copy(int **colour_table, int *colours, ColorMapObject *colourmap) {
7828ca98
AMH
76 GifColorType *mapentry;
77 int q;
2ff8ed30 78 int colourmapsize = colourmap->ColorCount;
7828ca98 79
2ff8ed30 80 if(colours) *colours = colourmapsize;
7828ca98
AMH
81 if(!colour_table) return;
82
2d20f216
TC
83 *colour_table = mymalloc(sizeof(int) * colourmapsize * 3);
84 memset(*colour_table, 0, sizeof(int) * colourmapsize * 3);
7828ca98 85
2ff8ed30
AMH
86 for(q=0; q<colourmapsize; q++) {
87 mapentry = &colourmap->Colors[q];
7828ca98
AMH
88 (*colour_table)[q*3 + 0] = mapentry->Red;
89 (*colour_table)[q*3 + 1] = mapentry->Green;
90 (*colour_table)[q*3 + 2] = mapentry->Blue;
91 }
92}
93
4922fb3a
TC
94static const
95char gif_version_str[] = GIF_LIB_VERSION;
96
97double
ec6d8908 98i_giflib_version(void) {
4922fb3a
TC
99 const char *p = gif_version_str;
100
101 while (*p && (*p < '0' || *p > '9'))
102 ++p;
103
104 if (!*p)
105 return 0;
106
107 return strtod(p, NULL);
ec6d8908 108}
7828ca98 109
a3923855
TC
110/*
111=item i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours)
112
113Internal. Low-level function for reading a GIF file. The caller must
114create the appropriate GifFileType object and pass it in.
115
116=cut
117*/
7828ca98 118
02d1d628
AMH
119i_img *
120i_readgif_low(GifFileType *GifFile, int **colour_table, int *colours) {
121 i_img *im;
122 int i, j, Size, Row, Col, Width, Height, ExtCode, Count, x;
2ff8ed30
AMH
123 int cmapcnt = 0, ImageNum = 0, BackGround = 0, ColorMapSize = 0;
124 ColorMapObject *ColorMap;
98c79495 125
02d1d628
AMH
126 GifRecordType RecordType;
127 GifByteType *Extension;
128
129 GifRowType GifRow;
130 static GifColorType *ColorMapEntry;
131 i_color col;
132
02d1d628
AMH
133 mm_log((1,"i_readgif_low(GifFile %p, colour_table %p, colours %p)\n", GifFile, colour_table, colours));
134
e6172a17
TC
135 /* it's possible that the caller has called us with *colour_table being
136 non-NULL, but we check that to see if we need to free an allocated
137 colour table on error.
138 */
895dbd34 139 if (colour_table) *colour_table = NULL;
e6172a17 140
02d1d628
AMH
141 BackGround = GifFile->SBackGroundColor;
142 ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap);
02d1d628 143
7828ca98
AMH
144 if (ColorMap) {
145 ColorMapSize = ColorMap->ColorCount;
146 i_colortable_copy(colour_table, colours, ColorMap);
147 cmapcnt++;
02d1d628 148 }
02d1d628 149
77157728
TC
150 if (!i_int_check_image_file_limits(GifFile->SWidth, GifFile->SHeight, 3, sizeof(i_sample_t))) {
151 if (colour_table && *colour_table) {
152 myfree(*colour_table);
153 *colour_table = NULL;
154 }
155 DGifCloseFile(GifFile);
156 mm_log((1, "i_readgif: image size exceeds limits\n"));
157 return NULL;
158 }
7828ca98 159
ec6d8908 160 im = i_img_8_new(GifFile->SWidth, GifFile->SHeight, 3);
8c68bf11
TC
161 if (!im) {
162 if (colour_table && *colour_table) {
163 myfree(*colour_table);
164 *colour_table = NULL;
165 }
166 DGifCloseFile(GifFile);
167 return NULL;
168 }
7828ca98 169
02d1d628
AMH
170 Size = GifFile->SWidth * sizeof(GifPixelType);
171
a73aeb5f 172 GifRow = mymalloc(Size);
02d1d628
AMH
173
174 for (i = 0; i < GifFile->SWidth; i++) GifRow[i] = GifFile->SBackGroundColor;
175
176 /* Scan the content of the GIF file and load the image(s) in: */
177 do {
178 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
77fc6afd
TC
179 gif_push_error();
180 i_push_error(0, "Unable to get record type");
e6172a17 181 if (colour_table && *colour_table) {
ea9f6207 182 myfree(*colour_table);
e6172a17
TC
183 *colour_table = NULL;
184 }
a73aeb5f 185 myfree(GifRow);
77fc6afd
TC
186 i_img_destroy(im);
187 DGifCloseFile(GifFile);
188 return NULL;
02d1d628
AMH
189 }
190
191 switch (RecordType) {
192 case IMAGE_DESC_RECORD_TYPE:
193 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
77fc6afd
TC
194 gif_push_error();
195 i_push_error(0, "Unable to get image descriptor");
e6172a17 196 if (colour_table && *colour_table) {
ea9f6207 197 myfree(*colour_table);
e6172a17
TC
198 *colour_table = NULL;
199 }
a73aeb5f 200 myfree(GifRow);
77fc6afd
TC
201 i_img_destroy(im);
202 DGifCloseFile(GifFile);
203 return NULL;
02d1d628 204 }
7828ca98 205
43a881d3
TC
206 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
207 mm_log((1, "Adding local colormap\n"));
208 ColorMapSize = ColorMap->ColorCount;
209 if ( cmapcnt == 0) {
7828ca98
AMH
210 i_colortable_copy(colour_table, colours, ColorMap);
211 cmapcnt++;
7828ca98 212 }
43a881d3
TC
213 } else {
214 /* No colormap and we are about to read in the image - abandon for now */
215 mm_log((1, "Going in with no colormap\n"));
216 i_push_error(0, "Image does not have a local or a global color map");
217 /* we can't have allocated a colour table here */
a73aeb5f 218 myfree(GifRow);
43a881d3
TC
219 i_img_destroy(im);
220 DGifCloseFile(GifFile);
221 return NULL;
7828ca98 222 }
43a881d3 223
02d1d628
AMH
224 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
225 Col = GifFile->Image.Left;
226 Width = GifFile->Image.Width;
227 Height = GifFile->Image.Height;
228 ImageNum++;
895dbd34 229 mm_log((1,"i_readgif_low: Image %d at (%d, %d) [%dx%d]: \n",ImageNum, Col, Row, Width, Height));
02d1d628
AMH
230
231 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
232 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
77fc6afd 233 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
e6172a17 234 if (colour_table && *colour_table) {
ea9f6207 235 myfree(*colour_table);
e6172a17
TC
236 *colour_table = NULL;
237 }
a73aeb5f 238 myfree(GifRow);
77fc6afd
TC
239 i_img_destroy(im);
240 DGifCloseFile(GifFile);
a73aeb5f 241 return NULL;
02d1d628
AMH
242 }
243 if (GifFile->Image.Interlace) {
244
245 for (Count = i = 0; i < 4; i++) for (j = Row + InterlacedOffset[i]; j < Row + Height; j += InterlacedJumps[i]) {
246 Count++;
43a881d3 247 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
77fc6afd
TC
248 gif_push_error();
249 i_push_error(0, "Reading GIF line");
e6172a17 250 if (colour_table && *colour_table) {
ea9f6207 251 myfree(*colour_table);
e6172a17
TC
252 *colour_table = NULL;
253 }
a73aeb5f 254 myfree(GifRow);
77fc6afd
TC
255 i_img_destroy(im);
256 DGifCloseFile(GifFile);
257 return NULL;
02d1d628
AMH
258 }
259
43a881d3 260 for (x = 0; x < Width; x++) {
02d1d628
AMH
261 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
262 col.rgb.r = ColorMapEntry->Red;
263 col.rgb.g = ColorMapEntry->Green;
264 col.rgb.b = ColorMapEntry->Blue;
43a881d3 265 i_ppix(im,Col+x,j,&col);
02d1d628
AMH
266 }
267
268 }
269 }
270 else {
271 for (i = 0; i < Height; i++) {
43a881d3 272 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
77fc6afd
TC
273 gif_push_error();
274 i_push_error(0, "Reading GIF line");
e6172a17 275 if (colour_table && *colour_table) {
ea9f6207 276 myfree(*colour_table);
e6172a17
TC
277 *colour_table = NULL;
278 }
a73aeb5f 279 myfree(GifRow);
77fc6afd
TC
280 i_img_destroy(im);
281 DGifCloseFile(GifFile);
282 return NULL;
02d1d628
AMH
283 }
284
43a881d3 285 for (x = 0; x < Width; x++) {
02d1d628
AMH
286 ColorMapEntry = &ColorMap->Colors[GifRow[x]];
287 col.rgb.r = ColorMapEntry->Red;
288 col.rgb.g = ColorMapEntry->Green;
289 col.rgb.b = ColorMapEntry->Blue;
43a881d3 290 i_ppix(im, Col+x, Row, &col);
02d1d628
AMH
291 }
292 Row++;
293 }
294 }
295 break;
296 case EXTENSION_RECORD_TYPE:
297 /* Skip any extension blocks in file: */
298 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
77fc6afd
TC
299 gif_push_error();
300 i_push_error(0, "Reading extension record");
e6172a17 301 if (colour_table && *colour_table) {
ea9f6207 302 myfree(*colour_table);
e6172a17
TC
303 *colour_table = NULL;
304 }
a73aeb5f 305 myfree(GifRow);
77fc6afd
TC
306 i_img_destroy(im);
307 DGifCloseFile(GifFile);
308 return NULL;
02d1d628
AMH
309 }
310 while (Extension != NULL) {
311 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
77fc6afd
TC
312 gif_push_error();
313 i_push_error(0, "reading next block of extension");
e6172a17 314 if (colour_table && *colour_table) {
ea9f6207 315 myfree(*colour_table);
e6172a17
TC
316 *colour_table = NULL;
317 }
a73aeb5f 318 myfree(GifRow);
77fc6afd
TC
319 i_img_destroy(im);
320 DGifCloseFile(GifFile);
321 return NULL;
02d1d628
AMH
322 }
323 }
324 break;
325 case TERMINATE_RECORD_TYPE:
326 break;
327 default: /* Should be traps by DGifGetRecordType. */
328 break;
329 }
330 } while (RecordType != TERMINATE_RECORD_TYPE);
331
332 myfree(GifRow);
333
334 if (DGifCloseFile(GifFile) == GIF_ERROR) {
77fc6afd
TC
335 gif_push_error();
336 i_push_error(0, "Closing GIF file object");
e6172a17 337 if (colour_table && *colour_table) {
ea9f6207 338 myfree(*colour_table);
e6172a17
TC
339 *colour_table = NULL;
340 }
77fc6afd
TC
341 i_img_destroy(im);
342 return NULL;
02d1d628 343 }
8c68bf11 344
ec6d8908 345 i_tags_set(&im->tags, "i_format", "gif", -1);
8c68bf11 346
02d1d628
AMH
347 return im;
348}
349
faa9b3e7
TC
350/*
351
352Internal function called by i_readgif_multi_low() in error handling
353
354*/
355static void free_images(i_img **imgs, int count) {
356 int i;
e310e5f9
TC
357
358 if (count) {
359 for (i = 0; i < count; ++i)
360 i_img_destroy(imgs[i]);
361 myfree(imgs);
362 }
faa9b3e7
TC
363}
364
365/*
f1adece7 366=item i_readgif_multi_low(GifFileType *gf, int *count, int page)
faa9b3e7
TC
367
368Reads one of more gif images from the given GIF file.
369
370Returns a pointer to an array of i_img *, and puts the count into
371*count.
372
f1adece7
TC
373If page is not -1 then the given image _only_ is returned from the
374file, where the first image is 0, the second 1 and so on.
375
faa9b3e7
TC
376Unlike the normal i_readgif*() functions the images are paletted
377images rather than a combined RGB image.
378
379This functions sets tags on the images returned:
380
381=over
382
383=item gif_left
384
385the offset of the image from the left of the "screen" ("Image Left
386Position")
387
388=item gif_top
389
390the offset of the image from the top of the "screen" ("Image Top Position")
391
392=item gif_interlace
393
394non-zero if the image was interlaced ("Interlace Flag")
395
396=item gif_screen_width
397
398=item gif_screen_height
399
400the size of the logical screen ("Logical Screen Width",
401"Logical Screen Height")
402
403=item gif_local_map
404
405Non-zero if this image had a local color map.
406
407=item gif_background
408
409The index in the global colormap of the logical screen's background
410color. This is only set if the current image uses the global
411colormap.
412
413=item gif_trans_index
414
415The index of the color in the colormap used for transparency. If the
416image has a transparency then it is returned as a 4 channel image with
417the alpha set to zero in this palette entry. ("Transparent Color Index")
418
419=item gif_delay
420
421The delay until the next frame is displayed, in 1/100 of a second.
422("Delay Time").
423
424=item gif_user_input
425
426whether or not a user input is expected before continuing (view dependent)
427("User Input Flag").
428
429=item gif_disposal
430
431how the next frame is displayed ("Disposal Method")
432
433=item gif_loop
434
435the number of loops from the Netscape Loop extension. This may be zero.
436
437=item gif_comment
438
439the first block of the first gif comment before each image.
440
441=back
442
443Where applicable, the ("name") is the name of that field from the GIF89
444standard.
445
446=cut
447*/
448
f1adece7 449i_img **i_readgif_multi_low(GifFileType *GifFile, int *count, int page) {
faa9b3e7 450 i_img *img;
a659442a 451 int i, j, Size, Width, Height, ExtCode, Count;
faa9b3e7
TC
452 int ImageNum = 0, BackGround = 0, ColorMapSize = 0;
453 ColorMapObject *ColorMap;
454
455 GifRecordType RecordType;
456 GifByteType *Extension;
457
458 GifRowType GifRow;
459 int got_gce = 0;
336f5078
TC
460 int trans_index = 0; /* transparent index if we see a GCE */
461 int gif_delay = 0; /* delay from a GCE */
462 int user_input = 0; /* user input flag from a GCE */
463 int disposal = 0; /* disposal method from a GCE */
faa9b3e7 464 int got_ns_loop = 0;
336f5078 465 int ns_loop = 0;
faa9b3e7
TC
466 char *comment = NULL; /* a comment */
467 i_img **results = NULL;
468 int result_alloc = 0;
469 int channels;
bcff4dd9 470 int image_colors = 0;
919e0000
TC
471 i_color black; /* used to expand the palette if needed */
472
473 for (i = 0; i < MAXCHANNELS; ++i)
474 black.channel[i] = 0;
faa9b3e7
TC
475
476 *count = 0;
477
478 mm_log((1,"i_readgif_multi_low(GifFile %p, , count %p)\n", GifFile, count));
479
480 BackGround = GifFile->SBackGroundColor;
481
482 Size = GifFile->SWidth * sizeof(GifPixelType);
483
ec6d8908 484 GifRow = (GifRowType) mymalloc(Size);
faa9b3e7
TC
485
486 /* Scan the content of the GIF file and load the image(s) in: */
487 do {
488 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
489 gif_push_error();
490 i_push_error(0, "Unable to get record type");
491 free_images(results, *count);
492 DGifCloseFile(GifFile);
f1adece7 493 myfree(GifRow);
b4810f72
TC
494 if (comment)
495 myfree(comment);
faa9b3e7
TC
496 return NULL;
497 }
498
499 switch (RecordType) {
500 case IMAGE_DESC_RECORD_TYPE:
501 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
502 gif_push_error();
503 i_push_error(0, "Unable to get image descriptor");
504 free_images(results, *count);
505 DGifCloseFile(GifFile);
f1adece7 506 myfree(GifRow);
b4810f72
TC
507 if (comment)
508 myfree(comment);
faa9b3e7
TC
509 return NULL;
510 }
511
faa9b3e7
TC
512 Width = GifFile->Image.Width;
513 Height = GifFile->Image.Height;
f1adece7
TC
514 if (page == -1 || page == ImageNum) {
515 if (( ColorMap = (GifFile->Image.ColorMap ? GifFile->Image.ColorMap : GifFile->SColorMap) )) {
516 mm_log((1, "Adding local colormap\n"));
517 ColorMapSize = ColorMap->ColorCount;
518 } else {
519 /* No colormap and we are about to read in the image -
520 abandon for now */
521 mm_log((1, "Going in with no colormap\n"));
522 i_push_error(0, "Image does not have a local or a global color map");
523 free_images(results, *count);
524 DGifCloseFile(GifFile);
525 myfree(GifRow);
b4810f72
TC
526 if (comment)
527 myfree(comment);
f1adece7
TC
528 return NULL;
529 }
530
531 channels = 3;
532 if (got_gce && trans_index >= 0)
533 channels = 4;
534 if (!i_int_check_image_file_limits(Width, Height, channels, sizeof(i_sample_t))) {
535 free_images(results, *count);
536 mm_log((1, "i_readgif: image size exceeds limits\n"));
35ccc108 537 DGifCloseFile(GifFile);
f1adece7 538 myfree(GifRow);
b4810f72
TC
539 if (comment)
540 myfree(comment);
f1adece7
TC
541 return NULL;
542 }
543 img = i_img_pal_new(Width, Height, channels, 256);
544 if (!img) {
545 free_images(results, *count);
35ccc108 546 DGifCloseFile(GifFile);
b4810f72
TC
547 if (comment)
548 myfree(comment);
549 myfree(GifRow);
f1adece7
TC
550 return NULL;
551 }
552 /* populate the palette of the new image */
553 mm_log((1, "ColorMapSize %d\n", ColorMapSize));
554 for (i = 0; i < ColorMapSize; ++i) {
555 i_color col;
556 col.rgba.r = ColorMap->Colors[i].Red;
557 col.rgba.g = ColorMap->Colors[i].Green;
558 col.rgba.b = ColorMap->Colors[i].Blue;
559 if (channels == 4 && trans_index == i)
560 col.rgba.a = 0;
561 else
562 col.rgba.a = 255;
563
564 i_addcolors(img, &col, 1);
565 }
bcff4dd9 566 image_colors = ColorMapSize;
f1adece7
TC
567 ++*count;
568 if (*count > result_alloc) {
569 if (result_alloc == 0) {
570 result_alloc = 5;
571 results = mymalloc(result_alloc * sizeof(i_img *));
572 }
573 else {
574 /* myrealloc never fails (it just dies if it can't allocate) */
575 result_alloc *= 2;
576 results = myrealloc(results, result_alloc * sizeof(i_img *));
577 }
578 }
579 results[*count-1] = img;
ec6d8908
TC
580 i_tags_set(&img->tags, "i_format", "gif", -1);
581 i_tags_setn(&img->tags, "gif_left", GifFile->Image.Left);
f1adece7 582 /**(char *)0 = 1;*/
ec6d8908
TC
583 i_tags_setn(&img->tags, "gif_top", GifFile->Image.Top);
584 i_tags_setn(&img->tags, "gif_interlace", GifFile->Image.Interlace);
585 i_tags_setn(&img->tags, "gif_screen_width", GifFile->SWidth);
586 i_tags_setn(&img->tags, "gif_screen_height", GifFile->SHeight);
587 i_tags_setn(&img->tags, "gif_colormap_size", ColorMapSize);
f1adece7 588 if (GifFile->SColorMap && !GifFile->Image.ColorMap) {
ec6d8908 589 i_tags_setn(&img->tags, "gif_background",
f1adece7
TC
590 GifFile->SBackGroundColor);
591 }
592 if (GifFile->Image.ColorMap) {
ec6d8908 593 i_tags_setn(&img->tags, "gif_localmap", 1);
f1adece7
TC
594 }
595 if (got_gce) {
596 if (trans_index >= 0) {
597 i_color trans;
ec6d8908 598 i_tags_setn(&img->tags, "gif_trans_index", trans_index);
f1adece7
TC
599 i_getcolors(img, trans_index, &trans, 1);
600 i_tags_set_color(&img->tags, "gif_trans_color", 0, &trans);
601 }
ec6d8908
TC
602 i_tags_setn(&img->tags, "gif_delay", gif_delay);
603 i_tags_setn(&img->tags, "gif_user_input", user_input);
604 i_tags_setn(&img->tags, "gif_disposal", disposal);
f1adece7
TC
605 }
606 got_gce = 0;
607 if (got_ns_loop)
ec6d8908 608 i_tags_setn(&img->tags, "gif_loop", ns_loop);
f1adece7 609 if (comment) {
ec6d8908 610 i_tags_set(&img->tags, "gif_comment", comment, strlen(comment));
f1adece7
TC
611 myfree(comment);
612 comment = NULL;
613 }
614
615 mm_log((1,"i_readgif_multi_low: Image %d at (%d, %d) [%dx%d]: \n",
616 ImageNum, GifFile->Image.Left, GifFile->Image.Top, Width, Height));
617
618 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
619 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
620 i_push_errorf(0, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
621 free_images(results, *count);
622 DGifCloseFile(GifFile);
623 myfree(GifRow);
b4810f72
TC
624 if (comment)
625 myfree(comment);
f1adece7
TC
626 return(0);
627 }
628
629 if (GifFile->Image.Interlace) {
630 for (Count = i = 0; i < 4; i++) {
631 for (j = InterlacedOffset[i]; j < Height;
632 j += InterlacedJumps[i]) {
633 Count++;
634 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
635 gif_push_error();
636 i_push_error(0, "Reading GIF line");
637 free_images(results, *count);
638 DGifCloseFile(GifFile);
639 myfree(GifRow);
b4810f72
TC
640 if (comment)
641 myfree(comment);
f1adece7
TC
642 return NULL;
643 }
bcff4dd9
TC
644
645 /* range check the scanline if needed */
646 if (image_colors != 256) {
647 int x;
648 for (x = 0; x < Width; ++x) {
649 while (GifRow[x] >= image_colors) {
650 /* expand the palette since a palette index is too big */
651 i_addcolors(img, &black, 1);
652 ++image_colors;
653 }
654 }
655 }
656
f1adece7
TC
657 i_ppal(img, 0, Width, j, GifRow);
658 }
659 }
660 }
661 else {
662 for (i = 0; i < Height; i++) {
663 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
664 gif_push_error();
665 i_push_error(0, "Reading GIF line");
666 free_images(results, *count);
667 DGifCloseFile(GifFile);
668 myfree(GifRow);
b4810f72
TC
669 if (comment)
670 myfree(comment);
f1adece7
TC
671 return NULL;
672 }
673
bcff4dd9
TC
674 /* range check the scanline if needed */
675 if (image_colors != 256) {
676 int x;
677 for (x = 0; x < Width; ++x) {
678 while (GifRow[x] >= image_colors) {
679 /* expand the palette since a palette index is too big */
680 i_addcolors(img, &black, 1);
681 ++image_colors;
682 }
683 }
684 }
685
f1adece7
TC
686 i_ppal(img, 0, Width, i, GifRow);
687 }
688 }
faa9b3e7 689
f1adece7
TC
690 /* must be only one image wanted and that was it */
691 if (page != -1) {
692 myfree(GifRow);
693 DGifCloseFile(GifFile);
b4810f72
TC
694 if (comment)
695 myfree(comment);
f1adece7 696 return results;
faa9b3e7
TC
697 }
698 }
699 else {
f1adece7
TC
700 /* skip the image */
701 /* whether interlaced or not, it has the same number of lines */
702 /* giflib does't have an interface to skip the image data */
faa9b3e7
TC
703 for (i = 0; i < Height; i++) {
704 if (DGifGetLine(GifFile, GifRow, Width) == GIF_ERROR) {
705 gif_push_error();
706 i_push_error(0, "Reading GIF line");
f1adece7
TC
707 free_images(results, *count);
708 myfree(GifRow);
faa9b3e7 709 DGifCloseFile(GifFile);
b4810f72
TC
710 if (comment)
711 myfree(comment);
faa9b3e7
TC
712 return NULL;
713 }
f1adece7 714 }
faa9b3e7 715
f1adece7
TC
716 /* kill the comment so we get the right comment for the page */
717 if (comment) {
718 myfree(comment);
719 comment = NULL;
faa9b3e7
TC
720 }
721 }
f1adece7 722 ImageNum++;
faa9b3e7
TC
723 break;
724 case EXTENSION_RECORD_TYPE:
725 /* Skip any extension blocks in file: */
726 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
727 gif_push_error();
728 i_push_error(0, "Reading extension record");
729 free_images(results, *count);
b4810f72 730 myfree(GifRow);
faa9b3e7 731 DGifCloseFile(GifFile);
b4810f72
TC
732 if (comment)
733 myfree(comment);
faa9b3e7
TC
734 return NULL;
735 }
2b82e731
TC
736 /* possibly this should be an error, but "be liberal in what you accept" */
737 if (!Extension)
738 break;
faa9b3e7
TC
739 if (ExtCode == 0xF9) {
740 got_gce = 1;
741 if (Extension[1] & 1)
742 trans_index = Extension[4];
743 else
744 trans_index = -1;
745 gif_delay = Extension[2] + 256 * Extension[3];
336f5078
TC
746 user_input = (Extension[1] & 2) != 0;
747 disposal = (Extension[1] >> 2) & 7;
faa9b3e7
TC
748 }
749 if (ExtCode == 0xFF && *Extension == 11) {
750 if (memcmp(Extension+1, "NETSCAPE2.0", 11) == 0) {
751 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
752 gif_push_error();
753 i_push_error(0, "reading loop extension");
754 free_images(results, *count);
b4810f72
TC
755 myfree(GifRow);
756 DGifCloseFile(GifFile);
757 if (comment)
758 myfree(comment);
faa9b3e7
TC
759 return NULL;
760 }
761 if (Extension && *Extension == 3) {
762 got_ns_loop = 1;
763 ns_loop = Extension[2] + 256 * Extension[3];
764 }
765 }
766 }
767 else if (ExtCode == 0xFE) {
768 /* while it's possible for a GIF file to contain more than one
769 comment, I'm only implementing a single comment per image,
770 with the comment saved into the following image.
771 If someone wants more than that they can implement it.
772 I also don't handle comments that take more than one block.
773 */
774 if (!comment) {
775 comment = mymalloc(*Extension+1);
776 memcpy(comment, Extension+1, *Extension);
777 comment[*Extension] = '\0';
778 }
779 }
780 while (Extension != NULL) {
781 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
782 gif_push_error();
783 i_push_error(0, "reading next block of extension");
784 free_images(results, *count);
b4810f72 785 myfree(GifRow);
faa9b3e7 786 DGifCloseFile(GifFile);
b4810f72
TC
787 if (comment)
788 myfree(comment);
faa9b3e7
TC
789 return NULL;
790 }
791 }
792 break;
793 case TERMINATE_RECORD_TYPE:
794 break;
795 default: /* Should be trapped by DGifGetRecordType. */
796 break;
797 }
798 } while (RecordType != TERMINATE_RECORD_TYPE);
799
800 if (comment) {
801 if (*count) {
ec6d8908
TC
802 i_tags_set(&(results[*count-1]->tags), "gif_comment", comment,
803 strlen(comment));
faa9b3e7
TC
804 }
805 myfree(comment);
806 }
807
808 myfree(GifRow);
809
810 if (DGifCloseFile(GifFile) == GIF_ERROR) {
811 gif_push_error();
812 i_push_error(0, "Closing GIF file object");
813 free_images(results, *count);
814 return NULL;
815 }
816
f1adece7
TC
817 if (ImageNum && page != -1) {
818 /* there were images, but the page selected wasn't found */
819 i_push_errorf(0, "page %d not found (%d total)", page, ImageNum);
820 free_images(results, *count);
821 return NULL;
822 }
823
faa9b3e7
TC
824 return results;
825}
826
10461f9a 827static int io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length);
10461f9a
TC
828
829/*
830=item i_readgif_multi_wiol(ig, int *count)
831
832=cut
833*/
834
835i_img **
836i_readgif_multi_wiol(io_glue *ig, int *count) {
faa9b3e7 837 GifFileType *GifFile;
faa9b3e7 838
faa9b3e7
TC
839 i_clear_error();
840
ec6d8908 841 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
faa9b3e7
TC
842 gif_push_error();
843 i_push_error(0, "Cannot create giflib callback object");
ec6d8908 844 mm_log((1,"i_readgif_multi_wiol: Unable to open callback datasource.\n"));
faa9b3e7
TC
845 return NULL;
846 }
ec6d8908 847
f1adece7 848 return i_readgif_multi_low(GifFile, count, -1);
faa9b3e7
TC
849}
850
10461f9a
TC
851static int
852io_glue_read_cb(GifFileType *gft, GifByteType *buf, int length) {
853 io_glue *ig = (io_glue *)gft->UserData;
854
855 return ig->readcb(ig, buf, length);
856}
857
10461f9a
TC
858i_img *
859i_readgif_wiol(io_glue *ig, int **color_table, int *colors) {
ec6d8908 860 GifFileType *GifFile;
10461f9a 861
10461f9a 862 i_clear_error();
10461f9a 863
ec6d8908
TC
864 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
865 gif_push_error();
866 i_push_error(0, "Cannot create giflib callback object");
867 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
868 return NULL;
10461f9a 869 }
ec6d8908
TC
870
871 return i_readgif_low(GifFile, color_table, colors);
10461f9a
TC
872}
873
f1adece7
TC
874/*
875=item i_readgif_single_low(GifFile, page)
876
877Lower level function to read a single image from a GIF.
878
879page must be non-negative.
880
881=cut
882*/
883static i_img *
884i_readgif_single_low(GifFileType *GifFile, int page) {
885 int count = 0;
886 i_img **imgs;
887
888 imgs = i_readgif_multi_low(GifFile, &count, page);
889
890 if (imgs && count) {
891 i_img *result = imgs[0];
892
893 myfree(imgs);
894 return result;
895 }
896 else {
897 /* i_readgif_multi_low() handles the errors appropriately */
898 return NULL;
899 }
900}
901
902/*
903=item i_readgif_single_wiol(ig, page)
904
905Read a single page from a GIF image file, where the page is indexed
906from 0.
907
908Returns NULL if the page isn't found.
909
910=cut
911*/
912
913i_img *
914i_readgif_single_wiol(io_glue *ig, int page) {
f1adece7
TC
915 i_clear_error();
916
917 if (page < 0) {
918 i_push_error(0, "page must be non-negative");
919 return NULL;
920 }
921
ec6d8908 922 GifFileType *GifFile;
f1adece7 923
ec6d8908
TC
924 if ((GifFile = DGifOpen((void *)ig, io_glue_read_cb )) == NULL) {
925 gif_push_error();
926 i_push_error(0, "Cannot create giflib callback object");
927 mm_log((1,"i_readgif_wiol: Unable to open callback datasource.\n"));
f1adece7 928 return NULL;
f1adece7 929 }
ec6d8908
TC
930
931 return i_readgif_single_low(GifFile, page);
f1adece7
TC
932}
933
a3923855
TC
934/*
935=item do_write(GifFileType *gf, i_gif_opts *opts, i_img *img, i_palidx *data)
936
937Internal. Low level image write function. Writes in interlace if
938that was requested in the GIF options.
939
940Returns non-zero on success.
941
942=cut
943*/
02d1d628 944static undef_int
97c4effc
TC
945do_write(GifFileType *gf, int interlace, i_img *img, i_palidx *data) {
946 if (interlace) {
02d1d628
AMH
947 int i, j;
948 for (i = 0; i < 4; ++i) {
949 for (j = InterlacedOffset[i]; j < img->ysize; j += InterlacedJumps[i]) {
950 if (EGifPutLine(gf, data+j*img->xsize, img->xsize) == GIF_ERROR) {
c48818b1
TC
951 gif_push_error();
952 i_push_error(0, "Could not save image data:");
02d1d628
AMH
953 mm_log((1, "Error in EGifPutLine\n"));
954 EGifCloseFile(gf);
955 return 0;
956 }
957 }
958 }
959 }
960 else {
961 int y;
962 for (y = 0; y < img->ysize; ++y) {
963 if (EGifPutLine(gf, data, img->xsize) == GIF_ERROR) {
c48818b1
TC
964 gif_push_error();
965 i_push_error(0, "Could not save image data:");
02d1d628
AMH
966 mm_log((1, "Error in EGifPutLine\n"));
967 EGifCloseFile(gf);
968 return 0;
969 }
970 data += img->xsize;
971 }
972 }
973
974 return 1;
975}
976
a3923855
TC
977/*
978=item do_gce(GifFileType *gf, int index, i_gif_opts *opts, int want_trans, int trans_index)
979
980Internal. Writes the GIF graphics control extension, if necessary.
981
982Returns non-zero on success.
983
984=cut
985*/
97c4effc 986static int do_gce(GifFileType *gf, i_img *img, int want_trans, int trans_index)
02d1d628
AMH
987{
988 unsigned char gce[4] = {0};
989 int want_gce = 0;
97c4effc
TC
990 int delay;
991 int user_input;
992 int disposal_method;
993
02d1d628
AMH
994 if (want_trans) {
995 gce[0] |= 1;
996 gce[3] = trans_index;
997 ++want_gce;
998 }
97c4effc
TC
999 if (i_tags_get_int(&img->tags, "gif_delay", 0, &delay)) {
1000 gce[1] = delay % 256;
1001 gce[2] = delay / 256;
02d1d628
AMH
1002 ++want_gce;
1003 }
97c4effc
TC
1004 if (i_tags_get_int(&img->tags, "gif_user_input", 0, &user_input)
1005 && user_input) {
1006 gce[0] |= 2;
02d1d628
AMH
1007 ++want_gce;
1008 }
97c4effc
TC
1009 if (i_tags_get_int(&img->tags, "gif_disposal", 0, &disposal_method)) {
1010 gce[0] |= (disposal_method & 3) << 2;
02d1d628
AMH
1011 ++want_gce;
1012 }
1013 if (want_gce) {
c48818b1
TC
1014 if (EGifPutExtension(gf, 0xF9, sizeof(gce), gce) == GIF_ERROR) {
1015 gif_push_error();
1016 i_push_error(0, "Could not save GCE");
1017 }
02d1d628
AMH
1018 }
1019 return 1;
1020}
1021
97c4effc
TC
1022/*
1023=item do_comments(gf, img)
1024
1025Write any comments in the image.
1026
1027=cut
1028*/
1029static int do_comments(GifFileType *gf, i_img *img) {
1030 int pos = -1;
1031
1032 while (i_tags_find(&img->tags, "gif_comment", pos+1, &pos)) {
1033 if (img->tags.tags[pos].data) {
1034 if (EGifPutComment(gf, img->tags.tags[pos].data) == GIF_ERROR) {
1035 return 0;
1036 }
1037 }
1038 else {
1039 char buf[50];
1040 sprintf(buf, "%d", img->tags.tags[pos].idata);
1041 if (EGifPutComment(gf, buf) == GIF_ERROR) {
1042 return 0;
1043 }
1044 }
1045 }
1046
1047 return 1;
1048}
1049
a3923855
TC
1050/*
1051=item do_ns_loop(GifFileType *gf, i_gif_opts *opts)
1052
1053Internal. Add the Netscape2.0 loop extension block, if requested.
1054
1660561c
TC
1055Giflib/libungif prior to 4.1.1 didn't support writing application
1056extension blocks, so we don't attempt to write them for older versions.
1057
1058Giflib/libungif prior to 4.1.3 used the wrong write mechanism when
1059writing extension blocks so that they could only be written to files.
a3923855
TC
1060
1061=cut
1062*/
97c4effc 1063static int do_ns_loop(GifFileType *gf, i_img *img)
02d1d628 1064{
02d1d628
AMH
1065 /* EGifPutExtension() doesn't appear to handle application
1066 extension blocks in any way
1067 Since giflib wraps the fd with a FILE * (and puts that in its
1068 private data), we can't do an end-run and write the data
1069 directly to the fd.
1070 There's no open interface that takes a FILE * either, so we
1071 can't workaround it that way either.
1072 If giflib's callback interface wasn't broken by default, I'd
1073 force file writes to use callbacks, but it is broken by default.
1074 */
28a9109d 1075 /* yes this was another attempt at supporting the loop extension */
97c4effc
TC
1076 int loop_count;
1077 if (i_tags_get_int(&img->tags, "gif_loop", 0, &loop_count)) {
28a9109d
TC
1078 unsigned char nsle[12] = "NETSCAPE2.0";
1079 unsigned char subblock[3];
1660561c 1080 if (EGifPutExtensionFirst(gf, APPLICATION_EXT_FUNC_CODE, 11, nsle) == GIF_ERROR) {
28a9109d
TC
1081 gif_push_error();
1082 i_push_error(0, "writing loop extension");
1083 return 0;
1084 }
1085 subblock[0] = 1;
97c4effc
TC
1086 subblock[1] = loop_count % 256;
1087 subblock[2] = loop_count / 256;
1660561c 1088 if (EGifPutExtensionLast(gf, APPLICATION_EXT_FUNC_CODE, 3, subblock) == GIF_ERROR) {
28a9109d 1089 gif_push_error();
7468f3fa 1090 i_push_error(0, "writing loop extension sub-block");
28a9109d
TC
1091 return 0;
1092 }
02d1d628 1093 }
1660561c 1094
02d1d628
AMH
1095 return 1;
1096}
1097
a3923855 1098/*
97c4effc 1099=item make_gif_map(i_quantize *quant, int want_trans)
a3923855
TC
1100
1101Create a giflib color map object from an Imager color map.
1102
1103=cut
1104*/
1105
97c4effc
TC
1106static ColorMapObject *make_gif_map(i_quantize *quant, i_img *img,
1107 int want_trans) {
02d1d628
AMH
1108 GifColorType colors[256];
1109 int i;
1110 int size = quant->mc_count;
1111 int map_size;
c48818b1 1112 ColorMapObject *map;
97c4effc 1113 i_color trans;
02d1d628
AMH
1114
1115 for (i = 0; i < quant->mc_count; ++i) {
1116 colors[i].Red = quant->mc_colors[i].rgb.r;
1117 colors[i].Green = quant->mc_colors[i].rgb.g;
1118 colors[i].Blue = quant->mc_colors[i].rgb.b;
1119 }
1120 if (want_trans) {
97c4effc
TC
1121 if (!i_tags_get_color(&img->tags, "gif_trans_color", 0, &trans))
1122 trans.rgb.r = trans.rgb.g = trans.rgb.b = 0;
1123 colors[size].Red = trans.rgb.r;
1124 colors[size].Green = trans.rgb.g;
1125 colors[size].Blue = trans.rgb.b;
02d1d628
AMH
1126 ++size;
1127 }
1128 map_size = 1;
1129 while (map_size < size)
1130 map_size <<= 1;
85363ac2
TC
1131 /* giflib spews for 1 colour maps, reasonable, I suppose */
1132 if (map_size == 1)
1133 map_size = 2;
10461f9a
TC
1134 while (i < map_size) {
1135 colors[i].Red = colors[i].Green = colors[i].Blue = 0;
1136 ++i;
1137 }
a743c0a6 1138
c48818b1 1139 map = MakeMapObject(map_size, colors);
a743c0a6 1140 mm_log((1, "XXX map is at %p and colors at %p\n", map, map->Colors));
c48818b1
TC
1141 if (!map) {
1142 gif_push_error();
1143 i_push_error(0, "Could not create color map object");
1144 return NULL;
1145 }
1146 return map;
02d1d628
AMH
1147}
1148
a3923855 1149/*
97c4effc 1150=item gif_set_version(i_quantize *quant, i_img *imgs, int count)
a3923855
TC
1151
1152We need to call EGifSetGifVersion() before opening the file - put that
1153common code here.
1154
1155Unfortunately giflib 4.1.0 crashes when we use this. Internally
1156giflib 4.1.0 has code:
1157
1158 static char *GifVersionPrefix = GIF87_STAMP;
1159
1160and the code that sets the version internally does:
1161
1162 strncpy(&GifVersionPrefix[3], Version, 3);
1163
1164which is very broken.
1165
1166Failing to set the correct GIF version doesn't seem to cause a problem
1167with readers.
1168
1660561c
TC
1169Modern versions (4.1.4 anyway) of giflib/libungif handle
1170EGifSetGifVersion correctly.
1171
1172If t/t105gif.t crashes here then run Makefile.PL with
1173--nogifsetversion, eg.:
1174
1175 perl Makefile.PL --nogifsetversion
1176
1177or install a less buggy giflib.
1178
a3923855 1179=cut
02d1d628 1180*/
a3923855 1181
97c4effc 1182static void gif_set_version(i_quantize *quant, i_img **imgs, int count) {
1660561c
TC
1183 int need_89a = 0;
1184 int temp;
1185 int i;
1186
1187 if (quant->transp != tr_none)
1188 need_89a = 1;
1189 else {
1190 for (i = 0; i < count; ++i) {
1191 if (i_tags_get_int(&imgs[i]->tags, "gif_delay", 0, &temp)) {
1192 need_89a = 1;
1193 break;
1194 }
1195 if (i_tags_get_int(&imgs[i]->tags, "gif_user_input", 0, &temp) && temp) {
1196 need_89a = 1;
1197 break;
1198 }
1199 if (i_tags_get_int(&imgs[i]->tags, "gif_disposal", 0, &temp)) {
1200 need_89a = 1;
1201 break;
1202 }
1203 if (i_tags_get_int(&imgs[i]->tags, "gif_loop", 0, &temp)) {
1204 need_89a = 1;
1205 break;
1206 }
1207 }
1208 }
1209 if (need_89a)
02d1d628 1210 EGifSetGifVersion("89a");
1660561c 1211 else
02d1d628 1212 EGifSetGifVersion("87a");
02d1d628
AMH
1213}
1214
bf9dd17c
TC
1215static int
1216in_palette(i_color *c, i_quantize *quant, int size) {
1217 int i;
1218
1219 for (i = 0; i < size; ++i) {
1220 if (c->channel[0] == quant->mc_colors[i].channel[0]
1221 && c->channel[1] == quant->mc_colors[i].channel[1]
1222 && c->channel[2] == quant->mc_colors[i].channel[2]) {
1223 return i;
1224 }
1225 }
1226
1227 return -1;
1228}
1229
1230/*
59c150a4 1231=item has_common_palette(imgs, count, quant)
bf9dd17c 1232
59c150a4
TC
1233Tests if all the given images are paletted and their colors are in the
1234palette produced.
bf9dd17c 1235
59c150a4
TC
1236Previously this would build a consolidated palette from the source,
1237but that meant that if the caller supplied a static palette (or
1238specified a fixed palette like "webmap") then we wouldn't be
1239quantizing to the caller specified palette.
bf9dd17c 1240
97c4effc
TC
1241=cut
1242*/
59c150a4 1243
bf9dd17c 1244static int
59c150a4 1245has_common_palette(i_img **imgs, int count, i_quantize *quant) {
a659442a 1246 int i;
bf9dd17c 1247 int imgn;
bf9dd17c 1248 char used[256];
59c150a4 1249 int col_count;
bf9dd17c
TC
1250
1251 /* we try to build a common palette here, if we can manage that, then
1252 that's the palette we use */
1253 for (imgn = 0; imgn < count; ++imgn) {
97c4effc 1254 int eliminate_unused;
bf9dd17c
TC
1255 if (imgs[imgn]->type != i_palette_type)
1256 return 0;
1257
97c4effc
TC
1258 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_eliminate_unused", 0,
1259 &eliminate_unused)) {
1260 eliminate_unused = 1;
1261 }
1262
1263 if (eliminate_unused) {
bf9dd17c
TC
1264 i_palidx *line = mymalloc(sizeof(i_palidx) * imgs[imgn]->xsize);
1265 int x, y;
1266 memset(used, 0, sizeof(used));
1267
1268 for (y = 0; y < imgs[imgn]->ysize; ++y) {
1269 i_gpal(imgs[imgn], 0, imgs[imgn]->xsize, y, line);
1270 for (x = 0; x < imgs[imgn]->xsize; ++x)
1271 used[line[x]] = 1;
1272 }
1273
1274 myfree(line);
1275 }
1276 else {
1277 /* assume all are in use */
1278 memset(used, 1, sizeof(used));
1279 }
1280
59c150a4
TC
1281 col_count = i_colorcount(imgs[imgn]);
1282 for (i = 0; i < col_count; ++i) {
bf9dd17c
TC
1283 i_color c;
1284
1285 i_getcolors(imgs[imgn], i, &c, 1);
1286 if (used[i]) {
59c150a4
TC
1287 if (in_palette(&c, quant, quant->mc_count) < 0) {
1288 mm_log((1, " color not found in palette, no palette shortcut\n"));
1289
1290 return 0;
bf9dd17c
TC
1291 }
1292 }
1293 }
1294 }
1295
59c150a4 1296 mm_log((1, " all colors found in palette, palette shortcut\n"));
bf9dd17c
TC
1297
1298 return 1;
1299}
1300
1301static i_palidx *
1302quant_paletted(i_quantize *quant, i_img *img) {
1303 i_palidx *data = mymalloc(sizeof(i_palidx) * img->xsize * img->ysize);
1304 i_palidx *p = data;
1305 i_palidx trans[256];
1306 int i;
8d14daab 1307 i_img_dim x, y;
bf9dd17c
TC
1308
1309 /* build a translation table */
1310 for (i = 0; i < i_colorcount(img); ++i) {
1311 i_color c;
1312 i_getcolors(img, i, &c, 1);
1313 trans[i] = in_palette(&c, quant, quant->mc_count);
1314 }
1315
1316 for (y = 0; y < img->ysize; ++y) {
1317 i_gpal(img, 0, img->xsize, y, data+img->xsize * y);
1318 for (x = 0; x < img->xsize; ++x) {
1319 *p = trans[*p];
1320 ++p;
1321 }
1322 }
1323
1324 return data;
1325}
1326
a3923855
TC
1327/*
1328=item i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count, i_gif_opts *opts)
1329
1330Internal. Low-level function that does the high-level GIF processing
1331:)
1332
1333Returns non-zero on success.
1334
1335=cut
1336*/
1337
02d1d628 1338static undef_int
97c4effc 1339i_writegif_low(i_quantize *quant, GifFileType *gf, i_img **imgs, int count) {
336f5078 1340 unsigned char *result = NULL;
02d1d628
AMH
1341 int color_bits;
1342 ColorMapObject *map;
1343 int scrw = 0, scrh = 0;
1344 int imgn, orig_count, orig_size;
1345 int posx, posy;
336f5078 1346 int trans_index = -1;
97c4effc
TC
1347 int *localmaps;
1348 int anylocal;
1349 i_img **glob_imgs; /* images that will use the global color map */
1350 int glob_img_count;
1351 i_color *orig_colors = quant->mc_colors;
1352 i_color *glob_colors = NULL;
336f5078 1353 int glob_color_count = 0;
97c4effc 1354 int glob_want_trans;
336f5078
TC
1355 int glob_paletted = 0; /* the global map was made from the image palettes */
1356 int colors_paletted = 0;
1357 int want_trans = 0;
97c4effc
TC
1358 int interlace;
1359 int gif_background;
1360
1361 mm_log((1, "i_writegif_low(quant %p, gf %p, imgs %p, count %d)\n",
1362 quant, gf, imgs, count));
1363
1364 /* *((char *)0) = 1; */ /* used to break into the debugger */
1365
1366 if (count <= 0) {
1367 i_push_error(0, "No images provided to write");
1368 return 0; /* what are you smoking? */
1369 }
02d1d628 1370
02d1d628
AMH
1371 /* sanity is nice */
1372 if (quant->mc_size > 256)
1373 quant->mc_size = 256;
1374 if (quant->mc_count > quant->mc_size)
1375 quant->mc_count = quant->mc_size;
1376
97c4effc
TC
1377 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_width", 0, &scrw))
1378 scrw = 0;
43b2b326 1379 if (!i_tags_get_int(&imgs[0]->tags, "gif_screen_height", 0, &scrh))
8d14daab 1380 scrh = 0;
97c4effc
TC
1381
1382 anylocal = 0;
ec6d8908
TC
1383 localmaps = mymalloc(sizeof(int) * count);
1384 glob_imgs = mymalloc(sizeof(i_img *) * count);
97c4effc
TC
1385 glob_img_count = 0;
1386 glob_want_trans = 0;
02d1d628 1387 for (imgn = 0; imgn < count; ++imgn) {
8d14daab
TC
1388 i_img *im = imgs[imgn];
1389 if (im->xsize > 0xFFFF || im->ysize > 0xFFFF) {
1390 i_push_error(0, "image too large for GIF");
1391 return 0;
1392 }
1393
97c4effc 1394 posx = posy = 0;
8d14daab
TC
1395 i_tags_get_int(&im->tags, "gif_left", 0, &posx);
1396 if (posx < 0) posx = 0;
1397 i_tags_get_int(&im->tags, "gif_top", 0, &posy);
1398 if (posy < 0) posy = 0;
1399 if (im->xsize + posx > scrw)
1400 scrw = im->xsize + posx;
1401 if (im->ysize + posy > scrh)
1402 scrh = im->ysize + posy;
1403 if (!i_tags_get_int(&im->tags, "gif_local_map", 0, localmaps+imgn))
97c4effc
TC
1404 localmaps[imgn] = 0;
1405 if (localmaps[imgn])
1406 anylocal = 1;
02d1d628 1407 else {
8d14daab 1408 if (im->channels == 4) {
97c4effc
TC
1409 glob_want_trans = 1;
1410 }
8d14daab 1411 glob_imgs[glob_img_count++] = im;
02d1d628
AMH
1412 }
1413 }
97c4effc 1414 glob_want_trans = glob_want_trans && quant->transp != tr_none ;
02d1d628 1415
8d14daab
TC
1416 if (scrw > 0xFFFF || scrh > 0xFFFF) {
1417 i_push_error(0, "screen size too large for GIF");
1418 return 0;
1419 }
1420
02d1d628
AMH
1421 orig_count = quant->mc_count;
1422 orig_size = quant->mc_size;
1423
97c4effc
TC
1424 if (glob_img_count) {
1425 /* this is ugly */
ec6d8908 1426 glob_colors = mymalloc(sizeof(i_color) * quant->mc_size);
97c4effc
TC
1427 quant->mc_colors = glob_colors;
1428 memcpy(glob_colors, orig_colors, sizeof(i_color) * quant->mc_count);
1429 /* we have some images that want to use the global map */
1430 if (glob_want_trans && quant->mc_count == 256) {
1431 mm_log((2, " disabling transparency for global map - no space\n"));
1432 glob_want_trans = 0;
1433 }
1434 if (glob_want_trans && quant->mc_size == 256) {
1435 mm_log((2, " reserving color for transparency\n"));
5dcf9039 1436 --quant->mc_size;
bf9dd17c 1437 }
59c150a4
TC
1438
1439 i_quant_makemap(quant, glob_imgs, glob_img_count);
1440 glob_paletted = has_common_palette(glob_imgs, glob_img_count, quant);
97c4effc
TC
1441 glob_color_count = quant->mc_count;
1442 quant->mc_colors = orig_colors;
1443 }
02d1d628 1444
97c4effc
TC
1445 /* use the global map if we have one, otherwise use the local map */
1446 gif_background = 0;
1447 if (glob_colors) {
1448 quant->mc_colors = glob_colors;
1449 quant->mc_count = glob_color_count;
1450 want_trans = glob_want_trans && imgs[0]->channels == 4;
1451
1452 if (!i_tags_get_int(&imgs[0]->tags, "gif_background", 0, &gif_background))
1453 gif_background = 0;
1454 if (gif_background < 0)
1455 gif_background = 0;
1456 if (gif_background >= glob_color_count)
1457 gif_background = 0;
1458 }
1459 else {
1460 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
59c150a4
TC
1461 i_quant_makemap(quant, imgs, 1);
1462 colors_paletted = has_common_palette(imgs, 1, quant);
97c4effc 1463 }
59c150a4 1464
97c4effc 1465 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
ec6d8908
TC
1466 myfree(glob_colors);
1467 myfree(localmaps);
1468 myfree(glob_imgs);
97c4effc
TC
1469 quant->mc_colors = orig_colors;
1470 EGifCloseFile(gf);
1471 mm_log((1, "Error in MakeMapObject"));
1472 return 0;
1473 }
1474 color_bits = 1;
1475 if (anylocal) {
1476 /* since we don't know how big some the local palettes could be
1477 we need to base the bits on the maximum number of colors */
1478 while (orig_size > (1 << color_bits))
1479 ++color_bits;
1480 }
1481 else {
1482 int count = quant->mc_count;
1483 if (want_trans)
1484 ++count;
1485 while (count > (1 << color_bits))
02d1d628 1486 ++color_bits;
97c4effc 1487 }
02d1d628 1488
97c4effc
TC
1489 if (EGifPutScreenDesc(gf, scrw, scrh, color_bits,
1490 gif_background, map) == GIF_ERROR) {
ec6d8908
TC
1491 myfree(glob_colors);
1492 myfree(localmaps);
1493 myfree(glob_imgs);
97c4effc
TC
1494 quant->mc_colors = orig_colors;
1495 gif_push_error();
1496 i_push_error(0, "Could not save screen descriptor");
02d1d628 1497 FreeMapObject(map);
97c4effc
TC
1498 myfree(result);
1499 EGifCloseFile(gf);
1500 mm_log((1, "Error in EGifPutScreenDesc."));
1501 return 0;
1502 }
1503 FreeMapObject(map);
02d1d628 1504
97c4effc
TC
1505 if (!i_tags_get_int(&imgs[0]->tags, "gif_left", 0, &posx))
1506 posx = 0;
1507 if (!i_tags_get_int(&imgs[0]->tags, "gif_top", 0, &posy))
1508 posy = 0;
02d1d628 1509
97c4effc
TC
1510 if (!localmaps[0]) {
1511 map = NULL;
1512 colors_paletted = glob_paletted;
1513 }
1514 else {
1515 /* if this image has a global map the colors in quant don't
1516 belong to this image, so build a palette */
1517 if (glob_colors) {
1518 /* generate the local map for this image */
1519 quant->mc_colors = orig_colors;
1520 quant->mc_size = orig_size;
1521 quant->mc_count = orig_count;
1522 want_trans = quant->transp != tr_none && imgs[0]->channels == 4;
1523
1524 /* if the caller gives us too many colours we can't do transparency */
1525 if (want_trans && quant->mc_count == 256)
1526 want_trans = 0;
1527 /* if they want transparency but give us a big size, make it smaller
1528 to give room for a transparency colour */
1529 if (want_trans && quant->mc_size == 256)
1530 --quant->mc_size;
59c150a4
TC
1531 i_quant_makemap(quant, imgs, 1);
1532 colors_paletted = has_common_palette(imgs, 1, quant);
97c4effc 1533 if ((map = make_gif_map(quant, imgs[0], want_trans)) == NULL) {
ec6d8908
TC
1534 myfree(glob_colors);
1535 myfree(localmaps);
1536 myfree(glob_imgs);
97c4effc 1537 EGifCloseFile(gf);
7ac6a2e9 1538 quant->mc_colors = orig_colors;
97c4effc
TC
1539 mm_log((1, "Error in MakeMapObject"));
1540 return 0;
1541 }
02d1d628 1542 }
97c4effc
TC
1543 else {
1544 /* the map we wrote was the map for this image - don't set the local
1545 map */
1546 map = NULL;
02d1d628 1547 }
97c4effc
TC
1548 }
1549
1550 if (colors_paletted)
1551 result = quant_paletted(quant, imgs[0]);
1552 else
92bda632 1553 result = i_quant_translate(quant, imgs[0]);
1501d9b3 1554 if (!result) {
ec6d8908
TC
1555 myfree(glob_colors);
1556 myfree(localmaps);
1557 myfree(glob_imgs);
1501d9b3
TC
1558 quant->mc_colors = orig_colors;
1559 EGifCloseFile(gf);
1560 return 0;
1561 }
97c4effc 1562 if (want_trans) {
92bda632 1563 i_quant_transparent(quant, result, imgs[0], quant->mc_count);
97c4effc
TC
1564 trans_index = quant->mc_count;
1565 }
1566
1567 if (!do_ns_loop(gf, imgs[0])) {
ec6d8908
TC
1568 myfree(glob_colors);
1569 myfree(localmaps);
1570 myfree(glob_imgs);
97c4effc
TC
1571 quant->mc_colors = orig_colors;
1572 return 0;
1573 }
1574
1575 if (!do_gce(gf, imgs[0], want_trans, trans_index)) {
ec6d8908
TC
1576 myfree(glob_colors);
1577 myfree(localmaps);
1578 myfree(glob_imgs);
97c4effc 1579 quant->mc_colors = orig_colors;
6cf6ca4e 1580 myfree(result);
97c4effc
TC
1581 EGifCloseFile(gf);
1582 return 0;
1583 }
1584
1585 if (!do_comments(gf, imgs[0])) {
ec6d8908
TC
1586 myfree(glob_colors);
1587 myfree(localmaps);
1588 myfree(glob_imgs);
97c4effc
TC
1589 quant->mc_colors = orig_colors;
1590 myfree(result);
1591 EGifCloseFile(gf);
1592 return 0;
1593 }
1594
1595 if (!i_tags_get_int(&imgs[0]->tags, "gif_interlace", 0, &interlace))
1596 interlace = 0;
1597 if (EGifPutImageDesc(gf, posx, posy, imgs[0]->xsize, imgs[0]->ysize,
1598 interlace, map) == GIF_ERROR) {
ec6d8908
TC
1599 myfree(glob_colors);
1600 myfree(localmaps);
1601 myfree(glob_imgs);
97c4effc
TC
1602 quant->mc_colors = orig_colors;
1603 gif_push_error();
1604 i_push_error(0, "Could not save image descriptor");
1605 EGifCloseFile(gf);
1606 mm_log((1, "Error in EGifPutImageDesc."));
1607 return 0;
1608 }
1609 if (map)
1610 FreeMapObject(map);
1611
1612 if (!do_write(gf, interlace, imgs[0], result)) {
ec6d8908
TC
1613 myfree(glob_colors);
1614 myfree(localmaps);
1615 myfree(glob_imgs);
97c4effc
TC
1616 quant->mc_colors = orig_colors;
1617 EGifCloseFile(gf);
1618 myfree(result);
1619 return 0;
1620 }
1621 myfree(result);
1622
1623 /* that first awful image is out of the way, do the rest */
1624 for (imgn = 1; imgn < count; ++imgn) {
1625 if (localmaps[imgn]) {
1626 quant->mc_colors = orig_colors;
02d1d628
AMH
1627 quant->mc_count = orig_count;
1628 quant->mc_size = orig_size;
97c4effc 1629
5dcf9039 1630 want_trans = quant->transp != tr_none
97c4effc 1631 && imgs[imgn]->channels == 4;
5dcf9039
TC
1632 /* if the caller gives us too many colours we can't do transparency */
1633 if (want_trans && quant->mc_count == 256)
1634 want_trans = 0;
1635 /* if they want transparency but give us a big size, make it smaller
1636 to give room for a transparency colour */
1637 if (want_trans && quant->mc_size == 256)
1638 --quant->mc_size;
1639
59c150a4 1640 if (has_common_palette(imgs+imgn, 1, quant)) {
bf9dd17c
TC
1641 result = quant_paletted(quant, imgs[imgn]);
1642 }
1643 else {
92bda632
TC
1644 i_quant_makemap(quant, imgs+imgn, 1);
1645 result = i_quant_translate(quant, imgs[imgn]);
bf9dd17c 1646 }
1501d9b3 1647 if (!result) {
ec6d8908
TC
1648 myfree(glob_colors);
1649 myfree(localmaps);
1650 myfree(glob_imgs);
1501d9b3
TC
1651 quant->mc_colors = orig_colors;
1652 EGifCloseFile(gf);
92bda632 1653 mm_log((1, "error in i_quant_translate()"));
1501d9b3
TC
1654 return 0;
1655 }
bf9dd17c 1656 if (want_trans) {
92bda632 1657 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
bf9dd17c
TC
1658 trans_index = quant->mc_count;
1659 }
1660
97c4effc 1661 if ((map = make_gif_map(quant, imgs[imgn], want_trans)) == NULL) {
ec6d8908
TC
1662 myfree(glob_colors);
1663 myfree(localmaps);
1664 myfree(glob_imgs);
97c4effc
TC
1665 quant->mc_colors = orig_colors;
1666 myfree(result);
1667 EGifCloseFile(gf);
1668 mm_log((1, "Error in MakeMapObject."));
1669 return 0;
02d1d628 1670 }
bf9dd17c
TC
1671 }
1672 else {
97c4effc
TC
1673 quant->mc_colors = glob_colors;
1674 quant->mc_count = glob_color_count;
1675 if (glob_paletted)
1676 result = quant_paletted(quant, imgs[imgn]);
1677 else
92bda632 1678 result = i_quant_translate(quant, imgs[imgn]);
97c4effc
TC
1679 want_trans = glob_want_trans && imgs[imgn]->channels == 4;
1680 if (want_trans) {
92bda632 1681 i_quant_transparent(quant, result, imgs[imgn], quant->mc_count);
97c4effc
TC
1682 trans_index = quant->mc_count;
1683 }
1684 map = NULL;
bf9dd17c 1685 }
5dcf9039 1686
97c4effc 1687 if (!do_gce(gf, imgs[imgn], want_trans, trans_index)) {
ec6d8908
TC
1688 myfree(glob_colors);
1689 myfree(localmaps);
1690 myfree(glob_imgs);
97c4effc 1691 quant->mc_colors = orig_colors;
02d1d628
AMH
1692 myfree(result);
1693 EGifCloseFile(gf);
02d1d628
AMH
1694 return 0;
1695 }
02d1d628 1696
97c4effc 1697 if (!do_comments(gf, imgs[imgn])) {
ec6d8908
TC
1698 myfree(glob_colors);
1699 myfree(localmaps);
1700 myfree(glob_imgs);
97c4effc 1701 quant->mc_colors = orig_colors;
02d1d628
AMH
1702 myfree(result);
1703 EGifCloseFile(gf);
02d1d628
AMH
1704 return 0;
1705 }
02d1d628 1706
97c4effc
TC
1707 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_left", 0, &posx))
1708 posx = 0;
1709 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_top", 0, &posy))
1710 posy = 0;
1711
1712 if (!i_tags_get_int(&imgs[imgn]->tags, "gif_interlace", 0, &interlace))
1713 interlace = 0;
1714 if (EGifPutImageDesc(gf, posx, posy, imgs[imgn]->xsize,
1715 imgs[imgn]->ysize, interlace, map) == GIF_ERROR) {
ec6d8908
TC
1716 myfree(glob_colors);
1717 myfree(localmaps);
1718 myfree(glob_imgs);
97c4effc 1719 quant->mc_colors = orig_colors;
c48818b1
TC
1720 gif_push_error();
1721 i_push_error(0, "Could not save image descriptor");
97c4effc
TC
1722 myfree(result);
1723 if (map)
1724 FreeMapObject(map);
02d1d628
AMH
1725 EGifCloseFile(gf);
1726 mm_log((1, "Error in EGifPutImageDesc."));
1727 return 0;
1728 }
97c4effc
TC
1729 if (map)
1730 FreeMapObject(map);
1731
1732 if (!do_write(gf, interlace, imgs[imgn], result)) {
ec6d8908
TC
1733 myfree(glob_colors);
1734 myfree(localmaps);
1735 myfree(glob_imgs);
97c4effc 1736 quant->mc_colors = orig_colors;
02d1d628
AMH
1737 EGifCloseFile(gf);
1738 myfree(result);
1739 return 0;
1740 }
1741 myfree(result);
02d1d628 1742 }
97c4effc 1743
02d1d628 1744 if (EGifCloseFile(gf) == GIF_ERROR) {
ec6d8908
TC
1745 myfree(glob_colors);
1746 myfree(localmaps);
1747 myfree(glob_imgs);
c48818b1
TC
1748 gif_push_error();
1749 i_push_error(0, "Could not close GIF file");
02d1d628
AMH
1750 mm_log((1, "Error in EGifCloseFile\n"));
1751 return 0;
1752 }
7ac6a2e9
TC
1753 if (glob_colors) {
1754 int i;
1755 for (i = 0; i < glob_color_count; ++i)
1756 orig_colors[i] = glob_colors[i];
1757 }
1758
ec6d8908
TC
1759 myfree(glob_colors);
1760 myfree(localmaps);
1761 myfree(glob_imgs);
97c4effc 1762 quant->mc_colors = orig_colors;
02d1d628
AMH
1763
1764 return 1;
1765}
1766
10461f9a
TC
1767static int
1768io_glue_write_cb(GifFileType *gft, const GifByteType *data, int length) {
1769 io_glue *ig = (io_glue *)gft->UserData;
1770
1771 return ig->writecb(ig, data, length);
1772}
1773
10461f9a
TC
1774
1775/*
1776=item i_writegif_wiol(ig, quant, opts, imgs, count)
1777
1778=cut
1779*/
1780undef_int
97c4effc 1781i_writegif_wiol(io_glue *ig, i_quantize *quant, i_img **imgs,
10461f9a 1782 int count) {
ec6d8908
TC
1783 GifFileType *GifFile;
1784 int result;
10461f9a 1785
ec6d8908 1786 i_clear_error();
10461f9a 1787
ec6d8908
TC
1788 gif_set_version(quant, imgs, count);
1789
1790 if ((GifFile = EGifOpen((void *)ig, io_glue_write_cb )) == NULL) {
1791 gif_push_error();
1792 i_push_error(0, "Cannot create giflib callback object");
1793 mm_log((1,"i_writegif_wiol: Unable to open callback datasource.\n"));
10461f9a 1794 return 0;
10461f9a 1795 }
ec6d8908
TC
1796
1797 result = i_writegif_low(quant, GifFile, imgs, count);
1798
1799 ig->closecb(ig);
1800
1801 return result;
10461f9a
TC
1802}
1803
c48818b1
TC
1804/*
1805=item gif_error_msg(int code)
1806
1807Grabs the most recent giflib error code from GifLastError() and
1808returns a string that describes that error.
1809
1810The returned pointer points to a static buffer, either from a literal
1811C string or a static buffer.
1812
97c4effc
TC
1813=cut
1814*/
c48818b1
TC
1815
1816static char const *gif_error_msg(int code) {
1817 static char msg[80];
1818
1819 switch (code) {
1820 case E_GIF_ERR_OPEN_FAILED: /* should not see this */
1821 return "Failed to open given file";
1822
1823 case E_GIF_ERR_WRITE_FAILED:
1824 return "Write failed";
1825
1826 case E_GIF_ERR_HAS_SCRN_DSCR: /* should not see this */
1827 return "Screen descriptor already passed to giflib";
1828
1829 case E_GIF_ERR_HAS_IMAG_DSCR: /* should not see this */
1830 return "Image descriptor already passed to giflib";
1831
1832 case E_GIF_ERR_NO_COLOR_MAP: /* should not see this */
1833 return "Neither global nor local color map set";
1834
1835 case E_GIF_ERR_DATA_TOO_BIG: /* should not see this */
1836 return "Too much pixel data passed to giflib";
1837
1838 case E_GIF_ERR_NOT_ENOUGH_MEM:
1839 return "Out of memory";
1840
1841 case E_GIF_ERR_DISK_IS_FULL:
1842 return "Disk is full";
1843
1844 case E_GIF_ERR_CLOSE_FAILED: /* should not see this */
1845 return "File close failed";
1846
1847 case E_GIF_ERR_NOT_WRITEABLE: /* should not see this */
1848 return "File not writable";
1849
1850 case D_GIF_ERR_OPEN_FAILED:
1851 return "Failed to open file";
1852
1853 case D_GIF_ERR_READ_FAILED:
1854 return "Failed to read from file";
1855
1856 case D_GIF_ERR_NOT_GIF_FILE:
1857 return "File is not a GIF file";
1858
1859 case D_GIF_ERR_NO_SCRN_DSCR:
1860 return "No screen descriptor detected - invalid file";
1861
1862 case D_GIF_ERR_NO_IMAG_DSCR:
1863 return "No image descriptor detected - invalid file";
1864
1865 case D_GIF_ERR_NO_COLOR_MAP:
1866 return "No global or local color map found";
1867
1868 case D_GIF_ERR_WRONG_RECORD:
1869 return "Wrong record type detected - invalid file?";
1870
1871 case D_GIF_ERR_DATA_TOO_BIG:
1872 return "Data in file too big for image";
1873
1874 case D_GIF_ERR_NOT_ENOUGH_MEM:
1875 return "Out of memory";
1876
1877 case D_GIF_ERR_CLOSE_FAILED:
1878 return "Close failed";
1879
1880 case D_GIF_ERR_NOT_READABLE:
1881 return "File not opened for read";
1882
1883 case D_GIF_ERR_IMAGE_DEFECT:
1884 return "Defective image";
1885
1886 case D_GIF_ERR_EOF_TOO_SOON:
1887 return "Unexpected EOF - invalid file";
1888
1889 default:
1890 sprintf(msg, "Unknown giflib error code %d", code);
1891 return msg;
1892 }
1893}
1894
1895/*
1896=item gif_push_error()
1897
1898Utility function that takes the current GIF error code, converts it to
1899an error message and pushes it on the error stack.
1900
1901=cut
1902*/
1903
faa9b3e7 1904static void gif_push_error(void) {
c48818b1
TC
1905 int code = GifLastError(); /* clears saved error */
1906
1907 i_push_error(code, gif_error_msg(code));
1908}
1909
a3923855
TC
1910/*
1911=head1 BUGS
1912
1913The Netscape loop extension isn't implemented. Giflib's extension
1914writing code doesn't seem to support writing named extensions in this
1915form.
1916
1917A bug in giflib is tickled by the i_writegif_callback(). This isn't a
1918problem on ungiflib, but causes a SEGV on giflib. A patch is provided
1919in t/t10formats.t
1920
1921The GIF file tag (GIF87a vs GIF89a) currently isn't set. Using the
1922supplied interface in giflib 4.1.0 causes a SEGV in
1923EGifSetGifVersion(). See L<gif_set_version> for an explanation.
1924
1925=head1 AUTHOR
1926
1927Arnar M. Hrafnkelsson, addi@umich.edu
1928
1929=head1 SEE ALSO
1930
1931perl(1), Imager(3)
1932
1933=cut
1934
1935*/