From b4e4a0ed9d66132efad0539bc5901b35ea262d88 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Mon, 7 May 2001 15:58:47 +0000 Subject: fix some shell typos 2001-05-04 Havoc Pennington * configure.in: fix some shell typos * gtk/gtkcolorsel.c (gtk_color_selection_destroy): warning fix * gtk/gtkimage.c: handle animations * gtk/gtkcheckbutton.c (gtk_check_button_size_request): request border_width * 2, not just border_width * gtk/gtkscale.c: add "format_value" signal to allow people to override the way values are drawn. (gtk_scale_get_value_size): fix width/height mistake, and compute size from actual displayed text, not from made-up text. * gtk/gtktexttag.c (gtk_text_tag_class_init): fix return type in signal registration * tests/testtext.c: Add "Remove all tags" menu item for testing * gtk/gtktextbuffer.c (gtk_text_buffer_remove_all_tags): implement * demos/gtk-demo/main.c (main): add hack so we can find modules without installing gtk * demos/gtk-demo/textview.c (insert_text): demo font scaling * gtk/gtkcellrenderertext.c: Add "scale" property (font scaling factor) (gtk_cell_renderer_text_set_property): remove some bogus g_object_notify * gtk/gtktexttag.c: add "scale" property which is a font scaling factor * gtk/gtktextlayout.c (add_text_attrs): add font scale attribute to layout * gtk/gtktextiter.c (gtk_text_iter_is_start): rename from gtk_text_iter_is_first 2001-05-04 Havoc Pennington * pixops/pixops.c (pixops_process): merge fix from stable: Patch from hoshem@mel.comcen.com.au to fix nonzero X offsets. Fixes bug #50371. * gdk-pixbuf/pixops/pixops.c (pixops_composite_nearest): merge from stable: Patch from OKADA Mitsuru to fix confusion of using "src" instead of "p". (pixops_composite_color_nearest): Use a more accurate (and correct, to begin with) compositing method. This cures checks showing through on images with no alpha. * gdk-pixbuf.c (gdk_pixbuf_fill): fix bug that left some trailing bytes unfilled. * gdk-pixbuf-io.h: fix UpdatedNotifyFunc to use signed ints * gdk-pixbuf-loader.h (struct _GdkPixbufLoaderClass): Change area_updated signal to use signed ints. Removed animation-related signals. * io-gif.c, io-gif-animation.h, io-gif-animation.c: Massive rewrite action * gdk-pixbuf-animation.c: Add GdkPixbufAnimationIter to abstract all the pesky details. Remove old frame-based API. Make GdkPixbufAnimation an abstract base class, derived by the loaders. --- gdk-pixbuf/ChangeLog | 29 ++ gdk-pixbuf/Makefile.am | 4 +- gdk-pixbuf/gdk-pixbuf-animation.c | 655 ++++++++++++++++++++++++++++---------- gdk-pixbuf/gdk-pixbuf-io.h | 21 +- gdk-pixbuf/gdk-pixbuf-loader.c | 157 ++------- gdk-pixbuf/gdk-pixbuf-loader.h | 24 +- gdk-pixbuf/gdk-pixbuf-private.h | 63 ++-- gdk-pixbuf/gdk-pixbuf-util.c | 2 + gdk-pixbuf/gdk-pixbuf.c | 4 +- gdk-pixbuf/gdk-pixbuf.h | 34 +- gdk-pixbuf/io-bmp.c | 12 +- gdk-pixbuf/io-gif-animation.c | 557 ++++++++++++++++++++++++++++++++ gdk-pixbuf/io-gif-animation.h | 169 ++++++++++ gdk-pixbuf/io-gif.c | 514 ++++++++++++++++++++---------- gdk-pixbuf/io-ico.c | 9 +- gdk-pixbuf/io-jpeg.c | 5 +- gdk-pixbuf/io-png.c | 4 +- gdk-pixbuf/io-pnm.c | 5 +- gdk-pixbuf/io-ras.c | 8 +- gdk-pixbuf/io-tiff.c | 4 +- gdk-pixbuf/io-wbmp.c | 12 +- gdk-pixbuf/io-xbm.c | 4 +- gdk-pixbuf/io-xpm.c | 6 +- gdk-pixbuf/pixops/pixops.c | 45 ++- 24 files changed, 1749 insertions(+), 598 deletions(-) create mode 100644 gdk-pixbuf/io-gif-animation.c create mode 100644 gdk-pixbuf/io-gif-animation.h (limited to 'gdk-pixbuf') diff --git a/gdk-pixbuf/ChangeLog b/gdk-pixbuf/ChangeLog index 9dea2a36a3..f00bda8f65 100644 --- a/gdk-pixbuf/ChangeLog +++ b/gdk-pixbuf/ChangeLog @@ -1,3 +1,32 @@ +2001-05-04 Havoc Pennington + + * pixops/pixops.c (pixops_process): merge fix from stable: Patch + from hoshem@mel.comcen.com.au to fix nonzero X offsets. Fixes + bug #50371. + + * gdk-pixbuf/pixops/pixops.c (pixops_composite_nearest): merge + from stable: Patch from OKADA Mitsuru to fix + confusion of using "src" instead of "p". + (pixops_composite_color_nearest): Use a more accurate (and + correct, to begin with) compositing method. This cures checks + showing through on images with no alpha. + + * gdk-pixbuf.c (gdk_pixbuf_fill): fix bug that left some trailing + bytes unfilled. + + * gdk-pixbuf-io.h: fix UpdatedNotifyFunc to use signed ints + + * gdk-pixbuf-loader.h (struct _GdkPixbufLoaderClass): Change + area_updated signal to use signed ints. Removed animation-related + signals. + + * io-gif.c, io-gif-animation.h, io-gif-animation.c: Massive + rewrite action + + * gdk-pixbuf-animation.c: Add GdkPixbufAnimationIter to abstract + all the pesky details. Remove old frame-based API. Make + GdkPixbufAnimation an abstract base class, derived by the loaders. + Sun Apr 22 15:51:32 2001 Owen Taylor * Makefile.am (LDADDS): Add $(MATH_LIB). diff --git a/gdk-pixbuf/Makefile.am b/gdk-pixbuf/Makefile.am index c8ba6e0883..b006435ccd 100644 --- a/gdk-pixbuf/Makefile.am +++ b/gdk-pixbuf/Makefile.am @@ -32,8 +32,8 @@ libpixbufloader_xpm_la_LIBADD = # # The GIF loader # -libpixbufloader_static_gif_la_SOURCES = io-gif.c -libpixbufloader_gif_la_SOURCES = io-gif.c +libpixbufloader_static_gif_la_SOURCES = io-gif.c io-gif-animation.c io-gif-animation.h +libpixbufloader_gif_la_SOURCES = io-gif.c io-gif-animation.c io-gif-animation.h libpixbufloader_gif_la_LDFLAGS = -avoid-version -module libpixbufloader_gif_la_LIBADD = diff --git a/gdk-pixbuf/gdk-pixbuf-animation.c b/gdk-pixbuf/gdk-pixbuf-animation.c index cc32559048..274813c503 100644 --- a/gdk-pixbuf/gdk-pixbuf-animation.c +++ b/gdk-pixbuf/gdk-pixbuf-animation.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* GdkPixbuf library - Simple animation support * * Copyright (C) 1999 The Free Software Foundation @@ -26,12 +27,59 @@ #include "gdk-pixbuf-io.h" #include "gdk-pixbuf-private.h" -static void gdk_pixbuf_animation_class_init (GdkPixbufAnimationClass *klass); -static void gdk_pixbuf_animation_finalize (GObject *object); +typedef struct _GdkPixbufNonAnim GdkPixbufNonAnim; +typedef struct _GdkPixbufNonAnimClass GdkPixbufNonAnimClass; + +#define GDK_TYPE_PIXBUF_NON_ANIM (gdk_pixbuf_non_anim_get_type ()) +#define GDK_PIXBUF_NON_ANIM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnim)) +#define GDK_IS_PIXBUF_NON_ANIM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_NON_ANIM)) + +#define GDK_PIXBUF_NON_ANIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnimClass)) +#define GDK_IS_PIXBUF_NON_ANIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_NON_ANIM)) +#define GDK_PIXBUF_NON_ANIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_NON_ANIM, GdkPixbufNonAnimClass)) + +/* Private part of the GdkPixbufNonAnim structure */ +struct _GdkPixbufNonAnim { + GdkPixbufAnimation parent_instance; + + GdkPixbuf *pixbuf; +}; + +struct _GdkPixbufNonAnimClass { + GdkPixbufAnimationClass parent_class; + +}; + +static GType gdk_pixbuf_non_anim_get_type (void) G_GNUC_CONST; -static gpointer parent_class; +typedef struct _GdkPixbufNonAnimIter GdkPixbufNonAnimIter; +typedef struct _GdkPixbufNonAnimIterClass GdkPixbufNonAnimIterClass; + + +#define GDK_TYPE_PIXBUF_NON_ANIM_ITER (gdk_pixbuf_non_anim_iter_get_type ()) +#define GDK_PIXBUF_NON_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIter)) +#define GDK_IS_PIXBUF_NON_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_NON_ANIM_ITER)) + +#define GDK_PIXBUF_NON_ANIM_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIterClass)) +#define GDK_IS_PIXBUF_NON_ANIM_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_NON_ANIM_ITER)) +#define GDK_PIXBUF_NON_ANIM_ITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_NON_ANIM_ITER, GdkPixbufNonAnimIterClass)) + +struct _GdkPixbufNonAnimIter { + GdkPixbufAnimationIter parent_instance; + + GdkPixbufNonAnim *non_anim; +}; + +struct _GdkPixbufNonAnimIterClass { + GdkPixbufAnimationIterClass parent_class; + +}; + +static GType gdk_pixbuf_non_anim_iter_get_type (void) G_GNUC_CONST; + + GType gdk_pixbuf_animation_get_type (void) @@ -43,7 +91,7 @@ gdk_pixbuf_animation_get_type (void) sizeof (GdkPixbufAnimationClass), (GBaseInitFunc) NULL, (GBaseFinalizeFunc) NULL, - (GClassInitFunc) gdk_pixbuf_animation_class_init, + (GClassInitFunc) NULL, NULL, /* class_finalize */ NULL, /* class_data */ sizeof (GdkPixbufAnimation), @@ -59,35 +107,6 @@ gdk_pixbuf_animation_get_type (void) return object_type; } -static void -gdk_pixbuf_animation_class_init (GdkPixbufAnimationClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - parent_class = g_type_class_peek_parent (klass); - - object_class->finalize = gdk_pixbuf_animation_finalize; -} - -static void -gdk_pixbuf_animation_finalize (GObject *object) -{ - GdkPixbufAnimation *animation = GDK_PIXBUF_ANIMATION (object); - - GList *l; - GdkPixbufFrame *frame; - - for (l = animation->frames; l; l = l->next) { - frame = l->data; - gdk_pixbuf_unref (frame->pixbuf); - g_free (frame); - } - - g_list_free (animation->frames); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - /** @@ -154,7 +173,6 @@ gdk_pixbuf_animation_new_from_file (const char *filename, if (image_module->load_animation == NULL) { GdkPixbuf *pixbuf; - GdkPixbufFrame *frame; /* Keep this logic in sync with gdk_pixbuf_new_from_file() */ @@ -191,20 +209,9 @@ gdk_pixbuf_animation_new_from_file (const char *filename, if (pixbuf == NULL) return NULL; - frame = g_new (GdkPixbufFrame, 1); - frame->pixbuf = pixbuf; - g_object_ref (G_OBJECT (frame->pixbuf)); - frame->x_offset = 0; - frame->y_offset = 0; - frame->delay_time = -1; - frame->action = GDK_PIXBUF_FRAME_RETAIN; + animation = gdk_pixbuf_non_anim_new (pixbuf); - animation = g_object_new (GDK_TYPE_PIXBUF_ANIMATION, NULL); - - animation->n_frames = 1; - animation->frames = g_list_prepend (NULL, frame); - animation->width = gdk_pixbuf_get_width (pixbuf); - animation->height = gdk_pixbuf_get_height (pixbuf); + g_object_unref (G_OBJECT (pixbuf)); } else { fseek (f, 0, SEEK_SET); animation = (* image_module->load_animation) (f, error); @@ -261,6 +268,46 @@ gdk_pixbuf_animation_unref (GdkPixbufAnimation *animation) g_object_unref (G_OBJECT (animation)); } +/** + * gdk_pixbuf_animation_is_static_image: + * @animation: a #GdkPixbufAnimation + * + * If you load a file with gdk_pixbuf_animation_new_from_file() and it turns + * out to be a plain, unanimated image, then this function will return + * %TRUE. Use gdk_pixbuf_animation_get_static_image() to retrieve + * the image. + * + * Return value: %TRUE if the "animation" was really just an image + **/ +gboolean +gdk_pixbuf_animation_is_static_image (GdkPixbufAnimation *animation) +{ + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), FALSE); + + return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->is_static_image (animation); +} + +/** + * gdk_pixbuf_animation_get_static_image: + * @animation: a #GdkPixbufAnimation + * + * If an animation is really just a plain image (has only one frame), + * this function returns that image. If the animation is an animation, + * this function returns a reasonable thing to display as a static + * unanimated image, which might be the first frame, or something more + * sophisticated. If an animation hasn't loaded any frames yet, this + * function will return %NULL. + * + * Return value: unanimated image representing the animation + **/ +GdkPixbuf* +gdk_pixbuf_animation_get_static_image (GdkPixbufAnimation *animation) +{ + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL); + + return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_static_image (animation); +} + /** * gdk_pixbuf_animation_get_width: * @animation: An animation. @@ -272,9 +319,16 @@ gdk_pixbuf_animation_unref (GdkPixbufAnimation *animation) int gdk_pixbuf_animation_get_width (GdkPixbufAnimation *animation) { - g_return_val_if_fail (animation != NULL, -1); + int width; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), 0); + + width = 0; + GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_size (animation, + &width, NULL); + - return animation->width; + return width; } /** @@ -288,179 +342,444 @@ gdk_pixbuf_animation_get_width (GdkPixbufAnimation *animation) int gdk_pixbuf_animation_get_height (GdkPixbufAnimation *animation) { - g_return_val_if_fail (animation != NULL, -1); - - return animation->height; -} + int height; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), 0); -/** - * gdk_pixbuf_animation_get_num_frames: - * @animation: An animation. - * - * Queries the number of frames in a pixbuf animation. - * - * Return value: Number of frames in the animation. - **/ -int -gdk_pixbuf_animation_get_num_frames (GdkPixbufAnimation *animation) -{ - g_return_val_if_fail (animation != NULL, -1); + height = 0; + GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_size (animation, + NULL, &height); + - return animation->n_frames; + return height; } + /** - * gdk_pixbuf_animation_get_frames: - * @animation: An animation. + * gdk_pixbuf_animation_get_iter: + * @animation: a #GdkPixbufAnimation + * @start_time: time when the animation starts playing + * + * Get an iterator for displaying an animation. The iterator provides + * the frames that should be displayed at a given time. + * It should be freed after use with g_object_unref(). + * + * @start_time would normally come from g_get_current_time(), and + * marks the beginning of animation playback. After creating an + * iterator, you should immediately display the pixbuf returned by + * gdk_pixbuf_animation_iter_get_pixbuf(). Then, you should install a + * timeout (with g_timeout_add()) or by some other mechanism ensure + * that you'll update the image after + * gdk_pixbuf_animation_iter_get_delay_time() milliseconds. Each time + * the image is updated, you should reinstall the timeout with the new, + * possibly-changed delay time. + * + * As a shortcut, if @start_time is %NULL, the result of + * g_get_current_time() will be used automatically. * - * Queries the list of frames of an animation. + * To update the image (i.e. possibly change the result of + * gdk_pixbuf_animation_iter_get_pixbuf() to a new frame of the animation), + * call gdk_pixbuf_animation_iter_advance(). + * + * If you're using #GdkPixbufLoader, in addition to updating the image + * after the delay time, you should also update it whenever you + * receive the area_updated signal and + * gdk_pixbuf_animation_iter_on_currently_loading_frame() returns + * %TRUE. In this case, the frame currently being fed into the loader + * has received new data, so needs to be refreshed. The delay time for + * a frame may also be modified after an area_updated signal, for + * example if the delay time for a frame is encoded in the data after + * the frame itself. So your timeout should be reinstalled after any + * area_updated signal. + * + * A delay time of -1 is possible, indicating "infinite." * - * Return value: List of frames in the animation; this is a #GList of - * #GdkPixbufFrame structures. + * Return value: an iterator to move over the animation **/ -GList * -gdk_pixbuf_animation_get_frames (GdkPixbufAnimation *animation) +GdkPixbufAnimationIter* +gdk_pixbuf_animation_get_iter (GdkPixbufAnimation *animation, + const GTimeVal *start_time) { - g_return_val_if_fail (animation != NULL, NULL); + GTimeVal val; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION (animation), NULL); + - return animation->frames; + if (start_time) + val = *start_time; + else + g_get_current_time (&val); + + return GDK_PIXBUF_ANIMATION_GET_CLASS (animation)->get_iter (animation, &val); } -/** - * gdk_pixbuf_frame_get_pixbuf: - * @frame: A pixbuf animation frame. - * - * Queries the pixbuf of an animation frame. - * - * Return value: A pixbuf. - **/ -GdkPixbuf * -gdk_pixbuf_frame_get_pixbuf (GdkPixbufFrame *frame) +GType +gdk_pixbuf_animation_iter_get_type (void) { - g_return_val_if_fail (frame != NULL, NULL); + static GType object_type = 0; - return frame->pixbuf; + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufAnimationIterClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) NULL, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufAnimationIter), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (G_TYPE_OBJECT, + "GdkPixbufAnimationIter", + &object_info, 0); + } + + return object_type; } /** - * gdk_pixbuf_frame_get_x_offset: - * @frame: A pixbuf animation frame. + * gdk_pixbuf_animation_iter_get_delay_time: + * @iter: an animation iterator * - * Queries the X offset of an animation frame. - * - * Return value: X offset from the top left corner of the animation. + * Gets the number of milliseconds the current pixbuf should be displayed, + * or -1 if the current pixbuf should be displayed forever. g_timeout_add() + * conveniently takes a timeout in milliseconds, so you can use a timeout + * to schedule the next update. + * + * Return value: delay time in milliseconds (thousandths of a second) **/ int -gdk_pixbuf_frame_get_x_offset (GdkPixbufFrame *frame) +gdk_pixbuf_animation_iter_get_delay_time (GdkPixbufAnimationIter *iter) { - g_return_val_if_fail (frame != NULL, -1); - - return frame->x_offset; + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), -1); + + return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->get_delay_time (iter); } /** - * gdk_pixbuf_frame_get_y_offset: - * @frame: A pixbuf animation frame. + * gdk_pixbuf_animation_iter_get_pixbuf: + * @iter: an animation iterator * - * Queries the Y offset of an animation frame. - * - * Return value: Y offset from the top left corner of the animation. + * Gets the current pixbuf which should be displayed; the pixbuf will + * be the same size as the animation itself + * (gdk_pixbuf_animation_get_width(), + * gdk_pixbuf_animation_get_height()). This pixbuf should be displayed + * for gdk_pixbuf_animation_get_delay_time() milliseconds. The caller + * of this function does not own a reference to the returned pixbuf; + * the returned pixbuf will become invalid when the iterator advances + * to the next frame, which may happen anytime you call + * gdk_pixbuf_animation_iter_advance(). Copy the pixbuf to keep it + * (don't just add a reference), as it may get recycled as you advance + * the iterator. + * + * Return value: the pixbuf to be displayed **/ -int -gdk_pixbuf_frame_get_y_offset (GdkPixbufFrame *frame) +GdkPixbuf* +gdk_pixbuf_animation_iter_get_pixbuf (GdkPixbufAnimationIter *iter) { - g_return_val_if_fail (frame != NULL, -1); - - return frame->y_offset; + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), NULL); + + return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->get_pixbuf (iter); } /** - * gdk_pixbuf_frame_get_delay_time: - * @frame: A pixbuf animation frame. - * - * Queries the delay time in milliseconds of an animation frame. + * gdk_pixbuf_animation_iter_on_currently_loading_frame: + * @iter: a #GdkPixbufAnimationIter + * + * Used to determine how to respond to the area_updated signal on + * #GdkPixbufLoader when loading an animation. area_updated is emitted + * for an area of the frame currently streaming in to the loader. So if + * you're on the currently loading frame, you need to redraw the screen for + * the updated area. * - * Return value: Delay time in milliseconds. + * Return value: %TRUE if the frame we're on is partially loaded, or the last frame **/ -int -gdk_pixbuf_frame_get_delay_time (GdkPixbufFrame *frame) +gboolean +gdk_pixbuf_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter) { - g_return_val_if_fail (frame != NULL, -1); - - return frame->delay_time; + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), FALSE); + + return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->on_currently_loading_frame (iter); } /** - * gdk_pixbuf_frame_get_action: - * @frame: A pixbuf animation frame. + * gdk_pixbuf_animation_iter_advance: + * @iter: a #GdkPixbufAnimationIter + * @current_time: current time + * + * Possibly advances an animation to a new frame. Chooses the frame based + * on the start time passed to gdk_pixbuf_animation_get_iter(). * - * Queries the overlay action of an animation frame. + * @current_time would normally come from g_get_current_time(), and + * must be greater than or equal to the time passed to + * gdk_pixbuf_animation_get_iter(), and must increase or remain + * unchanged each time gdk_pixbuf_animation_iter_get_pixbuf() is + * called. That is, you can't go backward in time; animations only + * play forward. + * + * As a shortcut, pass %NULL for the current time and g_get_current_time() + * will be invoked on your behalf. So you only need to explicitly pass + * @current_time if you're doing something odd like playing the animation + * at double speed. + * + * If this function returns %FALSE, there's no need to update the animation + * display, assuming the display had been rendered prior to advancing; + * if %TRUE, you need to call gdk_animation_iter_get_pixbuf() and update the + * display with the new pixbuf. + * + * Returns: %TRUE if the image may need updating * - * Return value: Overlay action for this frame. **/ -GdkPixbufFrameAction -gdk_pixbuf_frame_get_action (GdkPixbufFrame *frame) +gboolean +gdk_pixbuf_animation_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time) { - g_return_val_if_fail (frame != NULL, GDK_PIXBUF_FRAME_RETAIN); + GTimeVal val; + + g_return_val_if_fail (GDK_IS_PIXBUF_ANIMATION_ITER (iter), FALSE); - return frame->action; + if (current_time) + val = *current_time; + else + g_get_current_time (&val); + + return GDK_PIXBUF_ANIMATION_ITER_GET_CLASS (iter)->advance (iter, &val); } -/** - * gdk_pixbuf_frame_copy: - * @src: a #GdkPixbufFrame to copy - * - * Copies a #GdkPixbufFrame. Free the result - * with gdk_pixbuf_frame_free(). - * - * Return value: a new #GdkPixbufFrame - **/ -GdkPixbufFrame* -gdk_pixbuf_frame_copy (GdkPixbufFrame *src) + + +static void gdk_pixbuf_non_anim_class_init (GdkPixbufNonAnimClass *klass); +static void gdk_pixbuf_non_anim_finalize (GObject *object); + +static gboolean gdk_pixbuf_non_anim_is_static_image (GdkPixbufAnimation *animation); +static GdkPixbuf* gdk_pixbuf_non_anim_get_static_image (GdkPixbufAnimation *animation); +static void gdk_pixbuf_non_anim_get_size (GdkPixbufAnimation *anim, + int *width, + int *height); +static GdkPixbufAnimationIter* gdk_pixbuf_non_anim_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time); + + + + + +static gpointer non_parent_class; + +GType +gdk_pixbuf_non_anim_get_type (void) { - GdkPixbufFrame *frame; - - frame = g_new (GdkPixbufFrame, 1); - frame->pixbuf = src->pixbuf; - g_object_ref (G_OBJECT (frame->pixbuf)); - frame->x_offset = src->x_offset; - frame->y_offset = src->y_offset; - frame->delay_time = src->delay_time; - frame->action = src->action; - - return frame; + static GType object_type = 0; + + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufNonAnimClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_pixbuf_non_anim_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufNonAnim), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION, + "GdkPixbufNonAnim", + &object_info, 0); + } + + return object_type; } -/** - * gdk_pixbuf_frame_free: - * @frame: a #GdkPixbufFrame - * - * Frees a #GdkPixbufFrame. Don't do this with frames you got from - * #GdkPixbufAnimation, usually the animation owns those (it doesn't - * make a copy before returning the frame). - **/ -void -gdk_pixbuf_frame_free (GdkPixbufFrame *frame) +static void +gdk_pixbuf_non_anim_class_init (GdkPixbufNonAnimClass *klass) { - g_return_if_fail (frame != NULL); + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass); + + non_parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_pixbuf_non_anim_finalize; + + anim_class->is_static_image = gdk_pixbuf_non_anim_is_static_image; + anim_class->get_static_image = gdk_pixbuf_non_anim_get_static_image; + anim_class->get_size = gdk_pixbuf_non_anim_get_size; + anim_class->get_iter = gdk_pixbuf_non_anim_get_iter; +} + +static void +gdk_pixbuf_non_anim_finalize (GObject *object) +{ + GdkPixbufNonAnim *non_anim = GDK_PIXBUF_NON_ANIM (object); + + if (non_anim->pixbuf) + g_object_unref (G_OBJECT (non_anim->pixbuf)); + + G_OBJECT_CLASS (non_parent_class)->finalize (object); +} + +GdkPixbufAnimation* +gdk_pixbuf_non_anim_new (GdkPixbuf *pixbuf) +{ + GdkPixbufNonAnim *non_anim; + + non_anim = g_object_new (GDK_TYPE_PIXBUF_NON_ANIM, NULL); + + non_anim->pixbuf = pixbuf; + + if (pixbuf) + g_object_ref (G_OBJECT (pixbuf)); - g_object_unref (G_OBJECT (frame->pixbuf)); - g_free (frame); + return GDK_PIXBUF_ANIMATION (non_anim); } +static gboolean +gdk_pixbuf_non_anim_is_static_image (GdkPixbufAnimation *animation) +{ + + return TRUE; +} + +static GdkPixbuf* +gdk_pixbuf_non_anim_get_static_image (GdkPixbufAnimation *animation) +{ + GdkPixbufNonAnim *non_anim; + + non_anim = GDK_PIXBUF_NON_ANIM (animation); + + return non_anim->pixbuf; +} + +static void +gdk_pixbuf_non_anim_get_size (GdkPixbufAnimation *anim, + int *width, + int *height) +{ + GdkPixbufNonAnim *non_anim; + + non_anim = GDK_PIXBUF_NON_ANIM (anim); + + if (width) + *width = gdk_pixbuf_get_width (non_anim->pixbuf); + + if (height) + *height = gdk_pixbuf_get_height (non_anim->pixbuf); +} + + +static GdkPixbufAnimationIter* +gdk_pixbuf_non_anim_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time) +{ + GdkPixbufNonAnimIter *iter; + + iter = g_object_new (GDK_TYPE_PIXBUF_NON_ANIM_ITER, NULL); + + iter->non_anim = GDK_PIXBUF_NON_ANIM (anim); + + g_object_ref (G_OBJECT (iter->non_anim)); + + return GDK_PIXBUF_ANIMATION_ITER (iter); +} + + + +static void gdk_pixbuf_non_anim_iter_class_init (GdkPixbufNonAnimIterClass *klass); +static void gdk_pixbuf_non_anim_iter_finalize (GObject *object); +static int gdk_pixbuf_non_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter); +static GdkPixbuf* gdk_pixbuf_non_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter); +static gboolean gdk_pixbuf_non_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter); +static gboolean gdk_pixbuf_non_anim_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time); + + + + + +static gpointer non_iter_parent_class; + GType -gdk_pixbuf_frame_get_type (void) +gdk_pixbuf_non_anim_iter_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufNonAnimIterClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_pixbuf_non_anim_iter_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufNonAnimIter), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER, + "GdkPixbufNonAnimIter", + &object_info, 0); + } + + return object_type; +} + +static void +gdk_pixbuf_non_anim_iter_class_init (GdkPixbufNonAnimIterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkPixbufAnimationIterClass *anim_iter_class = + GDK_PIXBUF_ANIMATION_ITER_CLASS (klass); + + non_iter_parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_pixbuf_non_anim_iter_finalize; + + anim_iter_class->get_delay_time = gdk_pixbuf_non_anim_iter_get_delay_time; + anim_iter_class->get_pixbuf = gdk_pixbuf_non_anim_iter_get_pixbuf; + anim_iter_class->on_currently_loading_frame = gdk_pixbuf_non_anim_iter_on_currently_loading_frame; + anim_iter_class->advance = gdk_pixbuf_non_anim_iter_advance; +} + +static void +gdk_pixbuf_non_anim_iter_finalize (GObject *object) { - static GType our_type = 0; + GdkPixbufNonAnimIter *iter = GDK_PIXBUF_NON_ANIM_ITER (object); - if (our_type == 0) - our_type = g_boxed_type_register_static ("GdkPixbufFrame", - NULL, - gdk_pixbuf_frame_copy, - gdk_pixbuf_frame_free, - FALSE); + g_object_unref (G_OBJECT (iter->non_anim)); + + G_OBJECT_CLASS (non_iter_parent_class)->finalize (object); +} + +static int +gdk_pixbuf_non_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter) +{ + return -1; /* show only frame forever */ +} - return our_type; +static GdkPixbuf* +gdk_pixbuf_non_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter) +{ + return GDK_PIXBUF_NON_ANIM_ITER (iter)->non_anim->pixbuf; } + + +static gboolean +gdk_pixbuf_non_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter) +{ + return TRUE; +} + +static gboolean +gdk_pixbuf_non_anim_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time) +{ + + /* Advancing never requires a refresh */ + return FALSE; +} + diff --git a/gdk-pixbuf/gdk-pixbuf-io.h b/gdk-pixbuf/gdk-pixbuf-io.h index d4cf389fdd..b66bcaf08b 100644 --- a/gdk-pixbuf/gdk-pixbuf-io.h +++ b/gdk-pixbuf/gdk-pixbuf-io.h @@ -39,16 +39,15 @@ extern "C" { -typedef void (* ModulePreparedNotifyFunc) (GdkPixbuf *pixbuf, gpointer user_data); -typedef void (* ModuleUpdatedNotifyFunc) (GdkPixbuf *pixbuf, - guint x, guint y, - guint width, guint height, - gpointer user_data); -/* Needed only for animated images. */ -typedef void (* ModuleFrameDoneNotifyFunc) (GdkPixbufFrame *frame, - gpointer user_data); -typedef void (* ModuleAnimationDoneNotifyFunc) (GdkPixbuf *pixbuf, - gpointer user_data); +typedef void (* ModulePreparedNotifyFunc) (GdkPixbuf *pixbuf, + GdkPixbufAnimation *anim, + gpointer user_data); +typedef void (* ModuleUpdatedNotifyFunc) (GdkPixbuf *pixbuf, + int x, + int y, + int width, + int height, + gpointer user_data); typedef struct _GdkPixbufModule GdkPixbufModule; struct _GdkPixbufModule { @@ -63,8 +62,6 @@ struct _GdkPixbufModule { gpointer (* begin_load) (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error); gboolean (* stop_load) (gpointer context, diff --git a/gdk-pixbuf/gdk-pixbuf-loader.c b/gdk-pixbuf/gdk-pixbuf-loader.c index 2a8d2658e4..a9148d7fd5 100644 --- a/gdk-pixbuf/gdk-pixbuf-loader.c +++ b/gdk-pixbuf/gdk-pixbuf-loader.c @@ -35,8 +35,6 @@ enum { AREA_UPDATED, AREA_PREPARED, - FRAME_DONE, - ANIMATION_DONE, CLOSED, LAST_SIGNAL }; @@ -56,7 +54,6 @@ static guint pixbuf_loader_signals[LAST_SIGNAL] = { 0 }; typedef struct { - GdkPixbuf *pixbuf; GdkPixbufAnimation *animation; gboolean closed; guchar header_buf[LOADER_HEADER_SIZE]; @@ -136,25 +133,6 @@ gdk_pixbuf_loader_class_init (GdkPixbufLoaderClass *class) G_TYPE_INT, G_TYPE_INT); - pixbuf_loader_signals[FRAME_DONE] = - g_signal_newc ("frame_done", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GdkPixbufLoaderClass, frame_done), - NULL, NULL, - gdk_pixbuf_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - GDK_TYPE_PIXBUF_FRAME); - - pixbuf_loader_signals[ANIMATION_DONE] = - g_signal_newc ("animation_done", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GdkPixbufLoaderClass, animation_done), - NULL, NULL, - gdk_pixbuf_marshal_VOID__VOID, - G_TYPE_NONE, 0); - pixbuf_loader_signals[CLOSED] = g_signal_newc ("closed", G_TYPE_FROM_CLASS (object_class), @@ -188,9 +166,6 @@ gdk_pixbuf_loader_finalize (GObject *object) if (priv->animation) gdk_pixbuf_animation_unref (priv->animation); - - if (priv->pixbuf) - gdk_pixbuf_unref (priv->pixbuf); g_free (priv); @@ -198,26 +173,30 @@ gdk_pixbuf_loader_finalize (GObject *object) } static void -gdk_pixbuf_loader_prepare (GdkPixbuf *pixbuf, - gpointer loader) +gdk_pixbuf_loader_prepare (GdkPixbuf *pixbuf, + GdkPixbufAnimation *anim, + gpointer loader) { GdkPixbufLoaderPrivate *priv = NULL; priv = GDK_PIXBUF_LOADER (loader)->priv; - gdk_pixbuf_ref (pixbuf); - g_assert (priv->pixbuf == NULL); + if (anim) + g_object_ref (G_OBJECT (anim)); + else + anim = gdk_pixbuf_non_anim_new (pixbuf); + + priv->animation = anim; - priv->pixbuf = pixbuf; g_signal_emit (G_OBJECT (loader), pixbuf_loader_signals[AREA_PREPARED], 0); } static void gdk_pixbuf_loader_update (GdkPixbuf *pixbuf, - guint x, - guint y, - guint width, - guint height, + gint x, + gint y, + gint width, + gint height, gpointer loader) { GdkPixbufLoaderPrivate *priv = NULL; @@ -229,82 +208,8 @@ gdk_pixbuf_loader_update (GdkPixbuf *pixbuf, 0, x, y, /* sanity check in here. Defend against an errant loader */ - MIN (width, gdk_pixbuf_get_width (priv->pixbuf)), - MIN (height, gdk_pixbuf_get_height (priv->pixbuf))); -} - -static void -gdk_pixbuf_loader_frame_done (GdkPixbufFrame *frame, - gpointer loader) -{ - GdkPixbufLoaderPrivate *priv = NULL; - - priv = GDK_PIXBUF_LOADER (loader)->priv; - - priv->pixbuf = NULL; - - if (priv->animation == NULL) - { - priv->animation = g_object_new (GDK_TYPE_PIXBUF_ANIMATION, NULL); - - priv->animation->n_frames = 0; - priv->animation->width = gdk_pixbuf_get_width (frame->pixbuf) + frame->x_offset; - priv->animation->height = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset; - } - else - { - int w, h; - - /* update bbox size */ - w = gdk_pixbuf_get_width (frame->pixbuf) + frame->x_offset; - h = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset; - - if (w > priv->animation->width) { - priv->animation->width = w; - } - if (h > priv->animation->height) { - priv->animation->height = h; - } - } - - priv->animation->frames = g_list_append (priv->animation->frames, frame); - priv->animation->n_frames++; - g_signal_emit (G_OBJECT (loader), - pixbuf_loader_signals[FRAME_DONE], - 0, - frame); -} - -static void -gdk_pixbuf_loader_animation_done (GdkPixbuf *pixbuf, - gpointer loader) -{ - GdkPixbufLoaderPrivate *priv = NULL; - GdkPixbufFrame *frame; - GList *current = NULL; - gint h, w; - - priv = GDK_PIXBUF_LOADER (loader)->priv; - priv->pixbuf = NULL; - - current = gdk_pixbuf_animation_get_frames (priv->animation); - - while (current) - { - frame = (GdkPixbufFrame *) current->data; - - /* update bbox size */ - w = gdk_pixbuf_get_width (frame->pixbuf) + frame->x_offset; - h = gdk_pixbuf_get_height (frame->pixbuf) + frame->y_offset; - - if (w > priv->animation->width) - priv->animation->width = w; - if (h > priv->animation->height) - priv->animation->height = h; - current = current->next; - } - - g_signal_emit (G_OBJECT (loader), pixbuf_loader_signals[ANIMATION_DONE], 0); + MIN (width, gdk_pixbuf_animation_get_width (priv->animation)), + MIN (height, gdk_pixbuf_animation_get_height (priv->animation))); } static gint @@ -353,8 +258,6 @@ gdk_pixbuf_loader_load_module (GdkPixbufLoader *loader, priv->context = priv->image_module->begin_load (gdk_pixbuf_loader_prepare, gdk_pixbuf_loader_update, - gdk_pixbuf_loader_frame_done, - gdk_pixbuf_loader_animation_done, loader, error); @@ -450,7 +353,7 @@ gdk_pixbuf_loader_write (GdkPixbufLoader *loader, { gint eaten; - eaten = gdk_pixbuf_loader_eat_header_write(loader, buf, count, error); + eaten = gdk_pixbuf_loader_eat_header_write (loader, buf, count, error); if (eaten <= 0) return FALSE; @@ -540,13 +443,14 @@ gdk_pixbuf_loader_new_with_type (const char *image_type, * "area_prepared" signal has been emitted by the loader; this means * that enough data has been read to know the size of the image that * will be allocated. If the loader has not received enough data via - * gdk_pixbuf_loader_write(), then this function returns NULL. The + * gdk_pixbuf_loader_write(), then this function returns %NULL. The * returned pixbuf will be the same in all future calls to the loader, * so simply calling gdk_pixbuf_ref() should be sufficient to continue * using it. Additionally, if the loader is an animation, it will - * return the first frame of the animation. + * return the "static image" of the animation + * (see gdk_pixbuf_animation_get_static_image()). * - * Return value: The GdkPixbuf that the loader is creating, or NULL if not + * Return value: The #GdkPixbuf that the loader is creating, or %NULL if not * enough data has been read to determine how to create the image buffer. **/ GdkPixbuf * @@ -560,19 +464,9 @@ gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader) priv = loader->priv; if (priv->animation) - { - GList *list; - - list = gdk_pixbuf_animation_get_frames (priv->animation); - if (list != NULL) - { - GdkPixbufFrame *frame = list->data; - - return gdk_pixbuf_frame_get_pixbuf (frame); - } - } - - return priv->pixbuf; + return gdk_pixbuf_animation_get_static_image (priv->animation); + else + return NULL; } /** @@ -581,8 +475,9 @@ gdk_pixbuf_loader_get_pixbuf (GdkPixbufLoader *loader) * * Queries the GdkPixbufAnimation that a pixbuf loader is currently creating. * In general it only makes sense to call this function afer the "area_prepared" - * signal has been emitted by the loader. If the image is not an animation, - * then it will return NULL. + * signal has been emitted by the loader. If the loader doesn't have enough + * bytes yet (hasn't emitted the area_prepared signal) this function will return + * %NULL. * * Return value: The GdkPixbufAnimation that the loader is loading, or NULL if not enough data has been read to determine the information. diff --git a/gdk-pixbuf/gdk-pixbuf-loader.h b/gdk-pixbuf/gdk-pixbuf-loader.h index 9c47478a22..eea72f30d8 100644 --- a/gdk-pixbuf/gdk-pixbuf-loader.h +++ b/gdk-pixbuf/gdk-pixbuf-loader.h @@ -51,21 +51,21 @@ typedef struct _GdkPixbufLoaderClass GdkPixbufLoaderClass; struct _GdkPixbufLoaderClass { GObjectClass parent_class; - - void (*area_prepared) (GdkPixbufLoader *loader); - void (*area_updated) (GdkPixbufLoader *loader, - guint x, - guint y, - guint width, - guint height); - void (*frame_done) (GdkPixbufLoader *loader, - GdkPixbufFrame *frame); - void (*animation_done) (GdkPixbufLoader *loader); - void (*closed) (GdkPixbufLoader *loader); + + void (*area_prepared) (GdkPixbufLoader *loader); + + /* Last known frame needs a redraw for x, y, width, height */ + void (*area_updated) (GdkPixbufLoader *loader, + int x, + int y, + int width, + int height); + + void (*closed) (GdkPixbufLoader *loader); }; -GType gdk_pixbuf_loader_get_type (void) G_GNUC_CONST; +GType gdk_pixbuf_loader_get_type (void) G_GNUC_CONST; GdkPixbufLoader * gdk_pixbuf_loader_new (void); GdkPixbufLoader * gdk_pixbuf_loader_new_with_type (const char *image_type, GError **error); diff --git a/gdk-pixbuf/gdk-pixbuf-private.h b/gdk-pixbuf/gdk-pixbuf-private.h index e8eb7248a4..0bdae8715d 100644 --- a/gdk-pixbuf/gdk-pixbuf-private.h +++ b/gdk-pixbuf/gdk-pixbuf-private.h @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* GdkPixbuf library - Private declarations * * Copyright (C) 1999 The Free Software Foundation @@ -73,22 +74,6 @@ struct _GdkPixbufClass { }; -/* Private part of the GdkPixbufFrame structure */ -struct _GdkPixbufFrame { - /* The pixbuf with this frame's image data */ - GdkPixbuf *pixbuf; - - /* Offsets for overlaying onto the animation's area */ - int x_offset; - int y_offset; - - /* Frame duration in ms */ - int delay_time; - - /* Overlay mode */ - GdkPixbufFrameAction action; -}; - typedef struct _GdkPixbufAnimationClass GdkPixbufAnimationClass; #define GDK_PIXBUF_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_ANIMATION, GdkPixbufAnimationClass)) @@ -99,22 +84,50 @@ typedef struct _GdkPixbufAnimationClass GdkPixbufAnimationClass; struct _GdkPixbufAnimation { GObject parent_instance; - /* Number of frames */ - int n_frames; - - /* List of GdkPixbufFrame structures */ - GList *frames; - - /* bounding box size */ - int width, height; }; struct _GdkPixbufAnimationClass { GObjectClass parent_class; + gboolean (*is_static_image) (GdkPixbufAnimation *anim); + + GdkPixbuf* (*get_static_image) (GdkPixbufAnimation *anim); + + void (*get_size) (GdkPixbufAnimation *anim, + int *width, + int *height); + + GdkPixbufAnimationIter* (*get_iter) (GdkPixbufAnimation *anim, + const GTimeVal *start_time); + +}; + + + +typedef struct _GdkPixbufAnimationIterClass GdkPixbufAnimationIterClass; + +#define GDK_PIXBUF_ANIMATION_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_ANIMATION_ITER, GdkPixbufAnimationIterClass)) +#define GDK_IS_PIXBUF_ANIMATION_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_ANIMATION_ITER)) +#define GDK_PIXBUF_ANIMATION_ITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_ANIMATION_ITER, GdkPixbufAnimationIterClass)) + +struct _GdkPixbufAnimationIter { + GObject parent_instance; }; +struct _GdkPixbufAnimationIterClass { + GObjectClass parent_class; + + int (*get_delay_time) (GdkPixbufAnimationIter *iter); + + GdkPixbuf* (*get_pixbuf) (GdkPixbufAnimationIter *iter); + + gboolean (*on_currently_loading_frame) (GdkPixbufAnimationIter *iter); + + gboolean (*advance) (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time); +}; + #define GDK_PIXBUF_INLINE_MAGIC_NUMBER 0x47646B50 /* 'GdkP' */ @@ -125,6 +138,8 @@ typedef enum GDK_PIXBUF_INLINE_RLE = 1 } GdkPixbufInlineFormat; + +GdkPixbufAnimation* gdk_pixbuf_non_anim_new (GdkPixbuf *pixbuf); #endif diff --git a/gdk-pixbuf/gdk-pixbuf-util.c b/gdk-pixbuf/gdk-pixbuf-util.c index bee4fb5bb7..0f869b9471 100644 --- a/gdk-pixbuf/gdk-pixbuf-util.c +++ b/gdk-pixbuf/gdk-pixbuf-util.c @@ -121,6 +121,8 @@ gdk_pixbuf_copy_area (const GdkPixbuf *src_pixbuf, g_return_if_fail (dest_x >= 0 && dest_x + width <= dest_pixbuf->width); g_return_if_fail (dest_y >= 0 && dest_y + height <= dest_pixbuf->height); + g_return_if_fail (!(gdk_pixbuf_get_has_alpha (src_pixbuf) && !gdk_pixbuf_get_has_alpha (dest_pixbuf))); + /* This will perform format conversions automatically */ gdk_pixbuf_scale (src_pixbuf, diff --git a/gdk-pixbuf/gdk-pixbuf.c b/gdk-pixbuf/gdk-pixbuf.c index 12b8e61441..cfc0bb323c 100644 --- a/gdk-pixbuf/gdk-pixbuf.c +++ b/gdk-pixbuf/gdk-pixbuf.c @@ -412,7 +412,7 @@ gdk_pixbuf_error_quark (void) /** * gdk_pixbuf_fill: * @pixbuf: a #GdkPixbuf - * @pixel: RGBA pixel to clear to (0xffffff00 is opaque white, 0x000000ff transparent black) + * @pixel: RGBA pixel to clear to (0xffffffff is opaque white, 0x00000000 transparent black) * * Clears a pixbuf to the given RGBA value, converting the RGBA value into * the pixbuf's pixel format. The alpha will be ignored if the pixbuf @@ -454,7 +454,7 @@ gdk_pixbuf_fill (GdkPixbuf *pixbuf, p = pixels; end = pixels + pixbuf->rowstride * pixbuf->height; - end -= (pixbuf->rowstride - pixbuf->width); + end -= (pixbuf->rowstride - pixbuf->width * pixbuf->n_channels); while (p < end) { *p++ = r; diff --git a/gdk-pixbuf/gdk-pixbuf.h b/gdk-pixbuf/gdk-pixbuf.h index b8b5a03af0..46ff3533ce 100644 --- a/gdk-pixbuf/gdk-pixbuf.h +++ b/gdk-pixbuf/gdk-pixbuf.h @@ -54,6 +54,7 @@ typedef enum { /* All of these are opaque structures */ typedef struct _GdkPixbuf GdkPixbuf; typedef struct _GdkPixbufAnimation GdkPixbufAnimation; +typedef struct _GdkPixbufAnimationIter GdkPixbufAnimationIter; typedef struct _GdkPixbufFrame GdkPixbufFrame; #define GDK_TYPE_PIXBUF (gdk_pixbuf_get_type ()) @@ -64,6 +65,9 @@ typedef struct _GdkPixbufFrame GdkPixbufFrame; #define GDK_PIXBUF_ANIMATION(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_ANIMATION, GdkPixbufAnimation)) #define GDK_IS_PIXBUF_ANIMATION(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_ANIMATION)) +#define GDK_TYPE_PIXBUF_ANIMATION_ITER (gdk_pixbuf_animation_iter_get_type ()) +#define GDK_PIXBUF_ANIMATION_ITER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_ANIMATION_ITER, GdkPixbufAnimationIter)) +#define GDK_IS_PIXBUF_ANIMATION_ITER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_ANIMATION_ITER)) /* Handler that must free the pixel array */ typedef void (* GdkPixbufDestroyNotify) (guchar *pixels, gpointer data); @@ -255,13 +259,6 @@ GdkPixbuf *gdk_pixbuf_composite_color_simple (const GdkPixbuf *src, /* Animation support */ -/* GIF-like animation overlay modes for frames */ -typedef enum { - GDK_PIXBUF_FRAME_RETAIN, - GDK_PIXBUF_FRAME_DISPOSE, - GDK_PIXBUF_FRAME_REVERT -} GdkPixbufFrameAction; - GType gdk_pixbuf_animation_get_type (void) G_GNUC_CONST; GdkPixbufAnimation *gdk_pixbuf_animation_new_from_file (const char *filename, @@ -272,21 +269,20 @@ void gdk_pixbuf_animation_unref (GdkPixbufAnimation *an int gdk_pixbuf_animation_get_width (GdkPixbufAnimation *animation); int gdk_pixbuf_animation_get_height (GdkPixbufAnimation *animation); -GList *gdk_pixbuf_animation_get_frames (GdkPixbufAnimation *animation); -int gdk_pixbuf_animation_get_num_frames (GdkPixbufAnimation *animation); +gboolean gdk_pixbuf_animation_is_static_image (GdkPixbufAnimation *animation); +GdkPixbuf *gdk_pixbuf_animation_get_static_image (GdkPixbufAnimation *animation); -/* Frame accessors */ +GdkPixbufAnimationIter *gdk_pixbuf_animation_get_iter (GdkPixbufAnimation *animation, + const GTimeVal *start_time); +GType gdk_pixbuf_animation_iter_get_type (void) G_GNUC_CONST; +int gdk_pixbuf_animation_iter_get_delay_time (GdkPixbufAnimationIter *iter); +GdkPixbuf *gdk_pixbuf_animation_iter_get_pixbuf (GdkPixbufAnimationIter *iter); +gboolean gdk_pixbuf_animation_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter); +gboolean gdk_pixbuf_animation_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time); -GdkPixbuf *gdk_pixbuf_frame_get_pixbuf (GdkPixbufFrame *frame); -int gdk_pixbuf_frame_get_x_offset (GdkPixbufFrame *frame); -int gdk_pixbuf_frame_get_y_offset (GdkPixbufFrame *frame); -int gdk_pixbuf_frame_get_delay_time (GdkPixbufFrame *frame); -GdkPixbufFrameAction gdk_pixbuf_frame_get_action (GdkPixbufFrame *frame); -GdkPixbufFrame *gdk_pixbuf_frame_copy (GdkPixbufFrame *src); -void gdk_pixbuf_frame_free (GdkPixbufFrame *frame); -GType gdk_pixbuf_frame_get_type (void) G_GNUC_CONST; -#define GDK_TYPE_PIXBUF_FRAME gdk_pixbuf_frame_get_type () + #include diff --git a/gdk-pixbuf/io-bmp.c b/gdk-pixbuf/io-bmp.c index c5108bcc1d..ce112f85ac 100644 --- a/gdk-pixbuf/io-bmp.c +++ b/gdk-pixbuf/io-bmp.c @@ -174,9 +174,7 @@ struct bmp_progressive_state { static gpointer gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc - anim_done_func, gpointer user_data, + gpointer user_data, GError **error); static gboolean gdk_pixbuf__bmp_image_stop_load(gpointer data, GError **error); @@ -198,7 +196,7 @@ static GdkPixbuf *gdk_pixbuf__bmp_image_load(FILE * f, GError **error) GdkPixbuf *pb; State = - gdk_pixbuf__bmp_image_begin_load(NULL, NULL, NULL, NULL, NULL, + gdk_pixbuf__bmp_image_begin_load(NULL, NULL, NULL, error); if (State == NULL) @@ -312,7 +310,7 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH, if (State->prepared_func != NULL) /* Notify the client that we are ready to go */ - (*State->prepared_func) (State->pixbuf, State->user_data); + (*State->prepared_func) (State->pixbuf, NULL, State->user_data); } @@ -328,9 +326,7 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH, static gpointer gdk_pixbuf__bmp_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc - anim_done_func, gpointer user_data, + gpointer user_data, GError **error) { struct bmp_progressive_state *context; diff --git a/gdk-pixbuf/io-gif-animation.c b/gdk-pixbuf/io-gif-animation.c new file mode 100644 index 0000000000..fe164a0958 --- /dev/null +++ b/gdk-pixbuf/io-gif-animation.c @@ -0,0 +1,557 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* GdkPixbuf library - animated gif support + * + * Copyright (C) 1999 The Free Software Foundation + * + * Authors: Jonathan Blandford + * Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include "gdk-pixbuf-io.h" +#include "gdk-pixbuf-private.h" +#include "io-gif-animation.h" + +static void gdk_pixbuf_gif_anim_class_init (GdkPixbufGifAnimClass *klass); +static void gdk_pixbuf_gif_anim_finalize (GObject *object); + +static gboolean gdk_pixbuf_gif_anim_is_static_image (GdkPixbufAnimation *animation); +static GdkPixbuf* gdk_pixbuf_gif_anim_get_static_image (GdkPixbufAnimation *animation); + +static void gdk_pixbuf_gif_anim_get_size (GdkPixbufAnimation *anim, + int *width, + int *height); +static GdkPixbufAnimationIter* gdk_pixbuf_gif_anim_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time); + + + + +static gpointer parent_class; + +GType +gdk_pixbuf_gif_anim_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufGifAnimClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_pixbuf_gif_anim_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufGifAnim), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION, + "GdkPixbufGifAnim", + &object_info, 0); + } + + return object_type; +} + +static void +gdk_pixbuf_gif_anim_class_init (GdkPixbufGifAnimClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkPixbufAnimationClass *anim_class = GDK_PIXBUF_ANIMATION_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_pixbuf_gif_anim_finalize; + + anim_class->is_static_image = gdk_pixbuf_gif_anim_is_static_image; + anim_class->get_static_image = gdk_pixbuf_gif_anim_get_static_image; + anim_class->get_size = gdk_pixbuf_gif_anim_get_size; + anim_class->get_iter = gdk_pixbuf_gif_anim_get_iter; +} + +static void +gdk_pixbuf_gif_anim_finalize (GObject *object) +{ + GdkPixbufGifAnim *gif_anim = GDK_PIXBUF_GIF_ANIM (object); + + GList *l; + GdkPixbufFrame *frame; + + for (l = gif_anim->frames; l; l = l->next) { + frame = l->data; + gdk_pixbuf_unref (frame->pixbuf); + if (frame->composited) + gdk_pixbuf_unref (frame->composited); + if (frame->revert) + gdk_pixbuf_unref (frame->revert); + g_free (frame); + } + + g_list_free (gif_anim->frames); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gdk_pixbuf_gif_anim_is_static_image (GdkPixbufAnimation *animation) +{ + GdkPixbufGifAnim *gif_anim; + + gif_anim = GDK_PIXBUF_GIF_ANIM (animation); + + return (gif_anim->frames != NULL && + gif_anim->frames->next == NULL); +} + +static GdkPixbuf* +gdk_pixbuf_gif_anim_get_static_image (GdkPixbufAnimation *animation) +{ + GdkPixbufGifAnim *gif_anim; + + gif_anim = GDK_PIXBUF_GIF_ANIM (animation); + + if (gif_anim->frames == NULL) + return NULL; + else + return GDK_PIXBUF (((GdkPixbufFrame*)gif_anim->frames->data)->pixbuf); +} + +static void +gdk_pixbuf_gif_anim_get_size (GdkPixbufAnimation *anim, + int *width, + int *height) +{ + GdkPixbufGifAnim *gif_anim; + + gif_anim = GDK_PIXBUF_GIF_ANIM (anim); + + if (width) + *width = gif_anim->width; + + if (height) + *height = gif_anim->height; +} + + +static void +iter_clear (GdkPixbufGifAnimIter *iter) +{ + iter->current_frame = NULL; +} + +static void +iter_restart (GdkPixbufGifAnimIter *iter) +{ + iter_clear (iter); + + iter->current_frame = iter->gif_anim->frames; +} + +static GdkPixbufAnimationIter* +gdk_pixbuf_gif_anim_get_iter (GdkPixbufAnimation *anim, + const GTimeVal *start_time) +{ + GdkPixbufGifAnimIter *iter; + + iter = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM_ITER, NULL); + + iter->gif_anim = GDK_PIXBUF_GIF_ANIM (anim); + + g_object_ref (G_OBJECT (iter->gif_anim)); + + iter_restart (iter); + + iter->start_time = *start_time; + iter->current_time = *start_time; + + return GDK_PIXBUF_ANIMATION_ITER (iter); +} + + + +static void gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass *klass); +static void gdk_pixbuf_gif_anim_iter_finalize (GObject *object); + +static int gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter *iter); +static GdkPixbuf* gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter *iter); +static gboolean gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *iter); +static gboolean gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter *iter, + const GTimeVal *current_time); + + + +static gpointer iter_parent_class; + +GType +gdk_pixbuf_gif_anim_iter_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) { + static const GTypeInfo object_info = { + sizeof (GdkPixbufGifAnimIterClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) gdk_pixbuf_gif_anim_iter_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GdkPixbufGifAnimIter), + 0, /* n_preallocs */ + (GInstanceInitFunc) NULL, + }; + + object_type = g_type_register_static (GDK_TYPE_PIXBUF_ANIMATION_ITER, + "GdkPixbufGifAnimIter", + &object_info, 0); + } + + return object_type; +} + +static void +gdk_pixbuf_gif_anim_iter_class_init (GdkPixbufGifAnimIterClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GdkPixbufAnimationIterClass *anim_iter_class = + GDK_PIXBUF_ANIMATION_ITER_CLASS (klass); + + iter_parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = gdk_pixbuf_gif_anim_iter_finalize; + + anim_iter_class->get_delay_time = gdk_pixbuf_gif_anim_iter_get_delay_time; + anim_iter_class->get_pixbuf = gdk_pixbuf_gif_anim_iter_get_pixbuf; + anim_iter_class->on_currently_loading_frame = gdk_pixbuf_gif_anim_iter_on_currently_loading_frame; + anim_iter_class->advance = gdk_pixbuf_gif_anim_iter_advance; +} + +static void +gdk_pixbuf_gif_anim_iter_finalize (GObject *object) +{ + GdkPixbufGifAnimIter *iter = GDK_PIXBUF_GIF_ANIM_ITER (object); + + iter_clear (iter); + + g_object_unref (G_OBJECT (iter->gif_anim)); + + G_OBJECT_CLASS (iter_parent_class)->finalize (object); +} + +static gboolean +gdk_pixbuf_gif_anim_iter_advance (GdkPixbufAnimationIter *anim_iter, + const GTimeVal *current_time) +{ + GdkPixbufGifAnimIter *iter; + gint elapsed; + GList *tmp; + GList *old; + + iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter); + + iter->current_time = *current_time; + + /* We use milliseconds for all times */ + elapsed = + (((iter->current_time.tv_sec - iter->start_time.tv_sec) * G_USEC_PER_SEC + + iter->current_time.tv_usec - iter->start_time.tv_usec)) / 1000; + + if (elapsed < 0) { + /* Try to compensate; probably the system clock + * was set backwards + */ + iter->start_time = iter->current_time; + elapsed = 0; + } + + /* See how many times we've already played the full animation, + * and subtract time for that. + */ + elapsed = elapsed % iter->gif_anim->total_time; + + g_assert (elapsed < iter->gif_anim->total_time); + + iter->position = elapsed; + + /* Now move to the proper frame */ + tmp = iter->gif_anim->frames; + while (tmp != NULL) { + GdkPixbufFrame *frame = tmp->data; + + if (iter->position >= frame->elapsed && + iter->position < (frame->elapsed + frame->delay_time)) + break; + + tmp = tmp->next; + } + + old = iter->current_frame; + + iter->current_frame = tmp; + + return iter->current_frame != old; +} + +int +gdk_pixbuf_gif_anim_iter_get_delay_time (GdkPixbufAnimationIter *anim_iter) +{ + GdkPixbufFrame *frame; + GdkPixbufGifAnimIter *iter; + + iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter); + + if (iter->current_frame) { + frame = iter->current_frame->data; + +#if 0 + g_print ("frame start: %d pos: %d frame len: %d frame remaining: %d\n", + frame->elapsed, + iter->position, + frame->delay_time, + frame->delay_time - (iter->position - frame->elapsed)); +#endif + + return frame->delay_time - (iter->position - frame->elapsed); + } else { + return -1; /* show last frame forever */ + } +} + +void +gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim, + GdkPixbufFrame *frame) +{ + GList *link; + GList *tmp; + + link = g_list_find (gif_anim->frames, frame); + + if (frame->need_recomposite || frame->composited == NULL) { + /* For now, to composite we start with the last + * composited frame and composite everything up to + * here. + */ + + /* Rewind to last composited frame. */ + tmp = link; + while (tmp != NULL) { + GdkPixbufFrame *f = tmp->data; + + if (f->need_recomposite) { + if (f->composited) { + g_object_unref (G_OBJECT (f->composited)); + f->composited = NULL; + } + } + + if (f->composited != NULL) + break; + + tmp = tmp->prev; + } + + /* Go forward, compositing all frames up to the current frame */ + if (tmp == NULL) + tmp = gif_anim->frames; + + while (tmp != NULL) { + GdkPixbufFrame *f = tmp->data; + + if (f->need_recomposite) { + if (f->composited) { + g_object_unref (G_OBJECT (f->composited)); + f->composited = NULL; + } + } + + if (f->composited != NULL) + goto next; + + if (tmp->prev == NULL) { + /* First frame may be smaller than the whole image; + * if so, we make the area outside it full alpha if the + * image has alpha, and background color otherwise. + * GIF spec doesn't actually say what to do about this. + */ + f->composited = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, + 8, gif_anim->width, gif_anim->height); + + /* alpha gets dumped if f->composited has no alpha */ + + gdk_pixbuf_fill (f->composited, + (gif_anim->bg_red << 24) | + (gif_anim->bg_green << 16) | + (gif_anim->bg_blue << 8) | + (f->bg_transparent ? 0 : 255)); + + 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 (f->action == GDK_PIXBUF_FRAME_REVERT) + g_warning ("First frame of GIF has bad dispose mode, GIF loader should not have loaded this image"); + + f->need_recomposite = FALSE; + } else { + GdkPixbufFrame *prev_frame; + + prev_frame = tmp->prev->data; + + /* Init f->composited with what we should have after the previous + * frame + */ + + if (prev_frame->action == GDK_PIXBUF_FRAME_RETAIN) { + 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); + + /* 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) | + prev_frame->bg_transparent ? 0 : 255); + + g_object_unref (G_OBJECT (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); + } 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 (G_OBJECT (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); + + f->need_recomposite = FALSE; + } + + next: + if (tmp == link) + break; + + tmp = tmp->next; + } + } + + g_assert (frame->composited != NULL); + g_assert (gdk_pixbuf_get_width (frame->composited) == gif_anim->width); + g_assert (gdk_pixbuf_get_height (frame->composited) == gif_anim->height); +} + +GdkPixbuf* +gdk_pixbuf_gif_anim_iter_get_pixbuf (GdkPixbufAnimationIter *anim_iter) +{ + GdkPixbufGifAnimIter *iter; + GdkPixbufFrame *frame; + + iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter); + + frame = iter->current_frame ? iter->current_frame->data : NULL; + +#if 0 + if (FALSE && frame) + g_print ("current frame %d dispose mode %d %d x %d\n", + g_list_index (iter->gif_anim->frames, + frame), + frame->action, + gdk_pixbuf_get_width (frame->pixbuf), + gdk_pixbuf_get_height (frame->pixbuf)); +#endif + + if (frame == NULL) + return NULL; + + gdk_pixbuf_gif_anim_frame_composite (iter->gif_anim, frame); + + return frame->composited; +} + +static gboolean +gdk_pixbuf_gif_anim_iter_on_currently_loading_frame (GdkPixbufAnimationIter *anim_iter) +{ + GdkPixbufGifAnimIter *iter; + + iter = GDK_PIXBUF_GIF_ANIM_ITER (anim_iter); + + return iter->current_frame == NULL || iter->current_frame->next == NULL; +} diff --git a/gdk-pixbuf/io-gif-animation.h b/gdk-pixbuf/io-gif-animation.h new file mode 100644 index 0000000000..ce1ebaa6f9 --- /dev/null +++ b/gdk-pixbuf/io-gif-animation.h @@ -0,0 +1,169 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ +/* GdkPixbuf library - GIF loader declarations + * + * Copyright (C) 1999 The Free Software Foundation + * + * Authors: Mark Crichton + * Miguel de Icaza + * Federico Mena-Quintero + * Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef GDK_PIXBUF_GIF_H +#define GDK_PIXBUF_GIF_H + +#include "gdk-pixbuf-private.h" + +typedef enum { + /* Keep this frame and composite next frame over it */ + /* (GIF disposal method 1) */ + GDK_PIXBUF_FRAME_RETAIN, + /* Revert to background color before compositing next frame */ + /* (GIF disposal method 2) */ + GDK_PIXBUF_FRAME_DISPOSE, + /* Revert to previously-displayed composite image after + * displaying this frame + */ + /* (GIF disposal method 3) */ + GDK_PIXBUF_FRAME_REVERT +} GdkPixbufFrameAction; + + + +typedef struct _GdkPixbufGifAnim GdkPixbufGifAnim; +typedef struct _GdkPixbufGifAnimClass GdkPixbufGifAnimClass; + +#define GDK_TYPE_PIXBUF_GIF_ANIM (gdk_pixbuf_gif_anim_get_type ()) +#define GDK_PIXBUF_GIF_ANIM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_GIF_ANIM, GdkPixbufGifAnim)) +#define GDK_IS_PIXBUF_GIF_ANIM(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_GIF_ANIM)) + +#define GDK_PIXBUF_GIF_ANIM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_GIF_ANIM, GdkPixbufGifAnimClass)) +#define GDK_IS_PIXBUF_GIF_ANIM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_GIF_ANIM)) +#define GDK_PIXBUF_GIF_ANIM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_GIF_ANIM, GdkPixbufGifAnimClass)) + +/* Private part of the GdkPixbufGifAnim structure */ +struct _GdkPixbufGifAnim { + GdkPixbufAnimation parent_instance; + + /* Number of frames */ + int n_frames; + + /* Total length of animation */ + int total_time; + + /* List of GdkPixbufFrame structures */ + GList *frames; + + /* bounding box size */ + int width, height; + + guchar bg_red; + guchar bg_green; + guchar bg_blue; +}; + +struct _GdkPixbufGifAnimClass { + GdkPixbufAnimationClass parent_class; + +}; + +GType gdk_pixbuf_gif_anim_get_type (void) G_GNUC_CONST; + + + +typedef struct _GdkPixbufGifAnimIter GdkPixbufGifAnimIter; +typedef struct _GdkPixbufGifAnimIterClass GdkPixbufGifAnimIterClass; + + +#define GDK_TYPE_PIXBUF_GIF_ANIM_ITER (gdk_pixbuf_gif_anim_iter_get_type ()) +#define GDK_PIXBUF_GIF_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_PIXBUF_GIF_ANIM_ITER, GdkPixbufGifAnimIter)) +#define GDK_IS_PIXBUF_GIF_ANIM_ITER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_PIXBUF_GIF_ANIM_ITER)) + +#define GDK_PIXBUF_GIF_ANIM_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_PIXBUF_GIF_ANIM_ITER, GdkPixbufGifAnimIterClass)) +#define GDK_IS_PIXBUF_GIF_ANIM_ITER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_PIXBUF_GIF_ANIM_ITER)) +#define GDK_PIXBUF_GIF_ANIM_ITER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_PIXBUF_GIF_ANIM_ITER, GdkPixbufGifAnimIterClass)) + +struct _GdkPixbufGifAnimIter { + GdkPixbufAnimationIter parent_instance; + + GdkPixbufGifAnim *gif_anim; + + GTimeVal start_time; + GTimeVal current_time; + + /* Time in milliseconds into this run of the animation */ + gint position; + + GList *current_frame; +}; + +struct _GdkPixbufGifAnimIterClass { + GdkPixbufAnimationIterClass parent_class; + +}; + +GType gdk_pixbuf_gif_anim_iter_get_type (void) G_GNUC_CONST; + + + +struct _GdkPixbufFrame { + /* The pixbuf with this frame's image data */ + GdkPixbuf *pixbuf; + + /* Offsets for overlaying onto the GIF graphic area */ + int x_offset; + int y_offset; + + /* Frame duration in ms */ + int delay_time; + + /* Sum of preceding delay times */ + int elapsed; + + /* Overlay mode */ + GdkPixbufFrameAction action; + + /* TRUE if the pixbuf has been modified since + * the last frame composite operation + */ + gboolean need_recomposite; + + /* TRUE if the background for this frame is transparent */ + gboolean bg_transparent; + + /* The below reflects the "use hell of a lot of RAM" + * philosophy of coding + */ + + /* Cached composite image (the image you actually display + * for this frame) + */ + GdkPixbuf *composited; + + /* Cached revert image (the contents of the area + * covered by the frame prior to compositing; + * same size as pixbuf, not as the composite image; only + * used for FRAME_REVERT frames) + */ + GdkPixbuf *revert; +}; + +void gdk_pixbuf_gif_anim_frame_composite (GdkPixbufGifAnim *gif_anim, + GdkPixbufFrame *frame); + +#endif diff --git a/gdk-pixbuf/io-gif.c b/gdk-pixbuf/io-gif.c index 5be68023fc..c3c1b8728c 100644 --- a/gdk-pixbuf/io-gif.c +++ b/gdk-pixbuf/io-gif.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */ /* GdkPixbuf library - GIF image loader * * Copyright (C) 1999 Mark Crichton @@ -30,10 +31,6 @@ * in it got a bit messy. Basicly, every function is written to expect a failed * read_gif, and lets you call it again assuming that the bytes are there. * - * A note on Animations: - * Currently, it doesn't correctly read the different colormap per frame. This - * needs implementing sometime. - * * Return vals. * Unless otherwise specified, these are the return vals for most functions: * @@ -59,11 +56,15 @@ #include #include #include +#include #include "gdk-pixbuf-private.h" #include "gdk-pixbuf-io.h" +#include "io-gif-animation.h" +#undef DUMP_IMAGE_DETAILS + #define MAXCOLORMAPSIZE 256 #define MAX_LZW_BITS 12 @@ -88,7 +89,7 @@ enum { GIF_LZW_FILL_BUFFER, GIF_LZW_CLEAR_CODE, GIF_GET_LZW, - GIF_DONE, + GIF_DONE }; @@ -107,19 +108,27 @@ struct _GifContext int state; /* really only relevant for progressive loading */ unsigned int width; unsigned int height; - CMap color_map; - CMap frame_color_map; - unsigned int bit_pixel; - unsigned int color_resolution; - unsigned int background; + + 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 frame_cmap_active; + CMap frame_color_map; + gint frame_colormap_size; + unsigned int frame_bit_pixel; + unsigned int aspect_ratio; GdkPixbuf *pixbuf; - GdkPixbufAnimation *animation; + GdkPixbufGifAnim *animation; GdkPixbufFrame *frame; Gif89 gif89; - /* stuff per frame. As we only support the first one, not so - * relevant. But still needed */ + /* stuff per frame. */ int frame_len; int frame_height; int frame_interlace; @@ -132,18 +141,12 @@ struct _GifContext /* progressive read, only. */ ModulePreparedNotifyFunc prepare_func; ModuleUpdatedNotifyFunc update_func; - ModuleFrameDoneNotifyFunc frame_done_func; - ModuleAnimationDoneNotifyFunc anim_done_func; gpointer user_data; guchar *buf; guint ptr; guint size; guint amount_needed; - /* colormap context */ - gint colormap_index; - gint colormap_flag; - /* extension context */ guchar extension_label; guchar extension_flag; @@ -207,7 +210,14 @@ gif_read (GifContext *context, guchar *buffer, size_t len) count += len; g_print ("Fsize :%d\tcount :%d\t", len, count); #endif - retval = (fread(buffer, len, 1, context->file) != 0); + retval = (fread(buffer, len, 1, context->file) != 0); + + if (!retval && ferror (context->file)) + g_set_error (context->error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + _("Failure reading GIF: %s"), strerror (errno)); + #ifdef IO_GIFDEBUG if (len < 100) { for (i = 0; i < len; i++) @@ -247,16 +257,14 @@ gif_read (GifContext *context, guchar *buffer, size_t len) static void gif_set_get_colormap (GifContext *context) { - context->colormap_flag = TRUE; - context->colormap_index = 0; + context->global_colormap_size = 0; context->state = GIF_GET_COLORMAP; } static void gif_set_get_colormap2 (GifContext *context) { - context->colormap_flag = TRUE; - context->colormap_index = 0; + context->frame_colormap_size = 0; context->state = GIF_GET_COLORMAP2; } @@ -265,18 +273,43 @@ gif_get_colormap (GifContext *context) { unsigned char rgb[3]; - while (context->colormap_index < context->bit_pixel) { + while (context->global_colormap_size < context->global_bit_pixel) { + if (!gif_read (context, rgb, sizeof (rgb))) { + 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->global_colormap_size ++; + } + + return 0; +} + + +static gint +gif_get_colormap2 (GifContext *context) +{ + unsigned char rgb[3]; + + while (context->frame_colormap_size < context->frame_bit_pixel) { if (!gif_read (context, rgb, sizeof (rgb))) { - /*g_message (_("GIF: bad colormap\n"));*/ return -1; } - context->color_map[0][context->colormap_index] = rgb[0]; - context->color_map[1][context->colormap_index] = rgb[1]; - context->color_map[2][context->colormap_index] = rgb[2]; + context->frame_color_map[0][context->frame_colormap_size] = rgb[0]; + context->frame_color_map[1][context->frame_colormap_size] = rgb[1]; + context->frame_color_map[2][context->frame_colormap_size] = rgb[2]; - context->colormap_flag &= (rgb[0] == rgb[1] && rgb[1] == rgb[2]); - context->colormap_index ++; + context->frame_colormap_size ++; } return 0; @@ -348,9 +381,11 @@ gif_get_extension (GifContext *context) retval = get_data_block (context, (unsigned char *) context->block_buf, NULL); if (retval != 0) return retval; - if (context->pixbuf == NULL) { + + if (context->frame == NULL) { /* I only want to set the transparency if I haven't - * created the pixbuf yet. */ + * created the frame yet. + */ context->gif89.disposal = (context->block_buf[0] >> 2) & 0x7; context->gif89.input_flag = (context->block_buf[0] >> 1) & 0x1; context->gif89.delay_time = LM_to_uint (context->block_buf[1], context->block_buf[2]); @@ -623,18 +658,24 @@ 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->pixbuf) + context->draw_xpos * 4; - *pixel = context->color_map [0][(guchar) v]; - *(pixel+1) = context->color_map [1][(guchar) v]; - *(pixel+2) = context->color_map [2][(guchar) v]; - *(pixel+3) = (guchar) ((v == context->gif89.transparent) ? 0 : 65535); + 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->pixbuf) + context->draw_xpos * 3; - *pixel = context->color_map [0][(guchar) v]; - *(pixel+1) = context->color_map [1][(guchar) v]; - *(pixel+2) = context->color_map [2][(guchar) v]; + 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]; } } @@ -682,80 +723,123 @@ gif_get_lzw (GifContext *context) gint first_pass; /* bounds for emitting the area_updated signal */ gint v; - if (context->pixbuf == NULL) { - context->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, - context->gif89.transparent != -1, - 8, - context->frame_len, - context->frame_height); - - if (context->prepare_func) - (* context->prepare_func) (context->pixbuf, context->user_data); - if (context->animation || context->frame_done_func || context->anim_done_func) { - context->frame = g_new (GdkPixbufFrame, 1); - context->frame->x_offset = context->x_offset; - context->frame->y_offset = context->y_offset;; - context->frame->delay_time = context->gif89.delay_time; - switch (context->gif89.disposal) { - case 0: - case 1: - context->frame->action = GDK_PIXBUF_FRAME_RETAIN; - break; - case 2: - context->frame->action = GDK_PIXBUF_FRAME_DISPOSE; - break; - case 3: - context->frame->action = GDK_PIXBUF_FRAME_REVERT; - break; - default: - context->frame->action = GDK_PIXBUF_FRAME_RETAIN; - break; - } - context->frame->pixbuf = context->pixbuf; - if (context->animation) { - int w,h; - context->animation->n_frames ++; - context->animation->frames = g_list_append (context->animation->frames, context->frame); - w = gdk_pixbuf_get_width (context->pixbuf); - h = gdk_pixbuf_get_height (context->pixbuf); - if (w > context->animation->width) - context->animation->width = w; - if (h > context->animation->height) - context->animation->height = h; - } - } - } - dest = gdk_pixbuf_get_pixels (context->pixbuf); + if (context->frame == NULL) { + context->frame = g_new (GdkPixbufFrame, 1); + + context->frame->composited = NULL; + context->frame->revert = NULL; + + context->frame->pixbuf = + gdk_pixbuf_new (GDK_COLORSPACE_RGB, + TRUE, + 8, + context->frame_len, + context->frame_height); + + context->frame->x_offset = context->x_offset; + context->frame->y_offset = context->y_offset; + context->frame->need_recomposite = TRUE; + + /* GIF delay is in hundredths, we want thousandths */ + context->frame->delay_time = context->gif89.delay_time * 10; + context->frame->elapsed = context->animation->total_time; + context->animation->total_time += context->frame->delay_time; + + switch (context->gif89.disposal) { + case 0: + case 1: + context->frame->action = GDK_PIXBUF_FRAME_RETAIN; + break; + case 2: + context->frame->action = GDK_PIXBUF_FRAME_DISPOSE; + break; + case 3: + context->frame->action = GDK_PIXBUF_FRAME_REVERT; + break; + default: + context->frame->action = GDK_PIXBUF_FRAME_RETAIN; + break; + } + + 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) + context->animation->width = w; + if (h > context->animation->height) + context->animation->height = h; + } + + /* Only call prepare_func for the first frame */ + if (context->animation->frames->next == NULL) { + 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; + + link = g_list_find (context->animation->frames, context->frame); + + prev_frame = link->prev->data; + + 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); + } + } + + dest = gdk_pixbuf_get_pixels (context->frame->pixbuf); bound_flag = FALSE; lower_bound = upper_bound = context->draw_ypos; first_pass = context->draw_pass; while (TRUE) { + guchar (*cmap)[MAXCOLORMAPSIZE]; + + if (context->frame_cmap_active) + cmap = context->frame_color_map; + else + cmap = context->global_color_map; + v = lzw_read_byte (context); if (v < 0) { goto finished_data; } bound_flag = TRUE; - if (context->gif89.transparent != -1) { - temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 4; - *temp = context->color_map [0][(guchar) v]; - *(temp+1) = context->color_map [1][(guchar) v]; - *(temp+2) = context->color_map [2][(guchar) v]; - *(temp+3) = (guchar) ((v == context->gif89.transparent) ? 0 : -1); - } else { - temp = dest + context->draw_ypos * gdk_pixbuf_get_rowstride (context->pixbuf) + context->draw_xpos * 3; - *temp = context->color_map [0][(guchar) v]; - *(temp+1) = context->color_map [1][(guchar) v]; - *(temp+2) = context->color_map [2][(guchar) v]; - } + g_assert (gdk_pixbuf_get_has_alpha (context->frame->pixbuf)); + + 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) { @@ -796,7 +880,7 @@ gif_get_lzw (GifContext *context) lower_bound = 0; upper_bound = context->frame_height; } else { - + } } else upper_bound = context->draw_ypos; @@ -804,56 +888,60 @@ gif_get_lzw (GifContext *context) if (context->draw_ypos >= context->frame_height) break; } + done: - /* we got enough data. there may be more (ie, newer layers) but we can quit now */ - if (context->animation || context->frame_done_func || context->anim_done_func) { - context->state = GIF_GET_NEXT_STEP; - } else - context->state = GIF_DONE; - v = 0; + + context->state = GIF_GET_NEXT_STEP; + + v = 0; + 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) { (* context->update_func) - (context->pixbuf, + (context->frame->pixbuf, 0, lower_bound, - gdk_pixbuf_get_width (context->pixbuf), + gdk_pixbuf_get_width (context->frame->pixbuf), upper_bound - lower_bound, context->user_data); } else { if (lower_bound <= upper_bound) { (* context->update_func) - (context->pixbuf, - 0, 0, - gdk_pixbuf_get_width (context->pixbuf), - gdk_pixbuf_get_height (context->pixbuf), + (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); } else { (* context->update_func) - (context->pixbuf, - 0, 0, - gdk_pixbuf_get_width (context->pixbuf), + (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->pixbuf, - 0, lower_bound, - gdk_pixbuf_get_width (context->pixbuf), - gdk_pixbuf_get_height (context->pixbuf), + (context->frame->pixbuf, + context->frame->x_offset, + lower_bound + context->frame->y_offset, + gdk_pixbuf_get_width (context->frame->pixbuf), + gdk_pixbuf_get_height (context->frame->pixbuf), context->user_data); } } } - if ((context->animation || context->frame_done_func || context->anim_done_func) - && context->state == GIF_GET_NEXT_STEP) { - if (context->frame_done_func) - (* context->frame_done_func) (context->frame, - context->user_data); - if (context->frame_done_func) - gdk_pixbuf_unref (context->pixbuf); - context->pixbuf = NULL; + if (context->state == GIF_GET_NEXT_STEP) { + /* 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; } return v; @@ -943,16 +1031,36 @@ gif_init (GifContext *context) context->width = LM_to_uint (buf[0], buf[1]); context->height = LM_to_uint (buf[2], buf[3]); - context->bit_pixel = 2 << (buf[4] & 0x07); - context->color_resolution = (((buf[4] & 0x70) >> 3) + 1); - context->background = buf[5]; + /* The 4th byte is + * high bit: whether to use the background index + * next 3: color resolution + * next: whether colormap is sorted by priority of allocation + * last 3: size of colormap + */ + context->global_bit_pixel = 2 << (buf[4] & 0x07); + context->global_color_resolution = (((buf[4] & 0x70) >> 3) + 1); + context->has_global_cmap = (buf[4] & 0x80) != 0; + context->background_index = buf[5]; context->aspect_ratio = buf[6]; - if (BitSet (buf[4], LOCALCOLORMAP)) { + /* 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; + + if (context->has_global_cmap) { gif_set_get_colormap (context); } else { context->state = GIF_GET_NEXT_STEP; } + +#ifdef DUMP_IMAGE_DETAILS + g_print (">Image width: %d height: %d global_cmap: %d background: %d\n", + context->width, context->height, context->has_global_cmap, context->background_index); +#endif + return 0; } @@ -966,38 +1074,81 @@ static gint gif_get_frame_info (GifContext *context) { unsigned char buf[9]; + 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]); 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->height) { - /* we don't want to resize things. So we exit */ + 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 + * dispose mode. + */ + context->state = GIF_DONE; g_set_error (context->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_CORRUPT_IMAGE, - _("GIF animation contained a frame with an incorrect size")); + _("First frame of GIF image had 'revert to previous' as its disposal mode.")); return -2; } +#ifdef DUMP_IMAGE_DETAILS + g_print (">width: %d height: %d xoffset: %d yoffset: %d disposal: %d delay: %d transparent: %d\n", + context->frame_len, context->frame_height, context->x_offset, context->y_offset, + context->gif89.disposal, context->gif89.delay_time, context->gif89.transparent); +#endif + context->frame_interlace = BitSet (buf[8], INTERLACE); if (BitSet (buf[8], LOCALCOLORMAP)) { + +#ifdef DUMP_IMAGE_DETAILS + g_print (">has local colormap\n"); +#endif + /* Does this frame have it's own colormap. */ /* really only relevant when looking at the first frame * of an animated gif. */ /* if it does, we need to re-read in the colormap, * the gray_scale, and the bit_pixel */ - context->bit_pixel = 1 << ((buf[8] & 0x07) + 1); + context->frame_cmap_active = TRUE; + context->frame_bit_pixel = 1 << ((buf[8] & 0x07) + 1); gif_set_get_colormap2 (context); return 0; } + + if (!context->has_global_cmap) { + context->state = GIF_DONE; + + g_set_error (context->error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("GIF image has no global colormap, and a frame inside it has no local colormap.")); + + return -2; + } + gif_set_prepare_lzw (context); return 0; @@ -1037,6 +1188,8 @@ gif_get_next_step (GifContext *context) } +#define LOG(x) + static gint gif_main_loop (GifContext *context) { @@ -1045,52 +1198,63 @@ gif_main_loop (GifContext *context) do { switch (context->state) { case GIF_START: + LOG("start\n"); retval = gif_init (context); break; case GIF_GET_COLORMAP: + LOG("get_colormap\n"); retval = gif_get_colormap (context); if (retval == 0) context->state = GIF_GET_NEXT_STEP; break; case GIF_GET_NEXT_STEP: + LOG("next_step\n"); retval = gif_get_next_step (context); break; case GIF_GET_FRAME_INFO: + LOG("frame_info\n"); retval = gif_get_frame_info (context); break; case GIF_GET_EXTENTION: + LOG("get_extension\n"); retval = gif_get_extension (context); if (retval == 0) context->state = GIF_GET_NEXT_STEP; break; case GIF_GET_COLORMAP2: - retval = gif_get_colormap (context); + LOG("get_colormap2\n"); + retval = gif_get_colormap2 (context); if (retval == 0) gif_set_prepare_lzw (context); break; case GIF_PREPARE_LZW: + LOG("prepare_lzw\n"); retval = gif_prepare_lzw (context); break; case GIF_LZW_FILL_BUFFER: + LOG("fill_buffer\n"); retval = gif_lzw_fill_buffer (context); break; case GIF_LZW_CLEAR_CODE: + LOG("clear_code\n"); retval = gif_lzw_clear_code (context); break; case GIF_GET_LZW: + LOG("get_lzw\n"); retval = gif_get_lzw (context); break; case GIF_DONE: + LOG("done\n"); default: retval = 0; goto done; @@ -1107,13 +1271,12 @@ new_context (void) context = g_new0 (GifContext, 1); - context->pixbuf = NULL; + context->animation = g_object_new (GDK_TYPE_PIXBUF_GIF_ANIM, NULL); + context->frame = NULL; context->file = NULL; context->state = GIF_START; context->prepare_func = NULL; context->update_func = NULL; - context->frame_done_func = NULL; - context->anim_done_func = NULL; context->user_data = NULL; context->buf = NULL; context->amount_needed = 0; @@ -1137,9 +1300,22 @@ gdk_pixbuf__gif_image_load (FILE *file, GError **error) context->file = file; context->error = error; - gif_main_loop (context); + if (gif_main_loop (context) == -1 || context->animation->frames == NULL) { + if (context->error && *(context->error) == NULL) + g_set_error (context->error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("GIF file was missing some data (perhaps it was truncated somehow?)")); + } + + pixbuf = gdk_pixbuf_animation_get_static_image (GDK_PIXBUF_ANIMATION (context->animation)); - pixbuf = context->pixbuf; + if (pixbuf) + g_object_ref (G_OBJECT (pixbuf)); + + g_object_unref (G_OBJECT (context->animation)); + + g_free (context->buf); g_free (context); return pixbuf; @@ -1148,8 +1324,6 @@ gdk_pixbuf__gif_image_load (FILE *file, GError **error) static gpointer gdk_pixbuf__gif_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -1162,8 +1336,6 @@ gdk_pixbuf__gif_image_begin_load (ModulePreparedNotifyFunc prepare_func, context->error = error; context->prepare_func = prepare_func; context->update_func = update_func; - context->frame_done_func = frame_done_func; - context->anim_done_func = anim_done_func; context->user_data = user_data; return (gpointer) context; @@ -1173,21 +1345,23 @@ static gboolean gdk_pixbuf__gif_image_stop_load (gpointer data, GError **error) { GifContext *context = (GifContext *) data; - - /* FIXME: free the animation data */ + gboolean retval = TRUE; - /* FIXME this thing needs to report errors if - * we have unused image data - */ + if (context->state != GIF_DONE) { + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("GIF image was truncated or incomplete.")); + + retval = FALSE; + } - if (context->pixbuf) - gdk_pixbuf_unref (context->pixbuf); - if (context->animation) - gdk_pixbuf_animation_unref (context->animation); -/* g_free (context->buf);*/ + g_object_unref (G_OBJECT (context->animation)); + + g_free (context->buf); g_free (context); - return TRUE; + return retval; } static gboolean @@ -1264,18 +1438,28 @@ gdk_pixbuf__gif_image_load_animation (FILE *file, context = new_context (); context->error = error; - - context->animation = g_object_new (GDK_TYPE_PIXBUF_ANIMATION, NULL); - - context->animation->n_frames = 0; - context->animation->frames = NULL; - context->animation->width = 0; - context->animation->height = 0; context->file = file; - gif_main_loop (context); + if (gif_main_loop (context) == -1 || context->animation->frames == NULL) { + if (context->error && *(context->error) == NULL) + g_set_error (context->error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_CORRUPT_IMAGE, + _("GIF file was missing some data (perhaps it was truncated somehow?)")); - animation = context->animation; + g_object_unref (G_OBJECT (context->animation)); + context->animation = NULL; + } + + if (context->animation) + animation = GDK_PIXBUF_ANIMATION (context->animation); + else + animation = NULL; + + if (context->error && *(context->error)) + g_print ("%s\n", (*(context->error))->message); + + g_free (context->buf); g_free (context); return animation; } diff --git a/gdk-pixbuf/io-ico.c b/gdk-pixbuf/io-ico.c index dc6dadb2f8..6a2ca48840 100644 --- a/gdk-pixbuf/io-ico.c +++ b/gdk-pixbuf/io-ico.c @@ -156,8 +156,6 @@ struct ico_progressive_state { static gpointer gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error); static gboolean gdk_pixbuf__ico_image_stop_load(gpointer data, GError **error); @@ -178,8 +176,8 @@ gdk_pixbuf__ico_image_load(FILE * f, GError **error) GdkPixbuf *pb; - State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL, - NULL, NULL, error); + State = gdk_pixbuf__ico_image_begin_load(NULL, NULL, NULL, error); + if (State == NULL) return NULL; @@ -363,6 +361,7 @@ static void DecodeHeader(guchar *Data, gint Bytes, if (State->prepared_func != NULL) /* Notify the client that we are ready to go */ (*State->prepared_func) (State->pixbuf, + NULL, State->user_data); } @@ -378,8 +377,6 @@ static void DecodeHeader(guchar *Data, gint Bytes, static gpointer gdk_pixbuf__ico_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { diff --git a/gdk-pixbuf/io-jpeg.c b/gdk-pixbuf/io-jpeg.c index 2c295131d9..c707d347af 100644 --- a/gdk-pixbuf/io-jpeg.c +++ b/gdk-pixbuf/io-jpeg.c @@ -96,8 +96,6 @@ typedef struct { static GdkPixbuf *gdk_pixbuf__jpeg_image_load (FILE *f, GError **error); static gpointer gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc func, ModuleUpdatedNotifyFunc func2, - ModuleFrameDoneNotifyFunc func3, - ModuleAnimationDoneNotifyFunc func4, gpointer user_data, GError **error); static gboolean gdk_pixbuf__jpeg_image_stop_load (gpointer context, GError **error); @@ -321,8 +319,6 @@ skip_input_data (j_decompress_ptr cinfo, long num_bytes) gpointer gdk_pixbuf__jpeg_image_begin_load (ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -539,6 +535,7 @@ gdk_pixbuf__jpeg_image_load_increment (gpointer data, /* Notify the client that we are ready to go */ (* context->prepared_func) (context->pixbuf, + NULL, context->user_data); } else if (!context->did_prescan) { diff --git a/gdk-pixbuf/io-png.c b/gdk-pixbuf/io-png.c index 9ece35d40d..21b1d4b296 100644 --- a/gdk-pixbuf/io-png.c +++ b/gdk-pixbuf/io-png.c @@ -324,8 +324,6 @@ struct _LoadContext { static gpointer gdk_pixbuf__png_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -540,7 +538,7 @@ png_info_callback (png_structp png_read_ptr, /* Notify the client that we are ready to go */ if (lc->prepare_func) - (* lc->prepare_func) (lc->pixbuf, lc->notify_user_data); + (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data); return; } diff --git a/gdk-pixbuf/io-pnm.c b/gdk-pixbuf/io-pnm.c index 87c392db5a..cd7dd9d9bd 100644 --- a/gdk-pixbuf/io-pnm.c +++ b/gdk-pixbuf/io-pnm.c @@ -84,8 +84,6 @@ typedef struct { static GdkPixbuf *gdk_pixbuf__pnm_image_load (FILE *f, GError **error); static gpointer gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc func, ModuleUpdatedNotifyFunc func2, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error); static gboolean gdk_pixbuf__pnm_image_stop_load (gpointer context, GError **error); @@ -763,8 +761,6 @@ gdk_pixbuf__pnm_image_load (FILE *f, GError **error) static gpointer gdk_pixbuf__pnm_image_begin_load (ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -923,6 +919,7 @@ gdk_pixbuf__pnm_image_load_increment (gpointer data, /* Notify the client that we are ready to go */ (* context->prepared_func) (context->pixbuf, + NULL, context->user_data); } diff --git a/gdk-pixbuf/io-ras.c b/gdk-pixbuf/io-ras.c index 25a3c0b7a5..84b7c27f92 100644 --- a/gdk-pixbuf/io-ras.c +++ b/gdk-pixbuf/io-ras.c @@ -96,8 +96,6 @@ struct ras_progressive_state { static gpointer gdk_pixbuf__ras_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error); static gboolean gdk_pixbuf__ras_image_stop_load(gpointer data, GError **error); @@ -116,8 +114,7 @@ static GdkPixbuf *gdk_pixbuf__ras_image_load(FILE * f, GError **error) GdkPixbuf *pb; - State = gdk_pixbuf__ras_image_begin_load(NULL, NULL, NULL, - NULL, NULL, error); + State = gdk_pixbuf__ras_image_begin_load(NULL, NULL, NULL, error); membuf = g_malloc(4096); @@ -195,6 +192,7 @@ static void RAS2State(struct rasterfile *RAS, if (State->prepared_func != NULL) /* Notify the client that we are ready to go */ (*State->prepared_func) (State->pixbuf, + NULL, State->user_data); } @@ -219,8 +217,6 @@ static void RAS2State(struct rasterfile *RAS, static gpointer gdk_pixbuf__ras_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { diff --git a/gdk-pixbuf/io-tiff.c b/gdk-pixbuf/io-tiff.c index 9b23f5d719..51040dcdad 100644 --- a/gdk-pixbuf/io-tiff.c +++ b/gdk-pixbuf/io-tiff.c @@ -92,7 +92,7 @@ gdk_pixbuf__tiff_image_load_real (FILE *f, TiffData *context, GError **error) } if (context) - (* context->prepare_func) (pixbuf, context->user_data); + (* context->prepare_func) (pixbuf, NULL, context->user_data); /* Yes, it needs to be _TIFFMalloc... */ rast = (uint32 *) _TIFFmalloc (num_pixs * sizeof (uint32)); @@ -163,8 +163,6 @@ gdk_pixbuf__tiff_image_load (FILE *f, GError **error) static gpointer gdk_pixbuf__tiff_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { diff --git a/gdk-pixbuf/io-wbmp.c b/gdk-pixbuf/io-wbmp.c index 1b7784b13d..1537d83d62 100644 --- a/gdk-pixbuf/io-wbmp.c +++ b/gdk-pixbuf/io-wbmp.c @@ -65,9 +65,7 @@ struct wbmp_progressive_state { static gpointer gdk_pixbuf__wbmp_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc - anim_done_func, gpointer user_data, + gpointer user_data, GError **error); static gboolean gdk_pixbuf__wbmp_image_stop_load(gpointer data, GError **error); @@ -87,7 +85,7 @@ static GdkPixbuf *gdk_pixbuf__wbmp_image_load(FILE * f, GError **error) GdkPixbuf *pb; - State = gdk_pixbuf__wbmp_image_begin_load(NULL, NULL, NULL, NULL, NULL, + State = gdk_pixbuf__wbmp_image_begin_load(NULL, NULL, NULL, error); if (State == NULL) @@ -120,9 +118,7 @@ static GdkPixbuf *gdk_pixbuf__wbmp_image_load(FILE * f, GError **error) static gpointer gdk_pixbuf__wbmp_image_begin_load(ModulePreparedNotifyFunc prepared_func, ModuleUpdatedNotifyFunc updated_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc - anim_done_func, gpointer user_data, + gpointer user_data, GError **error) { struct wbmp_progressive_state *context; @@ -285,7 +281,7 @@ static gboolean gdk_pixbuf__wbmp_image_load_increment(gpointer data, g_assert(context->pixbuf); if(context->prepared_func) - context->prepared_func(context->pixbuf, context->user_data); + context->prepared_func(context->pixbuf, NULL, context->user_data); } } else if(context->needmore) diff --git a/gdk-pixbuf/io-xbm.c b/gdk-pixbuf/io-xbm.c index 05d8f994a4..0041488eb8 100644 --- a/gdk-pixbuf/io-xbm.c +++ b/gdk-pixbuf/io-xbm.c @@ -300,7 +300,7 @@ gdk_pixbuf__xbm_image_load_real (FILE *f, XBMData *context, GError **error) row_stride = gdk_pixbuf_get_rowstride (pixbuf); if (context) - (* context->prepare_func) (pixbuf, context->user_data); + (* context->prepare_func) (pixbuf, NULL, context->user_data); /* Initialize PIXBUF */ @@ -355,8 +355,6 @@ gdk_pixbuf__xbm_image_load (FILE *f, GError **error) static gpointer gdk_pixbuf__xbm_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { diff --git a/gdk-pixbuf/io-xpm.c b/gdk-pixbuf/io-xpm.c index 0ddc3c234e..1f99088c3d 100644 --- a/gdk-pixbuf/io-xpm.c +++ b/gdk-pixbuf/io-xpm.c @@ -1424,8 +1424,6 @@ struct _XPMContext static gpointer gdk_pixbuf__xpm_image_begin_load (ModulePreparedNotifyFunc prepare_func, ModuleUpdatedNotifyFunc update_func, - ModuleFrameDoneNotifyFunc frame_done_func, - ModuleAnimationDoneNotifyFunc anim_done_func, gpointer user_data, GError **error) { @@ -1471,7 +1469,9 @@ gdk_pixbuf__xpm_image_stop_load (gpointer data, pixbuf = gdk_pixbuf__xpm_image_load (context->file, error); if (pixbuf != NULL) { - (* context->prepare_func) (pixbuf, context->user_data); + (* context->prepare_func) (pixbuf, + NULL, + context->user_data); (* context->update_func) (pixbuf, 0, 0, pixbuf->width, pixbuf->height, context->user_data); gdk_pixbuf_unref (pixbuf); diff --git a/gdk-pixbuf/pixops/pixops.c b/gdk-pixbuf/pixops/pixops.c index ea19607153..7a13e043ba 100644 --- a/gdk-pixbuf/pixops/pixops.c +++ b/gdk-pixbuf/pixops/pixops.c @@ -1,4 +1,5 @@ #include +#include #include "config.h" #include "pixops.h" @@ -93,6 +94,7 @@ pixops_scale_nearest (guchar *dest_buf, for (i = 0; i < (render_y1 - render_y0); i++) { const guchar *src = src_buf + (((i + render_y0) * y_step + y_step / 2) >> SCALE_SHIFT) * src_rowstride; + /* FIXME Owen needs to look at this */ guchar *dest = dest_buf + i * dest_rowstride; x = render_x0 * x_step + x_step / 2; @@ -160,7 +162,6 @@ pixops_composite_nearest (guchar *dest_buf, for (i = 0; i < (render_y1 - render_y0); i++) { const guchar *src = src_buf + (((i + render_y0) * y_step + y_step / 2) >> SCALE_SHIFT) * src_rowstride; - /* FIXME Owen needs to look at this */ guchar *dest = dest_buf + i * dest_rowstride; x = render_x0 * x_step + x_step / 2; @@ -183,9 +184,9 @@ pixops_composite_nearest (guchar *dest_buf, if (w != 0) { - dest[0] = (w0 * src[0] + w1 * dest[0]) / w; - dest[1] = (w0 * src[1] + w1 * dest[1]) / w; - dest[2] = (w0 * src[2] + w1 * dest[2]) / w; + dest[0] = (w0 * p[0] + w1 * dest[0]) / w; + dest[1] = (w0 * p[1] + w1 * dest[1]) / w; + dest[2] = (w0 * p[2] + w1 * dest[2]) / w; dest[3] = w / 0xff; } else @@ -274,25 +275,39 @@ pixops_composite_color_nearest (guchar *dest_buf, for (j=0 ; j < (render_x1 - render_x0); j++) { const guchar *p = src + (x >> SCALE_SHIFT) * src_channels; - unsigned int a0; + int a0; + int tmp; if (src_has_alpha) a0 = (p[3] * overall_alpha + 0xff) >> 8; else a0 = overall_alpha; - if (((j + check_x) >> check_shift) & 1) + if (a0 == 255) { - dest[0] = r2 + ((a0 * ((int)p[0] - r2) + 0xff) >> 8); - dest[1] = g2 + ((a0 * ((int)p[1] - g2) + 0xff) >> 8); - dest[2] = b2 + ((a0 * ((int)p[2] - b2) + 0xff) >> 8); + dest[0] = p[0]; + dest[1] = p[1]; + dest[2] = p[2]; } else - { - dest[0] = r1 + ((a0 * ((int)p[0] - r1) + 0xff) >> 8); - dest[1] = g1 + ((a0 * ((int)p[1] - g1) + 0xff) >> 8); - dest[2] = b1 + ((a0 * ((int)p[2] - b1) + 0xff) >> 8); - } + if (((j + check_x) >> check_shift) & 1) + { + tmp = ((int) p[0] - r2) * a0; + dest[0] = r2 + ((tmp + (tmp >> 8) + 0x80) >> 8); + tmp = ((int) p[1] - g2) * a0; + dest[1] = g2 + ((tmp + (tmp >> 8) + 0x80) >> 8); + tmp = ((int) p[2] - b2) * a0; + dest[2] = b2 + ((tmp + (tmp >> 8) + 0x80) >> 8); + } + else + { + tmp = ((int) p[0] - r1) * a0; + dest[0] = r1 + ((tmp + (tmp >> 8) + 0x80) >> 8); + tmp = ((int) p[1] - g1) * a0; + dest[1] = g1 + ((tmp + (tmp >> 8) + 0x80) >> 8); + tmp = ((int) p[2] - b1) * a0; + dest[2] = b1 + ((tmp + (tmp >> 8) + 0x80) >> 8); + } if (dest_channels == 4) dest[3] = 0xff; @@ -1003,7 +1018,7 @@ pixops_process (guchar *dest_buf, dest_x += (new_outbuf - outbuf) / dest_channels; - x = dest_x * x_step + scaled_x_offset; + x = (dest_x - check_x + render_x0) * x_step + scaled_x_offset; outbuf = new_outbuf; while (outbuf < outbuf_end) -- cgit v1.2.1