diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index 2b345cf3a1..7e4f11a6d5 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,20 @@ +2003-06-22 Matthias Clasen + + * io-gif.c (gif_get_extension): Reset block_count to 0 for all application extensions, otherwise the data blocks + of unknown extensions are not propertly skipped. + + Second half of the fix for #106962, handle frames extending beyond the logical screen: + + * io-gif.c (clip_frame): New helper function to clip a rectangle to the logical screen size of the gif. + (maybe_update): New helper function to call update_func only if the rectangle is not completely off-bounds. + (gif_get_lzw): Read frames extending outside the logical screen size, but be careful clip to the logical screen size + when operating on the composite pixbuf and when calling update_func. + (gif_init): Set the animation size to the logical screen size. + (gif_get_frame_info): Don't refuse to load images with frames extending beyond the logical screen size. + + * io-gif-animation.c (gdk_pixbuf_gif_anim_frame_composite): Be careful to clip all rectangles to the logical screen + size, also handle the fact that frames may be completely off-bounds. + 2003-06-21 Matthias Clasen * io-gif.c: Merge from 2.2: Fix update_func() arguments and accept empty diff --git a/gdk-pixbuf/io-gif-animation.c b/gdk-pixbuf/io-gif-animation.c index 97062ba9c9..bf50a9363c 100644 --- a/gdk-pixbuf/io-gif-animation.c +++ b/gdk-pixbuf/io-gif-animation.c @@ -391,7 +391,9 @@ gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim, while (tmp != NULL) { GdkPixbufFrame *f = tmp->data; - + gint clipped_width = MIN (gif_anim->width - f->x_offset, gdk_pixbuf_get_width (f->pixbuf)); + gint clipped_height = MIN (gif_anim->height - f->y_offset, gdk_pixbuf_get_height (f->pixbuf)); + if (f->need_recomposite) { if (f->composited) { g_object_unref (f->composited); @@ -419,26 +421,17 @@ gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim, (gif_anim->bg_green << 16) | (gif_anim->bg_blue << 8)); - gdk_pixbuf_composite (f->pixbuf, - f->composited, - f->x_offset, - f->y_offset, - gdk_pixbuf_get_width (f->pixbuf), - gdk_pixbuf_get_height (f->pixbuf), - f->x_offset, f->y_offset, - 1.0, 1.0, - GDK_INTERP_BILINEAR, - 255); -#if 0 - gdk_pixbuf_copy_area (f->pixbuf, - 0, 0, - gdk_pixbuf_get_width (f->pixbuf), - gdk_pixbuf_get_height (f->pixbuf), - f->composited, - f->x_offset, - f->y_offset); - -#endif + if (clipped_width > 0 && clipped_height > 0) + gdk_pixbuf_composite (f->pixbuf, + f->composited, + f->x_offset, + f->y_offset, + clipped_width, + clipped_height, + f->x_offset, f->y_offset, + 1.0, 1.0, + GDK_INTERP_BILINEAR, + 255); if (f->action == GDK_PIXBUF_FRAME_REVERT) g_warning ("First frame of GIF has bad dispose mode, GIF loader should not have loaded this image"); @@ -449,6 +442,9 @@ gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim, prev_frame = tmp->prev->data; + gint prev_clipped_width = MIN (gif_anim->width - prev_frame->x_offset, gdk_pixbuf_get_width (prev_frame->pixbuf)); + gint prev_clipped_height = MIN (gif_anim->height - prev_frame->y_offset, gdk_pixbuf_get_height (prev_frame->pixbuf)); + /* Init f->composited with what we should have after the previous * frame */ @@ -457,66 +453,71 @@ gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim, f->composited = gdk_pixbuf_copy (prev_frame->composited); } else if (prev_frame->action == GDK_PIXBUF_FRAME_DISPOSE) { - GdkPixbuf *area; - f->composited = gdk_pixbuf_copy (prev_frame->composited); + if (prev_clipped_width > 0 && prev_clipped_height > 0) { + /* Clear area of previous frame to background */ + GdkPixbuf *area; - /* Clear area of previous frame to background */ - area = gdk_pixbuf_new_subpixbuf (f->composited, - prev_frame->x_offset, - prev_frame->y_offset, - gdk_pixbuf_get_width (prev_frame->pixbuf), - gdk_pixbuf_get_height (prev_frame->pixbuf)); - - gdk_pixbuf_fill (area, - (gif_anim->bg_red << 24) | - (gif_anim->bg_green << 16) | - (gif_anim->bg_blue << 8)); - - g_object_unref (area); - + area = gdk_pixbuf_new_subpixbuf (f->composited, + prev_frame->x_offset, + prev_frame->y_offset, + prev_clipped_width, + prev_clipped_height); + + gdk_pixbuf_fill (area, + (gif_anim->bg_red << 24) | + (gif_anim->bg_green << 16) | + (gif_anim->bg_blue << 8)); + + g_object_unref (area); + } } else if (prev_frame->action == GDK_PIXBUF_FRAME_REVERT) { f->composited = gdk_pixbuf_copy (prev_frame->composited); - - /* Copy in the revert frame */ - gdk_pixbuf_copy_area (prev_frame->revert, - 0, 0, - gdk_pixbuf_get_width (prev_frame->revert), - gdk_pixbuf_get_height (prev_frame->revert), - f->composited, - prev_frame->x_offset, - prev_frame->y_offset); + if (prev_clipped_width > 0 && prev_clipped_height > 0) { + /* Copy in the revert frame */ + gdk_pixbuf_copy_area (prev_frame->revert, + 0, 0, + gdk_pixbuf_get_width (prev_frame->revert), + gdk_pixbuf_get_height (prev_frame->revert), + f->composited, + prev_frame->x_offset, + prev_frame->y_offset); + } } else { g_warning ("Unknown revert action for GIF frame"); } if (f->revert == NULL && f->action == GDK_PIXBUF_FRAME_REVERT) { - /* We need to save the contents before compositing */ - GdkPixbuf *area; - - area = gdk_pixbuf_new_subpixbuf (f->composited, - f->x_offset, - f->y_offset, - gdk_pixbuf_get_width (f->pixbuf), - gdk_pixbuf_get_height (f->pixbuf)); - - f->revert = gdk_pixbuf_copy (area); - - g_object_unref (area); + if (clipped_width > 0 && clipped_height > 0) { + /* We need to save the contents before compositing */ + GdkPixbuf *area; + + area = gdk_pixbuf_new_subpixbuf (f->composited, + f->x_offset, + f->y_offset, + clipped_width, + clipped_height); + + f->revert = gdk_pixbuf_copy (area); + + g_object_unref (area); + } } - /* Put current frame onto f->composited */ - gdk_pixbuf_composite (f->pixbuf, - f->composited, - f->x_offset, - f->y_offset, - gdk_pixbuf_get_width (f->pixbuf), - gdk_pixbuf_get_height (f->pixbuf), - f->x_offset, f->y_offset, - 1.0, 1.0, - GDK_INTERP_NEAREST, - 255); + if (clipped_width > 0 && clipped_height > 0) { + /* Put current frame onto f->composited */ + gdk_pixbuf_composite (f->pixbuf, + f->composited, + f->x_offset, + f->y_offset, + clipped_width, + clipped_height, + f->x_offset, f->y_offset, + 1.0, 1.0, + GDK_INTERP_NEAREST, + 255); + } f->need_recomposite = FALSE; } diff --git a/gdk-pixbuf/io-gif.c b/gdk-pixbuf/io-gif.c index e5e7657ec9..c37251eb4b 100644 --- a/gdk-pixbuf/io-gif.c +++ b/gdk-pixbuf/io-gif.c @@ -410,8 +410,8 @@ gif_get_extension (GifContext *context) if (!strncmp (context->block_buf, "NETSCAPE2.0", 11) || !strncmp (context->block_buf, "ANIMEXTS1.0", 11)) { context->in_loop_extension = TRUE; - context->block_count = 0; } + context->block_count = 0; } if (context->in_loop_extension) { do { @@ -777,6 +777,49 @@ set_need_recomposite (gpointer data, gpointer user_data) frame->need_recomposite = TRUE; } +/* 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) @@ -863,25 +906,8 @@ gif_get_lzw (GifContext *context) context->frame->bg_transparent = (context->gif89.transparent == context->background_index); - { - /* Update animation size */ - int w, h; - - context->animation->n_frames ++; - context->animation->frames = g_list_append (context->animation->frames, context->frame); - - w = context->frame->x_offset + - gdk_pixbuf_get_width (context->frame->pixbuf); - h = context->frame->y_offset + - gdk_pixbuf_get_height (context->frame->pixbuf); - if (w > context->animation->width || h > context->animation->height) { - g_list_foreach (context->animation->frames, set_need_recomposite, NULL); - } - if (w > context->animation->width) - context->animation->width = w; - if (h > context->animation->height) - context->animation->height = h; - } + 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) { @@ -893,6 +919,7 @@ gif_get_lzw (GifContext *context) /* 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); @@ -900,13 +927,15 @@ gif_get_lzw (GifContext *context) gdk_pixbuf_gif_anim_frame_composite (context->animation, prev_frame); - gdk_pixbuf_copy_area (prev_frame->composited, - context->frame->x_offset, - context->frame->y_offset, - gdk_pixbuf_get_width (context->frame->pixbuf), - gdk_pixbuf_get_height (context->frame->pixbuf), - context->frame->pixbuf, - 0, 0); + 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); } } @@ -1005,37 +1034,29 @@ gif_get_lzw (GifContext *context) if (bound_flag && context->update_func) { if (lower_bound <= upper_bound && first_pass == context->draw_pass) { - (* context->update_func) - (context->frame->pixbuf, - context->frame->x_offset, - context->frame->y_offset + lower_bound, - gdk_pixbuf_get_width (context->frame->pixbuf), - upper_bound - lower_bound, - context->user_data); + maybe_update (context, + context->frame->x_offset, + context->frame->y_offset + lower_bound, + gdk_pixbuf_get_width (context->frame->pixbuf), + upper_bound - lower_bound); } else { if (lower_bound <= upper_bound) { - (* context->update_func) - (context->frame->pixbuf, - context->frame->x_offset, - context->frame->y_offset, - gdk_pixbuf_get_width (context->frame->pixbuf), - gdk_pixbuf_get_height (context->frame->pixbuf), - context->user_data); + 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 { - (* context->update_func) - (context->frame->pixbuf, - context->frame->x_offset, - context->frame->y_offset, - gdk_pixbuf_get_width (context->frame->pixbuf), - upper_bound, - context->user_data); - (* context->update_func) - (context->frame->pixbuf, - 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, - context->user_data); + 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); } } } @@ -1164,7 +1185,10 @@ gif_init (GifContext *context) 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; + if (context->has_global_cmap) { gif_set_get_colormap (context); } else { @@ -1200,19 +1224,6 @@ gif_get_frame_info (GifContext *context) context->x_offset = LM_to_uint (buf[0], buf[1]); context->y_offset = LM_to_uint (buf[2], buf[3]); - if (((context->frame_height + context->y_offset) > context->height) || - ((context->frame_len + context->x_offset) > context->width)) { - /* All frames must fit in the image bounds */ - context->state = GIF_DONE; - - g_set_error (context->error, - GDK_PIXBUF_ERROR, - GDK_PIXBUF_ERROR_CORRUPT_IMAGE, - _("GIF image contained a frame appearing outside the image bounds.")); - - return -2; - } - if (context->animation->frames == NULL && context->gif89.disposal == 3) { /* First frame can't have "revert to previous" as its