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