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

Revision 18805, 11.7 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18804, 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 <math.h>
35
36#include "rsvg-css.h"
37
38typedef struct _RsvgPaintServerSolid RsvgPaintServerSolid;
39typedef struct _RsvgPaintServerLinGrad RsvgPaintServerLinGrad;
40typedef struct _RsvgPaintServerRadGrad RsvgPaintServerRadGrad;
41
42struct _RsvgPaintServer {
43        int refcnt;
44        void (*free) (RsvgPaintServer *self);
45        void (*render) (RsvgPaintServer *self, ArtRender *ar, const RsvgPSCtx *ctx);
46};
47
48struct _RsvgPaintServerSolid {
49        RsvgPaintServer super;
50        guint32 rgb;
51};
52
53struct _RsvgPaintServerLinGrad {
54        RsvgPaintServer super;
55        RsvgLinearGradient *gradient;
56        ArtGradientLinear *agl;
57};
58
59struct _RsvgPaintServerRadGrad {
60        RsvgPaintServer super;
61        RsvgRadialGradient *gradient;
62        ArtGradientRadial *agr;
63};
64
65static void
66rsvg_paint_server_solid_free (RsvgPaintServer *self)
67{
68        g_free (self);
69}
70
71static void
72rsvg_paint_server_solid_render (RsvgPaintServer *self, ArtRender *ar,
73                                                                const RsvgPSCtx *ctx)
74{
75        RsvgPaintServerSolid *z = (RsvgPaintServerSolid *)self;
76        guint32 rgb = z->rgb;
77        ArtPixMaxDepth color[3];
78       
79        color[0] = ART_PIX_MAX_FROM_8 (rgb >> 16);
80        color[1] = ART_PIX_MAX_FROM_8 ((rgb >> 8) & 0xff);
81        color[2] = ART_PIX_MAX_FROM_8 (rgb  & 0xff);
82       
83        art_render_image_solid (ar, color);
84}
85
86static RsvgPaintServer *
87rsvg_paint_server_solid (guint32 rgb)
88{
89        RsvgPaintServerSolid *result = g_new (RsvgPaintServerSolid, 1);
90       
91        result->super.refcnt = 1;
92        result->super.free = rsvg_paint_server_solid_free;
93        result->super.render = rsvg_paint_server_solid_render;
94       
95        result->rgb = rgb;
96       
97        return &result->super;
98}
99
100static void
101rsvg_paint_server_lin_grad_free (RsvgPaintServer *self)
102{
103        RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
104       
105        if (z->agl)
106                g_free (z->agl->stops);
107        g_free (z->agl);
108        g_free (self);
109}
110
111static ArtGradientStop *
112rsvg_paint_art_stops_from_rsvg (RsvgGradientStops *rstops)
113{
114        ArtGradientStop *stops;
115        int n_stop = rstops->n_stop;
116        int i;
117       
118        stops = g_new (ArtGradientStop, n_stop);
119        for (i = 0; i < n_stop; i++)
120                {
121                        guint32 rgba;
122                        guint32 r, g, b, a;
123                       
124                        stops[i].offset = rstops->stop[i].offset;
125                        rgba = rstops->stop[i].rgba;
126                        /* convert from separated to premultiplied alpha */
127                        a = rgba & 0xff;
128                        r = (rgba >> 24) * a + 0x80;
129                        r = (r + (r >> 8)) >> 8;
130                        g = ((rgba >> 16) & 0xff) * a + 0x80;
131                        g = (g + (g >> 8)) >> 8;
132                        b = ((rgba >> 8) & 0xff) * a + 0x80;
133                        b = (b + (b >> 8)) >> 8;
134                        stops[i].color[0] = ART_PIX_MAX_FROM_8(r);
135                        stops[i].color[1] = ART_PIX_MAX_FROM_8(g);
136                        stops[i].color[2] = ART_PIX_MAX_FROM_8(b);
137                        stops[i].color[3] = ART_PIX_MAX_FROM_8(a);
138                }
139        return stops;
140}
141
142static void
143rsvg_paint_server_lin_grad_render (RsvgPaintServer *self, ArtRender *ar,
144                                                                   const RsvgPSCtx *ctx)
145{
146        RsvgPaintServerLinGrad *z = (RsvgPaintServerLinGrad *)self;
147        RsvgLinearGradient *rlg = z->gradient;
148        ArtGradientLinear *agl;
149        double x1, y1, x2, y2;
150        double dx, dy, scale;
151        double affine[6];
152        int i;
153
154        agl = z->agl;
155        if (agl == NULL)
156                {
157                        if (rlg->stops->n_stop == 0)
158                                {
159                                        return;
160                                }
161                        agl = g_new (ArtGradientLinear, 1);
162                        agl->n_stops = rlg->stops->n_stop;
163                        agl->stops = rsvg_paint_art_stops_from_rsvg (rlg->stops);
164                        z->agl = agl;
165                }
166
167        if (rlg->obj_bbox) {
168                affine[0] = ar->x1 - ar->x0;
169                affine[1] = 0.;         
170                affine[2] = 0.;
171                affine[3] = ar->y1 - ar->y0;
172                affine[4] = ar->x0;
173                affine[5] = ar->y0;
174
175                art_affine_multiply (affine, affine, rlg->affine);
176        } else {
177                for (i = 0; i < 6; i++)
178                        affine[i] = rlg->affine[i];
179        }
180       
181        /* compute [xy][12] in pixel space */
182        x1 = rlg->x1 * affine[0] + rlg->y1 * affine[2] + affine[4];
183        y1 = rlg->x1 * affine[1] + rlg->y1 * affine[3] + affine[5];
184        x2 = rlg->x2 * affine[0] + rlg->y2 * affine[2] + affine[4];
185        y2 = rlg->x2 * affine[1] + rlg->y2 * affine[3] + affine[5];
186       
187        /* solve a, b, c so ax1 + by1 + c = 0 and ax2 + by2 + c = 1, maximum
188           gradient is in x1,y1 to x2,y2 dir */
189        dx = x2 - x1;
190        dy = y2 - y1;
191
192        /* workaround for an evil devide by 0 bug - not sure if this is sufficient */
193        if (fabs(dx + dy) <= 0.0000001)
194                scale = 0.;
195        else
196                scale = 1.0 / (dx * dx + dy * dy);
197        agl->a = dx * scale;
198        agl->b = dy * scale;
199        agl->c = -(x1 * agl->a + y1 * agl->b);
200       
201        agl->spread = rlg->spread;
202        art_render_gradient_linear (ar, agl, ART_FILTER_NEAREST);
203}
204
205static RsvgPaintServer *
206rsvg_paint_server_lin_grad (RsvgLinearGradient *gradient)
207{
208        RsvgPaintServerLinGrad *result = g_new (RsvgPaintServerLinGrad, 1);
209       
210        result->super.refcnt = 1;
211        result->super.free = rsvg_paint_server_lin_grad_free;
212        result->super.render = rsvg_paint_server_lin_grad_render;
213       
214        result->gradient = gradient;
215        result->agl = NULL;
216       
217        return &result->super;
218}
219
220static void
221rsvg_paint_server_rad_grad_free (RsvgPaintServer *self)
222{
223        RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
224
225        if (z->agr)
226                g_free (z->agr->stops);
227        g_free (z->agr);
228        g_free (self);
229}
230
231static void
232rsvg_paint_server_rad_grad_render (RsvgPaintServer *self, ArtRender *ar,
233                                                                   const RsvgPSCtx *ctx)
234{
235        RsvgPaintServerRadGrad *z = (RsvgPaintServerRadGrad *)self;
236        RsvgRadialGradient *rrg = z->gradient;
237        ArtGradientRadial *agr;
238        double aff1[6], aff2[6], affine[6];
239        int i;
240
241        if (rrg->obj_bbox) {
242                affine[0] = ar->x1 - ar->x0;
243                affine[1] = 0.;         
244                affine[2] = 0.;
245                affine[3] = ar->y1 - ar->y0;
246                affine[4] = ar->x0;
247                affine[5] = ar->y0;
248
249                art_affine_multiply (affine, affine, rrg->affine);
250        } else {
251                for (i = 0; i < 6; i++)
252                        affine[i] = rrg->affine[i];
253        }
254
255        agr = z->agr;
256        if (agr == NULL)
257                {
258                        if (rrg->stops->n_stop == 0)
259                                {
260                                        return;
261                                }
262                        agr = g_new (ArtGradientRadial, 1);
263                        agr->n_stops = rrg->stops->n_stop;
264                        agr->stops = rsvg_paint_art_stops_from_rsvg (rrg->stops);
265                        z->agr = agr;
266                }
267       
268        art_affine_scale (aff1, rrg->r, rrg->r);
269        art_affine_translate (aff2, rrg->cx, rrg->cy);
270        art_affine_multiply (aff1, aff1, aff2);
271        art_affine_multiply (aff1, aff1, affine);
272        art_affine_invert (agr->affine, aff1);
273       
274        /* todo: libart doesn't support spreads on radial gradients */
275
276        agr->fx = (rrg->fx - rrg->cx) / rrg->r;
277        agr->fy = (rrg->fy - rrg->cy) / rrg->r;
278       
279        art_render_gradient_radial (ar, agr, ART_FILTER_NEAREST);
280}
281
282static RsvgPaintServer *
283rsvg_paint_server_rad_grad (RsvgRadialGradient *gradient)
284{
285        RsvgPaintServerRadGrad *result = g_new (RsvgPaintServerRadGrad, 1);
286       
287        result->super.refcnt = 1;
288        result->super.free = rsvg_paint_server_rad_grad_free;
289        result->super.render = rsvg_paint_server_rad_grad_render;
290       
291        result->gradient = gradient;
292        result->agr = NULL;
293       
294        return &result->super;
295}
296
297/**
298 * rsvg_paint_server_parse: Parse an SVG paint specification.
299 * @defs: Defs for looking up gradients.
300 * @str: The SVG paint specification string to parse.
301 *
302 * Parses the paint specification @str, creating a new paint server
303 * object.
304 *
305 * Return value: The newly created paint server, or NULL on error.
306 **/
307RsvgPaintServer *
308rsvg_paint_server_parse (const RsvgDefs *defs, const char *str)
309{
310        guint32 rgb;
311       
312        if (!strcmp (str, "none"))
313                return NULL;
314        if (!strncmp (str, "url(", 4))
315                {
316                        const char *p = str + 4;
317                        int ix;
318                        char *name;
319                        RsvgDefVal *val;
320                       
321                        while (g_ascii_isspace (*p)) p++;
322                        if (*p != '#')
323                                return NULL;
324                        p++;
325                        for (ix = 0; p[ix]; ix++)
326                                if (p[ix] == ')') break;
327                        if (p[ix] != ')')
328                                return NULL;
329                        name = g_strndup (p, ix);
330                        val = rsvg_defs_lookup (defs, name);
331                        g_free (name);
332                        if (val == NULL)
333                                return NULL;
334                        switch (val->type)
335                                {
336                                case RSVG_DEF_LINGRAD:
337                                        return rsvg_paint_server_lin_grad ((RsvgLinearGradient *)val);
338                                case RSVG_DEF_RADGRAD:
339                                        return rsvg_paint_server_rad_grad ((RsvgRadialGradient *)val);
340                                default:
341                                        return NULL;
342                                }
343                }
344  else
345          {
346                  rgb = rsvg_css_parse_color (str);
347                  return rsvg_paint_server_solid (rgb);
348          }
349}
350
351/**
352 * rsvg_render_paint_server: Render paint server as image source for libart.
353 * @ar: Libart render object.
354 * @ps: Paint server object.
355 *
356 * Hooks up @ps as an image source for a libart rendering operation.
357 **/
358void
359rsvg_render_paint_server (ArtRender *ar, RsvgPaintServer *ps,
360                                                  const RsvgPSCtx *ctx)
361{
362        g_return_if_fail (ar != NULL);
363        if (ps != NULL)
364                ps->render (ps, ar, ctx);
365}
366
367/**
368 * rsvg_paint_server_ref: Reference a paint server object.
369 * @ps: The paint server object to reference.
370 **/
371void
372rsvg_paint_server_ref (RsvgPaintServer *ps)
373{
374        if (ps == NULL)
375                return;
376        ps->refcnt++;
377}
378
379/**
380 * rsvg_paint_server_unref: Unreference a paint server object.
381 * @ps: The paint server object to unreference.
382 **/
383void
384rsvg_paint_server_unref (RsvgPaintServer *ps)
385{
386        if (ps == NULL)
387                return;
388        if (--ps->refcnt == 0)
389                ps->free (ps);
390}
391
392RsvgRadialGradient *
393rsvg_clone_radial_gradient (const RsvgRadialGradient *grad, gboolean * shallow_cloned)
394{
395        RsvgRadialGradient * clone = NULL;
396        int i;
397       
398        clone = g_new0 (RsvgRadialGradient, 1);
399        clone->super.type = RSVG_DEF_RADGRAD;
400        clone->super.free = rsvg_radial_gradient_free;
401       
402        clone->obj_bbox = grad->obj_bbox;
403        for (i = 0; i < 6; i++)
404                clone->affine[i] = grad->affine[i];
405
406        if (grad->stops != NULL) {
407                clone->stops = g_new (RsvgGradientStops, 1);
408                clone->stops->n_stop = grad->stops->n_stop;
409                clone->stops->stop = g_new (RsvgGradientStop, grad->stops->n_stop);
410       
411                for (i = 0; i < grad->stops->n_stop; i++)
412                        clone->stops->stop[i] = grad->stops->stop[i];
413        }
414
415        clone->spread = grad->spread;
416
417        /* EVIL EVIL - SVG can base LinearGradients on
418           RadialGradients, and vice-versa. it is legal, though:
419           http://www.w3.org/TR/SVG11/pservers.html#LinearGradients
420        */
421        if (grad->super.type == RSVG_DEF_RADGRAD) {
422                clone->cx = grad->cx;
423                clone->cy = grad->cy;
424                clone->r  = grad->r;
425                clone->fx = grad->fx;
426                clone->fy = grad->fy;
427               
428                *shallow_cloned = FALSE;
429        } else {
430                *shallow_cloned = TRUE;
431        }
432       
433        return clone;
434}
435
436RsvgLinearGradient *
437rsvg_clone_linear_gradient (const RsvgLinearGradient *grad, gboolean * shallow_cloned)
438{
439        RsvgLinearGradient * clone = NULL;
440        int i;
441       
442        clone = g_new0 (RsvgLinearGradient, 1);
443        clone->super.type = RSVG_DEF_LINGRAD;
444        clone->super.free = rsvg_linear_gradient_free;
445       
446        clone->obj_bbox = grad->obj_bbox;
447        for (i = 0; i < 6; i++)
448                clone->affine[i] = grad->affine[i];
449
450        if (grad->stops != NULL) {
451                clone->stops = g_new (RsvgGradientStops, 1);
452                clone->stops->n_stop = grad->stops->n_stop;
453                clone->stops->stop = g_new (RsvgGradientStop, grad->stops->n_stop);
454               
455                for (i = 0; i < grad->stops->n_stop; i++)
456                        clone->stops->stop[i] = grad->stops->stop[i];
457        }
458
459        clone->spread = grad->spread;
460
461        /* EVIL EVIL - SVG can base LinearGradients on
462           RadialGradients, and vice-versa. it is legal, though:
463           http://www.w3.org/TR/SVG11/pservers.html#LinearGradients
464        */
465        if (grad->super.type == RSVG_DEF_LINGRAD) {
466                clone->x1 = grad->x1;
467                clone->y1 = grad->y1;
468                clone->x2 = grad->x2;
469                clone->y2 = grad->y2;
470
471                *shallow_cloned = FALSE;
472        } else {
473                *shallow_cloned = TRUE;
474        }
475
476        return clone;
477}
Note: See TracBrowser for help on using the repository browser.