[eog/gnome-2-32: 5/34] Render SVGs using librsvg when filtering is enabled



commit 64213a202d0191d138150894e16615967940700f
Author: Hiroyuki Ikezoe <hiikezoe gnome org>
Date:   Sun Apr 18 14:12:17 2010 +0200

    Render SVGs using librsvg when filtering is enabled
    
    Redraws scaled SVGs correctly instead of just showing a scaled and
    filtered pixbuf. Note that depending on the filters used by the image
    rendering can be quite slow at large zoom factors. Fixes bug 108435.

 configure.ac            |   18 ++++
 src/eog-image-private.h |    6 ++
 src/eog-image.c         |   68 +++++++++++++-
 src/eog-image.h         |   10 ++
 src/eog-scroll-view.c   |  226 ++++++++++++++++++++++++++++++++++++++++++-----
 src/eog-transform.c     |   11 +++
 src/eog-transform.h     |    2 +
 src/main.c              |    6 ++
 8 files changed, 320 insertions(+), 27 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index d44423f..cc93761 100644
--- a/configure.ac
+++ b/configure.ac
@@ -275,6 +275,24 @@ AM_CONDITIONAL([HAVE_DBUS], [test "x$have_dbus" = "xyes"])
 LIBXML2_REQUIRED=2.0
 PKG_CHECK_MODULES(LIBXML2, [libxml-2.0 >= $LIBXML2_REQUIRED])
 
+# ***************
+# RSVG (optional for scaling svg image)
+# ***************
+
+LIBRSVG_REQUIRED=2.26.0
+
+AC_ARG_WITH([librsvg], AC_HELP_STRING([--without-librsvg], [disable RSVG support]))
+have_rsvg=no
+if test x$with_librsvg != xno; then
+    PKG_CHECK_MODULES(RSVG, librsvg-2.0 >= $LIBRSVG_REQUIRED, have_rsvg=yes, have_rsvg=no)
+fi
+if test "x$have_rsvg" = "xyes"; then
+  AC_DEFINE(HAVE_RSVG, 1, [RSVG Support.])
+  EOG_MODULES="$EOG_MODULES librsvg-2.0 >= $LIBRSVG_REQUIRED"
+fi
+
+AM_CONDITIONAL([HAVE_RSVG], [test "x$have_rsvg" = "xyes"])
+
 # ****************
 # CFLAGS/LIBS init
 # ****************
diff --git a/src/eog-image-private.h b/src/eog-image-private.h
index a28c0fa..3bf2e54 100644
--- a/src/eog-image-private.h
+++ b/src/eog-image-private.h
@@ -23,6 +23,9 @@
 #define __EOG_IMAGE_PRIVATE_H__
 
 #include "eog-image.h"
+#ifdef HAVE_RSVG
+#include <librsvg/rsvg.h>
+#endif
 
 G_BEGIN_DECLS
 
@@ -39,6 +42,9 @@ struct _EogImagePrivate {
 	GdkPixbufAnimationIter *anim_iter;
 	gboolean          is_playing;
 	GdkPixbuf        *thumbnail;
+#ifdef HAVE_RSVG
+	RsvgHandle       *svg;
+#endif
 
 	gint              width;
 	gint              height;
diff --git a/src/eog-image.c b/src/eog-image.c
index 850c8a0..e6297d7 100644
--- a/src/eog-image.c
+++ b/src/eog-image.c
@@ -111,6 +111,13 @@ eog_image_free_mem_private (EogImage *image)
 			priv->image = NULL;
 		}
 
+#ifdef HAVE_RSVG
+		if (priv->svg != NULL) {
+			g_object_unref (priv->svg);
+			priv->svg = NULL;
+		}
+#endif
+
 #ifdef HAVE_EXIF
 		if (priv->exif != NULL) {
 			exif_data_unref (priv->exif);
@@ -295,6 +302,9 @@ eog_image_init (EogImage *img)
 #ifdef HAVE_LCMS
 	img->priv->profile = NULL;
 #endif
+#ifdef HAVE_RSVG
+	img->priv->svg = NULL;
+#endif
 }
 
 EogImage *
@@ -936,6 +946,17 @@ eog_image_real_load (EogImage *img,
 	if (read_image_data || read_only_dimension) {
 		gboolean checked_threadsafety = FALSE;
 
+#ifdef HAVE_RSVG
+		if (priv->svg != NULL) {
+			g_object_unref (priv->svg);
+			priv->svg = NULL;
+		}
+
+		if (!strcmp (mime_type, "image/svg+xml")) {
+			/* Keep the object for rendering */
+			priv->svg = rsvg_handle_new ();
+		}
+#endif
 		loader = gdk_pixbuf_loader_new_with_mime_type (mime_type, error);
 
 		if (error && *error) {
@@ -987,10 +1008,18 @@ eog_image_real_load (EogImage *img,
 			break;
 		}
 
-		if ((read_image_data || read_only_dimension) &&
-		    !gdk_pixbuf_loader_write (loader, buffer, bytes_read, error)) {
-			failed = TRUE;
-			break;
+		if ((read_image_data || read_only_dimension)) {
+			if (!gdk_pixbuf_loader_write (loader, buffer, bytes_read, error)) {
+				failed = TRUE;
+				break;
+			}
+#ifdef HAVE_RSVG
+			if (eog_image_is_svg (img) &&
+			    !rsvg_handle_write (priv->svg, buffer, bytes_read, error)) {
+				failed = TRUE;
+				break;
+			}
+#endif
 		}
 
 		bytes_read_total += bytes_read;
@@ -1060,6 +1089,10 @@ eog_image_real_load (EogImage *img,
 				g_clear_error (error);
 			}
 	        }
+#ifdef HAVE_RSVG
+		if (eog_image_is_svg (img))
+			rsvg_handle_close (priv->svg, error);
+#endif
         }
 
 	g_free (buffer);
@@ -2148,3 +2181,30 @@ eog_image_start_animation (EogImage *img)
 
 	return TRUE;
 }
+
+#ifdef HAVE_RSVG
+gboolean
+eog_image_is_svg (EogImage *img)
+{
+	g_return_val_if_fail (EOG_IS_IMAGE (img), FALSE);
+
+	return (img->priv->svg != NULL);
+}
+
+RsvgHandle *
+eog_image_get_svg (EogImage *img)
+{
+	g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
+
+	return img->priv->svg;
+}
+
+EogTransform *
+eog_image_get_transform (EogImage *img)
+{
+	g_return_val_if_fail (EOG_IS_IMAGE (img), NULL);
+
+	return img->priv->trans;
+}
+
+#endif
diff --git a/src/eog-image.h b/src/eog-image.h
index 52dffc3..75181f9 100644
--- a/src/eog-image.h
+++ b/src/eog-image.h
@@ -44,6 +44,10 @@
 #include <exempi/xmp.h>
 #endif
 
+#ifdef HAVE_RSVG
+#include <librsvg/rsvg.h>
+#endif
+
 G_BEGIN_DECLS
 
 #ifndef __EOG_IMAGE_DECLR__
@@ -199,6 +203,12 @@ gboolean          eog_image_is_animation             (EogImage *img);
 
 gboolean          eog_image_start_animation          (EogImage *img);
 
+#ifdef HAVE_RSVG
+gboolean          eog_image_is_svg                   (EogImage *img);
+RsvgHandle       *eog_image_get_svg                  (EogImage *img);
+EogTransform     *eog_image_get_transform            (EogImage *img);
+#endif
+
 G_END_DECLS
 
 #endif /* __EOG_IMAGE_H__ */
diff --git a/src/eog-scroll-view.c b/src/eog-scroll-view.c
index 3e50a31..52f3973 100644
--- a/src/eog-scroll-view.c
+++ b/src/eog-scroll-view.c
@@ -6,6 +6,10 @@
 #include <math.h>
 #include <gdk-pixbuf/gdk-pixbuf.h>
 #include <gdk/gdkkeysyms.h>
+#ifdef HAVE_RSVG
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+#endif
 
 #include "eog-marshal.h"
 #include "eog-scroll-view.h"
@@ -133,6 +137,8 @@ struct _EogScrollViewPrivate {
 
 	/* the type of the cursor we are currently showing */
 	EogScrollViewCursor cursor;
+
+	cairo_surface_t *background_surface;
 };
 
 static void scroll_by (EogScrollView *view, int xofs, int yofs);
@@ -537,6 +543,190 @@ paint_background (EogScrollView *view, EogIRect *r, EogIRect *rect)
 	}
 }
 
+static void
+get_transparency_params (EogScrollView *view, int *size, guint32 *color1, guint32 *color2)
+{
+	EogScrollViewPrivate *priv;
+
+	priv = view->priv;
+
+	/* Compute transparency parameters */
+	switch (priv->transp_style) {
+	case EOG_TRANSP_BACKGROUND: {
+		GdkColor color = gtk_widget_get_style (GTK_WIDGET (priv->display))->bg[GTK_STATE_NORMAL];
+
+		*color1 = *color2 = (((color.red & 0xff00) << 8)
+				       | (color.green & 0xff00)
+				       | ((color.blue & 0xff00) >> 8));
+		break; }
+
+	case EOG_TRANSP_CHECKED:
+		*color1 = CHECK_GRAY;
+		*color2 = CHECK_LIGHT;
+		break;
+
+	case EOG_TRANSP_COLOR:
+		*color1 = *color2 = priv->transp_color;
+		break;
+
+	default:
+		g_assert_not_reached ();
+	};
+
+	*size = CHECK_MEDIUM;
+}
+
+#ifdef HAVE_RSVG
+static cairo_surface_t *
+create_background_surface (EogScrollView *view)
+{
+	int check_size;
+	guint32 check_1 = 0;
+	guint32 check_2 = 0;
+	cairo_surface_t *surface;
+	cairo_t *check_cr;
+
+	get_transparency_params (view, &check_size, &check_1, &check_2);
+
+	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, check_size * 2, check_size * 2);
+	check_cr = cairo_create (surface);
+	cairo_set_source_rgba (check_cr,
+			       ((check_1 & 0xff0000) >> 16) / 255.,
+			       ((check_1 & 0x00ff00) >> 8)  / 255.,
+			        (check_1 & 0x0000ff)        / 255.,
+				1.);
+	cairo_rectangle (check_cr, 0., 0., check_size, check_size);
+	cairo_fill (check_cr);
+	cairo_translate (check_cr, check_size, check_size);
+	cairo_rectangle (check_cr, 0., 0., check_size, check_size);
+	cairo_fill (check_cr);
+
+	cairo_set_source_rgba (check_cr,
+			       ((check_2 & 0xff0000) >> 16) / 255.,
+			       ((check_2 & 0x00ff00) >> 8)  / 255.,
+			        (check_2 & 0x0000ff)        / 255.,
+				1.);
+	cairo_translate (check_cr, -check_size, 0);
+	cairo_rectangle (check_cr, 0., 0., check_size, check_size);
+	cairo_fill (check_cr);
+	cairo_translate (check_cr, check_size, -check_size);
+	cairo_rectangle (check_cr, 0., 0., check_size, check_size);
+	cairo_fill (check_cr);
+	cairo_destroy (check_cr);
+
+	return surface;
+}
+
+static void
+draw_svg_background (EogScrollView *view, cairo_t *cr, EogIRect *render_rect, EogIRect *image_rect)
+{
+	EogScrollViewPrivate *priv;
+
+	priv = view->priv;
+
+	if (priv->background_surface == NULL)
+		priv->background_surface = create_background_surface (view);
+
+	cairo_set_source_surface (cr, priv->background_surface,
+				  - (render_rect->x0 - image_rect->x0) % (CHECK_MEDIUM * 2),
+				  - (render_rect->y0 - image_rect->y0) % (CHECK_MEDIUM * 2));
+	cairo_pattern_set_extend (cairo_get_source (cr), CAIRO_EXTEND_REPEAT);
+	cairo_rectangle (cr,
+			 0,
+			 0,
+			 render_rect->x1 - render_rect->x0,
+			 render_rect->y1 - render_rect->y0);
+	cairo_fill (cr);
+}
+
+static cairo_surface_t *
+draw_svg_on_image_surface (EogScrollView *view, EogIRect *render_rect, EogIRect *image_rect)
+{
+	EogScrollViewPrivate *priv;
+	cairo_t *cr;
+	cairo_surface_t *surface;
+	cairo_matrix_t matrix, translate, scale;
+	EogTransform *transform;
+
+	priv = view->priv;
+
+	surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+					      render_rect->x1 - render_rect->x0,
+					      render_rect->y1 - render_rect->y0);
+	cr = cairo_create (surface);
+
+	cairo_save (cr);
+	draw_svg_background (view, cr, render_rect, image_rect);
+	cairo_restore (cr);
+
+	cairo_matrix_init_identity (&matrix);
+	transform = eog_image_get_transform (priv->image);
+	if (transform) {
+		cairo_matrix_t affine;
+		double image_offset_x = 0., image_offset_y = 0.;
+
+		eog_transform_get_affine (transform, &affine);
+		cairo_matrix_multiply (&matrix, &affine, &matrix);
+
+		switch (eog_transform_get_transform_type (transform)) {
+		case EOG_TRANSFORM_ROT_90:
+		case EOG_TRANSFORM_FLIP_HORIZONTAL:
+			image_offset_x = (double) gdk_pixbuf_get_width (priv->pixbuf);
+			break;
+		case EOG_TRANSFORM_ROT_270:
+		case EOG_TRANSFORM_FLIP_VERTICAL:
+			image_offset_y = (double) gdk_pixbuf_get_height (priv->pixbuf);
+			break;
+		case EOG_TRANSFORM_ROT_180:
+		case EOG_TRANSFORM_TRANSPOSE:
+		case EOG_TRANSFORM_TRANSVERSE:
+			image_offset_x = (double) gdk_pixbuf_get_width (priv->pixbuf);
+			image_offset_y = (double) gdk_pixbuf_get_height (priv->pixbuf);
+			break;
+		case EOG_TRANSFORM_NONE:
+		default:
+			break;
+		}
+
+		cairo_matrix_init_translate (&translate, image_offset_x, image_offset_y);
+		cairo_matrix_multiply (&matrix, &matrix, &translate);
+	}
+
+	cairo_matrix_init_scale (&scale, priv->zoom, priv->zoom);
+	cairo_matrix_multiply (&matrix, &matrix, &scale);
+	cairo_matrix_init_translate (&translate, image_rect->x0, image_rect->y0);
+	cairo_matrix_multiply (&matrix, &matrix, &translate);
+	cairo_matrix_init_translate (&translate, -render_rect->x0, -render_rect->y0);
+	cairo_matrix_multiply (&matrix, &matrix, &translate);
+
+	cairo_set_matrix (cr, &matrix);
+
+	rsvg_handle_render_cairo (eog_image_get_svg (priv->image), cr);
+	cairo_destroy (cr);
+
+	return surface;
+}
+
+static void
+draw_svg (EogScrollView *view, EogIRect *render_rect, EogIRect *image_rect)
+{
+	EogScrollViewPrivate *priv;
+	cairo_t *cr;
+	cairo_surface_t *surface;
+	GdkWindow *window;
+
+	priv = view->priv;
+
+	window = gtk_widget_get_window (GTK_WIDGET (priv->display));
+	surface = draw_svg_on_image_surface (view, render_rect, image_rect);
+
+	cr = gdk_cairo_create (window);
+	cairo_set_source_surface (cr, surface, render_rect->x0, render_rect->y0);
+	cairo_paint (cr);
+	cairo_destroy (cr);
+}
+#endif
+
 /* Paints a rectangle of the dirty region */
 static void
 paint_rectangle (EogScrollView *view, EogIRect *rect, GdkInterpType interp_type)
@@ -656,6 +846,12 @@ paint_rectangle (EogScrollView *view, EogIRect *rect, GdkInterpType interp_type)
 	eog_debug_message (DEBUG_WINDOW, "%s: x0: %i,\t y0: %i,\t x1: %i,\t y1: %i\n",
 			   str, d.x0, d.y0, d.x1, d.y1);
 
+#ifdef HAVE_RSVG
+	if (eog_image_is_svg (view->priv->image) && interp_type != GDK_INTERP_NEAREST) {
+		draw_svg (view, &d, &r);
+		return;
+	}
+#endif
 	/* Short-circuit the fast case to avoid a memcpy() */
 
 	if (is_unity_zoom (view)
@@ -693,29 +889,7 @@ paint_rectangle (EogScrollView *view, EogIRect *rect, GdkInterpType interp_type)
 	}
 
 	/* Compute transparency parameters */
-	switch (priv->transp_style) {
-	case EOG_TRANSP_BACKGROUND: {
-		GdkColor color = gtk_widget_get_style (GTK_WIDGET (priv->display))->bg[GTK_STATE_NORMAL];
-
-		check_1 = check_2 = (((color.red & 0xff00) << 8)
-				     | (color.green & 0xff00)
-				     | ((color.blue & 0xff00) >> 8));
-		break; }
-
-	case EOG_TRANSP_CHECKED:
-		check_1 = CHECK_GRAY;
-		check_2 = CHECK_LIGHT;
-		break;
-
-	case EOG_TRANSP_COLOR:
-		check_1 = check_2 = priv->transp_color;
-		break;
-
-	default:
-		g_assert_not_reached ();
-	};
-
-	check_size = CHECK_MEDIUM;
+	get_transparency_params (view, &check_size, &check_1, &check_2);
 
 	/* Draw! */
 	gdk_pixbuf_composite_color (priv->pixbuf,
@@ -2010,6 +2184,7 @@ eog_scroll_view_init (EogScrollView *view)
 	priv->transp_color = 0;
 	priv->cursor = EOG_SCROLL_VIEW_CURSOR_NORMAL;
 	priv->menu = NULL;
+	priv->background_surface = NULL;
 }
 
 static void
@@ -2033,6 +2208,11 @@ eog_scroll_view_dispose (GObject *object)
 		priv->idle_id = 0;
 	}
 
+	if (priv->background_surface != NULL) {
+		cairo_surface_destroy (priv->background_surface);
+		priv->background_surface = NULL;
+	}
+
 	free_image_resources (view);
 
 	G_OBJECT_CLASS (eog_scroll_view_parent_class)->dispose (object);
diff --git a/src/eog-transform.c b/src/eog-transform.c
index 3c730b3..3a4c7f8 100644
--- a/src/eog-transform.c
+++ b/src/eog-transform.c
@@ -405,3 +405,14 @@ eog_transform_get_transform_type (EogTransform *trans)
 
 	return EOG_TRANSFORM_NONE;
 }
+
+gboolean
+eog_transform_get_affine (EogTransform *trans, cairo_matrix_t *affine)
+{
+	g_return_val_if_fail (EOG_IS_TRANSFORM (trans), FALSE);
+
+	_eog_cairo_matrix_copy (&trans->priv->affine, affine);
+
+	return TRUE;
+}
+
diff --git a/src/eog-transform.h b/src/eog-transform.h
index 9b11c35..94c4222 100644
--- a/src/eog-transform.h
+++ b/src/eog-transform.h
@@ -66,6 +66,8 @@ EogTransform* eog_transform_new (EogTransformType trans);
 
 EogTransformType eog_transform_get_transform_type (EogTransform *trans);
 
+gboolean         eog_transform_get_affine (EogTransform *trans, cairo_matrix_t *affine);
+
 G_END_DECLS
 
 #endif /* _EOG_TRANSFORM_H_ */
diff --git a/src/main.c b/src/main.c
index 4e5e7e3..1eccb45 100644
--- a/src/main.c
+++ b/src/main.c
@@ -231,6 +231,9 @@ main (int argc, char **argv)
 #ifdef HAVE_EXEMPI
  	xmp_init();
 #endif
+#ifdef HAVE_RSVG
+	rsvg_init();
+#endif
 	eog_debug_init ();
 	eog_job_queue_init ();
 	gdk_threads_init ();
@@ -257,6 +260,9 @@ main (int argc, char **argv)
 
 	eog_plugin_engine_shutdown ();
 
+#ifdef HAVE_RSVG
+	rsvg_term();
+#endif
 #ifdef HAVE_EXEMPI
 	xmp_terminate();
 #endif



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