1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - GIF image loader
3 *
4 * Copyright (C) 1999 Mark Crichton
5 * Copyright (C) 1999 The Free Software Foundation
6 *
7 * Authors: Jonathan Blandford <jrb@redhat.com>
8 * Adapted from the gimp gif filter written by Adam Moss <adam@gimp.org>
9 * Gimp work based on earlier work.
10 * Permission to relicense under the LGPL obtained.
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
24 */
25
26 /* This loader is very hairy code.
27 *
28 * The main loop was not designed for incremental loading, so when it was hacked
29 * in it got a bit messy. Basically, every function is written to expect a failed
30 * read_gif, and lets you call it again assuming that the bytes are there.
31 *
32 * Return vals.
33 * Unless otherwise specified, these are the return vals for most functions:
34 *
35 * 0 -> success
36 * -1 -> more bytes needed.
37 * -2 -> failure; abort the load
38 * -3 -> control needs to be passed back to the main loop
39 * \_ (most of the time returning 0 will get this, but not always)
40 *
41 * >1 -> for functions that get a guchar, the char will be returned.
42 *
43 * -jrb (11/03/1999)
44 */
45
46 /*
47 * If you have any images that crash this code, please, please let me know and
48 * send them to me.
49 * <jrb@redhat.com>
50 */
51
52
53
54 #include "config.h"
55 #include <stdio.h>
56 #include <string.h>
57 #include <errno.h>
58 #include <glib/gi18n-lib.h>
59 #include "gdk-pixbuf-io.h"
60 #include "io-gif-animation.h"
61
62
63
64 #undef DUMP_IMAGE_DETAILS
65 #undef IO_GIFDEBUG
66
67 #define MAXCOLORMAPSIZE 256
68
69 #define INTERLACE 0x40
70 #define LOCALCOLORMAP 0x80
71 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
72 #define LM_to_uint(a,b) (((b)<<8)|(a))
73
74
75
76 typedef unsigned char CMap[3][MAXCOLORMAPSIZE];
77
78 /* Possible states we can be in. */
79 enum {
80 GIF_START,
81 GIF_GET_COLORMAP,
82 GIF_GET_NEXT_STEP,
83 GIF_GET_FRAME_INFO,
84 GIF_GET_EXTENSION,
85 GIF_GET_COLORMAP2,
86 GIF_PREPARE_LZW,
87 GIF_GET_LZW,
88 GIF_DONE
89 };
90
91
92 typedef struct _Gif89 Gif89;
93 struct _Gif89
94 {
95 int transparent;
96 int delay_time;
97 int input_flag;
98 int disposal;
99 };
100
101 typedef struct _GifContext GifContext;
102 struct _GifContext
103 {
104 int state; /* really only relevant for progressive loading */
105 unsigned int width;
106 unsigned int height;
107
108 gboolean has_global_cmap;
109
110 gint global_colormap_size;
111 unsigned int global_bit_pixel;
112 unsigned int global_color_resolution;
113 unsigned int background_index;
114
115 gboolean frame_cmap_active;
116 CMap frame_color_map;
117 gint frame_colormap_size;
118 unsigned int frame_bit_pixel;
119
120 unsigned int aspect_ratio;
121 GdkPixbufGifAnim *animation;
122 GdkPixbufFrame *frame;
123 Gif89 gif89;
124
125 /* stuff per frame. */
126 int frame_len;
127 int frame_height;
128 int frame_interlace;
129 int x_offset;
130 int y_offset;
131
132 /* Static read only */
133 FILE *file;
134
135 /* progressive read, only. */
136 GdkPixbufModuleSizeFunc size_func;
137 GdkPixbufModulePreparedFunc prepare_func;
138 GdkPixbufModuleUpdatedFunc update_func;
139 gpointer user_data;
140 guchar *buf;
141 gsize ptr;
142 gsize size;
143 gsize amount_needed;
144
145 /* extension context */
146 guchar extension_label;
147 guchar extension_flag;
148 gboolean in_loop_extension;
149
150 /* get block context */
151 guchar block_count;
152 guchar block_buf[280];
153
154 guchar lzw_set_code_size;
155
156 /* error pointer */
157 GError **error;
158 };
159
160
161
162 #ifdef IO_GIFDEBUG
163 static int count = 0;
164 #endif
165
166 /* Returns TRUE if read is OK,
167 * FALSE if more memory is needed. */
168 static gboolean
gif_read(GifContext * context,guchar * buffer,size_t len)169 gif_read (GifContext *context, guchar *buffer, size_t len)
170 {
171 gboolean retval;
172 #ifdef IO_GIFDEBUG
173 gint i;
174 #endif
175 if (context->file) {
176 #ifdef IO_GIFDEBUG
177 count += len;
178 g_print ("Fsize :%zi\tcount :%d\t", len, count);
179 #endif
180 retval = (fread (buffer, 1, len, context->file) == len);
181
182 if (!retval && ferror (context->file)) {
183 gint save_errno = errno;
184 g_set_error (context->error,
185 G_FILE_ERROR,
186 g_file_error_from_errno (save_errno),
187 _("Failure reading GIF: %s"),
188 g_strerror (save_errno));
189 }
190
191 #ifdef IO_GIFDEBUG
192 if (len < 100) {
193 for (i = 0; i < len; i++)
194 g_print ("%d ", buffer[i]);
195 }
196 g_print ("\n");
197 #endif
198
199 return retval;
200 } else {
201 #ifdef IO_GIFDEBUG
202 /* g_print ("\tlooking for %d bytes. size == %d, ptr == %d\n", len, context->size, context->ptr); */
203 #endif
204 if ((context->size - context->ptr) >= len) {
205 #ifdef IO_GIFDEBUG
206 count += len;
207 #endif
208 memcpy (buffer, context->buf + context->ptr, len);
209 context->ptr += len;
210 context->amount_needed = 0;
211 #ifdef IO_GIFDEBUG
212 g_print ("Psize :%zi\tcount :%d\t", len, count);
213 if (len < 100) {
214 for (i = 0; i < len; i++)
215 g_print ("%d ", buffer[i]);
216 }
217 g_print ("\n");
218 #endif
219 return TRUE;
220 }
221 context->amount_needed = len - (context->size - context->ptr);
222 }
223 return FALSE;
224 }
225
226 /* Changes the stage to be GIF_GET_COLORMAP */
227 static void
gif_set_get_colormap(GifContext * context)228 gif_set_get_colormap (GifContext *context)
229 {
230 context->global_colormap_size = 0;
231 context->state = GIF_GET_COLORMAP;
232 }
233
234 static void
gif_set_get_colormap2(GifContext * context)235 gif_set_get_colormap2 (GifContext *context)
236 {
237 context->frame_colormap_size = 0;
238 context->state = GIF_GET_COLORMAP2;
239 }
240
241 static gint
gif_get_colormap(GifContext * context)242 gif_get_colormap (GifContext *context)
243 {
244 unsigned char rgb[3];
245
246 while (context->global_colormap_size < context->global_bit_pixel) {
247 if (!gif_read (context, rgb, sizeof (rgb))) {
248 return -1;
249 }
250
251 context->animation->color_map[context->global_colormap_size * 3 + 0] = rgb[0];
252 context->animation->color_map[context->global_colormap_size * 3 + 1] = rgb[1];
253 context->animation->color_map[context->global_colormap_size * 3 + 2] = rgb[2];
254
255 context->global_colormap_size ++;
256 }
257
258 return 0;
259 }
260
261
262 static gint
gif_get_colormap2(GifContext * context)263 gif_get_colormap2 (GifContext *context)
264 {
265 unsigned char rgb[3];
266
267 while (context->frame_colormap_size < context->frame_bit_pixel) {
268 if (!gif_read (context, rgb, sizeof (rgb))) {
269 return -1;
270 }
271
272 context->frame_color_map[0][context->frame_colormap_size] = rgb[0];
273 context->frame_color_map[1][context->frame_colormap_size] = rgb[1];
274 context->frame_color_map[2][context->frame_colormap_size] = rgb[2];
275
276 context->frame_colormap_size ++;
277 }
278
279 return 0;
280 }
281
282 /*
283 * in order for this function to work, we need to perform some black magic.
284 * We want to return -1 to let the calling function know, as before, that it needs
285 * more bytes. If we return 0, we were able to successfully read all block->count bytes.
286 * Problem is, we don't want to reread block_count every time, so we check to see if
287 * context->block_count is 0 before we read in the function.
288 *
289 * As a result, context->block_count MUST be 0 the first time the get_data_block is called
290 * within a context, and cannot be 0 the second time it's called.
291 */
292
293 static int
get_data_block(GifContext * context,unsigned char * buf,gint * empty_block)294 get_data_block (GifContext *context,
295 unsigned char *buf,
296 gint *empty_block)
297 {
298
299 if (context->block_count == 0) {
300 if (!gif_read (context, &context->block_count, 1)) {
301 return -1;
302 }
303 }
304
305 if (context->block_count == 0)
306 if (empty_block) {
307 *empty_block = TRUE;
308 return 0;
309 }
310
311 if (!gif_read (context, buf, context->block_count)) {
312 return -1;
313 }
314
315 return 0;
316 }
317
318 static void
gif_set_get_extension(GifContext * context)319 gif_set_get_extension (GifContext *context)
320 {
321 context->state = GIF_GET_EXTENSION;
322 context->extension_flag = TRUE;
323 context->extension_label = 0;
324 context->block_count = 0;
325 }
326
327 static int
gif_get_extension(GifContext * context)328 gif_get_extension (GifContext *context)
329 {
330 gint retval;
331 gint empty_block = FALSE;
332
333 if (context->extension_flag) {
334 if (context->extension_label == 0) {
335 /* I guess bad things can happen if we have an extension of 0 )-: */
336 /* I should look into this sometime */
337 if (!gif_read (context, & context->extension_label , 1)) {
338 return -1;
339 }
340 }
341
342 switch (context->extension_label) {
343 case 0xf9: /* Graphic Control Extension */
344 retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
345 if (retval != 0)
346 return retval;
347
348 if (context->frame == NULL) {
349 /* I only want to set the transparency if I haven't
350 * created the frame yet.
351 */
352 context->gif89.disposal = (context->block_buf[0] >> 2) & 0x7;
353 context->gif89.input_flag = (context->block_buf[0] >> 1) & 0x1;
354 context->gif89.delay_time = LM_to_uint (context->block_buf[1], context->block_buf[2]);
355
356 if ((context->block_buf[0] & 0x1) != 0) {
357 context->gif89.transparent = context->block_buf[3];
358 } else {
359 context->gif89.transparent = -1;
360 }
361 }
362
363 /* Now we've successfully loaded this one, we continue on our way */
364 context->block_count = 0;
365 context->extension_flag = FALSE;
366 break;
367 case 0xff: /* application extension */
368 if (!context->in_loop_extension) {
369 retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
370 if (retval != 0)
371 return retval;
372 if (!strncmp ((gchar *)context->block_buf, "NETSCAPE2.0", 11) ||
373 !strncmp ((gchar *)context->block_buf, "ANIMEXTS1.0", 11)) {
374 context->in_loop_extension = TRUE;
375 }
376 context->block_count = 0;
377 }
378 if (context->in_loop_extension) {
379 do {
380 retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
381 if (retval != 0)
382 return retval;
383 if (context->block_buf[0] == 0x01) {
384 context->animation->loop = context->block_buf[1] + (context->block_buf[2] << 8);
385 if (context->animation->loop != 0)
386 context->animation->loop++;
387 }
388 context->block_count = 0;
389 }
390 while (!empty_block);
391 context->in_loop_extension = FALSE;
392 context->extension_flag = FALSE;
393 return 0;
394 }
395 break;
396 default:
397 /* Unhandled extension */
398 break;
399 }
400 }
401 /* read all blocks, until I get an empty block, in case there was an extension I didn't know about. */
402 do {
403 retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
404 if (retval != 0)
405 return retval;
406 context->block_count = 0;
407 } while (!empty_block);
408
409 return 0;
410 }
411
412 static void
gif_set_get_lzw(GifContext * context)413 gif_set_get_lzw (GifContext *context)
414 {
415 context->state = GIF_GET_LZW;
416 }
417
418 static int
gif_get_lzw(GifContext * context)419 gif_get_lzw (GifContext *context)
420 {
421 if (context->frame == NULL) {
422 int rowstride;
423 guint64 len;
424
425 rowstride = gdk_pixbuf_calculate_rowstride (GDK_COLORSPACE_RGB,
426 TRUE,
427 8,
428 context->frame_len,
429 context->frame_height);
430 if (rowstride < 0 ||
431 !g_uint64_checked_mul (&len, rowstride, context->frame_height) ||
432 len >= G_MAXINT) {
433 g_set_error_literal (context->error,
434 GDK_PIXBUF_ERROR,
435 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
436 _("Not enough memory to load GIF file"));
437 return -2;
438 }
439
440 context->frame = g_new0 (GdkPixbufFrame, 1);
441
442 context->frame->lzw_data = g_byte_array_new ();
443 context->frame->lzw_code_size = context->lzw_set_code_size;
444
445 context->frame->width = context->frame_len;
446 context->frame->height = context->frame_height;
447 context->frame->x_offset = context->x_offset;
448 context->frame->y_offset = context->y_offset;
449 context->frame->interlace = context->frame_interlace;
450
451 if (context->frame_colormap_size > 0) {
452 int i;
453
454 context->frame->color_map = g_malloc (256 * 3);
455 context->frame->color_map_allocated = TRUE;
456 for (i = 0; i < 256; i++) {
457 context->frame->color_map[i * 3 + 0] = context->frame_color_map[0][i];
458 context->frame->color_map[i * 3 + 1] = context->frame_color_map[1][i];
459 context->frame->color_map[i * 3 + 2] = context->frame_color_map[2][i];
460 }
461 }
462 else {
463 context->frame->color_map = context->animation->color_map;
464 }
465
466 context->frame->transparent_index = context->gif89.transparent;
467
468 /* GIF delay is in hundredths, we want thousandths */
469 context->frame->delay_time = context->gif89.delay_time * 10;
470
471 /* GIFs with delay time 0 are mostly broken, but they
472 * just want a default, "not that fast" delay.
473 */
474 if (context->frame->delay_time == 0)
475 context->frame->delay_time = 100;
476
477 /* No GIFs gets to play faster than 50 fps. They just
478 * lock up poor gtk.
479 */
480 if (context->frame->delay_time < 20)
481 context->frame->delay_time = 20; /* 20 = "fast" */
482
483 context->frame->elapsed = context->animation->total_time;
484 context->animation->total_time += context->frame->delay_time;
485
486 switch (context->gif89.disposal) {
487 case 0:
488 case 1:
489 context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
490 break;
491 case 2:
492 context->frame->action = GDK_PIXBUF_FRAME_DISPOSE;
493 break;
494 case 3:
495 context->frame->action = GDK_PIXBUF_FRAME_REVERT;
496 break;
497 default:
498 context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
499 break;
500 }
501
502 context->animation->frames = g_list_append (context->animation->frames, context->frame);
503
504 /* Notify when have first frame */
505 if (context->animation->frames->next == NULL && context->prepare_func != NULL) {
506 GdkPixbuf *pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
507 if (pixbuf != NULL)
508 (* context->prepare_func) (pixbuf,
509 GDK_PIXBUF_ANIMATION (context->animation),
510 context->user_data);
511 }
512 }
513
514 /* read all blocks */
515 while (TRUE) {
516 gint retval, empty_block = FALSE;
517
518 retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
519
520 /* Notify frame update */
521 if ((retval != 0 || empty_block) && context->animation->frames->next == NULL && context->update_func != NULL) {
522 GdkPixbuf *pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
523 if (pixbuf)
524 (* context->update_func) (pixbuf,
525 0, 0, context->frame->width, context->frame->height,
526 context->user_data);
527 }
528
529 if (retval != 0)
530 return retval;
531
532 if (empty_block) {
533 context->frame = NULL;
534 context->state = GIF_GET_NEXT_STEP;
535 return 0;
536 }
537
538 g_byte_array_append (context->frame->lzw_data, context->block_buf, context->block_count);
539 if (context->animation->last_frame == context->frame)
540 context->animation->last_frame = NULL;
541 context->block_count = 0;
542 }
543 }
544
545 static void
gif_set_prepare_lzw(GifContext * context)546 gif_set_prepare_lzw (GifContext *context)
547 {
548 context->state = GIF_PREPARE_LZW;
549 }
550 static int
gif_prepare_lzw(GifContext * context)551 gif_prepare_lzw (GifContext *context)
552 {
553 if (!gif_read (context, &(context->lzw_set_code_size), 1)) {
554 /*g_message (_("GIF: EOF / read error on image data\n"));*/
555 return -1;
556 }
557
558 if (context->lzw_set_code_size > 12) {
559 g_set_error_literal (context->error,
560 GDK_PIXBUF_ERROR,
561 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
562 _("GIF image is corrupt (incorrect LZW compression)"));
563 return -2;
564 }
565
566 gif_set_get_lzw (context);
567
568 return 0;
569 }
570
571 /* needs 13 bytes to proceed. */
572 static gint
gif_init(GifContext * context)573 gif_init (GifContext *context)
574 {
575 unsigned char buf[16];
576 char version[4];
577
578 if (!gif_read (context, buf, 6)) {
579 /* Unable to read magic number,
580 * gif_read() should have set error
581 */
582 return -1;
583 }
584
585 if (strncmp ((char *) buf, "GIF", 3) != 0) {
586 /* Not a GIF file */
587 g_set_error_literal (context->error,
588 GDK_PIXBUF_ERROR,
589 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
590 _("File does not appear to be a GIF file"));
591 return -2;
592 }
593
594 strncpy (version, (char *) buf + 3, 3);
595 version[3] = '\0';
596
597 if ((strcmp (version, "87a") != 0) && (strcmp (version, "89a") != 0)) {
598 gchar *escaped_version;
599
600 /* bad version number, not '87a' or '89a' */
601 escaped_version = g_strescape (version, NULL);
602 g_set_error (context->error,
603 GDK_PIXBUF_ERROR,
604 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
605 _("Version %s of the GIF file format is not supported"),
606 escaped_version);
607 g_free (escaped_version);
608 return -2;
609 }
610
611 /* read the screen descriptor */
612 if (!gif_read (context, buf, 7)) {
613 /* Failed to read screen descriptor, error set */
614 return -1;
615 }
616
617 context->width = LM_to_uint (buf[0], buf[1]);
618 context->height = LM_to_uint (buf[2], buf[3]);
619 /* The 4th byte is
620 * high bit: whether to use the background index
621 * next 3: color resolution
622 * next: whether colormap is sorted by priority of allocation
623 * last 3: size of colormap
624 */
625 context->global_bit_pixel = 2 << (buf[4] & 0x07);
626 context->global_color_resolution = (((buf[4] & 0x70) >> 3) + 1);
627 context->has_global_cmap = (buf[4] & 0x80) != 0;
628 context->background_index = buf[5];
629 context->aspect_ratio = buf[6];
630
631 context->animation->width = context->width;
632 context->animation->height = context->height;
633
634 if (context->size_func) {
635 gint width, height;
636
637 width = context->width;
638 height = context->height;
639
640 (*context->size_func) (&width, &height, context->user_data);
641
642 if (width == 0 || height == 0) {
643 g_set_error_literal (context->error,
644 GDK_PIXBUF_ERROR,
645 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
646 _("Resulting GIF image has zero size"));
647 return -2;
648 }
649 }
650
651 if (context->has_global_cmap) {
652 gif_set_get_colormap (context);
653 } else {
654 context->state = GIF_GET_NEXT_STEP;
655 }
656
657 #ifdef DUMP_IMAGE_DETAILS
658 g_print (">Image width: %d height: %d global_cmap: %d background: %d\n",
659 context->width, context->height, context->has_global_cmap, context->background_index);
660 #endif
661
662 return 0;
663 }
664
665 static void
gif_set_get_frame_info(GifContext * context)666 gif_set_get_frame_info (GifContext *context)
667 {
668 context->state = GIF_GET_FRAME_INFO;
669 }
670
671 static gint
gif_get_frame_info(GifContext * context)672 gif_get_frame_info (GifContext *context)
673 {
674 unsigned char buf[9];
675
676 if (!gif_read (context, buf, 9)) {
677 return -1;
678 }
679
680 /* Okay, we got all the info we need. Lets record it */
681 context->frame_len = LM_to_uint (buf[4], buf[5]);
682 context->frame_height = LM_to_uint (buf[6], buf[7]);
683 context->x_offset = LM_to_uint (buf[0], buf[1]);
684 context->y_offset = LM_to_uint (buf[2], buf[3]);
685
686 if (context->animation->frames == NULL &&
687 context->gif89.disposal == 3) {
688 /* First frame can't have "revert to previous" as its
689 * dispose mode. Silently use "retain" instead.
690 */
691 context->gif89.disposal = 0;
692 }
693
694 context->frame_interlace = BitSet (buf[8], INTERLACE);
695
696 #ifdef DUMP_IMAGE_DETAILS
697 g_print (">width: %d height: %d xoffset: %d yoffset: %d disposal: %d delay: %d transparent: %d interlace: %d\n",
698 context->frame_len, context->frame_height, context->x_offset, context->y_offset,
699 context->gif89.disposal, context->gif89.delay_time, context->gif89.transparent, context->frame_interlace);
700 #endif
701
702 if (BitSet (buf[8], LOCALCOLORMAP)) {
703
704 #ifdef DUMP_IMAGE_DETAILS
705 g_print (">has local colormap\n");
706 #endif
707
708 /* Does this frame have it's own colormap. */
709 /* really only relevant when looking at the first frame
710 * of an animated gif. */
711 /* if it does, we need to re-read in the colormap,
712 * the gray_scale, and the bit_pixel */
713 context->frame_cmap_active = TRUE;
714 context->frame_bit_pixel = 1 << ((buf[8] & 0x07) + 1);
715 gif_set_get_colormap2 (context);
716 return 0;
717 }
718
719 if (!context->has_global_cmap) {
720 context->state = GIF_DONE;
721
722 g_set_error_literal (context->error,
723 GDK_PIXBUF_ERROR,
724 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
725 _("GIF image has no global colormap, and a frame inside it has no local colormap."));
726
727 return -2;
728 }
729
730 gif_set_prepare_lzw (context);
731 return 0;
732
733 }
734
735 static gint
gif_get_next_step(GifContext * context)736 gif_get_next_step (GifContext *context)
737 {
738 unsigned char c;
739 while (TRUE) {
740 if (!gif_read (context, &c, 1)) {
741 return -1;
742 }
743 if (c == ';') {
744 /* GIF terminator */
745 /* hmm. Not 100% sure what to do about this. Should
746 * i try to return a blank image instead? */
747 context->state = GIF_DONE;
748 return 0;
749 }
750
751 if (c == '!') {
752 /* Check the extension */
753 gif_set_get_extension (context);
754 return 0;
755 }
756
757 /* look for frame */
758 if (c != ',') {
759 /* Not a valid start character */
760 continue;
761 }
762 /* load the frame */
763 gif_set_get_frame_info (context);
764 return 0;
765 }
766 }
767
768
769 #define LOG(x) /* g_print ("%s: %s\n", G_STRLOC, x); */
770
771 static gint
gif_main_loop(GifContext * context)772 gif_main_loop (GifContext *context)
773 {
774 gint retval = 0;
775
776 do {
777 switch (context->state) {
778 case GIF_START:
779 LOG("start\n");
780 retval = gif_init (context);
781 break;
782
783 case GIF_GET_COLORMAP:
784 LOG("get_colormap\n");
785 retval = gif_get_colormap (context);
786 if (retval == 0)
787 context->state = GIF_GET_NEXT_STEP;
788 break;
789
790 case GIF_GET_NEXT_STEP:
791 LOG("next_step\n");
792 retval = gif_get_next_step (context);
793 break;
794
795 case GIF_GET_FRAME_INFO:
796 LOG("frame_info\n");
797 retval = gif_get_frame_info (context);
798 break;
799
800 case GIF_GET_EXTENSION:
801 LOG("get_extension\n");
802 retval = gif_get_extension (context);
803 if (retval == 0)
804 context->state = GIF_GET_NEXT_STEP;
805 break;
806
807 case GIF_GET_COLORMAP2:
808 LOG("get_colormap2\n");
809 retval = gif_get_colormap2 (context);
810 if (retval == 0)
811 gif_set_prepare_lzw (context);
812 break;
813
814 case GIF_PREPARE_LZW:
815 LOG("prepare_lzw\n");
816 retval = gif_prepare_lzw (context);
817 break;
818
819 case GIF_GET_LZW:
820 LOG("get_lzw\n");
821 retval = gif_get_lzw (context);
822 break;
823
824 case GIF_DONE:
825 LOG("done\n");
826 /* fall through */
827 default:
828 retval = 0;
829 goto done;
830 };
831 } while ((retval == 0) || (retval == -3));
832 done:
833 return retval;
834 }
835
836 static GifContext *
new_context(void)837 new_context (void)
838 {
839 GifContext *context;
840
841 context = g_try_malloc (sizeof (GifContext));
842 if (context == NULL)
843 return NULL;
844
845 memset (context, 0, sizeof (GifContext));
846
847 context->animation = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM, NULL);
848 context->frame = NULL;
849 context->file = NULL;
850 context->state = GIF_START;
851 context->size_func = NULL;
852 context->prepare_func = NULL;
853 context->update_func = NULL;
854 context->user_data = NULL;
855 context->buf = NULL;
856 context->amount_needed = 13;
857 context->buf = g_new (guchar, context->amount_needed);
858 context->gif89.transparent = -1;
859 context->gif89.delay_time = -1;
860 context->gif89.input_flag = -1;
861 context->gif89.disposal = -1;
862 context->animation->loop = 1;
863 context->in_loop_extension = FALSE;
864
865 return context;
866 }
867 /* Shared library entry point */
868 static GdkPixbuf *
gdk_pixbuf__gif_image_load(FILE * file,GError ** error)869 gdk_pixbuf__gif_image_load (FILE *file, GError **error)
870 {
871 GifContext *context;
872 GdkPixbuf *pixbuf;
873 gint retval;
874
875 g_return_val_if_fail (file != NULL, NULL);
876
877 context = new_context ();
878
879 if (context == NULL) {
880 g_set_error_literal (error,
881 GDK_PIXBUF_ERROR,
882 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
883 _("Not enough memory to load GIF file"));
884 return NULL;
885 }
886
887 context->file = file;
888 context->error = error;
889
890 retval = gif_main_loop (context);
891 if (retval == -1 || context->animation->frames == NULL) {
892 if (context->error && *(context->error) == NULL)
893 g_set_error_literal (context->error,
894 GDK_PIXBUF_ERROR,
895 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
896 _("GIF file was missing some data (perhaps it was truncated somehow?)"));
897 }
898 else if (retval == -2) {
899 pixbuf = NULL;
900 goto out;
901 }
902
903 pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
904
905 if (pixbuf)
906 g_object_ref (pixbuf);
907
908 out:
909 g_object_unref (context->animation);
910
911 g_free (context->buf);
912 g_free (context);
913
914 return pixbuf;
915 }
916
917 static gpointer
gdk_pixbuf__gif_image_begin_load(GdkPixbufModuleSizeFunc size_func,GdkPixbufModulePreparedFunc prepare_func,GdkPixbufModuleUpdatedFunc update_func,gpointer user_data,GError ** error)918 gdk_pixbuf__gif_image_begin_load (GdkPixbufModuleSizeFunc size_func,
919 GdkPixbufModulePreparedFunc prepare_func,
920 GdkPixbufModuleUpdatedFunc update_func,
921 gpointer user_data,
922 GError **error)
923 {
924 GifContext *context;
925
926 #ifdef IO_GIFDEBUG
927 count = 0;
928 #endif
929 context = new_context ();
930
931 if (context == NULL) {
932 g_set_error_literal (error,
933 GDK_PIXBUF_ERROR,
934 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
935 _("Not enough memory to load GIF file"));
936 return NULL;
937 }
938
939 context->error = error;
940 context->size_func = size_func;
941 context->prepare_func = prepare_func;
942 context->update_func = update_func;
943 context->user_data = user_data;
944
945 return (gpointer) context;
946 }
947
948 static gboolean
gdk_pixbuf__gif_image_stop_load(gpointer data,GError ** error)949 gdk_pixbuf__gif_image_stop_load (gpointer data, GError **error)
950 {
951 GifContext *context = (GifContext *) data;
952 gboolean retval = TRUE;
953
954 if (context->animation->frames == NULL) {
955 g_set_error_literal (error,
956 GDK_PIXBUF_ERROR,
957 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
958 _("GIF image was truncated or incomplete."));
959
960 retval = FALSE;
961 } else if (context->state != GIF_DONE) {
962 g_set_error_literal (error,
963 GDK_PIXBUF_ERROR,
964 GDK_PIXBUF_ERROR_INCOMPLETE_ANIMATION,
965 _("Not all frames of the GIF image were loaded."));
966
967 retval = FALSE;
968 }
969
970 g_object_unref (context->animation);
971
972 g_free (context->buf);
973 g_free (context);
974
975 return retval;
976 }
977
978 static gboolean
gdk_pixbuf__gif_image_load_increment(gpointer data,const guchar * buf,guint size,GError ** error)979 gdk_pixbuf__gif_image_load_increment (gpointer data,
980 const guchar *buf, guint size,
981 GError **error)
982 {
983 gint retval;
984 GifContext *context = (GifContext *) data;
985
986 context->error = error;
987
988 if (context->amount_needed == 0) {
989 /* we aren't looking for some bytes. */
990 /* we can use buf now, but we don't want to keep it around at all.
991 * it will be gone by the end of the call. */
992 context->buf = (guchar*) buf; /* very dubious const cast */
993 context->ptr = 0;
994 context->size = size;
995 } else {
996 /* we need some bytes */
997 if (size < context->amount_needed) {
998 context->amount_needed -= size;
999 /* copy it over and return */
1000 memcpy (context->buf + context->size, buf, size);
1001 context->size += size;
1002 return TRUE;
1003 } else if (size == context->amount_needed) {
1004 memcpy (context->buf + context->size, buf, size);
1005 context->size += size;
1006 } else {
1007 context->buf = g_realloc (context->buf, context->size + size);
1008 memcpy (context->buf + context->size, buf, size);
1009 context->size += size;
1010 }
1011 }
1012
1013 retval = gif_main_loop (context);
1014
1015 if (retval == -2) {
1016 if (context->buf == buf)
1017 context->buf = NULL;
1018 return FALSE;
1019 }
1020 if (retval == -1) {
1021 /* we didn't have enough memory */
1022 /* prepare for the next image_load_increment */
1023 if (context->buf == buf) {
1024 g_assert (context->size == size);
1025 context->buf = g_new (guchar, context->amount_needed + (context->size - context->ptr));
1026 memcpy (context->buf, buf + context->ptr, context->size - context->ptr);
1027 } else {
1028 /* copy the left overs to the begining of the buffer */
1029 /* and realloc the memory */
1030 memmove (context->buf, context->buf + context->ptr, context->size - context->ptr);
1031 context->buf = g_realloc (context->buf, context->amount_needed + (context->size - context->ptr));
1032 }
1033 context->size = context->size - context->ptr;
1034 context->ptr = 0;
1035 } else {
1036 /* we are prolly all done */
1037 if (context->buf == buf)
1038 context->buf = NULL;
1039 }
1040 return TRUE;
1041 }
1042
1043 static GdkPixbufAnimation *
gdk_pixbuf__gif_image_load_animation(FILE * file,GError ** error)1044 gdk_pixbuf__gif_image_load_animation (FILE *file,
1045 GError **error)
1046 {
1047 GifContext *context;
1048 GdkPixbufAnimation *animation;
1049
1050 g_return_val_if_fail (file != NULL, NULL);
1051
1052 context = new_context ();
1053
1054 if (context == NULL) {
1055 g_set_error_literal (error,
1056 GDK_PIXBUF_ERROR,
1057 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
1058 _("Not enough memory to load GIF file"));
1059 return NULL;
1060 }
1061
1062 context->error = error;
1063 context->file = file;
1064
1065 if (gif_main_loop (context) == -1 || context->animation->frames == NULL) {
1066 if (context->error && *(context->error) == NULL)
1067 g_set_error_literal (context->error,
1068 GDK_PIXBUF_ERROR,
1069 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1070 _("GIF file was missing some data (perhaps it was truncated somehow?)"));
1071
1072 g_object_unref (context->animation);
1073 context->animation = NULL;
1074 }
1075
1076 if (context->animation)
1077 animation = GDK_PIXBUF_ANIMATION (context->animation);
1078 else
1079 animation = NULL;
1080
1081 if (context->error && *(context->error))
1082 g_print ("%s\n", (*(context->error))->message);
1083
1084 g_free (context->buf);
1085 g_free (context);
1086 return animation;
1087 }
1088
1089 #ifndef INCLUDE_gif
1090 #define MODULE_ENTRY(function) G_MODULE_EXPORT void function
1091 #else
1092 #define MODULE_ENTRY(function) void _gdk_pixbuf__gif_ ## function
1093 #endif
1094
MODULE_ENTRY(fill_vtable)1095 MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
1096 {
1097 module->load = gdk_pixbuf__gif_image_load;
1098 module->begin_load = gdk_pixbuf__gif_image_begin_load;
1099 module->stop_load = gdk_pixbuf__gif_image_stop_load;
1100 module->load_increment = gdk_pixbuf__gif_image_load_increment;
1101 module->load_animation = gdk_pixbuf__gif_image_load_animation;
1102 }
1103
MODULE_ENTRY(fill_info)1104 MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
1105 {
1106 static const GdkPixbufModulePattern signature[] = {
1107 { "GIF8", NULL, 100 },
1108 { NULL, NULL, 0 }
1109 };
1110 static const gchar *mime_types[] = {
1111 "image/gif",
1112 NULL
1113 };
1114 static const gchar *extensions[] = {
1115 "gif",
1116 NULL
1117 };
1118
1119 info->name = "gif";
1120 info->signature = (GdkPixbufModulePattern *) signature;
1121 info->description = NC_("image format", "GIF");
1122 info->mime_types = (gchar **) mime_types;
1123 info->extensions = (gchar **) extensions;
1124 info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
1125 info->license = "LGPL";
1126 }
1127