diff options
author | Raph Levien <raph@src.gnome.org> | 2000-05-29 22:12:11 +0000 |
---|---|---|
committer | Raph Levien <raph@src.gnome.org> | 2000-05-29 22:12:11 +0000 |
commit | 55cffa7797e8d5f2eac983a312b37c32cd6a6445 (patch) | |
tree | cf8fa81b376fd77de75345d88a3c4226cfe90586 /librsvg/rsvg-paint-server.c | |
parent | 17b8049c949872e40db2f603fad2dd1552168dfb (diff) | |
download | nautilus-55cffa7797e8d5f2eac983a312b37c32cd6a6445.tar.gz |
Added gradient handling to librsvg.
Diffstat (limited to 'librsvg/rsvg-paint-server.c')
-rw-r--r-- | librsvg/rsvg-paint-server.c | 345 |
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); +} |