From 21caf5f03041e236157e967bf2c07832b5ba551b Mon Sep 17 00:00:00 2001 From: Hiroyuki Ikezoe Date: Sun, 18 Apr 2010 12:12:17 +0000 Subject: 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. --- diff --git a/configure.ac b/configure.ac index 48d6fca..ef51307 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 +#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 #endif +#ifdef HAVE_RSVG +#include +#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 #include #include +#ifdef HAVE_RSVG +#include +#include +#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 03c2241..ddc2d17 100644 --- a/src/main.c +++ b/src/main.c @@ -232,6 +232,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 (); @@ -258,6 +261,9 @@ main (int argc, char **argv) eog_plugin_engine_shutdown (); +#ifdef HAVE_RSVG + rsvg_term(); +#endif #ifdef HAVE_EXEMPI xmp_terminate(); #endif -- cgit v0.8.3.1