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