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