diff options
-rw-r--r-- | ChangeLog | 219 | ||||
-rw-r--r-- | Makefile.am | 47 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | configure.in | 66 | ||||
-rw-r--r-- | gtk-engine/Makefile.am | 2 | ||||
-rw-r--r-- | gtk-engine/svg-draw.c | 2 | ||||
-rw-r--r-- | gtk-engine/svg-render.c | 133 | ||||
-rw-r--r-- | gtk-engine/svg.h | 18 | ||||
-rw-r--r-- | moz-plugin/Makefile.am | 9 | ||||
-rw-r--r-- | moz-plugin/moz-plugin.c | 235 | ||||
-rw-r--r-- | rsvg-css.c | 54 | ||||
-rw-r--r-- | rsvg-css.h | 3 | ||||
-rw-r--r-- | rsvg-defs.c | 15 | ||||
-rw-r--r-- | rsvg-defs.h | 6 | ||||
-rw-r--r-- | rsvg-file-util.c | 87 | ||||
-rw-r--r-- | rsvg-filter.c | 4860 | ||||
-rw-r--r-- | rsvg-filter.h | 123 | ||||
-rw-r--r-- | rsvg-paint-server.c | 3 | ||||
-rw-r--r-- | rsvg-private.h | 58 | ||||
-rw-r--r-- | rsvg-shapes.c | 1062 | ||||
-rw-r--r-- | rsvg-shapes.h | 35 | ||||
-rw-r--r-- | rsvg-styles.c | 703 | ||||
-rw-r--r-- | rsvg-styles.h | 49 | ||||
-rw-r--r-- | rsvg-text-vectors.c | 196 | ||||
-rw-r--r-- | rsvg-text.c | 90 | ||||
-rw-r--r-- | rsvg-text.h | 4 | ||||
-rw-r--r-- | rsvg.c | 459 | ||||
-rw-r--r-- | test-display.c | 142 | ||||
-rw-r--r-- | test-performance.c | 79 |
29 files changed, 7834 insertions, 928 deletions
@@ -1,37 +1,159 @@ -== librsvg 2.6.2 == +2004-03-17 Dom Lachowicz <cinamod@hotmail.com> -2004-03-15 Caleb Moore <c.moore@student.unsw.edu.au> + * *: re-merge rsvg-filters branch back into HEAD + +2004-03-16 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-styles.c: Implemented masks. + +2004-03-16 Caleb Moore <c.moore@student.unsw.edu.au> * rsvg-bpath-util.c: Applied double move fix contributed by ross alexander. + +2004-03-14 Dom Lachowicz <cinamod@.hotmail.com> + + * test-performance.c: Make this a useful performance tester -2004-03-15 Dom Lachowicz <cinamod@hotmail.com> +2004-03-12 Dom Lachowicz <cinamod@hotmail.com> - * rsvg-css.c: Work around something for jimmac + * rsvg-shapes.c: Allow for base64 embedded image data 2004-03-11 Dom Lachowicz <cinamod@hotmail.com> - * rsvg.c: More work on 105316 + * rsvg.c: Ability to scale SVGs that don't provide a width or height +2004-03-11 Dom Lachowicz <cinamod@hotmail.com> + + * rsvg-shapes.c: Improve image scaling, translation. Rotation is still + horked, though. Agree with Caleb's comments below. + +2004-03-11 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-shapes.c, rsvg-styles.c: added state domination, ability for a + use's values to overwrite even explicitly set state variables in the + symbol. + * rsvg-shapes.c: Fixed image bitmap sizing, may need to give the whole + system a good rewrite sometime to avoid having to use crappy libart + rotation etc. and eventually make it easier to port to cairo, added + ability to use filters with images. + * rsvg-filter.c: Fixed arithmetic compositition. + * rsvg-text.c: fixed little filter bug I opened up a few days ago + * rsvg-defs.c: fixed memory leak that means unnamed defs are never + freed. + +2004-03-10 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-shapes.c: exported drawable structures and functions + * rsvg-filter.c: made feImage work for internal references (like use) + * rsvg-css.c: fixed spelling of dodgerblue (was dogerblue) + +2004-03-09 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-shapes.c: HUUUUGE fixes to use + +2004-03-09 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-shapes.c: made groups "use"able + * rsvg-styles.c: changed inheritance mechanism + 2004-03-07 Dom Lachowicz <cinamod@hotmail.com> - * rsvg.c: Do something for clahey (bug #105316) + * rsvg.c: Do something for clahey (bug 105316) -2004-03-05 Dodji Seketeli <dodji@gnome.org> +2004-03-07 Dom Lachowicz <cinamod@hotmail.com> - * rsvg-styles.c: - (ccss_property): changed the signature of this function - to comply with the new signature of CRDocHandler::property() - in libcroco. + * gtk-engine/*.[ch]: Theme engine now renders SVGs at their display + size, thus cutting down on a lot of nasty raster scaling + +2004-03-04 Dom Lachowicz <cinamod@hotmail.com> -2004-03-04 Glynn Foster <glynn.foster@sun.com> + * configure.in: Forward-port things from Gman + * rsvg-styles.c: Forward-port patch from Dodji + * *.c: Use new RsvgPropertyBag interface + +2004-03-04 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Added final filter: feTile - * Makefile.am, configure.in, librsvg-2.0-uninstalled.pc.in: - Add uninstalled pkg-config file. +2004-03-04 Caleb Moore <c.moore@student.unsw.edu.au> -2004-02-19 Dom Lachowicz <cinamod@hotmail.com> + * rsvg-filter.c: Fixed feDiffuseLighting and feSpecular lighting to scale + more consistantly - * Makefile.am: disable vector text path code for the 2.6 release +2004-03-03 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Fixed feDiffuseLighting and feSpecular lighting for small + kernel length at the expense of speed, hopefully this can be optimised later. + Fixed a "what the hell was I thinking?!" bug in filter dimentioning. + +2004-03-02 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Added new filters: feDiffuseLighting and feSpecular lighting + +2004-02-29 Dom Lachowicz <cinamod@hotmail.com> + + * moz-plugin/moz-plugin.c: Make Netscape/Mozilla plugin kind-of work + +2004-02-29 Dom Lachowicz <cinamod@hotmail.com> + + * configure.in: + * Makefile.am: + * test-display.c: Build + install 'rsvg-view' + * rsvg-file-util.c: + * rsvg-private.h: Export some things that I need in order to build rsvg-view + +2004-02-29 Dom Lachowicz <cinamod@hotmail.com> + + * rsvg-filter.c: Added new filter: feImage + +2004-02-29 Dom Lachowicz <cinamod@hotmail.com> + + * rsvg-filter.c: Added new filter: feTurbulence + +2004-03-01 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Implemented bi-linear filtering for displacement + map. The algorithm is still pretty crap but it makes it look a lot + better than it did before. + +2004-02-29 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Added new filter: feDisplacementMap + +2004-02-28 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-text.c: Made standard text the default. Vector text was + annoying me. + * rsvg-filter.c: Fixed a segfault in feComponentTransfer + +2004-02-28 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-styles.c: made filters render before opacity is taken care of, + this seems like the official way it is done + * rsvg-filter.c: fixed a few problem with coordinate units and + dimentions + +2004-02-27 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-styles.c: implemented the "enable-background" style value. + This allows backgrounds to be used by filters even when they are + are based on multiple opacity levels. It also allows one to specify + exactly how many levels of groups one wants to be included in the + background. + +2004-02-19 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-styles.c, rsvg-styles.h: Changed "opacity groups" into + "discrete layers" which are like opacity groups but handle filters + as well as opacity groups and will eventually handle masks. This fixes + bugs relating to transperency and filters being used at the same time. + + * rsvg-filter.c: fixed a couple of little bugs relating to gaussian + blurs with a standard deviation less than 1 in an axis, it will now + just ommit blurring in this axis, saving us time. Also disabled perfect + blurs because they are really quite silly most of the time, Dom was + right. 2004-02-18 Brian Koebbe <brian@koebbe.org> @@ -40,7 +162,7 @@ 2004-02-17 Thomas Vander Stichele <thomas at apestaart dot org> * rsvg-gz.c: (rsvg_handle_gz_close_impl): - catch read error from gsf_input_read + catch read error from gsf_input_read break from infinite loops that do not manage to write (#134653) 2004-02-16 Dom Lachowicz <cinamod@hotmail.com> @@ -54,10 +176,69 @@ 2004-02-14 Dom Lachowicz <cinamod@hotmail.com> * rsvg.c: Fix bug #133947 + +2004-02-11 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: added feComposite filter + +2004-02-07 Dom Lachowicz <cinamod@hotmail.com> + + * rsvg-styles.[ch]: Implement get_font_size() function + * rsvg-shapes.c: Use the above fn + * rsvg-filter.c: Ditto + +2004-02-06 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: made gassian blur use 3 box blurs (technically 6 motion blurs) when the geometric mean of the standard deviations of the filter is over two. Misc enhancements in feConvolveMatrix + +2004-02-05 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Made colormatrix a lot safer, added checking to make sure colvolvematrix doesn't segfault, fixed embarrasing little buglet in feOffset. + * rsvg-filter.c: fixed table in feComponantTransfer, I won't pretend to know how I did it. It truely is an aweful hack, but its the best we have and it seems to work as per the examples + +2004-02-04 Dom Lachowicz <cinamod@hotmail.com> + + * rsvg-filter.c: Fix bug in pixbuf_get_alpha(), speed up pixbuf_new_cleared(), + be more paranoid in ComponentTransfer + +2004-02-04 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Implemented the feMorphology filter + +2004-02-04 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Implemented the ComponentTransfer filter, it works properly now + +2004-02-03 Dom Lachowicz <cinamod@hotmail.com> + + * rsvg-filter.[ch]: Start implementing ComponentTransfer filter + Indent, make code ansi-compliant. + * rsvg-css.[ch]: Tweak VBOX parsing, free unused string array properly + + +2004-02-03 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Implemented ColorMatrix filter, Made GaussianBlur scaled according to the current coordinate system + * rsvg-css.c: Added \n to the types of whitespace that splits arguments in list + +2004-02-02 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Implemented Merge filter, implemented Offset filter + +2004-02-01 Caleb Moore <c.moore@student.unsw.edu.au> + + * rsvg-filter.c: Implemented ConvolveMatrix filter -2004-01-31 Dom Lachowicz <cinamod@hotmail.com> +2004-02-01 Caleb Moore <c.moore@student.unsw.edu.au> - * rsvg-css.*: Hopefully fix bug #113538 + * rsvg-syles.c: implemented filter groups using the same code as the opacity grops + * rsvg-private.h: added a current filter pointer for loading primitives in context + * rsvg-defs.c: added new def type: filter + * rsvg.c: allowed filters and filter primitives to be specified in file, also added the ability for a group to use a filter if neccisary + * rsvg-shapes.c: added the ability for a shape to use a filter if it needs to + * rsvg-text.c: added the ability for text to use a filter if it needs to + * rsvg-filter.c: added new file + * rsvg-filter.h: added new file 2004-01-29 Dom Lachowicz <cinamod@hotmail.com> diff --git a/Makefile.am b/Makefile.am index 917aa9d1..19a5aa81 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,8 +1,14 @@ -SUBDIRS = . gtk-engine gdk-pixbuf-loader doc +SUBDIRS = . gtk-engine gdk-pixbuf-loader moz-plugin doc -bin_PROGRAMS = rsvg +if HAVE_GTK +target_rsvg_view = rsvg-view +else +target_rsvg_view = +endif + +bin_PROGRAMS = rsvg $(target_rsvg_view) -noinst_PROGRAMS = test-performance #test-display +noinst_PROGRAMS = test-performance man_MANS = rsvg.1 @@ -19,17 +25,6 @@ else libm = -lm endif -EXTRA_DIST = \ - COPYING.LIB \ - librsvg.spec.in \ - librsvg-2.0.pc.in \ - librsvg-2.0-uninstalled.pc.in \ - librsvg-zip.in \ - rsvg-text-vectors.c \ - rsvg-gz.c \ - rsvg-gz.h \ - $(man_MANS) - lib_LTLIBRARIES = librsvg-2.la librsvg_2_la_SOURCES = \ @@ -47,11 +42,14 @@ librsvg_2_la_SOURCES = \ rsvg-path.h \ rsvg-private.h \ rsvg-file-util.c \ + rsvg-filter.c \ + rsvg-filter.h \ rsvg-shapes.c \ rsvg-shapes.h \ rsvg-styles.c \ rsvg-styles.h \ rsvg-text.c \ + rsvg-text-vectors.c \ rsvg-text.h \ rsvg.c @@ -71,6 +69,7 @@ INCLUDES = \ -I$(top_builddir) \ $(LIBRSVG_CFLAGS) \ $(GTK_CFLAGS) \ + $(GDK_X11_CFLAGS) \ -DG_LOG_DOMAIN=\"librsvg\" \ -DDATADIR="\"$(datadir)\"" @@ -92,10 +91,22 @@ test_performance_LDFLAGS = test_performance_DEPENDENCIES = $(DEPS) test_performance_LDADD = $(LDADDS) $(libm) -#test_display_SOURCES=test-display.c -#test_display_LDFLAGS = -#test_display_DEPENDENCIES = $(DEPS) -#test_display_LDADD = $(LDADDS) $(GTK_LIBS) $(libm) +rsvg_view_SOURCES = \ + test-display.c +rsvg_view_LDFLAGS = +rsvg_view_DEPENDENCIES = $(DEPS) +rsvg_view_LDADD = $(LDADDS) $(GTK_LIBS) $(GDK_X11_LIBS) $(libm) + +EXTRA_DIST = \ + COPYING.LIB \ + librsvg.spec.in \ + librsvg-2.0.pc.in \ + librsvg-2.0-uninstalled.pc.in \ + librsvg-zip.in \ + rsvg-gz.c \ + rsvg-gz.h \ + $(man_MANS) \ + $(rsvg_view_SOURCES) tests: rsvg @@ -6,8 +6,6 @@ TODO: * Fixing image transforms - * Image effects/filters - * Fill patterns * Add actual GError support in the loader, rather then the g_warnings @@ -18,5 +16,4 @@ On the back burner: * Make error messages translatable (requires adding gettext). Requests from Christian: - * gaussian blur * gtk theme engine supporting automatic mouseover-and-light-up effect diff --git a/configure.in b/configure.in index 4be2456d..71ce6370 100644 --- a/configure.in +++ b/configure.in @@ -19,8 +19,8 @@ AC_SUBST(POPT_REQUIRED) dnl =========================================================================== LIBRSVG_MAJOR_VERSION=2 -LIBRSVG_MINOR_VERSION=6 -LIBRSVG_MICRO_VERSION=2 +LIBRSVG_MINOR_VERSION=7 +LIBRSVG_MICRO_VERSION=1 AC_SUBST(LIBRSVG_MAJOR_VERSION) AC_SUBST(LIBRSVG_MINOR_VERSION) AC_SUBST(LIBRSVG_MICRO_VERSION) @@ -203,23 +203,36 @@ else AC_MSG_RESULT(no) fi +have_gtk=no +PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 1.3.12, have_gtk=yes, + have_gtk=no) + +GTK_VERSION= +if test "x$have_gtk" = "xyes"; then + GTK_VERSION=`$PKG_CONFIG --variable=gtk_binary_version gtk+-2.0` +fi +AC_SUBST(GTK_CFLAGS) +AC_SUBST(GTK_LIBS) +AC_SUBST(GTK_VERSION) +AM_CONDITIONAL(HAVE_GTK, test "x$have_gtk" = "xyes") + +have_gdk_x11=no +PKG_CHECK_MODULES(GDK_X11, gdk-x11-2.0 >= 2.0.0, have_gdk_x11=yes, have_gdk_x11=no) +AC_SUBST(GDK_X11_CFLAGS) +AC_SUBST(GDK_X11_LIBS) + +if test "x$have_gdk_x11" = "xyes"; then + AC_DEFINE(ENABLE_XEMBED, 1, [Is XEmbed available]) +fi + AC_ARG_ENABLE(gtk-theme, [ --enable-gtk-theme Enable a RSVG based GTK+ theme engine [default=auto]],, enable_gtk_theme=yes) if test "x$enable_gtk_theme" = "xyes"; then - GTK_VERSION= - - PKG_CHECK_MODULES(GTK, gtk+-2.0 >= 1.3.12, enable_gtk_theme=yes, - enable_gtk_theme=no) - - if test "x$enable_gtk_theme" = "xyes"; then - GTK_VERSION=`$PKG_CONFIG --variable=gtk_binary_version gtk+-2.0` - fi - -AC_SUBST(GTK_CFLAGS) -AC_SUBST(GTK_LIBS) -AC_SUBST(GTK_VERSION) + if test "x$have_gtk" = "xno"; then + enable_gtk_theme=no; + fi fi AM_CONDITIONAL(ENABLE_GTK_ENGINE, test "x$enable_gtk_theme" = "xyes") @@ -241,6 +254,26 @@ fi AM_CONDITIONAL(ENABLE_PIXBUF_LOADER, test x$enable_pixbuf_loader = xyes) ################################################## +################################################## + +MOZILLA_CFLAGS= +if test -z "$MOZILLA_CONFIG"; then + AC_PATH_PROG(MOZILLA_CONFIG, mozilla-config, no) +fi +if test "x$MOZILLA_CONFIG" != "xno"; then + _mozilla_include_dir=`mozilla-config --cflags|sed 's/-I\(.*\) .*/\1/'` + MOZILLA_CFLAGS="-I$_mozilla_include_dir/plugin -I$_mozilla_include_dir/java -I$_mozilla_include_dir/nspr" +else + AC_MSG_WARN([mozilla-config not found. Mozilla/Netscape plugin will not be built]) +fi +build_mozilla_plugin=no +if test "x$MOZILLA_CFLAGS" != "x"; then + build_mozilla_plugin=yes +fi +AM_CONDITIONAL(WITH_MOZILLA,[test "x$build_mozilla_plugin" = "xyes"]) +AC_SUBST(MOZILLA_CFLAGS) + +################################################## # Checks for gtk-doc and docbook-tools ################################################## @@ -310,6 +343,7 @@ gtk-engine/examples/Makefile gtk-engine/examples/bubble/Makefile gtk-engine/examples/bubble/gtk-2.0/Makefile gdk-pixbuf-loader/Makefile +moz-plugin/Makefile ]) dnl ============================================================================================= @@ -321,10 +355,10 @@ librsvg-$LIBRSVG_VERSION Build GdkPixbuf loader: ${enable_pixbuf_loader} Build theme engine: ${enable_gtk_theme} + Build Netscape plugin: ${build_mozilla_plugin} Handle svgz files: ${test_gsf} Use libcroco for css parsing: ${test_croco} Build documentation: ${enable_gtk_doc} " -AC_MSG_RESULT([$croco_warning -]); +AC_MSG_RESULT([$croco_warning]); diff --git a/gtk-engine/Makefile.am b/gtk-engine/Makefile.am index 55853c52..50067a9e 100644 --- a/gtk-engine/Makefile.am +++ b/gtk-engine/Makefile.am @@ -30,3 +30,5 @@ libsvg_la_SOURCES = \ libsvg_la_LDFLAGS = -avoid-version -module $(no_undefined) libsvg_la_LIBADD = $(GTK_LIBS) $(LIBRSVG_LIBS) $(LIBCROCO_LIBS) $(top_builddir)/librsvg-2.la + +EXTRA_DIST = $(libsvg_la_SOURCES) diff --git a/gtk-engine/svg-draw.c b/gtk-engine/svg-draw.c index a1916203..0e673842 100644 --- a/gtk-engine/svg-draw.c +++ b/gtk-engine/svg-draw.c @@ -200,7 +200,7 @@ draw_gap_image(GtkStyle *style, components |= COMPONENT_CENTER; if (image->gap_start) - pixbuf = theme_pixbuf_get_pixbuf (image->gap_start); + pixbuf = theme_pixbuf_get_pixbuf (image->gap_start, -1, -1); switch (gap_side) { diff --git a/gtk-engine/svg-render.c b/gtk-engine/svg-render.c index c5dfbf88..3031e22a 100644 --- a/gtk-engine/svg-render.c +++ b/gtk-engine/svg-render.c @@ -21,11 +21,13 @@ * Carsten Haitzler <raster@rasterman.com> */ +#include <stdio.h> #include <string.h> #include "svg.h" #include <gdk-pixbuf/gdk-pixbuf.h> #include <rsvg.h> +#include <rsvg-gz.h> GCache *pixbuf_cache = NULL; @@ -443,7 +445,7 @@ theme_pixbuf_new (void) { ThemePixbuf *result = g_new0 (ThemePixbuf, 1); result->filename = NULL; - result->pixbuf = NULL; + result->svg_bytes = NULL; result->stretch = TRUE; result->border_left = 0; @@ -465,10 +467,10 @@ void theme_pixbuf_set_filename (ThemePixbuf *theme_pb, const char *filename) { - if (theme_pb->pixbuf) + if (theme_pb->svg_bytes) { - g_cache_remove (pixbuf_cache, theme_pb->pixbuf); - theme_pb->pixbuf = NULL; + g_cache_remove (pixbuf_cache, theme_pb->svg_bytes); + theme_pb->svg_bytes = NULL; } if (theme_pb->filename) @@ -546,11 +548,11 @@ compute_hint (GdkPixbuf *pixbuf, } static void -theme_pixbuf_compute_hints (ThemePixbuf *theme_pb) +theme_pixbuf_compute_hints (ThemePixbuf *theme_pb, GdkPixbuf *pixbuf) { int i, j; - gint width = gdk_pixbuf_get_width (theme_pb->pixbuf); - gint height = gdk_pixbuf_get_height (theme_pb->pixbuf); + gint width = gdk_pixbuf_get_width (pixbuf); + gint height = gdk_pixbuf_get_height (pixbuf); if (theme_pb->border_left + theme_pb->border_right > width || theme_pb->border_top + theme_pb->border_bottom > height) @@ -610,7 +612,7 @@ theme_pixbuf_compute_hints (ThemePixbuf *theme_pb) break; } - theme_pb->hints[i][j] = compute_hint (theme_pb->pixbuf, x0, x1, y0, y1); + theme_pb->hints[i][j] = compute_hint (pixbuf, x0, x1, y0, y1); } } @@ -627,9 +629,6 @@ theme_pixbuf_set_border (ThemePixbuf *theme_pb, theme_pb->border_right = right; theme_pb->border_top = top; theme_pb->border_bottom = bottom; - - if (theme_pb->pixbuf) - theme_pixbuf_compute_hints (theme_pb); } void @@ -637,46 +636,116 @@ theme_pixbuf_set_stretch (ThemePixbuf *theme_pb, gboolean stretch) { theme_pb->stretch = stretch; +} + +static void +svg_cache_value_free(gpointer foo) +{ + GByteArray * arr; + + arr = (GByteArray *)foo; + if(arr != NULL) + g_byte_array_free(arr, TRUE); +} + +#define SVG_BUFFER_SIZE (1024*8) + +static GByteArray * +svg_cache_value_new (gchar *filename) +{ + GByteArray *result = NULL; + FILE *fp; + + fp = fopen(filename, "rb"); + if(fp) + { + size_t nread; + char buf[SVG_BUFFER_SIZE]; + + result = g_byte_array_new(); + while((nread = fread(buf, 1, sizeof(buf), fp)) > 0) + g_byte_array_append(result, buf, nread); + + fclose(fp); + } + else + { + g_warning("Couldn't load theme part: %s\n", filename); + } + + return result; +} + +struct SizeInfo +{ + gint width, height; +}; + +static void set_size_fn(gint *width, gint *height, gpointer foo) +{ + struct SizeInfo * info = (struct SizeInfo *)foo; - if (theme_pb->pixbuf) - theme_pixbuf_compute_hints (theme_pb); + *width = info->width; + *height = info->height; } static GdkPixbuf * -pixbuf_cache_value_new (gchar *filename) +get_pixbuf(GByteArray * arr, gint width, gint height) { - GError *err = NULL; - - GdkPixbuf *result = rsvg_pixbuf_from_file (filename, &err); - if (!result) + RsvgHandle * handle; + GdkPixbuf * result; + + if(!arr || !arr->len) + return NULL; + +#ifdef HAVE_SVGZ + if((arr->len >= 2) && (arr->data[0] == (guchar)0x1f) && (arr->data[1] == (guchar)0x8b)) + handle = rsvg_handle_new_gz(); + else +#endif + handle = rsvg_handle_new(); + + if(width > 0 && height > 0) { - g_warning ("Rsvg theme: Cannot load SVG file %s: %s\n", - filename, err->message); - g_error_free (err); + struct SizeInfo info; + + info.width = width; + info.height = height; + + rsvg_handle_set_size_callback(handle, set_size_fn, &info, NULL); } + rsvg_handle_write(handle, arr->data, arr->len, NULL); + rsvg_handle_close(handle, NULL); + result = rsvg_handle_get_pixbuf(handle); + + rsvg_handle_free(handle); + return result; } GdkPixbuf * -theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb) +theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb, gint width, gint height) { - if (!theme_pb->pixbuf) + GdkPixbuf *result = NULL; + + if (!theme_pb->svg_bytes) { if (!pixbuf_cache) - pixbuf_cache = g_cache_new ((GCacheNewFunc)pixbuf_cache_value_new, - (GCacheDestroyFunc)gdk_pixbuf_unref, + pixbuf_cache = g_cache_new ((GCacheNewFunc)svg_cache_value_new, + (GCacheDestroyFunc)svg_cache_value_free, (GCacheDupFunc)g_strdup, (GCacheDestroyFunc)g_free, g_str_hash, g_direct_hash, g_str_equal); - theme_pb->pixbuf = g_cache_insert (pixbuf_cache, theme_pb->filename); - - if (theme_pb->stretch) - theme_pixbuf_compute_hints (theme_pb); + theme_pb->svg_bytes = g_cache_insert (pixbuf_cache, theme_pb->filename); } - return theme_pb->pixbuf; + result = get_pixbuf(theme_pb->svg_bytes, width, height); + if(result) + theme_pixbuf_compute_hints(theme_pb, result); + + return result; } void @@ -691,7 +760,7 @@ theme_pixbuf_render (ThemePixbuf *theme_pb, gint width, gint height) { - GdkPixbuf *pixbuf = theme_pixbuf_get_pixbuf (theme_pb); + GdkPixbuf *pixbuf = theme_pixbuf_get_pixbuf (theme_pb, width, height); gint src_x[4], src_y[4], dest_x[4], dest_y[4]; gint pixbuf_width = gdk_pixbuf_get_width (pixbuf); gint pixbuf_height = gdk_pixbuf_get_height (pixbuf); @@ -804,4 +873,6 @@ theme_pixbuf_render (ThemePixbuf *theme_pb, gdk_pixmap_unref (tmp_pixmap); } } + + g_object_unref(G_OBJECT(pixbuf)); } diff --git a/gtk-engine/svg.h b/gtk-engine/svg.h index 6de7e0e5..107c71b3 100644 --- a/gtk-engine/svg.h +++ b/gtk-engine/svg.h @@ -129,14 +129,14 @@ typedef enum { struct _ThemePixbuf { - gchar *filename; - GdkPixbuf *pixbuf; - gboolean stretch; - gint border_left; - gint border_right; - gint border_bottom; - gint border_top; - guint hints[3][3]; + gchar *filename; + GByteArray *svg_bytes; + gboolean stretch; + gint border_left; + gint border_right; + gint border_bottom; + gint border_top; + guint hints[3][3]; }; struct _ThemeMatchData @@ -173,7 +173,7 @@ ThemePixbuf *theme_pixbuf_new (void); void theme_pixbuf_destroy (ThemePixbuf *theme_pb); void theme_pixbuf_set_filename (ThemePixbuf *theme_pb, const char *filename); -GdkPixbuf * theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb); +GdkPixbuf * theme_pixbuf_get_pixbuf (ThemePixbuf *theme_pb, gint width, gint height); void theme_pixbuf_set_border (ThemePixbuf *theme_pb, gint left, gint right, diff --git a/moz-plugin/Makefile.am b/moz-plugin/Makefile.am index 9c439aac..2c3c078d 100644 --- a/moz-plugin/Makefile.am +++ b/moz-plugin/Makefile.am @@ -1,4 +1,3 @@ - plugindir = $(libdir)/mozilla/plugins/ if WITH_MOZILLA @@ -7,10 +6,12 @@ else plugin_LTLIBRARIES = endif +INCLUDES+=$(MOZILLA_CFLAGS) -DBINDIR=\"$(bindir)/\" $(GDK_X11_CFLAGS) + libmozsvgdec_la_SOURCES = moz-plugin.c -libmozsvgdec_la_CFLAGS = $(MOZILLA_CFLAGS) $(X_CFLAGS) -DBINDIR=\"$(bindir)\" -libmozsvgdec_la_LDFLAGS = -module -avoid-version $(X_LIBS) -libmozsvgdec_la_LIBADD = -lXt $(LIBRSVG_LIBS) \ +libmozsvgdec_la_LDFLAGS = -module -avoid-version +libmozsvgdec_la_LIBADD = $(LIBRSVG_LIBS) \ $(LIBGSF_LIBS) $(LIBCROCO_LIBS)\ $(top_builddir)/librsvg-2.la +EXTRA_DIST=moz-plugin.c diff --git a/moz-plugin/moz-plugin.c b/moz-plugin/moz-plugin.c index a0662035..1135d8f9 100644 --- a/moz-plugin/moz-plugin.c +++ b/moz-plugin/moz-plugin.c @@ -2,7 +2,7 @@ /* moz-plugin.c: Mozilla plugin - Copyright (C) 2003 Dom Lachowicz <cinamod@hotmail.com> + Copyright (C) 2003-2004 Dom Lachowicz <cinamod@hotmail.com> Copyright (C) 2003 David Schleef <ds@schleef.org> This program is free software; you can redistribute it and/or @@ -23,34 +23,131 @@ Author: Dom Lachowicz <cinamod@hotmail.com> */ +#include <config.h> + #include <stdio.h> #include <stdint.h> #include <unistd.h> #include <sys/wait.h> -#include <config.h> #include <X11/Xlib.h> #include <X11/Intrinsic.h> +#include <glib.h> + #define XP_UNIX 1 #define MOZ_X11 1 #include "npapi.h" #include "npupp.h" -#define DEBUG(x) +#define DEBUG(x) /* printf x */ typedef struct { NPP instance; - Display *display; Window window; - int x; - int y; + int width, height; + + GByteArray * bytes; + + int send_fd; + int player_pid; } Plugin; static NPNetscapeFuncs mozilla_funcs; +static void +plugin_kill (Plugin * plugin) +{ + if(plugin->send_fd > 0) + { + close (plugin->send_fd); + plugin->send_fd = -1; + } + + if(plugin->player_pid > 0) + { + kill (plugin->player_pid, SIGKILL); + waitpid (plugin->player_pid, NULL, 0); + + plugin->player_pid = -1; + } +} + +static void +plugin_fork (Plugin * plugin) +{ + char xid_str[20]; + char width_str[20]; + char height_str[20]; + char *argv[20]; + int argc = 0; + GError *err = NULL; + + DEBUG(("plugin fork\n")); + + sprintf (xid_str, "%ld", plugin->window); + + argv[argc++] = BINDIR "rsvg-view"; + argv[argc++] = "-i"; /* xid */ + argv[argc++] = xid_str; + + if (plugin->width) + { + sprintf (width_str, "%d", plugin->width); + argv[argc++] = "-w"; /* width */ + argv[argc++] = width_str; + } + + if (plugin->height) + { + sprintf (height_str, "%d", plugin->height); + argv[argc++] = "-h"; /* height */ + argv[argc++] = height_str; + } + + argv[argc++] = "-s"; + argv[argc] = NULL; + + if(!g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_STDOUT_TO_DEV_NULL, + NULL, NULL, &plugin->player_pid, + &plugin->send_fd, NULL, NULL, &err)) + { + DEBUG(("Spawn failed\n")); + + if(err) + { + fprintf(stderr, "%s\n", err->message); + g_error_free(err); + } + } +} + +static void +plugin_redraw (Plugin * plugin) +{ + DEBUG(("plugin redraw\n")); + + if(plugin && plugin->bytes && plugin->bytes->len) + { + if (plugin->player_pid <= 0) + { + plugin_fork (plugin); + + if(plugin->player_pid > 0) + { + size_t nwritten = 0; + while(nwritten < plugin->bytes->len) + nwritten += write (plugin->send_fd, plugin->bytes->data + nwritten, plugin->bytes->len - nwritten); + + g_byte_array_free (plugin->bytes, TRUE); + plugin->bytes = 0; + } + } + } +} + static NPError plugin_newp (NPMIMEType mime_type, NPP instance, uint16_t mode, int16_t argc, char *argn[], char *argv[], @@ -59,14 +156,14 @@ plugin_newp (NPMIMEType mime_type, NPP instance, Plugin *plugin; int i; - DEBUG ("plugin_newp"); - + DEBUG (("plugin_newp\n")); + if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; instance->pdata = mozilla_funcs.memalloc (sizeof (Plugin)); plugin = (Plugin *) instance->pdata; - + if (plugin == NULL) return NPERR_OUT_OF_MEMORY_ERROR; memset (plugin, 0, sizeof (Plugin)); @@ -76,18 +173,16 @@ plugin_newp (NPMIMEType mime_type, NPP instance, for (i = 0; i < argc; i++) { - printf ("argv[%d] %s %s\n", i, argn[i], argv[i]); + DEBUG (("argv[%d] %s %s\n", i, argn[i], argv[i])); + if (strcmp (argn[i], "width") == 0) - { - plugin->width = strtol (argv[i], NULL, 0); - } + plugin->width = strtol (argv[i], NULL, 0); + if (strcmp (argn[i], "height") == 0) - { - plugin->height = strtol (argv[i], NULL, 0); - } - } - - return NPERR_NO_ERROR; + plugin->height = strtol (argv[i], NULL, 0); + } + + return NPERR_NO_ERROR; } static NPError @@ -95,17 +190,20 @@ plugin_destroy (NPP instance, NPSavedData ** save) { Plugin *plugin; - DEBUG ("plugin_destroy"); + DEBUG (("plugin_destroy\n")); if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; plugin = (Plugin *) instance->pdata; if (plugin == NULL) - { - return NPERR_NO_ERROR; - } + return NPERR_NO_ERROR; + + if(plugin->bytes) + g_byte_array_free (plugin->bytes, TRUE); + plugin_kill (plugin); + mozilla_funcs.memfree (instance->pdata); instance->pdata = NULL; @@ -116,45 +214,45 @@ static NPError plugin_set_window (NPP instance, NPWindow * window) { Plugin *plugin; - - DEBUG ("plugin_set_window"); + + DEBUG (("plugin_set_window\n")); if (instance == NULL) return NPERR_INVALID_INSTANCE_ERROR; - + plugin = (Plugin *) instance->pdata; if (plugin == NULL) return NPERR_INVALID_INSTANCE_ERROR; if (plugin->window) { - DEBUG ("existing window"); + DEBUG (("existing window\n")); + if (plugin->window == (Window) window->window) { - DEBUG ("resize"); - /* Resize event */ - /* Not currently handled */ + DEBUG (("resize\n")); + + plugin->width = window->width; + plugin->height = window->height; + + plugin_redraw (plugin); } else { - DEBUG ("change"); - printf ("ack. window changed!\n"); + DEBUG (("change. ack. window changed!\n")); } } else { NPSetWindowCallbackStruct *ws_info; - DEBUG ("about to fork"); + DEBUG (("about to fork\n")); ws_info = window->ws_info; plugin->window = (Window) window->window; - plugin->display = ws_info->display; - - plugin_fork (plugin); } - DEBUG ("leaving plugin_set_window"); + DEBUG (("leaving plugin_set_window\n")); return NPERR_NO_ERROR; } @@ -163,16 +261,47 @@ static NPError plugin_new_stream (NPP instance, NPMIMEType type, const char *window, NPStream ** stream_ptr) { - DEBUG ("plugin_new_stream"); + Plugin *plugin; + + DEBUG (("plugin_new_stream\n")); + + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + plugin = (Plugin *) instance->pdata; + if (plugin == NULL) + return NPERR_NO_ERROR; + + g_return_val_if_fail(plugin->bytes == NULL, NPERR_NO_ERROR); + + plugin->bytes = g_byte_array_new(); + return NPERR_NO_ERROR; } static NPError plugin_destroy_stream (NPP instance, NPStream * stream, NPError reason) { - DEBUG ("plugin_destroy_stream"); + Plugin *plugin; + + DEBUG (("plugin_destroy_stream\n")); + + if (instance == NULL) + return NPERR_INVALID_INSTANCE_ERROR; + + plugin = (Plugin *) instance->pdata; + if (plugin == NULL) + return NPERR_NO_ERROR; + /* trigger */ + plugin_redraw (plugin); + + if(plugin->send_fd > 0) + { + close (plugin->send_fd); + plugin->send_fd = -1; + } + return NPERR_NO_ERROR; } @@ -181,9 +310,9 @@ plugin_write_ready (NPP instance, NPStream * stream) { /* This is arbitrary */ - DEBUG ("plugin_write_ready"); + DEBUG (("plugin_write_ready\n")); - return 4096; + return (8*1024); } static int32 @@ -192,18 +321,21 @@ plugin_write (NPP instance, NPStream * stream, int32 offset, { Plugin *plugin; - DEBUG ("plugin_write"); + DEBUG (("plugin_write\n")); if (instance == NULL) return 0; + plugin = (Plugin *) instance->pdata; if (plugin == NULL) return 0; - /* TODO */ - write (plugin->send_fd, buffer, len); + if (!plugin->bytes) + return 0; + g_byte_array_append (plugin->bytes, buffer, len); + return len; } @@ -212,7 +344,7 @@ plugin_stream_as_file (NPP instance, NPStream * stream, const char *fname) { Plugin *plugin; - DEBUG ("plugin_stream_as_file"); + DEBUG (("plugin_stream_as_file\n")); if (instance == NULL) return; @@ -221,11 +353,13 @@ plugin_stream_as_file (NPP instance, NPStream * stream, const char *fname) if (plugin == NULL) return; - printf ("plugin_stream_as_file\n"); + DEBUG (("plugin_stream_as_file\n")); } /* exported functions */ +NPError NP_GetValue (void *future, NPPVariable variable, void *value); + NPError NP_GetValue (void *future, NPPVariable variable, void *value) { @@ -250,19 +384,20 @@ NP_GetValue (void *future, NPPVariable variable, void *value) default: err = NPERR_GENERIC_ERROR; } - return err; + + return err; } char * NP_GetMIMEDescription (void) { - return ("application/svg:svg:Scalable Vector Graphics"); + return ("image/svg+xml:svg:Scalable Vector Graphics"); } NPError NP_Initialize (NPNetscapeFuncs * moz_funcs, NPPluginFuncs * plugin_funcs) { - printf ("NP_Initialize\n"); + DEBUG (("NP_Initialize\n")); if (moz_funcs == NULL || plugin_funcs == NULL) return NPERR_INVALID_FUNCTABLE_ERROR; @@ -273,7 +408,7 @@ NP_Initialize (NPNetscapeFuncs * moz_funcs, NPPluginFuncs * plugin_funcs) return NPERR_INVALID_FUNCTABLE_ERROR; if (plugin_funcs->size < sizeof (NPPluginFuncs)) return NPERR_INVALID_FUNCTABLE_ERROR; - + memcpy (&mozilla_funcs, moz_funcs, sizeof (NPNetscapeFuncs)); plugin_funcs->version = (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR; @@ -53,29 +53,29 @@ gboolean rsvg_css_parse_vbox (const char * vbox, double * x, double * y, double * w, double * h) { - gchar ** list; + gdouble * list; guint list_len; - list = rsvg_css_parse_list(vbox, &list_len); + list = rsvg_css_parse_number_list(vbox, &list_len); - if(!list) + if(!(list && list_len)) return FALSE; else if(list_len != 4) { - g_strfreev(list); + g_free(list); return FALSE; } else { - /* TODO: error checking */ - *x = g_ascii_strtod(list[0], NULL); - *y = g_ascii_strtod(list[1], NULL); - *w = g_ascii_strtod(list[2], NULL); - *h = g_ascii_strtod(list[3], NULL); + *x = list[0]; + *y = list[1]; + *w = list[2]; + *h = list[3]; - g_strfreev(list); + g_free(list); return TRUE; } } + /** * rsvg_css_parse_length: Parse CSS2 length to a pixel value. * @str: Original string. @@ -340,7 +340,7 @@ rsvg_css_parse_color (const char *str) { "deepskyblue", PACK_RGB (0,191,255) }, { "dimgray", PACK_RGB (105,105,105) }, { "dimgrey", PACK_RGB (105,105,105) }, - { "dogerblue", PACK_RGB (30,144,255) }, + { "dodgerblue", PACK_RGB (30,144,255) }, { "firebrick", PACK_RGB (178,34,34) }, { "floralwhite" , PACK_RGB (255,255,240)}, { "forestgreen", PACK_RGB (34,139,34) }, @@ -669,7 +669,7 @@ rsvg_css_parse_font_family (const char * str, const char * inherit) return str; } -#if !defined(HAVE_STRTOK_R) /* && !GLIB_CHECK_VERSION(2, 3, 2) */ +#if !defined(HAVE_STRTOK_R) && !GLIB_CHECK_VERSION(2, 3, 2) static char * strtok_r(char *s, const char *delim, char **last) @@ -717,7 +717,7 @@ rsvg_css_parse_list(const char * in_str, guint * out_list_len) /* this may fix bug #113538 */ - string_array = g_strsplit_set(in_str, ", \t", -1); + string_array = g_strsplit_set(in_str, ", \t\n", -1); for(n = 0; string_array[n] != NULL; n++) ; @@ -767,6 +767,34 @@ rsvg_css_parse_list(const char * in_str, guint * out_list_len) #endif } +gdouble * +rsvg_css_parse_number_list(const char * in_str, guint * out_list_len){ + gchar ** string_array; + gdouble * output; + guint len, i; + + if(out_list_len) + *out_list_len = 0; + + string_array = rsvg_css_parse_list(in_str, &len); + + if(!(string_array && len)) + return NULL; + + output = g_new(gdouble, len); + + /* TODO: some error checking */ + for (i = 0; i < len; i++) + output[i] = g_ascii_strtod(string_array[i], NULL); + + g_strfreev(string_array); + + if (out_list_len != NULL) + *out_list_len = len; + + return output; +} + void rsvg_css_parse_number_optional_number(const char * str, double *x, double *y) @@ -85,6 +85,9 @@ rsvg_css_parse_number_optional_number(const char * str, gchar ** rsvg_css_parse_list(const char * in_str, guint * out_list_len); +gdouble * +rsvg_css_parse_number_list(const char * in_str, guint * out_list_len); + G_END_DECLS #endif /* RSVG_CSS_H */ diff --git a/rsvg-defs.c b/rsvg-defs.c index f94db754..096b384f 100644 --- a/rsvg-defs.c +++ b/rsvg-defs.c @@ -23,6 +23,7 @@ */ #include "config.h" +#include "rsvg-private.h" #include "rsvg-defs.h" #include <glib/ghash.h> @@ -32,6 +33,7 @@ struct _RsvgDefs { GHashTable *hash; + GPtrArray *unnamed; }; RsvgDefs * @@ -40,6 +42,7 @@ rsvg_defs_new (void) RsvgDefs *result = g_new (RsvgDefs, 1); result->hash = g_hash_table_new (g_str_hash, g_str_equal); + result->unnamed = g_ptr_array_new (); return result; } @@ -53,9 +56,10 @@ rsvg_defs_lookup (const RsvgDefs *defs, const char *name) void rsvg_defs_set (RsvgDefs *defs, const char *name, RsvgDefVal *val) { - g_return_if_fail(name); - - g_hash_table_insert (defs->hash, g_strdup (name), val); + if (name == NULL) + g_ptr_array_add(defs->unnamed, val); + else + g_hash_table_insert (defs->hash, g_strdup (name), val); } static void @@ -69,7 +73,12 @@ rsvg_defs_free_each (gpointer key, gpointer value, gpointer user_data) void rsvg_defs_free (RsvgDefs *defs) { + guint i; g_hash_table_foreach (defs->hash, rsvg_defs_free_each, NULL); g_hash_table_destroy (defs->hash); + for (i = 0; i < defs->unnamed->len; i++) + ((RsvgDefVal *)g_ptr_array_index(defs->unnamed, i))->free(g_ptr_array_index(defs->unnamed, i)); + + g_ptr_array_free(defs->unnamed, FALSE); g_free (defs); } diff --git a/rsvg-defs.h b/rsvg-defs.h index 3dc6445d..350bdb7f 100644 --- a/rsvg-defs.h +++ b/rsvg-defs.h @@ -32,9 +32,6 @@ G_BEGIN_DECLS -typedef struct _RsvgDefs RsvgDefs; -typedef struct _RsvgDefVal RsvgDefVal; - typedef enum { /* todo: general question: should this be high level, ie a generic paint server, coupled with a paint server interface; or low level, @@ -43,7 +40,8 @@ typedef enum { RSVG_DEF_LINGRAD, RSVG_DEF_RADGRAD, RSVG_DEF_PATTERN, - RSVG_DEF_PATH + RSVG_DEF_PATH, + RSVG_DEF_FILTER } RsvgDefType; struct _RsvgDefVal { diff --git a/rsvg-file-util.c b/rsvg-file-util.c index 58d61bb6..8d45b10a 100644 --- a/rsvg-file-util.c +++ b/rsvg-file-util.c @@ -25,6 +25,7 @@ #include "config.h" #include "rsvg.h" +#include "rsvg-private.h" #if HAVE_SVGZ #include "rsvg-gz.h" @@ -35,23 +36,11 @@ #include <stdlib.h> #include <math.h> -#define SVG_BUFFER_SIZE (1024 * 8) - -typedef enum { - RSVG_SIZE_ZOOM, - RSVG_SIZE_WH, - RSVG_SIZE_WH_MAX, - RSVG_SIZE_ZOOM_MAX -} RsvgSizeType; +#ifdef G_OS_UNIX +#include <fcntl.h> +#endif -struct RsvgSizeCallbackData -{ - RsvgSizeType type; - double x_zoom; - double y_zoom; - gint width; - gint height; -}; +#define SVG_BUFFER_SIZE (1024 * 8) static void rsvg_size_callback (int *width, @@ -133,7 +122,7 @@ rsvg_pixbuf_from_file_with_size_data_ex (RsvgHandle * handle, rsvg_handle_set_size_callback (handle, rsvg_size_callback, data, NULL); - while ((result = fread (chars, 1, SVG_BUFFER_SIZE, f)) > 0) + while (!feof(f) && !ferror(f) && ((result = fread (chars, 1, SVG_BUFFER_SIZE, f)) > 0)) rsvg_handle_write (handle, chars, result, error); rsvg_handle_close (handle, error); @@ -143,60 +132,72 @@ rsvg_pixbuf_from_file_with_size_data_ex (RsvgHandle * handle, return retval; } -static GdkPixbuf * -rsvg_pixbuf_from_file_with_size_data (const gchar * file_name, - struct RsvgSizeCallbackData * data, - GError ** error) +/* private */ +GdkPixbuf * +rsvg_pixbuf_from_stdio_file_with_size_data(FILE * f, + struct RsvgSizeCallbackData * data, + GError ** error) { -#if HAVE_SVGZ RsvgHandle * handle; + GdkPixbuf * retval; guchar chars[SVG_BUFFER_SIZE]; - GdkPixbuf *retval; - gint result; - FILE *f = fopen (file_name, "rb"); + int result; - if (!f) - { - g_set_error (error, G_FILE_ERROR, - g_file_error_from_errno (errno), - g_strerror (errno)); - return NULL; - } - result = fread (chars, 1, SVG_BUFFER_SIZE, f); if (result == 0) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), g_strerror (errno)); - fclose (f); return NULL; } +#if HAVE_SVGZ /* test for GZ marker */ if ((result >= 2) && (chars[0] == (guchar)0x1f) && (chars[1] == (guchar)0x8b)) handle = rsvg_handle_new_gz (); else handle = rsvg_handle_new (); +#else + handle = rsvg_handle_new (); +#endif rsvg_handle_set_size_callback (handle, rsvg_size_callback, data, NULL); + rsvg_handle_write (handle, chars, result, error); - while ((result = fread (chars, 1, SVG_BUFFER_SIZE, f)) > 0) + while (!feof(f) && !ferror(f) && ((result = fread (chars, 1, SVG_BUFFER_SIZE, f)) > 0)) rsvg_handle_write (handle, chars, result, error); rsvg_handle_close (handle, error); retval = rsvg_handle_get_pixbuf (handle); - - fclose (f); - rsvg_handle_free (handle); - return retval; -#else - RsvgHandle * handle = rsvg_handle_new (); - GdkPixbuf * retval = rsvg_pixbuf_from_file_with_size_data_ex (handle, file_name, data, error); rsvg_handle_free (handle); + return retval; -#endif +} + +/* private */ +GdkPixbuf * +rsvg_pixbuf_from_file_with_size_data (const gchar * file_name, + struct RsvgSizeCallbackData * data, + GError ** error) +{ + GdkPixbuf * pixbuf; + FILE *f = fopen (file_name, "rb"); + + if (!f) + { + g_set_error (error, G_FILE_ERROR, + g_file_error_from_errno (errno), + g_strerror (errno)); + return NULL; + } + + pixbuf = rsvg_pixbuf_from_stdio_file_with_size_data(f, data, error); + + fclose(f); + + return pixbuf; } diff --git a/rsvg-filter.c b/rsvg-filter.c new file mode 100644 index 00000000..7e36bb11 --- /dev/null +++ b/rsvg-filter.c @@ -0,0 +1,4860 @@ +/* vim: set sw=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + rsvg-filter.c: Provides filters + + Copyright (C) 2004 Caleb Moore + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Caleb Moore <calebmm@tpg.com.au> +*/ + +#include "rsvg-private.h" +#include "rsvg-filter.h" +#include "rsvg-styles.h" +#include "rsvg-shapes.h" +#include "rsvg-css.h" +#include <libart_lgpl/art_rgba.h> + +#include <math.h> + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif /* M_PI */ + +#define PERFECTBLUR 0 + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterContext RsvgFilterContext; + +struct _RsvgFilterContext +{ + gint width, height; + RsvgFilter *filter; + GHashTable *results; + GdkPixbuf *source; + GdkPixbuf *bg; + GdkPixbuf *lastresult; + double affine[6]; + double paffine[6]; + RsvgHandle * ctx; +}; + +typedef struct _RsvgFilterPrimitive RsvgFilterPrimitive; + +struct _RsvgFilterPrimitive +{ + double x, y, width, height; + GString *in; + GString *result; + gboolean sizedefaults; + + void (*free) (RsvgFilterPrimitive * self); + void (*render) (RsvgFilterPrimitive * self, RsvgFilterContext * ctx); +}; + +typedef struct +{ + gint x1, y1, x2, y2; +} FPBox; + +/*************************************************************/ +/*************************************************************/ + +static void +rsvg_filter_primitive_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + self->render (self, ctx); +} + +static void +rsvg_filter_primitive_free (RsvgFilterPrimitive * self) +{ + self->free (self); +} + +static FPBox +rsvg_filter_primitive_get_bounds (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + FPBox output; + int skip; + skip = 0; + + if (self == NULL) + skip = 1; + else if (self->sizedefaults) + skip = 1; + + if (skip) + { + output.x1 = ctx->affine[0] * ctx->filter->x + ctx->affine[4]; + output.y1 = ctx->affine[3] * ctx->filter->y + ctx->affine[5]; + output.x2 = + ctx->affine[0] * (ctx->filter->x + ctx->filter->width) + + ctx->affine[4]; + output.y2 = + ctx->affine[3] * (ctx->filter->y + ctx->filter->height) + + ctx->affine[5]; + + if (output.x1 < 0) + output.x1 = 0; + if (output.x2 > ctx->width) + output.x2 = ctx->width; + if (output.y1 < 0) + output.y1 = 0; + if (output.y2 > ctx->height) + output.y2 = ctx->height; + + return output; + } + + output.x1 = ctx->paffine[0] * self->x + ctx->paffine[4]; + output.y1 = ctx->paffine[3] * self->y + ctx->paffine[5]; + output.x2 = ctx->paffine[0] * (self->x + self->width) + ctx->paffine[4]; + output.y2 = ctx->paffine[3] * (self->y + self->height) + ctx->paffine[5]; + + if (output.x1 < ctx->affine[0] * ctx->filter->x + ctx->affine[4]) + output.x1 = ctx->affine[0] * ctx->filter->x + ctx->affine[4]; + if (output.x2 > + ctx->affine[0] * (ctx->filter->x + ctx->filter->width) + ctx->affine[4]) + output.x2 = + ctx->affine[0] * (ctx->filter->x + ctx->filter->width) + ctx->affine[4]; + if (output.y1 < ctx->affine[3] * ctx->filter->y + ctx->affine[5]) + output.y1 = ctx->affine[3] * ctx->filter->y + ctx->affine[5]; + if (output.y2 > ctx->affine[3] * (ctx->filter->y + ctx->filter->height) + + ctx->affine[5]) + output.y2 = ctx->affine[3] * (ctx->filter->y + ctx->filter->height) + + ctx->affine[5]; + + if (output.x1 < 0) + output.x1 = 0; + if (output.x2 > ctx->width) + output.x2 = ctx->width; + if (output.y1 < 0) + output.y1 = 0; + if (output.y2 > ctx->height) + output.y2 = ctx->height; + + return output; +} + +static GdkPixbuf * +gdk_pixbuf_new_cleared (GdkColorspace colorspace, gboolean has_alpha, int bits_per_sample, + int width, int height) +{ + GdkPixbuf *pb; + guchar *data; + + pb = gdk_pixbuf_new (colorspace, has_alpha, bits_per_sample, width, height); + data = gdk_pixbuf_get_pixels (pb); + memset(data, 0, width * height * 4); + + return pb; +} + +static guchar +gdk_pixbuf_get_interp_pixel(guchar * src, gdouble ox, gdouble oy, guchar ch, FPBox boundarys, guint rowstride) +{ + double xmod, ymod; + double dist1, dist2, dist3, dist4; + double c, c1, c2, c3, c4; + + xmod = fmod(ox, 1.0); + ymod = fmod(oy, 1.0); + + dist1 = (1 - xmod) * (1 - ymod); + dist2 = (xmod) * (1 - ymod); + dist3 = (xmod) * (ymod); + dist4 = (1 - xmod) * (ymod); + + if (floor(ox) <= boundarys.x1 || floor(ox) >= boundarys.x2 || + floor(oy) <= boundarys.y1 || floor(oy) >= boundarys.y2) + c1 = 0; + else + c1 = src[(guint)floor(oy) * rowstride + (guint)floor(ox) * 4 + ch]; + + if (ceil(ox) <= boundarys.x1 || ceil(ox) >= boundarys.x2 || + floor(oy) <= boundarys.y1 || floor(oy) >= boundarys.y2) + c2 = 0; + else + c2 = src[(guint)floor(oy) * rowstride + (guint)ceil(ox) * 4 + ch]; + + if (ceil(ox) <= boundarys.x1 || ceil(ox) >= boundarys.x2 || + ceil(oy) <= boundarys.y1 || ceil(oy) >= boundarys.y2) + c3 = 0; + else + c3 = src[(guint)ceil(oy) * rowstride + (guint)ceil(ox) * 4 + ch]; + + if (floor(ox) <= boundarys.x1 || floor(ox) >= boundarys.x2 || + ceil(oy) <= boundarys.y1 || ceil(oy) >= boundarys.y2) + c4 = 0; + else + c4 = src[(guint)ceil(oy) * rowstride + (guint)floor(ox) * 4 + ch]; + + c = (c1 * dist1 + c2 * dist2 + c3 * dist3 + c4 * dist4) / (dist1 + dist2 + dist3 + dist4); + + return (guchar)c; +} + +static void +alpha_blt (GdkPixbuf * src, gint srcx, gint srcy, gint srcwidth, + gint srcheight, GdkPixbuf * dst, gint dstx, gint dsty) +{ + gint rightx; + gint bottomy; + gint dstwidth; + gint dstheight; + + gint srcoffsetx; + gint srcoffsety; + gint dstoffsetx; + gint dstoffsety; + + gint x, y, srcrowstride, dstrowstride, sx, sy, dx, dy; + guchar *src_pixels, *dst_pixels; + + dstheight = srcheight; + dstwidth = srcwidth; + + rightx = srcx + srcwidth; + bottomy = srcy + srcheight; + + if (rightx > gdk_pixbuf_get_width (src)) + rightx = gdk_pixbuf_get_width (src); + if (bottomy > gdk_pixbuf_get_height (src)) + bottomy = gdk_pixbuf_get_height (src); + srcwidth = rightx - srcx; + srcheight = bottomy - srcy; + + rightx = dstx + dstwidth; + bottomy = dsty + dstheight; + if (rightx > gdk_pixbuf_get_width (dst)) + rightx = gdk_pixbuf_get_width (dst); + if (bottomy > gdk_pixbuf_get_height (dst)) + bottomy = gdk_pixbuf_get_height (dst); + dstwidth = rightx - dstx; + dstheight = bottomy - dsty; + + if (dstwidth < srcwidth) + srcwidth = dstwidth; + if (dstheight < srcheight) + srcheight = dstheight; + + if (srcx < 0) + srcoffsetx = 0 - srcx; + else + srcoffsetx = 0; + + if (srcy < 0) + srcoffsety = 0 - srcy; + else + srcoffsety = 0; + + if (dstx < 0) + dstoffsetx = 0 - dstx; + else + dstoffsetx = 0; + + if (dsty < 0) + dstoffsety = 0 - dsty; + else + dstoffsety = 0; + + if (dstoffsetx > srcoffsetx) + srcoffsetx = dstoffsetx; + if (dstoffsety > srcoffsety) + srcoffsety = dstoffsety; + + srcrowstride = gdk_pixbuf_get_rowstride (src); + dstrowstride = gdk_pixbuf_get_rowstride (dst); + + src_pixels = gdk_pixbuf_get_pixels (src); + dst_pixels = gdk_pixbuf_get_pixels (dst); + + for (y = srcoffsety; y < srcheight; y++) + for (x = srcoffsetx; x < srcwidth; x++) + { + guchar r, g, b, a; + + sx = x + srcx; + sy = y + srcy; + dx = x + dstx; + dy = y + dsty; + a = src_pixels[4 * sx + sy * srcrowstride + 3]; + if (a) + { + r = src_pixels[4 * sx + sy * srcrowstride]; + g = src_pixels[4 * sx + 1 + sy * srcrowstride]; + b = src_pixels[4 * sx + 2 + sy * srcrowstride]; + art_rgba_run_alpha (dst_pixels + 4 * dx + + dy * dstrowstride, r, g, b, a, 1); + } + } +} + +static void +rsvg_filter_fix_coordinate_system (RsvgFilterContext * ctx, RsvgState * state) +{ + int i, j; + int x, y, height, width; + guchar *pixels; + int stride; + int currentindex; + + i = j = 0; + + x = y = width = height = 0; + + /* First for object bounding box coordinates we need to know how much of the + source has been drawn on */ + pixels = gdk_pixbuf_get_pixels (ctx->source); + stride = gdk_pixbuf_get_rowstride (ctx->source); + x = y = height = width = -1; + + + if (ctx->filter->filterunits == objectBoundingBox || + ctx->filter->primitiveunits != objectBoundingBox) + { + /* move in from the top to find the y value */ + for (i = 0; i < gdk_pixbuf_get_height (ctx->source); i++) + { + for (j = 0; j < gdk_pixbuf_get_width (ctx->source); j++) + { + currentindex = i * stride + j * 4; + if (pixels[currentindex + 0] != 0 || pixels[currentindex + 1] != 0 + || pixels[currentindex + 2] != 0 + || pixels[currentindex + 3] != 0) + { + y = i; + break; + } + } + if (y != -1) + break; + } + + /* move in from the bottom to find the height */ + for (i = gdk_pixbuf_get_height (ctx->source) - 1; i >= 0; i--) + { + for (j = 0; j < gdk_pixbuf_get_width (ctx->source); j++) + { + currentindex = i * stride + j * 4; + if (pixels[currentindex + 0] != 0 || pixels[currentindex + 1] != 0 + || pixels[currentindex + 2] != 0 + || pixels[currentindex + 3] != 0) + { + height = i - y; + break; + } + + } + if (height != -1) + break; + } + + /* move in from the left to find the x value */ + for (j = 0; j < gdk_pixbuf_get_width (ctx->source); j++) + { + for (i = y; i < (height + y); i++) + { + currentindex = i * stride + j * 4; + if (pixels[currentindex + 0] != 0 || pixels[currentindex + 1] != 0 + || pixels[currentindex + 2] != 0 + || pixels[currentindex + 3] != 0) + { + x = j; + break; + } + } + if (x != -1) + break; + } + + /* move in from the right side to find the width */ + for (j = gdk_pixbuf_get_width (ctx->source) - 1; j >= 0; j--) + { + for (i = y; i < (height + y); i++) + { + currentindex = i * stride + j * 4; + if (pixels[currentindex + 0] != 0 || pixels[currentindex + 1] != 0 + || pixels[currentindex + 2] != 0 + || pixels[currentindex + 3] != 0) + { + width = j - x; + break; + } + } + if (width != -1) + break; + } + } + + ctx->width = gdk_pixbuf_get_width (ctx->source); + ctx->height = gdk_pixbuf_get_height (ctx->source); + + if (ctx->filter->filterunits == userSpaceOnUse) + { + for (i = 0; i < 6; i++) + ctx->affine[i] = state->affine[i]; + } + else + { + ctx->affine[0] = width; + ctx->affine[1] = 0.; + ctx->affine[2] = 0.; + ctx->affine[3] = height; + ctx->affine[4] = x; + ctx->affine[5] = y; + } + + if (ctx->filter->primitiveunits == userSpaceOnUse) + { + for (i = 0; i < 6; i++) + ctx->paffine[i] = state->affine[i]; + } + else + { + ctx->paffine[0] = width; + ctx->paffine[1] = 0.; + ctx->paffine[2] = 0.; + ctx->paffine[3] = height; + ctx->paffine[4] = x; + ctx->paffine[5] = y; + } +} + +static void +rsvg_filter_free_pair (gpointer key, gpointer value, gpointer user_data) +{ + g_object_unref (G_OBJECT (value)); + g_free ((gchar *) key); +} + +/** + * rsvg_filter_render: Copy the source to the bg using a filter. + * @self: a pointer to the filter to use + * @source: a pointer to the source pixbuf + * @bg: the background pixbuf + * @context: the context + * + * This function will create a context for itself, set up the coordinate systems + * execute all its little primatives and then clean up its own mess + **/ +void +rsvg_filter_render (RsvgFilter * self, GdkPixbuf * source, GdkPixbuf * output, + GdkPixbuf * bg, RsvgHandle * context) +{ + RsvgFilterContext *ctx; + RsvgFilterPrimitive *current; + guint i; + FPBox bounds; + + ctx = g_new (RsvgFilterContext, 1); + ctx->filter = self; + ctx->source = source; + ctx->bg = bg; + ctx->results = g_hash_table_new (g_str_hash, g_str_equal); + ctx->ctx = context; + + g_object_ref (G_OBJECT (source)); + ctx->lastresult = source; + + rsvg_filter_fix_coordinate_system (ctx, rsvg_state_current (context)); + + for (i = 0; i < self->primitives->len; i++) + { + current = g_ptr_array_index (self->primitives, i); + rsvg_filter_primitive_render (current, ctx); + } + + g_hash_table_foreach (ctx->results, rsvg_filter_free_pair, NULL); + g_hash_table_destroy (ctx->results); + + bounds = rsvg_filter_primitive_get_bounds (NULL, ctx); + + alpha_blt (ctx->lastresult, bounds.x1, bounds.y1, bounds.x2 - bounds.x1, + bounds.y2 - bounds.y1, output, bounds.x1, bounds.y1); + g_object_unref (G_OBJECT (ctx->lastresult)); + g_free(ctx); +} + +/** + * rsvg_filter_store_result: Files a result into a context. + * @name: The name of the result + * @result: The pointer to the result + * @ctx: the context that this was called in + * + * Puts the new result into the hash for easy finding later, also + * Stores it as the last result + **/ +static void +rsvg_filter_store_result (GString * name, GdkPixbuf * result, + RsvgFilterContext * ctx) +{ + g_object_unref (G_OBJECT (ctx->lastresult)); + + if (strcmp (name->str, "")) + { + g_object_ref (G_OBJECT (result)); /* increments the references for the table */ + g_hash_table_insert (ctx->results, g_strdup (name->str), result); + } + + g_object_ref (G_OBJECT (result)); /* increments the references for the last result */ + ctx->lastresult = result; +} + +static GdkPixbuf * +pixbuf_get_alpha (GdkPixbuf * pb) +{ + guchar *data; + guchar *pbdata; + GdkPixbuf *output; + + gsize i, pbsize; + + pbsize = gdk_pixbuf_get_width (pb) * gdk_pixbuf_get_height (pb); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, + gdk_pixbuf_get_width (pb), + gdk_pixbuf_get_height (pb)); + + data = gdk_pixbuf_get_pixels (output); + pbdata = gdk_pixbuf_get_pixels (pb); + + for (i = 0; i < pbsize; i++) + data[i * 4 + 3] = pbdata[i * 4 + 3]; + + return output; +} + +/** + * rsvg_filter_get_in: Gets a pixbuf for a primative. + * @name: The name of the pixbuf + * @ctx: the context that this was called in + * + * Returns a pointer to the result that the name refers to, a special + * Pixbuf if the name is a special keyword or NULL if nothing was found + **/ +static GdkPixbuf * +rsvg_filter_get_in (GString * name, RsvgFilterContext * ctx) +{ + GdkPixbuf *output; + + if (!strcmp (name->str, "SourceGraphic")) + { + g_object_ref (G_OBJECT (ctx->source)); + return ctx->source; + } + else if (!strcmp (name->str, "BackgroundImage")) + { + g_object_ref (G_OBJECT (ctx->bg)); + return ctx->bg; + } + else if (!strcmp (name->str, "") || !strcmp (name->str, "none")) + { + g_object_ref (G_OBJECT (ctx->lastresult)); + return ctx->lastresult; + } + else if (!strcmp (name->str, "SourceAlpha")) + return pixbuf_get_alpha (ctx->source); + else if (!strcmp (name->str, "BackgroundAlpha")) + return pixbuf_get_alpha (ctx->bg); + + output = g_hash_table_lookup (ctx->results, name->str); + g_object_ref (G_OBJECT (output)); + + if (output != NULL) + return output; + + g_object_ref (G_OBJECT (ctx->lastresult)); + return ctx->lastresult; +} + +/** + * rsvg_filter_parse: Looks up an allready created filter. + * @defs: a pointer to the hash of definitions + * @str: a string with the name of the filter to be looked up + * + * Returns a pointer to the filter that the name refers to, or NULL + * if none was found + **/ +RsvgFilter * +rsvg_filter_parse (const RsvgDefs * defs, const char *str) +{ + if (!strncmp (str, "url(", 4)) + { + const char *p = str + 4; + int ix; + char *name; + RsvgDefVal *val; + + while (g_ascii_isspace (*p)) + p++; + + if (*p == '#') + { + p++; + for (ix = 0; p[ix]; ix++) + if (p[ix] == ')') + break; + + if (p[ix] == ')') + { + name = g_strndup (p, ix); + val = rsvg_defs_lookup (defs, name); + g_free (name); + + if (val && val->type == RSVG_DEF_FILTER) + return (RsvgFilter *) val; + } + } + } + + return NULL; +} + +/** + * rsvg_new_filter: Creates a black filter + * + * Creates a blank filter and assigns default values to everything + **/ +static RsvgFilter * +rsvg_new_filter (void) +{ + RsvgFilter *filter; + + filter = g_new (RsvgFilter, 1); + filter->filterunits = objectBoundingBox; + filter->primitiveunits = userSpaceOnUse; + filter->x = -0.1; + filter->y = -0.1; + filter->width = 1.2; + filter->height = 1.2; + filter->primitives = g_ptr_array_new (); + + return filter; +} + +/** + * rsvg_filter_free: Free a filter. + * @dself: The defval to be freed + * + * Frees a filter and all primatives associated with this filter, this is + * to be set as its free function to be used with rsvg defs + **/ +static void +rsvg_filter_free (RsvgDefVal * dself) +{ + RsvgFilterPrimitive *current; + RsvgFilter *self; + guint i; + + self = (RsvgFilter *) dself; + + for (i = 0; i < self->primitives->len; i++) + { + current = g_ptr_array_index (self->primitives, i); + rsvg_filter_primitive_free (current); + } +} + +/** + * rsvg_start_filter: Create a filter from xml arguments. + * @ctx: the current rsvg handle + * @atts: the xml attributes that set the filter's properties + * + * Creates a new filter and sets it as a def + * Also sets the context's current filter pointer to point to the + * newly created filter so that all subsiquent primatives are + * added to this filter until the filter is ended + **/ +void +rsvg_start_filter (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *id = NULL, *value; + RsvgFilter *filter; + double font_size; + + font_size = rsvg_state_current_font_size (ctx); + filter = rsvg_new_filter (); + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "filterUnits"))) + { + if (!strcmp (value, "userSpaceOnUse")) + filter->filterunits = userSpaceOnUse; + else + filter->filterunits = objectBoundingBox; + } + if ((value = rsvg_property_bag_lookup (atts, "primitiveUnits"))) + { + if (!strcmp (value, "objectBoundingBox")) + filter->primitiveunits = objectBoundingBox; + else + filter->primitiveunits = userSpaceOnUse; + } + if ((value = rsvg_property_bag_lookup (atts, "x"))) + filter->x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + if ((value = rsvg_property_bag_lookup (atts, "y"))) + filter->y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + if ((value = rsvg_property_bag_lookup (atts, "width"))) + filter->width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + if ((value = rsvg_property_bag_lookup (atts, "height"))) + filter->height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + } + + ctx->currentfilter = filter; + + /* set up the defval stuff */ + filter->super.type = RSVG_DEF_FILTER; + filter->super.free = &rsvg_filter_free; + rsvg_defs_set (ctx->defs, id, &filter->super); +} + +/** + * rsvg_end_filter: Create a filter from xml arguments. + * @ctx: the current rsvg handle + * + * Ends the current filter block by setting the currentfilter ot null + **/ +void +rsvg_end_filter (RsvgHandle * ctx) +{ + ctx->currentfilter = NULL; +} + +/*************************************************************/ +/*************************************************************/ + +typedef enum +{ + normal, multiply, screen, darken, lighten +} +RsvgFilterPrimitiveBlendMode; + +typedef struct _RsvgFilterPrimitiveBlend RsvgFilterPrimitiveBlend; +struct _RsvgFilterPrimitiveBlend +{ + RsvgFilterPrimitive super; + RsvgFilterPrimitiveBlendMode mode; + GString *in2; +}; + +static void +rsvg_filter_primitive_blend_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar i; + gint x, y; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *in2_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveBlend *bself; + + GdkPixbuf *output; + GdkPixbuf *in; + GdkPixbuf *in2; + + bself = (RsvgFilterPrimitiveBlend *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + in2 = rsvg_filter_get_in (bself->in2, ctx); + in2_pixels = gdk_pixbuf_get_pixels (in2); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + double qr, cr, qa, qb, ca, cb; + + qa = (double) in_pixels[4 * x + y * rowstride + 3] / 255.0; + qb = (double) in2_pixels[4 * x + y * rowstride + 3] / 255.0; + qr = 1 - (1 - qa) * (1 - qb); + cr = 0; + for (i = 0; i < 3; i++) + { + ca = (double) in_pixels[4 * x + y * rowstride + i] * qa / 255.0; + cb = (double) in2_pixels[4 * x + y * rowstride + i] * qb / 255.0; + switch (bself->mode) + { + case normal: + cr = (1 - qa) * cb + ca; + break; + case multiply: + cr = (1 - qa) * cb + (1 - qb) * ca + ca * cb; + break; + case screen: + cr = cb + ca - ca * cb; + break; + case darken: + cr = MIN ((1 - qa) * cb + ca, (1 - qb) * ca + cb); + break; + case lighten: + cr = MAX ((1 - qa) * cb + ca, (1 - qb) * ca + cb); + break; + } + cr *= 255.0 / qr; + if (cr > 255) + cr = 255; + if (cr < 0) + cr = 0; + output_pixels[4 * x + y * rowstride + i] = (guchar) cr; + + } + output_pixels[4 * x + y * rowstride + 3] = qr * 255.0; + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (in2)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_blend_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveBlend *bself; + + bself = (RsvgFilterPrimitiveBlend *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_string_free (bself->in2, TRUE); + g_free (bself); +} + +void +rsvg_start_filter_primitive_blend (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveBlend *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveBlend, 1); + filter->mode = normal; + filter->super.in = g_string_new ("none"); + filter->in2 = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "mode"))) + { + if (!strcmp (value, "multiply")) + filter->mode = multiply; + else if (!strcmp (value, "screen")) + filter->mode = screen; + else if (!strcmp (value, "darken")) + filter->mode = darken; + else if (!strcmp (value, "lighten")) + filter->mode = lighten; + else + filter->mode = normal; + } + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "in2"))) + g_string_assign (filter->in2, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + } + + filter->super.render = &rsvg_filter_primitive_blend_render; + filter->super.free = &rsvg_filter_primitive_blend_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveConvolveMatrix RsvgFilterPrimitiveConvolveMatrix; + +struct _RsvgFilterPrimitiveConvolveMatrix +{ + RsvgFilterPrimitive super; + double *KernelMatrix; + double divisor; + gint orderx, ordery; + double dx, dy; + double bias; + gint targetx, targety; + gboolean preservealpha; + gint edgemode; +}; + +static void +rsvg_filter_primitive_convolve_matrix_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar ch; + gint x, y; + gint i, j; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveConvolveMatrix *cself; + + GdkPixbuf *output; + GdkPixbuf *in; + + gint sx, sy, kx, ky; + guchar sval; + double kval, sum, dx, dy, targetx, targety; + + gint tempresult; + + cself = (RsvgFilterPrimitiveConvolveMatrix *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + targetx = cself->targetx * ctx->paffine[0]; + targety = cself->targety * ctx->paffine[3]; + + if (cself->dx != 0 || cself->dy != 0) + { + dx = cself->dx * ctx->paffine[0]; + dy = cself->dy * ctx->paffine[3]; + } + else + dx = dy = 1; + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + for (ch = 0; ch < 3 + !cself->preservealpha; ch++) + { + sum = 0; + for (i = 0; i < cself->ordery; i++) + for (j = 0; j < cself->orderx; j++) + { + sx = x - targetx + j * dx; + sy = y - targety + i * dy; + if (cself->edgemode == 0) + { + if (sx < boundarys.x1) + sx = boundarys.x1; + if (sx >= boundarys.x2) + sx = boundarys.x2 - 1; + if (sy < boundarys.y1) + sy = boundarys.y1; + if (sy >= boundarys.y2) + sy = boundarys.y2 - 1; + } + else if (cself->edgemode == 1) + { + if (sx < boundarys.x1 || (sx >= boundarys.x2)) + sx = boundarys.x1 + (sx - boundarys.x1) % + (boundarys.x2 - boundarys.x1); + if (sy < boundarys.y1 || (sy >= boundarys.y2)) + sy = boundarys.y1 + (sy - boundarys.y1) % + (boundarys.y2 - boundarys.y1); + } + else if (cself->edgemode == 2) + if (sx < boundarys.x1 || (sx >= boundarys.x2) || + sy < boundarys.y1 || (sy >= boundarys.y2)) + continue; + + kx = cself->orderx - j - 1; + ky = cself->ordery - i - 1; + sval = in_pixels[4 * sx + sy * rowstride + ch]; + kval = cself->KernelMatrix[kx + ky * cself->orderx]; + sum += (double) sval *kval; + } + tempresult = sum / cself->divisor + cself->bias; + + if (tempresult > 255) + tempresult = 255; + if (tempresult < 0) + tempresult = 0; + + output_pixels[4 * x + y * rowstride + ch] = tempresult; + } + if (cself->preservealpha) + output_pixels[4 * x + y * rowstride + 3] = + in_pixels[4 * x + y * rowstride + 3]; + } + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_convolve_matrix_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveConvolveMatrix *cself; + + cself = (RsvgFilterPrimitiveConvolveMatrix *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (cself->KernelMatrix); + g_free (cself); +} + +void +rsvg_start_filter_primitive_convolve_matrix (RsvgHandle * ctx, + RsvgPropertyBag * atts) +{ + int i, j, listlen; + double font_size; + const char *value; + RsvgFilterPrimitiveConvolveMatrix *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveConvolveMatrix, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + + filter->divisor = 0; + filter->bias = 0; + filter->targetx = 0; + filter->targety = 0; + filter->dx = 0; + filter->dy = 0; + filter->preservealpha = FALSE; + filter->edgemode = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "targetX"))) + filter->targetx = atoi (value); + if ((value = rsvg_property_bag_lookup (atts, "targetY"))) + filter->targety = atoi (value); + if ((value = rsvg_property_bag_lookup (atts, "bias"))) + filter->bias = atof (value); + if ((value = rsvg_property_bag_lookup (atts, "preserveAlpha"))) + { + if (!strcmp (value, "true")) + filter->preservealpha = TRUE; + else + filter->preservealpha = FALSE; + } + if ((value = rsvg_property_bag_lookup (atts, "divisor"))) + filter->divisor = atof (value); + if ((value = rsvg_property_bag_lookup (atts, "order"))) + { + double tempx, tempy; + rsvg_css_parse_number_optional_number (value, + &tempx, &tempy); + filter->orderx = tempx; + filter->ordery = tempy; + + } + if ((value = rsvg_property_bag_lookup (atts, "kernelUnitLength"))) + rsvg_css_parse_number_optional_number (value, + &filter->dx, &filter->dy); + + if ((value = rsvg_property_bag_lookup (atts, "kernelMatrix"))) + filter->KernelMatrix = + rsvg_css_parse_number_list (value, &listlen); + + if ((value = rsvg_property_bag_lookup (atts, "edgeMode"))) + { + if (!strcmp (value, "wrap")) + filter->edgemode = 1; + else if (!strcmp (value, "none")) + filter->edgemode = 2; + else + filter->edgemode = 0; + } + } + + if (filter->divisor == 0) + { + for (j = 0; j < filter->orderx; j++) + for (i = 0; i < filter->ordery; i++) + filter->divisor += filter->KernelMatrix[j + i * filter->orderx]; + } + + if (filter->divisor == 0) + filter->divisor = 1; + + if (listlen < filter->orderx * filter->ordery) + filter->orderx = filter->ordery = 0; + + filter->super.render = &rsvg_filter_primitive_convolve_matrix_render; + filter->super.free = &rsvg_filter_primitive_convolve_matrix_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveGaussianBlur +RsvgFilterPrimitiveGaussianBlur; + +struct _RsvgFilterPrimitiveGaussianBlur +{ + RsvgFilterPrimitive super; + double sdx, sdy; +}; + + +#if PERFECTBLUR != 0 +static void +true_blur (GdkPixbuf *in, GdkPixbuf *output, gfloat sdx, + gfloat sdy, FPBox boundarys) +{ + guchar ch; + gint x, y; + gint i, j; + gint rowstride, height, width; + + guchar *in_pixels; + guchar *output_pixels; + + gint sx, sy, kx, ky, kw, kh; + guchar sval; + double kval, sum; + + double *KernelMatrix; + double divisor; + + gint tempresult; + + kw = kh = 0; + + in_pixels = gdk_pixbuf_get_pixels (in); + output_pixels = gdk_pixbuf_get_pixels (output); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + /* find out the required x size for the kernel matrix */ + + for (i = 1; i < 20; i++) + { + if (exp (-(i * i) / (2 * sdx * sdx)) / sqrt (2 * M_PI * sdx * sdx) < + 0.0001) + { + break; + } + } + if (i < 1) + i = 1; + + kw = 2 * (i - 1); + + /* find out the required y size for the kernel matrix */ + for (i = 1; i < 20; i++) + { + if (exp (-(i * i) / (2 * sdy * sdy)) / sqrt (2 * M_PI * sdy * sdy) < + 0.0001) + { + break; + } + } + + if (i < 1) + i = 1; + + kh = 2 * (i - 1); + + KernelMatrix = g_new (double, kw * kh); + + /* create the kernel matrix */ + for (i = 0; i < kh; i++) + { + for (j = 0; j < kw; j++) + { + KernelMatrix[j + i * kw] = + (exp (-((j - kw / 2) * (j - kw / 2)) / (2 * sdx * sdx)) / + sqrt (2 * M_PI * sdx * sdx)) * + (exp (-((i - kh / 2) * (i - kh / 2)) / (2 * sdy * sdy)) / + sqrt (2 * M_PI * sdy * sdy)); + } + } + + /* find out the total of the values of the matrix */ + divisor = 0; + for (j = 0; j < kw; j++) + for (i = 0; i < kh; i++) + divisor += KernelMatrix[j + i * kw]; + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + for (ch = 0; ch < 4; ch++) + { + sum = 0; + for (i = 0; i < kh; i++) + for (j = 0; j < kw; j++) + { + sx = x + j - kw / 2; + sy = y + i - kh / 2; + + if (sx < boundarys.x1) + sx = boundarys.x1; + if (sx >= boundarys.x2) + sx = boundarys.x2 - 1; + if (sy < boundarys.y1) + sy = boundarys.y1; + if (sy >= boundarys.y2) + sy = boundarys.y2 - 1; + + kx = kw - j - 1; + ky = kh - i - 1; + sval = in_pixels[4 * sx + sy * rowstride + ch]; + kval = KernelMatrix[kx + ky * kw]; + sum += (double) sval * kval; + } + + tempresult = sum / divisor; + if (tempresult > 255) + tempresult = 255; + if (tempresult < 0) + tempresult = 0; + + output_pixels[4 * x + y * rowstride + ch] = tempresult; + } + g_free (KernelMatrix); +} + +#endif + +static void +box_blur (GdkPixbuf *in, GdkPixbuf *output, GdkPixbuf *intermediate, gint kw, + gint kh, FPBox boundarys) +{ + guchar ch; + gint x, y; + gint rowstride, height, width; + + guchar *in_pixels; + guchar *output_pixels; + + gint sum; + + gint divisor; + + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + in_pixels = gdk_pixbuf_get_pixels (in); + output_pixels = gdk_pixbuf_get_pixels (intermediate); + + rowstride = gdk_pixbuf_get_rowstride (in); + + + if (kw >= 1) + { + for (ch = 0; ch < 4; ch++) + { + for (y = boundarys.y1; y < boundarys.y2; y++) + { + sum = 0; + divisor = 0; + for (x = boundarys.x1; x < boundarys.x1 + kw; x++) + { + divisor++; + sum += in_pixels[4 * x + y * rowstride + ch]; + if (x - kw / 2 >= 0 && x - kw / 2 < boundarys.x2) + { + output_pixels[4 * (x - kw / 2) + y * rowstride + ch] = sum / divisor; + } + } + for (x = boundarys.x1 + kw; x < boundarys.x2; x++) + { + sum -= in_pixels[4 * (x - kw) + y * rowstride + ch]; + sum += in_pixels[4 * x + y * rowstride + ch]; + output_pixels[4 * (x - kw / 2) + y * rowstride + ch] = sum / divisor; + } + for (x = boundarys.x2; x < boundarys.x2 + kw; x++) + { + divisor--; + sum -= in_pixels[4 * (x - kw) + y * rowstride + ch]; + if (x - kw / 2 >= 0 && x - kw / 2 < boundarys.x2) + { + output_pixels[4 * (x - kw / 2) + y * rowstride + ch] = sum / divisor; + } + } + } + } + } + else + intermediate = in; + + in_pixels = gdk_pixbuf_get_pixels (intermediate); + output_pixels = gdk_pixbuf_get_pixels (output); + + if (kh >= 1) + { + for (ch = 0; ch < 4; ch++) + { + for (x = boundarys.x1; x < boundarys.x2; x++) + { + sum = 0; + divisor = 0; + + for (y = boundarys.y1; y < boundarys.y1 + kh; y++) + { + divisor++; + sum += in_pixels[4 * x + y * rowstride + ch]; + if (y - kh / 2 >= 0 && y - kh / 2 < boundarys.y2) + { + output_pixels[4 * x + (y - kh / 2) * rowstride + ch] = sum / divisor; + } + } + for (; y < boundarys.y2; y++) + { + sum -= in_pixels[4 * x + (y - kh) * rowstride + ch]; + sum += in_pixels[4 * x + y * rowstride + ch]; + output_pixels[4 * x + (y - kh / 2) * rowstride + ch] = sum / divisor; + } + for (; y < boundarys.y2 + kh; y++) + { + divisor--; + sum -= in_pixels[4 * x + (y - kh) * rowstride + ch]; + if (y - kh / 2 >= 0 && y - kh / 2 < boundarys.y2) + { + output_pixels[4 * x + (y - kh / 2) * rowstride + ch] = sum / divisor; + } + } + } + } + + } + else + { + gdk_pixbuf_copy_area(intermediate, 0,0, width, height, + output, 0, 0); + } + +} + +static void +fast_blur (GdkPixbuf *in, GdkPixbuf *output, gfloat sx, + gfloat sy, FPBox boundarys) +{ + GdkPixbuf *intermediate1; + GdkPixbuf *intermediate2; + gint kx, ky; + + kx = floor(sx * 3*sqrt(2*M_PI)/4 + 0.5); + ky = floor(sy * 3*sqrt(2*M_PI)/4 + 0.5); + + intermediate1 = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, + gdk_pixbuf_get_width (in), + gdk_pixbuf_get_height (in)); + intermediate2 = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, + gdk_pixbuf_get_width (in), + gdk_pixbuf_get_height (in)); + + box_blur (in, intermediate2, intermediate1, kx, + ky, boundarys); + box_blur (intermediate2, intermediate2, intermediate1, kx, + ky, boundarys); + box_blur (intermediate2, output, intermediate1, kx, + ky, boundarys); + + g_object_unref (G_OBJECT (intermediate1)); + g_object_unref (G_OBJECT (intermediate2)); +} + +static void +rsvg_filter_primitive_gaussian_blur_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + RsvgFilterPrimitiveGaussianBlur *cself; + + GdkPixbuf *output; + GdkPixbuf *in; + FPBox boundarys; + gfloat sdx, sdy; + + cself = (RsvgFilterPrimitiveGaussianBlur *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, + gdk_pixbuf_get_width (in), + gdk_pixbuf_get_height (in)); + + /* scale the SD values */ + sdx = cself->sdx * ctx->paffine[0]; + sdy = cself->sdy * ctx->paffine[3]; + +#if PERFECTBLUR != 0 + if (sdx * sdy <= PERFECTBLUR) + true_blur (in, output, sdx, + sdy, boundarys); + else + fast_blur (in, output, sdx, + sdy, boundarys); +#else + fast_blur (in, output, sdx, + sdy, boundarys); +#endif + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_gaussian_blur_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveGaussianBlur *cself; + + cself = (RsvgFilterPrimitiveGaussianBlur *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (cself); +} + +void +rsvg_start_filter_primitive_gaussian_blur (RsvgHandle * ctx, + RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveGaussianBlur *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveGaussianBlur, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->sdx = 0; + filter->sdy = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "stdDeviation"))) + rsvg_css_parse_number_optional_number (value, + &filter->sdx, + &filter->sdy); + } + + filter->super.render = &rsvg_filter_primitive_gaussian_blur_render; + filter->super.free = &rsvg_filter_primitive_gaussian_blur_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveOffset RsvgFilterPrimitiveOffset; + +struct _RsvgFilterPrimitiveOffset +{ + RsvgFilterPrimitive super; + gint dx, dy; +}; + +static void +rsvg_filter_primitive_offset_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar ch; + gint x, y; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveOffset *oself; + + GdkPixbuf *output; + GdkPixbuf *in; + + int ox, oy; + + oself = (RsvgFilterPrimitiveOffset *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + + output_pixels = gdk_pixbuf_get_pixels (output); + + ox = ctx->paffine[0] * oself->dx; + oy = ctx->paffine[3] * oself->dy; + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + if (x - ox < boundarys.x1 || x - ox >= boundarys.x2) + continue; + if (y - oy < boundarys.y1 || y - oy >= boundarys.y2) + continue; + + for (ch = 0; ch < 4; ch++) + { + output_pixels[y * rowstride + x * 4 + ch] = + in_pixels[(y - oy) * rowstride + (x - ox) * 4 + ch]; + } + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_offset_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveOffset *oself; + + oself = (RsvgFilterPrimitiveOffset *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (oself); +} + +void +rsvg_start_filter_primitive_offset (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveOffset *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveOffset, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->dy = 0; + filter->dx = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "dx"))) + filter->dx = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + if ((value = rsvg_property_bag_lookup (atts, "dy"))) + filter->dy = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + } + + filter->super.render = &rsvg_filter_primitive_offset_render; + filter->super.free = &rsvg_filter_primitive_offset_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveMerge RsvgFilterPrimitiveMerge; + +struct _RsvgFilterPrimitiveMerge +{ + RsvgFilterPrimitive super; + GPtrArray *nodes; +}; + +static void +rsvg_filter_primitive_merge_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guint i; + FPBox boundarys; + + RsvgFilterPrimitiveMerge *mself; + + GdkPixbuf *output; + GdkPixbuf *in; + + mself = (RsvgFilterPrimitiveMerge *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, ctx->width, ctx->height); + + for (i = 0; i < mself->nodes->len; i++) + { + in = rsvg_filter_get_in (g_ptr_array_index (mself->nodes, i), ctx); + alpha_blt (in, boundarys.x1, boundarys.y1, boundarys.x2 - boundarys.x1, + boundarys.y2 - boundarys.y1, output, boundarys.x1, + boundarys.y1); + g_object_unref (G_OBJECT (in)); + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_merge_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveMerge *mself; + guint i; + + mself = (RsvgFilterPrimitiveMerge *) self; + g_string_free (self->result, TRUE); + + for (i = 0; i < mself->nodes->len; i++) + g_string_free (g_ptr_array_index (mself->nodes, i), TRUE); + g_ptr_array_free (mself->nodes, FALSE); + g_free (mself); +} + +void +rsvg_start_filter_primitive_merge (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveMerge *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveMerge, 1); + + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->nodes = g_ptr_array_new (); + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + } + + filter->super.render = &rsvg_filter_primitive_merge_render; + filter->super.free = &rsvg_filter_primitive_merge_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); + ctx->currentsubfilter = filter; +} + +void +rsvg_start_filter_primitive_merge_node (RsvgHandle * ctx, + RsvgPropertyBag * atts) +{ + const char *value; + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_ptr_array_add (((RsvgFilterPrimitiveMerge *) (ctx-> + currentsubfilter))-> + nodes, g_string_new (value)); + } +} + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveColourMatrix +RsvgFilterPrimitiveColourMatrix; + +struct _RsvgFilterPrimitiveColourMatrix +{ + RsvgFilterPrimitive super; + double *KernelMatrix; + double divisor; + gint orderx, ordery; + double bias; + gint targetx, targety; + gboolean preservealpha; +}; + +static void +rsvg_filter_primitive_colour_matrix_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar ch; + gint x, y; + gint i; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveColourMatrix *cself; + + GdkPixbuf *output; + GdkPixbuf *in; + + double sum; + + gint tempresult; + + cself = (RsvgFilterPrimitiveColourMatrix *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + for (ch = 0; ch < 4; ch++) + { + sum = 0; + for (i = 0; i < 4; i++) + { + sum += cself->KernelMatrix[ch * 5 + i] * + in_pixels[4 * x + y * rowstride + i]; + } + sum += cself->KernelMatrix[ch * 5 + 4]; + + tempresult = sum; + if (tempresult > 255) + tempresult = 255; + if (tempresult < 0) + tempresult = 0; + output_pixels[4 * x + y * rowstride + ch] = tempresult; + } + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_colour_matrix_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveColourMatrix *cself; + + cself = (RsvgFilterPrimitiveColourMatrix *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + if (cself->KernelMatrix) + g_free (cself->KernelMatrix); + g_free (cself); +} + +void +rsvg_start_filter_primitive_colour_matrix (RsvgHandle * ctx, + RsvgPropertyBag * atts) +{ + gint type, listlen; + double font_size; + const char *value; + RsvgFilterPrimitiveColourMatrix *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveColourMatrix, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + + filter->KernelMatrix = NULL; + + type = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "values"))) + { + filter->KernelMatrix = + rsvg_css_parse_number_list (value, &listlen); + } + if ((value = rsvg_property_bag_lookup (atts, "type"))) + { + if (!strcmp (value, "matrix")) + type = 0; + else if (!strcmp (value, "saturate")) + type = 1; + else if (!strcmp (value, "hueRotate")) + type = 2; + else if (!strcmp (value, "luminanceToAlpha")) + type = 3; + else + type = 0; + } + } + + if (type == 0) + { + if (listlen != 20) + { + if (filter->KernelMatrix != NULL) + g_free (filter->KernelMatrix); + filter->KernelMatrix = g_new0 (double, 20); + } + } + else if (type == 1) + { + float s; + if (listlen != 0) + { + s = filter->KernelMatrix[0]; + g_free (filter->KernelMatrix); + } + else + s = 1; + filter->KernelMatrix = g_new0 (double, 20); + + filter->KernelMatrix[0] = 0.213 + 0.787 * s; + filter->KernelMatrix[1] = 0.715 - 0.715 * s; + filter->KernelMatrix[2] = 0.072 - 0.072 * s; + filter->KernelMatrix[5] = 0.213 - 0.213 * s; + filter->KernelMatrix[6] = 0.715 + 0.285 * s; + filter->KernelMatrix[7] = 0.072 - 0.072 * s; + filter->KernelMatrix[10] = 0.213 - 0.213 * s; + filter->KernelMatrix[11] = 0.715 - 0.715 * s; + filter->KernelMatrix[12] = 0.072 + 0.928 * s; + filter->KernelMatrix[18] = 1; + } + else if (type == 2) + { + double cosval, sinval, arg; + + if (listlen != 0) + { + arg = filter->KernelMatrix[0]; + g_free (filter->KernelMatrix); + } + else + arg = 0; + + cosval = cos (arg); + sinval = sin (arg); + + filter->KernelMatrix = g_new0 (double, 20); + + filter->KernelMatrix[0] = 0.213 + cosval * 0.787 + sinval * -0.213; + filter->KernelMatrix[1] = 0.715 + cosval * -0.715 + sinval * -0.715; + filter->KernelMatrix[2] = 0.072 + cosval * -0.072 + sinval * 0.928; + filter->KernelMatrix[5] = 0.213 + cosval * -0.213 + sinval * 0.143; + filter->KernelMatrix[6] = 0.715 + cosval * 0.285 + sinval * 0.140; + filter->KernelMatrix[7] = 0.072 + cosval * -0.072 + sinval * -0.283; + filter->KernelMatrix[10] = 0.213 + cosval * -0.213 + sinval * -0.787; + filter->KernelMatrix[11] = 0.715 + cosval * -0.715 + sinval * 0.715; + filter->KernelMatrix[12] = 0.072 + cosval * 0.928 + sinval * 0.072; + filter->KernelMatrix[18] = 1; + } + else if (type == 3) + { + if (filter->KernelMatrix != NULL) + g_free (filter->KernelMatrix); + + filter->KernelMatrix = g_new0 (double, 20); + + filter->KernelMatrix[15] = 0.2125; + filter->KernelMatrix[16] = 0.7154; + filter->KernelMatrix[17] = 0.0721; + } + else + { + g_assert_not_reached(); + } + + filter->super.render = &rsvg_filter_primitive_colour_matrix_render; + filter->super.free = &rsvg_filter_primitive_colour_matrix_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +struct ComponentTransferData +{ + gdouble *tableValues; + guint nbTableValues; + + gdouble slope; + gdouble intercept; + gdouble amplitude; + gdouble exponent; + gdouble offset; +}; + +typedef gdouble (*ComponentTransferFunc) (gdouble C, + struct ComponentTransferData * + user_data); + +typedef struct _RsvgFilterPrimitiveComponentTransfer +RsvgFilterPrimitiveComponentTransfer; + + +struct _RsvgFilterPrimitiveComponentTransfer +{ + RsvgFilterPrimitive super; + ComponentTransferFunc Rfunction; + struct ComponentTransferData Rdata; + ComponentTransferFunc Gfunction; + struct ComponentTransferData Gdata; + ComponentTransferFunc Bfunction; + struct ComponentTransferData Bdata; + ComponentTransferFunc Afunction; + struct ComponentTransferData Adata; +}; + +static gint +get_component_transfer_table_value (gdouble C, + struct ComponentTransferData *user_data) +{ + gdouble N; + gint k; + N = user_data->nbTableValues; + + k = floor(C * N); + k -= 1; + if (k < 0) + k = 0; + return k; +} + +static gdouble +identity_component_transfer_func (gdouble C, + struct ComponentTransferData *user_data) +{ + return C; +} + +static gdouble +table_component_transfer_func (gdouble C, + struct ComponentTransferData *user_data) +{ + guint k; + gdouble vk, vk1; + gfloat distancefromlast; + + if (!user_data->nbTableValues) + return C; + + k = get_component_transfer_table_value (C, user_data); + + if (k == user_data->nbTableValues - 1) + return user_data->tableValues[k - 1]; + + vk = user_data->tableValues[k]; + vk1 = user_data->tableValues[k + 1]; + + distancefromlast = (C - ((double)k + 1) / (double)user_data->nbTableValues) * (double)user_data->nbTableValues; + + return (vk + distancefromlast * (vk1 - vk)); +} + +static gdouble +discrete_component_transfer_func (gdouble C, + struct ComponentTransferData *user_data) +{ + gint k; + + if (!user_data->nbTableValues) + return C; + + k = get_component_transfer_table_value (C, user_data); + + return user_data->tableValues[k]; +} + +static gdouble +linear_component_transfer_func (gdouble C, + struct ComponentTransferData *user_data) +{ + return (user_data->slope * C) + user_data->intercept; +} + +static gdouble +gamma_component_transfer_func (gdouble C, + struct ComponentTransferData *user_data) +{ + return user_data->amplitude * pow (C, + user_data->exponent) + user_data->offset; +} + +static void +rsvg_filter_primitive_component_transfer_render (RsvgFilterPrimitive * + self, + RsvgFilterContext * ctx) +{ + gint x, y; + gint temp; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveComponentTransfer *cself; + + GdkPixbuf *output; + GdkPixbuf *in; + cself = (RsvgFilterPrimitiveComponentTransfer *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + temp = cself->Rfunction((double)in_pixels[y * rowstride + x * 4] / 255.0, &cself->Rdata) * 255.0; + if (temp > 255) + temp = 255; + else if (temp < 0) + temp = 0; + output_pixels[y * rowstride + x * 4] = temp; + + temp = cself->Gfunction((double)in_pixels[y * rowstride + x * 4 + 1] / 255.0, &cself->Gdata) * 255.0; + if (temp > 255) + temp = 255; + else if (temp < 0) + temp = 0; + output_pixels[y * rowstride + x * 4 + 1] = temp; + + temp = cself->Bfunction((double)in_pixels[y * rowstride + x * 4 + 2] / 255.0, &cself->Bdata) * 255.0; + if (temp > 255) + temp = 255; + else if (temp < 0) + temp = 0; + output_pixels[y * rowstride + x * 4 + 2] = temp; + + temp = cself->Afunction((double)in_pixels[y * rowstride + x * 4 + 3] / 255.0, &cself->Adata) * 255.0; + if (temp > 255) + temp = 255; + else if (temp < 0) + temp = 0; + output_pixels[y * rowstride + x * 4 + 3] = temp; + } + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_component_transfer_free (RsvgFilterPrimitive * + self) +{ + RsvgFilterPrimitiveComponentTransfer *cself; + + cself = (RsvgFilterPrimitiveComponentTransfer *) self; + g_string_free (self->result, TRUE); + if (cself->Rdata.nbTableValues) + g_free (cself->Rdata.tableValues); + if (cself->Gdata.nbTableValues) + g_free (cself->Gdata.tableValues); + if (cself->Bdata.nbTableValues) + g_free (cself->Bdata.tableValues); + if (cself->Adata.nbTableValues) + g_free (cself->Adata.tableValues); + g_free (cself); +} + + +void +rsvg_start_filter_primitive_component_transfer (RsvgHandle * ctx, + RsvgPropertyBag * atts) +{ + double font_size; + const char *value; + RsvgFilterPrimitiveComponentTransfer *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveComponentTransfer, 1); + + filter->super.result = g_string_new ("none"); + filter->super.in = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->Rfunction = identity_component_transfer_func; + filter->Gfunction = identity_component_transfer_func; + filter->Bfunction = identity_component_transfer_func; + filter->Afunction = identity_component_transfer_func; + filter->Rdata.nbTableValues = 0; + filter->Gdata.nbTableValues = 0; + filter->Bdata.nbTableValues = 0; + filter->Adata.nbTableValues = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + } + + filter->super.render = &rsvg_filter_primitive_component_transfer_render; + filter->super.free = &rsvg_filter_primitive_component_transfer_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); + + ctx->currentsubfilter = filter; +} + +void +rsvg_start_filter_primitive_component_transfer_function (RsvgHandle * ctx, + RsvgPropertyBag * atts, char channel) +{ + const char *value; + + ComponentTransferFunc * function; + struct ComponentTransferData * data; + + function = NULL; + data = NULL; + + if (channel == 'r') + { + function = &((RsvgFilterPrimitiveComponentTransfer *)(ctx->currentsubfilter))->Rfunction; + data = &((RsvgFilterPrimitiveComponentTransfer *)(ctx->currentsubfilter))->Rdata; + } + else if (channel == 'g') + { + function = &((RsvgFilterPrimitiveComponentTransfer *)(ctx->currentsubfilter))->Gfunction; + data = &((RsvgFilterPrimitiveComponentTransfer *)(ctx->currentsubfilter))->Gdata; + } + else if (channel == 'b') + { + function = &((RsvgFilterPrimitiveComponentTransfer *)(ctx->currentsubfilter))->Bfunction; + data = &((RsvgFilterPrimitiveComponentTransfer *)(ctx->currentsubfilter))->Bdata; + } + else if (channel == 'a') + { + function = &((RsvgFilterPrimitiveComponentTransfer *)(ctx->currentsubfilter))->Afunction; + data = &((RsvgFilterPrimitiveComponentTransfer *)(ctx->currentsubfilter))->Adata; + } + else + { + g_assert_not_reached(); + } + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "type"))) + { + if (!strcmp (value, "identity")) + *function = identity_component_transfer_func; + else if (!strcmp (value, "table")) + *function = table_component_transfer_func; + else if (!strcmp (value, "discrete")) + *function = discrete_component_transfer_func; + else if (!strcmp (value, "linear")) + *function = linear_component_transfer_func; + else if (!strcmp (value, "gamma")) + *function = gamma_component_transfer_func; + } + if ((value = rsvg_property_bag_lookup (atts, "tableValues"))) + { + data->tableValues = + rsvg_css_parse_number_list (value, + &data->nbTableValues); + } + if ((value = rsvg_property_bag_lookup (atts, "slope"))) + { + data->slope = g_ascii_strtod(value, NULL); + } + if ((value = rsvg_property_bag_lookup (atts, "intercept"))) + { + data->intercept = g_ascii_strtod(value, NULL); + } + if ((value = rsvg_property_bag_lookup (atts, "amplitude"))) + { + data->amplitude = g_ascii_strtod(value, NULL); + } + if ((value = rsvg_property_bag_lookup (atts, "exponent"))) + { + data->exponent = g_ascii_strtod(value, NULL); + } + if ((value = rsvg_property_bag_lookup (atts, "offset"))) + { + data->offset = g_ascii_strtod(value, NULL); + } + } +} + + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveErode +RsvgFilterPrimitiveErode; + +struct _RsvgFilterPrimitiveErode +{ + RsvgFilterPrimitive super; + double rx, ry; + int mode; +}; + +static void +rsvg_filter_primitive_erode_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar ch, extreme; + gint x, y; + gint i, j; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveErode *cself; + + GdkPixbuf *output; + GdkPixbuf *in; + + gint kx, ky; + guchar val; + + cself = (RsvgFilterPrimitiveErode *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + /* scale the radius values */ + kx = cself->rx * ctx->paffine[0]; + ky = cself->ry * ctx->paffine[3]; + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + for (ch = 0; ch < 4; ch++) + { + if (cself->mode == 0) + extreme = 255; + else + extreme = 0; + for (i = -ky; i < ky + 1; i++) + for (j = -kx; j < kx + 1; j++) + { + if (y + i >= height || y + i < 0 || + x + j >= width || x + j < 0) + continue; + + val = in_pixels[(y + i) * rowstride + + (x + j) * 4 + ch]; + + + if (cself->mode == 0) + { + if (extreme > val) + extreme = val; + } + else + { + if (extreme < val) + extreme = val; + } + + } + output_pixels[y * rowstride + x * 4 + ch] = extreme; + } + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_erode_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveErode *cself; + + cself = (RsvgFilterPrimitiveErode *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (cself); +} + +void +rsvg_start_filter_primitive_erode (RsvgHandle * ctx, + RsvgPropertyBag * atts) +{ + const char *value; + + double font_size; + RsvgFilterPrimitiveErode *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveErode, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->rx = 0; + filter->ry = 0; + filter->mode = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "radius"))) + { + rsvg_css_parse_number_optional_number (value, + &filter->rx, + &filter->ry); + } + if ((value = rsvg_property_bag_lookup (atts, "operator"))) + { + if (!strcmp (value, "erode")) + filter->mode = 0; + else if (!strcmp (value, "dilate")) + filter->mode = 1; + } + } + + filter->super.render = &rsvg_filter_primitive_erode_render; + filter->super.free = &rsvg_filter_primitive_erode_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +typedef enum +{ + COMPOSITE_MODE_OVER, COMPOSITE_MODE_IN, COMPOSITE_MODE_OUT, + COMPOSITE_MODE_ATOP, COMPOSITE_MODE_XOR, COMPOSITE_MODE_ARITHMETIC +} +RsvgFilterPrimitiveCompositeMode; + +typedef struct _RsvgFilterPrimitiveComposite RsvgFilterPrimitiveComposite; +struct _RsvgFilterPrimitiveComposite +{ + RsvgFilterPrimitive super; + RsvgFilterPrimitiveCompositeMode mode; + GString *in2; + + gdouble k1, k2, k3, k4; +}; + +static void +rsvg_filter_primitive_composite_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar i; + gint x, y; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *in2_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveComposite *bself; + + GdkPixbuf *output; + GdkPixbuf *in; + GdkPixbuf *in2; + + bself = (RsvgFilterPrimitiveComposite *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + in2 = rsvg_filter_get_in (bself->in2, ctx); + in2_pixels = gdk_pixbuf_get_pixels (in2); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + double qr, cr, qa, qb, ca, cb, Fa, Fb, Fab, Fo; + + qa = (double) in_pixels[4 * x + y * rowstride + 3] / 255.0; + qb = (double) in2_pixels[4 * x + y * rowstride + 3] / 255.0; + cr = 0; + Fa = Fb = Fab = Fo = 0; + switch (bself->mode) + { + case COMPOSITE_MODE_OVER: + Fa = 1; + Fb = 1 - qa; + break; + case COMPOSITE_MODE_IN: + Fa = qb; + Fb = 0; + break; + case COMPOSITE_MODE_OUT: + Fa = 1 - qb; + Fb = 0; + break; + case COMPOSITE_MODE_ATOP: + Fa = qb; + Fb = 1 - qa; + break; + case COMPOSITE_MODE_XOR: + Fa = 1 - qb; + Fb = 1 - qa; + break; + case COMPOSITE_MODE_ARITHMETIC: + Fab = bself->k1; + Fa = bself->k2; + Fb = bself->k3; + Fo = bself->k4; + } + + qr = Fa * qa + Fb * qb + Fab * qa * qb; + + for (i = 0; i < 3; i++) + { + ca = (double) in_pixels[4 * x + y * rowstride + i] / 255.0 * qa; + cb = (double) in2_pixels[4 * x + y * rowstride + i] / 255.0 * qb; + + cr = (ca * Fa + cb * Fb + ca * cb * Fab + Fo) / qr; + if (cr > 1) + cr = 1; + if (cr < 0) + cr = 0; + output_pixels[4 * x + y * rowstride + i] = (guchar)(cr * 255.0); + + } + if (qr > 1) + qr = 1; + if (qr < 0) + qr = 0; + output_pixels[4 * x + y * rowstride + 3] = (guchar)(qr * 255.0); + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (in2)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_composite_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveComposite *bself; + + bself = (RsvgFilterPrimitiveComposite *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_string_free (bself->in2, TRUE); + g_free (bself); +} + +void +rsvg_start_filter_primitive_composite (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + double font_size; + const char *value; + RsvgFilterPrimitiveComposite *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveComposite, 1); + filter->mode = COMPOSITE_MODE_OVER; + filter->super.in = g_string_new ("none"); + filter->in2 = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->k1 = 0; + filter->k2 = 0; + filter->k3 = 0; + filter->k4 = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "operator"))) + { + if (!strcmp (value, "in")) + filter->mode = COMPOSITE_MODE_IN; + else if (!strcmp (value, "out")) + filter->mode = COMPOSITE_MODE_OUT; + else if (!strcmp (value, "atop")) + filter->mode = COMPOSITE_MODE_ATOP; + else if (!strcmp (value, "xor")) + filter->mode = COMPOSITE_MODE_XOR; + else if (!strcmp (value, + "arithmetic")) + filter->mode = COMPOSITE_MODE_ARITHMETIC; + else + filter->mode = COMPOSITE_MODE_OVER; + } + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "in2"))) + g_string_assign (filter->in2, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "k1"))) + { + filter->k1 = g_ascii_strtod(value, NULL); + } + if ((value = rsvg_property_bag_lookup (atts, "k2"))) + { + filter->k2 = g_ascii_strtod(value, NULL); + } + if ((value = rsvg_property_bag_lookup (atts, "k3"))) + { + filter->k3 = g_ascii_strtod(value, NULL); + } + if ((value = rsvg_property_bag_lookup (atts, "k4"))) + { + filter->k4 = g_ascii_strtod(value, NULL); + } + } + + filter->super.render = &rsvg_filter_primitive_composite_render; + filter->super.free = &rsvg_filter_primitive_composite_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveFlood +RsvgFilterPrimitiveFlood; + +struct _RsvgFilterPrimitiveFlood +{ + RsvgFilterPrimitive super; + guint32 colour; + guint opacity; +}; + +static void +rsvg_filter_primitive_flood_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar i; + gint x, y; + gint rowstride, height, width; + FPBox boundarys; + + guchar *output_pixels; + + RsvgFilterPrimitiveFlood *bself; + + GdkPixbuf *output; + + bself = (RsvgFilterPrimitiveFlood *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + height = ctx->height; + width = ctx->width; + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + rowstride = gdk_pixbuf_get_rowstride (output); + + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + for (i = 0; i < 3; i++) + { + output_pixels[4 * x + y * rowstride + i] = ((char *) + (&bself->colour))[2 - i]; + } + output_pixels[4 * x + y * rowstride + 3] = bself->opacity; + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_flood_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveFlood *cself; + + cself = (RsvgFilterPrimitiveFlood *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (cself); +} + +void +rsvg_start_filter_primitive_flood (RsvgHandle * ctx, + RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveFlood *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveFlood, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + + filter->opacity = 255; + filter->colour = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "flood-color"))) + { + filter->colour = rsvg_css_parse_color (value); + } + if ((value = rsvg_property_bag_lookup (atts, "flood-opacity"))) + { + filter->opacity = rsvg_css_parse_opacity (value); + } + } + + filter->super.render = &rsvg_filter_primitive_flood_render; + filter->super.free = &rsvg_filter_primitive_flood_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveDisplacementMap RsvgFilterPrimitiveDisplacementMap; + +struct _RsvgFilterPrimitiveDisplacementMap +{ + RsvgFilterPrimitive super; + gint dx, dy; + char xChannelSelector, yChannelSelector; + GString *in2; + double scale; +}; + +static void +rsvg_filter_primitive_displacement_map_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar ch, xch, ych; + gint x, y; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *in2_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveDisplacementMap *oself; + + GdkPixbuf *output; + GdkPixbuf *in; + GdkPixbuf *in2; + + double ox, oy; + + oself = (RsvgFilterPrimitiveDisplacementMap *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + in2 = rsvg_filter_get_in (oself->in2, ctx); + in2_pixels = gdk_pixbuf_get_pixels (in2); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + + output_pixels = gdk_pixbuf_get_pixels (output); + + switch (oself->xChannelSelector) + { + case 'R': + xch = 0; + break; + case 'G': + xch = 1; + break; + case 'B': + xch = 2; + break; + case 'A': + xch = 3; + break; + default: + xch = 4; + }; + + switch (oself->yChannelSelector) + { + case 'R': + ych = 0; + break; + case 'G': + ych = 1; + break; + case 'B': + ych = 2; + break; + case 'A': + ych = 3; + break; + default: + ych = 4; + }; + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + if (xch != 4) + ox = x + oself->scale * ctx->paffine[0] * + ((double)in2_pixels[y * rowstride + x * 4 + xch] / 255.0 - 0.5); + else + ox = x; + + if (ych != 4) + oy = y + oself->scale * ctx->paffine[3] * + ((double)in2_pixels[y * rowstride + x * 4 + ych] / 255.0 - 0.5); + else + oy = y; + + for (ch = 0; ch < 4; ch++) + { + output_pixels[y * rowstride + x * 4 + ch] = + gdk_pixbuf_get_interp_pixel(in_pixels, ox, oy, ch, boundarys, + rowstride); + } + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (in2)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_displacement_map_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveDisplacementMap *oself; + + oself = (RsvgFilterPrimitiveDisplacementMap *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_string_free (oself->in2, TRUE); + g_free (oself); +} + +void +rsvg_start_filter_primitive_displacement_map (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveDisplacementMap *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveDisplacementMap, 1); + + filter->super.in = g_string_new ("none"); + filter->in2 = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->xChannelSelector = ' '; + filter->yChannelSelector = ' '; + filter->scale = 0; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "in2"))) + g_string_assign (filter->in2, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "xChannelSelector"))) + filter->xChannelSelector = (value)[0]; + if ((value = rsvg_property_bag_lookup (atts, "yChannelSelector"))) + filter->yChannelSelector = (value)[0]; + if ((value = rsvg_property_bag_lookup (atts, "scale"))) + filter->scale = g_ascii_strtod(value, NULL); + } + + filter->super.render = &rsvg_filter_primitive_displacement_map_render; + filter->super.free = &rsvg_filter_primitive_displacement_map_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +/* Produces results in the range [1, 2**31 - 2]. + Algorithm is: r = (a * r) mod m + where a = 16807 and m = 2**31 - 1 = 2147483647 + See [Park & Miller], CACM vol. 31 no. 10 p. 1195, Oct. 1988 + To test: the algorithm should produce the result 1043618065 + as the 10,000th generated number if the original seed is 1. +*/ +#define feTurbulence_RAND_m 2147483647 /* 2**31 - 1 */ +#define feTurbulence_RAND_a 16807 /* 7**5; primitive root of m */ +#define feTurbulence_RAND_q 127773 /* m / a */ +#define feTurbulence_RAND_r 2836 /* m % a */ +#define feTurbulence_BSize 0x100 +#define feTurbulence_BM 0xff +#define feTurbulence_PerlinN 0x1000 +#define feTurbulence_NP 12 /* 2^PerlinN */ +#define feTurbulence_NM 0xfff + +typedef struct _RsvgFilterPrimitiveTurbulence RsvgFilterPrimitiveTurbulence; +struct _RsvgFilterPrimitiveTurbulence +{ + RsvgFilterPrimitive super; + + int uLatticeSelector[feTurbulence_BSize + feTurbulence_BSize + 2]; + double fGradient[4][feTurbulence_BSize + feTurbulence_BSize + 2][2]; + + int seed; + + double fBaseFreqX; + double fBaseFreqY; + + int nNumOctaves; + gboolean bFractalSum; + gboolean bDoStitching; +}; + +struct feTurbulence_StitchInfo +{ + int nWidth; /* How much to subtract to wrap for stitching. */ + int nHeight; + int nWrapX; /* Minimum value to wrap. */ + int nWrapY; +}; + +static long feTurbulence_setup_seed(int lSeed) +{ + if (lSeed <= 0) + lSeed = -(lSeed % (feTurbulence_RAND_m - 1)) + 1; + if (lSeed > feTurbulence_RAND_m - 1) + lSeed = feTurbulence_RAND_m - 1; + return lSeed; +} + +static long feTurbulence_random(int lSeed) +{ + long result; + + result = feTurbulence_RAND_a * (lSeed % feTurbulence_RAND_q) - feTurbulence_RAND_r * (lSeed / feTurbulence_RAND_q); + if (result <= 0) + result += feTurbulence_RAND_m; + return result; +} + +static void feTurbulence_init(RsvgFilterPrimitiveTurbulence *filter) +{ + double s; + int i, j, k, lSeed; + + lSeed = feTurbulence_setup_seed(filter->seed); + for(k = 0; k < 4; k++) + { + for(i = 0; i < feTurbulence_BSize; i++) + { + filter->uLatticeSelector[i] = i; + for (j = 0; j < 2; j++) + filter->fGradient[k][i][j] = (double)(((lSeed = feTurbulence_random(lSeed)) % (feTurbulence_BSize + feTurbulence_BSize)) - feTurbulence_BSize) / feTurbulence_BSize; + s = (double)(sqrt(filter->fGradient[k][i][0] * filter->fGradient[k][i][0] + filter->fGradient[k][i][1] * filter->fGradient[k][i][1])); + filter->fGradient[k][i][0] /= s; + filter->fGradient[k][i][1] /= s; + } + } + + while(--i) + { + k = filter->uLatticeSelector[i]; + filter->uLatticeSelector[i] = filter->uLatticeSelector[j = (lSeed = feTurbulence_random(lSeed)) % feTurbulence_BSize]; + filter->uLatticeSelector[j] = k; + } + + for(i = 0; i < feTurbulence_BSize + 2; i++) + { + filter->uLatticeSelector[feTurbulence_BSize + i] = filter->uLatticeSelector[i]; + for(k = 0; k < 4; k++) + for(j = 0; j < 2; j++) + filter->fGradient[k][feTurbulence_BSize + i][j] = filter->fGradient[k][i][j]; + } +} + +#define feTurbulence_s_curve(t) ( t * t * (3. - 2. * t) ) +#define feTurbulence_lerp(t, a, b) ( a + t * (b - a) ) + +static double feTurbulence_noise2(RsvgFilterPrimitiveTurbulence *filter, + int nColorChannel, double vec[2], + struct feTurbulence_StitchInfo *pStitchInfo) +{ + int bx0, bx1, by0, by1, b00, b10, b01, b11; + double rx0, rx1, ry0, ry1, *q, sx, sy, a, b, t, u, v; + register int i, j; + + t = vec[0] + feTurbulence_PerlinN; + bx0 = (int)t; + bx1 = bx0+1; + rx0 = t - (int)t; + rx1 = rx0 - 1.0f; + t = vec[1] + feTurbulence_PerlinN; + by0 = (int)t; + by1 = by0+1; + ry0 = t - (int)t; + ry1 = ry0 - 1.0f; + + /* If stitching, adjust lattice points accordingly. */ + if(pStitchInfo != NULL) + { + if(bx0 >= pStitchInfo->nWrapX) + bx0 -= pStitchInfo->nWidth; + if(bx1 >= pStitchInfo->nWrapX) + bx1 -= pStitchInfo->nWidth; + if(by0 >= pStitchInfo->nWrapY) + by0 -= pStitchInfo->nHeight; + if(by1 >= pStitchInfo->nWrapY) + by1 -= pStitchInfo->nHeight; + } + + bx0 &= feTurbulence_BM; + bx1 &= feTurbulence_BM; + by0 &= feTurbulence_BM; + by1 &= feTurbulence_BM; + i = filter->uLatticeSelector[bx0]; + j = filter->uLatticeSelector[bx1]; + b00 = filter->uLatticeSelector[i + by0]; + b10 = filter->uLatticeSelector[j + by0]; + b01 = filter->uLatticeSelector[i + by1]; + b11 = filter->uLatticeSelector[j + by1]; + sx = (double)(feTurbulence_s_curve(rx0)); + sy = (double)(feTurbulence_s_curve(ry0)); + q = filter->fGradient[nColorChannel][b00]; u = rx0 * q[0] + ry0 * q[1]; + q = filter->fGradient[nColorChannel][b10]; v = rx1 * q[0] + ry0 * q[1]; + a = feTurbulence_lerp(sx, u, v); + q = filter->fGradient[nColorChannel][b01]; u = rx0 * q[0] + ry1 * q[1]; + q = filter->fGradient[nColorChannel][b11]; v = rx1 * q[0] + ry1 * q[1]; + b = feTurbulence_lerp(sx, u, v); + + return feTurbulence_lerp(sy, a, b); +} + +static double feTurbulence_turbulence(RsvgFilterPrimitiveTurbulence *filter, + int nColorChannel, double *point, + double fTileX, double fTileY, double fTileWidth, double fTileHeight) +{ + struct feTurbulence_StitchInfo stitch; + struct feTurbulence_StitchInfo *pStitchInfo = NULL; /* Not stitching when NULL. */ + + double fSum = 0.0f, vec[2], ratio = 1.; + int nOctave; + + /* Adjust the base frequencies if necessary for stitching. */ + if(filter->bDoStitching) + { + /* When stitching tiled turbulence, the frequencies must be adjusted + so that the tile borders will be continuous. */ + if(filter->fBaseFreqX != 0.0) + { + double fLoFreq = (double)(floor(fTileWidth * filter->fBaseFreqX)) / fTileWidth; + double fHiFreq = (double)(ceil(fTileWidth * filter->fBaseFreqX)) / fTileWidth; + if(filter->fBaseFreqX / fLoFreq < fHiFreq / filter->fBaseFreqX) + filter->fBaseFreqX = fLoFreq; + else + filter->fBaseFreqX = fHiFreq; + } + + if(filter->fBaseFreqY != 0.0) + { + double fLoFreq = (double)(floor(fTileHeight * filter->fBaseFreqY)) / fTileHeight; + double fHiFreq = (double)(ceil(fTileHeight * filter->fBaseFreqY)) / fTileHeight; + if(filter->fBaseFreqY / fLoFreq < fHiFreq / filter->fBaseFreqY) + filter->fBaseFreqY = fLoFreq; + else + filter->fBaseFreqY = fHiFreq; + } + + /* Set up initial stitch values. */ + pStitchInfo = &stitch; + stitch.nWidth = (int)(fTileWidth * filter->fBaseFreqX + 0.5f); + stitch.nWrapX = fTileX * filter->fBaseFreqX + feTurbulence_PerlinN + stitch.nWidth; + stitch.nHeight = (int)(fTileHeight * filter->fBaseFreqY + 0.5f); + stitch.nWrapY = fTileY * filter->fBaseFreqY + feTurbulence_PerlinN + stitch.nHeight; + } + + vec[0] = point[0] * filter->fBaseFreqX; + vec[1] = point[1] * filter->fBaseFreqY; + + for(nOctave = 0; nOctave < filter->nNumOctaves; nOctave++) + { + if(filter->bFractalSum) + fSum += (double)(feTurbulence_noise2(filter, nColorChannel, vec, pStitchInfo) / ratio); + else + fSum += (double)(fabs(feTurbulence_noise2(filter, nColorChannel, vec, pStitchInfo)) / ratio); + + vec[0] *= 2; + vec[1] *= 2; + ratio *= 2; + + if(pStitchInfo != NULL) + { + /* Update stitch values. Subtracting PerlinN before the multiplication and + adding it afterward simplifies to subtracting it once. */ + stitch.nWidth *= 2; + stitch.nWrapX = 2 * stitch.nWrapX - feTurbulence_PerlinN; + stitch.nHeight *= 2; + stitch.nWrapY = 2 * stitch.nWrapY - feTurbulence_PerlinN; + } + } + + return fSum; +} + +static void +rsvg_filter_primitive_turbulence_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + RsvgFilterPrimitiveTurbulence *oself; + gint x, y, tileWidth, tileHeight, rowstride, width, height; + FPBox boundarys; + guchar *output_pixels; + GdkPixbuf *output; + + GdkPixbuf *in; + + in = rsvg_filter_get_in (self->in, ctx); + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + rowstride = gdk_pixbuf_get_rowstride (in); + + oself = (RsvgFilterPrimitiveTurbulence *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + tileWidth = (boundarys.x2 - boundarys.x1); + tileHeight = (boundarys.y2 - boundarys.y1); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = 0; y < tileHeight; y++) + { + for (x = 0; x < tileWidth; x++) + { + gint i; + double point[2] = {x+boundarys.x1, y+boundarys.y1}; + + for (i = 0; i < 4; i++) + { + double cr; + + cr = feTurbulence_turbulence(oself, i, point, (double)x, (double)y, (double)tileWidth, (double)tileHeight); + + if(oself->bFractalSum) + cr = ((cr * 255.) + 255.) / 2.; + else + cr = (cr * 255.); + + cr = CLAMP(cr, 0., 255.); + + output_pixels[(4 * (x+boundarys.x1)) + ((y+boundarys.y1) * rowstride) + i] = (guchar)(cr); + } + } + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_turbulence_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveTurbulence *oself; + + oself = (RsvgFilterPrimitiveTurbulence *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (oself); +} + +void +rsvg_start_filter_primitive_turbulence (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveTurbulence *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveTurbulence, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->fBaseFreqX = 0; + filter->fBaseFreqY = 0; + filter->nNumOctaves = 1; + filter->seed = 0; + filter->bDoStitching = 0; + filter->bFractalSum = 0; + + feTurbulence_init(filter); + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "baseFrequency"))) + rsvg_css_parse_number_optional_number(value, &filter->fBaseFreqX, &filter->fBaseFreqY); + if ((value = rsvg_property_bag_lookup (atts, "numOctaves"))) + filter->nNumOctaves = atoi(value); + if ((value = rsvg_property_bag_lookup (atts, "seed"))) + filter->seed = atoi(value); + if ((value = rsvg_property_bag_lookup (atts, "stitchTiles"))) + filter->bDoStitching = (!strcmp(value, "stitch")); + if ((value = rsvg_property_bag_lookup (atts, "type"))) + filter->bFractalSum = (!strcmp(value, "fractalNoise")); + } + + filter->super.render = &rsvg_filter_primitive_turbulence_render; + filter->super.free = &rsvg_filter_primitive_turbulence_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveImage RsvgFilterPrimitiveImage; + +struct _RsvgFilterPrimitiveImage +{ + RsvgFilterPrimitive super; + GString *href; +}; + +#ifndef GDK_PIXBUF_CHECK_VERSION +#define GDK_PIXBUF_CHECK_VERSION(major,minor,micro) \ + (GDK_PIXBUF_MAJOR > (major) || \ + (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR > (minor)) || \ + (GDK_PIXBUF_MAJOR == (major) && GDK_PIXBUF_MINOR == (minor) && \ + GDK_PIXBUF_MICRO >= (micro))) +#endif + + +static GdkPixbuf * +rsvg_filter_primitive_image_render_in (RsvgFilterPrimitive * self, + RsvgFilterContext * context) +{ + FPBox boundarys; + RsvgHandle * ctx; + RsvgFilterPrimitiveImage *oself; + int i; + RsvgDefVal * parent; + GdkPixbuf *img, *save; + RsvgDefsDrawable *drawable; + + ctx = context->ctx; + oself = (RsvgFilterPrimitiveImage *) self; + + if(!oself->href) + return NULL; + + parent = rsvg_defs_lookup (ctx->defs, oself->href->str+1); + if (!parent) + return NULL; + + drawable = (RsvgDefsDrawable*)parent; + + boundarys = rsvg_filter_primitive_get_bounds (self, context); + + img = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, context->width, context->height); + + save = ctx->pixbuf; + ctx->pixbuf = img; + + for (i = 0; i < 6; i++) + ctx->state[ctx->n_state - 1].affine[i] = context->paffine[i]; + + /* push the state stack */ + if (ctx->n_state == ctx->n_state_max) + ctx->state = g_renew (RsvgState, ctx->state, + ctx->n_state_max <<= 1); + if (ctx->n_state) + rsvg_state_inherit (&ctx->state[ctx->n_state], + &ctx->state[ctx->n_state - 1]); + else + rsvg_state_init (ctx->state); + ctx->n_state++; + + rsvg_defs_drawable_draw (drawable, ctx, 0); + + /* pop the state stack */ + ctx->n_state--; + rsvg_state_finalize (&ctx->state[ctx->n_state]); + + ctx->pixbuf = save; + return img; +} + +static GdkPixbuf * +rsvg_filter_primitive_image_render_ext (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + FPBox boundarys; + + RsvgFilterPrimitiveImage *oself; + + GdkPixbuf *img; + + oself = (RsvgFilterPrimitiveImage *) self; + + if(!oself->href) + return NULL; + + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + +#if GDK_PIXBUF_CHECK_VERSION(2,3,2) + img = gdk_pixbuf_new_from_file_at_size(oself->href->str, + boundarys.x2 - boundarys.x1, + boundarys.y2 - boundarys.y1, + NULL); +#else + img = gdk_pixbuf_new_from_file(oself->href->str, NULL); + if(img) + { + GdkPixbuf *scaled; + + scaled = gdk_pixbuf_scale_simple(img, boundarys.x2 - boundarys.x1, + boundarys.y2 - boundarys.y1, + GDK_INTERP_BILINEAR); + + g_object_unref (G_OBJECT (img)); + img = scaled; + } +#endif + + return img; +} + +static void +rsvg_filter_primitive_image_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + FPBox boundarys; + RsvgFilterPrimitiveImage *oself; + + GdkPixbuf *output, *img; + + oself = (RsvgFilterPrimitiveImage *) self; + + if(!oself->href) + return; + + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, ctx->width, ctx->height); + + img = rsvg_filter_primitive_image_render_in (self, ctx); + if (img == NULL) + { + img = rsvg_filter_primitive_image_render_ext (self, ctx); + if (img) + { + gdk_pixbuf_copy_area (img, 0, 0, + boundarys.x2 - boundarys.x1, + boundarys.y2 - boundarys.y1, + output, boundarys.x1, boundarys.y1); + g_object_unref (G_OBJECT (img)); + } + } + else + { + gdk_pixbuf_copy_area (img, boundarys.x1, boundarys.y1, boundarys.x2 - boundarys.x1, boundarys.y2 - boundarys.y1, + output, boundarys.x1, boundarys.y1); + g_object_unref (G_OBJECT (img)); + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_image_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveImage *oself; + + oself = (RsvgFilterPrimitiveImage *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + + if(oself->href) + g_string_free (oself->href, TRUE); + + g_free (oself); +} + +void +rsvg_start_filter_primitive_image (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveImage *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveImage, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "xlink:href"))) + { + filter->href = g_string_new (NULL); + g_string_assign (filter->href, value); + } + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + } + + filter->super.render = &rsvg_filter_primitive_image_render; + filter->super.free = &rsvg_filter_primitive_image_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + + +typedef struct _FactorAndMatrix FactorAndMatrix; + +struct _FactorAndMatrix +{ + gint matrix[9]; + gdouble factor; +}; + +typedef struct _vector3 vector3; + +struct _vector3 +{ + gdouble x; + gdouble y; + gdouble z; +}; + +static gdouble +norm (vector3 A) +{ + return sqrt(A.x*A.x + A.y*A.y + A.z*A.z); +} + +static gdouble +dotproduct (vector3 A, vector3 B) +{ + return A.x*B.x + A.y*B.y + A.z*B.z; +} + +static vector3 +normalise (vector3 A) +{ + double divisor; + divisor = norm(A); + + A.x /= divisor; + A.y /= divisor; + A.z /= divisor; + + return A; +} + +static FactorAndMatrix +get_light_normal_matrix_x (gint n) +{ + const static FactorAndMatrix matrix_list [] = + { + { + {0, 0, 0, + 0, -2, 2, + 0, -1, 1}, + 2.0/3.0 + }, + { + {0, 0, 0, + -2, 0, 2, + -1, 0, 1}, + 1.0/3.0 + }, + { + {0, 0, 0, + -2, 2, 0, + -1, 1, 0}, + 2.0/3.0 + }, + { + {0, -1, 1, + 0, -2, 2, + 0, -1, 1}, + 1.0/2.0 + }, + { + {-1, 0, 1, + -2, 0, 2, + -1, 0, 1}, + 1.0/4.0 + }, + { + {-1, 1, 0, + -2, 2, 0, + -1, 1, 0}, + 1.0/2.0 + }, + { + {0, -1, 1, + 0, -2, 2, + 0, 0, 0}, + 2.0/3.0 + }, + { + {-1, 0, 1, + -2, 0, 2, + 0, 0, 0}, + 1.0/3.0 + }, + { + {-1, 1, 0, + -2, 2, 0, + 0, 0, 0}, + 2.0/3.0 + } + }; + + return matrix_list[n]; +} + +static FactorAndMatrix +get_light_normal_matrix_y (gint n) +{ + const static FactorAndMatrix matrix_list [] = + { + { + {0, 0, 0, + 0, -2, -1, + 0, 2, 1}, + 2.0/3.0 + }, + { + {0, 0, 0, + -1, -2, -1, + 1, 2, 1}, + 1.0/3.0 + }, + { + {0, 0, 0, + -1, -2, 0, + 1, 2, 0}, + 2.0/3.0 + }, + { + + {0, -2, -1, + 0, 0, 0, + 0, 2, 1}, + 1.0/2.0 + }, + { + {-1, -2, -1, + 0, 0, 0, + 1, 2, 1}, + 1.0/4.0 + }, + { + {-1, -2, 0, + 0, 0, 0, + 1, 2, 0}, + 1.0/2.0 + }, + { + + {0, -2, -1, + 0, 2, 1, + 0, 0, 0}, + 2.0/3.0 + }, + { + {0, -2, -1, + 1, 2, 1, + 0, 0, 0}, + 1.0/3.0 + }, + { + {-1, -2, 0, + 1, 2, 0, + 0, 0, 0}, + 2.0/3.0 + + } + }; + + return matrix_list[n]; +} + +static vector3 +get_surface_normal (guchar * I, FPBox boundarys, gint x, gint y, + gdouble dx, gdouble dy, gdouble rawdx, gdouble rawdy, gdouble surfaceScale, gint rowstride) +{ + gint mrow, mcol; + FactorAndMatrix fnmx, fnmy; + gint *Kx, *Ky; + gdouble factorx, factory; + gdouble Nx, Ny; + vector3 output; + + if (x + dx >= boundarys.x2 - 1) + mcol = 2; + else if (x - dx < boundarys.x1 + 1) + mcol = 0; + else + mcol = 1; + + if (y + dy >= boundarys.y2 - 1) + mrow = 2; + else if (y - dy < boundarys.y1 + 1) + mrow = 0; + else + mrow = 1; + + fnmx = get_light_normal_matrix_x(mrow * 3 + mcol); + factorx = fnmx.factor / rawdx; + Kx = fnmx.matrix; + + fnmy = get_light_normal_matrix_y(mrow * 3 + mcol); + factory = fnmy.factor / rawdy; + Ky = fnmy.matrix; + + Nx = -surfaceScale * factorx * (gdouble) + (Kx[0]*gdk_pixbuf_get_interp_pixel(I,x-dx,y-dy, 3, boundarys, rowstride) + + Kx[1]*gdk_pixbuf_get_interp_pixel(I,x ,y-dy, 3, boundarys, rowstride) + + Kx[2]*gdk_pixbuf_get_interp_pixel(I,x+dx,y-dy, 3, boundarys, rowstride) + + Kx[3]*gdk_pixbuf_get_interp_pixel(I,x-dx,y , 3, boundarys, rowstride) + + Kx[4]*gdk_pixbuf_get_interp_pixel(I,x ,y , 3, boundarys, rowstride) + + Kx[5]*gdk_pixbuf_get_interp_pixel(I,x+dx,y , 3, boundarys, rowstride) + + Kx[6]*gdk_pixbuf_get_interp_pixel(I,x-dx,y+dy, 3, boundarys, rowstride) + + Kx[7]*gdk_pixbuf_get_interp_pixel(I,x ,y+dy, 3, boundarys, rowstride) + + Kx[8]*gdk_pixbuf_get_interp_pixel(I,x+dx,y+dy, 3, boundarys, rowstride)) / 255.0; + + Ny = -surfaceScale * factory * (gdouble) + (Ky[0]*gdk_pixbuf_get_interp_pixel(I,x-dx,y-dy, 3, boundarys, rowstride) + + Ky[1]*gdk_pixbuf_get_interp_pixel(I,x ,y-dy, 3, boundarys, rowstride) + + Ky[2]*gdk_pixbuf_get_interp_pixel(I,x+dx,y-dy, 3, boundarys, rowstride) + + Ky[3]*gdk_pixbuf_get_interp_pixel(I,x-dx,y , 3, boundarys, rowstride) + + Ky[4]*gdk_pixbuf_get_interp_pixel(I,x ,y , 3, boundarys, rowstride) + + Ky[5]*gdk_pixbuf_get_interp_pixel(I,x+dx,y , 3, boundarys, rowstride) + + Ky[6]*gdk_pixbuf_get_interp_pixel(I,x-dx,y+dy, 3, boundarys, rowstride) + + Ky[7]*gdk_pixbuf_get_interp_pixel(I,x ,y+dy, 3, boundarys, rowstride) + + Ky[8]*gdk_pixbuf_get_interp_pixel(I,x+dx,y+dy, 3, boundarys, rowstride)) / 255.0; + + output.x = Nx; + output.y = Ny; + + output.z = 1; + output = normalise(output); + return output; +} + +typedef enum { + DISTANTLIGHT, POINTLIGHT, SPOTLIGHT +} lightType; + +typedef struct _lightSource lightSource; + +struct _lightSource +{ + lightType type; + gdouble x; /*doubles as azimuth*/ + gdouble y; /*dounles as elevation*/ + gdouble z; + gdouble pointsAtX; + gdouble pointsAtY; + gdouble pointsAtZ; + gdouble specularExponent; + gdouble limitingconeAngle; +}; + +static vector3 +get_light_direction (lightSource source, gdouble x, gdouble y, gdouble z, gdouble * affine) +{ + vector3 output; + + switch (source.type) + { + case DISTANTLIGHT: + output.x = cos(source.x)*cos(source.y); + output.y = sin(source.x)*cos(source.y); + output.z = sin(source.y); + break; + case POINTLIGHT: + case SPOTLIGHT: + output.x = source.x * affine[0] + affine[4] - x; + output.y = source.y * affine[3] + affine[5] - y; + output.z = source.z * sqrt(affine[0] * affine[3]) - z; + output = normalise(output); + break; + } + return output; +} + +static vector3 +get_light_colour(lightSource source, vector3 colour, + gdouble x, gdouble y, gdouble z, gdouble * affine) +{ + double base; + vector3 s; + vector3 output; + + if (source.type != SPOTLIGHT) + return colour; + + s.x = source.pointsAtX * affine[0] + affine[4] - source.x; + s.y = source.pointsAtY * affine[3] + affine[5] - source.y; + s.z = source.pointsAtZ * sqrt(affine[0] * affine[3]) - source.z; + s = normalise(s); + + base = -dotproduct(get_light_direction (source, x, y, z, affine), s); + + if (base < 0) + { + output.x = 0; + output.y = 0; + output.z = 0; + return output; + } + + output.x = colour.x*pow(base, source.specularExponent); + output.y = colour.x*pow(base, source.specularExponent); + output.z = colour.x*pow(base, source.specularExponent); + + return output; +} + + +void +rsvg_start_filter_primitive_light_source (RsvgHandle * ctx, + RsvgPropertyBag * atts, char type) +{ + lightSource * data; + const char *value; + double font_size; + font_size = rsvg_state_current_font_size (ctx); + + data = (lightSource *)ctx->currentsubfilter; + + if (type == 's') + data->type = SPOTLIGHT; + else if (type == 'd') + data->type = DISTANTLIGHT; + else + data->type = POINTLIGHT; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "azimuth"))) + { + data->x = rsvg_css_parse_angle(value); + } + if ((value = rsvg_property_bag_lookup (atts, "elevation"))) + { + data->y = rsvg_css_parse_angle(value); + } + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + data->x = rsvg_css_parse_normalized_length(value, ctx->dpi, + 1, font_size); + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + data->y = rsvg_css_parse_normalized_length(value, ctx->dpi, + 1, font_size); + } + if ((value = rsvg_property_bag_lookup (atts, "z"))) + { + data->z = rsvg_css_parse_normalized_length(value, ctx->dpi, + 1, font_size); + } + if ((value = rsvg_property_bag_lookup (atts, "pointsAtX"))) + { + data->pointsAtX = rsvg_css_parse_normalized_length(value, ctx->dpi, + 1, font_size); + } + if ((value = rsvg_property_bag_lookup (atts, "pointsAtY"))) + { + data->pointsAtY = rsvg_css_parse_normalized_length(value, ctx->dpi, + 1, font_size); + } + if ((value = rsvg_property_bag_lookup (atts, "pointsAtZ"))) + { + data->pointsAtZ = rsvg_css_parse_normalized_length(value, ctx->dpi, + 1, font_size); + } + } +} + + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveDiffuseLighting RsvgFilterPrimitiveDiffuseLighting; + +struct _RsvgFilterPrimitiveDiffuseLighting +{ + RsvgFilterPrimitive super; + gdouble dx, dy; + double diffuseConstant; + double surfaceScale; + lightSource source; + guint32 lightingcolour; +}; + +static void +rsvg_filter_primitive_diffuse_lighting_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + gint x, y; + float dy, dx, rawdy, rawdx; + gdouble z; + gint rowstride, height, width; + gdouble factor, surfaceScale; + vector3 lightcolour, L, N; + vector3 colour; + + FPBox boundarys; + + guchar *in_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveDiffuseLighting *oself; + + GdkPixbuf *output; + GdkPixbuf *in; + + oself = (RsvgFilterPrimitiveDiffuseLighting *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + + output_pixels = gdk_pixbuf_get_pixels (output); + + colour.x = ((guchar *)(&oself->lightingcolour))[2] / 255.0; + colour.y = ((guchar *)(&oself->lightingcolour))[1] / 255.0; + colour.z = ((guchar *)(&oself->lightingcolour))[0] / 255.0; + + surfaceScale = oself->surfaceScale / 255.0 + * sqrt(ctx->paffine[0] * ctx->paffine[3]); + + if (oself->dy < 0 || oself->dx < 0) + { + dx = 1; + dy = 1; + rawdx = 1; + rawdy = 1; + } + else + { + dx = oself->dx * ctx->paffine[0]; + dy = oself->dy * ctx->paffine[3]; + rawdx = oself->dx; + rawdy = oself->dy; + } + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + z = surfaceScale * (double)in_pixels[y * rowstride + x * 4 + 3]; + L = get_light_direction(oself->source, x, y, z, ctx->paffine); + N = get_surface_normal(in_pixels, boundarys, x, y, + dx, dy, rawdx, rawdy, oself->surfaceScale, + rowstride); + lightcolour = get_light_colour(oself->source, colour, x, y, z, + ctx->paffine); + factor = dotproduct(N, L); + + output_pixels[y * rowstride + x * 4 ] = MAX(0,MIN(255, oself->diffuseConstant * factor * + lightcolour.x * 255.0)); + output_pixels[y * rowstride + x * 4 + 1] = MAX(0,MIN(255, oself->diffuseConstant * factor * + lightcolour.y * 255.0)); + output_pixels[y * rowstride + x * 4 + 2] = MAX(0,MIN(255, oself->diffuseConstant * factor * + lightcolour.z * 255.0)); + output_pixels[y * rowstride + x * 4 + 3] = 255; + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_diffuse_lighting_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveDiffuseLighting *oself; + + oself = (RsvgFilterPrimitiveDiffuseLighting *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (oself); +} + +void +rsvg_start_filter_primitive_diffuse_lighting (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveDiffuseLighting *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveDiffuseLighting, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->surfaceScale = 1; + filter->diffuseConstant = 1; + filter->dx = 1; + filter->dy = 1; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "kernelUnitLength"))) + rsvg_css_parse_number_optional_number (value, + &filter->dx, &filter->dy); + if ((value = rsvg_property_bag_lookup (atts, "lighting-color"))) + filter->lightingcolour = rsvg_css_parse_color (value); + if ((value = rsvg_property_bag_lookup (atts, "diffuseConstant"))) + filter->diffuseConstant = + g_ascii_strtod(value, NULL); + if ((value = rsvg_property_bag_lookup (atts, "surfaceScale"))) + filter->surfaceScale = + g_ascii_strtod(value, NULL); + } + + filter->super.render = &rsvg_filter_primitive_diffuse_lighting_render; + filter->super.free = &rsvg_filter_primitive_diffuse_lighting_free; + ctx->currentsubfilter = &filter->source; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveSpecularLighting RsvgFilterPrimitiveSpecularLighting; + +struct _RsvgFilterPrimitiveSpecularLighting +{ + RsvgFilterPrimitive super; + double specularConstant; + double specularExponent; + double surfaceScale; + lightSource source; + guint32 lightingcolour; +}; + +static void +rsvg_filter_primitive_specular_lighting_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + gint x, y, temp; + gdouble z, surfaceScale; + gint rowstride, height, width; + gdouble factor, max, base; + vector3 lightcolour; + vector3 colour; + vector3 L; + + FPBox boundarys; + + guchar *in_pixels; + guchar *output_pixels; + + RsvgFilterPrimitiveSpecularLighting *oself; + + GdkPixbuf *output; + GdkPixbuf *in; + + oself = (RsvgFilterPrimitiveSpecularLighting *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + height = gdk_pixbuf_get_height (in); + width = gdk_pixbuf_get_width (in); + + rowstride = gdk_pixbuf_get_rowstride (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + + output_pixels = gdk_pixbuf_get_pixels (output); + + colour.x = ((guchar *)(&oself->lightingcolour))[2] / 255.0; + colour.y = ((guchar *)(&oself->lightingcolour))[1] / 255.0; + colour.z = ((guchar *)(&oself->lightingcolour))[0] / 255.0; + + surfaceScale = oself->surfaceScale * sqrt(ctx->paffine[0] * ctx->paffine[3]) / 255.0; + + for (y = boundarys.y1; y < boundarys.y2; y++) + for (x = boundarys.x1; x < boundarys.x2; x++) + { + z = in_pixels[y * rowstride + x * 4 + 3] * surfaceScale; + L = get_light_direction(oself->source, x, y, z, ctx->paffine); + L.z += 1; + L = normalise(L); + + lightcolour = get_light_colour(oself->source, colour, x, y, z, + ctx->paffine); + base = dotproduct(get_surface_normal(in_pixels, boundarys, x, y, + 1, 1, 1.0 / ctx->paffine[0], 1.0 / ctx->paffine[3], + oself->surfaceScale, + rowstride), L); + + factor = pow(base, oself->specularExponent); + + max = 0; + temp = oself->specularConstant * factor* lightcolour.x * 255.0; + if (temp < 0) + temp = 0; + if (temp > 255) + temp = 255; + max = MAX(temp, max); + output_pixels[y * rowstride + x * 4 ] = temp; + temp = oself->specularConstant * factor * lightcolour.y * 255.0; + if (temp < 0) + temp = 0; + if (temp > 255) + temp = 255; + max = MAX(temp, max); + output_pixels[y * rowstride + x * 4 + 1] = temp; + temp = oself->specularConstant * factor * lightcolour.z * 255.0; + if (temp < 0) + temp = 0; + if (temp > 255) + temp = 255; + max = MAX(temp, max); + output_pixels[y * rowstride + x * 4 + 2] = temp; + + output_pixels[y * rowstride + x * 4 + 3] = max; + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (in)); + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_specular_lighting_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveSpecularLighting *oself; + + oself = (RsvgFilterPrimitiveSpecularLighting *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (oself); +} + +void +rsvg_start_filter_primitive_specular_lighting (RsvgHandle * ctx, RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveSpecularLighting *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveSpecularLighting, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + filter->surfaceScale = 1; + filter->specularConstant = 1; + filter->specularExponent = 1; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "lighting-color"))) + filter->lightingcolour = rsvg_css_parse_color (value); + if ((value = rsvg_property_bag_lookup (atts, "specularConstant"))) + filter->specularConstant = + g_ascii_strtod(value, NULL); + if ((value = rsvg_property_bag_lookup (atts, "specularExponent"))) + filter->specularExponent = + g_ascii_strtod(value, NULL); + if ((value = rsvg_property_bag_lookup (atts, "surfaceScale"))) + filter->surfaceScale = + g_ascii_strtod(value, NULL); + } + + filter->super.render = &rsvg_filter_primitive_specular_lighting_render; + filter->super.free = &rsvg_filter_primitive_specular_lighting_free; + ctx->currentsubfilter = &filter->source; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} + +/*************************************************************/ +/*************************************************************/ + +typedef struct _RsvgFilterPrimitiveTile +RsvgFilterPrimitiveTile; + +struct _RsvgFilterPrimitiveTile +{ + RsvgFilterPrimitive super; +}; + +static int +mod(int a, int b) +{ + while (a < 0) + a += b; + return a % b; +} + +static void +rsvg_filter_primitive_tile_render (RsvgFilterPrimitive * self, + RsvgFilterContext * ctx) +{ + guchar i; + gint x, y; + gint rowstride, height, width; + FPBox boundarys; + + guchar *in_pixels; + guchar *output_pixels; + + GdkPixbuf *output; + GdkPixbuf *in; + + RsvgFilterPrimitiveTile *bself; + + bself = (RsvgFilterPrimitiveTile *) self; + boundarys = rsvg_filter_primitive_get_bounds (self, ctx); + + height = ctx->height; + width = ctx->width; + + in = rsvg_filter_get_in (self->in, ctx); + in_pixels = gdk_pixbuf_get_pixels (in); + + output = gdk_pixbuf_new_cleared (GDK_COLORSPACE_RGB, 1, 8, width, height); + rowstride = gdk_pixbuf_get_rowstride (output); + + output_pixels = gdk_pixbuf_get_pixels (output); + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + for (i = 0; i < 4; i++) + { + output_pixels[4 * x + y * rowstride + i] = + in_pixels[(mod((x - boundarys.x1), (boundarys.x2 - boundarys.x1)) + + boundarys.x1) * 4 + + (mod((y - boundarys.y1), (boundarys.y2 - boundarys.y1)) + + boundarys.y1) * rowstride + i]; + } + + rsvg_filter_store_result (self->result, output, ctx); + + g_object_unref (G_OBJECT (output)); +} + +static void +rsvg_filter_primitive_tile_free (RsvgFilterPrimitive * self) +{ + RsvgFilterPrimitiveTile *cself; + + cself = (RsvgFilterPrimitiveTile *) self; + g_string_free (self->result, TRUE); + g_string_free (self->in, TRUE); + g_free (cself); +} + +void +rsvg_start_filter_primitive_tile (RsvgHandle * ctx, + RsvgPropertyBag * atts) +{ + const char *value; + double font_size; + RsvgFilterPrimitiveTile *filter; + + font_size = rsvg_state_current_font_size (ctx); + + filter = g_new (RsvgFilterPrimitiveTile, 1); + + filter->super.in = g_string_new ("none"); + filter->super.result = g_string_new ("none"); + filter->super.sizedefaults = 1; + + if (rsvg_property_bag_size (atts)) + { + if ((value = rsvg_property_bag_lookup (atts, "in"))) + g_string_assign (filter->super.in, value); + if ((value = rsvg_property_bag_lookup (atts, "result"))) + g_string_assign (filter->super.result, value); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + { + filter->super.x = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "y"))) + { + filter->super.y = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "width"))) + { + filter->super.width = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) + { + filter->super.height = + rsvg_css_parse_normalized_length (value, + ctx->dpi, + 1, + font_size); + filter->super.sizedefaults = 0; + } + } + + filter->super.render = &rsvg_filter_primitive_tile_render; + filter->super.free = &rsvg_filter_primitive_tile_free; + + g_ptr_array_add (((RsvgFilter *) (ctx->currentfilter))->primitives, + &filter->super); +} diff --git a/rsvg-filter.h b/rsvg-filter.h new file mode 100644 index 00000000..8ae46471 --- /dev/null +++ b/rsvg-filter.h @@ -0,0 +1,123 @@ +/* vim: set sw=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + rsvg-filter.h : Provides filters + + Copyright (C) 2004 Caleb Moore + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this program; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + + Author: Caleb Moore <calebmm@tpg.com.au> +*/ + +#ifndef RSVG_FILTER_H +#define RSVG_FILTER_H + +#include "rsvg.h" +#include "rsvg-defs.h" +#include <libxml/SAX.h> + +G_BEGIN_DECLS + +typedef enum { + objectBoundingBox, userSpaceOnUse +} RsvgFilterUnits; + +struct _RsvgFilter { + RsvgDefVal super; + int refcnt; + GPtrArray * primitives; + double x, y, width, height; + RsvgFilterUnits filterunits; + RsvgFilterUnits primitiveunits; +}; + +void +rsvg_filter_render (RsvgFilter *self, GdkPixbuf *source, GdkPixbuf *output, GdkPixbuf *bg, RsvgHandle *context); + +void +rsvg_start_filter (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_end_filter (RsvgHandle *ctx); + +RsvgFilter * +rsvg_filter_parse (const RsvgDefs *defs, const char *str); + +void +rsvg_start_filter_primitive_blend (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_start_filter_primitive_convolve_matrix (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_start_filter_primitive_gaussian_blur (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_start_filter_primitive_offset (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_start_filter_primitive_merge (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_start_filter_primitive_merge_node (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_start_filter_primitive_colour_matrix (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void +rsvg_start_filter_primitive_component_transfer (RsvgHandle * ctx, + RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_component_transfer_function (RsvgHandle * ctx, + RsvgPropertyBag * atts, char channel); + +void +rsvg_start_filter_primitive_erode (RsvgHandle * ctx, + RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_composite (RsvgHandle * ctx, RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_flood (RsvgHandle * ctx, + RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_displacement_map (RsvgHandle * ctx, RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_turbulence (RsvgHandle * ctx, RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_image (RsvgHandle * ctx, RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_diffuse_lighting (RsvgHandle * ctx, RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_light_source (RsvgHandle * ctx, + RsvgPropertyBag * atts, char type); + +void +rsvg_start_filter_primitive_specular_lighting (RsvgHandle * ctx, RsvgPropertyBag * atts); + +void +rsvg_start_filter_primitive_tile (RsvgHandle * ctx, RsvgPropertyBag * atts); + +G_END_DECLS + +#endif diff --git a/rsvg-paint-server.c b/rsvg-paint-server.c index 2962027b..2dabec60 100644 --- a/rsvg-paint-server.c +++ b/rsvg-paint-server.c @@ -23,8 +23,9 @@ */ #include "config.h" -#include "rsvg-paint-server.h" #include "rsvg-private.h" +#include "rsvg-defs.h" +#include "rsvg-paint-server.h" #include <glib/gmem.h> #include <glib/gmessages.h> diff --git a/rsvg-private.h b/rsvg-private.h index c34887d7..8a677504 100644 --- a/rsvg-private.h +++ b/rsvg-private.h @@ -27,18 +27,23 @@ #define RSVG_PRIVATE_H #include "rsvg.h" -#include "rsvg-styles.h" #include <libxml/SAX.h> #include <libxml/xmlmemory.h> +#include <pango/pangoft2.h> G_BEGIN_DECLS typedef struct RsvgSaxHandler RsvgSaxHandler; +typedef struct _RsvgPropertyBag RsvgPropertyBag; +typedef struct _RsvgState RsvgState; +typedef struct _RsvgDefs RsvgDefs; +typedef struct _RsvgDefVal RsvgDefVal; +typedef struct _RsvgFilter RsvgFilter; struct RsvgSaxHandler { void (*free) (RsvgSaxHandler *self); - void (*start_element) (RsvgSaxHandler *self, const xmlChar *name, const xmlChar **atts); + void (*start_element) (RsvgSaxHandler *self, const xmlChar *name, RsvgPropertyBag *atts); void (*end_element) (RsvgSaxHandler *self, const xmlChar *name); void (*characters) (RsvgSaxHandler *self, const xmlChar *ch, int len); }; @@ -56,6 +61,8 @@ struct RsvgHandle { RsvgDefs *defs; gboolean in_defs; + void *current_defs_group; + GHashTable *css_props; /* not a handler stack. each nested handler keeps @@ -77,6 +84,9 @@ struct RsvgHandle { GString * title; GString * desc; + void * currentfilter; + void * currentsubfilter; + /* virtual fns */ gboolean (* write) (RsvgHandle *handle, const guchar *buf, @@ -102,6 +112,50 @@ gboolean rsvg_handle_close_impl (RsvgHandle *handle, GError **error); void rsvg_handle_free_impl (RsvgHandle *handle); +typedef enum { + RSVG_SIZE_ZOOM, + RSVG_SIZE_WH, + RSVG_SIZE_WH_MAX, + RSVG_SIZE_ZOOM_MAX +} RsvgSizeType; + +struct RsvgSizeCallbackData +{ + RsvgSizeType type; + double x_zoom; + double y_zoom; + gint width; + gint height; +}; + +/* private */ +GdkPixbuf * +rsvg_pixbuf_from_file_with_size_data (const gchar * file_name, + struct RsvgSizeCallbackData * data, + GError ** error); +/* private */ +GdkPixbuf * +rsvg_pixbuf_from_stdio_file_with_size_data(FILE * f, + struct RsvgSizeCallbackData * data, + GError ** error); + +struct _RsvgPropertyBag +{ + GHashTable * props; +}; + +RsvgPropertyBag * +rsvg_property_bag_new (const xmlChar **atts); + +void +rsvg_property_bag_free (RsvgPropertyBag *bag); + +const char * +rsvg_property_bag_lookup (RsvgPropertyBag *bag, const char * key); + +guint +rsvg_property_bag_size (RsvgPropertyBag *bag); + G_END_DECLS #endif diff --git a/rsvg-shapes.c b/rsvg-shapes.c index 90719717..497f4efa 100644 --- a/rsvg-shapes.c +++ b/rsvg-shapes.c @@ -24,11 +24,12 @@ */ #include <string.h> #include <math.h> +#include <errno.h> -#include "rsvg-shapes.h" +#include "rsvg-private.h" #include "rsvg-styles.h" +#include "rsvg-shapes.h" #include "rsvg-css.h" -#include "rsvg-private.h" #include "rsvg-bpath-util.h" #include "rsvg-path.h" #include "rsvg-defs.h" @@ -45,13 +46,26 @@ /* 4/3 * (1-cos 45ƒ)/sin 45ƒ = 4/3 * sqrt(2) - 1 */ #define RSVG_ARC_MAGIC ((double) 0.5522847498) -struct _RsvgDefsPath { - RsvgDefVal super; - RsvgState state; + +typedef struct _RsvgDefsDrawablePath RsvgDefsDrawablePath; +typedef struct _RsvgDefsDrawableGroup RsvgDefsDrawableGroup; +typedef struct _RsvgDefsDrawableUse RsvgDefsDrawableUse; + +struct _RsvgDefsDrawablePath { + RsvgDefsDrawable super; char *d; }; - -typedef struct _RsvgDefsPath RsvgDefsPath; + +struct _RsvgDefsDrawableGroup { + RsvgDefsDrawable super; + GPtrArray *children; +}; + +struct _RsvgDefsDrawableUse { + RsvgDefsDrawable super; + RsvgDefsDrawable *child; +}; + /** * rsvg_close_vpath: Close a vector path. @@ -181,7 +195,7 @@ rsvg_render_svp (RsvgHandle *ctx, const ArtSVP *svp, } has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); - + render = art_render_new (0, 0, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), @@ -192,7 +206,7 @@ rsvg_render_svp (RsvgHandle *ctx, const ArtSVP *svp, gdk_pixbuf_get_bits_per_sample (pixbuf), has_alpha ? ART_ALPHA_SEPARATE : ART_ALPHA_NONE, NULL); - + art_render_svp (render, svp); art_render_mask_solid (render, (opacity << 8) + opacity + (opacity >> 7)); @@ -206,7 +220,6 @@ rsvg_render_svp (RsvgHandle *ctx, const ArtSVP *svp, state = rsvg_state_current(ctx); for (i = 0; i < 6; i++) gradctx.affine[i] = state->affine[i]; - rsvg_render_paint_server (render, ps, &gradctx); art_render_invoke (render); } @@ -244,11 +257,11 @@ rsvg_render_bpath (RsvgHandle *ctx, const ArtBpath *bpath) vpath = art_bez_path_to_vec (affine_bpath, 0.25); art_free (affine_bpath); - need_tmpbuf = (state->fill != NULL) && (state->stroke != NULL) && - state->opacity != 0xff; + need_tmpbuf = ((state->fill != NULL) && (state->stroke != NULL) && + state->opacity != 0xff) || state->filter; if (need_tmpbuf) - rsvg_push_opacity_group (ctx); + rsvg_push_discrete_layer (ctx); if (state->fill != NULL) { @@ -308,9 +321,9 @@ rsvg_render_bpath (RsvgHandle *ctx, const ArtBpath *bpath) rsvg_render_svp (ctx, svp, state->stroke, opacity); art_svp_free (svp); } - + if (need_tmpbuf) - rsvg_pop_opacity_group (ctx, state->opacity); + rsvg_pop_discrete_layer (ctx); art_free (vpath); } @@ -328,59 +341,241 @@ rsvg_render_path(RsvgHandle *ctx, const char *d) rsvg_bpath_def_free (bpath_def); } +void +rsvg_defs_drawable_draw (RsvgDefsDrawable * self, RsvgHandle *ctx, + int dominate) +{ + self->draw(self, ctx, dominate); +} + +static void +rsvg_defs_drawable_path_free (RsvgDefVal *self) +{ + RsvgDefsDrawablePath *z = (RsvgDefsDrawablePath *)self; + rsvg_state_finalize (&z->super.state); + g_free (z); +} + +static void +rsvg_defs_drawable_path_draw (RsvgDefsDrawable * self, RsvgHandle *ctx, + int dominate) +{ + RsvgState *state = rsvg_state_current (ctx); + RsvgDefsDrawablePath *path = (RsvgDefsDrawablePath*)self; + + /* combine state definitions */ + + rsvg_state_clone (state, &self->state); + if (ctx->n_state > 1) + { + if (dominate) + rsvg_state_dominate(state, &ctx->state[ctx->n_state - 2]); + else + rsvg_state_reinherit(state, &ctx->state[ctx->n_state - 2]); + } + if (state->opacity != 0xff || state->filter) + rsvg_push_discrete_layer (ctx); + + /* always want to render inside of a <use/> */ + rsvg_render_path (ctx, path->d); + + if (state->opacity != 0xff || state->filter) + rsvg_pop_discrete_layer (ctx); +} + static void -rsvg_defs_path_free (RsvgDefVal *self) +rsvg_defs_drawable_group_free (RsvgDefVal *self) { - RsvgDefsPath *z = (RsvgDefsPath *)self; - rsvg_state_finalize (&z->state); + RsvgDefsDrawableGroup *z = (RsvgDefsDrawableGroup *)self; + rsvg_state_finalize (&z->super.state); + g_ptr_array_free(z->children, FALSE); g_free (z); } +static void +rsvg_defs_drawable_group_draw (RsvgDefsDrawable * self, RsvgHandle *ctx, + int dominate) +{ + RsvgState *state = rsvg_state_current (ctx); + RsvgDefsDrawableGroup *group = (RsvgDefsDrawableGroup*)self; + guint i; + + /* combine state definitions */ + rsvg_state_clone (state, &self->state); + if (ctx->n_state > 1) + { + if (dominate) + rsvg_state_dominate(state, &ctx->state[ctx->n_state - 2]); + else + rsvg_state_reinherit(state, &ctx->state[ctx->n_state - 2]); + } + + if (state->opacity != 0xff || state->filter) + rsvg_push_discrete_layer (ctx); + + for (i = 0; i < group->children->len; i++) + { + /* push the state stack */ + if (ctx->n_state == ctx->n_state_max) + ctx->state = g_renew (RsvgState, ctx->state, + ctx->n_state_max <<= 1); + if (ctx->n_state) + rsvg_state_inherit (&ctx->state[ctx->n_state], + &ctx->state[ctx->n_state - 1]); + else + rsvg_state_init (ctx->state); + ctx->n_state++; + + rsvg_defs_drawable_draw (g_ptr_array_index(group->children, i), + ctx, 0); + + /* pop the state stack */ + ctx->n_state--; + rsvg_state_finalize (&ctx->state[ctx->n_state]); + } + + if (state->opacity != 0xff || state->filter) + rsvg_pop_discrete_layer (ctx); + +} + +static void +rsvg_defs_drawable_use_free (RsvgDefVal *self) +{ + RsvgDefsDrawableUse *z = (RsvgDefsDrawableUse *)self; + rsvg_state_finalize (&z->super.state); + g_free (z); +} + +static void +rsvg_defs_drawable_use_draw (RsvgDefsDrawable * self, RsvgHandle *ctx, + int dominate) +{ + RsvgState *state = rsvg_state_current (ctx); + RsvgDefsDrawableUse *use = (RsvgDefsDrawableUse*)self; + + /* combine state definitions */ + + rsvg_state_clone (state, &self->state); + if (ctx->n_state > 1) + { + if (dominate) + rsvg_state_dominate(state, &ctx->state[ctx->n_state - 2]); + else + rsvg_state_reinherit(state, &ctx->state[ctx->n_state - 2]); + } + + if (state->opacity != 0xff || state->filter) + rsvg_push_discrete_layer (ctx); + + + /* push the state stack */ + if (ctx->n_state == ctx->n_state_max) + ctx->state = g_renew (RsvgState, ctx->state, + ctx->n_state_max <<= 1); + if (ctx->n_state) + rsvg_state_inherit (&ctx->state[ctx->n_state], + &ctx->state[ctx->n_state - 1]); + else + rsvg_state_init (ctx->state); + ctx->n_state++; + + rsvg_defs_drawable_draw (use->child, ctx, 1); + + /* pop the state stack */ + ctx->n_state--; + rsvg_state_finalize (&ctx->state[ctx->n_state]); + + if (state->opacity != 0xff || state->filter) + rsvg_pop_discrete_layer (ctx); +} + +static void +rsvg_defs_drawable_group_pack (RsvgDefsDrawableGroup *self, RsvgDefsDrawable *child) +{ + RsvgDefsDrawableGroup *z = (RsvgDefsDrawableGroup *)self; + g_ptr_array_add(z->children, child); +} + +void +rsvg_push_def_group (RsvgHandle *ctx, const char * id) +{ + RsvgDefsDrawableGroup *group; + if (!ctx->in_defs) + return; + + group = g_new (RsvgDefsDrawableGroup, 1); + group->children = g_ptr_array_new(); + rsvg_state_clone (&group->super.state, rsvg_state_current (ctx)); + + group->super.super.type = RSVG_DEF_PATH; + group->super.super.free = rsvg_defs_drawable_group_free; + group->super.draw = rsvg_defs_drawable_group_draw; + + rsvg_defs_set (ctx->defs, id, &group->super.super); + + group->super.parent = (RsvgDefsDrawable *)ctx->current_defs_group; + if (group->super.parent != NULL) + rsvg_defs_drawable_group_pack((RsvgDefsDrawableGroup *)group->super.parent, + &group->super); + ctx->current_defs_group = group; +} + +void +rsvg_pop_def_group (RsvgHandle *ctx) +{ + RsvgDefsDrawableGroup * group; + if (!ctx->in_defs) + return; + group = (RsvgDefsDrawableGroup *)ctx->current_defs_group; + if (group == NULL) + return; + ctx->current_defs_group = group->super.parent; +} + void rsvg_handle_path (RsvgHandle *ctx, const char * d, const char * id) { if (!ctx->in_defs) rsvg_render_path (ctx, d); else { - RsvgDefsPath *path; + RsvgDefsDrawablePath *path; - if (id == NULL) - return; - - path = g_new (RsvgDefsPath, 1); + path = g_new (RsvgDefsDrawablePath, 1); path->d = g_strdup(d); - rsvg_state_clone (&path->state, rsvg_state_current (ctx)); - path->super.type = RSVG_DEF_PATH; - path->super.free = rsvg_defs_path_free; - - rsvg_defs_set (ctx->defs, id, &path->super); + rsvg_state_clone (&path->super.state, rsvg_state_current (ctx)); + path->super.super.type = RSVG_DEF_PATH; + path->super.super.free = rsvg_defs_drawable_path_free; + path->super.draw = rsvg_defs_drawable_path_draw; + rsvg_defs_set (ctx->defs, id, &path->super.super); + + path->super.parent = (RsvgDefsDrawable *)ctx->current_defs_group; + if (path->super.parent != NULL) + rsvg_defs_drawable_group_pack((RsvgDefsDrawableGroup *)path->super.parent, + &path->super); } } void -rsvg_start_path (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_path (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; - char *d = NULL; - const char * klazz = NULL, * id = NULL; + const char * klazz = NULL, * id = NULL, *value, *d = NULL; - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "d")) - d = (char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "class")) - klazz = (char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "d"))) + d = value; + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "path", klazz, id, atts); } if (d == NULL) return; - rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "path", klazz, id, atts); rsvg_handle_path (ctx, d, id); } @@ -410,35 +605,31 @@ rsvg_make_poly_point_list(const char * points) } static void -rsvg_start_any_poly(RsvgHandle *ctx, const xmlChar **atts, gboolean is_polyline) +rsvg_start_any_poly(RsvgHandle *ctx, RsvgPropertyBag *atts, gboolean is_polyline) { /* the only difference between polygon and polyline is that a polyline closes the path */ - int i; const char * verts = (const char *)NULL; GString * g = NULL; gchar ** pointlist = NULL; - const char * klazz = NULL, * id = NULL; + const char * klazz = NULL, * id = NULL, *value; - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - /* support for svg < 1.0 which used verts */ - if (!strcmp ((char *)atts[i], "verts") || !strcmp ((char *)atts[i], "points")) - verts = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + /* support for svg < 1.0 which used verts */ + if ((value = rsvg_property_bag_lookup (atts, "verts")) || (value = rsvg_property_bag_lookup (atts, "points"))) + verts = value; + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), (is_polyline ? "polyline" : "polygon"), klazz, id, atts); } if (!verts) - return; - - rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), (is_polyline ? "polyline" : "polygon"), klazz, id, atts); + return; /* todo: make the following more memory and CPU friendly */ g = rsvg_make_poly_point_list (verts); @@ -448,6 +639,7 @@ rsvg_start_any_poly(RsvgHandle *ctx, const xmlChar **atts, gboolean is_polyline) /* represent as a "moveto, lineto*, close" path */ if (pointlist) { + int i; GString * d = g_string_sized_new (strlen(verts)); g_string_append_printf (d, "M %s %s ", pointlist[0], pointlist[1] ); @@ -464,51 +656,45 @@ rsvg_start_any_poly(RsvgHandle *ctx, const xmlChar **atts, gboolean is_polyline) } void -rsvg_start_polygon (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_polygon (RsvgHandle *ctx, RsvgPropertyBag *atts) { rsvg_start_any_poly (ctx, atts, FALSE); } void -rsvg_start_polyline (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_polyline (RsvgHandle *ctx, RsvgPropertyBag *atts) { rsvg_start_any_poly (ctx, atts, TRUE); } void -rsvg_start_line (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_line (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; double x1 = 0, y1 = 0, x2 = 0, y2 = 0; GString * d = NULL; - const char * klazz = NULL, * id = NULL; + const char * klazz = NULL, * id = NULL, *value; char buf [G_ASCII_DTOSTR_BUF_SIZE]; double font_size; - if (ctx->n_state > 0) - font_size = rsvg_state_current (ctx)->font_size; - else - font_size = 12.0; + font_size = rsvg_state_current_font_size (ctx); - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "x1")) - x1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, font_size); - else if (!strcmp ((char *)atts[i], "y1")) - y1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, font_size); - if (!strcmp ((char *)atts[i], "x2")) - x2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, font_size); - else if (!strcmp ((char *)atts[i], "y2")) - y2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, font_size); - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "x1"))) + x1 = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, font_size); + if ((value = rsvg_property_bag_lookup (atts, "y1"))) + y1 = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, font_size); + if ((value = rsvg_property_bag_lookup (atts, "x2"))) + x2 = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, font_size); + if ((value = rsvg_property_bag_lookup (atts, "y2"))) + y2 = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, font_size); + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "line", klazz, id, atts); } - rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "line", klazz, id, atts); /* emulate a line using a path */ /* ("M %f %f L %f %f", x1, y1, x2, y2) */ @@ -527,46 +713,41 @@ rsvg_start_line (RsvgHandle *ctx, const xmlChar **atts) } void -rsvg_start_rect (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_rect (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; double x = 0., y = 0., w = 0, h = 0, rx = 0., ry = 0.; GString * d = NULL; - const char * klazz = NULL, * id = NULL; + const char * klazz = NULL, * id = NULL, *value; char buf [G_ASCII_DTOSTR_BUF_SIZE]; gboolean got_rx = FALSE, got_ry = FALSE; double font_size; - if (ctx->n_state > 0) - font_size = rsvg_state_current (ctx)->font_size; - else - font_size = 12.0; + font_size = rsvg_state_current_font_size (ctx); - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "x")) - x = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, font_size); - else if (!strcmp ((char *)atts[i], "y")) - y = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, font_size); - else if (!strcmp ((char *)atts[i], "width")) - w = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, font_size); - else if (!strcmp ((char *)atts[i], "height")) - h = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, font_size); - else if (!strcmp ((char *)atts[i], "rx")) { - rx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, font_size); - got_rx = TRUE; - } - else if (!strcmp ((char *)atts[i], "ry")) { - ry = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, font_size); - got_ry = TRUE; - } - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "x"))) + x = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, font_size); + if ((value = rsvg_property_bag_lookup (atts, "y"))) + y = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, font_size); + if ((value = rsvg_property_bag_lookup (atts, "width"))) + w = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, font_size); + if ((value = rsvg_property_bag_lookup (atts, "height"))) + h = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, font_size); + if ((value = rsvg_property_bag_lookup (atts, "rx"))) { + rx = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, font_size); + got_rx = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "ry"))) { + ry = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, font_size); + got_ry = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "rect", klazz, id, atts); } if (got_rx && !got_ry) @@ -580,9 +761,7 @@ rsvg_start_rect (RsvgHandle *ctx, const xmlChar **atts) if (rx > fabs(w / 2.)) rx = fabs(w / 2.); if (ry > fabs(h / 2.)) - ry = fabs(h / 2.); - - rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "rect", klazz, id, atts); + ry = fabs(h / 2.); /* incrementing y by 1 properly draws borders. this is a HACK */ y += .01; @@ -671,43 +850,36 @@ rsvg_start_rect (RsvgHandle *ctx, const xmlChar **atts) } void -rsvg_start_circle (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_circle (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; double cx = 0, cy = 0, r = 0; GString * d = NULL; - const char * klazz = NULL, * id = NULL; + const char * klazz = NULL, * id = NULL, *value; char buf [G_ASCII_DTOSTR_BUF_SIZE]; double font_size; - if (ctx->n_state > 0) - font_size = rsvg_state_current (ctx)->font_size; - else - font_size = 12.0; + font_size = rsvg_state_current_font_size (ctx); - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "cx")) - cx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, font_size); - else if (!strcmp ((char *)atts[i], "cy")) - cy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, font_size); - else if (!strcmp ((char *)atts[i], "r")) - r = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, - rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), - font_size); - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "cx"))) + cx = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, font_size); + if ((value = rsvg_property_bag_lookup (atts, "cy"))) + cy = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, font_size); + if ((value = rsvg_property_bag_lookup (atts, "r"))) + r = rsvg_css_parse_normalized_length (value, ctx->dpi, + rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), + font_size); + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "circle", klazz, id, atts); } if (r <= 0.) - return; - - rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "circle", klazz, id, atts); + return; /* approximate a circle using 4 bezier curves */ @@ -775,43 +947,36 @@ rsvg_start_circle (RsvgHandle *ctx, const xmlChar **atts) } void -rsvg_start_ellipse (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_ellipse (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; double cx = 0, cy = 0, rx = 0, ry = 0; GString * d = NULL; - const char * klazz = NULL, * id = NULL; + const char * klazz = NULL, * id = NULL, *value; char buf [G_ASCII_DTOSTR_BUF_SIZE]; double font_size; - if (ctx->n_state > 0) - font_size = rsvg_state_current (ctx)->font_size; - else - font_size = 12.0; + font_size = rsvg_state_current_font_size (ctx); - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "cx")) - cx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, font_size); - else if (!strcmp ((char *)atts[i], "cy")) - cy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, font_size); - else if (!strcmp ((char *)atts[i], "rx")) - rx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, font_size); - else if (!strcmp ((char *)atts[i], "ry")) - ry = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, font_size); - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "cx"))) + cx = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, font_size); + if ((value = rsvg_property_bag_lookup (atts, "cy"))) + cy = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, font_size); + if ((value = rsvg_property_bag_lookup (atts, "rx"))) + rx = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, font_size); + if ((value = rsvg_property_bag_lookup (atts, "ry"))) + ry = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, font_size); + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "ellipse", klazz, id, atts); } if (rx <= 0. || ry <= 0.) - return; - - rsvg_parse_style_attrs (ctx, rsvg_state_current (ctx), "ellipse", klazz, id, atts); + return; /* approximate an ellipse using 4 bezier curves */ @@ -878,22 +1043,371 @@ rsvg_start_ellipse (RsvgHandle *ctx, const xmlChar **atts) g_string_free (d, TRUE); } +static void +size_prepared_cb (GdkPixbufLoader *loader, + int width, + int height, + gpointer data) +{ + struct { + int width; + int height; + gboolean keep_aspect_ratio; + } *info = data; + + if (info->keep_aspect_ratio) { + if (width < 0) + width = 500; + if (height < 0) + height = 500; + + if ((double)height * (double)info->width > + (double)width * (double)info->height) { + width = 0.5 + (double)width * (double)info->height / (double)height; + height = info->height; + } else { + height = 0.5 + (double)height * (double)info->width / (double)width; + width = info->width; + } + } else { + width = info->width; + height = info->height; + } + + gdk_pixbuf_loader_set_size (loader, width, height); +} + +static const char s_UTF8_B64Alphabet[64] = { + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, /* A-Z */ + 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, /* a-z */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, /* 0-9 */ + 0x2b, /* + */ + 0x2f /* / */ +}; +static const char utf8_b64_pad = 0x3d; + +static gboolean b64_decode_char (char c, int * b64) +{ + if ((c >= 0x41) && (c <= 0x5a)) + { + *b64 = c - 0x41; + return TRUE; + } + if ((c >= 0x61) && (c <= 0x7a)) + { + *b64 = c - (0x61 - 26); + return TRUE; + } + if ((c >= 0x30) && (c <= 0x39)) + { + *b64 = c + (52 - 0x30); + return TRUE; + } + if (c == 0x2b) + { + *b64 = 62; + return TRUE; + } + if (c == 0x2f) + { + *b64 = 63; + return TRUE; + } + return FALSE; +} + +static gboolean utf8_base64_decode(char ** binptr, size_t * binlen, const char * b64ptr, size_t b64len) +{ + gboolean decoded = TRUE; + gboolean padding = FALSE; + + int i = 0; + glong ucs4_len, j; + + unsigned char byte1 = 0; + unsigned char byte2; + + gunichar ucs4, * ucs4_str; + + if (b64len == 0) + return TRUE; + + if ((binptr == 0) || (b64ptr == 0)) + return FALSE; + + ucs4_str = g_utf8_to_ucs4_fast(b64ptr, b64len, &ucs4_len); + + for(j = 0; j < ucs4_len; j++) + { + ucs4 = ucs4_str[j]; + if ((ucs4 & 0x7f) == ucs4) + { + int b64; + char c = (char)(ucs4); + + if (b64_decode_char (c, &b64)) + { + if (padding || (*binlen == 0)) + { + decoded = FALSE; + break; + } + + switch (i) + { + case 0: + byte1 = (unsigned char)(b64) << 2; + i++; + break; + case 1: + byte2 = (unsigned char)(b64); + byte1 |= byte2 >> 4; + *(*binptr)++ = (char)(byte1); + (*binlen)--; + byte1 = (byte2 & 0x0f) << 4; + i++; + break; + case 2: + byte2 = (unsigned char)(b64); + byte1 |= byte2 >> 2; + *(*binptr)++ = (char)(byte1); + (*binlen)--; + byte1 = (byte2 & 0x03) << 6; + i++; + break; + default: + byte1 |= (unsigned char)(b64); + *(*binptr)++ = (char)(byte1); + (*binlen)--; + i = 0; + break; + } + + if (!decoded) + break; + + continue; + } + else if (c == utf8_b64_pad) + { + switch (i) + { + case 0: + case 1: + decoded = FALSE; + break; + case 2: + if (*binlen == 0) + decoded = FALSE; + else + { + *(*binptr)++ = (char)(byte1); + (*binlen)--; + padding = TRUE; + } + i++; + break; + default: + if (!padding) + { + if (*binlen == 0) + decoded = FALSE; + else + { + *(*binptr)++ = (char)(byte1); + (*binlen)--; + padding = TRUE; + } + } + i = 0; + break; + } + if (!decoded) + break; + + continue; + } + } + if (g_unichar_isspace (ucs4)) + continue; + + decoded = FALSE; + break; + } + return decoded; +} + +static GdkPixbuf * +rsvg_pixbuf_new_from_data_at_size (const char *data, + int width, + int height, + gboolean keep_aspect_ratio, + GError **error) +{ + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + + char * buffer, *bufptr; + size_t buffer_len, buffer_max_len, data_len; + + struct { + gint width; + gint height; + gboolean keep_aspect_ratio; + } info; + + g_return_val_if_fail (data != NULL, NULL); + g_return_val_if_fail (width > 0 && height > 0, NULL); + + while (*data) if (*data++ == ',') break; + + data_len = strlen(data); + + buffer_max_len = ((data_len >> 2) + 1) * 3; + buffer_len = buffer_max_len; + buffer = g_new(char, buffer_max_len); + bufptr = buffer; + + if(!utf8_base64_decode(&bufptr, &buffer_len, data, data_len)) { + g_free(buffer); + return NULL; + } + + buffer_len = buffer_max_len - buffer_len; + + loader = gdk_pixbuf_loader_new (); + + info.width = width; + info.height = height; + info.keep_aspect_ratio = keep_aspect_ratio; + + g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info); + + if (!gdk_pixbuf_loader_write (loader, buffer, buffer_len, error)) { + gdk_pixbuf_loader_close (loader, NULL); + g_object_unref (loader); + return NULL; + } + + if (!gdk_pixbuf_loader_close (loader, error)) { + g_object_unref (loader); + return NULL; + } + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + if (!pixbuf) { + g_object_unref (loader); + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_FAILED, + "Failed to load image: reason not known, probably a corrupt image."); + return NULL; + } + + g_object_ref (pixbuf); + + g_object_unref (loader); + + g_free(buffer); + + return pixbuf; +} + +static GdkPixbuf * +rsvg_pixbuf_new_from_file_at_size (const char *filename, + int width, + int height, + gboolean keep_aspect_ratio, + GError **error) +{ + GdkPixbufLoader *loader; + GdkPixbuf *pixbuf; + + guchar buffer [4096]; + int length; + FILE *f; + struct { + gint width; + gint height; + gboolean keep_aspect_ratio; + } info; + + g_return_val_if_fail (filename != NULL, NULL); + g_return_val_if_fail (width > 0 && height > 0, NULL); + + f = fopen (filename, "rb"); + if (!f) { + g_set_error (error, + G_FILE_ERROR, + g_file_error_from_errno (errno), + "Failed to open file '%s': %s", + filename, g_strerror (errno)); + return NULL; + } + + loader = gdk_pixbuf_loader_new (); + + info.width = width; + info.height = height; + info.keep_aspect_ratio = keep_aspect_ratio; + + g_signal_connect (loader, "size-prepared", G_CALLBACK (size_prepared_cb), &info); + + while (!feof (f)) { + length = fread (buffer, 1, sizeof (buffer), f); + if (length > 0) + if (!gdk_pixbuf_loader_write (loader, buffer, length, error)) { + gdk_pixbuf_loader_close (loader, NULL); + fclose (f); + g_object_unref (loader); + return NULL; + } + } + + fclose (f); + + if (!gdk_pixbuf_loader_close (loader, error)) { + g_object_unref (loader); + return NULL; + } + + pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); + + if (!pixbuf) { + g_object_unref (loader); + g_set_error (error, + GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_FAILED, + "Failed to load image '%s': reason not known, probably a corrupt image file", + filename); + return NULL; + } + + g_object_ref (pixbuf); + + g_object_unref (loader); + + return pixbuf; +} + /* TODO 1: issue with affining alpha images - this is gdkpixbuf's fault... * TODO 2: issue with rotating images - do we want to rotate the whole * canvas 2x to get this right, only to have #1 bite us? */ void -rsvg_start_image (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_image (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; double x = 0., y = 0., w = -1., h = -1.; const char * href = NULL; - const char * klazz = NULL, * id = NULL; - + const char * klazz = NULL, * id = NULL, *value; + gboolean has_alpha; + GdkPixbuf *img; GError *err = NULL; - gboolean has_alpha; guchar *rgb = NULL; int dest_rowstride; double tmp_affine[6]; @@ -904,74 +1418,84 @@ rsvg_start_image (RsvgHandle *ctx, const xmlChar **atts) state = rsvg_state_current (ctx); - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "x")) - x = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - else if (!strcmp ((char *)atts[i], "y")) - y = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - else if (!strcmp ((char *)atts[i], "width")) - w = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - else if (!strcmp ((char *)atts[i], "height")) - h = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - /* path is used by some older adobe illustrator versions */ - else if (!strcmp ((char *)atts[i], "path") || !strcmp((char *)atts[i], "xlink:href")) - href = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "x"))) + x = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "y"))) + y = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "width"))) + w = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "height"))) + h = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + /* path is used by some older adobe illustrator versions */ + if ((value = rsvg_property_bag_lookup (atts, "path")) || (value = rsvg_property_bag_lookup (atts, "xlink:href"))) + href = value; + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, state, "image", klazz, id, atts); } if (!href || x < 0. || y < 0. || w <= 0. || h <= 0.) - return; - - rsvg_parse_style_attrs (ctx, state, "image", klazz, id, atts); + return; /* figure out if image is visible or not */ if (!state->visible) return; - img = gdk_pixbuf_new_from_file (href, &err); + w *= state->affine[0]; + h *= state->affine[3]; + + if(!strncmp(href, "data:", 5)) + img = rsvg_pixbuf_new_from_data_at_size (href, w, h, FALSE, &err); + else + img = rsvg_pixbuf_new_from_file_at_size (href, w, h, FALSE, &err); if (!img) { if (err) { - g_warning ("Couldn't load pixbuf (%s): %s\n", href, err->message); + g_warning ("Couldn't load image: %s\n", err->message); g_error_free (err); } return; } - - /* scale/resize the dest image */ - art_affine_scale (tmp_affine, (double)w / (double)gdk_pixbuf_get_width (img), - (double)h / (double)gdk_pixbuf_get_height (img)); - art_affine_multiply (state->affine, tmp_affine, state->affine); - + has_alpha = gdk_pixbuf_get_has_alpha (img); + if(0 /* has_alpha */) + { + GdkPixbuf *tmp_pixbuf; + + tmp_pixbuf = gdk_pixbuf_add_alpha(img, FALSE, 0, 0, 0); + g_object_unref(img); + img = tmp_pixbuf; + has_alpha = TRUE; + } + dest_rowstride = (int)(w * (has_alpha ? 4 : 3) + 3) & ~3; rgb = g_new (guchar, h * dest_rowstride); + /* we handle scaling above. we handle translation below. we don't handle rotation very well at all */ + tmp_affine[0] = tmp_affine[3] = 1; + tmp_affine[4] = tmp_affine[5] = 0; + tmp_affine[1] = state->affine[1]; + tmp_affine[2] = state->affine[2]; + if(has_alpha) art_rgb_rgba_affine (rgb, 0, 0, w, h, dest_rowstride, gdk_pixbuf_get_pixels (img), - gdk_pixbuf_get_width (img), - gdk_pixbuf_get_height (img), - gdk_pixbuf_get_rowstride (img), - state->affine, + w, h, dest_rowstride, + tmp_affine, ART_FILTER_NEAREST, NULL); else art_rgb_affine (rgb, 0, 0, w, h, dest_rowstride, gdk_pixbuf_get_pixels (img), - gdk_pixbuf_get_width (img), - gdk_pixbuf_get_height (img), - gdk_pixbuf_get_rowstride (img), - state->affine, + w, h, dest_rowstride, + tmp_affine, ART_FILTER_NEAREST, NULL); @@ -983,51 +1507,51 @@ rsvg_start_image (RsvgHandle *ctx, const xmlChar **atts) g_free (rgb); return; } - + + rsvg_push_discrete_layer(ctx); + gdk_pixbuf_copy_area (img, 0, 0, - gdk_pixbuf_get_width (img) * state->affine[0], - gdk_pixbuf_get_height (img) * state->affine[3], + gdk_pixbuf_get_width (img), + gdk_pixbuf_get_height (img), ctx->pixbuf, - state->affine[4] + x, + state->affine[4] + x, /* translate */ state->affine[5] + y); + + rsvg_pop_discrete_layer(ctx); g_object_unref (G_OBJECT (img)); g_free (rgb); } void -rsvg_start_use (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_use (RsvgHandle *ctx, RsvgPropertyBag *atts) { RsvgState *state = rsvg_state_current (ctx); - const char * klazz = NULL, *id = NULL, *xlink_href = NULL; - double x = 0, y = 0, width = 0, height = 0; - int i; - double affine[6]; + const char * klazz = NULL, *id = NULL, *xlink_href = NULL, *value; + double x = 0, y = 0, width = 0, height = 0; gboolean got_width = FALSE, got_height = FALSE; + double affine[6]; - if (atts != NULL) + if (rsvg_property_bag_size(atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "x")) - x = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - else if (!strcmp ((char *)atts[i], "y")) - y = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - else if (!strcmp ((char *)atts[i], "width")) { - width = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - got_width = TRUE; - } - else if (!strcmp ((char *)atts[i], "height")) { - height = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - got_height = TRUE; - } - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "xlink:href")) - xlink_href = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "x"))) + x = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "y"))) + y = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "width"))) { + width = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + got_width = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "height"))) { + height = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + got_height = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + if ((value = rsvg_property_bag_lookup (atts, "xlink:href"))) + xlink_href = value; } /* < 0 is an error, 0 disables rendering. TODO: handle positive values correctly */ @@ -1043,23 +1567,41 @@ rsvg_start_use (RsvgHandle *ctx, const xmlChar **atts) { case RSVG_DEF_PATH: { - RsvgDefsPath *path = (RsvgDefsPath*)parent; - - /* combine state definitions */ - rsvg_state_clone (state, &path->state); - art_affine_translate (affine, x, y); - art_affine_multiply (state->affine, affine, state->affine); - + RsvgDefsDrawable *drawable = (RsvgDefsDrawable*)parent; + RsvgDefsDrawableUse * use; + use = g_new (RsvgDefsDrawableUse, 1); + use->child = drawable; rsvg_parse_style_attrs (ctx, state, "use", klazz, id, atts); - if (state->opacity != 0xff) - rsvg_push_opacity_group (ctx); - - /* always want to render inside of a <use/> */ - rsvg_render_path (ctx, path->d); - break; + rsvg_state_clone (&use->super.state, state); + use->super.super.type = RSVG_DEF_PATH; + use->super.super.free = rsvg_defs_drawable_use_free; + use->super.draw = rsvg_defs_drawable_use_draw; + art_affine_translate(affine, x, y); + art_affine_multiply(use->super.state.affine, affine, use->super.state.affine); + art_affine_multiply(use->super.state.personal_affine, affine, use->super.state.personal_affine); + + if (!ctx->in_defs) + { + rsvg_defs_drawable_draw (&use->super, ctx, 1); + use->super.super.free(&use->super.super); + break; + } + else + { + rsvg_defs_set (ctx->defs, id, &use->super.super); + + use->super.parent = (RsvgDefsDrawable *)ctx->current_defs_group; + if (use->super.parent != NULL) + rsvg_defs_drawable_group_pack((RsvgDefsDrawableGroup *)use->super.parent, + &use->super); + + + break; + } } default: - g_warning ("Unhandled defs entry/type %s %d\n", id, parent->type); + g_warning ("Unhandled defs entry/type %s %d\n", id, + parent->type); return; } } diff --git a/rsvg-shapes.h b/rsvg-shapes.h index 8aa0a1a7..bc02ca12 100644 --- a/rsvg-shapes.h +++ b/rsvg-shapes.h @@ -33,15 +33,32 @@ G_BEGIN_DECLS void rsvg_handle_path (RsvgHandle *ctx, const char * d, const char * id); void rsvg_render_path (RsvgHandle *ctx, const char *d); -void rsvg_start_path (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_polygon (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_polyline (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_line (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_rect (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_circle (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_ellipse (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_image (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_use (RsvgHandle *ctx, const xmlChar **atts); +void rsvg_start_path (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_polygon (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_polyline (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_line (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_rect (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_circle (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_ellipse (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_image (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_use (RsvgHandle *ctx, RsvgPropertyBag *atts); + +void rsvg_push_def_group (RsvgHandle *ctx, const char * id); +void rsvg_pop_def_group (RsvgHandle *ctx); + +typedef struct _RsvgDefsDrawable RsvgDefsDrawable; + +struct _RsvgDefsDrawable { + RsvgDefVal super; + RsvgState state; + RsvgDefsDrawable * parent; + void (*draw) (RsvgDefsDrawable * self, RsvgHandle *ctx, int dominate); +}; + + +RsvgDefsDrawable * rsvg_mask_parse (const RsvgDefs * defs, const char *str); +void rsvg_defs_drawable_draw (RsvgDefsDrawable * self, RsvgHandle *ctx, + int dominate); G_END_DECLS diff --git a/rsvg-styles.c b/rsvg-styles.c index 4c83c2f4..4315b808 100644 --- a/rsvg-styles.c +++ b/rsvg-styles.c @@ -25,9 +25,11 @@ #include <string.h> #include "rsvg.h" +#include "rsvg-private.h" +#include "rsvg-filter.h" #include "rsvg-css.h" #include "rsvg-styles.h" -#include "rsvg-private.h" +#include "rsvg-shapes.h" #include <libart_lgpl/art_rgba.h> #include <libart_lgpl/art_affine.h> @@ -44,7 +46,8 @@ rsvg_state_init (RsvgState *state) memset (state, 0, sizeof (RsvgState)); art_affine_identity (state->affine); - + art_affine_identity (state->personal_affine); + state->mask = NULL; state->opacity = 0xff; state->fill = rsvg_paint_server_parse (NULL, "#000"); state->fill_opacity = 0xff; @@ -55,7 +58,9 @@ rsvg_state_init (RsvgState *state) state->join = ART_PATH_STROKE_JOIN_MITER; state->stop_opacity = 0xff; state->fill_rule = FILL_RULE_NONZERO; - + state->backgroundnew = FALSE; + state->save_pixbuf = NULL; + state->font_family = g_strdup ("Times New Roman"); state->font_size = 12.0; state->font_style = PANGO_STYLE_NORMAL; @@ -65,6 +70,31 @@ rsvg_state_init (RsvgState *state) state->text_dir = PANGO_DIRECTION_LTR; state->text_anchor = TEXT_ANCHOR_START; state->visible = TRUE; + state->filter = NULL; + + state->has_fill_server = FALSE; + state->has_fill_opacity = FALSE; + state->has_fill_rule = FALSE; + state->has_stroke_server = FALSE; + state->has_stroke_opacity = FALSE; + state->has_stroke_width = FALSE; + state->has_miter_limit = FALSE; + state->has_cap = FALSE; + state->has_join = FALSE; + state->has_dash = FALSE; + state->has_visible = FALSE; + state->has_stop_color = FALSE; + state->has_stop_opacity = FALSE; + state->has_font_size = FALSE; + state->has_font_family = FALSE; + state->has_lang = FALSE; + state->has_font_style = FALSE; + state->has_font_variant = FALSE; + state->has_font_weight = FALSE; + state->has_font_stretch = FALSE; + state->has_font_decor = FALSE; + state->has_text_dir = FALSE; + state->has_text_anchor = FALSE; } void @@ -78,7 +108,6 @@ rsvg_state_clone (RsvgState *dst, const RsvgState *src) rsvg_paint_server_ref (dst->fill); rsvg_paint_server_ref (dst->stroke); dst->save_pixbuf = NULL; - dst->opacity = 0xff; if (src->dash.n_dash > 0) { @@ -89,6 +118,149 @@ rsvg_state_clone (RsvgState *dst, const RsvgState *src) } void +rsvg_state_reinherit (RsvgState *dst, const RsvgState *src) +{ + gint i; + + if (!dst->has_fill_server) + { + rsvg_paint_server_unref (dst->fill); + dst->fill = src->fill; + rsvg_paint_server_ref (dst->fill); + } + if (!dst->has_fill_opacity) + dst->fill_opacity = src->fill_opacity; + if (!dst->has_fill_rule) + dst->fill_rule = src->fill_rule; + if (!dst->has_stroke_server) + { + rsvg_paint_server_unref (dst->stroke); + dst->stroke = src->stroke; + rsvg_paint_server_ref (dst->stroke); + } + if (!dst->has_stroke_opacity) + dst->stroke_opacity = src->stroke_opacity; + if (!dst->has_stroke_width) + dst->stroke_width = src->stroke_width; + if (!dst->has_miter_limit) + dst->miter_limit = src->miter_limit; + if (!dst->has_cap) + dst->cap = src->cap; + if (!dst->has_join) + dst->join = src->join; + if (!dst->has_stop_color) + dst->stop_color = src->stop_color; + if (!dst->has_stop_opacity) + dst->stop_opacity = src->stop_opacity; + if (!dst->has_visible) + dst->visible = src->visible; + if (!dst->has_font_size) + dst->font_size = src->font_size; + if (!dst->has_font_style) + dst->font_style = src->font_style; + if (!dst->has_font_variant) + dst->font_variant = src->font_variant; + if (!dst->has_font_weight) + dst->font_weight = src->font_weight; + if (!dst->has_font_stretch) + dst->font_stretch = src->font_stretch; + if (!dst->has_font_decor) + dst->font_decor = src->font_decor; + if (!dst->has_text_dir) + dst->text_dir = src->text_dir; + if (!dst->has_text_anchor) + dst->text_anchor = src->text_anchor; + + if (!dst->has_font_family) + dst->font_family = g_strdup (src->font_family); + if (!dst->has_lang) + dst->lang = g_strdup (src->lang); + + if (src->dash.n_dash > 0 && !dst->has_dash) + { + dst->dash.dash = g_new (gdouble, src->dash.n_dash); + for (i = 0; i < src->dash.n_dash; i++) + dst->dash.dash[i] = src->dash.dash[i]; + } + art_affine_multiply (dst->affine, dst->personal_affine, src->affine); +} + +void +rsvg_state_dominate (RsvgState *dst, const RsvgState *src) +{ + gint i; + + if (!dst->has_fill_server || src->has_fill_server) + { + rsvg_paint_server_unref (dst->fill); + dst->fill = src->fill; + rsvg_paint_server_ref (dst->fill); + } + if (!dst->has_fill_opacity || src->has_fill_opacity) + dst->fill_opacity = src->fill_opacity; + if (!dst->has_fill_rule || src->has_fill_rule) + dst->fill_rule = src->fill_rule; + if (!dst->has_stroke_server || src->has_stroke_server) + { + rsvg_paint_server_unref (dst->stroke); + dst->stroke = src->stroke; + rsvg_paint_server_ref (dst->stroke); + } + if (!dst->has_stroke_opacity || src->has_stroke_opacity) + dst->stroke_opacity = src->stroke_opacity; + if (!dst->has_stroke_width || src->has_stroke_width) + dst->stroke_width = src->stroke_width; + if (!dst->has_miter_limit || src->has_miter_limit) + dst->miter_limit = src->miter_limit; + if (!dst->has_cap || src->has_cap) + dst->cap = src->cap; + if (!dst->has_join || src->has_join) + dst->join = src->join; + if (!dst->has_stop_color || src->has_stop_color) + dst->stop_color = src->stop_color; + if (!dst->has_stop_opacity || src->has_stop_opacity) + dst->stop_opacity = src->stop_opacity; + if (!dst->has_visible || src->has_visible) + dst->visible = src->visible; + if (!dst->has_font_size || src->has_font_size) + dst->font_size = src->font_size; + if (!dst->has_font_style || src->has_font_style) + dst->font_style = src->font_style; + if (!dst->has_font_variant || src->has_font_variant) + dst->font_variant = src->font_variant; + if (!dst->has_font_weight || src->has_font_weight) + dst->font_weight = src->font_weight; + if (!dst->has_font_stretch || src->has_font_stretch) + dst->font_stretch = src->font_stretch; + if (!dst->has_font_decor || src->has_font_decor) + dst->font_decor = src->font_decor; + if (!dst->has_text_dir || src->has_text_dir) + dst->text_dir = src->text_dir; + if (!dst->has_text_anchor || src->has_text_anchor) + dst->text_anchor = src->text_anchor; + + if (!dst->has_font_family || src->has_font_family) + dst->font_family = g_strdup (src->font_family); + if (!dst->has_lang || src->has_lang) + dst->lang = g_strdup (src->lang); + + if (src->dash.n_dash > 0 && (!dst->has_dash || src->has_dash)) + { + dst->dash.dash = g_new (gdouble, src->dash.n_dash); + for (i = 0; i < src->dash.n_dash; i++) + dst->dash.dash[i] = src->dash.dash[i]; + } + art_affine_multiply (dst->affine, dst->personal_affine, src->affine); +} + +void +rsvg_state_inherit (RsvgState *dst, const RsvgState *src) +{ + rsvg_state_init(dst); + rsvg_state_reinherit(dst,src); +} + +void rsvg_state_finalize (RsvgState *state) { g_free (state->font_family); @@ -112,51 +284,73 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) { state->opacity = rsvg_css_parse_opacity (str + arg_off); } + else if (rsvg_css_param_match (str, "filter")) + state->filter = rsvg_filter_parse(ctx->defs, str + arg_off); + else if (rsvg_css_param_match (str, "mask")) + state->mask = rsvg_mask_parse(ctx->defs, str + arg_off); + else if (rsvg_css_param_match (str, "enable-background")) + { + if (!strcmp (str + arg_off, "new")) + state->backgroundnew = TRUE; + else + state->backgroundnew = FALSE; + } else if (rsvg_css_param_match (str, "display")) { + state->has_visible = TRUE; if (!strcmp (str + arg_off, "none")) state->visible = FALSE; else if (strcmp (str + arg_off, "inherit") != 0) state->visible = TRUE; - /* else inherit */ + else + state->has_visible = FALSE; } else if (rsvg_css_param_match (str, "visibility")) { + state->has_visible = TRUE; if (!strcmp (str + arg_off, "visable")) state->visible = TRUE; else if (strcmp (str + arg_off, "inherit") != 0) state->visible = FALSE; /* collapse or hidden */ - /* else inherit */ + else + state->has_visible = FALSE; } else if (rsvg_css_param_match (str, "fill")) { rsvg_paint_server_unref (state->fill); state->fill = rsvg_paint_server_parse (ctx->defs, str + arg_off); + state->has_fill_server = TRUE; } else if (rsvg_css_param_match (str, "fill-opacity")) { state->fill_opacity = rsvg_css_parse_opacity (str + arg_off); + state->has_fill_opacity = TRUE; } else if (rsvg_css_param_match (str, "fill-rule")) { + state->has_fill_rule = TRUE; if (!strcmp (str + arg_off, "nonzero")) state->fill_rule = FILL_RULE_NONZERO; else if (!strcmp (str + arg_off, "evenodd")) state->fill_rule = FILL_RULE_EVENODD; - /*else inherit*/ + else + state->has_fill_rule = FALSE; } else if (rsvg_css_param_match (str, "stroke")) { rsvg_paint_server_unref (state->stroke); state->stroke = rsvg_paint_server_parse (ctx->defs, str + arg_off); + state->has_stroke_server = TRUE; } else if (rsvg_css_param_match (str, "stroke-width")) { state->stroke_width = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi, (gdouble)ctx->height, state->font_size); + state->has_stroke_width = TRUE; } else if (rsvg_css_param_match (str, "stroke-linecap")) { + state->has_cap = TRUE; if (!strcmp (str + arg_off, "butt")) state->cap = ART_PATH_STROKE_CAP_BUTT; else if (!strcmp (str + arg_off, "round")) @@ -169,9 +363,11 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) else if (rsvg_css_param_match (str, "stroke-opacity")) { state->stroke_opacity = rsvg_css_parse_opacity (str + arg_off); + state->has_stroke_opacity = TRUE; } else if (rsvg_css_param_match (str, "stroke-linejoin")) { + state->has_join = TRUE; if (!strcmp (str + arg_off, "miter")) state->join = ART_PATH_STROKE_JOIN_MITER; else if (!strcmp (str + arg_off, "round")) @@ -185,39 +381,49 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) { state->font_size = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi, (gdouble)ctx->height, state->font_size); + state->has_font_size = TRUE; } else if (rsvg_css_param_match (str, "font-family")) { char * save = g_strdup (rsvg_css_parse_font_family (str + arg_off, parent_state->font_family)); g_free (state->font_family); state->font_family = save; + state->has_font_family = TRUE; } else if (rsvg_css_param_match (str, "xml:lang")) { char * save = g_strdup (str + arg_off); g_free (state->lang); state->lang = save; + state->has_lang = TRUE; } else if (rsvg_css_param_match (str, "font-style")) { state->font_style = rsvg_css_parse_font_style (str + arg_off, parent_state->font_style); + state->has_font_style = TRUE; } else if (rsvg_css_param_match (str, "font-variant")) { state->font_variant = rsvg_css_parse_font_variant (str + arg_off, parent_state->font_variant); + state->has_font_variant = TRUE; } else if (rsvg_css_param_match (str, "font-weight")) { state->font_weight = rsvg_css_parse_font_weight (str + arg_off, parent_state->font_weight); + state->has_font_weight = TRUE; } else if (rsvg_css_param_match (str, "font-stretch")) { state->font_stretch = rsvg_css_parse_font_stretch (str + arg_off, parent_state->font_stretch); + state->has_font_stretch = TRUE; } else if (rsvg_css_param_match (str, "text-decoration")) { if (!strcmp (str + arg_off, "inherit")) - state->font_decor = parent_state->font_decor; + { + state->has_font_decor = FALSE; + state->font_decor = parent_state->font_decor; + } else { if (strstr (str + arg_off, "underline")) @@ -226,13 +432,18 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) state->font_decor |= TEXT_OVERLINE; if (strstr (str + arg_off, "strike") || strstr (str + arg_off, "line-through")) /* strike though or line-through */ state->font_decor |= TEXT_STRIKE; + state->has_font_decor = TRUE; } } else if (rsvg_css_param_match (str, "writing-mode")) { + state->has_text_dir = TRUE; /* lr-tb | rl-tb | tb-rl | lr | rl | tb | inherit */ if (!strcmp (str + arg_off, "inherit")) - state->text_dir = parent_state->text_dir; + { + state->text_dir = parent_state->text_dir; + state->has_text_dir = FALSE; + } else if (!strcmp (str + arg_off, "rl")) state->text_dir = PANGO_DIRECTION_RTL; else if (!strcmp (str + arg_off, "tb-rl") || @@ -242,11 +453,16 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) state->text_dir = PANGO_DIRECTION_TTB_LTR; else state->text_dir = PANGO_DIRECTION_LTR; + } else if (rsvg_css_param_match (str, "text-anchor")) { + state->has_text_anchor = TRUE; if (!strcmp (str + arg_off, "inherit")) - state->text_anchor = parent_state->text_anchor; + { + state->text_anchor = parent_state->text_anchor; + state->has_text_anchor = FALSE; + } else { if (strstr (str + arg_off, "start")) @@ -259,18 +475,22 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) } else if (rsvg_css_param_match (str, "stop-color")) { + state->has_stop_color = TRUE; state->stop_color = rsvg_css_parse_color (str + arg_off); } else if (rsvg_css_param_match (str, "stop-opacity")) { + state->has_stop_opacity = TRUE; state->stop_opacity = rsvg_css_parse_opacity (str + arg_off); } else if (rsvg_css_param_match (str, "stroke-miterlimit")) { + state->has_miter_limit = TRUE; state->miter_limit = g_ascii_strtod (str + arg_off, NULL); } else if (rsvg_css_param_match (str, "stroke-dashoffset")) { + state->has_dash = TRUE; state->dash.offset = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi, rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), state->font_size); if (state->dash.offset < 0.) @@ -278,6 +498,7 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) } else if (rsvg_css_param_match (str, "stroke-dasharray")) { + state->has_dash = TRUE; if(!strcmp(str + arg_off, "none")) { if (state->dash.n_dash != 0) @@ -320,59 +541,57 @@ rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str) } } -/* tell whether @str is a supported style argument - whenever something gets added to parse_arg, please - remember to add it here too -*/ -gboolean -rsvg_is_style_arg(const char *str) +void rsvg_parse_style_pair (RsvgHandle *ctx, RsvgState *state, + const char *key, const char *val) { - static GHashTable *styles = NULL; - if (!styles) - { - styles = g_hash_table_new (g_str_hash, g_str_equal); - - g_hash_table_insert (styles, "display", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "fill", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "fill-opacity", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "fill-rule", GINT_TO_POINTER -(TRUE)); - g_hash_table_insert (styles, "font-family", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "font-size", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "font-stretch", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "font-style", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "font-variant", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "font-weight", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "opacity", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stop-color", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stop-opacity", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stroke", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stroke-dasharray", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stroke-dashoffset", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stroke-linecap", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stroke-linejoin", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stroke-miterlimit", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stroke-opacity", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "stroke-width", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "text-anchor", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "text-decoration", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "visibility", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "writing-mode", GINT_TO_POINTER (TRUE)); - g_hash_table_insert (styles, "xml:lang", GINT_TO_POINTER (TRUE)); - } - - /* this will default to 0 (FALSE) on a failed lookup */ - return GPOINTER_TO_INT (g_hash_table_lookup (styles, str)); + gchar * str = g_strdup_printf ("%s:%s", key, val); + rsvg_parse_style_arg (ctx, state, str); + g_free (str); +} + +static void rsvg_lookup_parse_style_pair(RsvgHandle *ctx, RsvgState *state, + const char *key, RsvgPropertyBag *atts) +{ + const char * value; + + if((value = rsvg_property_bag_lookup (atts, key)) != NULL) + rsvg_parse_style_pair (ctx, state, key, value); } /* take a pair of the form (fill="#ff00ff") and parse it as a style */ void -rsvg_parse_style_pair (RsvgHandle *ctx, RsvgState *state, - const char *key, const char *val) +rsvg_parse_style_pairs (RsvgHandle *ctx, RsvgState *state, + RsvgPropertyBag *atts) { - gchar * str = g_strdup_printf ("%s:%s", key, val); - rsvg_parse_style_arg (ctx, state, str); - g_free (str); + rsvg_lookup_parse_style_pair (ctx, state, "display", atts); + rsvg_lookup_parse_style_pair (ctx, state, "enable-background", atts); + rsvg_lookup_parse_style_pair (ctx, state, "fill", atts); + rsvg_lookup_parse_style_pair (ctx, state, "fill-opacity", atts); + rsvg_lookup_parse_style_pair (ctx, state, "fill-rule", atts); + rsvg_lookup_parse_style_pair (ctx, state, "font-family", atts); + rsvg_lookup_parse_style_pair (ctx, state, "font-size", atts); + rsvg_lookup_parse_style_pair (ctx, state, "font-stretch", atts); + rsvg_lookup_parse_style_pair (ctx, state, "font-style", atts); + rsvg_lookup_parse_style_pair (ctx, state, "font-variant", atts); + rsvg_lookup_parse_style_pair (ctx, state, "font-weight", atts); + rsvg_lookup_parse_style_pair (ctx, state, "opacity", atts); + rsvg_lookup_parse_style_pair (ctx, state, "mask", atts); + rsvg_lookup_parse_style_pair (ctx, state, "filter", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stop-color", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stop-opacity", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stroke", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stroke-dasharray", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stroke-dashoffset", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stroke-linecap", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stroke-linejoin", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stroke-miterlimit", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stroke-opacity", atts); + rsvg_lookup_parse_style_pair (ctx, state, "stroke-width", atts); + rsvg_lookup_parse_style_pair (ctx, state, "text-anchor", atts); + rsvg_lookup_parse_style_pair (ctx, state, "text-decoration", atts); + rsvg_lookup_parse_style_pair (ctx, state, "visibility", atts); + rsvg_lookup_parse_style_pair (ctx, state, "writing-mode", atts); + rsvg_lookup_parse_style_pair (ctx, state, "xml:lang", atts); } /* Split a CSS2 style into individual style arguments, setting attributes @@ -776,7 +995,10 @@ rsvg_parse_transform_attr (RsvgHandle *ctx, RsvgState *state, const char *str) double affine[6]; if (rsvg_parse_transform (affine, str)) - art_affine_multiply (state->affine, affine, state->affine); + { + art_affine_multiply (state->personal_affine, affine, state->personal_affine); + art_affine_multiply (state->affine, affine, state->affine); + } } static gboolean @@ -810,7 +1032,7 @@ rsvg_parse_style_attrs (RsvgHandle * ctx, const char * tag, const char * klazz, const char * id, - const xmlChar **atts) + RsvgPropertyBag *atts) { int i = 0, j = 0; char * target = NULL; @@ -889,17 +1111,16 @@ rsvg_parse_style_attrs (RsvgHandle * ctx, } } - if (atts != NULL) + if (rsvg_property_bag_size(atts) > 0) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "style")) - rsvg_parse_style (ctx, state, (char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "transform")) - rsvg_parse_transform_attr (ctx, state, (char *)atts[i + 1]); - else if (rsvg_is_style_arg ((char *)atts[i])) - rsvg_parse_style_pair (ctx, state, (char *)atts[i], (char *)atts[i + 1]); - } + const char * value; + + if ((value = rsvg_property_bag_lookup (atts, "style")) != NULL) + rsvg_parse_style (ctx, state, value); + if ((value = rsvg_property_bag_lookup (atts, "transform")) != NULL) + rsvg_parse_transform_attr (ctx, state, value); + + rsvg_parse_style_pairs (ctx, state, atts); } } @@ -918,16 +1139,20 @@ rsvg_pixmap_destroy (guchar *pixels, gpointer data) * stack. **/ void -rsvg_push_opacity_group (RsvgHandle *ctx) +rsvg_push_discrete_layer (RsvgHandle *ctx) { RsvgState *state; GdkPixbuf *pixbuf; art_u8 *pixels; int width, height, rowstride; - + state = &ctx->state[ctx->n_state - 1]; pixbuf = ctx->pixbuf; + if (state->filter == NULL && state->opacity == 0xFF && + !state->backgroundnew && state->mask == NULL) + return; + state->save_pixbuf = pixbuf; if (pixbuf == NULL) @@ -960,19 +1185,10 @@ rsvg_push_opacity_group (RsvgHandle *ctx) ctx->pixbuf = pixbuf; } -/** - * rsvg_pop_opacity_group: End a transparency group. - * @ctx: Context in which to push. - * @opacity: Opacity for blending (0..255). - * - * Pops a new transparency group from the stack, recompositing with the - * next on stack. - **/ -void -rsvg_pop_opacity_group (RsvgHandle *ctx, int opacity) +static void +rsvg_use_opacity (RsvgHandle *ctx, int opacity, + GdkPixbuf *tos, GdkPixbuf *nos) { - RsvgState *state = &ctx->state[ctx->n_state - 1]; - GdkPixbuf *tos, *nos; art_u8 *tos_pixels, *nos_pixels; int width; int height; @@ -980,8 +1196,6 @@ rsvg_pop_opacity_group (RsvgHandle *ctx, int opacity) int x, y; int tmp; - tos = ctx->pixbuf; - nos = state->save_pixbuf; if (tos == NULL || nos == NULL) { @@ -1021,6 +1235,279 @@ rsvg_pop_opacity_group (RsvgHandle *ctx, int opacity) tos_pixels += rowstride; nos_pixels += rowstride; } +} + +static void +rsvg_use_mask (RsvgHandle *ctx, RsvgDefsDrawable * maskref, + GdkPixbuf *tos, GdkPixbuf *nos) +{ + art_u8 *tos_pixels, *nos_pixels, *mask_pixels; + int width; + int height; + int rowstride; + int x, y; + + GdkPixbuf *save, *mask; + RsvgDefsDrawable *drawable; + + drawable = (RsvgDefsDrawable*)maskref; + + mask = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 1, 8, + gdk_pixbuf_get_width(tos), + gdk_pixbuf_get_height(tos)); + + save = ctx->pixbuf; + + if (ctx->n_state == ctx->n_state_max) + ctx->state = g_renew (RsvgState, ctx->state, + ctx->n_state_max <<= 1); + if (ctx->n_state) + rsvg_state_inherit (&ctx->state[ctx->n_state], + &ctx->state[ctx->n_state - 1]); + else + rsvg_state_init (ctx->state); + ctx->n_state++; + + ctx->pixbuf = mask; + + rsvg_defs_drawable_draw (drawable, ctx, 0); + + /* pop the state stack */ + ctx->n_state--; + rsvg_state_finalize (&ctx->state[ctx->n_state]); + + ctx->pixbuf = save; + + if (tos == NULL || nos == NULL) + { + /* FIXME: What warning/GError here? */ + return; + } + + if (!gdk_pixbuf_get_has_alpha (nos)) + { + g_warning ("push/pop transparency group on non-alpha buffer nyi"); + return; + } + + width = gdk_pixbuf_get_width (tos); + height = gdk_pixbuf_get_height (tos); + rowstride = gdk_pixbuf_get_rowstride (tos); + + tos_pixels = gdk_pixbuf_get_pixels (tos); + nos_pixels = gdk_pixbuf_get_pixels (nos); + mask_pixels = gdk_pixbuf_get_pixels (mask); + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + guchar r, g, b, a, rm, gm, bm, am; + gdouble luminance; + a = tos_pixels[4 * x + 3]; + if (a) + { + r = tos_pixels[4 * x]; + g = tos_pixels[4 * x + 1]; + b = tos_pixels[4 * x + 2]; + + rm = mask_pixels[4 * x]; + gm = mask_pixels[4 * x + 1]; + bm = mask_pixels[4 * x + 2]; + am = mask_pixels[4 * x + 3]; + + luminance = ((gdouble)rm * 0.2125 + + (gdouble)gm * 0.7154 + + (gdouble)bm * 0.0721) / 255.; + a = (guchar)((gdouble)a * luminance + * (gdouble)am / 255.); + art_rgba_run_alpha (nos_pixels + 4 * x, r, g, b, a, 1); + } + } + tos_pixels += rowstride; + nos_pixels += rowstride; + mask_pixels += rowstride; + } +} + +RsvgDefsDrawable * +rsvg_mask_parse (const RsvgDefs * defs, const char *str) +{ + if (!strncmp (str, "url(", 4)) + { + const char *p = str + 4; + int ix; + char *name; + RsvgDefVal *val; + + while (g_ascii_isspace (*p)) + p++; + + if (*p == '#') + { + p++; + for (ix = 0; p[ix]; ix++) + if (p[ix] == ')') + break; + + if (p[ix] == ')') + { + name = g_strndup (p, ix); + val = rsvg_defs_lookup (defs, name); + g_free (name); + + if (val && val->type == RSVG_DEF_PATH) + { + return (RsvgDefsDrawable *) val; + } + } + } + } + return NULL; +} + +static GdkPixbuf * +get_next_out(gint * operationsleft, GdkPixbuf * in, GdkPixbuf * tos, + GdkPixbuf * nos, GdkPixbuf *intermediate) +{ + GdkPixbuf * out; + + if (*operationsleft == 1) + out = nos; + else + { + if (in == tos) + out = intermediate; + else + out = tos; + gdk_pixbuf_fill(out, 0x00000000); + } + (*operationsleft)--; + + return out; +} + +static GdkPixbuf * +rsvg_compile_bg(RsvgHandle *ctx, RsvgState *topstate) +{ + int i, foundstate; + GdkPixbuf *intermediate, *lastintermediate; + RsvgState *state, *lastvalid; + lastvalid = NULL; + foundstate = 0; + + lastintermediate = gdk_pixbuf_copy(topstate->save_pixbuf); + + for (i = ctx->n_state - 1; i >= 0; i--) + { + state = &ctx->state[i]; + if (state == topstate) + { + foundstate = 1; + } + else if (!foundstate) + continue; + if (state->backgroundnew) + break; + if (state->save_pixbuf) + { + if (lastvalid) + { + intermediate = gdk_pixbuf_copy(state->save_pixbuf); + rsvg_use_opacity(ctx, 0xFF, lastintermediate, intermediate); + g_object_unref(lastintermediate); + lastintermediate = intermediate; + } + lastvalid = state; + } + } + return lastintermediate; +} + +static void +rsvg_composite_layer(RsvgHandle *ctx, RsvgState *state, GdkPixbuf *tos, GdkPixbuf *nos) +{ + RsvgFilter *filter = state->filter; + int opacity = state->opacity; + RsvgDefsDrawable * mask = state->mask; + GdkPixbuf *intermediate; + GdkPixbuf *in, *out, *insidebg; + int operationsleft; + + intermediate = NULL; + + if (state->filter == NULL && state->opacity == 0xFF && + !state->backgroundnew && state->mask == NULL) + return; + + operationsleft = 0; + + if (opacity != 0xFF) + operationsleft++; + if (filter != NULL) + operationsleft++; + if (mask != NULL) + operationsleft++; + + if (operationsleft > 1) + intermediate = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 1, 8, + gdk_pixbuf_get_width (tos), + gdk_pixbuf_get_height (tos)); + + in = tos; + + if (operationsleft == 0) + { + rsvg_use_opacity (ctx, 0xFF, tos, nos); + } + + if (filter != NULL) + { + out = get_next_out(&operationsleft, in, tos, nos, intermediate); + insidebg = rsvg_compile_bg(ctx, state); + rsvg_filter_render (filter, in, out, insidebg, ctx); + g_object_unref (insidebg); + in = out; + } + if (opacity != 0xFF) + { + out = get_next_out(&operationsleft, in, tos, nos, intermediate); + rsvg_use_opacity (ctx, opacity, in, out); + in = out; + } + if (mask != NULL) + { + out = get_next_out(&operationsleft, in, tos, nos, intermediate); + rsvg_use_mask (ctx, mask, in, out); + in = out; + } + + if (intermediate != NULL) + g_object_unref (intermediate); + +} + +/** + * rsvg_pop_discrete_layer: End a transparency group. + * @ctx: Context in which to push. + * + * Pops a new transparency group from the stack, recompositing with the + * next on stack using a filter, transperency value, or a mask to do so + **/ + +void +rsvg_pop_discrete_layer(RsvgHandle *ctx) +{ + GdkPixbuf *tos, *nos; + RsvgState *state; + + state = rsvg_state_current(ctx); + + tos = ctx->pixbuf; + nos = state->save_pixbuf; + + if (nos != NULL) + rsvg_composite_layer(ctx, state, tos, nos); g_object_unref (tos); ctx->pixbuf = nos; @@ -1033,3 +1520,49 @@ rsvg_state_current (RsvgHandle *ctx) return &ctx->state[ctx->n_state - 1]; return NULL; } + +double +rsvg_state_current_font_size (RsvgHandle *ctx) +{ + if (ctx->n_state > 0) + return ctx->state[ctx->n_state - 1].font_size; + else + return 12.0; +} + +RsvgPropertyBag * +rsvg_property_bag_new (const xmlChar **atts) +{ + RsvgPropertyBag * bag; + int i; + + bag = g_new (RsvgPropertyBag, 1); + bag->props = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); + + if (atts != NULL) + { + for (i = 0; atts[i] != NULL; i += 2) + g_hash_table_insert (bag->props, (gpointer)atts[i], (gpointer)atts[i+1]); + } + + return bag; +} + +void +rsvg_property_bag_free (RsvgPropertyBag *bag) +{ + g_hash_table_destroy (bag->props); + g_free (bag); +} + +const char * +rsvg_property_bag_lookup (RsvgPropertyBag *bag, const char * key) +{ + return (const char *)g_hash_table_lookup (bag->props, (gconstpointer)key); +} + +guint +rsvg_property_bag_size (RsvgPropertyBag *bag) +{ + return g_hash_table_size (bag->props); +} diff --git a/rsvg-styles.h b/rsvg-styles.h index 6c4755ab..03a0666e 100644 --- a/rsvg-styles.h +++ b/rsvg-styles.h @@ -56,52 +56,83 @@ enum { FILL_RULE_NONZERO = 1 }; -typedef struct { +struct _RsvgState { double affine[6]; + double personal_affine[6]; gint opacity; /* 0..255 */ RsvgPaintServer *fill; + gboolean has_fill_server; gint fill_opacity; /* 0..255 */ - + gboolean has_fill_opacity; gint fill_rule; + gboolean has_fill_rule; + + RsvgFilter *filter; + void *mask; + gboolean backgroundnew; RsvgPaintServer *stroke; + gboolean has_stroke_server; gint stroke_opacity; /* 0..255 */ + gboolean has_stroke_opacity; double stroke_width; + gboolean has_stroke_width; double miter_limit; + gboolean has_miter_limit; ArtPathStrokeCapType cap; + gboolean has_cap; ArtPathStrokeJoinType join; + gboolean has_join; double font_size; + gboolean has_font_size; char * font_family; + gboolean has_font_family; char * lang; + gboolean has_lang; PangoStyle font_style; + gboolean has_font_style; PangoVariant font_variant; + gboolean has_font_variant; PangoWeight font_weight; + gboolean has_font_weight; PangoStretch font_stretch; + gboolean has_font_stretch; TextDecoration font_decor; + gboolean has_font_decor; PangoDirection text_dir; - TextAnchor text_anchor; + gboolean has_text_dir; + TextAnchor text_anchor; + gboolean has_text_anchor; guint text_offset; guint32 stop_color; /* rgb */ + gboolean has_stop_color; gint stop_opacity; /* 0..255 */ + gboolean has_stop_opacity; gboolean visible; + gboolean has_visible; ArtVpathDash dash; + gboolean has_dash; GdkPixbuf *save_pixbuf; -} RsvgState; +}; void rsvg_state_init (RsvgState *state); void rsvg_state_clone (RsvgState *dst, const RsvgState *src); +void rsvg_state_inherit (RsvgState *dst, const RsvgState *src); +void rsvg_state_reinherit (RsvgState *dst, const RsvgState *src); +void rsvg_state_dominate (RsvgState *dst, const RsvgState *src); void rsvg_state_finalize (RsvgState *state); -gboolean rsvg_is_style_arg(const char *str); +void rsvg_parse_style_pairs (RsvgHandle *ctx, RsvgState *state, + RsvgPropertyBag *atts); void rsvg_parse_style_pair (RsvgHandle *ctx, RsvgState *state, const char *key, const char *val); void rsvg_parse_style (RsvgHandle *ctx, RsvgState *state, const char *str); @@ -109,14 +140,16 @@ void rsvg_parse_cssbuffer (RsvgHandle *ctx, const char * buff, size_t buflen); void rsvg_parse_style_attrs (RsvgHandle *ctx, RsvgState *state, const char * tag, const char * klazz, const char * id, - const xmlChar **atts); + RsvgPropertyBag *atts); gdouble rsvg_viewport_percentage (gdouble width, gdouble height); -void rsvg_pop_opacity_group (RsvgHandle *ctx, int opacity); -void rsvg_push_opacity_group (RsvgHandle *ctx); +void +rsvg_pop_discrete_layer(RsvgHandle *ctx); +void rsvg_push_discrete_layer (RsvgHandle *ctx); gboolean rsvg_parse_transform (double dst[6], const char *src); RsvgState * rsvg_state_current (RsvgHandle *ctx); +double rsvg_state_current_font_size (RsvgHandle *ctx); G_END_DECLS diff --git a/rsvg-text-vectors.c b/rsvg-text-vectors.c index 9992e4b3..6c44335a 100644 --- a/rsvg-text-vectors.c +++ b/rsvg-text-vectors.c @@ -23,6 +23,7 @@ #include <string.h> #include "rsvg-private.h" +#include "rsvg-styles.h" #include "rsvg-text.h" #include "rsvg-shapes.h" #include "rsvg-css.h" @@ -36,6 +37,8 @@ #include FT_GLYPH_H #include FT_OUTLINE_H +#define RSVG_TEXT_DEBUG + typedef struct _RsvgTextLayout RsvgTextLayout; struct _RsvgTextLayout @@ -202,76 +205,24 @@ rsvg_text_layout_render_flags (RsvgTextLayout *layout) } static void -rsvg_text_layout_render_glyphs (RsvgTextLayout *layout, - PangoFont *font, - PangoGlyphString *glyphs, - RsvgTextRenderFunc render_func, - gint x, - gint y, - gpointer render_data) -{ - PangoGlyphInfo *gi; - FT_Int32 flags; - FT_Vector pos; - gint i; - gint x_position = 0; - - flags = rsvg_text_layout_render_flags (layout); - - for (i = 0, gi = glyphs->glyphs; i < glyphs->num_glyphs; i++, gi++) - { - if (gi->glyph) - { - pos.x = x + x_position + gi->geometry.x_offset; - pos.y = y + gi->geometry.y_offset; - - /* FT_Vector_Transform (&pos, &trafo); */ - - render_func (font, gi->glyph, flags, NULL /* &trafo */, - pos.x, pos.y, - render_data); - } - - x_position += glyphs->glyphs[i].geometry.width; - } -} - -static void -rsvg_text_layout_render_line (RsvgTextLayout *layout, - PangoLayoutLine *line, - RsvgTextRenderFunc render_func, - gint x, - gint y, - gpointer render_data) -{ - PangoRectangle rect; - GSList *list; - gint x_off = 0; - - for (list = line->runs; list; list = list->next) - { - PangoLayoutRun *run = list->data; - - pango_glyph_string_extents (run->glyphs, run->item->analysis.font, - NULL, &rect); - rsvg_text_layout_render_glyphs (layout, - run->item->analysis.font, run->glyphs, - render_func, - x + x_off, y, - render_data); - - x_off += rect.width; - } -} - -static void rsvg_text_vector_coords (RenderCtx *ctx, const FT_Vector *vector, gdouble *x, gdouble *y) { - *x = (gdouble)vector->x; - *y = (gdouble)vector->y; + *x = ctx->offset_x + (gdouble)vector->x / 64.; + *y = ctx->offset_y - (gdouble)vector->y / 64.; +} + +static void +print266 (FT_Vector *pnt, + gchar *msg) +{ +#ifdef RSVG_TEXT_DEBUG + g_print ("%s Point (%d,%d)\n", + msg, (gint)pnt->x, + (gint)pnt->y); +#endif } static gint @@ -284,6 +235,8 @@ moveto (FT_Vector *to, ctx = (RenderCtx *)data; + print266(to, "Moveto"); + if (ctx->wrote) g_string_append(ctx->path, "Z "); else @@ -310,6 +263,11 @@ lineto (FT_Vector *to, ctx = (RenderCtx *)data; + if (!ctx->wrote) + return 0; + + print266(to, "Lineto"); + g_string_append_c(ctx->path, 'L'); rsvg_text_vector_coords(ctx, to, &x, &y); @@ -332,6 +290,12 @@ conicto (FT_Vector *ftcontrol, ctx = (RenderCtx *)data; + if (!ctx->wrote) + return 0; + + print266(ftcontrol, "Conicto Control"); + print266(to, "Conicto"); + g_string_append_c(ctx->path, 'Q'); rsvg_text_vector_coords(ctx, ftcontrol, &x, &y); @@ -361,6 +325,13 @@ cubicto (FT_Vector *ftcontrol1, ctx = (RenderCtx *)data; + if (!ctx->wrote) + return 0; + + print266(ftcontrol1, "Cubicto Control1"); + print266(ftcontrol2, "Cubicto Control2"); + print266(to, "Cubicto"); + g_string_append_c(ctx->path, 'C'); rsvg_text_vector_coords(ctx, ftcontrol1, &x, &y); @@ -385,6 +356,66 @@ cubicto (FT_Vector *ftcontrol1, } static void +rsvg_text_layout_render_trafo (RsvgTextLayout *layout, + FT_Matrix *trafo) +{ + RsvgState * state; + + state = rsvg_state_current(layout->ctx); + if(state) + { + trafo->xx = state->affine[0] * 65536.0; + trafo->xy = state->affine[1] * 65536.0; + trafo->yx = state->affine[2] * 65536.0; + trafo->yy = state->affine[3] * 65536.0; + } + else + { + trafo->xx = 1; + trafo->xy = 0; + trafo->yx = 0; + trafo->yy = 1; + } +} + +static void +rsvg_text_layout_render_glyphs (RsvgTextLayout *layout, + PangoFont *font, + PangoGlyphString *glyphs, + RsvgTextRenderFunc render_func, + gint x, + gint y, + gpointer render_data) +{ + PangoGlyphInfo *gi; + FT_Int32 flags; + FT_Matrix trafo; + FT_Vector pos; + gint i; + gint x_position = 0; + + flags = rsvg_text_layout_render_flags (layout); + rsvg_text_layout_render_trafo (layout, &trafo); + + for (i = 0, gi = glyphs->glyphs; i < glyphs->num_glyphs; i++, gi++) + { + if (gi->glyph) + { + pos.x = x + x_position + gi->geometry.x_offset; + pos.y = y + gi->geometry.y_offset; + + FT_Vector_Transform (&pos, &trafo); + + render_func (font, gi->glyph, flags, NULL /* &trafo */, + pos.x, pos.y, + render_data); + } + + x_position += glyphs->glyphs[i].geometry.width; + } +} + +static void rsvg_text_render_vectors (PangoFont *font, PangoGlyph pango_glyph, FT_Int32 flags, @@ -428,6 +459,34 @@ rsvg_text_render_vectors (PangoFont *font, } static void +rsvg_text_layout_render_line (RsvgTextLayout *layout, + PangoLayoutLine *line, + RsvgTextRenderFunc render_func, + gint x, + gint y, + gpointer render_data) +{ + PangoRectangle rect; + GSList *list; + gint x_off = 0; + + for (list = line->runs; list; list = list->next) + { + PangoLayoutRun *run = list->data; + + pango_glyph_string_extents (run->glyphs, run->item->analysis.font, + NULL, &rect); + rsvg_text_layout_render_glyphs (layout, + run->item->analysis.font, run->glyphs, + render_func, + x + x_off, y, + render_data); + + x_off += rect.width; + } +} + +static void rsvg_text_layout_render (RsvgTextLayout *layout, RsvgTextRenderFunc render_func, gpointer render_data) @@ -482,9 +541,10 @@ rsvg_text_render_text (RsvgHandle *ctx, if (render->wrote) g_string_append_c(render->path, 'Z'); - /* Only a debugging aid for now */ - g_print("%s\n\n", render->path->str); - +#ifdef RSVG_TEXT_DEBUG + fprintf(stdout, "%s\n", render->path->str); +#endif + rsvg_handle_path (ctx, render->path->str, id); rsvg_render_ctx_free (render); diff --git a/rsvg-text.c b/rsvg-text.c index dd4d4325..95d85c3c 100644 --- a/rsvg-text.c +++ b/rsvg-text.c @@ -26,6 +26,8 @@ #include <string.h> #include "rsvg-private.h" +#include "rsvg-styles.h" +#include "rsvg-filter.h" #include "rsvg-text.h" #include "rsvg-css.h" @@ -105,6 +107,8 @@ rsvg_text_render_text_bitmap (RsvgHandle *ctx, RsvgPSCtx gradctx; int i; + rsvg_push_discrete_layer(ctx); + pixbuf = ctx->pixbuf; if (pixbuf == NULL) { @@ -225,6 +229,9 @@ rsvg_text_render_text_bitmap (RsvgHandle *ctx, g_free (bitmap.buffer); state->text_offset += line_ink_rect.width; + + rsvg_pop_discrete_layer(ctx); + } #endif @@ -285,34 +292,32 @@ rsvg_text_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) } void -rsvg_start_tspan (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_tspan (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; double affine[6] ; double x, y, dx, dy; RsvgState *state; - const char * klazz = NULL, * id = NULL; + const char * klazz = NULL, * id = NULL, *value; x = y = dx = dy = 0.; state = rsvg_state_current (ctx); - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "x")) - x = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - else if (!strcmp ((char *)atts[i], "y")) - y = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - else if (!strcmp ((char *)atts[i], "dx")) - dx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - else if (!strcmp ((char *)atts[i], "dy")) - dy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "x"))) + x = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "y"))) + y = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "dx"))) + dx = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "dy"))) + dy = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, state, "tspan", klazz, id, atts); } /* todo: transform() is illegal here */ @@ -324,16 +329,15 @@ rsvg_start_tspan (RsvgHandle *ctx, const xmlChar **atts) art_affine_translate (affine, x, y); art_affine_multiply (state->affine, affine, state->affine); } - rsvg_parse_style_attrs (ctx, state, "tspan", klazz, id, atts); } static void rsvg_text_handler_start (RsvgSaxHandler *self, const xmlChar *name, - const xmlChar **atts) + RsvgPropertyBag *atts) { RsvgSaxHandlerText *z = (RsvgSaxHandlerText *)self; RsvgHandle *ctx = z->ctx; - + /* push the state stack */ if (ctx->n_state == ctx->n_state_max) ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1); @@ -354,7 +358,7 @@ rsvg_text_handler_end (RsvgSaxHandler *self, const xmlChar *name) { RsvgSaxHandlerText *z = (RsvgSaxHandlerText *)self; RsvgHandle *ctx = z->ctx; - + if (!strcmp ((char *)name, "tspan")) { /* advance the text offset */ @@ -377,12 +381,11 @@ rsvg_text_handler_end (RsvgSaxHandler *self, const xmlChar *name) } void -rsvg_start_text (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_text (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; double affine[6] ; double x, y, dx, dy; - const char * klazz = NULL, * id = NULL; + const char * klazz = NULL, * id = NULL, *value; RsvgState *state; RsvgSaxHandlerText *handler = g_new0 (RsvgSaxHandlerText, 1); @@ -396,32 +399,29 @@ rsvg_start_text (RsvgHandle *ctx, const xmlChar **atts) state = rsvg_state_current (ctx); - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "x")) - x = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - else if (!strcmp ((char *)atts[i], "y")) - y = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - else if (!strcmp ((char *)atts[i], "dx")) - dx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - else if (!strcmp ((char *)atts[i], "dy")) - dy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - else if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } + if ((value = rsvg_property_bag_lookup (atts, "x"))) + x = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "y"))) + y = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "dx"))) + dx = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "dy"))) + dy = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, state, "text", klazz, id, atts); } x += dx ; y += dy ; art_affine_translate (affine, x, y); - art_affine_multiply (state->affine, affine, state->affine); - - rsvg_parse_style_attrs (ctx, state, "text", klazz, id, atts); + art_affine_multiply (state->affine, affine, state->affine); handler->parent = ctx->handler; ctx->handler = &handler->super; diff --git a/rsvg-text.h b/rsvg-text.h index 8c244197..bc83df99 100644 --- a/rsvg-text.h +++ b/rsvg-text.h @@ -30,8 +30,8 @@ G_BEGIN_DECLS -void rsvg_start_text (RsvgHandle *ctx, const xmlChar **atts); -void rsvg_start_tspan (RsvgHandle *ctx, const xmlChar **atts); +void rsvg_start_text (RsvgHandle *ctx, RsvgPropertyBag *atts); +void rsvg_start_tspan (RsvgHandle *ctx, RsvgPropertyBag *atts); char * make_valid_utf8 (const char *str); void @@ -26,11 +26,12 @@ #include "config.h" #include "rsvg.h" +#include "rsvg-private.h" #include "rsvg-css.h" #include "rsvg-styles.h" -#include "rsvg-private.h" #include "rsvg-shapes.h" #include "rsvg-text.h" +#include "rsvg-filter.h" #include <math.h> #include <string.h> @@ -70,49 +71,45 @@ rsvg_pixmap_destroy (guchar *pixels, gpointer data) } static void -rsvg_start_svg (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_svg (RsvgHandle *ctx, RsvgPropertyBag *atts) { - int i; int width = -1, height = -1, x = -1, y = -1; int rowstride; art_u8 *pixels; gint percent, em, ex; RsvgState *state; - gboolean has_alpha = TRUE; gint new_width, new_height; double x_zoom = 1.; double y_zoom = 1.; double affine[6]; + const char * value; double vbox_x = 0, vbox_y = 0, vbox_w = 0, vbox_h = 0; gboolean has_vbox = FALSE; - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) + /* x & y should be ignored since we should always be the outermost SVG, + at least for now, but i'll include them here anyway */ + if ((value = rsvg_property_bag_lookup (atts, "width"))) + width = rsvg_css_parse_length (value, ctx->dpi, &percent, &em, &ex); + if ((value = rsvg_property_bag_lookup (atts, "height"))) + height = rsvg_css_parse_length (value, ctx->dpi, &percent, &em, &ex); + if ((value = rsvg_property_bag_lookup (atts, "x"))) + x = rsvg_css_parse_length (value, ctx->dpi, &percent, &em, &ex); + if ((value = rsvg_property_bag_lookup (atts, "y"))) + y = rsvg_css_parse_length (value, ctx->dpi, &percent, &em, &ex); + if ((value = rsvg_property_bag_lookup (atts, "viewBox"))) { - /* x & y should be ignored since we should always be the outermost SVG, - at least for now, but i'll include them here anyway */ - if (!strcmp ((char *)atts[i], "width")) - width = rsvg_css_parse_length ((char *)atts[i + 1], ctx->dpi, &percent, &em, &ex); - else if (!strcmp ((char *)atts[i], "height")) - height = rsvg_css_parse_length ((char *)atts[i + 1], ctx->dpi, &percent, &em, &ex); - else if (!strcmp ((char *)atts[i], "x")) - x = rsvg_css_parse_length ((char *)atts[i + 1], ctx->dpi, &percent, &em, &ex); - else if (!strcmp ((char *)atts[i], "y")) - y = rsvg_css_parse_length ((char *)atts[i + 1], ctx->dpi, &percent, &em, &ex); - else if (!strcmp ((char *)atts[i], "viewBox")) - { - has_vbox = rsvg_css_parse_vbox ((char *)atts[i + 1], &vbox_x, &vbox_y, - &vbox_w, &vbox_h); - } + has_vbox = rsvg_css_parse_vbox (value, &vbox_x, &vbox_y, + &vbox_w, &vbox_h); } - + if (has_vbox && vbox_w > 0. && vbox_h > 0.) { new_width = (int)floor (vbox_w); new_height = (int)floor (vbox_h); - + /* apply the sizing function on the *original* width and height to acquire our real destination size. we'll scale it against the viewBox's coordinates later */ @@ -184,7 +181,7 @@ rsvg_start_svg (RsvgHandle *ctx, const xmlChar **atts) g_warning ("rsvg_start_svg: width too large"); return; } - rowstride = (new_width * (has_alpha ? 4 : 3) + 3) & ~3; + rowstride = (new_width * 4 + 3) & ~3; if (rowstride > INT_MAX / new_height) { /* FIXME: GError here? */ @@ -201,10 +198,10 @@ rsvg_start_svg (RsvgHandle *ctx, const xmlChar **atts) g_warning ("rsvg_start_svg: dimensions too large"); return; } - memset (pixels, has_alpha ? 0 : 255, rowstride * new_height); + memset (pixels, 0, rowstride * new_height); ctx->pixbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, - has_alpha, 8, + TRUE, 8, new_width, new_height, rowstride, rsvg_pixmap_destroy, @@ -213,35 +210,32 @@ rsvg_start_svg (RsvgHandle *ctx, const xmlChar **atts) } static void -rsvg_start_g (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_g (RsvgHandle *ctx, RsvgPropertyBag *atts) { RsvgState *state = rsvg_state_current (ctx); - const char * klazz = NULL, * id = NULL; - int i; + const char * klazz = NULL, * id = NULL, *value; - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) - { - if (!strcmp ((char *)atts[i], "class")) - klazz = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - } - } - - rsvg_parse_style_attrs (ctx, state, "g", klazz, id, atts); - if (state->opacity != 0xff) - rsvg_push_opacity_group (ctx); + if ((value = rsvg_property_bag_lookup (atts, "class"))) + klazz = value; + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + + rsvg_parse_style_attrs (ctx, state, "g", klazz, id, atts); + } + + rsvg_push_def_group (ctx, id); + if (!ctx->in_defs) + rsvg_push_discrete_layer (ctx); } static void rsvg_end_g (RsvgHandle *ctx) { - RsvgState *state = rsvg_state_current (ctx); - - if (state->opacity != 0xff) - rsvg_pop_opacity_group (ctx, state->opacity); + rsvg_pop_def_group (ctx); + if (!ctx->in_defs) + rsvg_pop_discrete_layer (ctx); } typedef struct _RsvgSaxHandlerDefs { @@ -276,43 +270,39 @@ rsvg_gradient_stop_handler_free (RsvgSaxHandler *self) static void rsvg_gradient_stop_handler_start (RsvgSaxHandler *self, const xmlChar *name, - const xmlChar **atts) + RsvgPropertyBag *atts) { RsvgSaxHandlerGstops *z = (RsvgSaxHandlerGstops *)self; RsvgGradientStops *stops = z->stops; - int i; double offset = 0; gboolean got_offset = FALSE; RsvgState state; int n_stop; + const char *value; if (strcmp ((char *)name, "stop")) return; rsvg_state_init (&state); - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) + if ((value = rsvg_property_bag_lookup (atts, "offset"))) { - if (!strcmp ((char *)atts[i], "offset")) - { - /* either a number [0,1] or a percentage */ - offset = rsvg_css_parse_normalized_length ((char *)atts[i + 1], z->ctx->dpi, 1., 0.); - - if (offset < 0.) - offset = 0.; - else if (offset > 1.) - offset = 1.; - - got_offset = TRUE; - } - else if (!strcmp ((char *)atts[i], "style")) - rsvg_parse_style (z->ctx, &state, (char *)atts[i + 1]); - else if (rsvg_is_style_arg ((char *)atts[i])) - rsvg_parse_style_pair (z->ctx, &state, - (char *)atts[i], (char *)atts[i + 1]); + /* either a number [0,1] or a percentage */ + offset = rsvg_css_parse_normalized_length (value, z->ctx->dpi, 1., 0.); + + if (offset < 0.) + offset = 0.; + else if (offset > 1.) + offset = 1.; + + got_offset = TRUE; } + if ((value = rsvg_property_bag_lookup (atts, "style"))) + rsvg_parse_style (z->ctx, &state, value); + + rsvg_parse_style_pairs (z->ctx, &state, atts); } rsvg_state_finalize (&state); @@ -401,70 +391,67 @@ rsvg_linear_gradient_free (RsvgDefVal *self) } static void -rsvg_start_linear_gradient (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_linear_gradient (RsvgHandle *ctx, RsvgPropertyBag *atts) { RsvgState *state = rsvg_state_current (ctx); RsvgLinearGradient *grad = NULL; - int i; - const char *id = NULL; + const char *id = NULL, *value; double x1 = 0., y1 = 0., x2 = 0., y2 = 0.; ArtGradientSpread spread = ART_GRADIENT_PAD; const char * xlink_href = NULL; gboolean obj_bbox = TRUE; gboolean got_x1, got_x2, got_y1, got_y2, got_spread, got_transform, got_bbox, cloned, shallow_cloned; double affine[6]; + int i; got_x1 = got_x2 = got_y1 = got_y2 = got_spread = got_transform = got_bbox = cloned = shallow_cloned = FALSE; - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + if ((value = rsvg_property_bag_lookup (atts, "x1"))) { + x1 = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + got_x1 = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "y1"))) { + y1 = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + got_y1 = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "x2"))) { + x2 = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + got_x2 = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "y2"))) { + y2 = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + got_y2 = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "spreadMethod"))) { - if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "x1")) { - x1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - got_x1 = TRUE; - } - else if (!strcmp ((char *)atts[i], "y1")) { - y1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - got_y1 = TRUE; - } - else if (!strcmp ((char *)atts[i], "x2")) { - x2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - got_x2 = TRUE; + if (!strcmp (value, "pad")) { + spread = ART_GRADIENT_PAD; + got_spread = TRUE; } - else if (!strcmp ((char *)atts[i], "y2")) { - y2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - got_y2 = TRUE; + else if (!strcmp (value, "reflect")) { + spread = ART_GRADIENT_REFLECT; + got_spread = TRUE; } - else if (!strcmp ((char *)atts[i], "spreadMethod")) - { - if (!strcmp ((char *)atts[i + 1], "pad")) { - spread = ART_GRADIENT_PAD; - got_spread = TRUE; - } - else if (!strcmp ((char *)atts[i + 1], "reflect")) { - spread = ART_GRADIENT_REFLECT; - got_spread = TRUE; - } - else if (!strcmp ((char *)atts[i + 1], "repeat")) { - spread = ART_GRADIENT_REPEAT; - got_spread = TRUE; - } - } - else if (!strcmp ((char *)atts[i], "xlink:href")) - xlink_href = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "gradientTransform")) - got_transform = rsvg_parse_transform (affine, (const char *)atts[i + 1]); - else if (!strcmp ((char *)atts[i], "gradientUnits")) { - if (!strcmp ((char *)atts[i+1], "userSpaceOnUse")) - obj_bbox = FALSE; - got_bbox = TRUE; + else if (!strcmp (value, "repeat")) { + spread = ART_GRADIENT_REPEAT; + got_spread = TRUE; } } + if ((value = rsvg_property_bag_lookup (atts, "xlink:href"))) + xlink_href = value; + if ((value = rsvg_property_bag_lookup (atts, "gradientTransform"))) + got_transform = rsvg_parse_transform (affine, value); + if ((value = rsvg_property_bag_lookup (atts, "gradientUnits"))) { + if (!strcmp (value, "userSpaceOnUse")) + obj_bbox = FALSE; + got_bbox = TRUE; + } } - + /* set up 100% as the default if not gotten */ if (!got_x2) { if (obj_bbox) @@ -521,75 +508,72 @@ rsvg_radial_gradient_free (RsvgDefVal *self) } static void -rsvg_start_radial_gradient (RsvgHandle *ctx, const xmlChar **atts, const char * tag) /* tag for conicalGradient */ +rsvg_start_radial_gradient (RsvgHandle *ctx, RsvgPropertyBag *atts, const char * tag) /* tag for conicalGradient */ { RsvgState *state = rsvg_state_current (ctx); RsvgRadialGradient *grad = NULL; - int i; const char *id = NULL; double cx = 0., cy = 0., r = 0., fx = 0., fy = 0.; - const char * xlink_href = NULL; + const char * xlink_href = NULL, *value; ArtGradientSpread spread = ART_GRADIENT_PAD; gboolean obj_bbox = TRUE; gboolean got_cx, got_cy, got_r, got_fx, got_fy, got_spread, got_transform, got_bbox, cloned, shallow_cloned; double affine[6]; - + int i; + got_cx = got_cy = got_r = got_fx = got_fy = got_spread = got_transform = got_bbox = cloned = shallow_cloned = FALSE; - if (atts != NULL) + if (rsvg_property_bag_size (atts)) { - for (i = 0; atts[i] != NULL; i += 2) + if ((value = rsvg_property_bag_lookup (atts, "id"))) + id = value; + if ((value = rsvg_property_bag_lookup (atts, "cx"))) { + cx = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + got_cx = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "cy"))) { + cy = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + got_cy = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "r"))) { + r = rsvg_css_parse_normalized_length (value, ctx->dpi, + rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), + state->font_size); + got_r = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "fx"))) { + fx = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->width, state->font_size); + got_fx = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "fy"))) { + fy = rsvg_css_parse_normalized_length (value, ctx->dpi, (gdouble)ctx->height, state->font_size); + got_fy = TRUE; + } + if ((value = rsvg_property_bag_lookup (atts, "xlink:href"))) + xlink_href = value; + if ((value = rsvg_property_bag_lookup (atts, "gradientTransform"))) { + got_transform = rsvg_parse_transform (affine, value); + } + if ((value = rsvg_property_bag_lookup (atts, "spreadMethod"))) { - if (!strcmp ((char *)atts[i], "id")) - id = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "cx")) { - cx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - got_cx = TRUE; - } - else if (!strcmp ((char *)atts[i], "cy")) { - cy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - got_cy = TRUE; + if (!strcmp (value, "pad")) { + spread = ART_GRADIENT_PAD; + got_spread = TRUE; } - else if (!strcmp ((char *)atts[i], "r")) { - r = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, - rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), - state->font_size); - got_r = TRUE; + else if (!strcmp (value, "reflect")) { + spread = ART_GRADIENT_REFLECT; + got_spread = TRUE; } - else if (!strcmp ((char *)atts[i], "fx")) { - fx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size); - got_fx = TRUE; - } - else if (!strcmp ((char *)atts[i], "fy")) { - fy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size); - got_fy = TRUE; - } - else if (!strcmp ((char *)atts[i], "xlink:href")) - xlink_href = (const char *)atts[i + 1]; - else if (!strcmp ((char *)atts[i], "gradientTransform")) { - got_transform = rsvg_parse_transform (affine, (const char *)atts[i + 1]); - } - else if (!strcmp ((char *)atts[i], "spreadMethod")) - { - if (!strcmp ((char *)atts[i + 1], "pad")) { - spread = ART_GRADIENT_PAD; - got_spread = TRUE; - } - else if (!strcmp ((char *)atts[i + 1], "reflect")) { - spread = ART_GRADIENT_REFLECT; - got_spread = TRUE; - } - else if (!strcmp ((char *)atts[i + 1], "repeat")) { - spread = ART_GRADIENT_REPEAT; - got_spread = TRUE; - } - } - else if (!strcmp ((char *)atts[i], "gradientUnits")) { - if (!strcmp ((char *)atts[i+1], "userSpaceOnUse")) - obj_bbox = FALSE; - got_bbox = TRUE; + else if (!strcmp (value, "repeat")) { + spread = ART_GRADIENT_REPEAT; + got_spread = TRUE; } } + if ((value = rsvg_property_bag_lookup (atts, "gradientUnits"))) { + if (!strcmp (value, "userSpaceOnUse")) + obj_bbox = FALSE; + got_bbox = TRUE; + } } if (xlink_href != NULL) @@ -677,7 +661,7 @@ rsvg_style_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) static void rsvg_style_handler_start (RsvgSaxHandler *self, const xmlChar *name, - const xmlChar **atts) + RsvgPropertyBag *atts) { } @@ -699,7 +683,7 @@ rsvg_style_handler_end (RsvgSaxHandler *self, const xmlChar *name) } static void -rsvg_start_style (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_style (RsvgHandle *ctx, RsvgPropertyBag *atts) { RsvgSaxHandlerStyle *handler = g_new0 (RsvgSaxHandlerStyle, 1); @@ -729,8 +713,64 @@ rsvg_defs_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) } static void +rsvg_filter_handler_start (RsvgHandle *ctx, const xmlChar *name, + RsvgPropertyBag *atts) +{ + if (!strcmp ((char *)name, "filter")) + rsvg_start_filter (ctx, atts); + else if (!strcmp ((char *)name, "feBlend")) + rsvg_start_filter_primitive_blend (ctx, atts); + else if (!strcmp ((char *)name, "feColorMatrix")) + rsvg_start_filter_primitive_colour_matrix(ctx, atts); + else if (!strcmp ((char *)name, "feComponentTransfer")) + rsvg_start_filter_primitive_component_transfer(ctx, atts); + else if (!strcmp ((char *)name, "feComposite")) + rsvg_start_filter_primitive_composite(ctx, atts); + else if (!strcmp ((char *)name, "feConvolveMatrix")) + rsvg_start_filter_primitive_convolve_matrix (ctx, atts); + else if (!strcmp ((char *)name, "feDiffuseLighting")) + rsvg_start_filter_primitive_diffuse_lighting(ctx, atts); + else if (!strcmp ((char *)name, "feDisplacementMap")) + rsvg_start_filter_primitive_displacement_map(ctx, atts); + else if (!strcmp ((char *)name, "feFlood")) + rsvg_start_filter_primitive_flood(ctx, atts); + else if (!strcmp ((char *)name, "feGaussianBlur")) + rsvg_start_filter_primitive_gaussian_blur (ctx, atts); + else if (!strcmp ((char *)name, "feImage")) + rsvg_start_filter_primitive_image (ctx, atts); + else if (!strcmp ((char *)name, "feMerge")) + rsvg_start_filter_primitive_merge(ctx, atts); + else if (!strcmp ((char *)name, "feMorphology")) + rsvg_start_filter_primitive_erode(ctx, atts); + else if (!strcmp ((char *)name, "feOffset")) + rsvg_start_filter_primitive_offset(ctx, atts); + else if (!strcmp ((char *)name, "feSpecularLighting")) + rsvg_start_filter_primitive_specular_lighting(ctx, atts); + else if (!strcmp ((char *)name, "feTile")) + rsvg_start_filter_primitive_tile(ctx, atts); + else if (!strcmp ((char *)name, "feTurbulence")) + rsvg_start_filter_primitive_turbulence(ctx, atts); + else if (!strcmp ((char *)name, "feDistantLight")) + rsvg_start_filter_primitive_light_source(ctx, atts, 'd'); + else if (!strcmp ((char *)name, "feSpotLight")) + rsvg_start_filter_primitive_light_source(ctx, atts, 's'); + else if (!strcmp ((char *)name, "fePointLight")) + rsvg_start_filter_primitive_light_source(ctx, atts, 'p'); + else if (!strcmp ((char *)name, "feMergeNode")) + rsvg_start_filter_primitive_merge_node(ctx, atts); + else if (!strcmp ((char *)name, "feFuncR")) + rsvg_start_filter_primitive_component_transfer_function(ctx, atts, 'r'); + else if (!strcmp ((char *)name, "feFuncG")) + rsvg_start_filter_primitive_component_transfer_function(ctx, atts, 'g'); + else if (!strcmp ((char *)name, "feFuncB")) + rsvg_start_filter_primitive_component_transfer_function(ctx, atts, 'b'); + else if (!strcmp ((char *)name, "feFuncA")) + rsvg_start_filter_primitive_component_transfer_function(ctx, atts, 'a'); +} + +static void rsvg_defs_handler_start (RsvgSaxHandler *self, const xmlChar *name, - const xmlChar **atts) + RsvgPropertyBag *atts) { RsvgSaxHandlerDefs *z = (RsvgSaxHandlerDefs *)self; RsvgHandle *ctx = z->ctx; @@ -739,8 +779,10 @@ rsvg_defs_handler_start (RsvgSaxHandler *self, const xmlChar *name, if (ctx->n_state == ctx->n_state_max) ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1); if (ctx->n_state) - rsvg_state_clone (&ctx->state[ctx->n_state], - &ctx->state[ctx->n_state - 1]); + { + rsvg_state_inherit (&ctx->state[ctx->n_state], + &ctx->state[ctx->n_state - 1]); + } else rsvg_state_init (ctx->state); ctx->n_state++; @@ -759,6 +801,10 @@ rsvg_defs_handler_start (RsvgSaxHandler *self, const xmlChar *name, rsvg_start_radial_gradient (ctx, atts, "conicalGradient"); else if (!strcmp ((char *)name, "style")) rsvg_start_style (ctx, atts); + else if (!strcmp ((char *)name, "g")) + rsvg_start_g (ctx, atts); + else if (!strcmp ((char *)name, "use")) + rsvg_start_use (ctx, atts); else if (!strcmp ((char *)name, "path")) rsvg_start_path (ctx, atts); else if (!strcmp ((char *)name, "line")) @@ -773,6 +819,8 @@ rsvg_defs_handler_start (RsvgSaxHandler *self, const xmlChar *name, rsvg_start_polygon (ctx, atts); else if (!strcmp ((char *)name, "polyline")) rsvg_start_polyline (ctx, atts); + + rsvg_filter_handler_start (ctx, name, atts); } static void @@ -790,6 +838,11 @@ rsvg_defs_handler_end (RsvgSaxHandler *self, const xmlChar *name) } ctx->in_defs = FALSE; } + + if (!strcmp ((char *)name, "g")) + rsvg_end_g (ctx); + if (!strcmp ((char *)name, "filter")) + rsvg_end_filter (ctx); /* pop the state stack */ ctx->n_state--; @@ -797,7 +850,7 @@ rsvg_defs_handler_end (RsvgSaxHandler *self, const xmlChar *name) } static void -rsvg_start_defs (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_defs (RsvgHandle *ctx, RsvgPropertyBag *atts) { RsvgSaxHandlerDefs *handler = g_new0 (RsvgSaxHandlerDefs, 1); @@ -850,7 +903,7 @@ rsvg_desc_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) static void rsvg_desc_handler_start (RsvgSaxHandler *self, const xmlChar *name, - const xmlChar **atts) + RsvgPropertyBag *atts) { } @@ -875,7 +928,7 @@ rsvg_desc_handler_end (RsvgSaxHandler *self, const xmlChar *name) } static void -rsvg_start_desc (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_desc (RsvgHandle *ctx, RsvgPropertyBag *atts) { RsvgSaxHandlerDesc *handler = g_new0 (RsvgSaxHandlerDesc, 1); @@ -927,7 +980,7 @@ rsvg_title_handler_characters (RsvgSaxHandler *self, const xmlChar *ch, int len) static void rsvg_title_handler_start (RsvgSaxHandler *self, const xmlChar *name, - const xmlChar **atts) + RsvgPropertyBag *atts) { } @@ -952,7 +1005,7 @@ rsvg_title_handler_end (RsvgSaxHandler *self, const xmlChar *name) } static void -rsvg_start_title (RsvgHandle *ctx, const xmlChar **atts) +rsvg_start_title (RsvgHandle *ctx, RsvgPropertyBag *atts) { RsvgSaxHandlerTitle *handler = g_new0 (RsvgSaxHandlerTitle, 1); @@ -972,11 +1025,15 @@ rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts) { RsvgHandle *ctx = (RsvgHandle *)data; + RsvgPropertyBag * bag; + + bag = rsvg_property_bag_new(atts); + if (ctx->handler) { ctx->handler_nest++; if (ctx->handler->start_element != NULL) - ctx->handler->start_element (ctx->handler, name, atts); + ctx->handler->start_element (ctx->handler, name, bag); } else { @@ -984,53 +1041,59 @@ rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts) if (ctx->n_state == ctx->n_state_max) ctx->state = g_renew (RsvgState, ctx->state, ctx->n_state_max <<= 1); if (ctx->n_state) - rsvg_state_clone (&ctx->state[ctx->n_state], - &ctx->state[ctx->n_state - 1]); + { + rsvg_state_inherit (&ctx->state[ctx->n_state], + &ctx->state[ctx->n_state - 1]); + } else rsvg_state_init (ctx->state); ctx->n_state++; if (!strcmp ((char *)name, "svg")) - rsvg_start_svg (ctx, atts); + rsvg_start_svg (ctx, bag); else if (!strcmp ((char *)name, "g")) - rsvg_start_g (ctx, atts); + rsvg_start_g (ctx, bag); else if (!strcmp ((char *)name, "defs")) - rsvg_start_defs (ctx, atts); + rsvg_start_defs (ctx, bag); else if (!strcmp ((char *)name, "path")) - rsvg_start_path (ctx, atts); + rsvg_start_path (ctx, bag); else if (!strcmp ((char *)name, "line")) - rsvg_start_line (ctx, atts); + rsvg_start_line (ctx, bag); else if (!strcmp ((char *)name, "rect")) - rsvg_start_rect (ctx, atts); + rsvg_start_rect (ctx, bag); else if (!strcmp ((char *)name, "circle")) - rsvg_start_circle (ctx, atts); + rsvg_start_circle (ctx, bag); else if (!strcmp ((char *)name, "ellipse")) - rsvg_start_ellipse (ctx, atts); + rsvg_start_ellipse (ctx, bag); else if (!strcmp ((char *)name, "polygon")) - rsvg_start_polygon (ctx, atts); + rsvg_start_polygon (ctx, bag); else if (!strcmp ((char *)name, "polyline")) - rsvg_start_polyline (ctx, atts); + rsvg_start_polyline (ctx, bag); else if (!strcmp ((char *)name, "use")) - rsvg_start_use (ctx, atts); + rsvg_start_use (ctx, bag); else if (!strcmp ((char *)name, "text")) - rsvg_start_text (ctx, atts); + rsvg_start_text (ctx, bag); else if (!strcmp ((char *)name, "image")) - rsvg_start_image (ctx, atts); + rsvg_start_image (ctx, bag); else if (!strcmp ((char *)name, "style")) - rsvg_start_style (ctx, atts); + rsvg_start_style (ctx, bag); else if (!strcmp ((char *)name, "title")) - rsvg_start_title (ctx, atts); + rsvg_start_title (ctx, bag); else if (!strcmp ((char *)name, "desc")) - rsvg_start_desc (ctx, atts); + rsvg_start_desc (ctx, bag); /* see conicalGradient discussion above */ else if (!strcmp ((char *)name, "linearGradient")) - rsvg_start_linear_gradient (ctx, atts); + rsvg_start_linear_gradient (ctx, bag); else if (!strcmp ((char *)name, "radialGradient")) - rsvg_start_radial_gradient (ctx, atts, "radialGradient"); + rsvg_start_radial_gradient (ctx, bag, "radialGradient"); else if (!strcmp ((char *)name, "conicalGradient")) - rsvg_start_radial_gradient (ctx, atts, "conicalGradient"); + rsvg_start_radial_gradient (ctx, bag, "conicalGradient"); + + rsvg_filter_handler_start (ctx, name, bag); } + + rsvg_property_bag_free(bag); } static void @@ -1054,6 +1117,8 @@ rsvg_end_element (void *data, const xmlChar *name) if (!strcmp ((char *)name, "g")) rsvg_end_g (ctx); + if (!strcmp ((char *)name, "filter")) + rsvg_end_filter (ctx); else if (!strcmp ((char *)name, "defs")) { ctx->in_defs = FALSE; } @@ -1323,7 +1388,7 @@ rsvg_handle_init (RsvgHandle * handle) g_free, g_free); handle->ctxt = NULL; - + handle->current_defs_group = NULL; handle->title = g_string_new (NULL); handle->desc = g_string_new (NULL); } diff --git a/test-display.c b/test-display.c index d7f699f0..846ff8f5 100644 --- a/test-display.c +++ b/test-display.c @@ -1,7 +1,7 @@ /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- * test-display: * - * Copyright (C) 2002 Dom Lachowicz + * Copyright (C) 2002-2004 Dom Lachowicz * * This program is released into the PUBLIC DOMAIN, and is meant to be a * useful example if how to draw a SVG image inside of a GtkWidget. This @@ -13,19 +13,25 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE * * Simple utility to view a SVG file inside of a GtkWindow - * - * To compile, basically: - * gcc `pkg-config --cflags --libs gtk+-2.0 librsvg-2.0` -lpopt -o svg-display test-display.c */ #include "config.h" #include "rsvg.h" +#include "rsvg-private.h" #include <stdio.h> #include <stdlib.h> #include <popt.h> #include <gtk/gtk.h> +#include <gdk/gdk.h> + +#ifdef ENABLE_XEMBED +#include <gdk/gdkx.h> +#endif + +#define DEFAULT_WIDTH 240 +#define DEFAULT_HEIGHT 240 static void quit_cb (GtkWidget *win, gpointer unused) @@ -34,24 +40,73 @@ quit_cb (GtkWidget *win, gpointer unused) gtk_main_quit(); } +static void +win_embedded_cb (GtkPlug *plug, gpointer data) +{ +} + static void -view_pixbuf (GdkPixbuf * pixbuf) +view_pixbuf (GdkPixbuf * pixbuf, int xid) { GtkWidget *win, *img; - + gint width, height; + /* create toplevel window and set its title */ - win = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_title (GTK_WINDOW(win), "SVG Viewer"); - + + if(xid > 0) + { + GdkWindow *gdk_parent; + + win = gtk_plug_new(0); + g_signal_connect(G_OBJECT(win), "embedded", + G_CALLBACK(win_embedded_cb), NULL); + + gdk_parent = gdk_window_foreign_new(xid); + gdk_window_get_geometry(gdk_parent, NULL, NULL, &width, &height, NULL); + } + else + { + win = gtk_window_new (GTK_WINDOW_TOPLEVEL); + width = MIN(gdk_pixbuf_get_width (pixbuf), DEFAULT_WIDTH) + 20; + height = MIN(gdk_pixbuf_get_height (pixbuf), DEFAULT_HEIGHT) + 20; + + gtk_window_set_title (GTK_WINDOW(win), "SVG Viewer"); + } + + gtk_window_set_default_size(GTK_WINDOW(win), width, height); + /* exit when 'X' is clicked */ g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(quit_cb), NULL); - + g_signal_connect(G_OBJECT(win), "delete_event", G_CALLBACK(quit_cb), NULL); + /* create a new image */ img = gtk_image_new_from_pixbuf (pixbuf); - + /* pack the window with the image */ - gtk_container_add(GTK_CONTAINER(win), img); + if(xid > 0) + { + gtk_container_add(GTK_CONTAINER(win), img); + } + else + { + GtkWidget *scroll; + + scroll = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(scroll), img); + gtk_container_add(GTK_CONTAINER(win), scroll); + } + gtk_widget_show_all (win); + +#ifdef ENABLE_XEMBED + if(xid > 0){ + XReparentWindow(GDK_WINDOW_XDISPLAY(win->window), + GDK_WINDOW_XID(win->window), + xid, 0, 0); + XMapWindow(GDK_WINDOW_XDISPLAY(win->window), + GDK_WINDOW_XID(win->window)); + } +#endif } int @@ -64,14 +119,23 @@ main (int argc, char **argv) int width = -1; int height = -1; int bVersion = 0; - + + int xid = -1; + int from_stdin = 0; + + struct RsvgSizeCallbackData size_data; + struct poptOption options_table[] = { - { "dpi" , 'd', POPT_ARG_DOUBLE, &dpi, 0, "Pixels Per Inch", "<float>"}, - { "x-zoom", 'x', POPT_ARG_DOUBLE, &x_zoom, 0, "x zoom factor", "<float>" }, - { "y-zoom", 'y', POPT_ARG_DOUBLE, &y_zoom, 0, "y zoom factor", "<float>" }, - { "width", 'w', POPT_ARG_INT, &width, 0, "width", "<int>" }, - { "height", 'h', POPT_ARG_INT, &height, 0, "height", "<int>" }, - { "version", 'v', POPT_ARG_NONE, &bVersion, 0, "show version information", NULL }, +#ifdef ENABLE_XEMBED + { "xid", 'i', POPT_ARG_INT, &xid, 0, "XWindow ID [for X11 embedding]", "<int>" }, +#endif + { "stdin", 's', POPT_ARG_NONE, &from_stdin, 0, "Use stdin", NULL }, + { "dpi", 'd', POPT_ARG_DOUBLE, &dpi, 0, "Pixels Per Inch", "<float>" }, + { "x-zoom", 'x', POPT_ARG_DOUBLE, &x_zoom, 0, "x zoom factor", "<float>" }, + { "y-zoom", 'y', POPT_ARG_DOUBLE, &y_zoom, 0, "y zoom factor", "<float>" }, + { "width", 'w', POPT_ARG_INT, &width, 0, "width", "<int>" }, + { "height", 'h', POPT_ARG_INT, &height, 0, "height", "<int>" }, + { "version", 'v', POPT_ARG_NONE, &bVersion, 0, "show version information", NULL }, POPT_AUTOHELP POPT_TABLEEND }; @@ -80,15 +144,15 @@ main (int argc, char **argv) gint n_args = 0; GdkPixbuf *pixbuf; - popt_context = poptGetContext ("svg-display", argc, (const char **)argv, options_table, 0); - poptSetOtherOptionHelp(popt_context, "[OPTIONS...] file.svg"); + popt_context = poptGetContext ("rsvg-view", argc, (const char **)argv, options_table, 0); + poptSetOtherOptionHelp(popt_context, "[OPTIONS...] [file.svg]"); c = poptGetNextOpt (popt_context); args = poptGetArgs (popt_context); if (bVersion != 0) { - printf ("svg-display version %s\n", VERSION); + printf ("rsvg-view version %s\n", VERSION); return 0; } @@ -98,7 +162,7 @@ main (int argc, char **argv) n_args++; } - if (n_args != 1) + if ((!from_stdin) && (n_args != 1)) { poptPrintHelp (popt_context, stderr, 0); poptFreeContext (popt_context); @@ -113,24 +177,42 @@ main (int argc, char **argv) /* if both are unspecified, assume user wants to zoom the pixbuf in at least 1 dimension */ if (width == -1 && height == -1) - pixbuf = rsvg_pixbuf_from_file_at_zoom (args[0], x_zoom, y_zoom, NULL); + { + size_data.type = RSVG_SIZE_ZOOM; + size_data.x_zoom = x_zoom; + size_data.y_zoom = y_zoom; + } /* if both are unspecified, assume user wants to resize pixbuf in at least 1 dimension */ else if (x_zoom == 1.0 && y_zoom == 1.0) - pixbuf = rsvg_pixbuf_from_file_at_size (args[0], width, height, NULL); + { + size_data.type = RSVG_SIZE_WH; + size_data.width = width; + size_data.height = height; + } + /* assume the user wants to zoom the pixbuf, but cap the maximum size */ else - /* assume the user wants to zoom the pixbuf, but cap the maximum size */ - pixbuf = rsvg_pixbuf_from_file_at_zoom_with_max (args[0], x_zoom, y_zoom, - width, height, NULL); + { + size_data.type = RSVG_SIZE_ZOOM_MAX; + size_data.x_zoom = x_zoom; + size_data.y_zoom = y_zoom; + size_data.width = width; + size_data.height = height; + } + if(from_stdin) + pixbuf = rsvg_pixbuf_from_stdio_file_with_size_data (stdin, &size_data, NULL); + else + pixbuf = rsvg_pixbuf_from_file_with_size_data (args[0], &size_data, NULL); + poptFreeContext (popt_context); - + if (!pixbuf) { fprintf (stderr, "Error displaying pixbuf!\n"); return 1; } - view_pixbuf (pixbuf); + view_pixbuf (pixbuf, xid); /* run the gtk+ main loop */ gtk_main (); diff --git a/test-performance.c b/test-performance.c index 2c175750..32bc21f5 100644 --- a/test-performance.c +++ b/test-performance.c @@ -3,6 +3,7 @@ test-performance.c: performance tests. Copyright (C) 2002 Ximian, Inc. + Copyright (C) 2004 Dom Lachowicz This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as @@ -20,12 +21,14 @@ Boston, MA 02111-1307, USA. Author: Michael Meeks <michael@ximian.com> + Author: Dom Lachowicz <cinamod@hotmail.com> */ #include <config.h> #include <glib.h> #include <stdio.h> #include <stdlib.h> +#include <popt.h> #include "rsvg.h" @@ -34,25 +37,87 @@ main (int argc, const char **argv) { int i, count = 10; GTimer *timer; - GdkPixbuf *pixbuf; - const char *fname; + + poptContext popt_context; + double x_zoom = 1.0; + double y_zoom = 1.0; + double dpi = -1.0; + int width = -1; + int height = -1; + int bVersion = 0; + + struct poptOption options_table[] = { + { "dpi" , 'd', POPT_ARG_DOUBLE, NULL, 0, "pixels per inch", "<float>"}, + { "x-zoom", 'x', POPT_ARG_DOUBLE, NULL, 0, "x zoom factor", "<float>" }, + { "y-zoom", 'y', POPT_ARG_DOUBLE, NULL, 0, "y zoom factor", "<float>" }, + { "width", 'w', POPT_ARG_INT, NULL, 0, "width", "<int>" }, + { "height", 'h', POPT_ARG_INT, NULL, 0, "height", "<int>" }, + { "count", 'c', POPT_ARG_INT, NULL, 0, "number of times to render the SVG", "<int>" }, + { "version", 'v', POPT_ARG_NONE, NULL, 0, "show version information", NULL }, + POPT_AUTOHELP + POPT_TABLEEND + }; + int c; + const char * const *args; + gint n_args = 0; + GdkPixbuf *pixbuf; + + options_table[0].arg = &dpi; + options_table[1].arg = &x_zoom; + options_table[2].arg = &y_zoom; + options_table[3].arg = &width; + options_table[4].arg = &height; + options_table[5].arg = &count; + options_table[6].arg = &bVersion; + + popt_context = poptGetContext ("test-performance", argc, argv, options_table, 0); + poptSetOtherOptionHelp(popt_context, "[OPTIONS...] file.svg"); + + c = poptGetNextOpt (popt_context); + args = poptGetArgs (popt_context); + + if (bVersion != 0) + { + g_print ("test-performance version %s\n", VERSION); + return 0; + } + + if (args) + while (args[n_args] != NULL) + n_args++; + + if (n_args != 1) + { + poptPrintHelp (popt_context, stderr, 0); + poptFreeContext (popt_context); + return 1; + } g_type_init (); - fname = argv [argc - 1]; - fprintf (stderr, "File '%s'\n", fname); + fprintf (stdout, "File '%s'\n", args[0]); timer = g_timer_new (); g_timer_start (timer); for (i = 0; i < count; i++) { - pixbuf = rsvg_pixbuf_from_file_at_zoom ( - fname, 1.5, 1.5, NULL); + /* if both are unspecified, assume user wants to zoom the pixbuf in at least 1 dimension */ + if (width == -1 && height == -1) + pixbuf = rsvg_pixbuf_from_file_at_zoom (args[0], x_zoom, y_zoom, NULL); + /* if both are unspecified, assume user wants to resize pixbuf in at least 1 dimension */ + else if (x_zoom == 1.0 && y_zoom == 1.0) + pixbuf = rsvg_pixbuf_from_file_at_size (args[0], width, height, NULL); + else + /* assume the user wants to zoom the pixbuf, but cap the maximum size */ + pixbuf = rsvg_pixbuf_from_file_at_zoom_with_max (args[0], x_zoom, y_zoom, + width, height, NULL); + g_object_unref (pixbuf); } - fprintf (stderr, "Scaling took %g(s)\n", + fprintf (stdout, "Rendering took %g(s)\n", g_timer_elapsed (timer, NULL) / count); + g_timer_destroy(timer); return 0; } |