summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog219
-rw-r--r--Makefile.am47
-rw-r--r--TODO3
-rw-r--r--configure.in66
-rw-r--r--gtk-engine/Makefile.am2
-rw-r--r--gtk-engine/svg-draw.c2
-rw-r--r--gtk-engine/svg-render.c133
-rw-r--r--gtk-engine/svg.h18
-rw-r--r--moz-plugin/Makefile.am9
-rw-r--r--moz-plugin/moz-plugin.c235
-rw-r--r--rsvg-css.c54
-rw-r--r--rsvg-css.h3
-rw-r--r--rsvg-defs.c15
-rw-r--r--rsvg-defs.h6
-rw-r--r--rsvg-file-util.c87
-rw-r--r--rsvg-filter.c4860
-rw-r--r--rsvg-filter.h123
-rw-r--r--rsvg-paint-server.c3
-rw-r--r--rsvg-private.h58
-rw-r--r--rsvg-shapes.c1062
-rw-r--r--rsvg-shapes.h35
-rw-r--r--rsvg-styles.c703
-rw-r--r--rsvg-styles.h49
-rw-r--r--rsvg-text-vectors.c196
-rw-r--r--rsvg-text.c90
-rw-r--r--rsvg-text.h4
-rw-r--r--rsvg.c459
-rw-r--r--test-display.c142
-rw-r--r--test-performance.c79
29 files changed, 7834 insertions, 928 deletions
diff --git a/ChangeLog b/ChangeLog
index 758cc35d..99a76b17 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/TODO b/TODO
index d4049179..2e32f3a5 100644
--- a/TODO
+++ b/TODO
@@ -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;
diff --git a/rsvg-css.c b/rsvg-css.c
index c964b1be..7db1f5c4 100644
--- a/rsvg-css.c
+++ b/rsvg-css.c
@@ -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)
diff --git a/rsvg-css.h b/rsvg-css.h
index 86d54995..25a79dbf 100644
--- a/rsvg-css.h
+++ b/rsvg-css.h
@@ -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
diff --git a/rsvg.c b/rsvg.c
index 2544db14..29cf7ec8 100644
--- a/rsvg.c
+++ b/rsvg.c
@@ -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;
}