1 /* GdkPixbuf library - GIF image loader
3 * Copyright (C) 1999 Mark Crichton
4 * Copyright (C) 1999 The Free Software Foundation
6 * Authors: Jonathan Blandford <jrb@redhat.com>
7 * Adapted from the gimp gif filter written by Adam Moss <adam@gimp.org>
8 * Gimp work based on earlier work.
9 * Permission to relicense under the LGPL obtained.
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the
23 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 * Boston, MA 02111-1307, USA.
27 /* This loader is very hairy code.
29 * The main loop was not designed for incremental loading, so when it was hacked
30 * in it got a bit messy. Basicly, every function is written to expect a failed
31 * read_gif, and lets you call it again assuming that the bytes are there.
33 * A note on Animations:
34 * Currently, it doesn't correctly read the different colormap per frame. This
35 * needs implementing sometime.
38 * Unless otherwise specified, these are the return vals for most functions:
41 * -1 -> more bytes needed.
42 * -2 -> failure; abort the load
43 * -3 -> control needs to be passed back to the main loop
44 * \_ (most of the time returning 0 will get this, but not always)
46 * >1 -> for functions that get a guchar, the char will be returned.
52 * If you have any images that crash this code, please, please let me know and
62 #include "gdk-pixbuf-private.h"
63 #include "gdk-pixbuf-io.h"
67 #define MAXCOLORMAPSIZE 256
68 #define MAX_LZW_BITS 12
70 #define INTERLACE 0x40
71 #define LOCALCOLORMAP 0x80
72 #define BitSet(byte, bit) (((byte) & (bit)) == (bit))
73 #define LM_to_uint(a,b) (((b)<<8)|(a))
77 typedef unsigned char CMap[3][MAXCOLORMAPSIZE];
79 /* Possible states we can be in. */
95 typedef struct _Gif89 Gif89;
104 typedef struct _GifContext GifContext;
107 int state; /* really only relevant for progressive loading */
111 CMap frame_color_map;
112 unsigned int bit_pixel;
113 unsigned int color_resolution;
114 unsigned int background;
115 unsigned int aspect_ratio;
117 GdkPixbufAnimation *animation;
118 GdkPixbufFrame *frame;
121 /* stuff per frame. As we only support the first one, not so
122 * relevant. But still needed */
129 /* Static read only */
132 /* progressive read, only. */
133 ModulePreparedNotifyFunc prepare_func;
134 ModuleUpdatedNotifyFunc update_func;
135 ModuleFrameDoneNotifyFunc frame_done_func;
136 ModuleAnimationDoneNotifyFunc anim_done_func;
143 /* colormap context */
147 /* extension context */
148 guchar extension_label;
149 guchar extension_flag;
151 /* get block context */
153 guchar block_buf[280];
156 int old_state; /* used by lzw_fill buffer */
157 /* get_code context */
162 int lzw_code_pending;
167 guchar lzw_set_code_size;
169 gint lzw_max_code_size;
176 gint lzw_table[2][(1 << MAX_LZW_BITS)];
177 gint lzw_stack[(1 << (MAX_LZW_BITS)) * 2 + 1];
179 /* painting context */
188 static int GetDataBlock (GifContext *, unsigned char *);
193 static int count = 0;
196 /* Returns TRUE if read is OK,
197 * FALSE if more memory is needed. */
199 gif_read (GifContext *context, guchar *buffer, size_t len)
208 g_print ("Fsize :%d\tcount :%d\t", len, count);
210 retval = (fread(buffer, len, 1, context->file) != 0);
213 for (i = 0; i < len; i++)
214 g_print ("%d ", buffer[i]);
222 /* g_print ("\tlooking for %d bytes. size == %d, ptr == %d\n", len, context->size, context->ptr); */
224 if ((context->size - context->ptr) >= len) {
228 memcpy (buffer, context->buf + context->ptr, len);
230 context->amount_needed = 0;
232 g_print ("Psize :%d\tcount :%d\t", len, count);
234 for (i = 0; i < len; i++)
235 g_print ("%d ", buffer[i]);
241 context->amount_needed = len - (context->size - context->ptr);
246 /* Changes the stage to be GIF_GET_COLORMAP */
248 gif_set_get_colormap (GifContext *context)
250 context->colormap_flag = TRUE;
251 context->colormap_index = 0;
252 context->state = GIF_GET_COLORMAP;
256 gif_set_get_colormap2 (GifContext *context)
258 context->colormap_flag = TRUE;
259 context->colormap_index = 0;
260 context->state = GIF_GET_COLORMAP2;
264 gif_get_colormap (GifContext *context)
266 unsigned char rgb[3];
268 while (context->colormap_index < context->bit_pixel) {
269 if (!gif_read (context, rgb, sizeof (rgb))) {
270 /*g_message (_("GIF: bad colormap\n"));*/
274 context->color_map[0][context->colormap_index] = rgb[0];
275 context->color_map[1][context->colormap_index] = rgb[1];
276 context->color_map[2][context->colormap_index] = rgb[2];
278 context->colormap_flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]);
279 context->colormap_index ++;
286 * in order for this function to work, we need to perform some black magic.
287 * We want to return -1 to let the calling function know, as before, that it needs
288 * more bytes. If we return 0, we were able to successfully read all block->count bytes.
289 * Problem is, we don't want to reread block_count every time, so we check to see if
290 * context->block_count is 0 before we read in the function.
292 * As a result, context->block_count MUST be 0 the first time the get_data_block is called
293 * within a context, and cannot be 0 the second time it's called.
297 get_data_block (GifContext *context,
302 if (context->block_count == 0) {
303 if (!gif_read (context, &context->block_count, 1)) {
308 if (context->block_count == 0)
314 if (!gif_read (context, buf, context->block_count)) {
322 gif_set_get_extension (GifContext *context)
324 context->state = GIF_GET_EXTENTION;
325 context->extension_flag = TRUE;
326 context->extension_label = 0;
327 context->block_count = 0;
328 context->block_ptr = 0;
332 gif_get_extension (GifContext *context)
335 gint empty_block = FALSE;
337 if (context->extension_flag) {
338 if (context->extension_label == 0) {
339 /* I guess bad things can happen if we have an extension of 0 )-: */
340 /* I should look into this sometime */
341 if (!gif_read (context, & context->extension_label , 1)) {
346 switch (context->extension_label) {
347 case 0xf9: /* Graphic Control Extension */
348 retval = get_data_block (context, (unsigned char *) context->block_buf, NULL);
351 if (context->pixbuf == NULL) {
352 /* I only want to set the transparency if I haven't
353 * created the pixbuf yet. */
354 context->gif89.disposal = (context->block_buf[0] >> 2) & 0x7;
355 context->gif89.input_flag = (context->block_buf[0] >> 1) & 0x1;
356 context->gif89.delay_time = LM_to_uint (context->block_buf[1], context->block_buf[2]);
358 if ((context->block_buf[0] & 0x1) != 0) {
359 context->gif89.transparent = context->block_buf[3];
361 context->gif89.transparent = -1;
365 /* Now we've successfully loaded this one, we continue on our way */
366 context->block_count = 0;
367 context->extension_flag = FALSE;
369 /* Unhandled extension */
373 /* read all blocks, until I get an empty block, in case there was an extension I didn't know about. */
375 retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
378 context->block_count = 0;
379 } while (!empty_block);
384 static int ZeroDataBlock = FALSE;
387 GetDataBlock (GifContext *context,
390 /* unsigned char count; */
392 if (!gif_read (context, &context->block_count, 1)) {
393 /*g_message (_("GIF: error in getting DataBlock size\n"));*/
397 ZeroDataBlock = context->block_count == 0;
399 if ((context->block_count != 0) && (!gif_read (context, buf, context->block_count))) {
400 /*g_message (_("GIF: error in reading DataBlock\n"));*/
404 return context->block_count;
409 gif_set_lzw_fill_buffer (GifContext *context)
411 context->block_count = 0;
412 context->old_state = context->state;
413 context->state = GIF_LZW_FILL_BUFFER;
417 gif_lzw_fill_buffer (GifContext *context)
421 if (context->code_done) {
422 if (context->code_curbit >= context->code_lastbit) {
423 g_set_error (context->error,
425 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
426 _("GIF file was missing some data (perhaps it was truncated somehow?)"));
430 /* Is this supposed to be an error or what? */
431 /* g_message ("trying to read more data after we've done stuff\n"); */
432 g_set_error (context->error,
434 GDK_PIXBUF_ERROR_FAILED,
435 _("Internal error in the GIF loader (%s)"),
441 context->block_buf[0] = context->block_buf[context->code_last_byte - 2];
442 context->block_buf[1] = context->block_buf[context->code_last_byte - 1];
444 retval = get_data_block (context, &context->block_buf[2], NULL);
449 if (context->block_count == 0)
450 context->code_done = TRUE;
452 context->code_last_byte = 2 + context->block_count;
453 context->code_curbit = (context->code_curbit - context->code_lastbit) + 16;
454 context->code_lastbit = (2 + context->block_count) * 8;
456 context->state = context->old_state;
461 get_code (GifContext *context,
466 if ((context->code_curbit + code_size) >= context->code_lastbit){
467 gif_set_lzw_fill_buffer (context);
472 for (i = context->code_curbit, j = 0; j < code_size; ++i, ++j)
473 ret |= ((context->block_buf[i / 8] & (1 << (i % 8))) != 0) << j;
475 context->code_curbit += code_size;
482 set_gif_lzw_clear_code (GifContext *context)
484 context->state = GIF_LZW_CLEAR_CODE;
485 context->lzw_code_pending = -1;
489 gif_lzw_clear_code (GifContext *context)
493 code = get_code (context, context->lzw_code_size);
497 context->lzw_firstcode = context->lzw_oldcode = code;
498 context->lzw_code_pending = code;
499 context->state = GIF_GET_LZW;
504 lzw_read_byte (GifContext *context)
511 if (context->lzw_code_pending != -1) {
512 retval = context->lzw_code_pending;
513 context->lzw_code_pending = -1;
517 if (context->lzw_fresh) {
518 context->lzw_fresh = FALSE;
520 retval = get_code (context, context->lzw_code_size);
525 context->lzw_firstcode = context->lzw_oldcode = retval;
526 } while (context->lzw_firstcode == context->lzw_clear_code);
527 return context->lzw_firstcode;
530 if (context->lzw_sp > context->lzw_stack) {
531 my_retval = *--(context->lzw_sp);
535 while ((code = get_code (context, context->lzw_code_size)) >= 0) {
536 if (code == context->lzw_clear_code) {
537 for (i = 0; i < context->lzw_clear_code; ++i) {
538 context->lzw_table[0][i] = 0;
539 context->lzw_table[1][i] = i;
541 for (; i < (1 << MAX_LZW_BITS); ++i)
542 context->lzw_table[0][i] = context->lzw_table[1][i] = 0;
543 context->lzw_code_size = context->lzw_set_code_size + 1;
544 context->lzw_max_code_size = 2 * context->lzw_clear_code;
545 context->lzw_max_code = context->lzw_clear_code + 2;
546 context->lzw_sp = context->lzw_stack;
548 set_gif_lzw_clear_code (context);
550 } else if (code == context->lzw_end_code) {
552 unsigned char buf[260];
554 /*g_error (" DID WE EVER EVER GET HERE\n");*/
555 g_warning ("Unhandled Case. If you have an image that causes this, let me <jrb@redhat.com> know.\n");
561 while ((count = GetDataBlock (context, buf)) > 0)
565 /*g_print (_("GIF: missing EOD in data stream (common occurence)"));*/
572 if (code >= context->lzw_max_code) {
573 *(context->lzw_sp)++ = context->lzw_firstcode;
574 code = context->lzw_oldcode;
577 while (code >= context->lzw_clear_code) {
578 *(context->lzw_sp)++ = context->lzw_table[1][code];
580 if (code == context->lzw_table[0][code]) {
581 g_set_error (context->error,
583 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
584 _("Circular table entry in GIF file"));
587 code = context->lzw_table[0][code];
590 *(context->lzw_sp)++ = context->lzw_firstcode = context->lzw_table[1][code];
592 if ((code = context->lzw_max_code) < (1 << MAX_LZW_BITS)) {
593 context->lzw_table[0][code] = context->lzw_oldcode;
594 context->lzw_table[1][code] = context->lzw_firstcode;
595 ++context->lzw_max_code;
596 if ((context->lzw_max_code >= context->lzw_max_code_size) &&
597 (context->lzw_max_code_size < (1 << MAX_LZW_BITS))) {
598 context->lzw_max_code_size *= 2;
599 ++context->lzw_code_size;
603 context->lzw_oldcode = incode;
605 if (context->lzw_sp > context->lzw_stack) {
606 my_retval = *--(context->lzw_sp);
614 gif_set_get_lzw (GifContext *context)
616 context->state = GIF_GET_LZW;
617 context->draw_xpos = 0;
618 context->draw_ypos = 0;
619 context->draw_pass = 0;
623 gif_fill_in_pixels (GifContext *context, guchar *dest, gint offset, guchar v)
625 guchar *pixel = NULL;
627 if (context->gif89.transparent != -1) {
628 pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 4;
629 *pixel = context->color_map [0][(guchar) v];
630 *(pixel+1) = context->color_map [1][(guchar) v];
631 *(pixel+2) = context->color_map [2][(guchar) v];
632 *(pixel+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 65535);
634 pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 3;
635 *pixel = context->color_map [0][(guchar) v];
636 *(pixel+1) = context->color_map [1][(guchar) v];
637 *(pixel+2) = context->color_map [2][(guchar) v];
642 /* only called if progressive and interlaced */
644 gif_fill_in_lines (GifContext *context, guchar *dest, guchar v)
646 switch (context->draw_pass) {
648 if (context->draw_ypos > 4) {
649 gif_fill_in_pixels (context, dest, -4, v);
650 gif_fill_in_pixels (context, dest, -3, v);
652 if (context->draw_ypos < (context->frame_height - 4)) {
653 gif_fill_in_pixels (context, dest, 3, v);
654 gif_fill_in_pixels (context, dest, 4, v);
656 /* we don't need a break here. We draw the outer pixels first, then the
657 * inner ones, then the innermost ones. case 0 needs to draw all 3 bands.
658 * case 1, just the last two, and case 2 just draws the last one*/
660 if (context->draw_ypos > 2)
661 gif_fill_in_pixels (context, dest, -2, v);
662 if (context->draw_ypos < (context->frame_height - 2))
663 gif_fill_in_pixels (context, dest, 2, v);
664 /* no break as above. */
666 if (context->draw_ypos > 1)
667 gif_fill_in_pixels (context, dest, -1, v);
668 if (context->draw_ypos < (context->frame_height - 1))
669 gif_fill_in_pixels (context, dest, 1, v);
677 gif_get_lzw (GifContext *context)
680 gint lower_bound, upper_bound; /* bounds for emitting the area_updated signal */
682 gint first_pass; /* bounds for emitting the area_updated signal */
685 if (context->pixbuf == NULL) {
686 context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
687 context->gif89.transparent != -1,
690 context->frame_height);
692 if (context->prepare_func)
693 (* context->prepare_func) (context->pixbuf, context->user_data);
694 if (context->animation || context->frame_done_func || context->anim_done_func) {
695 context->frame = g_new (GdkPixbufFrame, 1);
696 context->frame->x_offset = context->x_offset;
697 context->frame->y_offset = context->y_offset;;
698 context->frame->delay_time = context->gif89.delay_time;
699 switch (context->gif89.disposal) {
702 context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
705 context->frame->action = GDK_PIXBUF_FRAME_DISPOSE;
708 context->frame->action = GDK_PIXBUF_FRAME_REVERT;
711 context->frame->action = GDK_PIXBUF_FRAME_RETAIN;
714 context->frame->pixbuf = context->pixbuf;
715 if (context->animation) {
717 context->animation->n_frames ++;
718 context->animation->frames = g_list_append (context->animation->frames, context->frame);
719 w = gdk_pixbuf_get_width (context->pixbuf);
720 h = gdk_pixbuf_get_height (context->pixbuf);
721 if (w > context->animation->width)
722 context->animation->width = w;
723 if (h > context->animation->height)
724 context->animation->height = h;
728 dest = gdk_pixbuf_get_pixels (context->pixbuf);
731 lower_bound = upper_bound = context->draw_ypos;
732 first_pass = context->draw_pass;
735 v = lzw_read_byte (context);
741 if (context->gif89.transparent != -1) {
742 temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 4;
743 *temp = context->color_map [0][(guchar) v];
744 *(temp+1) = context->color_map [1][(guchar) v];
745 *(temp+2) = context->color_map [2][(guchar) v];
746 *(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : -1);
748 temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 3;
749 *temp = context->color_map [0][(guchar) v];
750 *(temp+1) = context->color_map [1][(guchar) v];
751 *(temp+2) = context->color_map [2][(guchar) v];
754 if (context->prepare_func && context->frame_interlace)
755 gif_fill_in_lines (context, dest, v);
757 context->draw_xpos++;
759 if (context->draw_xpos == context->frame_len) {
760 context->draw_xpos = 0;
761 if (context->frame_interlace) {
762 switch (context->draw_pass) {
765 context->draw_ypos += 8;
768 context->draw_ypos += 4;
771 context->draw_ypos += 2;
775 if (context->draw_ypos >= context->frame_height) {
776 context->draw_pass++;
777 switch (context->draw_pass) {
779 context->draw_ypos = 4;
782 context->draw_ypos = 2;
785 context->draw_ypos = 1;
792 context->draw_ypos++;
794 if (context->draw_pass != first_pass) {
795 if (context->draw_ypos > lower_bound) {
797 upper_bound = context->frame_height;
802 upper_bound = context->draw_ypos;
804 if (context->draw_ypos >= context->frame_height)
808 /* we got enough data. there may be more (ie, newer layers) but we can quit now */
809 if (context->animation || context->frame_done_func || context->anim_done_func) {
810 context->state = GIF_GET_NEXT_STEP;
812 context->state = GIF_DONE;
815 if (bound_flag && context->update_func) {
816 if (lower_bound <= upper_bound && first_pass == context->draw_pass) {
817 (* context->update_func)
820 gdk_pixbuf_get_width (context->pixbuf),
821 upper_bound - lower_bound,
824 if (lower_bound <= upper_bound) {
825 (* context->update_func)
828 gdk_pixbuf_get_width (context->pixbuf),
829 gdk_pixbuf_get_height (context->pixbuf),
832 (* context->update_func)
835 gdk_pixbuf_get_width (context->pixbuf),
838 (* context->update_func)
841 gdk_pixbuf_get_width (context->pixbuf),
842 gdk_pixbuf_get_height (context->pixbuf),
848 if ((context->animation || context->frame_done_func || context->anim_done_func)
849 && context->state == GIF_GET_NEXT_STEP) {
850 if (context->frame_done_func)
851 (* context->frame_done_func) (context->frame,
853 if (context->frame_done_func)
854 gdk_pixbuf_unref (context->pixbuf);
855 context->pixbuf = NULL;
856 context->frame = NULL;
863 gif_set_prepare_lzw (GifContext *context)
865 context->state = GIF_PREPARE_LZW;
866 context->lzw_code_pending = -1;
869 gif_prepare_lzw (GifContext *context)
873 if (!gif_read (context, &(context->lzw_set_code_size), 1)) {
874 /*g_message (_("GIF: EOF / read error on image data\n"));*/
878 context->lzw_code_size = context->lzw_set_code_size + 1;
879 context->lzw_clear_code = 1 << context->lzw_set_code_size;
880 context->lzw_end_code = context->lzw_clear_code + 1;
881 context->lzw_max_code_size = 2 * context->lzw_clear_code;
882 context->lzw_max_code = context->lzw_clear_code + 2;
883 context->lzw_fresh = TRUE;
884 context->code_curbit = 0;
885 context->code_lastbit = 0;
886 context->code_last_byte = 0;
887 context->code_done = FALSE;
889 for (i = 0; i < context->lzw_clear_code; ++i) {
890 context->lzw_table[0][i] = 0;
891 context->lzw_table[1][i] = i;
893 for (; i < (1 << MAX_LZW_BITS); ++i)
894 context->lzw_table[0][i] = context->lzw_table[1][0] = 0;
896 context->lzw_sp = context->lzw_stack;
897 gif_set_get_lzw (context);
902 /* needs 13 bytes to proceed. */
904 gif_init (GifContext *context)
906 unsigned char buf[16];
909 if (!gif_read (context, buf, 6)) {
910 /* Unable to read magic number,
911 * gif_read() should have set error
916 if (strncmp ((char *) buf, "GIF", 3) != 0) {
918 g_set_error (context->error,
920 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
921 _("File does not appear to be a GIF file"));
925 strncpy (version, (char *) buf + 3, 3);
928 if ((strcmp (version, "87a") != 0) && (strcmp (version, "89a") != 0)) {
929 /* bad version number, not '87a' or '89a' */
930 g_set_error (context->error,
932 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
933 _("Version %s of the GIF file format is not supported"),
938 /* read the screen descriptor */
939 if (!gif_read (context, buf, 7)) {
940 /* Failed to read screen descriptor, error set */
944 context->width = LM_to_uint (buf[0], buf[1]);
945 context->height = LM_to_uint (buf[2], buf[3]);
946 context->bit_pixel = 2 << (buf[4] & 0x07);
947 context->color_resolution = (((buf[4] & 0x70) >> 3) + 1);
948 context->background = buf[5];
949 context->aspect_ratio = buf[6];
951 if (BitSet (buf[4], LOCALCOLORMAP)) {
952 gif_set_get_colormap (context);
954 context->state = GIF_GET_NEXT_STEP;
960 gif_set_get_frame_info (GifContext *context)
962 context->state = GIF_GET_FRAME_INFO;
966 gif_get_frame_info (GifContext *context)
968 unsigned char buf[9];
969 if (!gif_read (context, buf, 9)) {
972 /* Okay, we got all the info we need. Lets record it */
973 context->frame_len = LM_to_uint (buf[4], buf[5]);
974 context->frame_height = LM_to_uint (buf[6], buf[7]);
975 context->x_offset = LM_to_uint (buf[0], buf[1]);
976 context->y_offset = LM_to_uint (buf[2], buf[3]);
978 if (context->frame_height > context->height) {
979 /* we don't want to resize things. So we exit */
980 context->state = GIF_DONE;
982 g_set_error (context->error,
984 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
985 _("GIF animation contained a frame with an incorrect size"));
990 context->frame_interlace = BitSet (buf[8], INTERLACE);
991 if (BitSet (buf[8], LOCALCOLORMAP)) {
992 /* Does this frame have it's own colormap. */
993 /* really only relevant when looking at the first frame
994 * of an animated gif. */
995 /* if it does, we need to re-read in the colormap,
996 * the gray_scale, and the bit_pixel */
997 context->bit_pixel = 1 << ((buf[8] & 0x07) + 1);
998 gif_set_get_colormap2 (context);
1001 gif_set_prepare_lzw (context);
1007 gif_get_next_step (GifContext *context)
1011 if (!gif_read (context, &c, 1)) {
1015 /* GIF terminator */
1016 /* hmm. Not 100% sure what to do about this. Should
1017 * i try to return a blank image instead? */
1018 context->state = GIF_DONE;
1023 /* Check the extention */
1024 gif_set_get_extension (context);
1028 /* look for frame */
1030 /* Not a valid start character */
1033 /* load the frame */
1034 gif_set_get_frame_info (context);
1041 gif_main_loop (GifContext *context)
1046 switch (context->state) {
1048 retval = gif_init (context);
1051 case GIF_GET_COLORMAP:
1052 retval = gif_get_colormap (context);
1054 context->state = GIF_GET_NEXT_STEP;
1057 case GIF_GET_NEXT_STEP:
1058 retval = gif_get_next_step (context);
1061 case GIF_GET_FRAME_INFO:
1062 retval = gif_get_frame_info (context);
1065 case GIF_GET_EXTENTION:
1066 retval = gif_get_extension (context);
1068 context->state = GIF_GET_NEXT_STEP;
1071 case GIF_GET_COLORMAP2:
1072 retval = gif_get_colormap (context);
1074 gif_set_prepare_lzw (context);
1077 case GIF_PREPARE_LZW:
1078 retval = gif_prepare_lzw (context);
1081 case GIF_LZW_FILL_BUFFER:
1082 retval = gif_lzw_fill_buffer (context);
1085 case GIF_LZW_CLEAR_CODE:
1086 retval = gif_lzw_clear_code (context);
1090 retval = gif_get_lzw (context);
1098 } while ((retval == 0) || (retval == -3));
1106 GifContext *context;
1108 context = g_new0 (GifContext, 1);
1110 context->pixbuf = NULL;
1111 context->file = NULL;
1112 context->state = GIF_START;
1113 context->prepare_func = NULL;
1114 context->update_func = NULL;
1115 context->frame_done_func = NULL;
1116 context->anim_done_func = NULL;
1117 context->user_data = NULL;
1118 context->buf = NULL;
1119 context->amount_needed = 0;
1120 context->gif89.transparent = -1;
1121 context->gif89.delay_time = -1;
1122 context->gif89.input_flag = -1;
1123 context->gif89.disposal = -1;
1127 /* Shared library entry point */
1129 gdk_pixbuf__gif_image_load (FILE *file, GError **error)
1131 GifContext *context;
1134 g_return_val_if_fail (file != NULL, NULL);
1136 context = new_context ();
1137 context->file = file;
1138 context->error = error;
1140 gif_main_loop (context);
1142 pixbuf = context->pixbuf;
1149 gdk_pixbuf__gif_image_begin_load (ModulePreparedNotifyFunc prepare_func,
1150 ModuleUpdatedNotifyFunc update_func,
1151 ModuleFrameDoneNotifyFunc frame_done_func,
1152 ModuleAnimationDoneNotifyFunc anim_done_func,
1156 GifContext *context;
1161 context = new_context ();
1162 context->error = error;
1163 context->prepare_func = prepare_func;
1164 context->update_func = update_func;
1165 context->frame_done_func = frame_done_func;
1166 context->anim_done_func = anim_done_func;
1167 context->user_data = user_data;
1169 return (gpointer) context;
1173 gdk_pixbuf__gif_image_stop_load (gpointer data, GError **error)
1175 GifContext *context = (GifContext *) data;
1177 /* FIXME: free the animation data */
1179 /* FIXME this thing needs to report errors if
1180 * we have unused image data
1183 if (context->pixbuf)
1184 gdk_pixbuf_unref (context->pixbuf);
1185 if (context->animation)
1186 gdk_pixbuf_animation_unref (context->animation);
1187 /* g_free (context->buf);*/
1194 gdk_pixbuf__gif_image_load_increment (gpointer data,
1195 const guchar *buf, guint size,
1199 GifContext *context = (GifContext *) data;
1201 context->error = error;
1203 if (context->amount_needed == 0) {
1204 /* we aren't looking for some bytes. */
1205 /* we can use buf now, but we don't want to keep it around at all.
1206 * it will be gone by the end of the call. */
1207 context->buf = (guchar*) buf; /* very dubious const cast */
1209 context->size = size;
1211 /* we need some bytes */
1212 if (size < context->amount_needed) {
1213 context->amount_needed -= size;
1214 /* copy it over and return */
1215 memcpy (context->buf + context->size, buf, size);
1216 context->size += size;
1218 } else if (size == context->amount_needed) {
1219 memcpy (context->buf + context->size, buf, size);
1220 context->size += size;
1222 context->buf = g_realloc (context->buf, context->size + size);
1223 memcpy (context->buf + context->size, buf, size);
1224 context->size += size;
1228 retval = gif_main_loop (context);
1233 /* we didn't have enough memory */
1234 /* prepare for the next image_load_increment */
1235 if (context->buf == buf) {
1236 g_assert (context->size == size);
1237 context->buf = (guchar *)g_new (guchar, context->amount_needed + (context->size - context->ptr));
1238 memcpy (context->buf, buf + context->ptr, context->size - context->ptr);
1240 /* copy the left overs to the begining of the buffer */
1241 /* and realloc the memory */
1242 memmove (context->buf, context->buf + context->ptr, context->size - context->ptr);
1243 context->buf = g_realloc (context->buf, context->amount_needed + (context->size - context->ptr));
1245 context->size = context->size - context->ptr;
1248 /* we are prolly all done */
1249 if (context->buf == buf)
1250 context->buf = NULL;
1255 static GdkPixbufAnimation *
1256 gdk_pixbuf__gif_image_load_animation (FILE *file,
1259 GifContext *context;
1260 GdkPixbufAnimation *animation;
1262 g_return_val_if_fail (file != NULL, NULL);
1264 context = new_context ();
1266 context->error = error;
1268 context->animation = g_object_new (GDK_TYPE_PIXBUF_ANIMATION, NULL);
1270 context->animation->n_frames = 0;
1271 context->animation->frames = NULL;
1272 context->animation->width = 0;
1273 context->animation->height = 0;
1274 context->file = file;
1276 gif_main_loop (context);
1278 animation = context->animation;
1284 gdk_pixbuf__gif_fill_vtable (GdkPixbufModule *module)
1286 module->load = gdk_pixbuf__gif_image_load;
1287 module->begin_load = gdk_pixbuf__gif_image_begin_load;
1288 module->stop_load = gdk_pixbuf__gif_image_stop_load;
1289 module->load_increment = gdk_pixbuf__gif_image_load_increment;
1290 module->load_animation = gdk_pixbuf__gif_image_load_animation;