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

Revision 18609, 11.1 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18608, which included commits to RCS files with non-trunk default branches.
Line 
1/* vim: set sw=4: -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2/*
3   rsvg-paint-server.c: Implement the SVG paint server abstraction.
4 
5   Copyright (C) 2000 Eazel, Inc.
6 
7   This program is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Library General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11 
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   Library General Public License for more details.
16 
17   You should have received a copy of the GNU Library General Public
18   License along with this program; if not, write to the
19   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.
21 
22   Author: Raph Levien <raph@artofcode.com>
23*/
24
25#include "config.h"
26#include "rsvg-paint-server.h"
27#include "rsvg-private.h"
28
29#include <glib/gmem.h>
30#include <glib/gmessages.h>
31#include <glib/gstrfuncs.h>
32#include <libart_lgpl/art_affine.h>
33#include <string.h>
34#include "rsvg-css.h"
35
36typedef struct _RsvgPaintServerSolid RsvgPaintServerSolid;
37typedef struct _RsvgPaintServerLinGrad RsvgPaintServerLinGrad;
38typedef struct _RsvgPaintServerRadGrad RsvgPaintServerRadGrad;
39
40struct _RsvgPaintServer {
41        int refcnt;
42        void (*free) (RsvgPaintServer *self);
43        void (*render) (RsvgPaintServer *self, ArtRender *ar, const RsvgPSCtx *ctx);
44};
45
46struct _RsvgPaintServerSolid {
47        RsvgPaintServer super;
48        guint32 rgb;
49};
50
51struct _RsvgPaintServerLinGrad {
52        RsvgPaintServer super;
53        RsvgLinearGradient *gradient;
54        ArtGradientLinear *agl;
55};
56
57struct _RsvgPaintServerRadGrad {
58        RsvgPaintServer super;
59        RsvgRadialGradient *gradient;
60        ArtGradientRadial *agr;
61};
62
63static void
64rsvg_paint_server_solid_free (RsvgPaintServer *self)
65{
66        g_free (self);
67}
68
69static void
70rsvg_paint_server_solid_render (RsvgPaintServer *self, ArtRender *ar,
71                                                                const RsvgPSCtx *ctx)
72{
73        RsvgPaintServerSolid *z = (RsvgPaintServerSolid *)self;
74        guint32 rgb = z->rgb;
75        ArtPixMaxDepth color[3];
76       
77        color[0] = ART_PIX_MAX_FROM_8 (rgb >> 16);
78        color[1] = ART_PIX_MAX_FROM_8 ((rgb >> 8) & 0xff);
79        color[2] = ART_PIX_MAX_FROM_8 (rgb  & 0xff);
80       
81        art_render_image_solid (ar, color);
82}
83
84static RsvgPaintServer *
85rsvg_paint_server_solid (guint32 rgb)
86{
87        RsvgPaintServerSolid *result = g_new (RsvgPaintServerSolid, 1);
88       
89        result->super.refcnt = 1;
90        result->super.free = rsvg_paint_server_solid_free;
91        result->super.render = rsvg_paint_server_solid_render;
92       
93        result->rgb = rgb;
94       
95        return &result->super;
96}
97
98static void
99rsvg_paint_server_lin_grad_free (RsvgPaintServer *self)
100{
101        RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
102       
103        if (z->agl)
104                g_free (z->agl->stops);
105        g_free (z->agl);
106        g_free (self);
107}
108
109static ArtGradientStop *
110rsvg_paint_art_stops_from_rsvg (RsvgGradientStops *rstops)
111{
112        ArtGradientStop *stops;
113        int n_stop = rstops->n_stop;
114        int i;
115       
116        stops = g_new (ArtGradientStop, n_stop);
117        for (i = 0; i < n_stop; i++)
118                {
119                        guint32 rgba;
120                        guint32 r, g, b, a;
121                       
122                        stops[i].offset = rstops->stop[i].offset;
123                        rgba = rstops->stop[i].rgba;
124                        /* convert from separated to premultiplied alpha */
125                        a = rgba & 0xff;
126                        r = (rgba >> 24) * a + 0x80;
127                        r = (r + (r >> 8)) >> 8;
128                        g = ((rgba >> 16) & 0xff) * a + 0x80;
129                        g = (g + (g >> 8)) >> 8;
130                        b = ((rgba >> 8) & 0xff) * a + 0x80;
131                        b = (b + (b >> 8)) >> 8;
132                        stops[i].color[0] = ART_PIX_MAX_FROM_8(r);
133                        stops[i].color[1] = ART_PIX_MAX_FROM_8(g);
134                        stops[i].color[2] = ART_PIX_MAX_FROM_8(b);
135                        stops[i].color[3] = ART_PIX_MAX_FROM_8(a);
136                }
137        return stops;
138}
139
140static void
141rsvg_paint_server_lin_grad_render (RsvgPaintServer *self, ArtRender *ar,
142                                                                   const RsvgPSCtx *ctx)
143{
144        RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
145        RsvgLinearGradient *rlg = z->gradient;
146        ArtGradientLinear *agl;
147        double x1, y1, x2, y2;
148        double dx, dy, scale;
149       
150        agl = z->agl;
151        if (agl == NULL)
152                {
153                        if (rlg->stops->n_stop == 0)
154                                {
155                                        return;
156                                }
157                        agl = g_new (ArtGradientLinear, 1);
158                        agl->n_stops = rlg->stops->n_stop;
159                        agl->stops = rsvg_paint_art_stops_from_rsvg (rlg->stops);
160                        z->agl = agl;
161                }
162       
163        /* compute [xy][12] in pixel space */
164        /* todo: this code implicitly implements gradientUnits = userSpace */
165        x1 = rlg->x1 * rlg->affine[0] + rlg->y1 * rlg->affine[2] + rlg->affine[4];
166        y1 = rlg->x1 * rlg->affine[1] + rlg->y1 * rlg->affine[3] + rlg->affine[5];
167        x2 = rlg->x2 * rlg->affine[0] + rlg->y2 * rlg->affine[2] + rlg->affine[4];
168        y2 = rlg->x2 * rlg->affine[1] + rlg->y2 * rlg->affine[3] + rlg->affine[5];
169       
170        /* solve a, b, c so ax1 + by1 + c = 0 and ax2 + by2 + c = 1, maximum
171           gradient is in x1,y1 to x2,y2 dir */
172        dx = x2 - x1;
173        dy = y2 - y1;
174        scale = 1.0 / (dx * dx + dy * dy);
175        agl->a = dx * scale;
176        agl->b = dy * scale;
177        agl->c = -(x1 * agl->a + y1 * agl->b);
178       
179        agl->spread = rlg->spread;
180        art_render_gradient_linear (ar, agl, ART_FILTER_NEAREST);
181}
182
183static RsvgPaintServer *
184rsvg_paint_server_lin_grad (RsvgLinearGradient *gradient)
185{
186        RsvgPaintServerLinGrad *result = g_new (RsvgPaintServerLinGrad, 1);
187       
188        result->super.refcnt = 1;
189        result->super.free = rsvg_paint_server_lin_grad_free;
190        result->super.render = rsvg_paint_server_lin_grad_render;
191       
192        result->gradient = gradient;
193        result->agl = NULL;
194       
195        return &result->super;
196}
197
198static void
199rsvg_paint_server_rad_grad_free (RsvgPaintServer *self)
200{
201        RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
202
203        if (z->agr)
204                g_free (z->agr->stops);
205        g_free (z->agr);
206        g_free (self);
207}
208
209static void
210rsvg_paint_server_rad_grad_render (RsvgPaintServer *self, ArtRender *ar,
211                                                                   const RsvgPSCtx *ctx)
212{
213        RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
214        RsvgRadialGradient *rrg = z->gradient;
215        ArtGradientRadial *agr;
216        double aff1[6], aff2[6];
217       
218        agr = z->agr;
219        if (agr == NULL)
220                {
221                        if (rrg->stops->n_stop == 0)
222                                {
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        /* TODO: libart doesn't support spreads on radial gradients */
239
240        agr->fx = (rrg->fx - rrg->cx) / rrg->r;
241        agr->fy = (rrg->fy - rrg->cy) / rrg->r;
242       
243        art_render_gradient_radial (ar, agr, ART_FILTER_NEAREST);
244}
245
246static RsvgPaintServer *
247rsvg_paint_server_rad_grad (RsvgRadialGradient *gradient)
248{
249        RsvgPaintServerRadGrad *result = g_new (RsvgPaintServerRadGrad, 1);
250       
251        result->super.refcnt = 1;
252        result->super.free = rsvg_paint_server_rad_grad_free;
253        result->super.render = rsvg_paint_server_rad_grad_render;
254       
255        result->gradient = gradient;
256        result->agr = NULL;
257       
258        return &result->super;
259}
260
261/**
262 * rsvg_paint_server_parse: Parse an SVG paint specification.
263 * @defs: Defs for looking up gradients.
264 * @str: The SVG paint specification string to parse.
265 *
266 * Parses the paint specification @str, creating a new paint server
267 * object.
268 *
269 * Return value: The newly created paint server, or NULL on error.
270 **/
271RsvgPaintServer *
272rsvg_paint_server_parse (const RsvgDefs *defs, const char *str)
273{
274        guint32 rgb;
275       
276        if (!strcmp (str, "none"))
277                return NULL;
278        if (!strncmp (str, "url(", 4))
279                {
280                        const char *p = str + 4;
281                        int ix;
282                        char *name;
283                        RsvgDefVal *val;
284                       
285                        while (g_ascii_isspace (*p)) p++;
286                        if (*p != '#')
287                                return NULL;
288                        p++;
289                        for (ix = 0; p[ix]; ix++)
290                                if (p[ix] == ')') break;
291                        if (p[ix] != ')')
292                                return NULL;
293                        name = g_strndup (p, ix);
294                        val = rsvg_defs_lookup (defs, name);
295                        g_free (name);
296                        if (val == NULL)
297                                return NULL;
298                        switch (val->type)
299                                {
300                                case RSVG_DEF_LINGRAD:
301                                        return rsvg_paint_server_lin_grad ((RsvgLinearGradient *)val);
302                                case RSVG_DEF_RADGRAD:
303                                        return rsvg_paint_server_rad_grad ((RsvgRadialGradient *)val);
304                                default:
305                                        return NULL;
306                                }
307                }
308  else
309          {
310                  rgb = rsvg_css_parse_color (str);
311                  return rsvg_paint_server_solid (rgb);
312          }
313}
314
315/**
316 * rsvg_render_paint_server: Render paint server as image source for libart.
317 * @ar: Libart render object.
318 * @ps: Paint server object.
319 *
320 * Hooks up @ps as an image source for a libart rendering operation.
321 **/
322void
323rsvg_render_paint_server (ArtRender *ar, RsvgPaintServer *ps,
324                                                  const RsvgPSCtx *ctx)
325{
326        g_return_if_fail (ar != NULL);
327        if (ps != NULL)
328                ps->render (ps, ar, ctx);
329}
330
331/**
332 * rsvg_paint_server_ref: Reference a paint server object.
333 * @ps: The paint server object to reference.
334 **/
335void
336rsvg_paint_server_ref (RsvgPaintServer *ps)
337{
338        if (ps == NULL)
339                return;
340        ps->refcnt++;
341}
342
343/**
344 * rsvg_paint_server_unref: Unreference a paint server object.
345 * @ps: The paint server object to unreference.
346 **/
347void
348rsvg_paint_server_unref (RsvgPaintServer *ps)
349{
350        if (ps == NULL)
351                return;
352        if (--ps->refcnt == 0)
353                ps->free (ps);
354}
355
356RsvgRadialGradient *
357rsvg_clone_radial_gradient (const RsvgRadialGradient *grad, gboolean * shallow_cloned)
358{
359        RsvgRadialGradient * clone = NULL;
360        int i;
361       
362        clone = g_new0 (RsvgRadialGradient, 1);
363        clone->super.type = RSVG_DEF_RADGRAD;
364        clone->super.free = rsvg_radial_gradient_free;
365       
366        for (i = 0; i < 6; i++)
367                clone->affine[i] = grad->affine[i];
368
369        if (grad->stops != NULL) {
370                clone->stops = g_new (RsvgGradientStops, 1);
371                clone->stops->n_stop = grad->stops->n_stop;
372                clone->stops->stop = g_new (RsvgGradientStop, grad->stops->n_stop);
373       
374                for (i = 0; i < grad->stops->n_stop; i++)
375                        clone->stops->stop[i] = grad->stops->stop[i];
376        } else {
377                clone->stops = NULL;
378        }
379
380        clone->spread = grad->spread;
381
382        /* EVIL EVIL - sodipodi can base LinearGradients on
383           RadialGradients, and vice-versa. it is legal, though:
384           http://www.w3.org/TR/SVG11/pservers.html#LinearGradients
385        */
386        if (grad->super.type == RSVG_DEF_RADGRAD) {
387                clone->cx = grad->cx;
388                clone->cy = grad->cy;
389                clone->r  = grad->r;
390                clone->fx = grad->fx;
391                clone->fy = grad->fy;
392               
393                *shallow_cloned = FALSE;
394        } else {
395                *shallow_cloned = TRUE;
396        }
397       
398        return clone;
399}
400
401RsvgLinearGradient *
402rsvg_clone_linear_gradient (const RsvgLinearGradient *grad, gboolean * shallow_cloned)
403{
404        RsvgLinearGradient * clone = NULL;
405        int i;
406       
407        clone = g_new0 (RsvgLinearGradient, 1);
408        clone->super.type = RSVG_DEF_LINGRAD;
409        clone->super.free = rsvg_linear_gradient_free;
410       
411        for (i = 0; i < 6; i++)
412                clone->affine[i] = grad->affine[i];
413
414        if (grad->stops != NULL) {
415                clone->stops = g_new (RsvgGradientStops, 1);
416                clone->stops->n_stop = grad->stops->n_stop;
417                clone->stops->stop = g_new (RsvgGradientStop, grad->stops->n_stop);
418               
419                for (i = 0; i < grad->stops->n_stop; i++)
420                        clone->stops->stop[i] = grad->stops->stop[i];
421        } else {
422                clone->stops = NULL;
423        }
424
425        clone->spread = grad->spread;
426
427        /* EVIL EVIL - sodipodi can base LinearGradients on
428           RadialGradients, and vice-versa. it is legal, though:
429           http://www.w3.org/TR/SVG11/pservers.html#LinearGradients
430        */
431        if (grad->super.type == RSVG_DEF_LINGRAD) {
432                clone->x1 = grad->x1;
433                clone->y1 = grad->y1;
434                clone->x2 = grad->x2;
435                clone->y2 = grad->y2;
436
437                *shallow_cloned = FALSE;
438        } else {
439                *shallow_cloned = TRUE;
440        }
441
442        return clone;
443}
Note: See TracBrowser for help on using the repository browser.