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