]> Pileus Git - ~andy/gtk/blob - gdk-pixbuf/io-gif.c
Add properties to GtkAdjustment. (#64601, Murray Cumming)
[~andy/gtk] / gdk-pixbuf / io-gif.c
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, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27
28 /* This loader is very hairy code.
29  *
30  * The main loop was not designed for incremental loading, so when it was hacked
31  * in it got a bit messy.  Basicly, every function is written to expect a failed
32  * read_gif, and lets you call it again assuming that the bytes are there.
33  *
34  * Return vals.
35  * Unless otherwise specified, these are the return vals for most functions:
36  *
37  *  0 -> success
38  * -1 -> more bytes needed.
39  * -2 -> failure; abort the load
40  * -3 -> control needs to be passed back to the main loop
41  *        \_ (most of the time returning 0 will get this, but not always)
42  *
43  * >1 -> for functions that get a guchar, the char will be returned.
44  *
45  * -jrb (11/03/1999)
46  */
47
48 /*
49  * If you have any images that crash this code, please, please let me know and
50  * send them to me.
51  *                                            <jrb@redhat.com>
52  */
53
54 \f
55
56 #include <config.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <errno.h>
60 #include "gdk-pixbuf-private.h"
61 #include "gdk-pixbuf-io.h"
62 #include "io-gif-animation.h"
63
64 \f
65
66 #undef DUMP_IMAGE_DETAILS 
67 #undef IO_GIFDEBUG
68
69 #define MAXCOLORMAPSIZE  256
70 #define MAX_LZW_BITS     12
71
72 #define INTERLACE          0x40
73 #define LOCALCOLORMAP      0x80
74 #define BitSet(byte, bit)  (((byte) & (bit)) == (bit))
75 #define LM_to_uint(a,b)         (((b)<<8)|(a))
76
77 \f
78
79 typedef unsigned char CMap[3][MAXCOLORMAPSIZE];
80
81 /* Possible states we can be in. */
82 enum {
83         GIF_START,
84         GIF_GET_COLORMAP,
85         GIF_GET_NEXT_STEP,
86         GIF_GET_FRAME_INFO,
87         GIF_GET_EXTENSION,
88         GIF_GET_COLORMAP2,
89         GIF_PREPARE_LZW,
90         GIF_LZW_FILL_BUFFER,
91         GIF_LZW_CLEAR_CODE,
92         GIF_GET_LZW,
93         GIF_DONE
94 };
95
96
97 typedef struct _Gif89 Gif89;
98 struct _Gif89
99 {
100         int transparent;
101         int delay_time;
102         int input_flag;
103         int disposal;
104 };
105
106 typedef struct _GifContext GifContext;
107 struct _GifContext
108 {
109         int state; /* really only relevant for progressive loading */
110         unsigned int width;
111         unsigned int height;
112
113         gboolean has_global_cmap;
114
115         CMap global_color_map;
116         gint global_colormap_size;
117         unsigned int global_bit_pixel;
118         unsigned int global_color_resolution;
119         unsigned int background_index;
120
121         gboolean frame_cmap_active;
122         CMap frame_color_map;
123         gint frame_colormap_size;
124         unsigned int frame_bit_pixel;
125
126         unsigned int aspect_ratio;
127         GdkPixbufGifAnim *animation;
128         GdkPixbufFrame *frame;
129         Gif89 gif89;
130
131         /* stuff per frame. */
132         int frame_len;
133         int frame_height;
134         int frame_interlace;
135         int x_offset;
136         int y_offset;
137
138         /* Static read only */
139         FILE *file;
140
141         /* progressive read, only. */
142         GdkPixbufModulePreparedFunc prepare_func;
143         GdkPixbufModuleUpdatedFunc update_func;
144         gpointer user_data;
145         guchar *buf;
146         guint ptr;
147         guint size;
148         guint amount_needed;
149
150         /* extension context */
151         guchar extension_label;
152         guchar extension_flag;
153         gboolean in_loop_extension;
154
155         /* get block context */
156         guchar block_count;
157         guchar block_buf[280];
158         gint block_ptr;
159
160         int old_state; /* used by lzw_fill buffer */
161         /* get_code context */
162         int code_curbit;
163         int code_lastbit;
164         int code_done;
165         int code_last_byte;
166         int lzw_code_pending;
167
168         /* lzw context */
169         gint lzw_fresh;
170         gint lzw_code_size;
171         guchar lzw_set_code_size;
172         gint lzw_max_code;
173         gint lzw_max_code_size;
174         gint lzw_firstcode;
175         gint lzw_oldcode;
176         gint lzw_clear_code;
177         gint lzw_end_code;
178         gint *lzw_sp;
179
180         gint lzw_table[2][(1 << MAX_LZW_BITS)];
181         gint lzw_stack[(1 << (MAX_LZW_BITS)) * 2 + 1];
182
183         /* painting context */
184         gint draw_xpos;
185         gint draw_ypos;
186         gint draw_pass;
187
188         /* error pointer */
189         GError **error;
190 };
191
192 static int GetDataBlock (GifContext *, unsigned char *);
193
194 \f
195
196 #ifdef IO_GIFDEBUG
197 static int count = 0;
198 #endif
199
200 /* Returns TRUE if read is OK,
201  * FALSE if more memory is needed. */
202 static gboolean
203 gif_read (GifContext *context, guchar *buffer, size_t len)
204 {
205         gboolean retval;
206 #ifdef IO_GIFDEBUG
207         gint i;
208 #endif
209         if (context->file) {
210 #ifdef IO_GIFDEBUG
211                 count += len;
212                 g_print ("Fsize :%d\tcount :%d\t", len, count);
213 #endif
214                 retval = (fread(buffer, len, 1, context->file) != 0);
215
216                 if (!retval && ferror (context->file))
217                         g_set_error (context->error,
218                                      G_FILE_ERROR,
219                                      g_file_error_from_errno (errno),
220                                      _("Failure reading GIF: %s"), strerror (errno));
221                 
222 #ifdef IO_GIFDEBUG
223                 if (len < 100) {
224                         for (i = 0; i < len; i++)
225                                 g_print ("%d ", buffer[i]);
226                 }
227                 g_print ("\n");
228 #endif
229                 
230                 return retval;
231         } else {
232 #ifdef IO_GIFDEBUG
233 /*              g_print ("\tlooking for %d bytes.  size == %d, ptr == %d\n", len, context->size, context->ptr); */
234 #endif
235                 if ((context->size - context->ptr) >= len) {
236 #ifdef IO_GIFDEBUG
237                         count += len;
238 #endif
239                         memcpy (buffer, context->buf + context->ptr, len);
240                         context->ptr += len;
241                         context->amount_needed = 0;
242 #ifdef IO_GIFDEBUG
243                         g_print ("Psize :%d\tcount :%d\t", len, count);
244                         if (len < 100) {
245                                 for (i = 0; i < len; i++)
246                                         g_print ("%d ", buffer[i]);
247                         }
248                         g_print ("\n");
249 #endif
250                         return TRUE;
251                 }
252                 context->amount_needed = len - (context->size - context->ptr);
253         }
254         return FALSE;
255 }
256
257 /* Changes the stage to be GIF_GET_COLORMAP */
258 static void
259 gif_set_get_colormap (GifContext *context)
260 {
261         context->global_colormap_size = 0;
262         context->state = GIF_GET_COLORMAP;
263 }
264
265 static void
266 gif_set_get_colormap2 (GifContext *context)
267 {
268         context->frame_colormap_size = 0;
269         context->state = GIF_GET_COLORMAP2;
270 }
271
272 static gint
273 gif_get_colormap (GifContext *context)
274 {
275         unsigned char rgb[3];
276
277         while (context->global_colormap_size < context->global_bit_pixel) {
278                 if (!gif_read (context, rgb, sizeof (rgb))) {
279                         return -1;
280                 }
281
282                 context->global_color_map[0][context->global_colormap_size] = rgb[0];
283                 context->global_color_map[1][context->global_colormap_size] = rgb[1];
284                 context->global_color_map[2][context->global_colormap_size] = rgb[2];
285
286                 if (context->global_colormap_size == context->background_index) {
287                         context->animation->bg_red = rgb[0];
288                         context->animation->bg_green = rgb[1];
289                         context->animation->bg_blue = rgb[2];
290                 }
291
292                 context->global_colormap_size ++;
293         }
294
295         return 0;
296 }
297
298
299 static gint
300 gif_get_colormap2 (GifContext *context)
301 {
302         unsigned char rgb[3];
303
304         while (context->frame_colormap_size < context->frame_bit_pixel) {
305                 if (!gif_read (context, rgb, sizeof (rgb))) {
306                         return -1;
307                 }
308
309                 context->frame_color_map[0][context->frame_colormap_size] = rgb[0];
310                 context->frame_color_map[1][context->frame_colormap_size] = rgb[1];
311                 context->frame_color_map[2][context->frame_colormap_size] = rgb[2];
312
313                 context->frame_colormap_size ++;
314         }
315
316         return 0;
317 }
318
319 /*
320  * in order for this function to work, we need to perform some black magic.
321  * We want to return -1 to let the calling function know, as before, that it needs
322  * more bytes.  If we return 0, we were able to successfully read all block->count bytes.
323  * Problem is, we don't want to reread block_count every time, so we check to see if
324  * context->block_count is 0 before we read in the function.
325  *
326  * As a result, context->block_count MUST be 0 the first time the get_data_block is called
327  * within a context, and cannot be 0 the second time it's called.
328  */
329
330 static int
331 get_data_block (GifContext *context,
332                 unsigned char *buf,
333                 gint *empty_block)
334 {
335
336         if (context->block_count == 0) {
337                 if (!gif_read (context, &context->block_count, 1)) {
338                         return -1;
339                 }
340         }
341
342         if (context->block_count == 0)
343                 if (empty_block) {
344                         *empty_block = TRUE;
345                         return 0;
346                 }
347
348         if (!gif_read (context, buf, context->block_count)) {
349                 return -1;
350         }
351
352         return 0;
353 }
354
355 static void
356 gif_set_get_extension (GifContext *context)
357 {
358         context->state = GIF_GET_EXTENSION;
359         context->extension_flag = TRUE;
360         context->extension_label = 0;
361         context->block_count = 0;
362         context->block_ptr = 0;
363 }
364
365 static int
366 gif_get_extension (GifContext *context)
367 {
368         gint retval;
369         gint empty_block = FALSE;
370
371         if (context->extension_flag) {
372                 if (context->extension_label == 0) {
373                         /* I guess bad things can happen if we have an extension of 0 )-: */
374                         /* I should look into this sometime */
375                         if (!gif_read (context, & context->extension_label , 1)) {
376                                 return -1;
377                         }
378                 }
379
380                 switch (context->extension_label) {
381                 case 0xf9:                      /* Graphic Control Extension */
382                         retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
383                         if (retval != 0)
384                                 return retval;
385
386                         if (context->frame == NULL) {
387                                 /* I only want to set the transparency if I haven't
388                                  * created the frame yet.
389                                  */
390                                 context->gif89.disposal = (context->block_buf[0] >> 2) & 0x7;
391                                 context->gif89.input_flag = (context->block_buf[0] >> 1) & 0x1;
392                                 context->gif89.delay_time = LM_to_uint (context->block_buf[1], context->block_buf[2]);
393                                 
394                                 if ((context->block_buf[0] & 0x1) != 0) {
395                                         context->gif89.transparent = context->block_buf[3];
396                                 } else {
397                                         context->gif89.transparent = -1;
398                                 }
399                         }
400
401                         /* Now we've successfully loaded this one, we continue on our way */
402                         context->block_count = 0;
403                         context->extension_flag = FALSE;
404                         break;
405                 case 0xff: /* application extension */
406                         if (!context->in_loop_extension) { 
407                                 retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
408                                 if (retval != 0)
409                                         return retval;
410                                 if (!strncmp (context->block_buf, "NETSCAPE2.0", 11) ||
411                                     !strncmp (context->block_buf, "ANIMEXTS1.0", 11)) {
412                                         context->in_loop_extension = TRUE;
413                                 }
414                                 context->block_count = 0;
415                         }
416                         if (context->in_loop_extension) {
417                                 do {
418                                         retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
419                                         if (retval != 0)
420                                                 return retval;
421                                         if (context->block_buf[0] == 0x01) {
422                                                 context->animation->loop = context->block_buf[1] + (context->block_buf[2] << 8);
423                                                 if (context->animation->loop != 0) 
424                                                         context->animation->loop++;
425                                         }
426                                         context->block_count = 0;
427                                 }
428                                 while (!empty_block);
429                                 context->in_loop_extension = FALSE;
430                                 context->extension_flag = FALSE;
431                                 return 0;
432                         }
433                         break;                          
434                 default:
435                         /* Unhandled extension */
436                         break;
437                 }
438         }
439         /* read all blocks, until I get an empty block, in case there was an extension I didn't know about. */
440         do {
441                 retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
442                 if (retval != 0)
443                         return retval;
444                 context->block_count = 0;
445         } while (!empty_block);
446
447         return 0;
448 }
449
450 static int ZeroDataBlock = FALSE;
451
452 static int
453 GetDataBlock (GifContext *context,
454               unsigned char *buf)
455 {
456 /*      unsigned char count; */
457
458         if (!gif_read (context, &context->block_count, 1)) {
459                 /*g_message (_("GIF: error in getting DataBlock size\n"));*/
460                 return -1;
461         }
462
463         ZeroDataBlock = context->block_count == 0;
464
465         if ((context->block_count != 0) && (!gif_read (context, buf, context->block_count))) {
466                 /*g_message (_("GIF: error in reading DataBlock\n"));*/
467                 return -1;
468         }
469
470         return context->block_count;
471 }
472
473
474 static void
475 gif_set_lzw_fill_buffer (GifContext *context)
476 {
477         context->block_count = 0;
478         context->old_state = context->state;
479         context->state = GIF_LZW_FILL_BUFFER;
480 }
481
482 static int
483 gif_lzw_fill_buffer (GifContext *context)
484 {
485         gint retval;
486
487         if (context->code_done) {
488                 if (context->code_curbit >= context->code_lastbit) {
489                         g_set_error (context->error,
490                                      GDK_PIXBUF_ERROR,
491                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
492                                      _("GIF file was missing some data (perhaps it was truncated somehow?)"));
493
494                         return -2;
495                 }
496                 /* Is this supposed to be an error or what? */
497                 /* g_message ("trying to read more data after we've done stuff\n"); */
498                 g_set_error (context->error,
499                              GDK_PIXBUF_ERROR,
500                              GDK_PIXBUF_ERROR_FAILED,
501                              _("Internal error in the GIF loader (%s)"),
502                              G_STRLOC);
503                 
504                 return -2;
505         }
506
507         context->block_buf[0] = context->block_buf[context->code_last_byte - 2];
508         context->block_buf[1] = context->block_buf[context->code_last_byte - 1];
509
510         retval = get_data_block (context, &context->block_buf[2], NULL);
511
512         if (retval == -1)
513                 return -1;
514
515         if (context->block_count == 0)
516                 context->code_done = TRUE;
517
518         context->code_last_byte = 2 + context->block_count;
519         context->code_curbit = (context->code_curbit - context->code_lastbit) + 16;
520         context->code_lastbit = (2 + context->block_count) * 8;
521
522         context->state = context->old_state;
523         return 0;
524 }
525
526 static int
527 get_code (GifContext *context,
528           int   code_size)
529 {
530         int i, j, ret;
531
532         if ((context->code_curbit + code_size) >= context->code_lastbit){
533                 gif_set_lzw_fill_buffer (context);
534                 return -3;
535         }
536
537         ret = 0;
538         for (i = context->code_curbit, j = 0; j < code_size; ++i, ++j)
539                 ret |= ((context->block_buf[i / 8] & (1 << (i % 8))) != 0) << j;
540
541         context->code_curbit += code_size;
542
543         return ret;
544 }
545
546
547 static void
548 set_gif_lzw_clear_code (GifContext *context)
549 {
550         context->state = GIF_LZW_CLEAR_CODE;
551         context->lzw_code_pending = -1;
552 }
553
554 static int
555 gif_lzw_clear_code (GifContext *context)
556 {
557         gint code;
558
559         code = get_code (context, context->lzw_code_size);
560         if (code == -3)
561                 return -0;
562
563         context->lzw_firstcode = context->lzw_oldcode = code;
564         context->lzw_code_pending = code;
565         context->state = GIF_GET_LZW;
566         return 0;
567 }
568
569 #define CHECK_LZW_SP() G_STMT_START {                                           \
570         if ((guchar *)context->lzw_sp >=                                        \
571             (guchar *)context->lzw_stack + sizeof (context->lzw_stack)) {       \
572                  g_set_error (context->error,                                   \
573                              GDK_PIXBUF_ERROR,                                  \
574                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,                    \
575                              _("Stack overflow"));                              \
576                 return -2;                                                      \
577         }                                                                       \
578 } G_STMT_END
579
580 static int
581 lzw_read_byte (GifContext *context)
582 {
583         int code, incode;
584         gint retval;
585         gint my_retval;
586         register int i;
587
588         if (context->lzw_code_pending != -1) {
589                 retval = context->lzw_code_pending;
590                 context->lzw_code_pending = -1;
591                 return retval;
592         }
593
594         if (context->lzw_fresh) {
595                 context->lzw_fresh = FALSE;
596                 do {
597                         retval = get_code (context, context->lzw_code_size);
598                         if (retval < 0) {
599                                 return retval;
600                         }
601
602                         context->lzw_firstcode = context->lzw_oldcode = retval;
603                 } while (context->lzw_firstcode == context->lzw_clear_code);
604                 return context->lzw_firstcode;
605         }
606
607         if (context->lzw_sp > context->lzw_stack) {
608                 my_retval = *--(context->lzw_sp);
609                 return my_retval;
610         }
611
612         while ((code = get_code (context, context->lzw_code_size)) >= 0) {
613                 if (code == context->lzw_clear_code) {
614                         for (i = 0; i < context->lzw_clear_code; ++i) {
615                                 context->lzw_table[0][i] = 0;
616                                 context->lzw_table[1][i] = i;
617                         }
618                         for (; i < (1 << MAX_LZW_BITS); ++i)
619                                 context->lzw_table[0][i] = context->lzw_table[1][i] = 0;
620                         context->lzw_code_size = context->lzw_set_code_size + 1;
621                         context->lzw_max_code_size = 2 * context->lzw_clear_code;
622                         context->lzw_max_code = context->lzw_clear_code + 2;
623                         context->lzw_sp = context->lzw_stack;
624
625                         set_gif_lzw_clear_code (context);
626                         return -3;
627                 } else if (code == context->lzw_end_code) {
628                         int count;
629                         unsigned char buf[260];
630
631                         /*  FIXME - we should handle this case */
632                         g_set_error (context->error,
633                                      GDK_PIXBUF_ERROR,
634                                      GDK_PIXBUF_ERROR_FAILED,
635                                      _("GIF image loader can't understand this image."));
636                         return -2;
637                         
638                         if (ZeroDataBlock) {
639                                 return -2;
640                         }
641
642                         while ((count = GetDataBlock (context, buf)) > 0)
643                                 ;
644
645                         if (count != 0) {
646                                 /*g_print (_("GIF: missing EOD in data stream (common occurence)"));*/
647                                 return -2;
648                         }
649                 }
650
651                 incode = code;
652
653                 if (code >= context->lzw_max_code) {
654                         CHECK_LZW_SP ();
655                         *(context->lzw_sp)++ = context->lzw_firstcode;
656                         code = context->lzw_oldcode;
657                 }
658
659                 while (code >= context->lzw_clear_code) {
660                         if (code >= (1 << MAX_LZW_BITS)) {
661                                 g_set_error (context->error,
662                                              GDK_PIXBUF_ERROR,
663                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
664                                              _("Bad code encountered"));
665                                 return -2;
666                         }
667                         CHECK_LZW_SP ();
668                         *(context->lzw_sp)++ = context->lzw_table[1][code];
669
670                         if (code == context->lzw_table[0][code]) {
671                                 g_set_error (context->error,
672                                              GDK_PIXBUF_ERROR,
673                                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
674                                              _("Circular table entry in GIF file"));
675                                 return -2;
676                         }
677                         code = context->lzw_table[0][code];
678                 }
679
680                 CHECK_LZW_SP ();
681                 *(context->lzw_sp)++ = context->lzw_firstcode = context->lzw_table[1][code];
682
683                 if ((code = context->lzw_max_code) < (1 << MAX_LZW_BITS)) {
684                         context->lzw_table[0][code] = context->lzw_oldcode;
685                         context->lzw_table[1][code] = context->lzw_firstcode;
686                         ++context->lzw_max_code;
687                         if ((context->lzw_max_code >= context->lzw_max_code_size) &&
688                             (context->lzw_max_code_size < (1 << MAX_LZW_BITS))) {
689                                 context->lzw_max_code_size *= 2;
690                                 ++context->lzw_code_size;
691                         }
692                 }
693
694                 context->lzw_oldcode = incode;
695
696                 if (context->lzw_sp > context->lzw_stack) {
697                         my_retval = *--(context->lzw_sp);
698                         return my_retval;
699                 }
700         }
701         return code;
702 }
703
704 static void
705 gif_set_get_lzw (GifContext *context)
706 {
707         context->state = GIF_GET_LZW;
708         context->draw_xpos = 0;
709         context->draw_ypos = 0;
710         context->draw_pass = 0;
711 }
712
713 static void
714 gif_fill_in_pixels (GifContext *context, guchar *dest, gint offset, guchar v)
715 {
716         guchar *pixel = NULL;
717         guchar (*cmap)[MAXCOLORMAPSIZE];
718
719         if (context->frame_cmap_active)
720                 cmap = context->frame_color_map;
721         else
722                 cmap = context->global_color_map;
723         
724         if (context->gif89.transparent != -1) {
725                 pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 4;
726                 *pixel = cmap [0][(guchar) v];
727                 *(pixel+1) = cmap [1][(guchar) v];
728                 *(pixel+2) = cmap [2][(guchar) v];
729                 *(pixel+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 255);
730         } else {
731                 pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 3;
732                 *pixel = cmap [0][(guchar) v];
733                 *(pixel+1) = cmap [1][(guchar) v];
734                 *(pixel+2) = cmap [2][(guchar) v];
735         }
736 }
737
738
739 /* only called if progressive and interlaced */
740 static void
741 gif_fill_in_lines (GifContext *context, guchar *dest, guchar v)
742 {
743         switch (context->draw_pass) {
744         case 0:
745                 if (context->draw_ypos > 4) {
746                         gif_fill_in_pixels (context, dest, -4, v);
747                         gif_fill_in_pixels (context, dest, -3, v);
748                 }
749                 if (context->draw_ypos < (context->frame_height - 4)) {
750                         gif_fill_in_pixels (context, dest, 3, v);
751                         gif_fill_in_pixels (context, dest, 4, v);
752                 }
753                 /* we don't need a break here.  We draw the outer pixels first, then the
754                  * inner ones, then the innermost ones.  case 0 needs to draw all 3 bands.
755                  * case 1, just the last two, and case 2 just draws the last one*/
756         case 1:
757                 if (context->draw_ypos > 2)
758                         gif_fill_in_pixels (context, dest, -2, v);
759                 if (context->draw_ypos < (context->frame_height - 2))
760                         gif_fill_in_pixels (context, dest, 2, v);
761                 /* no break as above. */
762         case 2:
763                 if (context->draw_ypos > 1)
764                         gif_fill_in_pixels (context, dest, -1, v);
765                 if (context->draw_ypos < (context->frame_height - 1))
766                         gif_fill_in_pixels (context, dest, 1, v);
767         case 3:
768         default:
769                 break;
770         }
771 }
772
773 static void
774 set_need_recomposite (gpointer data, gpointer user_data)
775 {
776         GdkPixbufFrame *frame = (GdkPixbufFrame *)data;
777         frame->need_recomposite = TRUE;
778 }
779
780 /* Clips a rectancle to the base dimensions. Returns TRUE if the clipped rectangle is non-empty. */
781 static gboolean
782 clip_frame (GifContext *context, 
783             gint       *x, 
784             gint       *y, 
785             gint       *width, 
786             gint       *height)
787 {
788         gint orig_x, orig_y;
789         
790         orig_x = *x;
791         orig_y = *y;
792         *x = MAX (0, *x);
793         *y = MAX (0, *y);
794         *width = MIN (context->width, orig_x + *width) - *x;
795         *height = MIN (context->height, orig_y + *height) - *y;
796
797         if (*width > 0 && *height > 0)
798                 return TRUE;
799
800         /* The frame is completely off-bounds */
801
802         *x = 0;
803         *y = 0;
804         *width = 0;
805         *height = 0;
806
807         return FALSE;
808 }
809
810 /* Call update_func on the given rectangle, unless it is completely off-bounds */
811 static void
812 maybe_update (GifContext *context,
813               gint        x,
814               gint        y,
815               gint        width,
816               gint        height)
817 {
818         if (clip_frame (context, &x, &y, &width, &height))
819                 (*context->update_func) (context->frame->pixbuf, 
820                                          x, y, width, height,
821                                          context->user_data);
822 }
823
824 static int
825 gif_get_lzw (GifContext *context)
826 {
827         guchar *dest, *temp;
828         gint lower_bound, upper_bound; /* bounds for emitting the area_updated signal */
829         gboolean bound_flag;
830         gint first_pass; /* bounds for emitting the area_updated signal */
831         gint v;
832
833         if (context->frame == NULL) {
834                 context->frame = g_new (GdkPixbufFrame, 1);
835
836                 context->frame->composited = NULL;
837                 context->frame->revert = NULL;
838                 
839                 if (context->frame_len == 0 || context->frame_height == 0) {
840                         /* An empty frame, we just output a single transparent
841                          * pixel at (0, 0).
842                          */
843                         context->x_offset = 0;
844                         context->y_offset = 0;
845                         context->frame_len = 1;
846                         context->frame_height = 1;
847                         context->frame->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
848                         if (context->frame->pixbuf) {
849                                 guchar *pixels;
850
851                                 pixels = gdk_pixbuf_get_pixels (context->frame->pixbuf);
852                                 pixels[0] = 0;
853                                 pixels[1] = 0;
854                                 pixels[2] = 0;
855                                 pixels[3] = 0;
856                         }
857                 } else
858                         context->frame->pixbuf =
859                                 gdk_pixbuf_new (GDK_COLORSPACE_RGB,
860                                                 TRUE,
861                                                 8,
862                                                 context->frame_len,
863                                                 context->frame_height);
864                 if (!context->frame->pixbuf) {
865                         g_free (context->frame);
866                         g_set_error (context->error,
867                                      GDK_PIXBUF_ERROR,
868                                      GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
869                                      _("Not enough memory to load GIF file"));
870                         return -2;
871                 }
872
873                 context->frame->x_offset = context->x_offset;
874                 context->frame->y_offset = context->y_offset;
875                 context->frame->need_recomposite = TRUE;
876                 
877                 /* GIF delay is in hundredths, we want thousandths */
878                 context->frame->delay_time = context->gif89.delay_time * 10;
879
880                 /* Some GIFs apparently have delay time of 0,
881                  * that crashes everything so set it to "fast".
882                  * Also, timeouts less than 20 or so just lock up
883                  * the app or make the animation choppy, so fix them.
884                  */
885                 if (context->frame->delay_time < 20)
886                         context->frame->delay_time = 20; /* 20 = "fast" */
887                 
888                 context->frame->elapsed = context->animation->total_time;
889                 context->animation->total_time += context->frame->delay_time;                
890                 
891                 switch (context->gif89.disposal) {
892                 case 0:
893                 case 1:
894                         context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
895                         break;
896                 case 2:
897                         context->frame->action = GDK_PIXBUF_FRAME_DISPOSE;
898                         break;
899                 case 3:
900                         context->frame->action = GDK_PIXBUF_FRAME_REVERT;
901                         break;
902                 default:
903                         context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
904                         break;
905                 }
906
907                 context->frame->bg_transparent = (context->gif89.transparent == context->background_index);
908                 
909                 context->animation->n_frames ++;
910                 context->animation->frames = g_list_append (context->animation->frames, context->frame);
911
912                 /* Only call prepare_func for the first frame */
913                 if (context->animation->frames->next == NULL) { 
914                         if (context->prepare_func)
915                                 (* context->prepare_func) (context->frame->pixbuf,
916                                                            GDK_PIXBUF_ANIMATION (context->animation),
917                                                            context->user_data);
918                 } else {
919                         /* Otherwise init frame with last frame */
920                         GList *link;
921                         GdkPixbufFrame *prev_frame;
922                         gint x, y, w, h;
923                         
924                         link = g_list_find (context->animation->frames, context->frame);
925
926                         prev_frame = link->prev->data;
927
928                         gdk_pixbuf_gif_anim_frame_composite (context->animation, prev_frame);
929
930                         x = context->frame->x_offset;
931                         y = context->frame->y_offset;
932                         w = gdk_pixbuf_get_width (context->frame->pixbuf);
933                         h = gdk_pixbuf_get_height (context->frame->pixbuf);
934                         if (clip_frame (context, &x, &y, &w, &h))
935                                 gdk_pixbuf_copy_area (prev_frame->composited,
936                                                       x, y, w, h,
937                                                       context->frame->pixbuf,
938                                                       0, 0);
939                 }
940         }
941
942         dest = gdk_pixbuf_get_pixels (context->frame->pixbuf);
943
944         bound_flag = FALSE;
945         lower_bound = upper_bound = context->draw_ypos;
946         first_pass = context->draw_pass;
947
948         while (TRUE) {
949                 guchar (*cmap)[MAXCOLORMAPSIZE];
950
951                 if (context->frame_cmap_active)
952                         cmap = context->frame_color_map;
953                 else
954                         cmap = context->global_color_map;
955                 
956                 v = lzw_read_byte (context);
957                 if (v < 0) {
958                         goto finished_data;
959                 }
960                 bound_flag = TRUE;
961
962                 g_assert (gdk_pixbuf_get_has_alpha (context->frame->pixbuf));
963                 
964                 temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 4;
965                 *temp = cmap [0][(guchar) v];
966                 *(temp+1) = cmap [1][(guchar) v];
967                 *(temp+2) = cmap [2][(guchar) v];
968                 *(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 255);
969
970                 if (context->prepare_func && context->frame_interlace)
971                         gif_fill_in_lines (context, dest, v);
972
973                 context->draw_xpos++;
974                 
975                 if (context->draw_xpos == context->frame_len) {
976                         context->draw_xpos = 0;
977                         if (context->frame_interlace) {
978                                 switch (context->draw_pass) {
979                                 case 0:
980                                 case 1:
981                                         context->draw_ypos += 8;
982                                         break;
983                                 case 2:
984                                         context->draw_ypos += 4;
985                                         break;
986                                 case 3:
987                                         context->draw_ypos += 2;
988                                         break;
989                                 }
990
991                                 if (context->draw_ypos >= context->frame_height) {
992                                         context->draw_pass++;
993                                         switch (context->draw_pass) {
994                                         case 1:
995                                                 context->draw_ypos = 4;
996                                                 break;
997                                         case 2:
998                                                 context->draw_ypos = 2;
999                                                 break;
1000                                         case 3:
1001                                                 context->draw_ypos = 1;
1002                                                 break;
1003                                         default:
1004                                                 goto done;
1005                                         }
1006                                 }
1007                         } else {
1008                                 context->draw_ypos++;
1009                         }
1010                         if (context->draw_pass != first_pass) {
1011                                 if (context->draw_ypos > lower_bound) {
1012                                         lower_bound = 0;
1013                                         upper_bound = context->frame_height;
1014                                 } else {
1015                                         
1016                                 }
1017                         } else
1018                                 upper_bound = context->draw_ypos;
1019                 }
1020                 if (context->draw_ypos >= context->frame_height)
1021                         break;
1022         }
1023
1024  done:
1025
1026         context->state = GIF_GET_NEXT_STEP;
1027
1028         v = 0;
1029
1030  finished_data:
1031         
1032         if (bound_flag)
1033                 context->frame->need_recomposite = TRUE;
1034         
1035         if (bound_flag && context->update_func) {
1036                 if (lower_bound <= upper_bound && first_pass == context->draw_pass) {
1037                         maybe_update (context, 
1038                                       context->frame->x_offset,
1039                                       context->frame->y_offset + lower_bound,
1040                                       gdk_pixbuf_get_width (context->frame->pixbuf),
1041                                       upper_bound - lower_bound);
1042                 } else {
1043                         if (lower_bound <= upper_bound) {
1044                                 maybe_update (context,
1045                                               context->frame->x_offset,
1046                                               context->frame->y_offset,
1047                                               gdk_pixbuf_get_width (context->frame->pixbuf),
1048                                               gdk_pixbuf_get_height (context->frame->pixbuf));
1049                         } else {
1050                                 maybe_update (context,
1051                                               context->frame->x_offset,
1052                                               context->frame->y_offset,
1053                                               gdk_pixbuf_get_width (context->frame->pixbuf),
1054                                               upper_bound);
1055                                 maybe_update (context,
1056                                               context->frame->x_offset,
1057                                               context->frame->y_offset + lower_bound,
1058                                               gdk_pixbuf_get_width (context->frame->pixbuf),
1059                                               gdk_pixbuf_get_height (context->frame->pixbuf) - lower_bound);
1060                         }
1061                 }
1062         }
1063
1064         if (context->state == GIF_GET_NEXT_STEP) {
1065                 /* Will be freed with context->animation, we are just
1066                  * marking that we're done with it (no current frame)
1067                  */
1068                 context->frame = NULL;
1069                 context->frame_cmap_active = FALSE;
1070         }
1071         
1072         return v;
1073 }
1074
1075 static void
1076 gif_set_prepare_lzw (GifContext *context)
1077 {
1078         context->state = GIF_PREPARE_LZW;
1079         context->lzw_code_pending = -1;
1080 }
1081 static int
1082 gif_prepare_lzw (GifContext *context)
1083 {
1084         gint i;
1085
1086         if (!gif_read (context, &(context->lzw_set_code_size), 1)) {
1087                 /*g_message (_("GIF: EOF / read error on image data\n"));*/
1088                 return -1;
1089         }
1090         
1091         if (context->lzw_set_code_size > MAX_LZW_BITS) {
1092                 g_set_error (context->error,
1093                              GDK_PIXBUF_ERROR,
1094                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1095                              _("GIF image is corrupt (incorrect LZW compression)"));
1096                 return -2;
1097         }
1098
1099         context->lzw_code_size = context->lzw_set_code_size + 1;
1100         context->lzw_clear_code = 1 << context->lzw_set_code_size;
1101         context->lzw_end_code = context->lzw_clear_code + 1;
1102         context->lzw_max_code_size = 2 * context->lzw_clear_code;
1103         context->lzw_max_code = context->lzw_clear_code + 2;
1104         context->lzw_fresh = TRUE;
1105         context->code_curbit = 0;
1106         context->code_lastbit = 0;
1107         context->code_last_byte = 0;
1108         context->code_done = FALSE;
1109
1110         g_assert (context->lzw_clear_code <= 
1111                   G_N_ELEMENTS (context->lzw_table[0]));
1112
1113         for (i = 0; i < context->lzw_clear_code; ++i) {
1114                 context->lzw_table[0][i] = 0;
1115                 context->lzw_table[1][i] = i;
1116         }
1117         for (; i < (1 << MAX_LZW_BITS); ++i)
1118                 context->lzw_table[0][i] = context->lzw_table[1][0] = 0;
1119
1120         context->lzw_sp = context->lzw_stack;
1121         gif_set_get_lzw (context);
1122
1123         return 0;
1124 }
1125
1126 /* needs 13 bytes to proceed. */
1127 static gint
1128 gif_init (GifContext *context)
1129 {
1130         unsigned char buf[16];
1131         char version[4];
1132
1133         if (!gif_read (context, buf, 6)) {
1134                 /* Unable to read magic number,
1135                  * gif_read() should have set error
1136                  */
1137                 return -1;
1138         }
1139
1140         if (strncmp ((char *) buf, "GIF", 3) != 0) {
1141                 /* Not a GIF file */
1142                 g_set_error (context->error,
1143                              GDK_PIXBUF_ERROR,
1144                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1145                              _("File does not appear to be a GIF file"));
1146                 return -2;
1147         }
1148
1149         strncpy (version, (char *) buf + 3, 3);
1150         version[3] = '\0';
1151
1152         if ((strcmp (version, "87a") != 0) && (strcmp (version, "89a") != 0)) {
1153                 /* bad version number, not '87a' or '89a' */
1154                 g_set_error (context->error,
1155                              GDK_PIXBUF_ERROR,
1156                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1157                              _("Version %s of the GIF file format is not supported"),
1158                              version);
1159                 return -2;
1160         }
1161
1162         /* read the screen descriptor */
1163         if (!gif_read (context, buf, 7)) {
1164                 /* Failed to read screen descriptor, error set */
1165                 return -1;
1166         }
1167
1168         context->width = LM_to_uint (buf[0], buf[1]);
1169         context->height = LM_to_uint (buf[2], buf[3]);
1170         /* The 4th byte is
1171          * high bit: whether to use the background index
1172          * next 3:   color resolution
1173          * next:     whether colormap is sorted by priority of allocation
1174          * last 3:   size of colormap
1175          */
1176         context->global_bit_pixel = 2 << (buf[4] & 0x07);
1177         context->global_color_resolution = (((buf[4] & 0x70) >> 3) + 1);
1178         context->has_global_cmap = (buf[4] & 0x80) != 0;
1179         context->background_index = buf[5];
1180         context->aspect_ratio = buf[6];
1181
1182         /* Use background of transparent black as default, though if
1183          * one isn't set explicitly no one should ever use it.
1184          */
1185         context->animation->bg_red = 0;
1186         context->animation->bg_green = 0;
1187         context->animation->bg_blue = 0;
1188
1189         context->animation->width = context->width;
1190         context->animation->height = context->height;
1191
1192         if (context->has_global_cmap) {
1193                 gif_set_get_colormap (context);
1194         } else {
1195                 context->state = GIF_GET_NEXT_STEP;
1196         }
1197
1198 #ifdef DUMP_IMAGE_DETAILS
1199         g_print (">Image width: %d height: %d global_cmap: %d background: %d\n",
1200                  context->width, context->height, context->has_global_cmap, context->background_index);
1201 #endif
1202         
1203         return 0;
1204 }
1205
1206 static void
1207 gif_set_get_frame_info (GifContext *context)
1208 {
1209         context->state = GIF_GET_FRAME_INFO;
1210 }
1211
1212 static gint
1213 gif_get_frame_info (GifContext *context)
1214 {
1215         unsigned char buf[9];
1216         
1217         if (!gif_read (context, buf, 9)) {
1218                 return -1;
1219         }
1220         
1221         /* Okay, we got all the info we need.  Lets record it */
1222         context->frame_len = LM_to_uint (buf[4], buf[5]);
1223         context->frame_height = LM_to_uint (buf[6], buf[7]);
1224         context->x_offset = LM_to_uint (buf[0], buf[1]);
1225         context->y_offset = LM_to_uint (buf[2], buf[3]);
1226
1227         if (context->animation->frames == NULL &&
1228             context->gif89.disposal == 3) {
1229                 /* First frame can't have "revert to previous" as its
1230                  * dispose mode. Silently use "retain" instead.
1231                  */
1232                 context->gif89.disposal = 0;
1233         }
1234
1235         context->frame_interlace = BitSet (buf[8], INTERLACE);
1236
1237 #ifdef DUMP_IMAGE_DETAILS
1238         g_print (">width: %d height: %d xoffset: %d yoffset: %d disposal: %d delay: %d transparent: %d interlace: %d\n",
1239                  context->frame_len, context->frame_height, context->x_offset, context->y_offset,
1240                  context->gif89.disposal, context->gif89.delay_time, context->gif89.transparent, context->frame_interlace);
1241 #endif
1242         
1243         if (BitSet (buf[8], LOCALCOLORMAP)) {
1244
1245 #ifdef DUMP_IMAGE_DETAILS
1246                 g_print (">has local colormap\n");
1247 #endif
1248                 
1249                 /* Does this frame have it's own colormap. */
1250                 /* really only relevant when looking at the first frame
1251                  * of an animated gif. */
1252                 /* if it does, we need to re-read in the colormap,
1253                  * the gray_scale, and the bit_pixel */
1254                 context->frame_cmap_active = TRUE;
1255                 context->frame_bit_pixel = 1 << ((buf[8] & 0x07) + 1);
1256                 gif_set_get_colormap2 (context);
1257                 return 0;
1258         }
1259
1260         if (!context->has_global_cmap) {
1261                 context->state = GIF_DONE;
1262                 
1263                 g_set_error (context->error,
1264                              GDK_PIXBUF_ERROR,
1265                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1266                              _("GIF image has no global colormap, and a frame inside it has no local colormap."));
1267                 
1268                 return -2;
1269         }
1270
1271         gif_set_prepare_lzw (context);
1272         return 0;
1273
1274 }
1275
1276 static gint
1277 gif_get_next_step (GifContext *context)
1278 {
1279         unsigned char c;
1280         while (TRUE) {
1281                 if (!gif_read (context, &c, 1)) {
1282                         return -1;
1283                 }
1284                 if (c == ';') {
1285                         /* GIF terminator */
1286                         /* hmm.  Not 100% sure what to do about this.  Should
1287                          * i try to return a blank image instead? */
1288                         context->state = GIF_DONE;
1289                         return 0;
1290                 }
1291
1292                 if (c == '!') {
1293                         /* Check the extension */
1294                         gif_set_get_extension (context);
1295                         return 0;
1296                 }
1297
1298                 /* look for frame */
1299                 if (c != ',') {
1300                         /* Not a valid start character */
1301                         continue;
1302                 }
1303                 /* load the frame */
1304                 gif_set_get_frame_info (context);
1305                 return 0;
1306         }
1307 }
1308
1309
1310 #define LOG(x) /* g_print ("%d: %s\n", __LINE__, x); */
1311
1312 static gint
1313 gif_main_loop (GifContext *context)
1314 {
1315         gint retval = 0;
1316
1317         do {
1318                 switch (context->state) {
1319                 case GIF_START:
1320                         LOG("start\n");
1321                         retval = gif_init (context);
1322                         break;
1323
1324                 case GIF_GET_COLORMAP:
1325                         LOG("get_colormap\n");
1326                         retval = gif_get_colormap (context);
1327                         if (retval == 0)
1328                                 context->state = GIF_GET_NEXT_STEP;
1329                         break;
1330
1331                 case GIF_GET_NEXT_STEP:
1332                         LOG("next_step\n");
1333                         retval = gif_get_next_step (context);
1334                         break;
1335
1336                 case GIF_GET_FRAME_INFO:
1337                         LOG("frame_info\n");
1338                         retval = gif_get_frame_info (context);
1339                         break;
1340
1341                 case GIF_GET_EXTENSION:
1342                         LOG("get_extension\n");
1343                         retval = gif_get_extension (context);
1344                         if (retval == 0)
1345                                 context->state = GIF_GET_NEXT_STEP;
1346                         break;
1347
1348                 case GIF_GET_COLORMAP2:
1349                         LOG("get_colormap2\n");
1350                         retval = gif_get_colormap2 (context);
1351                         if (retval == 0)
1352                                 gif_set_prepare_lzw (context);
1353                         break;
1354
1355                 case GIF_PREPARE_LZW:
1356                         LOG("prepare_lzw\n");
1357                         retval = gif_prepare_lzw (context);
1358                         break;
1359
1360                 case GIF_LZW_FILL_BUFFER:
1361                         LOG("fill_buffer\n");
1362                         retval = gif_lzw_fill_buffer (context);
1363                         break;
1364
1365                 case GIF_LZW_CLEAR_CODE:
1366                         LOG("clear_code\n");
1367                         retval = gif_lzw_clear_code (context);
1368                         break;
1369
1370                 case GIF_GET_LZW:
1371                         LOG("get_lzw\n");
1372                         retval = gif_get_lzw (context);
1373                         break;
1374
1375                 case GIF_DONE:
1376                         LOG("done\n");
1377                 default:
1378                         retval = 0;
1379                         goto done;
1380                 };
1381         } while ((retval == 0) || (retval == -3));
1382  done:
1383         return retval;
1384 }
1385
1386 static GifContext *
1387 new_context (void)
1388 {
1389         GifContext *context;
1390
1391         context = g_try_malloc (sizeof (GifContext));
1392         if (context == NULL)
1393                 return NULL;
1394
1395         memset (context, 0, sizeof (GifContext));
1396         
1397         context->animation = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM, NULL);        
1398         context->frame = NULL;
1399         context->file = NULL;
1400         context->state = GIF_START;
1401         context->prepare_func = NULL;
1402         context->update_func = NULL;
1403         context->user_data = NULL;
1404         context->buf = NULL;
1405         context->amount_needed = 0;
1406         context->gif89.transparent = -1;
1407         context->gif89.delay_time = -1;
1408         context->gif89.input_flag = -1;
1409         context->gif89.disposal = -1;
1410         context->animation->loop = 1;
1411         context->in_loop_extension = FALSE;
1412
1413         return context;
1414 }
1415 /* Shared library entry point */
1416 static GdkPixbuf *
1417 gdk_pixbuf__gif_image_load (FILE *file, GError **error)
1418 {
1419         GifContext *context;
1420         GdkPixbuf *pixbuf;
1421
1422         g_return_val_if_fail (file != NULL, NULL);
1423
1424         context = new_context ();
1425
1426         if (context == NULL) {
1427                 g_set_error (error,
1428                              GDK_PIXBUF_ERROR,
1429                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
1430                              _("Not enough memory to load GIF file"));
1431                 return NULL;
1432         }
1433         
1434         context->file = file;
1435         context->error = error;
1436         
1437         if (gif_main_loop (context) == -1 || context->animation->frames == NULL) {
1438                 if (context->error && *(context->error) == NULL)
1439                         g_set_error (context->error,
1440                                      GDK_PIXBUF_ERROR,
1441                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1442                                      _("GIF file was missing some data (perhaps it was truncated somehow?)"));
1443         }
1444         
1445         pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
1446
1447         if (pixbuf)
1448                 g_object_ref (pixbuf);
1449
1450         g_object_unref (context->animation);
1451         
1452         g_free (context->buf);
1453         g_free (context);
1454  
1455         return pixbuf;
1456 }
1457
1458 static gpointer
1459 gdk_pixbuf__gif_image_begin_load (GdkPixbufModuleSizeFunc size_func,
1460                                   GdkPixbufModulePreparedFunc prepare_func,
1461                                   GdkPixbufModuleUpdatedFunc update_func,
1462                                   gpointer user_data,
1463                                   GError **error)
1464 {
1465         GifContext *context;
1466
1467 #ifdef IO_GIFDEBUG
1468         count = 0;
1469 #endif
1470         context = new_context ();
1471
1472         if (context == NULL) {
1473                 g_set_error (error,
1474                              GDK_PIXBUF_ERROR,
1475                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
1476                              _("Not enough memory to load GIF file"));
1477                 return NULL;
1478         }
1479         
1480         context->error = error;
1481         context->prepare_func = prepare_func;
1482         context->update_func = update_func;
1483         context->user_data = user_data;
1484
1485         return (gpointer) context;
1486 }
1487
1488 static gboolean
1489 gdk_pixbuf__gif_image_stop_load (gpointer data, GError **error)
1490 {
1491         GifContext *context = (GifContext *) data;
1492         gboolean retval = TRUE;
1493         
1494         if (context->state != GIF_DONE) {
1495                 g_set_error (error,
1496                              GDK_PIXBUF_ERROR,
1497                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1498                              _("GIF image was truncated or incomplete."));
1499
1500                 retval = FALSE;
1501         }
1502         
1503         g_object_unref (context->animation);
1504
1505         g_free (context->buf);
1506         g_free (context);
1507
1508         return retval;
1509 }
1510
1511 static gboolean
1512 gdk_pixbuf__gif_image_load_increment (gpointer data,
1513                                       const guchar *buf, guint size,
1514                                       GError **error)
1515 {
1516         gint retval;
1517         GifContext *context = (GifContext *) data;
1518
1519         context->error = error;
1520         
1521         if (context->amount_needed == 0) {
1522                 /* we aren't looking for some bytes. */
1523                 /* we can use buf now, but we don't want to keep it around at all.
1524                  * it will be gone by the end of the call. */
1525                 context->buf = (guchar*) buf; /* very dubious const cast */
1526                 context->ptr = 0;
1527                 context->size = size;
1528         } else {
1529                 /* we need some bytes */
1530                 if (size < context->amount_needed) {
1531                         context->amount_needed -= size;
1532                         /* copy it over and return */
1533                         memcpy (context->buf + context->size, buf, size);
1534                         context->size += size;
1535                         return TRUE;
1536                 } else if (size == context->amount_needed) {
1537                         memcpy (context->buf + context->size, buf, size);
1538                         context->size += size;
1539                 } else {
1540                         context->buf = g_realloc (context->buf, context->size + size);
1541                         memcpy (context->buf + context->size, buf, size);
1542                         context->size += size;
1543                 }
1544         }
1545
1546         retval = gif_main_loop (context);
1547
1548         if (retval == -2) {
1549                 if (context->buf == buf)
1550                         context->buf = NULL;
1551                 return FALSE;
1552         }
1553         if (retval == -1) {
1554                 /* we didn't have enough memory */
1555                 /* prepare for the next image_load_increment */
1556                 if (context->buf == buf) {
1557                         g_assert (context->size == size);
1558                         context->buf = (guchar *)g_new (guchar, context->amount_needed + (context->size - context->ptr));
1559                         memcpy (context->buf, buf + context->ptr, context->size - context->ptr);
1560                 } else {
1561                         /* copy the left overs to the begining of the buffer */
1562                         /* and realloc the memory */
1563                         memmove (context->buf, context->buf + context->ptr, context->size - context->ptr);
1564                         context->buf = g_realloc (context->buf, context->amount_needed + (context->size - context->ptr));
1565                 }
1566                 context->size = context->size - context->ptr;
1567                 context->ptr = 0;
1568         } else {
1569                 /* we are prolly all done */
1570                 if (context->buf == buf)
1571                         context->buf = NULL;
1572         }
1573         return TRUE;
1574 }
1575
1576 static GdkPixbufAnimation *
1577 gdk_pixbuf__gif_image_load_animation (FILE *file,
1578                                       GError **error)
1579 {
1580         GifContext *context;
1581         GdkPixbufAnimation *animation;
1582
1583         g_return_val_if_fail (file != NULL, NULL);
1584
1585         context = new_context ();
1586
1587         if (context == NULL) {
1588                 g_set_error (error,
1589                              GDK_PIXBUF_ERROR,
1590                              GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
1591                              _("Not enough memory to load GIF file"));
1592                 return NULL;
1593         }
1594         
1595         context->error = error;
1596         context->file = file;
1597
1598         if (gif_main_loop (context) == -1 || context->animation->frames == NULL) {
1599                 if (context->error && *(context->error) == NULL)
1600                         g_set_error (context->error,
1601                                      GDK_PIXBUF_ERROR,
1602                                      GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1603                                      _("GIF file was missing some data (perhaps it was truncated somehow?)"));
1604
1605                 g_object_unref (context->animation);
1606                 context->animation = NULL;
1607         }
1608
1609         if (context->animation)
1610                 animation = GDK_PIXBUF_ANIMATION (context->animation);
1611         else
1612                 animation = NULL;
1613
1614         if (context->error && *(context->error))
1615                 g_print ("%s\n", (*(context->error))->message);
1616         
1617         g_free (context->buf);
1618         g_free (context);
1619         return animation;
1620 }
1621
1622 void
1623 MODULE_ENTRY (gif, fill_vtable) (GdkPixbufModule *module)
1624 {
1625         module->load = gdk_pixbuf__gif_image_load;
1626         module->begin_load = gdk_pixbuf__gif_image_begin_load;
1627         module->stop_load = gdk_pixbuf__gif_image_stop_load;
1628         module->load_increment = gdk_pixbuf__gif_image_load_increment;
1629         module->load_animation = gdk_pixbuf__gif_image_load_animation;
1630 }
1631
1632 void
1633 MODULE_ENTRY (gif, fill_info) (GdkPixbufFormat *info)
1634 {
1635         static GdkPixbufModulePattern signature[] = {
1636                 { "GIF8", NULL, 100 },
1637                 { NULL, NULL, 0 }
1638         };
1639         static gchar * mime_types[] = {
1640                 "image/gif",
1641                 NULL
1642         };
1643         static gchar * extensions[] = {
1644                 "gif",
1645                 NULL
1646         };
1647
1648         info->name = "gif";
1649         info->signature = signature;
1650         info->description = N_("The GIF image format");
1651         info->mime_types = mime_types;
1652         info->extensions = extensions;
1653         info->flags = 0;
1654 }