From 55cffa7797e8d5f2eac983a312b37c32cd6a6445 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Mon, 29 May 2000 22:12:11 +0000 Subject: Added gradient handling to librsvg. --- librsvg/Makefile.am | 20 +- librsvg/art_render.c | 1140 +++++++++++++++++++++++++++++++++++++++++ librsvg/art_render.h | 188 +++++++ librsvg/art_render_gradient.c | 303 +++++++++++ librsvg/art_render_gradient.h | 90 ++++ librsvg/art_render_svp.c | 398 ++++++++++++++ librsvg/art_render_svp.h | 51 ++ librsvg/art_rgba.c | 259 ---------- librsvg/art_rgba.h | 43 -- librsvg/art_rgba_svp.c | 280 ---------- librsvg/art_rgba_svp.h | 40 -- librsvg/rsvg-css.c | 201 ++++++++ librsvg/rsvg-css.h | 18 + librsvg/rsvg-defs.c | 67 +++ librsvg/rsvg-defs.h | 30 ++ librsvg/rsvg-paint-server.c | 345 +++++++++++++ librsvg/rsvg-paint-server.h | 66 +++ librsvg/rsvg.c | 623 +++++++++++++--------- librsvg/test-rsvg.c | 53 +- librsvg/test.svg | 21 + 20 files changed, 3341 insertions(+), 895 deletions(-) create mode 100644 librsvg/art_render.c create mode 100644 librsvg/art_render.h create mode 100644 librsvg/art_render_gradient.c create mode 100644 librsvg/art_render_gradient.h create mode 100644 librsvg/art_render_svp.c create mode 100644 librsvg/art_render_svp.h delete mode 100644 librsvg/art_rgba.c delete mode 100644 librsvg/art_rgba.h delete mode 100644 librsvg/art_rgba_svp.c delete mode 100644 librsvg/art_rgba_svp.h create mode 100644 librsvg/rsvg-css.c create mode 100644 librsvg/rsvg-css.h create mode 100644 librsvg/rsvg-defs.c create mode 100644 librsvg/rsvg-defs.h create mode 100644 librsvg/rsvg-paint-server.c create mode 100644 librsvg/rsvg-paint-server.h create mode 100644 librsvg/test.svg (limited to 'librsvg') 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 + +#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 + */ + +#include +#include +#include + +#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 + +#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 + +#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 + +#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 + * Alexander Larsson + */ + +#include + +#include +#include +#include + +#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 + +#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 + * Alexander Larsson + */ + +#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 + +#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 + +#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 + */ + +/* Use an SVP as a render mask source. */ + +#include +#include +#include +#include + +#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 + +#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 + */ + +#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 + +#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 -#include -#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 -#include -#include -#include -#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 - -#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 +*/ + +#include +#include +#include + +#include + +#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 +*/ + +#include +#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 +*/ + +#include +#include + +#include + +#include +#include +#include +#include +#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 #include +#include #include #include #include @@ -39,20 +40,26 @@ #include #include -#include "art_rgba_svp.h" +#include "art_render.h" +#include "art_render_gradient.h" +#include "art_render_svp.h" #include #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); @@ -655,6 +535,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) { @@ -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 @@ + + + + + + + + + + + + + + + + + + + -- cgit v1.2.1