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