Hackers, Attached is a patch that improves the scaling quality of GdkPixbuf items on the GNOME Canvas. Rationale: The old way of doing scaling for GdkPixbufs on the GNOME Canvas for the gnome_canvas_pixbuf object was to let libart do it with a nearest neighbor approximation. The resulting image didn't look too good. This patch changes the behavior of gnome_canvas_pixbuf to do scaling using the pixel operations built into gdkpixbuf's. These operations allow scaling types like: NEAREST, TILES, BILINEAR and HYPER. So essentially when the pixbuf item is rendered it does its scaling itself, rather then letting libart do it and the scaling algorithm is now selectable. The canvas item gets all of the benefits of gdkpixbuf's built in scaling algorithms including the use of MMX acceleration. Note, if the items affine transformation includes a rotate libart still uses a nearest neighbor approximation to render the item. So it will still not look so good, but at least before the rotation the Pixbuf was scaled decently. Along with the use of the GdkPixbuf scaling routines I've also added functionality of scale caches. These scale caches work with the canvas item to cache scaled pixbufs so they don't have to be rescaled each time to item is rendered. The benefits of these caches is very apparent when you have a 512x512 pixbuf and change the zoom factor on the canvas very quickly on a non-superfast machine, or move the item, or anything else that causes a rerender. All of the features of this patch are customizable, so that if you want to use the scaling features of gdkpixbuf without a scale cache you can do that, or you tune the cache's behavior. The scale cache also recognizes hits on scaled pixbufs, so for each time that the cache scores a hit on a already scaled pixbuf its hit count is increased, which decreases its chances of being expunged from the cache due to a size limits. Detailed Description: *Added more arguments to GnomeCanvasPixbuf: use_scale_cache (gboolean) - enable scale caching scale_cache_limit (integer) - Maximum number of pixbufs to cache, -1 for unlimited. scale_type (enum) - GdkPixbuf scale type (GTK_INTERP_NEAREST, etc.) * gnome_canvas_pixbuf_render() : Changed rendering of pixbuf * to canvas so that if there is a scale component to the items * affine transform the scaling is done with gdkpixbuf's * functions rather the libarts. This will allow better visual * results after scaling. * gdk-pixbuf/gdk-pixbuf-scale-cache.c : New file. Implements * the scale cache facilities. * gdk-pixbuf/gdk-pixbuf-scale-cache.h : New file. This patch is against version 0.9.0 of GdkPixbuf. Since it only deals with the GNOME Canvas item for GdkPixbuf's it should have no impact on GTK+'s HEAD branch. I'd appreciate any feedback or comments. Thanks, Rusty -- Rusty Conover Systems Programmer Zoot Enterprises Inc, www.zootweb.com
diff -u -r ../../orig-gdk-pixbuf/AUTHORS ./AUTHORS --- ../../orig-gdk-pixbuf/AUTHORS Sat Feb 3 16:22:52 2001 +++ ./AUTHORS Sat Feb 3 16:54:45 2001 @@ -9,3 +9,4 @@ Cody Russell (bratsche gnome org) Arjan van de Ven (arjan fenrus demon nl) Michael Zucchi (zucchi zedzone mmc com au) +Rusty Conover (rconover zootweb com) Only in .: Makefile.in Only in .: autogen.sh Only in .: conftest.S Only in .: conftest.o Only in ./demo: Makefile.in Only in ./doc: Makefile.in Only in .: foo.patch diff -u -r ../../orig-gdk-pixbuf/gdk-pixbuf/Makefile.am ./gdk-pixbuf/Makefile.am --- ../../orig-gdk-pixbuf/gdk-pixbuf/Makefile.am Sat Feb 3 16:22:52 2001 +++ ./gdk-pixbuf/Makefile.am Sat Jan 13 23:54:24 2001 @@ -185,6 +185,7 @@ gdk-pixbuf-render.c \ gdk-pixbuf-scale.c \ gdk-pixbuf-util.c \ + gdk-pixbuf-scale-cache.c $(extra_sources) libgdk_pixbuf_la_LDFLAGS = -version-info 2:0:0 $(GLIB_LIBS) $(GTK_LIBS) @@ -217,6 +218,7 @@ gdk-pixbuf-features.h \ gdk-pixbuf-xlib.h \ gdk-pixbuf-xlibrgb.h \ + gdk-pixbuf-scale-cache.h \ $(CANVAS_PIXBUF_HEADERFILES) noinst_HEADERS = \ Only in ../../orig-gdk-pixbuf/gdk-pixbuf: Makefile.in Only in ../../orig-gdk-pixbuf/gdk-pixbuf: gdk-pixbuf-features.h Only in ./gdk-pixbuf: gdk-pixbuf-scale-cache.c Only in ./gdk-pixbuf: gdk-pixbuf-scale-cache.h diff -u -r ../../orig-gdk-pixbuf/gdk-pixbuf/gnome-canvas-pixbuf.c ./gdk-pixbuf/gnome-canvas-pixbuf.c --- ../../orig-gdk-pixbuf/gdk-pixbuf/gnome-canvas-pixbuf.c Sat Feb 3 16:22:52 2001 +++ ./gdk-pixbuf/gnome-canvas-pixbuf.c Sat Feb 3 16:29:11 2001 @@ -28,8 +28,8 @@ #include <libart_lgpl/art_rgb_rgba_affine.h> #include "gdk-pixbuf-private.h" #include "gnome-canvas-pixbuf.h" +#include "gdk-pixbuf-scale-cache.h" - /* Private part of the GnomeCanvasPixbuf structure */ typedef struct { @@ -61,9 +61,13 @@ /* Whether the transformation or size have changed */ guint need_xform_update : 1; + + /* Scaling cache data */ + GdkPixbufScaleCache *scale_cache; + GdkInterpType scale_type; + gint scale_cache_limit; } PixbufPrivate; - /* Object argument IDs */ enum { @@ -78,7 +82,11 @@ ARG_X, ARG_X_IN_PIXELS, ARG_Y, - ARG_Y_IN_PIXELS + ARG_Y_IN_PIXELS, + /* scale caching */ + ARG_USE_SCALE_CACHE, + ARG_SCALE_CACHE_LIMIT, + ARG_SCALE_TYPE }; static void gnome_canvas_pixbuf_class_init (GnomeCanvasPixbufClass *class); @@ -99,7 +107,6 @@ static GnomeCanvasItemClass *parent_class; - /** * gnome_canvas_pixbuf_get_type: @@ -169,6 +176,16 @@ gtk_object_add_arg_type ("GnomeCanvasPixbuf::y_in_pixels", GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_Y_IN_PIXELS); + gtk_object_add_arg_type ("GnomeCanvasPixbuf::use_scale_cache", + GTK_TYPE_BOOL, GTK_ARG_READWRITE, ARG_USE_SCALE_CACHE); + + gtk_object_add_arg_type ("GnomeCanvasPixbuf::scale_cache_limit", + GTK_TYPE_INT, GTK_ARG_READWRITE, ARG_SCALE_CACHE_LIMIT); + + gtk_object_add_arg_type ("GnomeCanvasPixbuf::scale_type", + GTK_TYPE_ENUM, GTK_ARG_READWRITE, ARG_SCALE_TYPE); + + object_class->destroy = gnome_canvas_pixbuf_destroy; object_class->set_arg = gnome_canvas_pixbuf_set_arg; @@ -194,6 +211,10 @@ priv->height = 0.0; priv->x = 0.0; priv->y = 0.0; + + priv->scale_cache = NULL; + priv->scale_type = GDK_INTERP_NEAREST; + priv->scale_cache_limit = -1; } /* Destroy handler for the pixbuf canvas item */ @@ -216,13 +237,17 @@ if (priv->pixbuf) gdk_pixbuf_unref (priv->pixbuf); + if (priv->scale_cache) + gdk_pixbuf_scale_cache_destroy(priv->scale_cache); + g_free (priv); if (GTK_OBJECT_CLASS (parent_class)->destroy) (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); + + } - /* Set_arg handler for the pixbuf canvas item */ static void @@ -242,12 +267,29 @@ case ARG_PIXBUF: pixbuf = GTK_VALUE_POINTER (*arg); if (pixbuf != priv->pixbuf) { + + /* deal with the scaling cache issues */ + if(priv->scale_cache) { + gdk_pixbuf_scale_cache_destroy(priv->scale_cache); + } + + if (pixbuf) { g_return_if_fail (pixbuf->colorspace == GDK_COLORSPACE_RGB); g_return_if_fail (pixbuf->n_channels == 3 || pixbuf->n_channels == 4); g_return_if_fail (pixbuf->bits_per_sample == 8); gdk_pixbuf_ref (pixbuf); + + if(priv->scale_cache) { + priv->scale_cache = gdk_pixbuf_scale_cache_new(pixbuf); + gdk_pixbuf_scale_cache_limit_set(priv->scale_cache, priv->scale_cache_limit); + } + + } else { + if(priv->scale_cache) { + priv->scale_cache = NULL; + } } if (priv->pixbuf) @@ -323,6 +365,30 @@ priv->need_xform_update = TRUE; gnome_canvas_item_request_update (item); break; + + case ARG_USE_SCALE_CACHE: + if(GTK_VALUE_BOOL(*arg)) { + if(!priv->scale_cache) { + priv->scale_cache = gdk_pixbuf_scale_cache_new(priv->pixbuf); + } + } else { + if(priv->scale_cache) { + gdk_pixbuf_scale_cache_destroy(priv->scale_cache); + priv->scale_cache = NULL; + } + } + break; + + case ARG_SCALE_CACHE_LIMIT: + if(priv->scale_cache) { + priv->scale_cache_limit = GTK_VALUE_INT(*arg); + gdk_pixbuf_scale_cache_limit_set(priv->scale_cache, priv->scale_cache_limit); + } + break; + + case ARG_SCALE_TYPE: + priv->scale_type = GTK_VALUE_ENUM(*arg); + break; default: break; @@ -384,6 +450,20 @@ GTK_VALUE_BOOL (*arg) = priv->y_in_pixels; break; + case ARG_USE_SCALE_CACHE: + GTK_VALUE_BOOL (*arg) = (priv->scale_cache) ? TRUE: FALSE; + break; + + case ARG_SCALE_CACHE_LIMIT: + + g_return_if_fail(priv->scale_cache != NULL); + GTK_VALUE_INT (*arg) = gdk_pixbuf_scale_cache_limit_get(priv->scale_cache); + break; + + case ARG_SCALE_TYPE: + GTK_VALUE_ENUM (*arg) = priv->scale_type; + break; + default: arg->type = GTK_TYPE_INVALID; break; @@ -720,8 +800,13 @@ { GnomeCanvasPixbuf *gcp; PixbufPrivate *priv; + GdkPixbuf *pixbuf_to_render = NULL; + gboolean explicit_unref = FALSE; + gboolean use_cached = FALSE; double i2c[6], render_affine[6]; + + gcp = GNOME_CANVAS_PIXBUF (item); priv = gcp->priv; @@ -732,13 +817,59 @@ compute_render_affine (gcp, render_affine, i2c); gnome_canvas_buf_ensure_buf (buf); - if (priv->pixbuf->has_alpha) + /* ok so here is the deal, + + raph's scaling functions in libart_gpl do nearest neighbor scaling + and rotating and things. This isn't good enough quality for production + use. So I've decided to cancel out the scaling factors in the affine, + then do a real gdkpixbuf scale to those dimensions then only use + nearest neighbor for rotations and translations, which it should be + okay for. + */ + + + + if(fabs(render_affine[0]-1.0) > GNOME_CANVAS_EPSILON || fabs(render_affine[3]-1.0) > GNOME_CANVAS_EPSILON) { + /* we need to do some scaling ! */ + const gdouble scaled_height = priv->pixbuf->height*render_affine[3]; + const gdouble scaled_width = priv->pixbuf->width*render_affine[0]; + + /* need to check that the scaled pixbuf will have a height and + width greater then zero */ + + + if(scaled_height > 1.0 && scaled_width > 1.0) { + if(priv->scale_cache) { + pixbuf_to_render = gdk_pixbuf_scale_cache_retrieve_scaled(priv->scale_cache, + render_affine[0], + render_affine[3], + priv->scale_type); + } else { + pixbuf_to_render = gdk_pixbuf_scale_simple(priv->pixbuf, + scaled_width, + scaled_height, + priv->scale_type); + explicit_unref = TRUE; + } + /* clear out the scaling affine */ + render_affine[0] = 1.0; + render_affine[3] = 1.0; + } + } + + + if(!pixbuf_to_render) { + pixbuf_to_render = priv->pixbuf; + } + + + if (pixbuf_to_render->has_alpha) art_rgb_rgba_affine (buf->buf, buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1, buf->buf_rowstride, - priv->pixbuf->pixels, - priv->pixbuf->width, priv->pixbuf->height, - priv->pixbuf->rowstride, + pixbuf_to_render->pixels, + pixbuf_to_render->width, pixbuf_to_render->height, + pixbuf_to_render->rowstride, render_affine, ART_FILTER_NEAREST, NULL); @@ -746,12 +877,16 @@ art_rgb_affine (buf->buf, buf->rect.x0, buf->rect.y0, buf->rect.x1, buf->rect.y1, buf->buf_rowstride, - priv->pixbuf->pixels, - priv->pixbuf->width, priv->pixbuf->height, - priv->pixbuf->rowstride, + pixbuf_to_render->pixels, + pixbuf_to_render->width, pixbuf_to_render->height, + pixbuf_to_render->rowstride, render_affine, ART_FILTER_NEAREST, NULL); + + if(explicit_unref) { + gdk_pixbuf_unref(pixbuf_to_render); + } buf->is_bg = FALSE; } Only in ../../orig-gdk-pixbuf/gdk-pixbuf/pixops: Makefile.in Only in ./gdk-pixbuf: sc.old.file Only in ../../orig-gdk-pixbuf/: gdk-pixbuf.spec Only in .: gdk_pixbufConf.sh Only in .: gdk_pixbuf_canvas_scale.patch Only in .: gdk_pixbuf_xlibConf.sh Only in .: gnomecanvaspixbufConf.sh
Attachment:
gdk-pixbuf-scale-cache.c
Description: gdk-pixbuf-scale-cache.c
Attachment:
gdk-pixbuf-scale-cache.h
Description: gdk-pixbuf-scale-cache.h