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