1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2 /* GdkPixbuf library - GIF image loader
4 * Copyright (C) 1999 Mark Crichton
5 * Copyright (C) 1999 The Free Software Foundation
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.
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.
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.
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.
28 /* This loader is very hairy code.
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.
35 * Unless otherwise specified, these are the return vals for most functions:
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)
43 * >1 -> for functions that get a guchar, the char will be returned.
49 * If you have any images that crash this code, please, please let me know and
60 #include "gdk-pixbuf-private.h"
61 #include "gdk-pixbuf-io.h"
62 #include "io-gif-animation.h"
66 #undef DUMP_IMAGE_DETAILS
68 #define MAXCOLORMAPSIZE 256
69 #define MAX_LZW_BITS 12
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))
78 typedef unsigned char CMap[3][MAXCOLORMAPSIZE];
80 /* Possible states we can be in. */
96 typedef struct _Gif89 Gif89;
105 typedef struct _GifContext GifContext;
108 int state; /* really only relevant for progressive loading */
112 gboolean has_global_cmap;
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;
120 gboolean frame_cmap_active;
121 CMap frame_color_map;
122 gint frame_colormap_size;
123 unsigned int frame_bit_pixel;
125 unsigned int aspect_ratio;
126 GdkPixbufGifAnim *animation;
127 GdkPixbufFrame *frame;
130 /* stuff per frame. */
137 /* Static read only */
140 /* progressive read, only. */
141 GdkPixbufModulePreparedFunc prepare_func;
142 GdkPixbufModuleUpdatedFunc update_func;
149 /* extension context */
150 guchar extension_label;
151 guchar extension_flag;
152 gboolean in_loop_extension;
154 /* get block context */
156 guchar block_buf[280];
159 int old_state; /* used by lzw_fill buffer */
160 /* get_code context */
165 int lzw_code_pending;
170 guchar lzw_set_code_size;
172 gint lzw_max_code_size;
179 gint lzw_table[2][(1 << MAX_LZW_BITS)];
180 gint lzw_stack[(1 << (MAX_LZW_BITS)) * 2 + 1];
182 /* painting context */
191 static int GetDataBlock (GifContext *, unsigned char *);
196 static int count = 0;
199 /* Returns TRUE if read is OK,
200 * FALSE if more memory is needed. */
202 gif_read (GifContext *context, guchar *buffer, size_t len)
211 g_print ("Fsize :%d\tcount :%d\t", len, count);
213 retval = (fread(buffer, len, 1, context->file) != 0);
215 if (!retval && ferror (context->file))
216 g_set_error (context->error,
218 g_file_error_from_errno (errno),
219 _("Failure reading GIF: %s"), strerror (errno));
223 for (i = 0; i < len; i++)
224 g_print ("%d ", buffer[i]);
232 /* g_print ("\tlooking for %d bytes. size == %d, ptr == %d\n", len, context->size, context->ptr); */
234 if ((context->size - context->ptr) >= len) {
238 memcpy (buffer, context->buf + context->ptr, len);
240 context->amount_needed = 0;
242 g_print ("Psize :%d\tcount :%d\t", len, count);
244 for (i = 0; i < len; i++)
245 g_print ("%d ", buffer[i]);
251 context->amount_needed = len - (context->size - context->ptr);
256 /* Changes the stage to be GIF_GET_COLORMAP */
258 gif_set_get_colormap (GifContext *context)
260 context->global_colormap_size = 0;
261 context->state = GIF_GET_COLORMAP;
265 gif_set_get_colormap2 (GifContext *context)
267 context->frame_colormap_size = 0;
268 context->state = GIF_GET_COLORMAP2;
272 gif_get_colormap (GifContext *context)
274 unsigned char rgb[3];
276 while (context->global_colormap_size < context->global_bit_pixel) {
277 if (!gif_read (context, rgb, sizeof (rgb))) {
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];
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];
291 context->global_colormap_size ++;
299 gif_get_colormap2 (GifContext *context)
301 unsigned char rgb[3];
303 while (context->frame_colormap_size < context->frame_bit_pixel) {
304 if (!gif_read (context, rgb, sizeof (rgb))) {
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];
312 context->frame_colormap_size ++;
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.
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.
330 get_data_block (GifContext *context,
335 if (context->block_count == 0) {
336 if (!gif_read (context, &context->block_count, 1)) {
341 if (context->block_count == 0)
347 if (!gif_read (context, buf, context->block_count)) {
355 gif_set_get_extension (GifContext *context)
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;
365 gif_get_extension (GifContext *context)
368 gint empty_block = FALSE;
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)) {
379 switch (context->extension_label) {
380 case 0xf9: /* Graphic Control Extension */
381 retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
385 if (context->frame == NULL) {
386 /* I only want to set the transparency if I haven't
387 * created the frame yet.
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]);
393 if ((context->block_buf[0] & 0x1) != 0) {
394 context->gif89.transparent = context->block_buf[3];
396 context->gif89.transparent = -1;
400 /* Now we've successfully loaded this one, we continue on our way */
401 context->block_count = 0;
402 context->extension_flag = FALSE;
404 case 0xff: /* application extension */
405 if (!context->in_loop_extension) {
406 retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
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;
415 if (context->in_loop_extension) {
417 retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
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++;
425 context->block_count = 0;
427 while (!empty_block);
428 context->in_loop_extension = FALSE;
429 context->extension_flag = FALSE;
434 /* Unhandled extension */
438 /* read all blocks, until I get an empty block, in case there was an extension I didn't know about. */
440 retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
443 context->block_count = 0;
444 } while (!empty_block);
449 static int ZeroDataBlock = FALSE;
452 GetDataBlock (GifContext *context,
455 /* unsigned char count; */
457 if (!gif_read (context, &context->block_count, 1)) {
458 /*g_message (_("GIF: error in getting DataBlock size\n"));*/
462 ZeroDataBlock = context->block_count == 0;
464 if ((context->block_count != 0) && (!gif_read (context, buf, context->block_count))) {
465 /*g_message (_("GIF: error in reading DataBlock\n"));*/
469 return context->block_count;
474 gif_set_lzw_fill_buffer (GifContext *context)
476 context->block_count = 0;
477 context->old_state = context->state;
478 context->state = GIF_LZW_FILL_BUFFER;
482 gif_lzw_fill_buffer (GifContext *context)
486 if (context->code_done) {
487 if (context->code_curbit >= context->code_lastbit) {
488 g_set_error (context->error,
490 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
491 _("GIF file was missing some data (perhaps it was truncated somehow?)"));
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,
499 GDK_PIXBUF_ERROR_FAILED,
500 _("Internal error in the GIF loader (%s)"),
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];
509 retval = get_data_block (context, &context->block_buf[2], NULL);
514 if (context->block_count == 0)
515 context->code_done = TRUE;
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;
521 context->state = context->old_state;
526 get_code (GifContext *context,
531 if ((context->code_curbit + code_size) >= context->code_lastbit){
532 gif_set_lzw_fill_buffer (context);
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;
540 context->code_curbit += code_size;
547 set_gif_lzw_clear_code (GifContext *context)
549 context->state = GIF_LZW_CLEAR_CODE;
550 context->lzw_code_pending = -1;
554 gif_lzw_clear_code (GifContext *context)
558 code = get_code (context, context->lzw_code_size);
562 context->lzw_firstcode = context->lzw_oldcode = code;
563 context->lzw_code_pending = code;
564 context->state = GIF_GET_LZW;
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, \
573 GDK_PIXBUF_ERROR_CORRUPT_IMAGE, \
574 _("Stack overflow")); \
580 lzw_read_byte (GifContext *context)
587 if (context->lzw_code_pending != -1) {
588 retval = context->lzw_code_pending;
589 context->lzw_code_pending = -1;
593 if (context->lzw_fresh) {
594 context->lzw_fresh = FALSE;
596 retval = get_code (context, context->lzw_code_size);
601 context->lzw_firstcode = context->lzw_oldcode = retval;
602 } while (context->lzw_firstcode == context->lzw_clear_code);
603 return context->lzw_firstcode;
606 if (context->lzw_sp > context->lzw_stack) {
607 my_retval = *--(context->lzw_sp);
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;
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;
624 set_gif_lzw_clear_code (context);
626 } else if (code == context->lzw_end_code) {
628 unsigned char buf[260];
630 /* FIXME - we should handle this case */
631 g_set_error (context->error,
633 GDK_PIXBUF_ERROR_FAILED,
634 _("GIF image loader can't understand this image."));
641 while ((count = GetDataBlock (context, buf)) > 0)
645 /*g_print (_("GIF: missing EOD in data stream (common occurence)"));*/
652 if (code >= context->lzw_max_code) {
654 *(context->lzw_sp)++ = context->lzw_firstcode;
655 code = context->lzw_oldcode;
658 while (code >= context->lzw_clear_code) {
659 if (code >= (1 << MAX_LZW_BITS)) {
660 g_set_error (context->error,
662 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
663 _("Bad code encountered"));
667 *(context->lzw_sp)++ = context->lzw_table[1][code];
669 if (code == context->lzw_table[0][code]) {
670 g_set_error (context->error,
672 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
673 _("Circular table entry in GIF file"));
676 code = context->lzw_table[0][code];
680 *(context->lzw_sp)++ = context->lzw_firstcode = context->lzw_table[1][code];
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;
693 context->lzw_oldcode = incode;
695 if (context->lzw_sp > context->lzw_stack) {
696 my_retval = *--(context->lzw_sp);
704 gif_set_get_lzw (GifContext *context)
706 context->state = GIF_GET_LZW;
707 context->draw_xpos = 0;
708 context->draw_ypos = 0;
709 context->draw_pass = 0;
713 gif_fill_in_pixels (GifContext *context, guchar *dest, gint offset, guchar v)
715 guchar *pixel = NULL;
716 guchar (*cmap)[MAXCOLORMAPSIZE];
718 if (context->frame_cmap_active)
719 cmap = context->frame_color_map;
721 cmap = context->global_color_map;
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);
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];
738 /* only called if progressive and interlaced */
740 gif_fill_in_lines (GifContext *context, guchar *dest, guchar v)
742 switch (context->draw_pass) {
744 if (context->draw_ypos > 4) {
745 gif_fill_in_pixels (context, dest, -4, v);
746 gif_fill_in_pixels (context, dest, -3, v);
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);
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*/
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. */
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);
773 set_need_recomposite (gpointer data, gpointer user_data)
775 GdkPixbufFrame *frame = (GdkPixbufFrame *)data;
776 frame->need_recomposite = TRUE;
781 gif_get_lzw (GifContext *context)
784 gint lower_bound, upper_bound; /* bounds for emitting the area_updated signal */
786 gint first_pass; /* bounds for emitting the area_updated signal */
789 if (context->frame == NULL) {
790 context->frame = g_new (GdkPixbufFrame, 1);
792 context->frame->composited = NULL;
793 context->frame->revert = NULL;
795 context->frame->pixbuf =
796 gdk_pixbuf_new (GDK_COLORSPACE_RGB,
800 context->frame_height);
801 if (!context->frame->pixbuf) {
802 g_free (context->frame);
803 g_set_error (context->error,
805 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
806 _("Not enough memory to load GIF file"));
810 context->frame->x_offset = context->x_offset;
811 context->frame->y_offset = context->y_offset;
812 context->frame->need_recomposite = TRUE;
814 /* GIF delay is in hundredths, we want thousandths */
815 context->frame->delay_time = context->gif89.delay_time * 10;
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.
822 if (context->frame->delay_time < 20)
823 context->frame->delay_time = 20; /* 20 = "fast" */
825 context->frame->elapsed = context->animation->total_time;
826 context->animation->total_time += context->frame->delay_time;
828 switch (context->gif89.disposal) {
831 context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
834 context->frame->action = GDK_PIXBUF_FRAME_DISPOSE;
837 context->frame->action = GDK_PIXBUF_FRAME_REVERT;
840 context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
844 context->frame->bg_transparent = (context->gif89.transparent == context->background_index);
847 /* Update animation size */
850 context->animation->n_frames ++;
851 context->animation->frames = g_list_append (context->animation->frames, context->frame);
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);
860 if (w > context->animation->width)
861 context->animation->width = w;
862 if (h > context->animation->height)
863 context->animation->height = h;
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),
873 /* Otherwise init frame with last frame */
875 GdkPixbufFrame *prev_frame;
877 link = g_list_find (context->animation->frames, context->frame);
879 prev_frame = link->prev->data;
881 gdk_pixbuf_gif_anim_frame_composite (context->animation, prev_frame);
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,
893 dest = gdk_pixbuf_get_pixels (context->frame->pixbuf);
896 lower_bound = upper_bound = context->draw_ypos;
897 first_pass = context->draw_pass;
900 guchar (*cmap)[MAXCOLORMAPSIZE];
902 if (context->frame_cmap_active)
903 cmap = context->frame_color_map;
905 cmap = context->global_color_map;
907 v = lzw_read_byte (context);
913 g_assert (gdk_pixbuf_get_has_alpha (context->frame->pixbuf));
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);
921 if (context->prepare_func && context->frame_interlace)
922 gif_fill_in_lines (context, dest, v);
924 context->draw_xpos++;
926 if (context->draw_xpos == context->frame_len) {
927 context->draw_xpos = 0;
928 if (context->frame_interlace) {
929 switch (context->draw_pass) {
932 context->draw_ypos += 8;
935 context->draw_ypos += 4;
938 context->draw_ypos += 2;
942 if (context->draw_ypos >= context->frame_height) {
943 context->draw_pass++;
944 switch (context->draw_pass) {
946 context->draw_ypos = 4;
949 context->draw_ypos = 2;
952 context->draw_ypos = 1;
959 context->draw_ypos++;
961 if (context->draw_pass != first_pass) {
962 if (context->draw_ypos > lower_bound) {
964 upper_bound = context->frame_height;
969 upper_bound = context->draw_ypos;
971 if (context->draw_ypos >= context->frame_height)
977 context->state = GIF_GET_NEXT_STEP;
984 context->frame->need_recomposite = TRUE;
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,
991 gdk_pixbuf_get_width (context->frame->pixbuf),
992 upper_bound - lower_bound,
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);
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),
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);
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)
1026 context->frame = NULL;
1027 context->frame_cmap_active = FALSE;
1034 gif_set_prepare_lzw (GifContext *context)
1036 context->state = GIF_PREPARE_LZW;
1037 context->lzw_code_pending = -1;
1040 gif_prepare_lzw (GifContext *context)
1044 if (!gif_read (context, &(context->lzw_set_code_size), 1)) {
1045 /*g_message (_("GIF: EOF / read error on image data\n"));*/
1049 if (context->lzw_set_code_size > MAX_LZW_BITS) {
1050 g_set_error (context->error,
1052 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1053 _("GIF image is corrupt (incorrect LZW compression)"));
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;
1068 g_assert (context->lzw_clear_code <=
1069 G_N_ELEMENTS (context->lzw_table[0]));
1071 for (i = 0; i < context->lzw_clear_code; ++i) {
1072 context->lzw_table[0][i] = 0;
1073 context->lzw_table[1][i] = i;
1075 for (; i < (1 << MAX_LZW_BITS); ++i)
1076 context->lzw_table[0][i] = context->lzw_table[1][0] = 0;
1078 context->lzw_sp = context->lzw_stack;
1079 gif_set_get_lzw (context);
1084 /* needs 13 bytes to proceed. */
1086 gif_init (GifContext *context)
1088 unsigned char buf[16];
1091 if (!gif_read (context, buf, 6)) {
1092 /* Unable to read magic number,
1093 * gif_read() should have set error
1098 if (strncmp ((char *) buf, "GIF", 3) != 0) {
1099 /* Not a GIF file */
1100 g_set_error (context->error,
1102 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1103 _("File does not appear to be a GIF file"));
1107 strncpy (version, (char *) buf + 3, 3);
1110 if ((strcmp (version, "87a") != 0) && (strcmp (version, "89a") != 0)) {
1111 /* bad version number, not '87a' or '89a' */
1112 g_set_error (context->error,
1114 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1115 _("Version %s of the GIF file format is not supported"),
1120 /* read the screen descriptor */
1121 if (!gif_read (context, buf, 7)) {
1122 /* Failed to read screen descriptor, error set */
1126 context->width = LM_to_uint (buf[0], buf[1]);
1127 context->height = LM_to_uint (buf[2], buf[3]);
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
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];
1140 /* Use background of transparent black as default, though if
1141 * one isn't set explicitly no one should ever use it.
1143 context->animation->bg_red = 0;
1144 context->animation->bg_green = 0;
1145 context->animation->bg_blue = 0;
1147 if (context->has_global_cmap) {
1148 gif_set_get_colormap (context);
1150 context->state = GIF_GET_NEXT_STEP;
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);
1162 gif_set_get_frame_info (GifContext *context)
1164 context->state = GIF_GET_FRAME_INFO;
1168 gif_get_frame_info (GifContext *context)
1170 unsigned char buf[9];
1172 if (!gif_read (context, buf, 9)) {
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]);
1182 if ((context->frame_height == 0) || (context->frame_len == 0)) {
1183 context->state = GIF_DONE;
1185 g_set_error (context->error,
1187 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1188 _("GIF image contained a frame with height or width 0."));
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;
1198 g_set_error (context->error,
1200 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1201 _("GIF image contained a frame appearing outside the image bounds."));
1206 if (context->animation->frames == NULL &&
1207 context->gif89.disposal == 3) {
1208 /* First frame can't have "revert to previous" as its
1212 context->state = GIF_DONE;
1214 g_set_error (context->error,
1216 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1217 _("First frame of GIF image had 'revert to previous' as its disposal mode."));
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);
1228 context->frame_interlace = BitSet (buf[8], INTERLACE);
1229 if (BitSet (buf[8], LOCALCOLORMAP)) {
1231 #ifdef DUMP_IMAGE_DETAILS
1232 g_print (">has local colormap\n");
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);
1246 if (!context->has_global_cmap) {
1247 context->state = GIF_DONE;
1249 g_set_error (context->error,
1251 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1252 _("GIF image has no global colormap, and a frame inside it has no local colormap."));
1257 gif_set_prepare_lzw (context);
1263 gif_get_next_step (GifContext *context)
1267 if (!gif_read (context, &c, 1)) {
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;
1279 /* Check the extention */
1280 gif_set_get_extension (context);
1284 /* look for frame */
1286 /* Not a valid start character */
1289 /* load the frame */
1290 gif_set_get_frame_info (context);
1296 #define LOG(x) /* g_print ("%d: %s\n", __LINE__, x); */
1299 gif_main_loop (GifContext *context)
1304 switch (context->state) {
1307 retval = gif_init (context);
1310 case GIF_GET_COLORMAP:
1311 LOG("get_colormap\n");
1312 retval = gif_get_colormap (context);
1314 context->state = GIF_GET_NEXT_STEP;
1317 case GIF_GET_NEXT_STEP:
1319 retval = gif_get_next_step (context);
1322 case GIF_GET_FRAME_INFO:
1323 LOG("frame_info\n");
1324 retval = gif_get_frame_info (context);
1327 case GIF_GET_EXTENSION:
1328 LOG("get_extension\n");
1329 retval = gif_get_extension (context);
1331 context->state = GIF_GET_NEXT_STEP;
1334 case GIF_GET_COLORMAP2:
1335 LOG("get_colormap2\n");
1336 retval = gif_get_colormap2 (context);
1338 gif_set_prepare_lzw (context);
1341 case GIF_PREPARE_LZW:
1342 LOG("prepare_lzw\n");
1343 retval = gif_prepare_lzw (context);
1346 case GIF_LZW_FILL_BUFFER:
1347 LOG("fill_buffer\n");
1348 retval = gif_lzw_fill_buffer (context);
1351 case GIF_LZW_CLEAR_CODE:
1352 LOG("clear_code\n");
1353 retval = gif_lzw_clear_code (context);
1358 retval = gif_get_lzw (context);
1367 } while ((retval == 0) || (retval == -3));
1375 GifContext *context;
1377 context = g_try_malloc (sizeof (GifContext));
1378 if (context == NULL)
1381 memset (context, 0, sizeof (GifContext));
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;
1401 /* Shared library entry point */
1403 gdk_pixbuf__gif_image_load (FILE *file, GError **error)
1405 GifContext *context;
1408 g_return_val_if_fail (file != NULL, NULL);
1410 context = new_context ();
1412 if (context == NULL) {
1415 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
1416 _("Not enough memory to load GIF file"));
1420 context->file = file;
1421 context->error = error;
1423 if (gif_main_loop (context) == -1 || context->animation->frames == NULL) {
1424 if (context->error && *(context->error) == NULL)
1425 g_set_error (context->error,
1427 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1428 _("GIF file was missing some data (perhaps it was truncated somehow?)"));
1431 pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
1434 g_object_ref (pixbuf);
1436 g_object_unref (context->animation);
1438 g_free (context->buf);
1445 gdk_pixbuf__gif_image_begin_load (GdkPixbufModuleSizeFunc size_func,
1446 GdkPixbufModulePreparedFunc prepare_func,
1447 GdkPixbufModuleUpdatedFunc update_func,
1451 GifContext *context;
1456 context = new_context ();
1458 if (context == NULL) {
1461 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
1462 _("Not enough memory to load GIF file"));
1466 context->error = error;
1467 context->prepare_func = prepare_func;
1468 context->update_func = update_func;
1469 context->user_data = user_data;
1471 return (gpointer) context;
1475 gdk_pixbuf__gif_image_stop_load (gpointer data, GError **error)
1477 GifContext *context = (GifContext *) data;
1478 gboolean retval = TRUE;
1480 if (context->state != GIF_DONE) {
1483 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1484 _("GIF image was truncated or incomplete."));
1489 g_object_unref (context->animation);
1491 g_free (context->buf);
1498 gdk_pixbuf__gif_image_load_increment (gpointer data,
1499 const guchar *buf, guint size,
1503 GifContext *context = (GifContext *) data;
1505 context->error = error;
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 */
1513 context->size = size;
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;
1522 } else if (size == context->amount_needed) {
1523 memcpy (context->buf + context->size, buf, size);
1524 context->size += size;
1526 context->buf = g_realloc (context->buf, context->size + size);
1527 memcpy (context->buf + context->size, buf, size);
1528 context->size += size;
1532 retval = gif_main_loop (context);
1535 if (context->buf == buf)
1536 context->buf = NULL;
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);
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));
1552 context->size = context->size - context->ptr;
1555 /* we are prolly all done */
1556 if (context->buf == buf)
1557 context->buf = NULL;
1562 static GdkPixbufAnimation *
1563 gdk_pixbuf__gif_image_load_animation (FILE *file,
1566 GifContext *context;
1567 GdkPixbufAnimation *animation;
1569 g_return_val_if_fail (file != NULL, NULL);
1571 context = new_context ();
1573 if (context == NULL) {
1576 GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
1577 _("Not enough memory to load GIF file"));
1581 context->error = error;
1582 context->file = file;
1584 if (gif_main_loop (context) == -1 || context->animation->frames == NULL) {
1585 if (context->error && *(context->error) == NULL)
1586 g_set_error (context->error,
1588 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
1589 _("GIF file was missing some data (perhaps it was truncated somehow?)"));
1591 g_object_unref (context->animation);
1592 context->animation = NULL;
1595 if (context->animation)
1596 animation = GDK_PIXBUF_ANIMATION (context->animation);
1600 if (context->error && *(context->error))
1601 g_print ("%s\n", (*(context->error))->message);
1603 g_free (context->buf);
1609 MODULE_ENTRY (gif, fill_vtable) (GdkPixbufModule *module)
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;
1619 MODULE_ENTRY (gif, fill_info) (GdkPixbufFormat *info)
1621 static GdkPixbufModulePattern signature[] = {
1622 { "GIF8", NULL, 100 },
1625 static gchar * mime_types[] = {
1629 static gchar * extensions[] = {
1635 info->signature = signature;
1636 info->description = N_("The GIF image format");
1637 info->mime_types = mime_types;
1638 info->extensions = extensions;