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