summaryrefslogtreecommitdiff
path: root/librsvg
diff options
context:
space:
mode:
authorRaph Levien <raph@src.gnome.org>2000-05-29 22:12:11 +0000
committerRaph Levien <raph@src.gnome.org>2000-05-29 22:12:11 +0000
commit55cffa7797e8d5f2eac983a312b37c32cd6a6445 (patch)
treecf8fa81b376fd77de75345d88a3c4226cfe90586 /librsvg
parent17b8049c949872e40db2f603fad2dd1552168dfb (diff)
downloadnautilus-55cffa7797e8d5f2eac983a312b37c32cd6a6445.tar.gz
Added gradient handling to librsvg.
Diffstat (limited to 'librsvg')
-rw-r--r--librsvg/Makefile.am20
-rw-r--r--librsvg/art_render.c1140
-rw-r--r--librsvg/art_render.h188
-rw-r--r--librsvg/art_render_gradient.c303
-rw-r--r--librsvg/art_render_gradient.h90
-rw-r--r--librsvg/art_render_svp.c398
-rw-r--r--librsvg/art_render_svp.h51
-rw-r--r--librsvg/art_rgba.c259
-rw-r--r--librsvg/art_rgba.h43
-rw-r--r--librsvg/art_rgba_svp.c280
-rw-r--r--librsvg/art_rgba_svp.h40
-rw-r--r--librsvg/rsvg-css.c201
-rw-r--r--librsvg/rsvg-css.h18
-rw-r--r--librsvg/rsvg-defs.c67
-rw-r--r--librsvg/rsvg-defs.h30
-rw-r--r--librsvg/rsvg-paint-server.c345
-rw-r--r--librsvg/rsvg-paint-server.h66
-rw-r--r--librsvg/rsvg.c623
-rw-r--r--librsvg/test-rsvg.c53
-rw-r--r--librsvg/test.svg21
20 files changed, 3341 insertions, 895 deletions
diff --git a/librsvg/Makefile.am b/librsvg/Makefile.am
index a31d5a162..20c9e7e47 100644
--- a/librsvg/Makefile.am
+++ b/librsvg/Makefile.am
@@ -29,14 +29,22 @@ librsvginclude_HEADERS= \
librsvg_la_SOURCES= \
rsvg.c \
rsvg.h \
- rsvg-path.c \
- rsvg-path.h \
rsvg-bpath-util.c \
rsvg-bpath-util.h \
- art_rgba.c \
- art_rgba.h \
- art_rgba_svp.c \
- art_rgba_svp.h \
+ rsvg-css.c \
+ rsvg-css.h \
+ rsvg-defs.c \
+ rsvg-defs.h \
+ rsvg-paint-server.c \
+ rsvg-paint-server.h \
+ rsvg-path.c \
+ rsvg-path.h \
+ art_render.c \
+ art_render.h \
+ art_render_gradient.c \
+ art_render_gradient.h \
+ art_render_svp.c \
+ art_render_svp.h \
$(NULL)
test_rsvg_SOURCES =\
diff --git a/librsvg/art_render.c b/librsvg/art_render.c
new file mode 100644
index 000000000..8a6c68879
--- /dev/null
+++ b/librsvg/art_render.c
@@ -0,0 +1,1140 @@
+/* This file is adapted from art_render.c in Libart version 2.3.0 */
+
+#include <libart_lgpl/libart-features.h>
+
+#if LIBART_MAJOR_VERSION == 2 && LIBART_MINOR_VERSION < 2
+
+/* Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ */
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_rgb.h>
+
+#include "art_render.h"
+
+typedef struct _ArtRenderPriv ArtRenderPriv;
+
+struct _ArtRenderPriv {
+ ArtRender super;
+
+ ArtImageSource *image_source;
+
+ int n_mask_source;
+ ArtMaskSource **mask_source;
+
+ int n_callbacks;
+ ArtRenderCallback **callbacks;
+};
+
+ArtRender *
+art_render_new (int x0, int y0, int x1, int y1,
+ art_u8 *pixels, int rowstride,
+ int n_chan, int depth, ArtAlphaType alpha_type,
+ ArtAlphaGamma *alphagamma)
+{
+ ArtRenderPriv *priv;
+ ArtRender *result;
+
+ priv = art_new (ArtRenderPriv, 1);
+ result = &priv->super;
+
+ if (n_chan > ART_MAX_CHAN)
+ {
+ art_warn ("art_render_new: n_chan = %d, exceeds %d max\n",
+ n_chan, ART_MAX_CHAN);
+ return NULL;
+ }
+ if (depth > ART_MAX_DEPTH)
+ {
+ art_warn ("art_render_new: depth = %d, exceeds %d max\n",
+ depth, ART_MAX_DEPTH);
+ return NULL;
+ }
+ if (x0 >= x1)
+ {
+ art_warn ("art_render_new: x0 >= x1 (x0 = %d, x1 = %d)\n", x0, x1);
+ return NULL;
+ }
+ result->x0 = x0;
+ result->y0 = y0;
+ result->x1 = x1;
+ result->y1 = y1;
+ result->pixels = pixels;
+ result->rowstride = rowstride;
+ result->n_chan = n_chan;
+ result->depth = depth;
+ result->alpha_type = alpha_type;
+
+ result->clear = ART_FALSE;
+ result->opacity = 0x10000;
+ result->compositing_mode = ART_COMPOSITE_NORMAL;
+ result->alphagamma = alphagamma;
+
+ result->alpha_buf = NULL;
+ result->image_buf = NULL;
+
+ result->run = NULL;
+ result->span_x = NULL;
+
+ result->need_span = ART_FALSE;
+
+ priv->image_source = NULL;
+
+ priv->n_mask_source = 0;
+ priv->mask_source = NULL;
+
+ return result;
+}
+
+/* todo on clear routines: I haven't really figured out what to do
+ with clearing the alpha channel. It _should_ be possible to clear
+ to an arbitrary RGBA color. */
+
+/**
+ * art_render_clear: Set clear color.
+ * @clear_color: Color with which to clear dest.
+ *
+ * Sets clear color, equivalent to actually clearing the destination
+ * buffer before rendering. This is the most general form.
+ **/
+void
+art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color)
+{
+ int i;
+ int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+
+ render->clear = ART_TRUE;
+ for (i = 0; i < n_ch; i++)
+ render->clear_color[i] = clear_color[i];
+}
+
+/**
+ * art_render_clear_rgb: Set clear color, given in RGB format.
+ * @clear_rgb: Clear color, in 0xRRGGBB format.
+ *
+ * Sets clear color, equivalent to actually clearing the destination
+ * buffer before rendering.
+ **/
+void
+art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb)
+{
+ if (render->n_chan != 3)
+ art_warn ("art_render_clear_rgb: called on render with %d channels, only works with 3\n",
+ render->n_chan);
+ else
+ {
+ int r, g, b;
+
+ render->clear = ART_TRUE;
+ r = clear_rgb >> 16;
+ g = (clear_rgb >> 8) & 0xff;
+ b = clear_rgb & 0xff;
+ render->clear_color[0] = ART_PIX_MAX_FROM_8(r);
+ render->clear_color[1] = ART_PIX_MAX_FROM_8(g);
+ render->clear_color[2] = ART_PIX_MAX_FROM_8(b);
+ }
+}
+
+static void
+art_render_nop_done (ArtRenderCallback *self, ArtRender *render)
+{
+}
+
+static void
+art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ int width = render->x1 - render->x0;
+ art_u8 r, g, b;
+ ArtPixMaxDepth color_max;
+
+ color_max = render->clear_color[0];
+ r = ART_PIX_8_FROM_MAX (color_max);
+ color_max = render->clear_color[1];
+ g = ART_PIX_8_FROM_MAX (color_max);
+ color_max = render->clear_color[2];
+ b = ART_PIX_8_FROM_MAX (color_max);
+
+ art_rgb_fill_run (dest, r, g, b, width);
+}
+
+static void
+art_render_clear_render_8 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ int width = render->x1 - render->x0;
+ int i, j;
+ int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+ int ix;
+ art_u8 color[ART_MAX_CHAN + 1];
+
+ for (j = 0; j < n_ch; j++)
+ {
+ ArtPixMaxDepth color_max = render->clear_color[j];
+ color[j] = ART_PIX_8_FROM_MAX (color_max);
+ }
+
+ ix = 0;
+ for (i = 0; i < width; i++)
+ for (j = 0; j < n_ch; j++)
+ dest[ix++] = color[j];
+}
+
+const ArtRenderCallback art_render_clear_rgb8_obj =
+{
+ art_render_clear_render_rgb8,
+ art_render_nop_done
+};
+
+const ArtRenderCallback art_render_clear_8_obj =
+{
+ art_render_clear_render_8,
+ art_render_nop_done
+};
+
+#if ART_MAX_DEPTH >= 16
+
+static void
+art_render_clear_render_16 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ int width = render->x1 - render->x0;
+ int i, j;
+ int n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
+ int ix;
+ art_u16 *dest_16 = (art_u16 *)dest;
+ art_u8 color[ART_MAX_CHAN + 1];
+
+ for (j = 0; j < n_ch; j++)
+ {
+ int color_16 = render->clear_color[j];
+ color[j] = color_16;
+ }
+
+ ix = 0;
+ for (i = 0; i < width; i++)
+ for (j = 0; j < n_ch; j++)
+ dest_16[ix++] = color[j];
+}
+
+const ArtRenderCallback art_render_clear_16_obj =
+{
+ art_render_clear_render_16,
+ art_render_nop_done
+};
+
+#endif /* ART_MAX_DEPTH >= 16 */
+
+/* todo: inline */
+static ArtRenderCallback *
+art_render_choose_clear_callback (ArtRender *render)
+{
+ ArtRenderCallback *clear_callback;
+
+ if (render->depth == 8)
+ {
+ if (render->n_chan == 3 &&
+ render->alpha_type == ART_ALPHA_NONE)
+ clear_callback = (ArtRenderCallback *)&art_render_clear_rgb8_obj;
+ else
+ clear_callback = (ArtRenderCallback *)&art_render_clear_8_obj;
+ }
+#if ART_MAX_DEPTH >= 16
+ else if (render->depth == 16)
+ clear_callback = (ArtRenderCallback *)&art_render_clear_16_obj;
+#endif
+ else
+ {
+ art_die ("art_render_choose_clear_callback: inconsistent render->depth = %d\n",
+ render->depth);
+ }
+ return clear_callback;
+}
+
+#if 0
+/* todo: get around to writing this */
+static void
+art_render_composite_render_noa_8_norm (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ int width = render->x1 - render->x0;
+
+}
+#endif
+
+/* This is the most general form of the function. It is slow but
+ (hopefully) correct. Actually, I'm still worried about roundoff
+ errors in the premul case - it seems to me that an off-by-one could
+ lead to overflow. */
+static void
+art_render_composite (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtRenderMaskRun *run = render->run;
+ art_u32 depth = render->depth;
+ int n_run = render->n_run;
+ int x0 = render->x0;
+ int x;
+ int run_x0, run_x1;
+ art_u8 *alpha_buf = render->alpha_buf;
+ art_u8 *image_buf = render->image_buf;
+ int i, j;
+ art_u32 tmp;
+ art_u32 run_alpha;
+ art_u32 alpha;
+ int image_ix;
+ art_u16 src[ART_MAX_CHAN + 1];
+ art_u16 dst[ART_MAX_CHAN + 1];
+ int n_chan = render->n_chan;
+ ArtAlphaType alpha_type = render->alpha_type;
+ int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
+ int dst_pixstride = n_ch * (depth >> 3);
+ int buf_depth = render->buf_depth;
+ ArtAlphaType buf_alpha = render->buf_alpha;
+ int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
+ int buf_pixstride = buf_n_ch * (buf_depth >> 3);
+ art_u8 *bufptr;
+ art_u32 src_alpha;
+ art_u32 src_mul;
+ art_u8 *dstptr;
+ art_u32 dst_alpha;
+ art_u32 dst_mul;
+
+ image_ix = 0;
+ for (i = 0; i < n_run - 1; i++)
+ {
+ run_x0 = run[i].x;
+ run_x1 = run[i + 1].x;
+ tmp = run[i].alpha;
+ if (tmp < 0x8100)
+ continue;
+
+ run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+ bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
+ dstptr = dest + (run_x0 - x0) * dst_pixstride;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ if (alpha_buf)
+ {
+ if (depth == 8)
+ {
+ tmp = run_alpha * alpha_buf[x - x0] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+ }
+ else /* (depth == 16) */
+ {
+ tmp = ((art_u16 *)alpha_buf)[x - x0];
+ tmp = (run_alpha * tmp + 0x8000) >> 8;
+ /* range 0x80 .. 0xffff80 */
+ alpha = (tmp + (tmp >> 16)) >> 8;
+ }
+ }
+ else
+ alpha = run_alpha;
+ /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
+
+ /* convert (src pixel * alpha) to premul alpha form,
+ store in src as 0..0xffff range */
+ if (buf_alpha == ART_ALPHA_NONE)
+ {
+ src_alpha = run_alpha;
+ src_mul = src_alpha;
+ }
+ else
+ {
+ if (buf_depth == 8)
+ {
+ tmp = run_alpha * bufptr[n_chan] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+ }
+ else /* (depth == 16) */
+ {
+ tmp = ((art_u16 *)bufptr)[n_chan];
+ tmp = (run_alpha * tmp + 0x8000) >> 8;
+ /* range 0x80 .. 0xffff80 */
+ src_alpha = (tmp + (tmp >> 16)) >> 8;
+ }
+ if (buf_alpha == ART_ALPHA_SEPARATE)
+ src_mul = src_alpha;
+ else /* buf_alpha == (ART_ALPHA_PREMUL) */
+ src_mul = run_alpha;
+ }
+ /* src_alpha is the (alpha of the source pixel * alpha),
+ range 0..0x10000 */
+
+ if (buf_depth == 8)
+ {
+ src_mul *= 0x101;
+ for (j = 0; j < n_chan; j++)
+ src[j] = (bufptr[j] * src_mul + 0x8000) >> 16;
+ }
+ else if (buf_depth == 16)
+ {
+ for (j = 0; j < n_chan; j++)
+ src[j] = (((art_u16 *)bufptr)[j] * src_mul + 0x8000) >> 16;
+ }
+ bufptr += buf_pixstride;
+
+ /* src[0..n_chan - 1] (range 0..0xffff) and src_alpha (range
+ 0..0x10000) now contain the source pixel with
+ premultiplied alpha */
+
+ /* convert dst pixel to premul alpha form,
+ store in dst as 0..0xffff range */
+ if (alpha_type == ART_ALPHA_NONE)
+ {
+ dst_alpha = 0x10000;
+ dst_mul = dst_alpha;
+ }
+ else
+ {
+ if (depth == 8)
+ {
+ tmp = dstptr[n_chan];
+ /* range 0..0xff */
+ dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+ }
+ else /* (depth == 16) */
+ {
+ tmp = ((art_u16 *)bufptr)[n_chan];
+ dst_alpha = (tmp + (tmp >> 15));
+ }
+ if (alpha_type == ART_ALPHA_SEPARATE)
+ dst_mul = dst_alpha;
+ else /* (alpha_type == ART_ALPHA_PREMUL) */
+ dst_mul = 0x10000;
+ }
+ /* dst_alpha is the alpha of the dest pixel,
+ range 0..0x10000 */
+
+ if (depth == 8)
+ {
+ dst_mul *= 0x101;
+ for (j = 0; j < n_chan; j++)
+ dst[j] = (dstptr[j] * dst_mul + 0x8000) >> 16;
+ }
+ else if (buf_depth == 16)
+ {
+ for (j = 0; j < n_chan; j++)
+ dst[j] = (((art_u16 *)dstptr)[j] * dst_mul + 0x8000) >> 16;
+ }
+
+ /* do the compositing, dst = (src over dst) */
+ for (j = 0; j < n_chan; j++)
+ {
+ art_u32 srcv, dstv;
+ art_u32 tmp;
+
+ srcv = src[j];
+ dstv = dst[j];
+ tmp = ((dstv * (0x10000 - src_alpha) + 0x8000) >> 16) + srcv;
+ tmp -= tmp >> 16;
+ dst[j] = tmp;
+ }
+
+ if (alpha_type == ART_ALPHA_NONE)
+ {
+ if (depth == 8)
+ dst_mul = 0xff;
+ else /* (depth == 16) */
+ dst_mul = 0xffff;
+ }
+ else
+ {
+ if (src_alpha >= 0x10000)
+ dst_alpha = 0x10000;
+ else
+ dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+ if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
+ {
+ if (depth == 8)
+ dst_mul = 0xff;
+ else /* (depth == 16) */
+ dst_mul = 0xffff;
+ }
+ else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
+ {
+ if (depth == 8)
+ dst_mul = 0xff0000 / dst_alpha;
+ else /* (depth == 16) */
+ dst_mul = 0xffff0000 / dst_alpha;
+ }
+ }
+ if (depth == 8)
+ {
+ for (j = 0; j < n_chan; j++)
+ dstptr[j] = (dst[j] * dst_mul + 0x8000) >> 16;
+ if (alpha_type != ART_ALPHA_NONE)
+ dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
+ }
+ else if (depth == 16)
+ {
+ for (j = 0; j < n_chan; j++)
+ ((art_u16 *)dstptr)[j] = (dst[j] * dst_mul + 0x8000) >> 16;
+ if (alpha_type != ART_ALPHA_NONE)
+ dstptr[n_chan] = (dst_alpha * 0xffff + 0x8000) >> 16;
+ }
+ dstptr += dst_pixstride;
+ }
+ }
+}
+
+const ArtRenderCallback art_render_composite_obj =
+{
+ art_render_composite,
+ art_render_nop_done
+};
+
+static void
+art_render_composite_8 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtRenderMaskRun *run = render->run;
+ int n_run = render->n_run;
+ int x0 = render->x0;
+ int x;
+ int run_x0, run_x1;
+ art_u8 *alpha_buf = render->alpha_buf;
+ art_u8 *image_buf = render->image_buf;
+ int i, j;
+ art_u32 tmp;
+ art_u32 run_alpha;
+ art_u32 alpha;
+ int image_ix;
+ int n_chan = render->n_chan;
+ ArtAlphaType alpha_type = render->alpha_type;
+ int n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
+ int dst_pixstride = n_ch;
+ ArtAlphaType buf_alpha = render->buf_alpha;
+ int buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
+ int buf_pixstride = buf_n_ch;
+ art_u8 *bufptr;
+ art_u32 src_alpha;
+ art_u32 src_mul;
+ art_u8 *dstptr;
+ art_u32 dst_alpha;
+ art_u32 dst_mul, dst_save_mul;
+
+ image_ix = 0;
+ for (i = 0; i < n_run - 1; i++)
+ {
+ run_x0 = run[i].x;
+ run_x1 = run[i + 1].x;
+ tmp = run[i].alpha;
+ if (tmp < 0x10000)
+ continue;
+
+ run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8; /* range [0 .. 0x10000] */
+ bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
+ dstptr = dest + (run_x0 - x0) * dst_pixstride;
+ for (x = run_x0; x < run_x1; x++)
+ {
+ if (alpha_buf)
+ {
+ tmp = run_alpha * alpha_buf[x - x0] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+ }
+ else
+ alpha = run_alpha;
+ /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */
+
+ /* convert (src pixel * alpha) to premul alpha form,
+ store in src as 0..0xffff range */
+ if (buf_alpha == ART_ALPHA_NONE)
+ {
+ src_alpha = run_alpha;
+ src_mul = src_alpha;
+ }
+ else
+ {
+ tmp = run_alpha * bufptr[n_chan] + 0x80;
+ /* range 0x80 .. 0xff0080 */
+ src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
+
+ if (buf_alpha == ART_ALPHA_SEPARATE)
+ src_mul = src_alpha;
+ else /* buf_alpha == (ART_ALPHA_PREMUL) */
+ src_mul = run_alpha;
+ }
+ /* src_alpha is the (alpha of the source pixel * alpha),
+ range 0..0x10000 */
+
+ src_mul *= 0x101;
+
+ if (alpha_type == ART_ALPHA_NONE)
+ {
+ dst_alpha = 0x10000;
+ dst_mul = dst_alpha;
+ }
+ else
+ {
+ tmp = dstptr[n_chan];
+ /* range 0..0xff */
+ dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
+ if (alpha_type == ART_ALPHA_SEPARATE)
+ dst_mul = dst_alpha;
+ else /* (alpha_type == ART_ALPHA_PREMUL) */
+ dst_mul = 0x10000;
+ }
+ /* dst_alpha is the alpha of the dest pixel,
+ range 0..0x10000 */
+
+ dst_mul *= 0x101;
+
+ if (alpha_type == ART_ALPHA_NONE)
+ {
+ dst_save_mul = 0xff;
+ }
+ else
+ {
+ if (src_alpha >= 0x10000)
+ dst_alpha = 0x10000;
+ else
+ dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
+ if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
+ {
+ dst_save_mul = 0xff;
+ }
+ else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
+ {
+ dst_save_mul = 0xff0000 / dst_alpha;
+ }
+ }
+ for (j = 0; j < n_chan; j++)
+ {
+ art_u32 src, dst;
+ art_u32 tmp;
+
+ src = (bufptr[j] * src_mul + 0x8000) >> 16;
+ dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
+ tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
+ tmp -= tmp >> 16;
+ dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
+ }
+ if (alpha_type != ART_ALPHA_NONE)
+ dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
+
+ bufptr += buf_pixstride;
+ dstptr += dst_pixstride;
+ }
+ }
+}
+
+const ArtRenderCallback art_render_composite_8_obj =
+{
+ art_render_composite_8,
+ art_render_nop_done
+};
+
+/* todo: inline */
+static ArtRenderCallback *
+art_render_choose_compositing_callback (ArtRender *render)
+{
+ if (render->depth == 8 && render->buf_depth == 8)
+ return (ArtRenderCallback *)&art_render_composite_8_obj;
+ return (ArtRenderCallback *)&art_render_composite_obj;
+}
+
+/**
+ * art_render_invoke_callbacks: Invoke the callbacks in the render object.
+ * @render: The render object.
+ * @y: The current Y coordinate value.
+ *
+ * Invokes the callbacks of the render object in the appropriate
+ * order. Drivers should call this routine once per scanline.
+ *
+ * todo: should management of dest devolve to this routine? very
+ * plausibly yes.
+ **/
+void
+art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y)
+{
+ ArtRenderPriv *priv = (ArtRenderPriv *)render;
+ int i;
+
+ for (i = 0; i < priv->n_callbacks; i++)
+ {
+ ArtRenderCallback *callback;
+
+ callback = priv->callbacks[i];
+ callback->render (callback, render, dest, y);
+ }
+}
+
+/**
+ * art_render_invoke: Perform the requested rendering task.
+ * @render: The render object.
+ *
+ * Invokes the renderer and all sources associated with it, to perform
+ * the requested rendering task.
+ **/
+void
+art_render_invoke (ArtRender *render)
+{
+ ArtRenderPriv *priv = (ArtRenderPriv *)render;
+ int width;
+ int best_driver, best_score;
+ int i;
+ int n_callbacks, n_callbacks_max;
+ ArtImageSource *image_source;
+ ArtImageSourceFlags image_flags;
+ int buf_depth;
+ ArtAlphaType buf_alpha;
+ art_boolean first = ART_TRUE;
+
+ if (render == NULL)
+ {
+ art_warn ("art_render_invoke: called with render == NULL\n");
+ return;
+ }
+ if (priv->image_source == NULL)
+ {
+ art_warn ("art_render_invoke: no image source given\n");
+ return;
+ }
+
+ width = render->x1 - render->x0;
+
+ render->run = art_new (ArtRenderMaskRun, width + 1);
+
+ /* Elect a mask source as driver. */
+ best_driver = -1;
+ best_score = 0;
+ for (i = 0; i < priv->n_mask_source; i++)
+ {
+ int score;
+ ArtMaskSource *mask_source;
+
+ mask_source = priv->mask_source[i];
+ score = mask_source->can_drive (mask_source, render);
+ if (score > best_score)
+ {
+ best_score = score;
+ best_driver = i;
+ }
+ }
+
+ /* Allocate alpha buffer if needed. */
+ if (priv->n_mask_source > 1 ||
+ (priv->n_mask_source == 1 && best_driver < 0))
+ {
+ render->alpha_buf = art_new (art_u8, (width * render->depth) >> 3);
+ }
+
+ /* Negotiate image rendering and compositing. */
+ image_source = priv->image_source;
+ image_source->negotiate (image_source, render, &image_flags, &buf_depth,
+ &buf_alpha);
+
+ /* Build callback list. */
+ n_callbacks_max = priv->n_mask_source + 3;
+ priv->callbacks = art_new (ArtRenderCallback *, n_callbacks_max);
+ n_callbacks = 0;
+ for (i = 0; i < priv->n_mask_source; i++)
+ if (i != best_driver)
+ {
+ ArtMaskSource *mask_source = priv->mask_source[i];
+
+ mask_source->prepare (mask_source, render, first);
+ first = ART_FALSE;
+ priv->callbacks[n_callbacks++] = &mask_source->super;
+ }
+
+ if (render->clear && !(image_flags & ART_IMAGE_SOURCE_CAN_CLEAR))
+ priv->callbacks[n_callbacks++] =
+ art_render_choose_clear_callback (render);
+
+ priv->callbacks[n_callbacks++] = &image_source->super;
+
+ /* Allocate image buffer and add compositing callback if needed. */
+ if (!(image_flags & ART_IMAGE_SOURCE_CAN_COMPOSITE))
+ {
+ int bytespp = ((render->n_chan + (buf_alpha != ART_ALPHA_NONE)) *
+ buf_depth) >> 3;
+ render->buf_depth = buf_depth;
+ render->buf_alpha = buf_alpha;
+ render->image_buf = art_new (art_u8, width * bytespp);
+ priv->callbacks[n_callbacks++] =
+ art_render_choose_compositing_callback (render);
+ }
+
+ priv->n_callbacks = n_callbacks;
+
+ if (render->need_span)
+ render->span_x = art_new (int, width + 1);
+
+ /* Invoke the driver */
+ if (best_driver >= 0)
+ {
+ ArtMaskSource *driver;
+
+ driver = priv->mask_source[best_driver];
+ driver->invoke_driver (driver, render);
+ }
+ else
+ {
+ art_u8 *dest_ptr = render->pixels;
+ int y;
+
+ /* Dummy driver */
+ render->n_run = 2;
+ render->run[0].x = render->x0;
+ render->run[0].alpha = 0x8000 + 0xff * render->opacity;
+ render->run[1].x = render->x1;
+ render->run[1].alpha = 0x8000;
+ if (render->need_span)
+ {
+ render->n_span = 2;
+ render->span_x[0] = render->x0;
+ render->span_x[1] = render->x1;
+ }
+ for (y = render->y0; y < render->y1; y++)
+ {
+ art_render_invoke_callbacks (render, dest_ptr, y);
+ dest_ptr += render->rowstride;
+ }
+ }
+
+ if (priv->mask_source != NULL)
+ art_free (priv->mask_source);
+
+ /* clean up callbacks */
+ for (i = 0; i < priv->n_callbacks; i++)
+ {
+ ArtRenderCallback *callback;
+
+ callback = priv->callbacks[i];
+ callback->done (callback, render);
+ }
+
+ /* Tear down object */
+ if (render->alpha_buf != NULL)
+ art_free (render->alpha_buf);
+ if (render->image_buf != NULL)
+ art_free (render->image_buf);
+ art_free (render->run);
+ if (render->span_x != NULL)
+ art_free (render->span_x);
+ art_free (priv->callbacks);
+ art_free (render);
+}
+
+/**
+ * art_render_mask_solid: Add a solid translucent mask.
+ * @render: The render object.
+ * @opacity: Opacity in [0..0x10000] form.
+ *
+ * Adds a translucent mask to the rendering object.
+ **/
+void
+art_render_mask_solid (ArtRender *render, int opacity)
+{
+ art_u32 old_opacity = render->opacity;
+ art_u32 new_opacity_tmp;
+
+ if (opacity == 0x10000)
+ /* avoid potential overflow */
+ return;
+ new_opacity_tmp = old_opacity * (art_u32)opacity + 0x8000;
+ render->opacity = new_opacity_tmp >> 16;
+}
+
+/**
+ * art_render_add_mask_source: Add a mask source to the render object.
+ * @render: Render object.
+ * @mask_source: Mask source to add.
+ *
+ * This routine adds a mask source to the render object. In general,
+ * client api's for adding mask sources should just take a render object,
+ * then the mask source creation function should call this function.
+ * Clients should never have to call this function directly, unless of
+ * course they're creating custom mask sources.
+ **/
+void
+art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source)
+{
+ ArtRenderPriv *priv = (ArtRenderPriv *)render;
+ int n_mask_source = priv->n_mask_source++;
+
+ if (n_mask_source == 0)
+ priv->mask_source = art_new (ArtMaskSource *, 1);
+ /* This predicate is true iff n_mask_source is a power of two */
+ else if (!(n_mask_source & (n_mask_source - 1)))
+ priv->mask_source = art_renew (priv->mask_source, ArtMaskSource *,
+ n_mask_source << 1);
+
+ priv->mask_source[n_mask_source] = mask_source;
+}
+
+/**
+ * art_render_add_image_source: Add a mask source to the render object.
+ * @render: Render object.
+ * @image_source: Image source to add.
+ *
+ * This routine adds an image source to the render object. In general,
+ * client api's for adding image sources should just take a render
+ * object, then the mask source creation function should call this
+ * function. Clients should never have to call this function
+ * directly, unless of course they're creating custom image sources.
+ **/
+void
+art_render_add_image_source (ArtRender *render, ArtImageSource *image_source)
+{
+ ArtRenderPriv *priv = (ArtRenderPriv *)render;
+
+ if (priv->image_source != NULL)
+ {
+ art_warn ("art_render_add_image_source: image source already present.\n");
+ return;
+ }
+ priv->image_source = image_source;
+}
+
+/* Solid image source object and methods. Perhaps this should go into a
+ separate file. */
+
+typedef struct _ArtImageSourceSolid ArtImageSourceSolid;
+
+struct _ArtImageSourceSolid {
+ ArtImageSource super;
+ ArtPixMaxDepth color[ART_MAX_CHAN];
+ art_u32 *rgbtab;
+ art_boolean init;
+};
+
+static void
+art_render_image_solid_done (ArtRenderCallback *self, ArtRender *render)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+
+ if (z->rgbtab != NULL)
+ art_free (z->rgbtab);
+ art_free (self);
+}
+
+static void
+art_render_image_solid_rgb8_opaq_init (ArtImageSourceSolid *self, ArtRender *render)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+ ArtPixMaxDepth color_max;
+ int r_fg, g_fg, b_fg;
+ int r_bg, g_bg, b_bg;
+ int r, g, b;
+ int dr, dg, db;
+ int i;
+ int tmp;
+ art_u32 *rgbtab;
+
+ rgbtab = art_new (art_u32, 256);
+ z->rgbtab = rgbtab;
+
+ color_max = self->color[0];
+ r_fg = ART_PIX_8_FROM_MAX (color_max);
+ color_max = self->color[1];
+ g_fg = ART_PIX_8_FROM_MAX (color_max);
+ color_max = self->color[2];
+ b_fg = ART_PIX_8_FROM_MAX (color_max);
+
+ color_max = render->clear_color[0];
+ r_bg = ART_PIX_8_FROM_MAX (color_max);
+ color_max = render->clear_color[1];
+ g_bg = ART_PIX_8_FROM_MAX (color_max);
+ color_max = render->clear_color[2];
+ b_bg = ART_PIX_8_FROM_MAX (color_max);
+
+ r = (r_bg << 16) + 0x8000;
+ g = (g_bg << 16) + 0x8000;
+ b = (b_bg << 16) + 0x8000;
+ tmp = ((r_fg - r_bg) << 16) + 0x80;
+ dr = (tmp + (tmp >> 8)) >> 8;
+ tmp = ((g_fg - g_bg) << 16) + 0x80;
+ dg = (tmp + (tmp >> 8)) >> 8;
+ tmp = ((b_fg - b_bg) << 16) + 0x80;
+ db = (tmp + (tmp >> 8)) >> 8;
+
+ for (i = 0; i < 256; i++)
+ {
+ rgbtab[i] = (r & 0xff0000) | ((g & 0xff0000) >> 8) | (b >> 16);
+ r += dr;
+ g += dg;
+ b += db;
+ }
+}
+
+static void
+art_render_image_solid_rgb8_opaq (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+ ArtRenderMaskRun *run = render->run;
+ int n_run = render->n_run;
+ art_u32 *rgbtab = z->rgbtab;
+ art_u32 rgb;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ int i;
+ int ix;
+
+ if (n_run > 0)
+ {
+ run_x1 = run[0].x;
+ if (run_x1 > x0)
+ {
+ rgb = rgbtab[0];
+ art_rgb_fill_run (dest,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ run_x1 - x0);
+ }
+ for (i = 0; i < n_run - 1; i++)
+ {
+ run_x0 = run_x1;
+ run_x1 = run[i + 1].x;
+ rgb = rgbtab[(run[i].alpha >> 16) & 0xff];
+ ix = (run_x0 - x0) * 3;
+#define OPTIMIZE_LEN_1
+#ifdef OPTIMIZE_LEN_1
+ if (run_x1 - run_x0 == 1)
+ {
+ dest[ix] = rgb >> 16;
+ dest[ix + 1] = (rgb >> 8) & 0xff;
+ dest[ix + 2] = rgb & 0xff;
+ }
+ else
+ {
+ art_rgb_fill_run (dest + ix,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ run_x1 - run_x0);
+ }
+#else
+ art_rgb_fill_run (dest + ix,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ run_x1 - run_x0);
+#endif
+ }
+ }
+ else
+ {
+ run_x1 = x0;
+ }
+ if (run_x1 < x1)
+ {
+ rgb = rgbtab[0];
+ art_rgb_fill_run (dest + (run_x1 - x0) * 3,
+ rgb >> 16, (rgb >> 8) & 0xff, rgb & 0xff,
+ x1 - run_x1);
+ }
+}
+
+static void
+art_render_image_solid_rgb8 (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+ int width = render->x1 - render->x0;
+ art_u8 r, g, b;
+ ArtPixMaxDepth color_max;
+
+ /* todo: replace this simple test with real sparseness */
+ if (z->init)
+ return;
+ z->init = ART_TRUE;
+
+ color_max = z->color[0];
+ r = ART_PIX_8_FROM_MAX (color_max);
+ color_max = z->color[1];
+ g = ART_PIX_8_FROM_MAX (color_max);
+ color_max = z->color[2];
+ b = ART_PIX_8_FROM_MAX (color_max);
+
+ art_rgb_fill_run (render->image_buf, r, g, b, width);
+}
+
+static void
+art_render_image_solid_negotiate (ArtImageSource *self, ArtRender *render,
+ ArtImageSourceFlags *p_flags,
+ int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+ ArtImageSourceSolid *z = (ArtImageSourceSolid *)self;
+ ArtImageSourceFlags flags = 0;
+ static void (*render_cbk) (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y);
+
+ render_cbk = NULL;
+
+ if (render->depth == 8 && render->n_chan == 3 &&
+ render->alpha_type == ART_ALPHA_NONE)
+ {
+ if (render->clear)
+ {
+ render_cbk = art_render_image_solid_rgb8_opaq;
+ flags |= ART_IMAGE_SOURCE_CAN_CLEAR;
+ art_render_image_solid_rgb8_opaq_init (z, render);
+ }
+ flags |= ART_IMAGE_SOURCE_CAN_COMPOSITE;
+ }
+ if (render_cbk == NULL)
+ {
+ if (render->depth == 8)
+ {
+ render_cbk = art_render_image_solid_rgb8;
+ *p_buf_depth = 8;
+ *p_alpha = ART_ALPHA_NONE; /* todo */
+ }
+ }
+ /* todo: general case */
+ self->super.render = render_cbk;
+ *p_flags = flags;
+}
+
+/**
+ * art_render_image_solid: Add a solid color image source.
+ * @render: The render object.
+ * @color: Color.
+ *
+ * Adds an image source with the solid color given by @color. The
+ * color need not be retained in memory after this call.
+ **/
+void
+art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color)
+{
+ ArtImageSourceSolid *image_source;
+ int i;
+
+ image_source = art_new (ArtImageSourceSolid, 1);
+ image_source->super.super.render = NULL;
+ image_source->super.super.done = art_render_image_solid_done;
+ image_source->super.negotiate = art_render_image_solid_negotiate;
+
+ for (i = 0; i < render->n_chan; i++)
+ image_source->color[i] = color[i];
+
+ image_source->rgbtab = NULL;
+ image_source->init = ART_FALSE;
+
+ art_render_add_image_source (render, &image_source->super);
+}
+
+#endif
diff --git a/librsvg/art_render.h b/librsvg/art_render.h
new file mode 100644
index 000000000..7bba3e9cf
--- /dev/null
+++ b/librsvg/art_render.h
@@ -0,0 +1,188 @@
+/* This file is adapted from art_render.h in Libart version 2.3.0 */
+
+#include <libart_lgpl/libart-features.h>
+
+#if LIBART_MAJOR_VERSION == 2 && LIBART_MINOR_VERSION < 2
+
+/*
+ * art_render.h: Modular rendering architecture.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ART_RENDER_H__
+#define __ART_RENDER_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* Render object */
+
+#ifndef ART_MAX_DEPTH
+#define ART_MAX_DEPTH 16
+#endif
+
+#if ART_MAX_DEPTH == 16
+typedef art_u16 ArtPixMaxDepth;
+#define ART_PIX_MAX_FROM_8(x) ((x) | ((x) << 8))
+#define ART_PIX_8_FROM_MAX(x) (((x) + 0x80 - (((x) + 0x80) >> 8)) >> 8)
+#else
+#if ART_MAX_DEPTH == 8
+typedef art_u8 ArtPixMaxDepth;
+#define ART_PIX_MAX_FROM_8(x) (x)
+#define ART_PIX_8_FROM_MAX(x) (x)
+#else
+#error ART_MAX_DEPTH must be either 8 or 16
+#endif
+#endif
+
+#define ART_MAX_CHAN 16
+
+typedef struct _ArtRender ArtRender;
+typedef struct _ArtRenderCallback ArtRenderCallback;
+typedef struct _ArtRenderMaskRun ArtRenderMaskRun;
+typedef struct _ArtImageSource ArtImageSource;
+typedef struct _ArtMaskSource ArtMaskSource;
+
+typedef enum {
+ ART_ALPHA_NONE = 0,
+ ART_ALPHA_SEPARATE = 1,
+ ART_ALPHA_PREMUL = 2
+} ArtAlphaType;
+
+typedef enum {
+ ART_COMPOSITE_NORMAL,
+ ART_COMPOSITE_MULTIPLY,
+ /* todo: more */
+ ART_COMPOSITE_CUSTOM
+} ArtCompositingMode;
+
+typedef enum {
+ ART_IMAGE_SOURCE_CAN_CLEAR = 1,
+ ART_IMAGE_SOURCE_CAN_COMPOSITE = 2
+} ArtImageSourceFlags;
+
+struct _ArtRenderMaskRun {
+ int x;
+ int alpha;
+};
+
+struct _ArtRenderCallback {
+ void (*render) (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y);
+ void (*done) (ArtRenderCallback *self, ArtRender *render);
+};
+
+struct _ArtImageSource {
+ ArtRenderCallback super;
+ void (*negotiate) (ArtImageSource *self, ArtRender *render,
+ ArtImageSourceFlags *p_flags,
+ int *p_buf_depth, ArtAlphaType *p_alpha_type);
+};
+
+struct _ArtMaskSource {
+ ArtRenderCallback super;
+ int (*can_drive) (ArtMaskSource *self, ArtRender *render);
+ /* For each mask source, ::prepare() is invoked if it is not
+ a driver, or ::invoke_driver() if it is. */
+ void (*invoke_driver) (ArtMaskSource *self, ArtRender *render);
+ void (*prepare) (ArtMaskSource *self, ArtRender *render, art_boolean first);
+};
+
+struct _ArtRender {
+ /* parameters of destination image */
+ int x0, y0;
+ int x1, y1;
+ art_u8 *pixels;
+ int rowstride;
+ int n_chan;
+ int depth;
+ ArtAlphaType alpha_type;
+
+ art_boolean clear;
+ ArtPixMaxDepth clear_color[ART_MAX_CHAN + 1];
+ art_u32 opacity; /* [0..0x10000] */
+
+ ArtCompositingMode compositing_mode;
+
+ ArtAlphaGamma *alphagamma;
+
+ art_u8 *alpha_buf;
+
+ /* parameters of intermediate buffer */
+ int buf_depth;
+ ArtAlphaType buf_alpha;
+ art_u8 *image_buf;
+
+ /* driving alpha scanline data */
+ /* A "run" is a contiguous sequence of x values with the same alpha value. */
+ int n_run;
+ ArtRenderMaskRun *run;
+
+ /* A "span" is a contiguous sequence of x values with non-zero alpha. */
+ int n_span;
+ int *span_x;
+
+ art_boolean need_span;
+};
+
+ArtRender *
+art_render_new (int x0, int y0, int x1, int y1,
+ art_u8 *pixels, int rowstride,
+ int n_chan, int depth, ArtAlphaType alpha_type,
+ ArtAlphaGamma *alphagamma);
+
+void
+art_render_invoke (ArtRender *render);
+
+void
+art_render_clear (ArtRender *render, const ArtPixMaxDepth *clear_color);
+
+void
+art_render_clear_rgb (ArtRender *render, art_u32 clear_rgb);
+
+void
+art_render_mask_solid (ArtRender *render, int opacity);
+
+void
+art_render_image_solid (ArtRender *render, ArtPixMaxDepth *color);
+
+/* The next two functions are for custom mask sources only. */
+void
+art_render_add_mask_source (ArtRender *render, ArtMaskSource *mask_source);
+
+void
+art_render_invoke_callbacks (ArtRender *render, art_u8 *dest, int y);
+
+/* The following function is for custom image sources only. */
+void
+art_render_add_image_source (ArtRender *render, ArtImageSource *image_source);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_H__ */
+
+#else
+
+#include <libart_lgpl/art_render.h>
+
+#endif
diff --git a/librsvg/art_render_gradient.c b/librsvg/art_render_gradient.c
new file mode 100644
index 000000000..9757e72c9
--- /dev/null
+++ b/librsvg/art_render_gradient.c
@@ -0,0 +1,303 @@
+/* This file is adapted from art_render_gradient.c in Libart version 2.3.0 */
+
+#include <libart_lgpl/libart-features.h>
+
+#if LIBART_MAJOR_VERSION == 2 && LIBART_MINOR_VERSION < 2
+
+/*
+ * art_render_gradient.c: Gradient image source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ * Alexander Larsson <alla@lysator.liu.se>
+ */
+
+#include <math.h>
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_filterlevel.h>
+
+#include "art_render.h"
+#include "art_render_gradient.h"
+
+typedef struct _ArtImageSourceGradLin ArtImageSourceGradLin;
+typedef struct _ArtImageSourceGradRad ArtImageSourceGradRad;
+
+struct _ArtImageSourceGradLin {
+ ArtImageSource super;
+ const ArtGradientLinear *gradient;
+};
+
+struct _ArtImageSourceGradRad {
+ ArtImageSource super;
+ const ArtGradientRadial *gradient;
+ double a;
+};
+
+#define EPSILON 1e-6
+
+/**
+ * art_render_gradient_setpix: Set a gradient pixel.
+ * @render: The render object.
+ * @dst: Pointer to destination (where to store pixel).
+ * @n_stops: Number of stops in @stops.
+ * @stops: The stops for the gradient.
+ * @offset: The offset.
+ *
+ * @n_stops must be > 0.
+ *
+ * Sets a gradient pixel, storing it at @dst.
+ **/
+static void
+art_render_gradient_setpix (ArtRender *render,
+ art_u8 *dst,
+ int n_stops, ArtGradientStop *stops,
+ double offset)
+{
+ int ix;
+ int j;
+ double off0, off1;
+ int n_ch = render->n_chan + 1;
+
+ for (ix = 0; ix < n_stops; ix++)
+ if (stops[ix].offset > offset)
+ break;
+ /* stops[ix - 1].offset < offset < stops[ix].offset */
+ if (ix > 0 && ix < n_stops)
+ {
+ off0 = stops[ix - 1].offset;
+ off1 = stops[ix].offset;
+ if (fabs (off1 - off0) > EPSILON)
+ {
+ double interp;
+
+ interp = (offset - off0) / (off1 - off0);
+ for (j = 0; j < n_ch; j++)
+ {
+ int z0, z1;
+ int z;
+ z0 = stops[ix - 1].color[j];
+ z1 = stops[ix].color[j];
+ z = floor (z0 + (z1 - z0) * interp + 0.5);
+ if (render->buf_depth == 8)
+ dst[j] = ART_PIX_8_FROM_MAX (z);
+ else /* (render->buf_depth == 16) */
+ ((art_u16 *)dst)[j] = z;
+ }
+ return;
+ }
+ }
+ else if (ix == n_stops)
+ ix--;
+
+ for (j = 0; j < n_ch + 1; j++)
+ {
+ int z;
+ z = stops[ix].color[j];
+ if (render->buf_depth == 8)
+ dst[j] = ART_PIX_8_FROM_MAX (z);
+ else /* (render->buf_depth == 16) */
+ ((art_u16 *)dst)[j] = z;
+ }
+}
+
+static void
+art_render_gradient_linear_done (ArtRenderCallback *self, ArtRender *render)
+{
+ art_free (self);
+}
+
+static void
+art_render_gradient_linear_render (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceGradLin *z = (ArtImageSourceGradLin *)self;
+ const ArtGradientLinear *gradient = z->gradient;
+ int pixstride = (render->n_chan + 1) * (render->depth >> 3);
+ int x;
+ int width = render->x1 - render->x0;
+ double offset, d_offset;
+ double actual_offset;
+ int n_stops = gradient->n_stops;
+ ArtGradientStop *stops = gradient->stops;
+ art_u8 *bufp = render->image_buf;
+ ArtGradientSpread spread = gradient->spread;
+
+ offset = render->x0 * gradient->a + y * gradient->b + gradient->c;
+ d_offset = gradient->a;
+
+ for (x = 0; x < width; x++)
+ {
+ if (spread == ART_GRADIENT_PAD)
+ actual_offset = offset;
+ else if (spread == ART_GRADIENT_REPEAT)
+ actual_offset = offset - floor (offset);
+ else /* (spread == ART_GRADIENT_REFLECT) */
+ {
+ double tmp;
+
+ tmp = offset - 2 * floor (0.5 * offset);
+ actual_offset = tmp > 1 ? 2 - tmp : tmp;
+ }
+ art_render_gradient_setpix (render, bufp, n_stops, stops, actual_offset);
+ offset += d_offset;
+ bufp += pixstride;
+ }
+}
+
+static void
+art_render_gradient_linear_negotiate (ArtImageSource *self, ArtRender *render,
+ ArtImageSourceFlags *p_flags,
+ int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+ self->super.render = art_render_gradient_linear_render;
+ *p_flags = 0;
+ *p_buf_depth = render->depth;
+ *p_alpha = ART_ALPHA_PREMUL;
+}
+
+/**
+ * art_render_gradient_linear: Add a linear gradient image source.
+ * @render: The render object.
+ * @gradient: The linear gradient.
+ *
+ * Adds the linear gradient @gradient as the image source for rendering
+ * in the render object @render.
+ **/
+void
+art_render_gradient_linear (ArtRender *render,
+ const ArtGradientLinear *gradient,
+ ArtFilterLevel level)
+{
+ ArtImageSourceGradLin *image_source = art_new (ArtImageSourceGradLin, 1);
+
+ image_source->super.super.render = NULL;
+ image_source->super.super.done = art_render_gradient_linear_done;
+ image_source->super.negotiate = art_render_gradient_linear_negotiate;
+
+ image_source->gradient = gradient;
+
+ art_render_add_image_source (render, &image_source->super);
+}
+
+static void
+art_render_gradient_radial_done (ArtRenderCallback *self, ArtRender *render)
+{
+ art_free (self);
+}
+
+static void
+art_render_gradient_radial_render (ArtRenderCallback *self, ArtRender *render,
+ art_u8 *dest, int y)
+{
+ ArtImageSourceGradRad *z = (ArtImageSourceGradRad *)self;
+ const ArtGradientRadial *gradient = z->gradient;
+ int pixstride = (render->n_chan + 1) * (render->depth >> 3);
+ int x;
+ int x0 = render->x0;
+ int width = render->x1 - x0;
+ int n_stops = gradient->n_stops;
+ ArtGradientStop *stops = gradient->stops;
+ art_u8 *bufp = render->image_buf;
+ double fx = gradient->fx;
+ double fy = gradient->fy;
+ double dx, dy;
+ double *affine = gradient->affine;
+ double aff0 = affine[0];
+ double aff1 = affine[1];
+ const double a = z->a;
+ const double arecip = 1.0 / a;
+ double b, db;
+ double c, dc, ddc;
+ double b_a, db_a;
+ double rad, drad, ddrad;
+
+ dx = x0 * aff0 + y * affine[2] + affine[4] - fx;
+ dy = x0 * aff1 + y * affine[3] + affine[5] - fy;
+ b = dx * fx + dy * fy;
+ db = aff0 * fx + aff1 * fy;
+ c = dx * dx + dy * dy;
+ dc = 2 * aff0 * dx + aff0 * aff0 + 2 * aff1 * dy + aff1 * aff1;
+ ddc = 2 * aff0 * aff0 + 2 * aff1 * aff1;
+
+ b_a = b * arecip;
+ db_a = db * arecip;
+
+ rad = b_a * b_a + c * arecip;
+ drad = 2 * b_a * db_a + db_a * db_a + dc * arecip;
+ ddrad = 2 * db_a * db_a + ddc * arecip;
+
+ for (x = 0; x < width; x++)
+ {
+ double z;
+
+ if (rad > 0)
+ z = b_a + sqrt (rad);
+ else
+ z = b_a;
+ art_render_gradient_setpix (render, bufp, n_stops, stops, z);
+ bufp += pixstride;
+ b_a += db_a;
+ rad += drad;
+ drad += ddrad;
+ }
+}
+
+static void
+art_render_gradient_radial_negotiate (ArtImageSource *self, ArtRender *render,
+ ArtImageSourceFlags *p_flags,
+ int *p_buf_depth, ArtAlphaType *p_alpha)
+{
+ self->super.render = art_render_gradient_radial_render;
+ *p_flags = 0;
+ *p_buf_depth = render->depth;
+ *p_alpha = ART_ALPHA_PREMUL;
+}
+
+/**
+ * art_render_gradient_radial: Add a radial gradient image source.
+ * @render: The render object.
+ * @gradient: The radial gradient.
+ *
+ * Adds the radial gradient @gradient as the image source for rendering
+ * in the render object @render.
+ **/
+void
+art_render_gradient_radial (ArtRender *render,
+ const ArtGradientRadial *gradient,
+ ArtFilterLevel level)
+{
+ ArtImageSourceGradRad *image_source = art_new (ArtImageSourceGradRad, 1);
+ double fx = gradient->fx;
+ double fy = gradient->fy;
+
+ image_source->super.super.render = NULL;
+ image_source->super.super.done = art_render_gradient_radial_done;
+ image_source->super.negotiate = art_render_gradient_radial_negotiate;
+
+ image_source->gradient = gradient;
+ /* todo: sanitycheck fx, fy? */
+ image_source->a = 1 - fx * fx - fy * fy;
+
+ art_render_add_image_source (render, &image_source->super);
+}
+
+#endif
diff --git a/librsvg/art_render_gradient.h b/librsvg/art_render_gradient.h
new file mode 100644
index 000000000..e3f2ff599
--- /dev/null
+++ b/librsvg/art_render_gradient.h
@@ -0,0 +1,90 @@
+/* This file is adapted from art_render_gradient.h in Libart version 2.3.0 */
+
+#include <libart_lgpl/libart-features.h>
+
+#if LIBART_MAJOR_VERSION == 2 && LIBART_MINOR_VERSION < 2
+
+/*
+ * art_render_gradient.h: Gradient image source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ * Alexander Larsson <alla@lysator.liu.se>
+ */
+
+#ifndef __ART_RENDER_GRADIENT_H__
+#define __ART_RENDER_GRADIENT_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef struct _ArtGradientLinear ArtGradientLinear;
+typedef struct _ArtGradientRadial ArtGradientRadial;
+typedef struct _ArtGradientStop ArtGradientStop;
+
+typedef enum {
+ ART_GRADIENT_PAD,
+ ART_GRADIENT_REFLECT,
+ ART_GRADIENT_REPEAT
+} ArtGradientSpread;
+
+struct _ArtGradientLinear {
+ double a;
+ double b;
+ double c;
+ ArtGradientSpread spread;
+ int n_stops;
+ ArtGradientStop *stops;
+};
+
+struct _ArtGradientRadial {
+ double affine[6]; /* transforms user coordinates to unit circle */
+ double fx, fy; /* focal point in unit circle coords */
+ int n_stops;
+ ArtGradientStop *stops;
+};
+
+struct _ArtGradientStop {
+ double offset;
+ ArtPixMaxDepth color[ART_MAX_CHAN + 1];
+};
+
+void
+art_render_gradient_linear (ArtRender *render,
+ const ArtGradientLinear *gradient,
+ ArtFilterLevel level);
+
+void
+art_render_gradient_radial (ArtRender *render,
+ const ArtGradientRadial *gradient,
+ ArtFilterLevel level);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_GRADIENT_H__ */
+
+#else
+
+#include <libart_lgpl/art_render_gradient.h>
+
+#endif
diff --git a/librsvg/art_render_svp.c b/librsvg/art_render_svp.c
new file mode 100644
index 000000000..2a3734bd3
--- /dev/null
+++ b/librsvg/art_render_svp.c
@@ -0,0 +1,398 @@
+/* This file is adapted from art_render_gradient.c in Libart version 2.3.0 */
+
+#include <libart_lgpl/libart-features.h>
+
+#if LIBART_MAJOR_VERSION == 2 && LIBART_MINOR_VERSION < 2
+
+/*
+ * art_render_gradient.c: SVP mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ */
+
+/* Use an SVP as a render mask source. */
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_svp.h>
+#include <libart_lgpl/art_svp_render_aa.h>
+
+#include "art_render.h"
+#include "art_render_svp.h"
+
+typedef struct _ArtMaskSourceSVP ArtMaskSourceSVP;
+
+struct _ArtMaskSourceSVP {
+ ArtMaskSource super;
+ ArtRender *render;
+ const ArtSVP *svp;
+ art_u8 *dest_ptr;
+};
+
+static void
+art_render_svp_done (ArtRenderCallback *self, ArtRender *render)
+{
+ art_free (self);
+}
+
+static int
+art_render_svp_can_drive (ArtMaskSource *self, ArtRender *render)
+{
+ return 10;
+}
+
+/* The basic art_render_svp_callback function is repeated four times,
+ for all combinations of non-unit opacity and generating spans. In
+ general, I'd consider this bad style, but in this case I plead
+ a measurable performance improvement. */
+
+static void
+art_render_svp_callback (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+ ArtRender *render = z->render;
+ int n_run = 0;
+ int i;
+ int running_sum = start;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ ArtRenderMaskRun *run = render->run;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0 && running_sum > 0x80ff)
+ {
+ run[0].x = x0;
+ run[0].alpha = running_sum;
+ n_run++;
+ }
+
+ for (i = 0; i < n_steps - 1; i++)
+ {
+ running_sum += steps[i].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[i + 1].x;
+ if (run_x1 > run_x0)
+ {
+ run[n_run].x = run_x0;
+ run[n_run].alpha = running_sum;
+ n_run++;
+ }
+ }
+ if (x1 > run_x1)
+ {
+ running_sum += steps[n_steps - 1].delta;
+ run[n_run].x = run_x1;
+ run[n_run].alpha = running_sum;
+ n_run++;
+ }
+ if (running_sum > 0x80ff)
+ {
+ run[n_run].x = x1;
+ run[n_run].alpha = 0x8000;
+ n_run++;
+ }
+ }
+
+ render->n_run = n_run;
+
+ art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+ z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_span (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+ ArtRender *render = z->render;
+ int n_run = 0;
+ int n_span = 0;
+ int i;
+ int running_sum = start;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ ArtRenderMaskRun *run = render->run;
+ int *span_x = render->span_x;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ if (run_x1 > x0 && running_sum > 0x80ff)
+ {
+ run[0].x = x0;
+ run[0].alpha = running_sum;
+ n_run++;
+ span_x[0] = x0;
+ n_span++;
+ }
+
+ for (i = 0; i < n_steps - 1; i++)
+ {
+ running_sum += steps[i].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[i + 1].x;
+ if (run_x1 > run_x0)
+ {
+ run[n_run].x = run_x0;
+ run[n_run].alpha = running_sum;
+ n_run++;
+ if ((n_span & 1) != (running_sum > 0x80ff))
+ span_x[n_span++] = run_x0;
+ }
+ }
+ if (x1 > run_x1)
+ {
+ running_sum += steps[n_steps - 1].delta;
+ run[n_run].x = run_x1;
+ run[n_run].alpha = running_sum;
+ n_run++;
+ if ((n_span & 1) != (running_sum > 0x80ff))
+ span_x[n_span++] = run_x1;
+ }
+ if (running_sum > 0x80ff)
+ {
+ run[n_run].x = x1;
+ run[n_run].alpha = 0x8000;
+ n_run++;
+ span_x[n_span++] = x1;
+ }
+ }
+
+ render->n_run = n_run;
+ render->n_span = n_span;
+
+ art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+ z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_opacity (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+ ArtRender *render = z->render;
+ int n_run = 0;
+ int i;
+ art_u32 running_sum;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ ArtRenderMaskRun *run = render->run;
+ art_u32 opacity = render->opacity;
+ art_u32 alpha;
+
+ running_sum = start - 0x7f80;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+ if (run_x1 > x0 && alpha > 0x80ff)
+ {
+ run[0].x = x0;
+ run[0].alpha = alpha;
+ n_run++;
+ }
+
+ for (i = 0; i < n_steps - 1; i++)
+ {
+ running_sum += steps[i].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[i + 1].x;
+ if (run_x1 > run_x0)
+ {
+ run[n_run].x = run_x0;
+ alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+ run[n_run].alpha = alpha;
+ n_run++;
+ }
+ }
+ if (x1 > run_x1)
+ {
+ running_sum += steps[n_steps - 1].delta;
+ run[n_run].x = run_x1;
+ alpha = ((running_sum >> 8) * opacity + 0x80080) >> 8;
+ run[n_run].alpha = alpha;
+ n_run++;
+ }
+ if (alpha > 0x80ff)
+ {
+ run[n_run].x = x1;
+ run[n_run].alpha = 0x8000;
+ n_run++;
+ }
+ }
+
+ render->n_run = n_run;
+
+ art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+ z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_callback_opacity_span (void *callback_data, int y,
+ int start, ArtSVPRenderAAStep *steps, int n_steps)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)callback_data;
+ ArtRender *render = z->render;
+ int n_run = 0;
+ int n_span = 0;
+ int i;
+ art_u32 running_sum;
+ int x0 = render->x0;
+ int x1 = render->x1;
+ int run_x0, run_x1;
+ ArtRenderMaskRun *run = render->run;
+ int *span_x = render->span_x;
+ art_u32 opacity = render->opacity;
+ art_u32 alpha;
+
+ running_sum = start - 0x7f80;
+
+ if (n_steps > 0)
+ {
+ run_x1 = steps[0].x;
+ alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+ if (run_x1 > x0 && alpha > 0x80ff)
+ {
+ run[0].x = x0;
+ run[0].alpha = alpha;
+ n_run++;
+ span_x[0] = x0;
+ n_span++;
+ }
+
+ for (i = 0; i < n_steps - 1; i++)
+ {
+ running_sum += steps[i].delta;
+ run_x0 = run_x1;
+ run_x1 = steps[i + 1].x;
+ if (run_x1 > run_x0)
+ {
+ run[n_run].x = run_x0;
+ alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+ run[n_run].alpha = alpha;
+ n_run++;
+ if ((n_span & 1) != (alpha > 0x80ff))
+ span_x[n_span++] = run_x0;
+ }
+ }
+ if (x1 > run_x1)
+ {
+ running_sum += steps[n_steps - 1].delta;
+ run[n_run].x = run_x1;
+ alpha = ((running_sum >> 8) * opacity + 0x800080) >> 8;
+ run[n_run].alpha = alpha;
+ n_run++;
+ if ((n_span & 1) != (alpha > 0x80ff))
+ span_x[n_span++] = run_x1;
+ }
+ if (alpha > 0x80ff)
+ {
+ run[n_run].x = x1;
+ run[n_run].alpha = 0x8000;
+ n_run++;
+ span_x[n_span++] = x1;
+ }
+ }
+
+ render->n_run = n_run;
+ render->n_span = n_span;
+
+ art_render_invoke_callbacks (render, z->dest_ptr, y);
+
+ z->dest_ptr += render->rowstride;
+}
+
+static void
+art_render_svp_invoke_driver (ArtMaskSource *self, ArtRender *render)
+{
+ ArtMaskSourceSVP *z = (ArtMaskSourceSVP *)self;
+ void (*callback) (void *callback_data,
+ int y,
+ int start,
+ ArtSVPRenderAAStep *steps, int n_steps);
+
+ z->dest_ptr = render->pixels;
+ if (render->opacity == 0x10000)
+ {
+ if (render->need_span)
+ callback = art_render_svp_callback_span;
+ else
+ callback = art_render_svp_callback;
+ }
+ else
+ {
+ if (render->need_span)
+ callback = art_render_svp_callback_opacity_span;
+ else
+ callback = art_render_svp_callback_opacity;
+ }
+
+ art_svp_render_aa (z->svp,
+ render->x0, render->y0,
+ render->x1, render->y1, callback,
+ self);
+ art_render_svp_done (&self->super, render);
+}
+
+static void
+art_render_svp_prepare (ArtMaskSource *self, ArtRender *render,
+ art_boolean first)
+{
+ /* todo */
+ art_die ("art_render_svp non-driver mode not yet implemented.\n");
+}
+
+/**
+ * art_render_svp: Use an SVP as a render mask source.
+ * @render: Render object.
+ * @svp: SVP.
+ *
+ * Adds @svp to the render object as a mask. Note: @svp must remain
+ * allocated until art_render_invoke() is called on @render.
+ **/
+void
+art_render_svp (ArtRender *render, const ArtSVP *svp)
+{
+ ArtMaskSourceSVP *mask_source;
+ mask_source = art_new (ArtMaskSourceSVP, 1);
+
+ mask_source->super.super.render = NULL;
+ mask_source->super.super.done = art_render_svp_done;
+ mask_source->super.can_drive = art_render_svp_can_drive;
+ mask_source->super.invoke_driver = art_render_svp_invoke_driver;
+ mask_source->super.prepare = art_render_svp_prepare;
+ mask_source->render = render;
+ mask_source->svp = svp;
+
+ art_render_add_mask_source (render, &mask_source->super);
+}
+
+#endif
diff --git a/librsvg/art_render_svp.h b/librsvg/art_render_svp.h
new file mode 100644
index 000000000..3c476b5b8
--- /dev/null
+++ b/librsvg/art_render_svp.h
@@ -0,0 +1,51 @@
+/* This file is adapted from art_render_svp.h in Libart version 2.3.0 */
+
+#include <libart_lgpl/libart-features.h>
+
+#if LIBART_MAJOR_VERSION == 2 && LIBART_MINOR_VERSION < 2
+
+/*
+ * art_render_gradient.h: SVP mask source for modular rendering.
+ *
+ * Libart_LGPL - library of basic graphic primitives
+ * Copyright (C) 2000 Raph Levien
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Raph Levien <raph@acm.org>
+ */
+
+#ifndef __ART_RENDER_SVP_H__
+#define __ART_RENDER_SVP_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+void
+art_render_svp (ArtRender *render, const ArtSVP *svp);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __ART_RENDER_SVP_H__ */
+
+#else
+
+#include <libart_lgpl/art_render_svp.h>
+
+#endif
diff --git a/librsvg/art_rgba.c b/librsvg/art_rgba.c
deleted file mode 100644
index 209f67564..000000000
--- a/librsvg/art_rgba.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * art_rgba.c: Functions for manipulating RGBA pixel data.
- *
- * Libart_GPL - library of basic graphic primitives
- * Copyright (C) 2000 Raph Levien
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#include <config.h>
-#include <libart_lgpl/art_misc.h>
-#include "art_rgba.h"
-
-#define ART_OPTIMIZE_SPACE
-
-#ifndef ART_OPTIMIZE_SPACE
-#include "art_rgba_table.c"
-#endif
-
-/**
- * art_rgba_rgba_composite: Composite RGBA image over RGBA buffer.
- * @dst: Destination RGBA buffer.
- * @src: Source RGBA buffer.
- * @n: Number of RGBA pixels to composite.
- *
- * Composites the RGBA pixels in @dst over the @src buffer.
- **/
-void
-art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n)
-{
- int i;
-#ifdef WORDS_BIGENDIAN
- art_u32 src_rgba, dst_rgba;
-#else
- art_u32 src_abgr, dst_abgr;
-#endif
- art_u8 src_alpha, dst_alpha;
-
- for (i = 0; i < n; i++)
- {
-#ifdef WORDS_BIGENDIAN
- src_rgba = ((art_u32 *)src)[i];
- src_alpha = src_rgba & 0xff;
-#else
- src_abgr = ((art_u32 *)src)[i];
- src_alpha = (src_abgr >> 24) & 0xff;
-#endif
- if (src_alpha)
- {
- if (src_alpha == 0xff ||
- (
-#ifdef WORDS_BIGENDIAN
- dst_rgba = ((art_u32 *)dst)[i],
- dst_alpha = dst_rgba & 0xff,
-#else
- dst_abgr = ((art_u32 *)dst)[i],
- dst_alpha = (dst_abgr >> 24),
-#endif
- dst_alpha == 0))
-#ifdef WORDS_BIGENDIAN
- ((art_u32 *)dst)[i] = src_rgba;
-#else
- ((art_u32 *)dst)[i] = src_abgr;
-#endif
- else
- {
- int r, g, b, a;
- int src_r, src_g, src_b;
- int dst_r, dst_g, dst_b;
- int tmp;
- int c;
-
-#ifdef ART_OPTIMIZE_SPACE
- tmp = (255 - src_alpha) * (255 - dst_alpha) + 0x80;
- a = 255 - ((tmp + (tmp >> 8)) >> 8);
- c = ((src_alpha << 16) + (a >> 1)) / a;
-#else
- tmp = art_rgba_composite_table[(src_alpha << 8) + dst_alpha];
- c = tmp & 0x1ffff;
- a = tmp >> 24;
-#endif
-#ifdef WORDS_BIGENDIAN
- src_r = (src_rgba >> 24) & 0xff;
- src_g = (src_rgba >> 16) & 0xff;
- src_b = (src_rgba >> 8) & 0xff;
- dst_r = (dst_rgba >> 24) & 0xff;
- dst_g = (dst_rgba >> 16) & 0xff;
- dst_b = (dst_rgba >> 8) & 0xff;
-#else
- src_r = src_abgr & 0xff;
- src_g = (src_abgr >> 8) & 0xff;
- src_b = (src_abgr >> 16) & 0xff;
- dst_r = dst_abgr & 0xff;
- dst_g = (dst_abgr >> 8) & 0xff;
- dst_b = (dst_abgr >> 16) & 0xff;
-#endif
- r = dst_r + (((src_r - dst_r) * c + 0x8000) >> 16);
- g = dst_g + (((src_g - dst_g) * c + 0x8000) >> 16);
- b = dst_b + (((src_b - dst_b) * c + 0x8000) >> 16);
-#ifdef WORDS_BIGENDIAN
- ((art_u32 *)dst)[i] = (r << 24) | (g << 16) | (b << 8) | a;
-#else
- ((art_u32 *)dst)[i] = (a << 24) | (b << 16) | (g << 8) | r;
-#endif
- }
- }
-#if 0
- /* it's not clear to me this optimization really wins */
- else
- {
- /* skip over run of transparent pixels */
- for (; i < n - 1; i++)
- {
-#ifdef WORDS_BIGENDIAN
- src_rgba = ((art_u32 *)src)[i + 1];
- if (src_rgba & 0xff)
- break;
-#else
- src_abgr = ((art_u32 *)src)[i + 1];
- if (src_abgr & 0xff000000)
- break;
-#endif
- }
- }
-#endif
- }
-}
-
-/**
- * art_rgba_fill_run: fill an RGBA buffer a solid RGB color.
- * @buf: Buffer to fill.
- * @r: Red, range 0..255.
- * @g: Green, range 0..255.
- * @b: Blue, range 0..255.
- * @n: Number of RGB triples to fill.
- *
- * Fills a buffer with @n copies of the (@r, @g, @b) triple, solid
- * alpha. Thus, locations @buf (inclusive) through @buf + 4 * @n
- * (exclusive) are written.
- **/
-void
-art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n)
-{
- int i;
-#ifdef WORDS_BIGENDIAN
- art_u32 src_rgba;
-#else
- art_u32 src_abgr;
-#endif
-
-#ifdef WORDS_BIGENDIAN
- src_rgba = (r << 24) | (g << 16) | (b << 8) | 255;
-#else
- src_abgr = (255 << 24) | (b << 16) | (g << 8) | r;
-#endif
- for (i = 0; i < n; i++)
- {
-#ifdef WORDS_BIGENDIAN
- ((art_u32 *)buf)[i] = src_rgba;
-#else
- ((art_u32 *)buf)[i] = src_abgr;
-#endif
- }
-}
-
-/**
- * art_rgba_run_alpha: Render semitransparent color over RGBA buffer.
- * @buf: Buffer for rendering.
- * @r: Red, range 0..255.
- * @g: Green, range 0..255.
- * @b: Blue, range 0..255.
- * @alpha: Alpha, range 0..255.
- * @n: Number of RGB triples to render.
- *
- * Renders a sequential run of solid (@r, @g, @b) color over @buf with
- * opacity @alpha. Note that the range of @alpha is 0..255, in contrast
- * to art_rgb_run_alpha, which has a range of 0..256.
- **/
-void
-art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n)
-{
- int i;
-#ifdef WORDS_BIGENDIAN
- art_u32 src_rgba, dst_rgba;
-#else
- art_u32 src_abgr, dst_abgr;
-#endif
- art_u8 dst_alpha;
- int a;
- int dst_r, dst_g, dst_b;
- int tmp;
- int c;
-
-#ifdef WORDS_BIGENDIAN
- src_rgba = (r << 24) | (g << 16) | (b << 8) | alpha;
-#else
- src_abgr = (alpha << 24) | (b << 16) | (g << 8) | r;
-#endif
- for (i = 0; i < n; i++)
- {
-#ifdef WORDS_BIGENDIAN
- dst_rgba = ((art_u32 *)buf)[i];
- dst_alpha = dst_rgba & 0xff;
-#else
- dst_abgr = ((art_u32 *)buf)[i];
- dst_alpha = (dst_abgr >> 24) & 0xff;
-#endif
- if (dst_alpha)
- {
-#ifdef ART_OPTIMIZE_SPACE
- tmp = (255 - alpha) * (255 - dst_alpha) + 0x80;
- a = 255 - ((tmp + (tmp >> 8)) >> 8);
- c = ((alpha << 16) + (a >> 1)) / a;
-#else
- tmp = art_rgba_composite_table[(alpha << 8) + dst_alpha];
- c = tmp & 0x1ffff;
- a = tmp >> 24;
-#endif
-#ifdef WORDS_BIGENDIAN
- dst_r = (dst_rgba >> 24) & 0xff;
- dst_g = (dst_rgba >> 16) & 0xff;
- dst_b = (dst_rgba >> 8) & 0xff;
-#else
- dst_r = dst_abgr & 0xff;
- dst_g = (dst_abgr >> 8) & 0xff;
- dst_b = (dst_abgr >> 16) & 0xff;
-#endif
- dst_r += (((r - dst_r) * c + 0x8000) >> 16);
- dst_g += (((g - dst_g) * c + 0x8000) >> 16);
- dst_b += (((b - dst_b) * c + 0x8000) >> 16);
-#ifdef WORDS_BIGENDIAN
- ((art_u32 *)buf)[i] = (dst_r << 24) | (dst_g << 16) | (dst_b << 8) | a;
-#else
- ((art_u32 *)buf)[i] = (a << 24) | (dst_b << 16) | (dst_g << 8) | dst_r;
-#endif
- }
- else
- {
-#ifdef WORDS_BIGENDIAN
- ((art_u32 *)buf)[i] = src_rgba;
-#else
- ((art_u32 *)buf)[i] = src_abgr;
-#endif
- }
- }
-}
diff --git a/librsvg/art_rgba.h b/librsvg/art_rgba.h
deleted file mode 100644
index 61dcb0329..000000000
--- a/librsvg/art_rgba.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * art_rgba.h: Functions for manipulating RGBA pixel data.
- *
- * Libart_GPL - library of basic graphic primitives
- * Copyright (C) 2000 Raph Levien
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#ifndef __ART_RGBA_H__
-#define __ART_RGBA_H__
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-void
-art_rgba_rgba_composite (art_u8 *dst, const art_u8 *src, int n);
-
-void
-art_rgba_fill_run (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int n);
-
-void
-art_rgba_run_alpha (art_u8 *buf, art_u8 r, art_u8 g, art_u8 b, int alpha, int n);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif
diff --git a/librsvg/art_rgba_svp.c b/librsvg/art_rgba_svp.c
deleted file mode 100644
index 29658cd70..000000000
--- a/librsvg/art_rgba_svp.c
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * art_rgba_svp.c: Render a sorted vector path over an RGBA buffer.
- *
- * Libart_GPL - library of basic graphic primitives
- * Copyright (C) 2000 Raph Levien
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#include <config.h>
-#include <libart_lgpl/art_misc.h>
-#include <libart_lgpl/art_svp.h>
-#include <libart_lgpl/art_svp_render_aa.h>
-#include "art_rgba.h"
-#include "art_rgba_svp.h"
-
-typedef struct _ArtRgbaSVPAlphaData ArtRgbaSVPAlphaData;
-
-struct _ArtRgbaSVPAlphaData {
- int alphatab[256];
- art_u8 r, g, b, alpha;
- art_u8 *buf;
- int rowstride;
- int x0, x1;
-};
-
-static void
-art_rgba_svp_alpha_callback (void *callback_data, int y,
- int start, ArtSVPRenderAAStep *steps, int n_steps)
-{
- ArtRgbaSVPAlphaData *data = callback_data;
- art_u8 *linebuf;
- int run_x0, run_x1;
- art_u32 running_sum = start;
- int x0, x1;
- int k;
- art_u8 r, g, b;
- int *alphatab;
- int alpha;
-
- linebuf = data->buf;
- x0 = data->x0;
- x1 = data->x1;
-
- r = data->r;
- g = data->g;
- b = data->b;
- alphatab = data->alphatab;
-
- if (n_steps > 0)
- {
- run_x1 = steps[0].x;
- if (run_x1 > x0)
- {
- alpha = (running_sum >> 16) & 0xff;
- if (alpha)
- art_rgba_run_alpha (linebuf,
- r, g, b, alphatab[alpha],
- run_x1 - x0);
- }
-
- /* render the steps into tmpbuf */
- for (k = 0; k < n_steps - 1; k++)
- {
- running_sum += steps[k].delta;
- run_x0 = run_x1;
- run_x1 = steps[k + 1].x;
- if (run_x1 > run_x0)
- {
- alpha = (running_sum >> 16) & 0xff;
- if (alpha)
- art_rgba_run_alpha (linebuf + ((run_x0 - x0) << 2),
- r, g, b, alphatab[alpha],
- run_x1 - run_x0);
- }
- }
- running_sum += steps[k].delta;
- if (x1 > run_x1)
- {
- alpha = (running_sum >> 16) & 0xff;
- if (alpha)
- art_rgba_run_alpha (linebuf + ((run_x1 - x0) << 2),
- r, g, b, alphatab[alpha],
- x1 - run_x1);
- }
- }
- else
- {
- alpha = (running_sum >> 16) & 0xff;
- if (alpha)
- art_rgba_run_alpha (linebuf,
- r, g, b, alphatab[alpha],
- x1 - x0);
- }
-
- data->buf += data->rowstride;
-}
-
-static void
-art_rgba_svp_alpha_opaque_callback (void *callback_data, int y,
- int start,
- ArtSVPRenderAAStep *steps, int n_steps)
-{
- ArtRgbaSVPAlphaData *data = callback_data;
- art_u8 *linebuf;
- int run_x0, run_x1;
- art_u32 running_sum = start;
- int x0, x1;
- int k;
- art_u8 r, g, b;
- int *alphatab;
- int alpha;
-
- linebuf = data->buf;
- x0 = data->x0;
- x1 = data->x1;
-
- r = data->r;
- g = data->g;
- b = data->b;
- alphatab = data->alphatab;
-
- if (n_steps > 0)
- {
- run_x1 = steps[0].x;
- if (run_x1 > x0)
- {
- alpha = running_sum >> 16;
- if (alpha)
- {
- if (alpha >= 255)
- art_rgba_fill_run (linebuf,
- r, g, b,
- run_x1 - x0);
- else
- art_rgba_run_alpha (linebuf,
- r, g, b, alphatab[alpha],
- run_x1 - x0);
- }
- }
-
- /* render the steps into tmpbuf */
- for (k = 0; k < n_steps - 1; k++)
- {
- running_sum += steps[k].delta;
- run_x0 = run_x1;
- run_x1 = steps[k + 1].x;
- if (run_x1 > run_x0)
- {
- alpha = running_sum >> 16;
- if (alpha)
- {
- if (alpha >= 255)
- art_rgba_fill_run (linebuf + ((run_x0 - x0) << 2),
- r, g, b,
- run_x1 - run_x0);
- else
- art_rgba_run_alpha (linebuf + ((run_x0 - x0) << 2),
- r, g, b, alphatab[alpha],
- run_x1 - run_x0);
- }
- }
- }
- running_sum += steps[k].delta;
- if (x1 > run_x1)
- {
- alpha = running_sum >> 16;
- if (alpha)
- {
- if (alpha >= 255)
- art_rgba_fill_run (linebuf + ((run_x1 - x0) << 2),
- r, g, b,
- x1 - run_x1);
- else
- art_rgba_run_alpha (linebuf + ((run_x1 - x0) << 2),
- r, g, b, alphatab[alpha],
- x1 - run_x1);
- }
- }
- }
- else
- {
- alpha = running_sum >> 16;
- if (alpha)
- {
- if (alpha >= 255)
- art_rgba_fill_run (linebuf,
- r, g, b,
- x1 - x0);
- else
- art_rgba_run_alpha (linebuf,
- r, g, b, alphatab[alpha],
- x1 - x0);
- }
- }
-
- data->buf += data->rowstride;
-}
-
-/**
- * art_rgba_svp_alpha: Alpha-composite sorted vector path over RGBA buffer.
- * @svp: The source sorted vector path.
- * @x0: Left coordinate of destination rectangle.
- * @y0: Top coordinate of destination rectangle.
- * @x1: Right coordinate of destination rectangle.
- * @y1: Bottom coordinate of destination rectangle.
- * @rgba: Color in 0xRRGGBBAA format.
- * @buf: Destination RGBA buffer.
- * @rowstride: Rowstride of @buf buffer.
- * @alphagamma: #ArtAlphaGamma for gamma-correcting the compositing.
- *
- * Renders the shape specified with @svp over the @buf RGBA buffer.
- * @x1 - @x0 specifies the width, and @y1 - @y0 specifies the height,
- * of the rectangle rendered. The new pixels are stored starting at
- * the first byte of @buf. Thus, the @x0 and @y0 parameters specify
- * an offset within @svp, and may be tweaked as a way of doing
- * integer-pixel translations without fiddling with @svp itself.
- *
- * The @rgba argument specifies the color for the rendering. Pixels of
- * entirely 0 winding number are left untouched. Pixels of entirely
- * 1 winding number have the color @rgba composited over them (ie,
- * are replaced by the red, green, blue components of @rgba if the alpha
- * component is 0xff). Pixels of intermediate coverage are interpolated
- * according to the rule in @alphagamma, or default to linear if
- * @alphagamma is NULL.
- **/
-void
-art_rgba_svp_alpha (const ArtSVP *svp,
- int x0, int y0, int x1, int y1,
- art_u32 rgba,
- art_u8 *buf, int rowstride,
- ArtAlphaGamma *alphagamma)
-{
- ArtRgbaSVPAlphaData data;
- int r, g, b, alpha;
- int i;
- int a, da;
-
- r = rgba >> 24;
- g = (rgba >> 16) & 0xff;
- b = (rgba >> 8) & 0xff;
- alpha = rgba & 0xff;
-
- data.r = r;
- data.g = g;
- data.b = b;
- data.alpha = alpha;
-
- a = 0x8000;
- da = (alpha * 65793 + 0x80) >> 8; /* 65793 equals 2 ^ 24 / 255 */
-
- for (i = 0; i < 256; i++)
- {
- data.alphatab[i] = a >> 16;
- a += da;
- }
-
- data.buf = buf;
- data.rowstride = rowstride;
- data.x0 = x0;
- data.x1 = x1;
- if (alpha == 255)
- art_svp_render_aa (svp, x0, y0, x1, y1, art_rgba_svp_alpha_opaque_callback,
- &data);
- else
- art_svp_render_aa (svp, x0, y0, x1, y1, art_rgba_svp_alpha_callback, &data);
-}
diff --git a/librsvg/art_rgba_svp.h b/librsvg/art_rgba_svp.h
deleted file mode 100644
index 315b23cdb..000000000
--- a/librsvg/art_rgba_svp.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * art_rgba_svp.h: Render a sorted vector path over an RGBA buffer.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- * 02111-1307, USA.
- */
-
-#ifndef __ART_RGBA_SVP_H__
-#define __ART_RGBA_SVP_H__
-
-#include <libart_lgpl/art_alphagamma.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-
-void
-art_rgba_svp_alpha (const ArtSVP *svp,
- int x0, int y0, int x1, int y1,
- art_u32 rgba,
- art_u8 *buf, int rowstride,
- ArtAlphaGamma *alphagamma);
-
-#ifdef __cplusplus
-}
-#endif /* __cplusplus */
-
-#endif
diff --git a/librsvg/rsvg-css.c b/librsvg/rsvg-css.c
new file mode 100644
index 000000000..f909bb900
--- /dev/null
+++ b/librsvg/rsvg-css.c
@@ -0,0 +1,201 @@
+/*
+ rsvg-css.c: Parse CSS basic data types.
+
+ Copyright (C) 2000 Eazel, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Raph Levien <raph@artofcode.com>
+*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <glib.h>
+
+#include "rsvg-css.h"
+
+/**
+ * rsvg_css_parse_length: Parse CSS2 length to a pixel value.
+ * @str: Original string.
+ * @fixed: Where to store boolean value of whether length is fixed.
+ *
+ * Parses a CSS2 length into a pixel value.
+ *
+ * Returns: returns the length.
+ **/
+double
+rsvg_css_parse_length (const char *str, gint *fixed)
+{
+ char *p;
+
+ /*
+ * The supported CSS length unit specifiers are:
+ * em, ex, px, pt, pc, cm, mm, in, and percentages.
+ */
+
+ *fixed = FALSE;
+
+ p = strstr (str, "px");
+ if (p != NULL)
+ {
+ *fixed = TRUE;
+ return atof (str);
+ }
+ p = strstr (str, "in");
+ if (p != NULL)
+ {
+ *fixed = TRUE;
+ /* return svg->pixels_per_inch * atof (str); */
+ }
+ p = strstr (str, "%");
+ if (p != NULL)
+ {
+ return 0.01 * atof (str);
+ }
+ return atof (str);
+}
+
+gboolean
+rsvg_css_param_match (const char *str, const char *param_name)
+{
+ int i;
+
+ for (i = 0; str[i] != '\0' && str[i] != ':'; i++)
+ if (param_name[i] != str[i])
+ return FALSE;
+ return str[i] == ':' && param_name[i] == '\0';
+}
+
+int
+rsvg_css_param_arg_offset (const char *str)
+{
+ int i;
+
+ for (i = 0; str[i] != '\0' && str[i] != ':'; i++);
+ if (str[i] != '\0') i++;
+ for (; str[i] == ' '; i++);
+ return i;
+}
+
+/* Parse a CSS2 color, returning rgb */
+guint32
+rsvg_css_parse_color (const char *str)
+{
+ gint val = 0;
+ static GHashTable *colors = NULL;
+
+ /* todo: better failure detection */
+
+ /*
+ * todo: handle the rgb (r, g, b) and rgb ( r%, g%, b%), syntax
+ * defined in http://www.w3.org/TR/REC-CSS2/syndata.html#color-units
+ */
+#ifdef VERBOSE
+ g_print ("color = %s\n", str);
+#endif
+ if (str[0] == '#')
+ {
+ int i;
+ for (i = 1; str[i]; i++)
+ {
+ int hexval;
+ if (str[i] >= '0' && str[i] <= '9')
+ hexval = str[i] - '0';
+ else if (str[i] >= 'A' && str[i] <= 'F')
+ hexval = str[i] - 'A' + 10;
+ else if (str[i] >= 'a' && str[i] <= 'f')
+ hexval = str[i] - 'a' + 10;
+ else
+ break;
+ val = (val << 4) + hexval;
+ }
+ /* handle #rgb case */
+ if (i == 4)
+ {
+ val = ((val & 0xf00) << 8) |
+ ((val & 0x0f0) << 4) |
+ (val & 0x00f);
+ val |= val << 4;
+ }
+#ifdef VERBOSE
+ printf ("val = %x\n", val);
+#endif
+ }
+ else
+ {
+ GString * string;
+ if (!colors)
+ {
+ colors = g_hash_table_new (g_str_hash, g_str_equal);
+
+ g_hash_table_insert (colors, "black", GINT_TO_POINTER (0x000000));
+ g_hash_table_insert (colors, "silver", GINT_TO_POINTER (0xc0c0c0));
+ g_hash_table_insert (colors, "gray", GINT_TO_POINTER (0x808080));
+ g_hash_table_insert (colors, "white", GINT_TO_POINTER (0xFFFFFF));
+ g_hash_table_insert (colors, "maroon", GINT_TO_POINTER (0x800000));
+ g_hash_table_insert (colors, "red", GINT_TO_POINTER (0xFF0000));
+ g_hash_table_insert (colors, "purple", GINT_TO_POINTER (0x800080));
+ g_hash_table_insert (colors, "fuchsia", GINT_TO_POINTER (0xFF00FF));
+ g_hash_table_insert (colors, "green", GINT_TO_POINTER (0x008000));
+ g_hash_table_insert (colors, "lime", GINT_TO_POINTER (0x00FF00));
+ g_hash_table_insert (colors, "olive", GINT_TO_POINTER (0x808000));
+ g_hash_table_insert (colors, "yellow", GINT_TO_POINTER (0xFFFF00));
+ g_hash_table_insert (colors, "navy", GINT_TO_POINTER (0x000080));
+ g_hash_table_insert (colors, "blue", GINT_TO_POINTER (0x0000FF));
+ g_hash_table_insert (colors, "teal", GINT_TO_POINTER (0x008080));
+ g_hash_table_insert (colors, "aqua", GINT_TO_POINTER (0x00FFFF));
+ }
+
+ string = g_string_down (g_string_new (str));
+
+ /* this will default to black on a failed lookup */
+ val = GPOINTER_TO_INT (g_hash_table_lookup (colors, string->str));
+ }
+
+ return val;
+}
+
+guint
+rsvg_css_parse_opacity (const char *str)
+{
+ char *end_ptr;
+ double opacity;
+
+ opacity = strtod (str, &end_ptr);
+
+ if (end_ptr[0] == '%')
+ opacity *= 0.01;
+
+ return floor (opacity * 255 + 0.5);
+}
+
+double
+rsvg_css_parse_fontsize (const char *str)
+{
+ char *end_ptr;
+ double size;
+
+ /* todo: handle absolute-size and relative-size tags and proper units */
+ size = strtod (str, &end_ptr);
+
+ if (end_ptr[0] == '%')
+ size = (36 * size * 0.01); /* todo: egregious hack */
+
+ return size;
+}
+
diff --git a/librsvg/rsvg-css.h b/librsvg/rsvg-css.h
new file mode 100644
index 000000000..6a6b645e5
--- /dev/null
+++ b/librsvg/rsvg-css.h
@@ -0,0 +1,18 @@
+
+double
+rsvg_css_parse_length (const char *str, gint *fixed);
+
+gboolean
+rsvg_css_param_match (const char *str, const char *param_name);
+
+int
+rsvg_css_param_arg_offset (const char *str);
+
+guint32
+rsvg_css_parse_color (const char *str);
+
+guint
+rsvg_css_parse_opacity (const char *str);
+
+double
+rsvg_css_parse_fontsize (const char *str);
diff --git a/librsvg/rsvg-defs.c b/librsvg/rsvg-defs.c
new file mode 100644
index 000000000..103b7ed5e
--- /dev/null
+++ b/librsvg/rsvg-defs.c
@@ -0,0 +1,67 @@
+/*
+ rsvg-defs.c: Manage SVG defs and references.
+
+ Copyright (C) 2000 Eazel, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Raph Levien <raph@artofcode.com>
+*/
+
+#include <glib.h>
+#include "rsvg-defs.h"
+
+struct _RsvgDefs {
+ GHashTable *hash;
+};
+
+RsvgDefs *
+rsvg_defs_new (void)
+{
+ RsvgDefs *result = g_new (RsvgDefs, 1);
+
+ result->hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ return result;
+}
+
+RsvgDefVal *
+rsvg_defs_lookup (const RsvgDefs *defs, const char *name)
+{
+ return (RsvgDefVal *)g_hash_table_lookup (defs->hash, name);
+}
+
+void
+rsvg_defs_set (RsvgDefs *defs, const char *name, RsvgDefVal *val)
+{
+ g_hash_table_insert (defs->hash, g_strdup (name), val);
+}
+
+static void
+rsvg_defs_free_each (gpointer key, gpointer value, gpointer user_data)
+{
+ RsvgDefVal *def_val = (RsvgDefVal *)value;
+ g_free (key);
+ def_val->free (def_val);
+}
+
+void
+rsvg_defs_free (RsvgDefs *defs)
+{
+ g_hash_table_foreach (defs->hash, rsvg_defs_free_each, NULL);
+ g_hash_table_destroy (defs->hash);
+ g_free (defs);
+}
diff --git a/librsvg/rsvg-defs.h b/librsvg/rsvg-defs.h
new file mode 100644
index 000000000..a71aab312
--- /dev/null
+++ b/librsvg/rsvg-defs.h
@@ -0,0 +1,30 @@
+/* A module for handling SVG defs */
+
+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,
+ ie specific definable things? For now, we're going low level,
+ but it's not clear that's the best way to go. */
+ RSVG_DEF_LINGRAD,
+ RSVG_DEF_RADGRAD
+} RsvgDefType;
+
+struct _RsvgDefVal {
+ RsvgDefType type;
+ void (*free) (RsvgDefVal *self);
+};
+
+RsvgDefs *
+rsvg_defs_new (void);
+
+RsvgDefVal *
+rsvg_defs_lookup (const RsvgDefs *defs, const char *name);
+
+void
+rsvg_defs_set (RsvgDefs *defs, const char *name, RsvgDefVal *val);
+
+void
+rsvg_defs_free (RsvgDefs *defs);
diff --git a/librsvg/rsvg-paint-server.c b/librsvg/rsvg-paint-server.c
new file mode 100644
index 000000000..53e34e022
--- /dev/null
+++ b/librsvg/rsvg-paint-server.c
@@ -0,0 +1,345 @@
+/*
+ rsvg-paint-server.c: Implement the SVG paint server abstraction.
+
+ Copyright (C) 2000 Eazel, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Raph Levien <raph@artofcode.com>
+*/
+
+#include <string.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_affine.h>
+#include "art_render.h"
+#include "art_render_gradient.h"
+
+#include "rsvg-css.h"
+#include "rsvg-defs.h"
+#include "rsvg-paint-server.h"
+
+typedef struct _RsvgPaintServerSolid RsvgPaintServerSolid;
+typedef struct _RsvgPaintServerLinGrad RsvgPaintServerLinGrad;
+typedef struct _RsvgPaintServerRadGrad RsvgPaintServerRadGrad;
+
+struct _RsvgPaintServer {
+ int refcnt;
+ void (*free) (RsvgPaintServer *self);
+ void (*render) (RsvgPaintServer *self, ArtRender *ar, const RsvgPSCtx *ctx);
+};
+
+struct _RsvgPaintServerSolid {
+ RsvgPaintServer super;
+ guint32 rgb;
+};
+
+struct _RsvgPaintServerLinGrad {
+ RsvgPaintServer super;
+ RsvgLinearGradient *gradient;
+ ArtGradientLinear *agl;
+};
+
+struct _RsvgPaintServerRadGrad {
+ RsvgPaintServer super;
+ RsvgRadialGradient *gradient;
+ ArtGradientRadial *agr;
+};
+
+static void
+rsvg_paint_server_solid_free (RsvgPaintServer *self)
+{
+ g_free (self);
+}
+
+static void
+rsvg_paint_server_solid_render (RsvgPaintServer *self, ArtRender *ar,
+ const RsvgPSCtx *ctx)
+{
+ RsvgPaintServerSolid *z = (RsvgPaintServerSolid *)self;
+ guint32 rgb = z->rgb;
+ ArtPixMaxDepth color[3];
+
+ color[0] = ART_PIX_MAX_FROM_8 (rgb >> 16);
+ color[1] = ART_PIX_MAX_FROM_8 ((rgb >> 8) & 0xff);
+ color[2] = ART_PIX_MAX_FROM_8 (rgb & 0xff);
+
+ art_render_image_solid (ar, color);
+}
+
+static RsvgPaintServer *
+rsvg_paint_server_solid (guint32 rgb)
+{
+ RsvgPaintServerSolid *result = g_new (RsvgPaintServerSolid, 1);
+
+ result->super.refcnt = 1;
+ result->super.free = rsvg_paint_server_solid_free;
+ result->super.render = rsvg_paint_server_solid_render;
+
+ result->rgb = rgb;
+
+ return &result->super;
+}
+
+static void
+rsvg_paint_server_lin_grad_free (RsvgPaintServer *self)
+{
+ RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
+
+ g_free (z->agl->stops);
+ g_free (z->agl);
+ g_free (self);
+}
+
+static ArtGradientStop *
+rsvg_paint_art_stops_from_rsvg (RsvgGradientStops *rstops)
+{
+ ArtGradientStop *stops;
+ int n_stop = rstops->n_stop;
+ int i;
+
+ stops = g_new (ArtGradientStop, n_stop);
+ for (i = 0; i < n_stop; i++)
+ {
+ guint32 rgba;
+ guint32 r, g, b, a;
+
+ stops[i].offset = rstops->stop[i].offset;
+ rgba = rstops->stop[i].rgba;
+ /* convert from separated to premultiplied alpha */
+ a = rgba & 0xff;
+ r = (rgba >> 24) * a + 0x80;
+ r = (r + (r >> 8)) >> 8;
+ g = ((rgba >> 16) & 0xff) * a + 0x80;
+ g = (g + (g >> 8)) >> 8;
+ b = ((rgba >> 8) & 0xff) * a + 0x80;
+ b = (b + (b >> 8)) >> 8;
+ stops[i].color[0] = ART_PIX_MAX_FROM_8(r);
+ stops[i].color[1] = ART_PIX_MAX_FROM_8(g);
+ stops[i].color[2] = ART_PIX_MAX_FROM_8(b);
+ stops[i].color[3] = ART_PIX_MAX_FROM_8(a);
+ }
+ return stops;
+}
+
+static void
+rsvg_paint_server_lin_grad_render (RsvgPaintServer *self, ArtRender *ar,
+ const RsvgPSCtx *ctx)
+{
+ RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
+ RsvgLinearGradient *rlg = z->gradient;
+ ArtGradientLinear *agl;
+ double x1, y1, x2, y2;
+ double dx, dy, scale;
+
+ agl = z->agl;
+ if (agl == NULL)
+ {
+ agl = g_new (ArtGradientLinear, 1);
+ agl->n_stops = rlg->stops->n_stop;
+ agl->stops = rsvg_paint_art_stops_from_rsvg (rlg->stops);
+ z->agl = agl;
+ }
+
+ /* compute [xy][12] in pixel space */
+ /* todo: this code implicitly implements gradientUnits = userSpace */
+ x1 = rlg->x1 * rlg->affine[0] + rlg->y1 * rlg->affine[2] + rlg->affine[4];
+ y1 = rlg->x1 * rlg->affine[1] + rlg->y1 * rlg->affine[3] + rlg->affine[5];
+ x2 = rlg->x2 * rlg->affine[0] + rlg->y2 * rlg->affine[2] + rlg->affine[4];
+ y2 = rlg->x2 * rlg->affine[1] + rlg->y2 * rlg->affine[3] + rlg->affine[5];
+
+ /* solve a, b, c so ax1 + by1 + c = 0 and ax2 + by2 + c = 1, maximum
+ gradient is in x1,y1 to x2,y2 dir */
+ dx = x2 - x1;
+ dy = y2 - y1;
+ scale = 1.0 / (dx * dx + dy * dy);
+ agl->a = dx * scale;
+ agl->b = dy * scale;
+ agl->c = -(x1 * agl->a + y1 * agl->b);
+
+ agl->spread = rlg->spread;
+ art_render_gradient_linear (ar, agl, ART_FILTER_NEAREST);
+}
+
+static RsvgPaintServer *
+rsvg_paint_server_lin_grad (RsvgLinearGradient *gradient)
+{
+ RsvgPaintServerLinGrad *result = g_new (RsvgPaintServerLinGrad, 1);
+
+ result->super.refcnt = 1;
+ result->super.free = rsvg_paint_server_lin_grad_free;
+ result->super.render = rsvg_paint_server_lin_grad_render;
+
+ result->gradient = gradient;
+ result->agl = NULL;
+
+ return &result->super;
+}
+
+static void
+rsvg_paint_server_rad_grad_free (RsvgPaintServer *self)
+{
+ RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
+
+ g_free (z->agr->stops);
+ g_free (z->agr);
+ g_free (self);
+}
+
+static void
+rsvg_paint_server_rad_grad_render (RsvgPaintServer *self, ArtRender *ar,
+ const RsvgPSCtx *ctx)
+{
+ RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
+ RsvgRadialGradient *rrg = z->gradient;
+ ArtGradientRadial *agr;
+ double aff1[6], aff2[6];
+
+ agr = z->agr;
+ if (agr == NULL)
+ {
+ agr = g_new (ArtGradientRadial, 1);
+ agr->n_stops = rrg->stops->n_stop;
+ agr->stops = rsvg_paint_art_stops_from_rsvg (rrg->stops);
+ z->agr = agr;
+ }
+
+ /* todo: this code implicitly implements gradientUnits = userSpace */
+ art_affine_scale (aff1, rrg->r, rrg->r);
+ art_affine_translate (aff2, rrg->cx, rrg->cy);
+ art_affine_multiply (aff1, aff1, aff2);
+ art_affine_multiply (aff1, aff1, rrg->affine);
+ art_affine_invert (agr->affine, aff1);
+
+ agr->fx = (rrg->fx - rrg->cx) / rrg->r;
+ agr->fy = (rrg->fy - rrg->cy) / rrg->r;
+
+ art_render_gradient_radial (ar, agr, ART_FILTER_NEAREST);
+}
+
+static RsvgPaintServer *
+rsvg_paint_server_rad_grad (RsvgRadialGradient *gradient)
+{
+ RsvgPaintServerRadGrad *result = g_new (RsvgPaintServerRadGrad, 1);
+
+ result->super.refcnt = 1;
+ result->super.free = rsvg_paint_server_rad_grad_free;
+ result->super.render = rsvg_paint_server_rad_grad_render;
+
+ result->gradient = gradient;
+ result->agr = NULL;
+
+ return &result->super;
+}
+
+/**
+ * rsvg_paint_server_parse: Parse an SVG paint specification.
+ * @defs: Defs for looking up gradients.
+ * @str: The SVG paint specification string to parse.
+ *
+ * Parses the paint specification @str, creating a new paint server
+ * object.
+ *
+ * Return value: The newly created paint server, or NULL on error.
+ **/
+RsvgPaintServer *
+rsvg_paint_server_parse (const RsvgDefs *defs, const char *str)
+{
+ guint32 rgb;
+
+ if (!strcmp (str, "none"))
+ return NULL;
+ if (!strncmp (str, "url(", 4))
+ {
+ const char *p = str + 4;
+ int ix;
+ char *name;
+ RsvgDefVal *val;
+
+ while (isspace (*p)) p++;
+ if (*p != '#')
+ return NULL;
+ p++;
+ for (ix = 0; p[ix]; ix++)
+ if (p[ix] == ')') break;
+ if (p[ix] != ')')
+ return NULL;
+ name = g_strndup (p, ix);
+ val = rsvg_defs_lookup (defs, name);
+ g_free (name);
+ if (val == NULL)
+ return NULL;
+ switch (val->type)
+ {
+ case RSVG_DEF_LINGRAD:
+ return rsvg_paint_server_lin_grad ((RsvgLinearGradient *)val);
+ case RSVG_DEF_RADGRAD:
+ return rsvg_paint_server_rad_grad ((RsvgRadialGradient *)val);
+ default:
+ return NULL;
+ }
+ }
+ else
+ {
+ rgb = rsvg_css_parse_color (str);
+ return rsvg_paint_server_solid (rgb);
+ }
+}
+
+/**
+ * rsvg_render_paint_server: Render paint server as image source for libart.
+ * @ar: Libart render object.
+ * @ps: Paint server object.
+ *
+ * Hooks up @ps as an image source for a libart rendering operation.
+ **/
+void
+rsvg_render_paint_server (ArtRender *ar, RsvgPaintServer *ps,
+ const RsvgPSCtx *ctx)
+{
+ if (ps != NULL)
+ ps->render (ps, ar, ctx);
+}
+
+/**
+ * rsvg_paint_server_ref: Reference a paint server object.
+ * @ps: The paint server object to reference.
+ **/
+void
+rsvg_paint_server_ref (RsvgPaintServer *ps)
+{
+ if (ps == NULL)
+ return;
+ ps->refcnt++;
+}
+
+/**
+ * rsvg_paint_server_unref: Unreference a paint server object.
+ * @ps: The paint server object to unreference.
+ **/
+void
+rsvg_paint_server_unref (RsvgPaintServer *ps)
+{
+ if (ps == NULL)
+ return;
+ if (--ps->refcnt == 0)
+ ps->free (ps);
+}
diff --git a/librsvg/rsvg-paint-server.h b/librsvg/rsvg-paint-server.h
new file mode 100644
index 000000000..55e766d48
--- /dev/null
+++ b/librsvg/rsvg-paint-server.h
@@ -0,0 +1,66 @@
+typedef struct _RsvgGradientStop RsvgGradientStop;
+typedef struct _RsvgGradientStops RsvgGradientStops;
+typedef struct _RsvgLinearGradient RsvgLinearGradient;
+typedef struct _RsvgRadialGradient RsvgRadialGradient;
+
+typedef struct _RsvgPaintServer RsvgPaintServer;
+
+typedef struct _RsvgPSCtx RsvgPSCtx;
+
+struct _RsvgPSCtx {
+/* todo: we need to take in some context information, including:
+
+ 1. The global affine transformation.
+
+ 2. User coordinates at time of reference (to implement
+ gradientUnits = "userSpaceOnUse").
+
+ 3. Object bounding box (to implement gradientUnits =
+ "objectBoundingBox").
+
+ Maybe signal for lazy evaluation of object bbox.
+*/
+};
+
+struct _RsvgGradientStop {
+ double offset;
+ guint32 rgba;
+};
+
+struct _RsvgGradientStops {
+ int n_stop;
+ RsvgGradientStop *stop;
+};
+
+struct _RsvgLinearGradient {
+ RsvgDefVal super;
+ double affine[6]; /* user space to actual at time of gradient def */
+ double x1, y1;
+ double x2, y2;
+ ArtGradientSpread spread;
+ RsvgGradientStops *stops;
+};
+
+struct _RsvgRadialGradient {
+ RsvgDefVal super;
+ double affine[6]; /* user space to actual at time of gradient def */
+ double cx, cy;
+ double r;
+ double fx, fy;
+ RsvgGradientStops *stops;
+};
+
+/* Create a new paint server based on a specification string. */
+RsvgPaintServer *
+rsvg_paint_server_parse (const RsvgDefs *defs, const char *str);
+
+void
+rsvg_render_paint_server (ArtRender *ar, RsvgPaintServer *ps,
+ const RsvgPSCtx *ctx);
+
+void
+rsvg_paint_server_ref (RsvgPaintServer *ps);
+
+void
+rsvg_paint_server_unref (RsvgPaintServer *ps);
+
diff --git a/librsvg/rsvg.c b/librsvg/rsvg.c
index 235956824..326531062 100644
--- a/librsvg/rsvg.c
+++ b/librsvg/rsvg.c
@@ -29,6 +29,7 @@
#include <glib.h>
#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_filterlevel.h>
#include <libart_lgpl/art_affine.h>
#include <libart_lgpl/art_svp.h>
#include <libart_lgpl/art_bpath.h>
@@ -39,20 +40,26 @@
#include <libart_lgpl/art_svp_vpath.h>
#include <libart_lgpl/art_svp_wind.h>
-#include "art_rgba_svp.h"
+#include "art_render.h"
+#include "art_render_gradient.h"
+#include "art_render_svp.h"
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "SAX.h"
#include "rsvg-bpath-util.h"
+#include "rsvg-defs.h"
#include "rsvg-path.h"
+#include "rsvg-css.h"
+#include "rsvg-paint-server.h"
#include "rsvg.h"
#define noVERBOSE
typedef struct _RsvgCtx RsvgCtx;
typedef struct _RsvgState RsvgState;
+typedef struct _RsvgSaxHandler RsvgSaxHandler;
struct _RsvgCtx {
GdkPixbuf *pixbuf;
@@ -63,17 +70,20 @@ struct _RsvgCtx {
RsvgState *state;
int n_state;
int n_state_max;
+
+ RsvgDefs *defs;
+
+ RsvgSaxHandler *handler; /* should this be a handler stack? */
+ int handler_nest;
};
struct _RsvgState {
double affine[6];
- gboolean fill;
- guint32 fill_color; /* rgb */
+ RsvgPaintServer *fill;
gint fill_opacity; /* 0...255 */
- gboolean stroke;
- guint32 stroke_color; /* rgb */
+ RsvgPaintServer *stroke;
gint stroke_opacity; /* 0..255 */
double stroke_width;
@@ -81,6 +91,17 @@ struct _RsvgState {
ArtPathStrokeJoinType join;
double font_size;
+
+ guint32 stop_color; /* rgb */
+ gint stop_opacity; /* 0..255 */
+
+ gboolean in_defs;
+};
+
+struct _RsvgSaxHandler {
+ void (*free) (RsvgSaxHandler *self);
+ void (*start_element) (RsvgSaxHandler *self, const xmlChar *name, const xmlChar **atts);
+ void (*end_element) (RsvgSaxHandler *self, const xmlChar *name);
};
static RsvgCtx *
@@ -94,67 +115,58 @@ rsvg_ctx_new (void)
result->n_state = 0;
result->n_state_max = 16;
result->state = g_new (RsvgState, result->n_state_max);
+ result->defs = rsvg_defs_new ();
+ result->handler = NULL;
+ result->handler_nest = 0;
return result;
}
-/* does not destroy the pixbuf */
-static void
-rsvg_ctx_free (RsvgCtx *ctx)
-{
- free (ctx->state);
- free (ctx);
-}
-
static void
rsvg_state_init (RsvgState *state)
{
art_affine_identity (state->affine);
- state->fill = 0;
- state->fill_color = 0;
+ state->fill = rsvg_paint_server_parse (NULL, "#000");
state->fill_opacity = 0xff;
- state->stroke = 0;
- state->stroke_color = 0;
+ state->stroke = NULL;
state->stroke_opacity = 0xff;
state->stroke_width = 1;
state->cap = ART_PATH_STROKE_CAP_BUTT;
state->join = ART_PATH_STROKE_JOIN_MITER;
+ state->stop_color = 0;
+ state->stop_opacity = 0xff;
+
+ state->in_defs = FALSE;
}
-/**
- * rsvg_css_parse_length: Parse CSS2 length to a pixel value.
- * @str: Original string.
- * @fixed: Where to store boolean value of whether length is fixed.
- *
- * Parses a CSS2 length into a pixel value.
- *
- * Returns: returns the length.
- **/
-static double
-rsvg_css_parse_length (const char *str, gint *fixed)
+static void
+rsvg_state_clone (RsvgState *dst, const RsvgState *src)
{
- char *p;
-
- /*
- * The supported CSS length unit specifiers are:
- * em, ex, px, pt, pc, cm, mm, in, and percentages.
- */
-
- *fixed = FALSE;
+ *dst = *src;
+ rsvg_paint_server_ref (dst->fill);
+ rsvg_paint_server_ref (dst->stroke);
+}
- p = strstr (str, "px");
- if (p != NULL)
- {
- *fixed = TRUE;
- return atof (str);
- }
- p = strstr (str, "in");
- if (p != NULL)
- {
- *fixed = TRUE;
- /* return svg->pixels_per_inch * atof (str); */
- }
- return atof (str);
+static void
+rsvg_state_finalize (RsvgState *state)
+{
+ rsvg_paint_server_unref (state->fill);
+ rsvg_paint_server_unref (state->stroke);
+}
+
+/* does not destroy the pixbuf */
+static void
+rsvg_ctx_free (RsvgCtx *ctx)
+{
+ int i;
+
+ rsvg_defs_free (ctx->defs);
+
+ for (i = 0; i < ctx->n_state; i++)
+ rsvg_state_finalize (&ctx->state[i]);
+ free (ctx->state);
+
+ free (ctx);
}
static void
@@ -208,181 +220,38 @@ rsvg_start_svg (RsvgCtx *ctx, const xmlChar **atts)
}
}
-static gboolean
-rsvg_css_param_match (const char *str, const char *param_name)
-{
- int i;
-
- for (i = 0; str[i] != '\0' && str[i] != ':'; i++)
- if (param_name[i] != str[i])
- return FALSE;
- return str[i] == ':' && param_name[i] == '\0';
-}
-
-static int
-rsvg_css_param_arg_offset (const char *str)
-{
- int i;
-
- for (i = 0; str[i] != '\0' && str[i] != ':'; i++);
- if (str[i] != '\0') i++;
- for (; str[i] == ' '; i++);
- return i;
-}
-
-/* Parse a CSS2 color, returning rgb */
-static guint32
-rsvg_css_parse_color (const char *str)
-{
- gint val = 0;
- static GHashTable *colors = NULL;
-
- /*
- * todo: handle the rgb (r, g, b) and rgb ( r%, g%, b%), syntax
- * defined in http://www.w3.org/TR/REC-CSS2/syndata.html#color-units
- */
-#ifdef VERBOSE
- g_print ("color = %s\n", str);
-#endif
- if (str[0] == '#')
- {
- int i;
- for (i = 1; str[i]; i++)
- {
- int hexval;
- if (str[i] >= '0' && str[i] <= '9')
- hexval = str[i] - '0';
- else if (str[i] >= 'A' && str[i] <= 'F')
- hexval = str[i] - 'A' + 10;
- else if (str[i] >= 'a' && str[i] <= 'f')
- hexval = str[i] - 'a' + 10;
- else
- break;
- val = (val << 4) + hexval;
- }
- /* handle #rgb case */
- if (i == 4)
- {
- val = ((val & 0xf00) << 8) |
- ((val & 0x0f0) << 4) |
- (val & 0x00f);
- val |= val << 4;
- }
-#ifdef VERBOSE
- printf ("val = %x\n", val);
-#endif
- }
- else
- {
- GString * string;
- if (!colors)
- {
- colors = g_hash_table_new (g_str_hash, g_str_equal);
-
- g_hash_table_insert (colors, "black", GINT_TO_POINTER (0x000000));
- g_hash_table_insert (colors, "silver", GINT_TO_POINTER (0xc0c0c0));
- g_hash_table_insert (colors, "gray", GINT_TO_POINTER (0x808080));
- g_hash_table_insert (colors, "white", GINT_TO_POINTER (0xFFFFFF));
- g_hash_table_insert (colors, "maroon", GINT_TO_POINTER (0x800000));
- g_hash_table_insert (colors, "red", GINT_TO_POINTER (0xFF0000));
- g_hash_table_insert (colors, "purple", GINT_TO_POINTER (0x800080));
- g_hash_table_insert (colors, "fuchsia", GINT_TO_POINTER (0xFF00FF));
- g_hash_table_insert (colors, "green", GINT_TO_POINTER (0x008000));
- g_hash_table_insert (colors, "lime", GINT_TO_POINTER (0x00FF00));
- g_hash_table_insert (colors, "olive", GINT_TO_POINTER (0x808000));
- g_hash_table_insert (colors, "yellow", GINT_TO_POINTER (0xFFFF00));
- g_hash_table_insert (colors, "navy", GINT_TO_POINTER (0x000080));
- g_hash_table_insert (colors, "blue", GINT_TO_POINTER (0x0000FF));
- g_hash_table_insert (colors, "teal", GINT_TO_POINTER (0x008080));
- g_hash_table_insert (colors, "aqua", GINT_TO_POINTER (0x00FFFF));
- }
-
- string = g_string_down (g_string_new (str));
-
-
- /* this will default to black on a failed lookup */
- val = GPOINTER_TO_INT (g_hash_table_lookup (colors, string->str));
- }
-
- return val;
-}
-
-static guint
-rsvg_css_parse_opacity (const char *str)
-{
- char *end_ptr;
- double opacity;
-
- opacity = strtod (str, &end_ptr);
-
- if (end_ptr[0] == '%')
- opacity *= 0.01;
-
- return floor (opacity * 255 + 0.5);
-}
-
-static double
-rsvg_css_parse_fontsize (RsvgState *state, const char *str)
-{
- char *end_ptr;
- double size;
-
- /* todo: handle absolute-size and relative-size tags and proper units */
- size = strtod (str, &end_ptr);
-
- if (end_ptr[0] == '%')
- size = (state->font_size * size * 0.01);
-
- return size;
-}
-
/* Parse a CSS2 style argument, setting the SVG context attributes. */
static void
-rsvg_parse_style_arg (RsvgState *state, const char *str)
+rsvg_parse_style_arg (RsvgCtx *ctx, RsvgState *state, const char *str)
{
int arg_off;
+ arg_off = rsvg_css_param_arg_offset (str);
if (rsvg_css_param_match (str, "opacity"))
{
- arg_off = rsvg_css_param_arg_offset (str);
/* state->opacity = rsvg_css_parse_opacity (str + arg_off); */
}
else if (rsvg_css_param_match (str, "fill"))
{
- arg_off = rsvg_css_param_arg_offset (str);
- if (!strcmp (str + arg_off, "none"))
- state->fill = 0;
- else
- {
- state->fill = 1;
- state->fill_color = rsvg_css_parse_color (str + arg_off);
- }
+ rsvg_paint_server_unref (state->fill);
+ state->fill = rsvg_paint_server_parse (ctx->defs, str + arg_off);
}
else if (rsvg_css_param_match (str, "fill-opacity"))
{
- arg_off = rsvg_css_param_arg_offset (str);
state->fill_opacity = rsvg_css_parse_opacity (str + arg_off);
}
else if (rsvg_css_param_match (str, "stroke"))
{
- arg_off = rsvg_css_param_arg_offset (str);
- if (!strcmp (str + arg_off, "none"))
- state->stroke = 0;
- else
- {
- state->stroke = 1;
- state->stroke_color = rsvg_css_parse_color (str + arg_off);
- }
+ rsvg_paint_server_unref (state->stroke);
+ state->stroke = rsvg_paint_server_parse (ctx->defs, str + arg_off);
}
else if (rsvg_css_param_match (str, "stroke-width"))
{
int fixed;
- arg_off = rsvg_css_param_arg_offset (str);
state->stroke_width = rsvg_css_parse_length (str + arg_off, &fixed);
}
else if (rsvg_css_param_match (str, "stroke-linecap"))
{
- arg_off = rsvg_css_param_arg_offset (str);
if (!strcmp (str + arg_off, "butt"))
state->cap = ART_PATH_STROKE_CAP_BUTT;
else if (!strcmp (str + arg_off, "round"))
@@ -394,12 +263,10 @@ rsvg_parse_style_arg (RsvgState *state, const char *str)
}
else if (rsvg_css_param_match (str, "stroke-opacity"))
{
- arg_off = rsvg_css_param_arg_offset (str);
state->stroke_opacity = rsvg_css_parse_opacity (str + arg_off);
}
else if (rsvg_css_param_match (str, "stroke-linejoin"))
{
- arg_off = rsvg_css_param_arg_offset (str);
if (!strcmp (str + arg_off, "miter"))
state->join = ART_PATH_STROKE_JOIN_MITER;
else if (!strcmp (str + arg_off, "round"))
@@ -411,14 +278,20 @@ rsvg_parse_style_arg (RsvgState *state, const char *str)
}
else if (rsvg_css_param_match (str, "font-size"))
{
- arg_off = rsvg_css_param_arg_offset (str);
- state->font_size = rsvg_css_parse_fontsize (state, str + arg_off);
+ state->font_size = rsvg_css_parse_fontsize (str + arg_off);
}
else if (rsvg_css_param_match (str, "font-family"))
{
- arg_off = rsvg_css_param_arg_offset (str);
/* state->font_family = g_strdup (str + arg_off); */
}
+ else if (rsvg_css_param_match (str, "stop-color"))
+ {
+ state->stop_color = rsvg_css_parse_color (str + arg_off);
+ }
+ else if (rsvg_css_param_match (str, "stop-opacity"))
+ {
+ state->stop_opacity = rsvg_css_parse_opacity (str + arg_off);
+ }
}
@@ -429,7 +302,7 @@ rsvg_parse_style_arg (RsvgState *state, const char *str)
implementation will happen later.
*/
static void
-rsvg_parse_style (RsvgState *state, const char *str)
+rsvg_parse_style (RsvgCtx *ctx, RsvgState *state, const char *str)
{
int start, end;
char *arg;
@@ -441,7 +314,7 @@ rsvg_parse_style (RsvgState *state, const char *str)
arg = g_new (char, 1 + end - start);
memcpy (arg, str + start, end - start);
arg[end - start] = '\0';
- rsvg_parse_style_arg (state, arg);
+ rsvg_parse_style_arg (ctx, state, arg);
g_free (arg);
start = end;
if (str[start] == ';') start++;
@@ -468,7 +341,7 @@ rsvg_parse_style_attrs (RsvgCtx *ctx, const xmlChar **atts)
for (i = 0; atts[i] != NULL; i += 2)
{
if (!strcmp ((char *)atts[i], "style"))
- rsvg_parse_style (&ctx->state[ctx->n_state - 1],
+ rsvg_parse_style (ctx, &ctx->state[ctx->n_state - 1],
(char *)atts[i + 1]);
}
}
@@ -546,30 +419,37 @@ rsvg_close_vpath (const ArtVpath *src)
* rsvg_render_svp: Render an SVP.
* @ctx: Context in which to render.
* @svp: SVP to render.
- * @rgba: Color.
+ * @ps: Paint server for rendering.
+ * @opacity: Opacity as 0..0xff.
*
* Renders the SVP over the pixbuf in @ctx.
**/
static void
-rsvg_render_svp (RsvgCtx *ctx, const ArtSVP *svp, guint32 rgba)
+rsvg_render_svp (RsvgCtx *ctx, const ArtSVP *svp,
+ RsvgPaintServer *ps, int opacity)
{
GdkPixbuf *pixbuf;
- pixbuf = ctx->pixbuf;
+ ArtRender *render;
+ gboolean has_alpha;
- if (gdk_pixbuf_get_has_alpha (pixbuf))
- art_rgba_svp_alpha (svp, 0, 0, gdk_pixbuf_get_width (pixbuf),
- gdk_pixbuf_get_height (pixbuf),
- rgba,
- gdk_pixbuf_get_pixels (pixbuf),
- gdk_pixbuf_get_rowstride (pixbuf),
- NULL);
- else
- art_rgb_svp_alpha (svp, 0, 0, gdk_pixbuf_get_width (pixbuf),
- gdk_pixbuf_get_height (pixbuf),
- rgba,
- gdk_pixbuf_get_pixels (pixbuf),
- gdk_pixbuf_get_rowstride (pixbuf),
- NULL);
+ pixbuf = ctx->pixbuf;
+ has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
+
+ render = art_render_new (0, 0,
+ gdk_pixbuf_get_width (pixbuf),
+ gdk_pixbuf_get_height (pixbuf),
+ gdk_pixbuf_get_pixels (pixbuf),
+ gdk_pixbuf_get_rowstride (pixbuf),
+ gdk_pixbuf_get_n_channels (pixbuf) -
+ (has_alpha ? 1 : 0),
+ 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));
+ rsvg_render_paint_server (render, ps, NULL); /* todo: paint server ctx */
+ art_render_invoke (render);
}
static void
@@ -589,7 +469,7 @@ rsvg_render_bpath (RsvgCtx *ctx, const ArtBpath *bpath)
vpath = art_bez_path_to_vec (affine_bpath, 0.25);
art_free (affine_bpath);
- if (state->fill)
+ if (state->fill != NULL)
{
ArtVpath *closed_vpath;
ArtVpath *perturbed_vpath;
@@ -607,22 +487,22 @@ rsvg_render_bpath (RsvgCtx *ctx, const ArtBpath *bpath)
svp = art_svp_rewind_uncrossed (tmp_svp, art_wind);
art_svp_free (tmp_svp);
- rsvg_render_svp (ctx, svp,
- (state->fill_color << 8) | state->fill_opacity);
+ rsvg_render_svp (ctx, svp, state->fill, state->fill_opacity);
art_svp_free (svp);
}
- if (state->stroke)
+ if (state->stroke != NULL)
{
- double stroke_width = state->stroke_width;
+ /* todo: libart doesn't yet implement anamorphic scaling of strokes */
+ double stroke_width = state->stroke_width *
+ art_affine_expansion (state->affine);
if (stroke_width < 0.25)
stroke_width = 0.25;
svp = art_svp_vpath_stroke (vpath, state->join, state->cap,
stroke_width, 4, 0.25);
- rsvg_render_svp (ctx, svp,
- (state->stroke_color << 8) | state->stroke_opacity);
+ rsvg_render_svp (ctx, svp, state->stroke, state->stroke_opacity);
art_svp_free (svp);
}
art_free (vpath);
@@ -656,6 +536,225 @@ rsvg_start_path (RsvgCtx *ctx, const xmlChar **atts)
}
static void
+rsvg_start_defs (RsvgCtx *ctx, const xmlChar **atts)
+{
+ RsvgState *state = &ctx->state[ctx->n_state - 1];
+
+ state->in_defs = TRUE;
+}
+
+typedef struct _RsvgSaxHandlerGstops RsvgSaxHandlerGstops;
+
+struct _RsvgSaxHandlerGstops {
+ RsvgSaxHandler super;
+ RsvgCtx *ctx;
+ RsvgGradientStops *stops;
+};
+
+static void
+rsvg_gradient_stop_handler_free (RsvgSaxHandler *self)
+{
+ g_free (self);
+}
+
+static void
+rsvg_gradient_stop_handler_start (RsvgSaxHandler *self, const xmlChar *name,
+ const xmlChar **atts)
+{
+ RsvgSaxHandlerGstops *z = (RsvgSaxHandlerGstops *)self;
+ RsvgGradientStops *stops = z->stops;
+ int i;
+ double offset = 0;
+ gboolean got_offset = FALSE;
+ gint fixed;
+ RsvgState state;
+ int n_stop;
+
+ if (strcmp ((char *)name, "stop"))
+ {
+ g_warning ("unexpected <%s> element in gradient\n", name);
+ return;
+ }
+
+ rsvg_state_init (&state);
+
+ if (atts != NULL)
+ {
+ for (i = 0; atts[i] != NULL; i += 2)
+ {
+ if (!strcmp ((char *)atts[i], "offset"))
+ {
+ offset = rsvg_css_parse_length ((char *)atts[i + 1], &fixed);
+ got_offset = TRUE;
+ }
+ else if (!strcmp ((char *)atts[i], "style"))
+ rsvg_parse_style (z->ctx, &state, (char *)atts[i + 1]);
+ }
+ }
+
+ rsvg_state_finalize (&state);
+
+ if (!got_offset)
+ {
+ g_warning ("gradient stop must specify offset\n");
+ return;
+ }
+
+ n_stop = stops->n_stop++;
+ if (n_stop == 0)
+ stops->stop = g_new (RsvgGradientStop, 1);
+ else if (!(n_stop & (n_stop - 1)))
+ /* double the allocation if size is a power of two */
+ stops->stop = g_renew (RsvgGradientStop, stops->stop, n_stop << 1);
+ stops->stop[n_stop].offset = offset;
+ stops->stop[n_stop].rgba = (state.stop_color << 8) | state.stop_opacity;
+}
+
+static void
+rsvg_gradient_stop_handler_end (RsvgSaxHandler *self, const xmlChar *name)
+{
+}
+
+static RsvgSaxHandler *
+rsvg_gradient_stop_handler_new (RsvgCtx *ctx, RsvgGradientStops **p_stops)
+{
+ RsvgSaxHandlerGstops *gstops = g_new (RsvgSaxHandlerGstops, 1);
+ RsvgGradientStops *stops = g_new (RsvgGradientStops, 1);
+
+ gstops->super.free = rsvg_gradient_stop_handler_free;
+ gstops->super.start_element = rsvg_gradient_stop_handler_start;
+ gstops->super.end_element = rsvg_gradient_stop_handler_end;
+ gstops->ctx = ctx;
+ gstops->stops = stops;
+
+ stops->n_stop = 0;
+ stops->stop = NULL;
+
+ *p_stops = stops;
+ return &gstops->super;
+}
+
+static void
+rsvg_linear_gradient_free (RsvgDefVal *self)
+{
+ RsvgLinearGradient *z = (RsvgLinearGradient *)self;
+
+ g_free (z->stops->stop);
+ g_free (z->stops);
+ g_free (self);
+}
+
+static void
+rsvg_start_linear_gradient (RsvgCtx *ctx, const xmlChar **atts)
+{
+ RsvgState *state = &ctx->state[ctx->n_state - 1];
+ RsvgLinearGradient *grad;
+ int i;
+ char *id = NULL;
+ double x1 = 0, y1 = 0, x2 = 100, y2 = 0;
+ ArtGradientSpread spread = ART_GRADIENT_PAD;
+
+ /* todo: only handles numeric coordinates in gradientUnits = userSpace */
+ if (atts != NULL)
+ {
+ for (i = 0; atts[i] != NULL; i += 2)
+ {
+ if (!strcmp ((char *)atts[i], "id"))
+ id = (char *)atts[i + 1];
+ else if (!strcmp ((char *)atts[i], "x1"))
+ x1 = atof ((char *)atts[i + 1]);
+ else if (!strcmp ((char *)atts[i], "y1"))
+ y1 = atof ((char *)atts[i + 1]);
+ else if (!strcmp ((char *)atts[i], "x2"))
+ x2 = atof ((char *)atts[i + 1]);
+ else if (!strcmp ((char *)atts[i], "y2"))
+ y2 = atof ((char *)atts[i + 1]);
+ else if (!strcmp ((char *)atts[i], "spreadMethod"))
+ {
+ if (!strcmp ((char *)atts[i + 1], "pad"))
+ spread = ART_GRADIENT_PAD;
+ else if (!strcmp ((char *)atts[i + 1], "reflect"))
+ spread = ART_GRADIENT_REFLECT;
+ else if (!strcmp ((char *)atts[i + 1], "repeat"))
+ spread = ART_GRADIENT_REPEAT;
+ }
+ }
+ }
+
+ grad = g_new (RsvgLinearGradient, 1);
+ grad->super.type = RSVG_DEF_LINGRAD;
+ grad->super.free = rsvg_linear_gradient_free;
+
+ ctx->handler = rsvg_gradient_stop_handler_new (ctx, &grad->stops);
+
+ rsvg_defs_set (ctx->defs, id, &grad->super);
+
+ for (i = 0; i < 6; i++)
+ grad->affine[i] = state->affine[i];
+ grad->x1 = x1;
+ grad->y1 = y1;
+ grad->x2 = x2;
+ grad->y2 = y2;
+ grad->spread = spread;
+}
+
+static void
+rsvg_radial_gradient_free (RsvgDefVal *self)
+{
+ RsvgRadialGradient *z = (RsvgRadialGradient *)self;
+
+ g_free (z->stops->stop);
+ g_free (z->stops);
+ g_free (self);
+}
+
+static void
+rsvg_start_radial_gradient (RsvgCtx *ctx, const xmlChar **atts)
+{
+ RsvgState *state = &ctx->state[ctx->n_state - 1];
+ RsvgRadialGradient *grad;
+ int i;
+ char *id = NULL;
+ double cx = 50, cy = 50, r = 50, fx = 50, fy = 50;
+
+ /* todo: only handles numeric coordinates in gradientUnits = userSpace */
+ if (atts != NULL)
+ {
+ for (i = 0; atts[i] != NULL; i += 2)
+ {
+ if (!strcmp ((char *)atts[i], "id"))
+ id = (char *)atts[i + 1];
+ else if (!strcmp ((char *)atts[i], "cx"))
+ cx = atof ((char *)atts[i + 1]);
+ else if (!strcmp ((char *)atts[i], "cy"))
+ cy = atof ((char *)atts[i + 1]);
+ else if (!strcmp ((char *)atts[i], "r"))
+ r = atof ((char *)atts[i + 1]);
+ else if (!strcmp ((char *)atts[i], "fx"))
+ fx = atof ((char *)atts[i + 1]);
+ else if (!strcmp ((char *)atts[i], "fy"))
+ fy = atof ((char *)atts[i + 1]);
+ }
+ }
+
+ grad = g_new (RsvgRadialGradient, 1);
+ grad->super.type = RSVG_DEF_RADGRAD;
+ grad->super.free = rsvg_radial_gradient_free;
+
+ ctx->handler = rsvg_gradient_stop_handler_new (ctx, &grad->stops);
+
+ rsvg_defs_set (ctx->defs, id, &grad->super);
+
+ for (i = 0; i < 6; i++)
+ grad->affine[i] = state->affine[i];
+ grad->cx = cx;
+ grad->cy = cy;
+ grad->r = r;
+ grad->fx = fx;
+ grad->fy = fy;
+}
+
+static void
rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts)
{
RsvgCtx *ctx = (RsvgCtx *)data;
@@ -674,21 +773,36 @@ rsvg_start_element (void *data, const xmlChar *name, const xmlChar **atts)
fprintf (stdout, ")\n");
#endif
- /* 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)
- ctx->state[ctx->n_state] = ctx->state[ctx->n_state - 1];
+ if (ctx->handler)
+ {
+ ctx->handler_nest++;
+ ctx->handler->start_element (ctx->handler, name, atts);
+ }
else
- rsvg_state_init (ctx->state);
- ctx->n_state++;
-
- if (!strcmp ((char *)name, "svg"))
- rsvg_start_svg (ctx, atts);
- else if (!strcmp ((char *)name, "g"))
- rsvg_start_g (ctx, atts);
- else if (!strcmp ((char *)name, "path"))
- rsvg_start_path (ctx, atts);
+ {
+ /* 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_clone (&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);
+ else if (!strcmp ((char *)name, "g"))
+ rsvg_start_g (ctx, atts);
+ else if (!strcmp ((char *)name, "path"))
+ rsvg_start_path (ctx, atts);
+ else if (!strcmp ((char *)name, "defs"))
+ rsvg_start_defs (ctx, atts);
+ else if (!strcmp ((char *)name, "linearGradient"))
+ rsvg_start_linear_gradient (ctx, atts);
+ else if (!strcmp ((char *)name, "radialGradient"))
+ rsvg_start_radial_gradient (ctx, atts);
+ }
}
static void
@@ -696,12 +810,27 @@ rsvg_end_element (void *data, const xmlChar *name)
{
RsvgCtx *ctx = (RsvgCtx *)data;
- /* pop the state stack */
- ctx->n_state--;
+ if (ctx->handler_nest > 0)
+ {
+ ctx->handler->end_element (ctx->handler, name);
+ ctx->handler_nest--;
+ }
+ else
+ {
+ if (ctx->handler != NULL)
+ {
+ ctx->handler->free (ctx->handler);
+ ctx->handler = NULL;
+ }
+
+ /* pop the state stack */
+ ctx->n_state--;
+ rsvg_state_finalize (&ctx->state[ctx->n_state]);
#ifdef VERBOSE
- fprintf (stdout, "SAX.endElement(%s)\n", (char *) name);
+ fprintf (stdout, "SAX.endElement(%s)\n", (char *) name);
#endif
+ }
}
xmlSAXHandler emptySAXHandlerStruct = {
diff --git a/librsvg/test-rsvg.c b/librsvg/test-rsvg.c
index 618491ba3..67e924d5e 100644
--- a/librsvg/test-rsvg.c
+++ b/librsvg/test-rsvg.c
@@ -158,44 +158,57 @@ main (int argc, char **argv)
char *out_fn;
GdkPixbuf *pixbuf;
char *zoom_str = "1.0";
+ int n_iter = 1;
poptContext optCtx;
struct poptOption optionsTable[] = {
{ "zoom", 'z', POPT_ARG_STRING, &zoom_str, 0, NULL, "zoom factor" },
+ { "num-iter", 'n', POPT_ARG_INT, &n_iter, 0, NULL, "number of iterations" },
POPT_AUTOHELP
{ NULL, 0, 0, NULL, 0 }
};
char c;
const char * const *args;
+ int i;
optCtx = poptGetContext ("test-rsvg", argc, (const char **)argv, optionsTable, 0);
c = poptGetNextOpt (optCtx);
args = poptGetArgs (optCtx);
- if (args == NULL || args[0] == NULL) {
- f = stdin;
- out_fn = "-";
- } else {
- f = fopen(args[0], "r");
- if (f == NULL) {
- fprintf(stderr, "Error opening source file %s\n", argv[0]);
- }
- if (args[1] == NULL)
+ for (i = 0; i < n_iter; i++) {
+ if (args == NULL || args[0] == NULL) {
+ if (n_iter > 1) {
+ fprintf (stderr, "Can't do multiple iterations on stdin\n");
+ exit (1);
+ }
+
+ f = stdin;
out_fn = "-";
- else
- out_fn = (char *)args[1];
- }
+ } else {
+ f = fopen(args[0], "r");
+ if (f == NULL) {
+ fprintf(stderr, "Error opening source file %s\n", argv[0]);
+ }
+ if (args[1] == NULL)
+ out_fn = "-";
+ else
+ out_fn = (char *)args[1];
+ }
- pixbuf = rsvg_render_file (f, atof (zoom_str));
+ pixbuf = rsvg_render_file (f, atof (zoom_str));
- if (f != stdin)
- fclose(f);
+ if (f != stdin)
+ fclose(f);
- if (pixbuf != NULL)
- save_pixbuf_to_file (pixbuf, out_fn);
- else {
- fprintf (stderr, "Error loading SVG file.\n");
- return 1;
+ if (pixbuf != NULL) {
+ if (n_iter > 1)
+ gdk_pixbuf_unref (pixbuf);
+ else
+ save_pixbuf_to_file (pixbuf, out_fn);
+ } else {
+ fprintf (stderr, "Error loading SVG file.\n");
+ return 1;
+ }
}
return 0;
diff --git a/librsvg/test.svg b/librsvg/test.svg
new file mode 100644
index 000000000..7d5f10b32
--- /dev/null
+++ b/librsvg/test.svg
@@ -0,0 +1,21 @@
+<svg width="200" height="200">
+ <defs>
+ <linearGradient id="LinGradient"
+ x1="50" y1="50" x2="150" y2="150" spreadMethod="repeat">
+ <stop offset="0%" style="stop-color:#00F; stop-opacity:10%"/>
+ <stop offset="100%" style="stop-color:#FFF"/>
+ </linearGradient>
+ <radialGradient id="RadGradient"
+ cx="100" cy="100" r="50" fx="145" fy="100">
+ <stop offset="0%" style="stop-color:#F00"/>
+ <stop offset="50%" style="stop-color:#FF0; stop-opacity:90%"/>
+ <stop offset="100%" style="stop-color:#000; stop-opacity:0"/>
+ </radialGradient>
+ </defs>
+ <g style="fill: url(#LinGradient)">
+ <path d="M30,30 L30,170 L170,170 L170,30z"/>
+ </g>
+ <g style="fill: url(#RadGradient); stroke: #000; stroke-width:3">
+ <path d="M50,50 L50,150 L150,150 L150,50z"/>
+ </g>
+</svg>