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