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