source: trunk/third/librsvg/rsvg-paint-server.c @ 18352

Revision 18352, 9.1 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18351, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2   rsvg-paint-server.c: Implement the SVG paint server abstraction.
3 
4   Copyright (C) 2000 Eazel, Inc.
5 
6   This program is free software; you can redistribute it and/or
7   modify it under the terms of the GNU Library General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10 
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   Library General Public License for more details.
15 
16   You should have received a copy of the GNU Library General Public
17   License along with this program; if not, write to the
18   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19   Boston, MA 02111-1307, USA.
20 
21   Author: Raph Levien <raph@artofcode.com>
22*/
23
24#include "config.h"
25#include "rsvg-paint-server.h"
26
27#include <glib/gmem.h>
28#include <glib/gmessages.h>
29#include <glib/gstrfuncs.h>
30#include <libart_lgpl/art_affine.h>
31#include <string.h>
32#include "rsvg-css.h"
33
34typedef struct _RsvgPaintServerSolid RsvgPaintServerSolid;
35typedef struct _RsvgPaintServerLinGrad RsvgPaintServerLinGrad;
36typedef struct _RsvgPaintServerRadGrad RsvgPaintServerRadGrad;
37
38struct _RsvgPaintServer {
39  int refcnt;
40  void (*free) (RsvgPaintServer *self);
41  void (*render) (RsvgPaintServer *self, ArtRender *ar, const RsvgPSCtx *ctx);
42};
43
44struct _RsvgPaintServerSolid {
45  RsvgPaintServer super;
46  guint32 rgb;
47};
48
49struct _RsvgPaintServerLinGrad {
50  RsvgPaintServer super;
51  RsvgLinearGradient *gradient;
52  ArtGradientLinear *agl;
53};
54
55struct _RsvgPaintServerRadGrad {
56  RsvgPaintServer super;
57  RsvgRadialGradient *gradient;
58  ArtGradientRadial *agr;
59};
60
61static void
62rsvg_paint_server_solid_free (RsvgPaintServer *self)
63{
64  g_free (self);
65}
66
67static void
68rsvg_paint_server_solid_render (RsvgPaintServer *self, ArtRender *ar,
69                                const RsvgPSCtx *ctx)
70{
71  RsvgPaintServerSolid *z = (RsvgPaintServerSolid *)self;
72  guint32 rgb = z->rgb;
73  ArtPixMaxDepth color[3];
74
75  color[0] = ART_PIX_MAX_FROM_8 (rgb >> 16);
76  color[1] = ART_PIX_MAX_FROM_8 ((rgb >> 8) & 0xff);
77  color[2] = ART_PIX_MAX_FROM_8 (rgb  & 0xff);
78
79  art_render_image_solid (ar, color);
80}
81
82static RsvgPaintServer *
83rsvg_paint_server_solid (guint32 rgb)
84{
85  RsvgPaintServerSolid *result = g_new (RsvgPaintServerSolid, 1);
86
87  result->super.refcnt = 1;
88  result->super.free = rsvg_paint_server_solid_free;
89  result->super.render = rsvg_paint_server_solid_render;
90
91  result->rgb = rgb;
92
93  return &result->super;
94}
95
96static void
97rsvg_paint_server_lin_grad_free (RsvgPaintServer *self)
98{
99  RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
100
101  if (z->agl)
102    g_free (z->agl->stops);
103  g_free (z->agl);
104  g_free (self);
105}
106
107static ArtGradientStop *
108rsvg_paint_art_stops_from_rsvg (RsvgGradientStops *rstops)
109{
110  ArtGradientStop *stops;
111  int n_stop = rstops->n_stop;
112  int i;
113
114  stops = g_new (ArtGradientStop, n_stop);
115  for (i = 0; i < n_stop; i++)
116    {
117      guint32 rgba;
118      guint32 r, g, b, a;
119
120      stops[i].offset = rstops->stop[i].offset;
121      rgba = rstops->stop[i].rgba;
122      /* convert from separated to premultiplied alpha */
123      a = rgba & 0xff;
124      r = (rgba >> 24) * a + 0x80;
125      r = (r + (r >> 8)) >> 8;
126      g = ((rgba >> 16) & 0xff) * a + 0x80;
127      g = (g + (g >> 8)) >> 8;
128      b = ((rgba >> 8) & 0xff) * a + 0x80;
129      b = (b + (b >> 8)) >> 8;
130      stops[i].color[0] = ART_PIX_MAX_FROM_8(r);
131      stops[i].color[1] = ART_PIX_MAX_FROM_8(g);
132      stops[i].color[2] = ART_PIX_MAX_FROM_8(b);
133      stops[i].color[3] = ART_PIX_MAX_FROM_8(a);
134    }
135  return stops;
136}
137
138static void
139rsvg_paint_server_lin_grad_render (RsvgPaintServer *self, ArtRender *ar,
140                                   const RsvgPSCtx *ctx)
141{
142  RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
143  RsvgLinearGradient *rlg = z->gradient;
144  ArtGradientLinear *agl;
145  double x1, y1, x2, y2;
146  double dx, dy, scale;
147
148  agl = z->agl;
149  if (agl == NULL)
150    {
151      if (rlg->stops->n_stop == 0)
152        {
153          /* g_warning ("gradient with no stops -- should be rejected by parser"); */
154          return;
155        }
156      agl = g_new (ArtGradientLinear, 1);
157      agl->n_stops = rlg->stops->n_stop;
158      agl->stops = rsvg_paint_art_stops_from_rsvg (rlg->stops);
159      z->agl = agl;
160    }
161
162  /* compute [xy][12] in pixel space */
163  /* todo: this code implicitly implements gradientUnits = userSpace */
164  x1 = rlg->x1 * rlg->affine[0] + rlg->y1 * rlg->affine[2] + rlg->affine[4];
165  y1 = rlg->x1 * rlg->affine[1] + rlg->y1 * rlg->affine[3] + rlg->affine[5];
166  x2 = rlg->x2 * rlg->affine[0] + rlg->y2 * rlg->affine[2] + rlg->affine[4];
167  y2 = rlg->x2 * rlg->affine[1] + rlg->y2 * rlg->affine[3] + rlg->affine[5];
168
169  /* solve a, b, c so ax1 + by1 + c = 0 and ax2 + by2 + c = 1, maximum
170     gradient is in x1,y1 to x2,y2 dir */
171  dx = x2 - x1;
172  dy = y2 - y1;
173  scale = 1.0 / (dx * dx + dy * dy);
174  agl->a = dx * scale;
175  agl->b = dy * scale;
176  agl->c = -(x1 * agl->a + y1 * agl->b);
177
178  agl->spread = rlg->spread;
179  art_render_gradient_linear (ar, agl, ART_FILTER_NEAREST);
180}
181
182static RsvgPaintServer *
183rsvg_paint_server_lin_grad (RsvgLinearGradient *gradient)
184{
185  RsvgPaintServerLinGrad *result = g_new (RsvgPaintServerLinGrad, 1);
186
187  result->super.refcnt = 1;
188  result->super.free = rsvg_paint_server_lin_grad_free;
189  result->super.render = rsvg_paint_server_lin_grad_render;
190
191  result->gradient = gradient;
192  result->agl = NULL;
193
194  return &result->super;
195}
196
197static void
198rsvg_paint_server_rad_grad_free (RsvgPaintServer *self)
199{
200  RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
201
202  if (z->agr)
203    g_free (z->agr->stops);
204  g_free (z->agr);
205  g_free (self);
206}
207
208static void
209rsvg_paint_server_rad_grad_render (RsvgPaintServer *self, ArtRender *ar,
210                                   const RsvgPSCtx *ctx)
211{
212  RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
213  RsvgRadialGradient *rrg = z->gradient;
214  ArtGradientRadial *agr;
215  double aff1[6], aff2[6];
216
217  agr = z->agr;
218  if (agr == NULL)
219    {
220      if (rrg->stops->n_stop == 0)
221        {
222          /* g_warning ("gradient with no stops -- should be rejected by parser"); */
223          return;
224        }
225      agr = g_new (ArtGradientRadial, 1);
226      agr->n_stops = rrg->stops->n_stop;
227      agr->stops = rsvg_paint_art_stops_from_rsvg (rrg->stops);
228      z->agr = agr;
229    }
230
231  /* todo: this code implicitly implements gradientUnits = userSpace */
232  art_affine_scale (aff1, rrg->r, rrg->r);
233  art_affine_translate (aff2, rrg->cx, rrg->cy);
234  art_affine_multiply (aff1, aff1, aff2);
235  art_affine_multiply (aff1, aff1, rrg->affine);
236  art_affine_invert (agr->affine, aff1);
237
238  agr->fx = (rrg->fx - rrg->cx) / rrg->r;
239  agr->fy = (rrg->fy - rrg->cy) / rrg->r;
240
241  art_render_gradient_radial (ar, agr, ART_FILTER_NEAREST);
242}
243
244static RsvgPaintServer *
245rsvg_paint_server_rad_grad (RsvgRadialGradient *gradient)
246{
247  RsvgPaintServerRadGrad *result = g_new (RsvgPaintServerRadGrad, 1);
248
249  result->super.refcnt = 1;
250  result->super.free = rsvg_paint_server_rad_grad_free;
251  result->super.render = rsvg_paint_server_rad_grad_render;
252
253  result->gradient = gradient;
254  result->agr = NULL;
255
256  return &result->super;
257}
258
259/**
260 * rsvg_paint_server_parse: Parse an SVG paint specification.
261 * @defs: Defs for looking up gradients.
262 * @str: The SVG paint specification string to parse.
263 *
264 * Parses the paint specification @str, creating a new paint server
265 * object.
266 *
267 * Return value: The newly created paint server, or NULL on error.
268 **/
269RsvgPaintServer *
270rsvg_paint_server_parse (const RsvgDefs *defs, const char *str)
271{
272  guint32 rgb;
273
274  if (!strcmp (str, "none"))
275    return NULL;
276  if (!strncmp (str, "url(", 4))
277    {
278      const char *p = str + 4;
279      int ix;
280      char *name;
281      RsvgDefVal *val;
282
283      while (g_ascii_isspace (*p)) p++;
284      if (*p != '#')
285        return NULL;
286      p++;
287      for (ix = 0; p[ix]; ix++)
288        if (p[ix] == ')') break;
289      if (p[ix] != ')')
290        return NULL;
291      name = g_strndup (p, ix);
292      val = rsvg_defs_lookup (defs, name);
293      g_free (name);
294      if (val == NULL)
295        return NULL;
296      switch (val->type)
297        {
298        case RSVG_DEF_LINGRAD:
299          return rsvg_paint_server_lin_grad ((RsvgLinearGradient *)val);
300        case RSVG_DEF_RADGRAD:
301          return rsvg_paint_server_rad_grad ((RsvgRadialGradient *)val);
302        default:
303          return NULL;
304        }
305    }
306  else
307    {
308      rgb = rsvg_css_parse_color (str);
309      return rsvg_paint_server_solid (rgb);
310    }
311}
312
313/**
314 * rsvg_render_paint_server: Render paint server as image source for libart.
315 * @ar: Libart render object.
316 * @ps: Paint server object.
317 *
318 * Hooks up @ps as an image source for a libart rendering operation.
319 **/
320void
321rsvg_render_paint_server (ArtRender *ar, RsvgPaintServer *ps,
322                          const RsvgPSCtx *ctx)
323{
324  g_return_if_fail (ar != NULL);
325  if (ps != NULL)
326    ps->render (ps, ar, ctx);
327}
328
329/**
330 * rsvg_paint_server_ref: Reference a paint server object.
331 * @ps: The paint server object to reference.
332 **/
333void
334rsvg_paint_server_ref (RsvgPaintServer *ps)
335{
336  if (ps == NULL)
337    return;
338  ps->refcnt++;
339}
340
341/**
342 * rsvg_paint_server_unref: Unreference a paint server object.
343 * @ps: The paint server object to unreference.
344 **/
345void
346rsvg_paint_server_unref (RsvgPaintServer *ps)
347{
348  if (ps == NULL)
349    return;
350  if (--ps->refcnt == 0)
351    ps->free (ps);
352}
Note: See TracBrowser for help on using the repository browser.