[gthumb] High quality zoom for SVG images



commit 0d12cb85290e57f476ee4197a871c7d705d0f8bc
Author: Paolo Bacchilega <paobac src gnome org>
Date:   Sun Jan 22 16:19:51 2012 +0100

    High quality zoom for SVG images
    
    requires librsvg
    
    [new feature, bug#628443]

 configure.ac                       |   21 ++++
 extensions/cairo_io/Makefile.am    |   10 ++-
 extensions/cairo_io/cairo-io-svg.c |  189 ++++++++++++++++++++++++++++++++++++
 extensions/cairo_io/cairo-io-svg.h |   40 ++++++++
 extensions/cairo_io/main.c         |    7 ++
 gthumb/gth-image-viewer.c          |   51 +++++++++-
 gthumb/gth-image.c                 |   46 ++++++++-
 gthumb/gth-image.h                 |   39 +++++---
 8 files changed, 381 insertions(+), 22 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b8e1501..4fb297b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -56,6 +56,7 @@ LIBSOUP_REQUIRED=2.36
 GNOME_KEYRING_REQUIRED=3.2.0
 LIBBRASERO_REQUIRED=3.2.0
 LIBCHAMPLAIN_REQUIRED=0.12.0
+LIBRSVG_REQUIRED=2.34.0
 
 dnl ===========================================================================
 
@@ -427,6 +428,25 @@ AC_MSG_RESULT($enable_libopenraw)
 
 dnl ===========================================================================
 
+AC_ARG_ENABLE([librsvg],
+	      [AS_HELP_STRING([--disable-librsvg],[do not compile code that uses the librsvg library [default=yes]])],,
+	      [enable_librsvg=yes])
+
+if test x$enable_librsvg = xyes ; then
+	PKG_CHECK_MODULES(LIBRSVG,
+			  librsvg-2.0 >= $LIBRSVG_REQUIRED,
+			  [enable_librsvg=yes],
+			  [enable_librsvg=no])
+	if test "x$enable_librsvg" = "xyes"; then
+		AC_DEFINE(HAVE_LIBRSVG, 1, [Define to 1 if librsvg support is included])
+	fi			  
+fi
+AC_SUBST(LIBRSVG_LIBS)
+AC_SUBST(LIBRSVG_CFLAGS)
+AM_CONDITIONAL(ENABLE_LIBRSVG, test "x$enable_librsvg" = xyes)
+
+dnl ===========================================================================
+
 AC_ARG_ENABLE([gnome-keyring],
 	      [AS_HELP_STRING([--disable-gnome-keyring],[do not compile code that uses the gnome-keyring library])],,
 	      [enable_gnome_keyring=yes])
@@ -704,4 +724,5 @@ Configuration:
 	Web albums           : ${enable_web_albums}
 	SM client support    : ${with_smclient}
 	Map support          : ${enable_libchamplain}
+	SVG support          : ${enable_librsvg}
 "
diff --git a/extensions/cairo_io/Makefile.am b/extensions/cairo_io/Makefile.am
index 7f4203c..68bcf4b 100644
--- a/extensions/cairo_io/Makefile.am
+++ b/extensions/cairo_io/Makefile.am
@@ -12,9 +12,15 @@ libcairo_io_la_SOURCES +=	\
 	cairo-io-png.h
 endif
 
-libcairo_io_la_CFLAGS = $(GTHUMB_CFLAGS) $(JPEG_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb
+if ENABLE_LIBRSVG
+libcairo_io_la_SOURCES +=	\
+	cairo-io-svg.c		\
+	cairo-io-svg.h
+endif
+
+libcairo_io_la_CFLAGS = $(GTHUMB_CFLAGS) $(JPEG_CFLAGS) $(LIBRSVG_CFLAGS) -I$(top_srcdir) -I$(top_builddir)/gthumb
 libcairo_io_la_LDFLAGS = $(EXTENSION_LIBTOOL_FLAGS)
-libcairo_io_la_LIBADD = $(GTHUMB_LIBS) $(JPEG_LIBS)
+libcairo_io_la_LIBADD = $(GTHUMB_LIBS) $(JPEG_LIBS) $(LIBRSVG_LIBS)
 if ENABLE_JPEG_TOOLS
 libcairo_io_la_LIBADD += ../jpeg_utils/libjpeg_utils.la
 endif
diff --git a/extensions/cairo_io/cairo-io-svg.c b/extensions/cairo_io/cairo-io-svg.c
new file mode 100644
index 0000000..b532eab
--- /dev/null
+++ b/extensions/cairo_io/cairo-io-svg.c
@@ -0,0 +1,189 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2011 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <gthumb.h>
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+#include "cairo-io-svg.h"
+
+
+/* GthImageSvg (private class) */
+
+
+#define GTH_IMAGE_SVG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), gth_image_svg_get_type(), GthImageSvg))
+
+
+typedef struct {
+	GthImage __parent;
+	RsvgHandle *rsvg;
+	int         original_width;
+	int         original_height;
+	double      last_zoom;
+} GthImageSvg;
+
+
+typedef GthImageClass GthImageSvgClass;
+
+
+static gpointer gth_image_svg_parent_class;
+
+
+G_DEFINE_TYPE (GthImageSvg, gth_image_svg, GTH_TYPE_IMAGE)
+
+
+static void
+gth_image_svg_finalize (GObject *object)
+{
+	GthImageSvg *self;
+
+	self = GTH_IMAGE_SVG (object);
+	_g_object_unref (self->rsvg);
+
+        G_OBJECT_CLASS (gth_image_svg_parent_class)->finalize (object);
+}
+
+
+static void
+gth_image_svg_init (GthImageSvg *self)
+{
+	self->rsvg = NULL;
+	self->original_width = 0;
+	self->original_height = 0;
+	self->last_zoom = 0.0;
+}
+
+
+static gboolean
+gth_image_svg_get_is_zoomable (GthImage *base)
+{
+	return (((GthImageSvg *) base)->rsvg != NULL);
+}
+
+
+static gboolean
+gth_image_svg_set_zoom (GthImage *base,
+			double    zoom,
+			int      *original_width,
+			int      *original_height)
+{
+	GthImageSvg     *self;
+	cairo_surface_t *surface;
+	cairo_t         *cr;
+	gboolean         changed = FALSE;
+
+	self = GTH_IMAGE_SVG (base);
+	if (self->rsvg == NULL)
+		return FALSE;
+
+	if (zoom != self->last_zoom) {
+		self->last_zoom = zoom;
+
+		surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+						      zoom * self->original_width,
+						      zoom * self->original_height);
+		cr = cairo_create (surface);
+		cairo_scale (cr, zoom, zoom);
+		rsvg_handle_render_cairo (self->rsvg, cr);
+		gth_image_set_cairo_surface (base, surface);
+		changed = TRUE;
+
+		cairo_destroy (cr);
+		cairo_surface_destroy (surface);
+	}
+
+	if (original_width != NULL)
+		*original_width = self->original_width;
+	if (original_height != NULL)
+		*original_height = self->original_height;
+
+	return changed;
+}
+
+
+static void
+gth_image_svg_class_init (GthImageSvgClass *klass)
+{
+	GObjectClass  *object_class;
+	GthImageClass *image_class;
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = gth_image_svg_finalize;
+
+	image_class = GTH_IMAGE_CLASS (klass);
+	image_class->get_is_zoomable = gth_image_svg_get_is_zoomable;
+	image_class->set_zoom = gth_image_svg_set_zoom;
+}
+
+
+static GthImage *
+gth_image_svg_new (void)
+{
+        return g_object_new (gth_image_svg_get_type (), NULL);
+}
+
+
+static void
+gth_image_svg_set_handle (GthImageSvg *self,
+			  RsvgHandle  *rsvg)
+{
+	RsvgDimensionData dimension_data;
+
+	if (self->rsvg == rsvg)
+		return;
+
+	self->rsvg = g_object_ref (rsvg);
+
+	rsvg_handle_get_dimensions (self->rsvg, &dimension_data);
+	self->original_width = dimension_data.width;
+	self->original_height = dimension_data.height;
+
+	gth_image_svg_set_zoom (GTH_IMAGE (self), 1.0, NULL, NULL);
+}
+
+
+/* -- _cairo_image_surface_create_from_svg -- */
+
+
+GthImage *
+_cairo_image_surface_create_from_svg (GthFileData   *file_data,
+				      int            requested_size,
+				      int           *original_width,
+				      int           *original_height,
+				      gpointer       user_data,
+				      GCancellable  *cancellable,
+				      GError       **error)
+{
+	GthImage   *image;
+	RsvgHandle *rsvg;
+
+	image = gth_image_svg_new ();
+	rsvg = rsvg_handle_new_from_gfile_sync (file_data->file,
+						RSVG_HANDLE_FLAGS_NONE,
+						cancellable,
+						error);
+	if (rsvg != NULL) {
+		gth_image_svg_set_handle (GTH_IMAGE_SVG (image), rsvg);
+		g_object_unref (rsvg);
+	}
+
+	return image;
+}
diff --git a/extensions/cairo_io/cairo-io-svg.h b/extensions/cairo_io/cairo-io-svg.h
new file mode 100644
index 0000000..b5fc2fa
--- /dev/null
+++ b/extensions/cairo_io/cairo-io-svg.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ *  GThumb
+ *
+ *  Copyright (C) 2012 Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program 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 General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef CAIRO_IO_SVG_H
+#define CAIRO_IO_SVG_H
+
+#include <gtk/gtk.h>
+#include <gthumb.h>
+
+G_BEGIN_DECLS
+
+GthImage *  _cairo_image_surface_create_from_svg (GthFileData   *file_data,
+						  int            requested_size,
+						  int           *original_width,
+						  int           *original_height,
+						  gpointer       user_data,
+						  GCancellable  *cancellable,
+						  GError       **error);
+
+G_END_DECLS
+
+#endif /* CAIRO_IO_SVG_H */
diff --git a/extensions/cairo_io/main.c b/extensions/cairo_io/main.c
index 4686146..7712eab 100644
--- a/extensions/cairo_io/main.c
+++ b/extensions/cairo_io/main.c
@@ -24,6 +24,7 @@
 #include <gthumb.h>
 #include "cairo-io-jpeg.h"
 #include "cairo-io-png.h"
+#include "cairo-io-svg.h"
 
 
 G_MODULE_EXPORT void
@@ -39,6 +40,12 @@ gthumb_extension_activate (void)
 					     GTH_IMAGE_FORMAT_CAIRO_SURFACE,
 					     "image/png",
 					     NULL);
+#ifdef HAVE_LIBRSVG
+	gth_main_register_image_loader_func (_cairo_image_surface_create_from_svg,
+					     GTH_IMAGE_FORMAT_CAIRO_SURFACE,
+					     "image/svg+xml",
+					     NULL);
+#endif
 }
 
 
diff --git a/gthumb/gth-image-viewer.c b/gthumb/gth-image-viewer.c
index 17b3db7..f29b448 100644
--- a/gthumb/gth-image-viewer.c
+++ b/gthumb/gth-image-viewer.c
@@ -73,6 +73,7 @@ struct _GthImageViewerPrivate {
 	GtkScrollablePolicy     hscroll_policy;
 	GtkScrollablePolicy     vscroll_policy;
 
+	GthImage               *image;
 	cairo_surface_t        *surface;
 	GdkPixbufAnimation     *animation;
 	int                     original_width;
@@ -169,6 +170,7 @@ gth_image_viewer_finalize (GObject *object)
 
 	g_list_foreach (self->priv->painters, (GFunc) painter_data_free, NULL);
 
+	_g_clear_object (&self->priv->image);
 	_g_clear_object (&self->priv->animation);
 	_g_clear_object (&self->priv->iter);
 	_cairo_clear_surface (&self->priv->iter_surface);
@@ -287,6 +289,13 @@ _gth_image_viewer_update_image_area (GthImageViewer *self)
 }
 
 
+static void _set_surface (GthImageViewer  *self,
+			  cairo_surface_t *surface,
+			  int              original_width,
+			  int              original_height,
+			  gboolean         better_quality);
+
+
 static void
 set_zoom (GthImageViewer *self,
 	  gdouble         zoom_level,
@@ -299,6 +308,29 @@ set_zoom (GthImageViewer *self,
 
 	g_return_if_fail (self != NULL);
 
+	if (gth_image_get_is_zoomable (self->priv->image)) {
+		int original_width;
+		int original_height;
+
+		if (gth_image_set_zoom (self->priv->image,
+				        zoom_level,
+				        &original_width,
+				        &original_height))
+		{
+			cairo_surface_t *surface;
+
+			surface = gth_image_get_cairo_surface (self->priv->image);
+			if (surface != NULL) {
+				_set_surface (self,
+					      surface,
+					      original_width,
+					      original_height,
+					      TRUE);
+				cairo_surface_destroy (surface);
+			}
+		}
+	}
+
 	/* try to keep the center of the view visible. */
 	_gth_image_viewer_get_visible_area_size (self, &visible_width, &visible_height);
 	zoom_ratio = zoom_level / self->priv->zoom_level;
@@ -1638,6 +1670,7 @@ _set_animation (GthImageViewer     *self,
 	_cairo_clear_surface (&self->priv->iter_surface);
 	_g_clear_object (&self->priv->animation);
 	_g_clear_object (&self->priv->iter);
+	_g_clear_object (&self->priv->image);
 
 	self->priv->animation = _g_object_ref (animation);
 	self->priv->is_void = (self->priv->animation == NULL);
@@ -1732,29 +1765,40 @@ gth_image_viewer_set_surface (GthImageViewer  *self,
 			      int              original_width,
 			      int              original_height)
 {
+	_g_clear_object (&self->priv->image);
 	_set_surface (self, surface, original_width, original_height, FALSE);
 }
 
 
 void
-gth_image_viewer_set_image (GthImageViewer *viewer,
+gth_image_viewer_set_image (GthImageViewer *self,
 			    GthImage       *image,
 			    int             original_width,
 			    int             original_height)
 {
+	if (self->priv->image != image)
+		_g_clear_object (&self->priv->image);
+
 	if (gth_image_is_animation (image)) {
 		GdkPixbufAnimation *animation;
 
 		animation = gth_image_get_pixbuf_animation (image);
-		gth_image_viewer_set_animation (viewer,	animation, original_width, original_height);
+		gth_image_viewer_set_animation (self,	animation, original_width, original_height);
 
 		g_object_unref (animation);
 	}
 	else {
 		cairo_surface_t *surface;
 
+		if (gth_image_get_is_zoomable (image)) {
+			self->priv->image = g_object_ref (image);
+			gth_image_set_zoom (self->priv->image,
+					    self->priv->zoom_level,
+					    &original_width,
+					    &original_height);
+		}
 		surface = gth_image_get_cairo_surface (image);
-		gth_image_viewer_set_surface (viewer, surface, original_width, original_height);
+		_set_surface (self, surface, original_width, original_height, FALSE);
 
 		cairo_surface_destroy (surface);
 	}
@@ -1770,6 +1814,7 @@ gth_image_viewer_set_void (GthImageViewer *self)
 	_cairo_clear_surface (&self->priv->iter_surface);
 	_g_clear_object (&self->priv->animation);
 	_g_clear_object (&self->priv->iter);
+	_g_clear_object (&self->priv->image);
 
 	self->priv->is_void = TRUE;
 	self->priv->is_animation = FALSE;
diff --git a/gthumb/gth-image.c b/gthumb/gth-image.c
index 46a1683..d999039 100644
--- a/gthumb/gth-image.c
+++ b/gthumb/gth-image.c
@@ -80,15 +80,35 @@ gth_image_finalize (GObject *object)
 }
 
 
+static gboolean
+base_get_is_zoomable (GthImage *image)
+{
+	return FALSE;
+}
+
+
+static gboolean
+base_set_zoom (GthImage *image,
+	       double    zoom,
+	       int      *original_width,
+	       int      *original_height)
+{
+	return FALSE;
+}
+
+
 static void
-gth_image_class_init (GthImageClass *class)
+gth_image_class_init (GthImageClass *klass)
 {
 	GObjectClass *gobject_class;
 
-	g_type_class_add_private (class, sizeof (GthImagePrivate));
+	g_type_class_add_private (klass, sizeof (GthImagePrivate));
 
-	gobject_class = (GObjectClass*) class;
+	gobject_class = (GObjectClass*) klass;
 	gobject_class->finalize = gth_image_finalize;
+
+	klass->get_is_zoomable = base_get_is_zoomable;
+	klass->set_zoom = base_set_zoom;
 }
 
 
@@ -164,6 +184,26 @@ gth_image_get_cairo_surface (GthImage *image)
 }
 
 
+gboolean
+gth_image_get_is_zoomable (GthImage *self)
+{
+	if (self == NULL)
+		return FALSE;
+	else
+		return GTH_IMAGE_GET_CLASS (self)->get_is_zoomable (self);
+}
+
+
+gboolean
+gth_image_set_zoom (GthImage *self,
+		    double    zoom,
+		    int      *original_width,
+		    int      *original_height)
+{
+	return GTH_IMAGE_GET_CLASS (self)->set_zoom (self, zoom, original_width, original_height);
+}
+
+
 void
 gth_image_set_pixbuf (GthImage  *image,
 		      GdkPixbuf *value)
diff --git a/gthumb/gth-image.h b/gthumb/gth-image.h
index dd1a068..81746c7 100644
--- a/gthumb/gth-image.h
+++ b/gthumb/gth-image.h
@@ -1,4 +1,4 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* -*- Mode: C; tab-width: 8;	 indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
 /*
  *  GThumb
@@ -56,6 +56,12 @@ struct _GthImage
 struct _GthImageClass
 {
 	GObjectClass __parent_class;
+
+	gboolean  (*get_is_zoomable)  (GthImage *image);
+	gboolean  (*set_zoom)         (GthImage *image,
+				       double    zoom,
+				       int      *original_width,
+				       int      *original_height);
 };
 
 
@@ -68,19 +74,24 @@ typedef GthImage * (*GthImageLoaderFunc) (GthFileData   *file_data,
 					  GError       **error);
 
 
-GType                 gth_image_get_type              (void);
-GthImage *            gth_image_new                   (void);
-GthImage *            gth_image_new_for_pixbuf        (GdkPixbuf          *value);
-void                  gth_image_set_cairo_surface     (GthImage           *image,
-						       cairo_surface_t    *value);
-cairo_surface_t *     gth_image_get_cairo_surface     (GthImage           *image);
-void                  gth_image_set_pixbuf            (GthImage           *image,
-						       GdkPixbuf          *value);
-GdkPixbuf *           gth_image_get_pixbuf            (GthImage           *image);
-void                  gth_image_set_pixbuf_animation  (GthImage           *image,
-						       GdkPixbufAnimation *value);
-GdkPixbufAnimation *  gth_image_get_pixbuf_animation  (GthImage           *image);
-gboolean              gth_image_is_animation          (GthImage           *image);
+GType                 gth_image_get_type                    (void);
+GthImage *            gth_image_new                         (void);
+GthImage *            gth_image_new_for_pixbuf              (GdkPixbuf          *value);
+void                  gth_image_set_cairo_surface           (GthImage           *image,
+						             cairo_surface_t    *value);
+cairo_surface_t *     gth_image_get_cairo_surface           (GthImage           *image);
+gboolean              gth_image_get_is_zoomable             (GthImage           *image);
+gboolean              gth_image_set_zoom                    (GthImage           *image,
+							     double              zoom,
+							     int                *original_width,
+							     int                *original_height);
+void                  gth_image_set_pixbuf                  (GthImage           *image,
+						             GdkPixbuf          *value);
+GdkPixbuf *           gth_image_get_pixbuf                  (GthImage           *image);
+void                  gth_image_set_pixbuf_animation        (GthImage           *image,
+						             GdkPixbufAnimation *value);
+GdkPixbufAnimation *  gth_image_get_pixbuf_animation        (GthImage           *image);
+gboolean              gth_image_is_animation                (GthImage           *image);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]