summaryrefslogtreecommitdiff
path: root/gdk-pixbuf/io-gif.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdk-pixbuf/io-gif.c')
-rw-r--r--gdk-pixbuf/io-gif.c478
1 files changed, 71 insertions, 407 deletions
diff --git a/gdk-pixbuf/io-gif.c b/gdk-pixbuf/io-gif.c
index 911499520..3fba50e11 100644
--- a/gdk-pixbuf/io-gif.c
+++ b/gdk-pixbuf/io-gif.c
@@ -58,7 +58,6 @@
#include <glib/gi18n-lib.h>
#include "gdk-pixbuf-io.h"
#include "io-gif-animation.h"
-#include "lzw.h"
@@ -108,12 +107,10 @@ struct _GifContext
gboolean has_global_cmap;
- CMap global_color_map;
gint global_colormap_size;
unsigned int global_bit_pixel;
unsigned int global_color_resolution;
unsigned int background_index;
- gboolean stop_after_first_frame;
gboolean frame_cmap_active;
CMap frame_color_map;
@@ -155,14 +152,6 @@ struct _GifContext
guchar block_buf[280];
guchar lzw_set_code_size;
- LZWDecoder *lzw_decoder;
- guint8 *index_buffer;
- gsize index_buffer_length;
-
- /* painting context */
- gint draw_xpos;
- gint draw_ypos;
- gint draw_pass;
/* error pointer */
GError **error;
@@ -259,15 +248,9 @@ gif_get_colormap (GifContext *context)
return -1;
}
- context->global_color_map[0][context->global_colormap_size] = rgb[0];
- context->global_color_map[1][context->global_colormap_size] = rgb[1];
- context->global_color_map[2][context->global_colormap_size] = rgb[2];
-
- if (context->global_colormap_size == context->background_index) {
- context->animation->bg_red = rgb[0];
- context->animation->bg_green = rgb[1];
- context->animation->bg_blue = rgb[2];
- }
+ context->animation->color_map[context->global_colormap_size * 3 + 0] = rgb[0];
+ context->animation->color_map[context->global_colormap_size * 3 + 1] = rgb[1];
+ context->animation->color_map[context->global_colormap_size * 3 + 2] = rgb[2];
context->global_colormap_size ++;
}
@@ -430,173 +413,23 @@ static void
gif_set_get_lzw (GifContext *context)
{
context->state = GIF_GET_LZW;
- context->draw_xpos = 0;
- context->draw_ypos = 0;
- context->draw_pass = 0;
-}
-
-static void
-gif_fill_in_pixels (GifContext *context, guchar *dest, gint offset, guchar v)
-{
- guchar *pixel = NULL;
- guchar (*cmap)[MAXCOLORMAPSIZE];
-
- if (context->frame_cmap_active)
- cmap = context->frame_color_map;
- else
- cmap = context->global_color_map;
-
- if (context->gif89.transparent != -1) {
- pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 4;
- *pixel = cmap [0][(guchar) v];
- *(pixel+1) = cmap [1][(guchar) v];
- *(pixel+2) = cmap [2][(guchar) v];
- *(pixel+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 255);
- } else {
- pixel = dest + (context->draw_ypos + offset) * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 3;
- *pixel = cmap [0][(guchar) v];
- *(pixel+1) = cmap [1][(guchar) v];
- *(pixel+2) = cmap [2][(guchar) v];
- }
-}
-
-
-/* only called if progressive and interlaced */
-static void
-gif_fill_in_lines (GifContext *context, guchar *dest, guchar v)
-{
- switch (context->draw_pass) {
- case 0:
- if (context->draw_ypos > 4) {
- gif_fill_in_pixels (context, dest, -4, v);
- gif_fill_in_pixels (context, dest, -3, v);
- }
- if (context->draw_ypos < (context->frame_height - 4)) {
- gif_fill_in_pixels (context, dest, 3, v);
- gif_fill_in_pixels (context, dest, 4, v);
- }
- /* we don't need a break here. We draw the outer pixels first, then the
- * inner ones, then the innermost ones. case 0 needs to draw all 3 bands.
- * case 1, just the last two, and case 2 just draws the last one*/
- case 1:
- if (context->draw_ypos > 2)
- gif_fill_in_pixels (context, dest, -2, v);
- if (context->draw_ypos < (context->frame_height - 2))
- gif_fill_in_pixels (context, dest, 2, v);
- /* no break as above. */
- case 2:
- if (context->draw_ypos > 1)
- gif_fill_in_pixels (context, dest, -1, v);
- if (context->draw_ypos < (context->frame_height - 1))
- gif_fill_in_pixels (context, dest, 1, v);
- case 3:
- default:
- break;
- }
-}
-
-/* Clips a rectancle to the base dimensions. Returns TRUE if the clipped rectangle is non-empty. */
-static gboolean
-clip_frame (GifContext *context,
- gint *x,
- gint *y,
- gint *width,
- gint *height)
-{
- gint orig_x, orig_y;
-
- orig_x = *x;
- orig_y = *y;
- *x = MAX (0, *x);
- *y = MAX (0, *y);
- *width = MIN (context->width, orig_x + *width) - *x;
- *height = MIN (context->height, orig_y + *height) - *y;
-
- if (*width > 0 && *height > 0)
- return TRUE;
-
- /* The frame is completely off-bounds */
-
- *x = 0;
- *y = 0;
- *width = 0;
- *height = 0;
-
- return FALSE;
-}
-
-/* Call update_func on the given rectangle, unless it is completely off-bounds */
-static void
-maybe_update (GifContext *context,
- gint x,
- gint y,
- gint width,
- gint height)
-{
- if (clip_frame (context, &x, &y, &width, &height))
- (*context->update_func) (context->frame->pixbuf,
- x, y, width, height,
- context->user_data);
}
static int
gif_get_lzw (GifContext *context)
{
- guchar *dest, *temp;
- gint lower_bound, upper_bound; /* bounds for emitting the area_updated signal */
- gboolean bound_flag;
- gint first_pass; /* bounds for emitting the area_updated signal */
- gint v;
-
if (context->frame == NULL) {
- context->frame = g_new (GdkPixbufFrame, 1);
-
- context->frame->composited = NULL;
- context->frame->revert = NULL;
-
- if (context->frame_len == 0 || context->frame_height == 0) {
- /* An empty frame, we just output a single transparent
- * pixel at (0, 0).
- */
- context->x_offset = 0;
- context->y_offset = 0;
- context->frame_len = 1;
- context->frame_height = 1;
- context->frame->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, 1, 1);
- if (context->frame->pixbuf) {
- guchar *pixels;
-
- pixels = gdk_pixbuf_get_pixels (context->frame->pixbuf);
- pixels[0] = 0;
- pixels[1] = 0;
- pixels[2] = 0;
- pixels[3] = 0;
- }
- } else {
- int rowstride;
- guint64 len;
-
- rowstride = gdk_pixbuf_calculate_rowstride (GDK_COLORSPACE_RGB,
- TRUE,
- 8,
- context->frame_len,
- context->frame_height);
- if (rowstride > 0 &&
- g_uint64_checked_mul (&len, rowstride, context->frame_height) &&
- len <= G_MAXINT) {
- context->frame->pixbuf =
- gdk_pixbuf_new (GDK_COLORSPACE_RGB,
- TRUE,
- 8,
- context->frame_len,
- context->frame_height);
- } else {
- context->frame->pixbuf = NULL;
- }
- }
-
- if (!context->frame->pixbuf) {
- g_free (context->frame);
+ int rowstride;
+ guint64 len;
+
+ rowstride = gdk_pixbuf_calculate_rowstride (GDK_COLORSPACE_RGB,
+ TRUE,
+ 8,
+ context->frame_len,
+ context->frame_height);
+ if (rowstride < 0 ||
+ !g_uint64_checked_mul (&len, rowstride, context->frame_height) ||
+ len >= G_MAXINT) {
g_set_error_literal (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
@@ -604,10 +437,34 @@ gif_get_lzw (GifContext *context)
return -2;
}
+ context->frame = g_new0 (GdkPixbufFrame, 1);
+
+ context->frame->lzw_data = g_byte_array_new ();
+ context->frame->lzw_code_size = context->lzw_set_code_size;
+
+ context->frame->width = context->frame_len;
+ context->frame->height = context->frame_height;
context->frame->x_offset = context->x_offset;
context->frame->y_offset = context->y_offset;
- context->frame->need_recomposite = TRUE;
-
+ context->frame->interlace = context->frame_interlace;
+
+ if (context->frame_colormap_size > 0) {
+ int i;
+
+ context->frame->color_map = g_malloc (256 * 3);
+ context->frame->color_map_allocated = TRUE;
+ for (i = 0; i < 256; i++) {
+ context->frame->color_map[i * 3 + 0] = context->frame_color_map[0][i];
+ context->frame->color_map[i * 3 + 1] = context->frame_color_map[1][i];
+ context->frame->color_map[i * 3 + 2] = context->frame_color_map[2][i];
+ }
+ }
+ else {
+ context->frame->color_map = context->animation->color_map;
+ }
+
+ context->frame->transparent_index = context->gif89.transparent;
+
/* GIF delay is in hundredths, we want thousandths */
context->frame->delay_time = context->gif89.delay_time * 10;
@@ -622,7 +479,7 @@ gif_get_lzw (GifContext *context)
*/
if (context->frame->delay_time < 20)
context->frame->delay_time = 20; /* 20 = "fast" */
-
+
context->frame->elapsed = context->animation->total_time;
context->animation->total_time += context->frame->delay_time;
@@ -642,228 +499,47 @@ gif_get_lzw (GifContext *context)
break;
}
- context->animation->n_frames ++;
context->animation->frames = g_list_append (context->animation->frames, context->frame);
- /* Only call prepare_func for the first frame */
- if (context->animation->frames->next == NULL) {
- if (context->animation->width == 0 )
- context->animation->width = gdk_pixbuf_get_width(context->frame->pixbuf);
- if (context->animation->height == 0)
- context->animation->height = gdk_pixbuf_get_height (context->frame->pixbuf);
-
- if (context->prepare_func)
- (* context->prepare_func) (context->frame->pixbuf,
- GDK_PIXBUF_ANIMATION (context->animation),
- context->user_data);
- } else {
- /* Otherwise init frame with last frame */
- GList *link;
- GdkPixbufFrame *prev_frame;
- gint x, y, w, h;
-
- link = g_list_find (context->animation->frames, context->frame);
-
- prev_frame = link->prev->data;
-
- gdk_pixbuf_gif_anim_frame_composite (context->animation, prev_frame);
-
- /* Composite failed */
- if (prev_frame->composited == NULL) {
- GdkPixbufFrame *frame = NULL;
- link = g_list_first (context->animation->frames);
- while (link != NULL) {
- frame = (GdkPixbufFrame *)link->data;
- if (frame != NULL) {
- if (frame->pixbuf != NULL)
- g_object_unref (frame->pixbuf);
- if (frame->composited != NULL)
- g_object_unref (frame->composited);
- if (frame->revert != NULL)
- g_object_unref (frame->revert);
- g_free (frame);
- }
- link = link->next;
- }
-
- g_list_free (context->animation->frames);
- context->animation->frames = NULL;
-
- g_set_error_literal (context->error,
- GDK_PIXBUF_ERROR,
- GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
- _("Not enough memory to composite a frame in GIF file"));
- return -2;
- }
-
- x = context->frame->x_offset;
- y = context->frame->y_offset;
- w = gdk_pixbuf_get_width (context->frame->pixbuf);
- h = gdk_pixbuf_get_height (context->frame->pixbuf);
- if (clip_frame (context, &x, &y, &w, &h))
- gdk_pixbuf_copy_area (prev_frame->composited,
- x, y, w, h,
- context->frame->pixbuf,
- 0, 0);
- }
+ /* Notify when have first frame */
+ if (context->animation->frames->next == NULL && context->prepare_func != NULL) {
+ GdkPixbuf *pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
+ if (pixbuf != NULL)
+ (* context->prepare_func) (pixbuf,
+ GDK_PIXBUF_ANIMATION (context->animation),
+ context->user_data);
+ }
}
- dest = gdk_pixbuf_get_pixels (context->frame->pixbuf);
-
- bound_flag = FALSE;
- lower_bound = upper_bound = context->draw_ypos;
- first_pass = context->draw_pass;
-
+ /* read all blocks */
while (TRUE) {
- guchar (*cmap)[MAXCOLORMAPSIZE];
- guint8 block[255];
- gint empty_block = FALSE;
- gsize n_indexes, i;
-
- if (context->frame_cmap_active)
- cmap = context->frame_color_map;
- else
- cmap = context->global_color_map;
-
- v = get_data_block (context, block, &empty_block);
- if (v < 0) {
- goto finished_data;
- }
- if (empty_block) {
- goto done;
- }
-
- bound_flag = TRUE;
+ gint retval, empty_block = FALSE;
- g_assert (gdk_pixbuf_get_has_alpha (context->frame->pixbuf));
-
- if (context->lzw_decoder == NULL) {
- context->lzw_decoder = lzw_decoder_new (context->lzw_set_code_size + 1);
- context->index_buffer_length = context->frame_len * context->frame_height;
- context->index_buffer = g_new (guint8, context->index_buffer_length);
- }
- n_indexes = lzw_decoder_feed (context->lzw_decoder, block, context->block_count, context->index_buffer, context->index_buffer_length);
- context->block_count = 0;
-
- for (i = 0; i < n_indexes; i++) {
- v = context->index_buffer[i];
-
- temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->frame->pixbuf) + context->draw_xpos * 4;
- *temp = cmap [0][(guchar) v];
- *(temp+1) = cmap [1][(guchar) v];
- *(temp+2) = cmap [2][(guchar) v];
- *(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 255);
-
- if (context->prepare_func && context->frame_interlace)
- gif_fill_in_lines (context, dest, v);
-
- context->draw_xpos++;
-
- if (context->draw_xpos == context->frame_len) {
- context->draw_xpos = 0;
- if (context->frame_interlace) {
- switch (context->draw_pass) {
- case 0:
- case 1:
- context->draw_ypos += 8;
- break;
- case 2:
- context->draw_ypos += 4;
- break;
- case 3:
- context->draw_ypos += 2;
- break;
- }
+ retval = get_data_block (context, (unsigned char *) context->block_buf, &empty_block);
- if (context->draw_ypos >= context->frame_height) {
- context->draw_pass++;
- switch (context->draw_pass) {
- case 1:
- context->draw_ypos = 4;
- break;
- case 2:
- context->draw_ypos = 2;
- break;
- case 3:
- context->draw_ypos = 1;
- break;
- default:
- goto done;
- }
- }
- } else {
- context->draw_ypos++;
- }
- if (context->draw_pass != first_pass) {
- if (context->draw_ypos > lower_bound) {
- lower_bound = 0;
- upper_bound = context->frame_height;
- } else {
-
- }
- } else
- upper_bound = context->draw_ypos;
- }
- if (context->draw_ypos >= context->frame_height)
- break;
+ /* Notify frame update */
+ if ((retval != 0 || empty_block) && context->animation->frames->next == NULL && context->update_func != NULL) {
+ GdkPixbuf *pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation));
+ if (pixbuf)
+ (* context->update_func) (pixbuf,
+ 0, 0, context->frame->width, context->frame->height,
+ context->user_data);
}
- }
-
- done:
-
- context->state = GIF_GET_NEXT_STEP;
- v = 0;
+ if (retval != 0)
+ return retval;
- finished_data:
-
- if (bound_flag)
- context->frame->need_recomposite = TRUE;
-
- if (bound_flag && context->update_func) {
- if (lower_bound <= upper_bound && first_pass == context->draw_pass) {
- maybe_update (context,
- context->frame->x_offset,
- context->frame->y_offset + lower_bound,
- gdk_pixbuf_get_width (context->frame->pixbuf),
- upper_bound - lower_bound + 1);
- } else {
- if (lower_bound <= upper_bound) {
- maybe_update (context,
- context->frame->x_offset,
- context->frame->y_offset,
- gdk_pixbuf_get_width (context->frame->pixbuf),
- gdk_pixbuf_get_height (context->frame->pixbuf));
- } else {
- maybe_update (context,
- context->frame->x_offset,
- context->frame->y_offset,
- gdk_pixbuf_get_width (context->frame->pixbuf),
- upper_bound);
- maybe_update (context,
- context->frame->x_offset,
- context->frame->y_offset + lower_bound,
- gdk_pixbuf_get_width (context->frame->pixbuf),
- gdk_pixbuf_get_height (context->frame->pixbuf) - lower_bound);
- }
+ if (empty_block) {
+ context->frame = NULL;
+ context->state = GIF_GET_NEXT_STEP;
+ return 0;
}
- }
-
- if (context->state == GIF_GET_NEXT_STEP) {
- g_clear_object (&context->lzw_decoder);
- g_clear_pointer (&context->index_buffer, g_free);
-
- /* Will be freed with context->animation, we are just
- * marking that we're done with it (no current frame)
- */
- context->frame = NULL;
- context->frame_cmap_active = FALSE;
- if (context->stop_after_first_frame)
- context->state = GIF_DONE;
+ g_byte_array_append (context->frame->lzw_data, context->block_buf, context->block_count);
+ if (context->animation->last_frame == context->frame)
+ context->animation->last_frame = NULL;
+ context->block_count = 0;
}
-
- return v;
}
static void
@@ -879,7 +555,7 @@ gif_prepare_lzw (GifContext *context)
return -1;
}
- if (context->lzw_set_code_size > LZW_CODE_MAX) {
+ if (context->lzw_set_code_size > 12) {
g_set_error_literal (context->error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
@@ -887,9 +563,6 @@ gif_prepare_lzw (GifContext *context)
return -2;
}
- context->lzw_decoder = NULL;
- context->index_buffer = NULL;
-
gif_set_get_lzw (context);
return 0;
@@ -955,13 +628,6 @@ gif_init (GifContext *context)
context->background_index = buf[5];
context->aspect_ratio = buf[6];
- /* Use background of transparent black as default, though if
- * one isn't set explicitly no one should ever use it.
- */
- context->animation->bg_red = 0;
- context->animation->bg_green = 0;
- context->animation->bg_blue = 0;
-
context->animation->width = context->width;
context->animation->height = context->height;
@@ -1010,7 +676,7 @@ gif_get_frame_info (GifContext *context)
if (!gif_read (context, buf, 9)) {
return -1;
}
-
+
/* Okay, we got all the info we need. Lets record it */
context->frame_len = LM_to_uint (buf[4], buf[5]);
context->frame_height = LM_to_uint (buf[6], buf[7]);
@@ -1195,7 +861,6 @@ new_context (void)
context->gif89.disposal = -1;
context->animation->loop = 1;
context->in_loop_extension = FALSE;
- context->stop_after_first_frame = FALSE;
return context;
}
@@ -1221,7 +886,6 @@ gdk_pixbuf__gif_image_load (FILE *file, GError **error)
context->file = file;
context->error = error;
- context->stop_after_first_frame = TRUE;
retval = gif_main_loop (context);
if (retval == -1 || context->animation->frames == NULL) {