summaryrefslogtreecommitdiff
path: root/librsvg/rsvg-paint-server.c
diff options
context:
space:
mode:
authorRaph Levien <raph@src.gnome.org>2000-05-29 22:12:11 +0000
committerRaph Levien <raph@src.gnome.org>2000-05-29 22:12:11 +0000
commit55cffa7797e8d5f2eac983a312b37c32cd6a6445 (patch)
treecf8fa81b376fd77de75345d88a3c4226cfe90586 /librsvg/rsvg-paint-server.c
parent17b8049c949872e40db2f603fad2dd1552168dfb (diff)
downloadnautilus-55cffa7797e8d5f2eac983a312b37c32cd6a6445.tar.gz
Added gradient handling to librsvg.
Diffstat (limited to 'librsvg/rsvg-paint-server.c')
-rw-r--r--librsvg/rsvg-paint-server.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/librsvg/rsvg-paint-server.c b/librsvg/rsvg-paint-server.c
new file mode 100644
index 000000000..53e34e022
--- /dev/null
+++ b/librsvg/rsvg-paint-server.c
@@ -0,0 +1,345 @@
+/*
+ rsvg-paint-server.c: Implement the SVG paint server abstraction.
+
+ Copyright (C) 2000 Eazel, Inc.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with this program; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+
+ Author: Raph Levien <raph@artofcode.com>
+*/
+
+#include <string.h>
+#include <ctype.h>
+
+#include <glib.h>
+
+#include <libart_lgpl/art_misc.h>
+#include <libart_lgpl/art_alphagamma.h>
+#include <libart_lgpl/art_filterlevel.h>
+#include <libart_lgpl/art_affine.h>
+#include "art_render.h"
+#include "art_render_gradient.h"
+
+#include "rsvg-css.h"
+#include "rsvg-defs.h"
+#include "rsvg-paint-server.h"
+
+typedef struct _RsvgPaintServerSolid RsvgPaintServerSolid;
+typedef struct _RsvgPaintServerLinGrad RsvgPaintServerLinGrad;
+typedef struct _RsvgPaintServerRadGrad RsvgPaintServerRadGrad;
+
+struct _RsvgPaintServer {
+ int refcnt;
+ void (*free) (RsvgPaintServer *self);
+ void (*render) (RsvgPaintServer *self, ArtRender *ar, const RsvgPSCtx *ctx);
+};
+
+struct _RsvgPaintServerSolid {
+ RsvgPaintServer super;
+ guint32 rgb;
+};
+
+struct _RsvgPaintServerLinGrad {
+ RsvgPaintServer super;
+ RsvgLinearGradient *gradient;
+ ArtGradientLinear *agl;
+};
+
+struct _RsvgPaintServerRadGrad {
+ RsvgPaintServer super;
+ RsvgRadialGradient *gradient;
+ ArtGradientRadial *agr;
+};
+
+static void
+rsvg_paint_server_solid_free (RsvgPaintServer *self)
+{
+ g_free (self);
+}
+
+static void
+rsvg_paint_server_solid_render (RsvgPaintServer *self, ArtRender *ar,
+ const RsvgPSCtx *ctx)
+{
+ RsvgPaintServerSolid *z = (RsvgPaintServerSolid *)self;
+ guint32 rgb = z->rgb;
+ ArtPixMaxDepth color[3];
+
+ color[0] = ART_PIX_MAX_FROM_8 (rgb >> 16);
+ color[1] = ART_PIX_MAX_FROM_8 ((rgb >> 8) & 0xff);
+ color[2] = ART_PIX_MAX_FROM_8 (rgb & 0xff);
+
+ art_render_image_solid (ar, color);
+}
+
+static RsvgPaintServer *
+rsvg_paint_server_solid (guint32 rgb)
+{
+ RsvgPaintServerSolid *result = g_new (RsvgPaintServerSolid, 1);
+
+ result->super.refcnt = 1;
+ result->super.free = rsvg_paint_server_solid_free;
+ result->super.render = rsvg_paint_server_solid_render;
+
+ result->rgb = rgb;
+
+ return &result->super;
+}
+
+static void
+rsvg_paint_server_lin_grad_free (RsvgPaintServer *self)
+{
+ RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
+
+ g_free (z->agl->stops);
+ g_free (z->agl);
+ g_free (self);
+}
+
+static ArtGradientStop *
+rsvg_paint_art_stops_from_rsvg (RsvgGradientStops *rstops)
+{
+ ArtGradientStop *stops;
+ int n_stop = rstops->n_stop;
+ int i;
+
+ stops = g_new (ArtGradientStop, n_stop);
+ for (i = 0; i < n_stop; i++)
+ {
+ guint32 rgba;
+ guint32 r, g, b, a;
+
+ stops[i].offset = rstops->stop[i].offset;
+ rgba = rstops->stop[i].rgba;
+ /* convert from separated to premultiplied alpha */
+ a = rgba & 0xff;
+ r = (rgba >> 24) * a + 0x80;
+ r = (r + (r >> 8)) >> 8;
+ g = ((rgba >> 16) & 0xff) * a + 0x80;
+ g = (g + (g >> 8)) >> 8;
+ b = ((rgba >> 8) & 0xff) * a + 0x80;
+ b = (b + (b >> 8)) >> 8;
+ stops[i].color[0] = ART_PIX_MAX_FROM_8(r);
+ stops[i].color[1] = ART_PIX_MAX_FROM_8(g);
+ stops[i].color[2] = ART_PIX_MAX_FROM_8(b);
+ stops[i].color[3] = ART_PIX_MAX_FROM_8(a);
+ }
+ return stops;
+}
+
+static void
+rsvg_paint_server_lin_grad_render (RsvgPaintServer *self, ArtRender *ar,
+ const RsvgPSCtx *ctx)
+{
+ RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
+ RsvgLinearGradient *rlg = z->gradient;
+ ArtGradientLinear *agl;
+ double x1, y1, x2, y2;
+ double dx, dy, scale;
+
+ agl = z->agl;
+ if (agl == NULL)
+ {
+ agl = g_new (ArtGradientLinear, 1);
+ agl->n_stops = rlg->stops->n_stop;
+ agl->stops = rsvg_paint_art_stops_from_rsvg (rlg->stops);
+ z->agl = agl;
+ }
+
+ /* compute [xy][12] in pixel space */
+ /* todo: this code implicitly implements gradientUnits = userSpace */
+ x1 = rlg->x1 * rlg->affine[0] + rlg->y1 * rlg->affine[2] + rlg->affine[4];
+ y1 = rlg->x1 * rlg->affine[1] + rlg->y1 * rlg->affine[3] + rlg->affine[5];
+ x2 = rlg->x2 * rlg->affine[0] + rlg->y2 * rlg->affine[2] + rlg->affine[4];
+ y2 = rlg->x2 * rlg->affine[1] + rlg->y2 * rlg->affine[3] + rlg->affine[5];
+
+ /* solve a, b, c so ax1 + by1 + c = 0 and ax2 + by2 + c = 1, maximum
+ gradient is in x1,y1 to x2,y2 dir */
+ dx = x2 - x1;
+ dy = y2 - y1;
+ scale = 1.0 / (dx * dx + dy * dy);
+ agl->a = dx * scale;
+ agl->b = dy * scale;
+ agl->c = -(x1 * agl->a + y1 * agl->b);
+
+ agl->spread = rlg->spread;
+ art_render_gradient_linear (ar, agl, ART_FILTER_NEAREST);
+}
+
+static RsvgPaintServer *
+rsvg_paint_server_lin_grad (RsvgLinearGradient *gradient)
+{
+ RsvgPaintServerLinGrad *result = g_new (RsvgPaintServerLinGrad, 1);
+
+ result->super.refcnt = 1;
+ result->super.free = rsvg_paint_server_lin_grad_free;
+ result->super.render = rsvg_paint_server_lin_grad_render;
+
+ result->gradient = gradient;
+ result->agl = NULL;
+
+ return &result->super;
+}
+
+static void
+rsvg_paint_server_rad_grad_free (RsvgPaintServer *self)
+{
+ RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
+
+ g_free (z->agr->stops);
+ g_free (z->agr);
+ g_free (self);
+}
+
+static void
+rsvg_paint_server_rad_grad_render (RsvgPaintServer *self, ArtRender *ar,
+ const RsvgPSCtx *ctx)
+{
+ RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
+ RsvgRadialGradient *rrg = z->gradient;
+ ArtGradientRadial *agr;
+ double aff1[6], aff2[6];
+
+ agr = z->agr;
+ if (agr == NULL)
+ {
+ agr = g_new (ArtGradientRadial, 1);
+ agr->n_stops = rrg->stops->n_stop;
+ agr->stops = rsvg_paint_art_stops_from_rsvg (rrg->stops);
+ z->agr = agr;
+ }
+
+ /* todo: this code implicitly implements gradientUnits = userSpace */
+ art_affine_scale (aff1, rrg->r, rrg->r);
+ art_affine_translate (aff2, rrg->cx, rrg->cy);
+ art_affine_multiply (aff1, aff1, aff2);
+ art_affine_multiply (aff1, aff1, rrg->affine);
+ art_affine_invert (agr->affine, aff1);
+
+ agr->fx = (rrg->fx - rrg->cx) / rrg->r;
+ agr->fy = (rrg->fy - rrg->cy) / rrg->r;
+
+ art_render_gradient_radial (ar, agr, ART_FILTER_NEAREST);
+}
+
+static RsvgPaintServer *
+rsvg_paint_server_rad_grad (RsvgRadialGradient *gradient)
+{
+ RsvgPaintServerRadGrad *result = g_new (RsvgPaintServerRadGrad, 1);
+
+ result->super.refcnt = 1;
+ result->super.free = rsvg_paint_server_rad_grad_free;
+ result->super.render = rsvg_paint_server_rad_grad_render;
+
+ result->gradient = gradient;
+ result->agr = NULL;
+
+ return &result->super;
+}
+
+/**
+ * rsvg_paint_server_parse: Parse an SVG paint specification.
+ * @defs: Defs for looking up gradients.
+ * @str: The SVG paint specification string to parse.
+ *
+ * Parses the paint specification @str, creating a new paint server
+ * object.
+ *
+ * Return value: The newly created paint server, or NULL on error.
+ **/
+RsvgPaintServer *
+rsvg_paint_server_parse (const RsvgDefs *defs, const char *str)
+{
+ guint32 rgb;
+
+ if (!strcmp (str, "none"))
+ return NULL;
+ if (!strncmp (str, "url(", 4))
+ {
+ const char *p = str + 4;
+ int ix;
+ char *name;
+ RsvgDefVal *val;
+
+ while (isspace (*p)) p++;
+ if (*p != '#')
+ return NULL;
+ p++;
+ for (ix = 0; p[ix]; ix++)
+ if (p[ix] == ')') break;
+ if (p[ix] != ')')
+ return NULL;
+ name = g_strndup (p, ix);
+ val = rsvg_defs_lookup (defs, name);
+ g_free (name);
+ if (val == NULL)
+ return NULL;
+ switch (val->type)
+ {
+ case RSVG_DEF_LINGRAD:
+ return rsvg_paint_server_lin_grad ((RsvgLinearGradient *)val);
+ case RSVG_DEF_RADGRAD:
+ return rsvg_paint_server_rad_grad ((RsvgRadialGradient *)val);
+ default:
+ return NULL;
+ }
+ }
+ else
+ {
+ rgb = rsvg_css_parse_color (str);
+ return rsvg_paint_server_solid (rgb);
+ }
+}
+
+/**
+ * rsvg_render_paint_server: Render paint server as image source for libart.
+ * @ar: Libart render object.
+ * @ps: Paint server object.
+ *
+ * Hooks up @ps as an image source for a libart rendering operation.
+ **/
+void
+rsvg_render_paint_server (ArtRender *ar, RsvgPaintServer *ps,
+ const RsvgPSCtx *ctx)
+{
+ if (ps != NULL)
+ ps->render (ps, ar, ctx);
+}
+
+/**
+ * rsvg_paint_server_ref: Reference a paint server object.
+ * @ps: The paint server object to reference.
+ **/
+void
+rsvg_paint_server_ref (RsvgPaintServer *ps)
+{
+ if (ps == NULL)
+ return;
+ ps->refcnt++;
+}
+
+/**
+ * rsvg_paint_server_unref: Unreference a paint server object.
+ * @ps: The paint server object to unreference.
+ **/
+void
+rsvg_paint_server_unref (RsvgPaintServer *ps)
+{
+ if (ps == NULL)
+ return;
+ if (--ps->refcnt == 0)
+ ps->free (ps);
+}