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

Revision 18609, 29.2 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-shapes.c: Draw SVG shapes
4
5   Copyright (C) 2000 Eazel, Inc.
6   Copyright (C) 2002 Dom Lachowicz <cinamod@hotmail.com>
7
8   This program is free software; you can redistribute it and/or
9   modify it under the terms of the GNU Library General Public License as
10   published by the Free Software Foundation; either version 2 of the
11   License, or (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   Library General Public License for more details.
17
18   You should have received a copy of the GNU Library General Public
19   License along with this program; if not, write to the
20   Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21   Boston, MA 02111-1307, USA.
22
23   Author: Raph Levien <raph@artofcode.com>
24*/
25
26#include "rsvg-shapes.h"
27#include "rsvg-styles.h"
28#include "rsvg-css.h"
29#include "rsvg-private.h"
30#include "rsvg-bpath-util.h"
31#include "rsvg-path.h"
32
33#include <libart_lgpl/art_affine.h>
34#include <libart_lgpl/art_vpath_bpath.h>
35#include <libart_lgpl/art_render_svp.h>
36#include <libart_lgpl/art_svp_vpath.h>
37#include <libart_lgpl/art_svp_intersect.h>
38#include <libart_lgpl/art_svp_vpath.h>
39#include <libart_lgpl/art_rgb_affine.h>
40#include <libart_lgpl/art_rgb_rgba_affine.h>
41
42/* 4/3 * (1-cos 45ƒ)/sin 45ƒ = 4/3 * sqrt(2) - 1 */
43#define RSVG_ARC_MAGIC ((double) 0.5522847498)
44
45/**
46 * rsvg_close_vpath: Close a vector path.
47 * @src: Source vector path.
48 *
49 * Closes any open subpaths in the vector path.
50 *
51 * Return value: Closed vector path, allocated with g_new.
52 **/
53static ArtVpath *
54rsvg_close_vpath (const ArtVpath *src)
55{
56        ArtVpath *result;
57        int n_result, n_result_max;
58        int src_ix;
59        double beg_x, beg_y;
60        gboolean open;
61       
62        n_result = 0;
63        n_result_max = 16;
64        result = g_new (ArtVpath, n_result_max);
65       
66        beg_x = 0;
67        beg_y = 0;
68        open = FALSE;
69       
70        for (src_ix = 0; src[src_ix].code != ART_END; src_ix++)
71                {
72                        if (n_result == n_result_max)
73                                result = g_renew (ArtVpath, result, n_result_max <<= 1);
74                        result[n_result].code = src[src_ix].code == ART_MOVETO_OPEN ?
75                                ART_MOVETO : src[src_ix].code;
76                        result[n_result].x = src[src_ix].x;
77                        result[n_result].y = src[src_ix].y;
78                        n_result++;
79                        if (src[src_ix].code == ART_MOVETO_OPEN)
80                                {
81                                        beg_x = src[src_ix].x;
82                                        beg_y = src[src_ix].y;
83                                        open = TRUE;
84                                }
85                        else if (src[src_ix + 1].code != ART_LINETO)
86                                {
87                                        if (open && (beg_x != src[src_ix].x || beg_y != src[src_ix].y))
88                                                {
89                                                        if (n_result == n_result_max)
90                                                                result = g_renew (ArtVpath, result, n_result_max <<= 1);
91                                                        result[n_result].code = ART_LINETO;
92                                                        result[n_result].x = beg_x;
93                                                        result[n_result].y = beg_y;
94                                                        n_result++;
95                                                }
96                                        open = FALSE;
97                                }
98                }
99        if (n_result == n_result_max)
100                result = g_renew (ArtVpath, result, n_result_max <<= 1);
101        result[n_result].code = ART_END;
102        result[n_result].x = 0.0;
103        result[n_result].y = 0.0;
104        return result;
105}
106
107/**
108 * rsvg_render_svp: Render an SVP.
109 * @ctx: Context in which to render.
110 * @svp: SVP to render.
111 * @ps: Paint server for rendering.
112 * @opacity: Opacity as 0..0xff.
113 *
114 * Renders the SVP over the pixbuf in @ctx.
115 **/
116static void
117rsvg_render_svp (RsvgHandle *ctx, const ArtSVP *svp,
118                                 RsvgPaintServer *ps, int opacity)
119{
120        GdkPixbuf *pixbuf;
121        ArtRender *render;
122        gboolean has_alpha;
123       
124        pixbuf = ctx->pixbuf;
125        if (pixbuf == NULL)
126                {
127                        /* FIXME: What warning/GError here? */
128                        return;
129                }
130       
131        has_alpha = gdk_pixbuf_get_has_alpha (pixbuf);
132       
133        render = art_render_new (0, 0,
134                                                         gdk_pixbuf_get_width (pixbuf),
135                                                         gdk_pixbuf_get_height (pixbuf),
136                                                         gdk_pixbuf_get_pixels (pixbuf),
137                                                         gdk_pixbuf_get_rowstride (pixbuf),
138                                                         gdk_pixbuf_get_n_channels (pixbuf) -
139                                                         (has_alpha ? 1 : 0),
140                                                         gdk_pixbuf_get_bits_per_sample (pixbuf),
141                                                         has_alpha ? ART_ALPHA_SEPARATE : ART_ALPHA_NONE,
142                                                         NULL);
143       
144        art_render_svp (render, svp);
145        art_render_mask_solid (render, (opacity << 8) + opacity + (opacity >> 7));
146        rsvg_render_paint_server (render, ps, NULL); /* todo: paint server ctx */
147        art_render_invoke (render);
148}
149
150static void
151rsvg_render_bpath (RsvgHandle *ctx, const ArtBpath *bpath)
152{
153        RsvgState *state;
154        ArtBpath *affine_bpath;
155        ArtVpath *vpath;
156        ArtSVP *svp;
157        GdkPixbuf *pixbuf;
158        gboolean need_tmpbuf;
159        int opacity;
160        int tmp;
161
162        pixbuf = ctx->pixbuf;
163        if (pixbuf == NULL)
164                {
165                        /* FIXME: What warning/GError here? */
166                        return;
167                }
168       
169        state = &ctx->state[ctx->n_state - 1];
170
171        /* todo: handle visibility stuff earlier for performance benefits
172         * handles all path based shapes. will handle text and images separately
173         */
174        if (!state->visible)
175                return;
176
177        affine_bpath = art_bpath_affine_transform (bpath,
178                                                                                           state->affine);
179       
180        vpath = art_bez_path_to_vec (affine_bpath, 0.25);
181        art_free (affine_bpath);
182       
183        need_tmpbuf = (state->fill != NULL) && (state->stroke != NULL) &&
184                state->opacity != 0xff;
185       
186        if (need_tmpbuf)
187                rsvg_push_opacity_group (ctx);
188       
189        if (state->fill != NULL)
190                {
191                        ArtVpath *closed_vpath;
192                        ArtSVP *svp2;
193                        ArtSvpWriter *swr;
194                       
195                        closed_vpath = rsvg_close_vpath (vpath);
196                        svp = art_svp_from_vpath (closed_vpath);
197                        g_free (closed_vpath);
198                       
199                        swr = art_svp_writer_rewind_new (ART_WIND_RULE_NONZERO);
200                        art_svp_intersector (svp, swr);
201                       
202                        svp2 = art_svp_writer_rewind_reap (swr);
203                        art_svp_free (svp);
204                       
205                        opacity = state->fill_opacity;
206                        if (!need_tmpbuf && state->opacity != 0xff)
207                                {
208                                        tmp = opacity * state->opacity + 0x80;
209                                        opacity = (tmp + (tmp >> 8)) >> 8;
210                                }
211                        rsvg_render_svp (ctx, svp2, state->fill, opacity);
212                        art_svp_free (svp2);
213                }
214       
215        if (state->stroke != NULL)
216                {
217                        /* todo: libart doesn't yet implement anamorphic scaling of strokes */
218                        double stroke_width = state->stroke_width *
219                                art_affine_expansion (state->affine);
220                       
221                        if (stroke_width < 0.25)
222                                stroke_width = 0.25;
223                       
224                        /* if the path is dashed, stroke it */
225                        if (state->dash.n_dash > 0)
226                                {
227                                        ArtVpath * dashed_vpath = art_vpath_dash (vpath, &state->dash);
228                                        art_free (vpath);
229                                        vpath = dashed_vpath;
230                                }
231                       
232                        svp = art_svp_vpath_stroke (vpath, state->join, state->cap,
233                                                                                stroke_width, state->miter_limit, 0.25);
234                        opacity = state->stroke_opacity;
235                        if (!need_tmpbuf && state->opacity != 0xff)
236                                {
237                                        tmp = opacity * state->opacity + 0x80;
238                                        opacity = (tmp + (tmp >> 8)) >> 8;
239                                }
240                        rsvg_render_svp (ctx, svp, state->stroke, opacity);
241                        art_svp_free (svp);
242                }
243       
244        if (need_tmpbuf)
245                rsvg_pop_opacity_group (ctx, state->opacity);
246       
247        art_free (vpath);
248}
249
250void
251rsvg_render_path(RsvgHandle *ctx, const char *d)
252{
253        RsvgBpathDef *bpath_def;
254       
255        bpath_def = rsvg_parse_path (d);
256        rsvg_bpath_def_art_finish (bpath_def);
257       
258        rsvg_render_bpath (ctx, bpath_def->bpath);
259       
260        rsvg_bpath_def_free (bpath_def);
261}
262
263void
264rsvg_start_path (RsvgHandle *ctx, const xmlChar **atts)
265{
266        int i;
267        char *d = NULL;
268        const char * klazz = NULL, * id = NULL;
269       
270        if (atts != NULL)
271                {
272                        for (i = 0; atts[i] != NULL; i += 2)
273                                {
274                                        if (!strcmp ((char *)atts[i], "d"))
275                                                d = (char *)atts[i + 1];
276                                        else if (!strcmp ((char *)atts[i], "class"))
277                                                klazz = (char *)atts[i + 1];
278                                        else if (!strcmp ((char *)atts[i], "id"))
279                                                id = (const char *)atts[i + 1];
280                                }
281                }
282       
283        if (d == NULL)
284                return;
285       
286        rsvg_parse_style_attrs (ctx, "path", klazz, id, atts);
287        rsvg_render_path (ctx, d);
288}
289
290static GString *
291rsvg_make_poly_point_list(const char * points)
292{
293        guint idx = 0, size = strlen(points);
294        GString * str = g_string_sized_new (size);
295       
296        while (idx < size)
297                {
298                        /* scan for first point */
299                        while (!g_ascii_isdigit (points[idx]) && (points[idx] != '.')
300                                   && (points[idx] != '-') && (idx < size))
301                                idx++;
302                       
303                        /* now build up the point list (everything until next letter!) */
304                        if (idx < size && points[idx] == '-')
305                                g_string_append_c (str, points[idx++]); /* handle leading '-' */
306                        while ((g_ascii_isdigit (points[idx]) || (points[idx] == '.')) && (idx < size))
307                                g_string_append_c (str, points[idx++]);
308                       
309                        g_string_append_c (str, ' ');
310                }
311       
312        return str;
313}
314
315static void
316rsvg_start_any_poly(RsvgHandle *ctx, const xmlChar **atts, gboolean is_polyline)
317{
318        /* the only difference between polygon and polyline is
319           that a polyline closes the path */
320       
321        int i;
322        const char * verts = (const char *)NULL;
323        GString * g = NULL;
324        gchar ** pointlist = NULL;
325        const char * klazz = NULL, * id = NULL;
326       
327        if (atts != NULL)
328                {
329                        for (i = 0; atts[i] != NULL; i += 2)
330                                {
331                                        /* support for svg < 1.0 which used verts */
332                                        if (!strcmp ((char *)atts[i], "verts") || !strcmp ((char *)atts[i], "points"))
333                                                verts = (const char *)atts[i + 1];
334                                        else if (!strcmp ((char *)atts[i], "class"))
335                                                klazz = (const char *)atts[i + 1];
336                                        else if (!strcmp ((char *)atts[i], "id"))
337                                                id = (const char *)atts[i + 1];
338                                }
339                }
340       
341        if (!verts)
342                return;
343       
344        rsvg_parse_style_attrs (ctx, (is_polyline ? "polyline" : "polygon"), klazz, id, atts);
345       
346        /* todo: make the following more memory and CPU friendly */
347        g = rsvg_make_poly_point_list (verts);
348        pointlist = g_strsplit (g->str, " ", -1);
349        g_string_free (g, TRUE);
350
351        /* represent as a "moveto, lineto*, close" path */ 
352        if (pointlist)
353                {
354                        GString * d = g_string_sized_new (strlen(verts));
355                        g_string_append_printf (d, "M %s %s ", pointlist[0], pointlist[1] );
356                       
357                        for (i = 2; pointlist[i] != NULL && pointlist[i][0] != '\0'; i += 2)
358                                g_string_append_printf (d, "L %s %s ", pointlist[i], pointlist[i+1]);
359                       
360                        if (!is_polyline)
361                                g_string_append (d, "Z");
362                       
363                        rsvg_render_path (ctx, d->str);
364                        g_string_free (d, TRUE);
365                        g_strfreev(pointlist);
366                }
367}
368
369void
370rsvg_start_polygon (RsvgHandle *ctx, const xmlChar **atts)
371{
372        rsvg_start_any_poly (ctx, atts, FALSE);
373}
374
375void
376rsvg_start_polyline (RsvgHandle *ctx, const xmlChar **atts)
377{
378        rsvg_start_any_poly (ctx, atts, TRUE);
379}
380
381void
382rsvg_start_line (RsvgHandle *ctx, const xmlChar **atts)
383{
384        int i;
385        double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
386        GString * d = NULL;
387        const char * klazz = NULL, * id = NULL;
388        RsvgState *state = &ctx->state[ctx->n_state - 1];
389        char buf [G_ASCII_DTOSTR_BUF_SIZE];
390
391        if (atts != NULL)
392                {
393                        for (i = 0; atts[i] != NULL; i += 2)
394                                {
395                                        if (!strcmp ((char *)atts[i], "x1"))
396                                                x1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
397                                        else if (!strcmp ((char *)atts[i], "y1"))
398                                                y1 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
399                                        if (!strcmp ((char *)atts[i], "x2"))
400                                                x2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
401                                        else if (!strcmp ((char *)atts[i], "y2"))
402                                                y2 = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
403                                        else if (!strcmp ((char *)atts[i], "class"))
404                                                klazz = (const char *)atts[i + 1];
405                                        else if (!strcmp ((char *)atts[i], "id"))
406                                                id = (const char *)atts[i + 1];
407                                }     
408                }
409        rsvg_parse_style_attrs (ctx, "line", klazz, id, atts);
410       
411        /* emulate a line using a path */
412        /* ("M %f %f L %f %f", x1, y1, x2, y2) */
413        d = g_string_new ("M ");   
414
415        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x1));
416        g_string_append_c (d, ' ');
417        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y1));
418        g_string_append (d, " L ");     
419        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x2));
420        g_string_append_c (d, ' ');
421        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y2));   
422
423        rsvg_render_path (ctx, d->str);
424        g_string_free (d, TRUE);
425}
426
427void
428rsvg_start_rect (RsvgHandle *ctx, const xmlChar **atts)
429{
430        int i;
431        double x = -1, y = -1, w = -1, h = -1, rx = 0., ry = 0.;
432        GString * d = NULL;
433        const char * klazz = NULL, * id = NULL;
434        RsvgState *state = &ctx->state[ctx->n_state - 1];
435        char buf [G_ASCII_DTOSTR_BUF_SIZE];
436        gboolean got_rx = FALSE, got_ry = FALSE;
437
438        if (atts != NULL)
439                {
440                        for (i = 0; atts[i] != NULL; i += 2)
441                                {
442                                        if (!strcmp ((char *)atts[i], "x"))
443                                                x = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
444                                        else if (!strcmp ((char *)atts[i], "y"))
445                                                y = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
446                                        else if (!strcmp ((char *)atts[i], "width"))
447                                                w = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
448                                        else if (!strcmp ((char *)atts[i], "height"))
449                                                h = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
450                                        else if (!strcmp ((char *)atts[i], "rx")) {
451                                                rx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
452                                                got_rx = TRUE;
453                                        }
454                                        else if (!strcmp ((char *)atts[i], "ry")) {
455                                                ry = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
456                                                got_ry = TRUE;
457                                        }
458                                        else if (!strcmp ((char *)atts[i], "class"))
459                                                klazz = (const char *)atts[i + 1];
460                                        else if (!strcmp ((char *)atts[i], "id"))
461                                                id = (const char *)atts[i + 1];
462                                }
463                }
464
465        if (got_rx && !got_ry)
466                ry = rx;
467       
468        if (x < 0. || y < 0. || w < 0. || h < 0. || rx < 0. || ry < 0.)
469                return;
470       
471        rsvg_parse_style_attrs (ctx, "rect", klazz, id, atts);
472       
473        /* incrementing y by 1 properly draws borders. this is a HACK */
474        y += 1.;
475       
476        /* emulate a rect using a path */
477        /*
478        d = g_strdup_printf ("M %f %f "
479                                                 "H %f "
480                                                 "A %f,%f %f,%f %f %f,%f "
481                                                 "V %f "
482                                                 "A %f,%f %f,%f %f %f,%f "
483                                                 "H %f "
484                                                 "A %f,%f %f,%f %f %f,%f "
485                                                 "V %f "
486                                                 "A %f,%f %f,%f %f %f,%f",
487                                                 x + rx, y,
488                                                 x + w - rx,
489                                                 rx, ry, 0., 0., 1., x + w, y + ry,
490                                                 y + h - ry,
491                                                 rx, ry, 0., 0., 1., x + w - rx, y + h,
492                                                 x + rx,
493                                                 rx, ry, 0., 0., 1., x, y + h - ry,
494                                                 y + ry,
495                                                 rx, ry, 0., 0., 1., x + rx, y);
496        */
497
498        d = g_string_new ("M ");
499        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + rx));
500        g_string_append_c (d, ' ');
501        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y));
502
503        g_string_append (d, " H ");
504        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + w - rx));
505
506        g_string_append (d, " A");
507        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), rx));
508        g_string_append_c (d, ' ');
509        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), ry));
510        g_string_append_c (d, ' ');
511        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
512        g_string_append_c (d, ' ');
513        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
514        g_string_append_c (d, ' ');
515        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 1.));
516        g_string_append_c (d, ' ');
517        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x+w));
518        g_string_append_c (d, ' ');
519        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y+ry));
520
521        g_string_append (d, " V ");
522        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y+h-ry));
523
524        g_string_append (d, " A");
525        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), rx));
526        g_string_append_c (d, ' ');
527        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), ry));
528        g_string_append_c (d, ' ');
529        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
530        g_string_append_c (d, ' ');
531        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
532        g_string_append_c (d, ' ');
533        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 1.));
534        g_string_append_c (d, ' ');
535        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + w - rx));
536        g_string_append_c (d, ' ');
537        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y + h));
538
539        g_string_append (d, " H ");
540        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x + rx));
541
542        g_string_append (d, " A");
543        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), rx));
544        g_string_append_c (d, ' ');
545        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), ry));
546        g_string_append_c (d, ' ');
547        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
548        g_string_append_c (d, ' ');
549        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
550        g_string_append_c (d, ' ');     
551        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 1.));
552        g_string_append_c (d, ' ');
553        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x));
554        g_string_append_c (d, ' ');
555        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y + h - ry));
556
557        g_string_append (d, " V ");
558        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y+ry));
559
560        g_string_append (d, " A");
561        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), rx));
562        g_string_append_c (d, ' ');
563        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), ry));
564        g_string_append_c (d, ' ');
565        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
566        g_string_append_c (d, ' ');     
567        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 0.));
568        g_string_append_c (d, ' ');
569        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), 1.));
570        g_string_append_c (d, ' ');
571        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), x+rx));
572        g_string_append_c (d, ' ');
573        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), y));
574
575        rsvg_render_path (ctx, d->str);
576        g_string_free (d, TRUE);
577}
578
579void
580rsvg_start_circle (RsvgHandle *ctx, const xmlChar **atts)
581{
582        int i;
583        double cx = 0, cy = 0, r = 0;
584        GString * d = NULL;
585        const char * klazz = NULL, * id = NULL;
586        RsvgState *state = &ctx->state[ctx->n_state - 1];
587        char buf [G_ASCII_DTOSTR_BUF_SIZE];
588
589        if (atts != NULL)
590                {
591                        for (i = 0; atts[i] != NULL; i += 2)
592                                {
593                                        if (!strcmp ((char *)atts[i], "cx"))
594                                                cx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
595                                        else if (!strcmp ((char *)atts[i], "cy"))
596                                                cy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
597                                        else if (!strcmp ((char *)atts[i], "r"))
598                                                r = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi,
599                                                                                                                          rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height),
600                                                                                                                          state->font_size);
601                                        else if (!strcmp ((char *)atts[i], "class"))
602                                                klazz = (const char *)atts[i + 1];
603                                        else if (!strcmp ((char *)atts[i], "id"))
604                                                id = (const char *)atts[i + 1];
605                                }
606                }
607       
608        if (cx < 0. || cy < 0. || r <= 0.)
609                return;
610       
611        rsvg_parse_style_attrs (ctx, "circle", klazz, id, atts);
612       
613        /* approximate a circle using 4 bezier curves */
614        /*
615        d = g_strdup_printf ("M %f %f "
616                                                 "C %f %f %f %f %f %f "
617                                                 "C %f %f %f %f %f %f "
618                                                 "C %f %f %f %f %f %f "
619                                                 "C %f %f %f %f %f %f "
620                                                 "Z",
621                                                 cx + r, cy,
622                                                 cx + r, cy + r * RSVG_ARC_MAGIC, cx + r * RSVG_ARC_MAGIC, cy + r, cx, cy + r,
623                                                 cx - r * RSVG_ARC_MAGIC, cy + r, cx - r, cy + r * RSVG_ARC_MAGIC, cx - r, cy,
624                                                 cx - r, cy - r * RSVG_ARC_MAGIC, cx - r * RSVG_ARC_MAGIC, cy - r, cx, cy - r,
625                                                 cx + r * RSVG_ARC_MAGIC, cy - r, cx + r, cy - r * RSVG_ARC_MAGIC, cx + r, cy
626                                                 );
627        */
628
629        d = g_string_new ("M ");
630        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx+r));
631        g_string_append_c (d, ' ');
632        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
633
634        g_string_append (d, " C ");
635        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx+r));
636        g_string_append_c (d, ' ');
637        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r * RSVG_ARC_MAGIC));
638        g_string_append_c (d, ' ');
639        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r * RSVG_ARC_MAGIC));
640        g_string_append_c (d, ' ');
641        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r));
642        g_string_append_c (d, ' ');
643        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx));
644        g_string_append_c (d, ' ');
645        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r));
646
647        g_string_append (d, " C ");
648        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r * RSVG_ARC_MAGIC));
649        g_string_append_c (d, ' ');
650        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r));
651        g_string_append_c (d, ' ');
652        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r));
653        g_string_append_c (d, ' ');
654        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + r * RSVG_ARC_MAGIC));
655        g_string_append_c (d, ' ');
656        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r));
657        g_string_append_c (d, ' ');
658        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
659
660        g_string_append (d, " C ");
661        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r));
662        g_string_append_c (d, ' ');
663        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r * RSVG_ARC_MAGIC));
664        g_string_append_c (d, ' ');
665        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - r * RSVG_ARC_MAGIC));
666        g_string_append_c (d, ' ');
667        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r));
668        g_string_append_c (d, ' ');
669        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx));
670        g_string_append_c (d, ' ');
671        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r));
672
673        g_string_append (d, " C ");
674        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r * RSVG_ARC_MAGIC));
675        g_string_append_c (d, ' ');
676        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r));
677        g_string_append_c (d, ' ');
678        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r));
679        g_string_append_c (d, ' ');
680        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - r * RSVG_ARC_MAGIC));
681        g_string_append_c (d, ' ');
682        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + r));
683        g_string_append_c (d, ' ');
684        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
685
686        g_string_append (d, " Z");
687
688        rsvg_render_path (ctx, d->str);
689        g_string_free (d, TRUE);
690}
691
692void
693rsvg_start_ellipse (RsvgHandle *ctx, const xmlChar **atts)
694{
695        int i;
696        double cx = 0, cy = 0, rx = 0, ry = 0;
697        GString * d = NULL;
698        const char * klazz = NULL, * id = NULL;
699        RsvgState *state = &ctx->state[ctx->n_state - 1];
700        char buf [G_ASCII_DTOSTR_BUF_SIZE];
701
702        if (atts != NULL)
703                {
704                        for (i = 0; atts[i] != NULL; i += 2)
705                                {
706                                        if (!strcmp ((char *)atts[i], "cx"))
707                                                cx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
708                                        else if (!strcmp ((char *)atts[i], "cy"))
709                                                cy = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
710                                        else if (!strcmp ((char *)atts[i], "rx"))
711                                                rx = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
712                                        else if (!strcmp ((char *)atts[i], "ry"))
713                                                ry = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
714                                        else if (!strcmp ((char *)atts[i], "class"))
715                                                klazz = (const char *)atts[i + 1];
716                                        else if (!strcmp ((char *)atts[i], "id"))
717                                                id = (const char *)atts[i + 1];
718                                }
719                }
720       
721        if (cx < 0. || cy < 0. || rx <= 0. || ry <= 0.)
722                return;
723       
724        rsvg_parse_style_attrs (ctx, "ellipse", klazz, id, atts);
725       
726        /* approximate an ellipse using 4 bezier curves */
727        /*
728        d = g_strdup_printf ("M %f %f "
729                                                 "C %f %f %f %f %f %f "
730                                                 "C %f %f %f %f %f %f "
731                                                 "C %f %f %f %f %f %f "
732                                                 "C %f %f %f %f %f %f "
733                                                 "Z",
734                                                 cx + rx, cy,
735                                                 cx + rx, cy - RSVG_ARC_MAGIC * ry, cx + RSVG_ARC_MAGIC * rx, cy - ry, cx, cy - ry,
736                                                 cx - RSVG_ARC_MAGIC * rx, cy - ry, cx - rx, cy - RSVG_ARC_MAGIC * ry, cx - rx, cy,
737                                                 cx - rx, cy + RSVG_ARC_MAGIC * ry, cx - RSVG_ARC_MAGIC * rx, cy + ry, cx, cy + ry,
738                                                 cx + RSVG_ARC_MAGIC * rx, cy + ry, cx + rx, cy + RSVG_ARC_MAGIC * ry, cx + rx, cy
739                                                 );
740        */
741        d = g_string_new ("M ");
742        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + rx));
743        g_string_append_c (d, ' ');
744        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
745
746        g_string_append (d, " C ");
747        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + rx));
748        g_string_append_c (d, ' ');
749        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - RSVG_ARC_MAGIC * ry));
750        g_string_append_c (d, ' ');
751        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + RSVG_ARC_MAGIC * rx));
752        g_string_append_c (d, ' ');
753        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - ry));
754        g_string_append_c (d, ' ');
755        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx));
756        g_string_append_c (d, ' ');
757        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - ry));
758
759        g_string_append (d, " C ");
760        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - RSVG_ARC_MAGIC * rx));
761        g_string_append_c (d, ' ');
762        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - ry));
763        g_string_append_c (d, ' ');
764        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - rx));
765        g_string_append_c (d, ' ');
766        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy - RSVG_ARC_MAGIC * ry));
767        g_string_append_c (d, ' ');
768        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - rx));
769        g_string_append_c (d, ' ');
770        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
771
772        g_string_append (d, " C ");
773        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - rx));
774        g_string_append_c (d, ' ');
775        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + RSVG_ARC_MAGIC * ry));
776        g_string_append_c (d, ' ');
777        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx - RSVG_ARC_MAGIC * rx));
778        g_string_append_c (d, ' ');
779        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + ry));
780        g_string_append_c (d, ' ');
781        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx));
782        g_string_append_c (d, ' ');
783        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + ry));
784
785        g_string_append (d, " C ");
786        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + RSVG_ARC_MAGIC * rx));
787        g_string_append_c (d, ' ');
788        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + ry));
789        g_string_append_c (d, ' ');
790        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + rx));
791        g_string_append_c (d, ' ');
792        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy + RSVG_ARC_MAGIC * ry));
793        g_string_append_c (d, ' ');
794        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cx + rx));
795        g_string_append_c (d, ' ');
796        g_string_append (d, g_ascii_dtostr (buf, sizeof (buf), cy));
797
798        g_string_append (d, " Z");
799
800        rsvg_render_path (ctx, d->str);
801        g_string_free (d, TRUE);
802}
803
804/* TODO 1: issue with affining alpha images - this is gdkpixbuf's fault...
805 * TODO 2: issue with rotating images - do we want to rotate the whole
806 *         canvas 2x to get this right, only to have #1 bite us?
807 */
808void
809rsvg_start_image (RsvgHandle *ctx, const xmlChar **atts)
810{
811        int i;
812        double x = 0., y = 0., w = -1., h = -1.;
813        const char * href = NULL;
814        const char * klazz = NULL, * id = NULL;
815       
816        GdkPixbuf *img;
817        GError *err = NULL;
818       
819        gboolean has_alpha;
820        guchar *rgb = NULL;
821        int dest_rowstride;
822        double tmp_affine[6];
823        RsvgState *state = &ctx->state[ctx->n_state - 1];
824       
825        if (atts != NULL)
826                {
827                        for (i = 0; atts[i] != NULL; i += 2)
828                                {
829                                        if (!strcmp ((char *)atts[i], "x"))
830                                                x = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
831                                        else if (!strcmp ((char *)atts[i], "y"))
832                                                y = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
833                                        else if (!strcmp ((char *)atts[i], "width"))
834                                                w = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->width, state->font_size);
835                                        else if (!strcmp ((char *)atts[i], "height"))
836                                                h = rsvg_css_parse_normalized_length ((char *)atts[i + 1], ctx->dpi, (gdouble)ctx->height, state->font_size);
837                                        /* path is used by some older adobe illustrator versions */
838                                        else if (!strcmp ((char *)atts[i], "path") || !strcmp((char *)atts[i], "xlink:href"))
839                                                href = (const char *)atts[i + 1];
840                                        else if (!strcmp ((char *)atts[i], "class"))
841                                                klazz = (const char *)atts[i + 1];
842                                        else if (!strcmp ((char *)atts[i], "id"))
843                                                id = (const char *)atts[i + 1];
844                                }
845                }
846       
847        if (!href || x < 0. || y < 0. || w <= 0. || h <= 0.)
848                return;
849       
850        rsvg_parse_style_attrs (ctx, "image", klazz, id, atts);
851       
852        /* figure out if image is visible or not */
853        if (!state->visible)
854                return;
855
856        img = gdk_pixbuf_new_from_file (href, &err);
857       
858        if (!img)
859                {
860                        if (err)
861                                {
862                                        g_warning ("Couldn't load pixbuf (%s): %s\n", href, err->message);
863                                        g_error_free (err);
864                                }
865                        return;
866                }
867       
868        /* scale/resize the dest image */
869        art_affine_scale (tmp_affine, (double)w / (double)gdk_pixbuf_get_width (img),
870                                          (double)h / (double)gdk_pixbuf_get_height (img));
871        art_affine_multiply (state->affine, tmp_affine, state->affine);
872       
873        has_alpha = gdk_pixbuf_get_has_alpha (img);
874        dest_rowstride = (int)(w * (has_alpha ? 4 : 3) + 3) & ~3;
875        rgb = g_new (guchar, h * dest_rowstride);
876       
877        if(has_alpha)
878                art_rgb_rgba_affine (rgb, 0, 0, w, h, dest_rowstride,
879                                                         gdk_pixbuf_get_pixels (img),
880                                                         gdk_pixbuf_get_width (img),
881                                                         gdk_pixbuf_get_height (img),
882                                                         gdk_pixbuf_get_rowstride (img),
883                                                         state->affine,
884                                                         ART_FILTER_NEAREST,
885                                                         NULL);
886        else
887                art_rgb_affine (rgb, 0, 0, w, h, dest_rowstride,
888                                                gdk_pixbuf_get_pixels (img),
889                                                gdk_pixbuf_get_width (img),
890                                                gdk_pixbuf_get_height (img),
891                                                gdk_pixbuf_get_rowstride (img),
892                                                state->affine,
893                                                ART_FILTER_NEAREST,
894                                                NULL);
895       
896        g_object_unref (G_OBJECT (img));
897        img = gdk_pixbuf_new_from_data (rgb, GDK_COLORSPACE_RGB, has_alpha, 8, w, h, dest_rowstride, NULL, NULL);
898       
899        if (!img)
900                {
901                        g_free (rgb);
902                        return;
903                }
904       
905        gdk_pixbuf_copy_area (img, 0, 0,
906                                                  gdk_pixbuf_get_width (img) * state->affine[0],
907                                                  gdk_pixbuf_get_height (img) * state->affine[3],
908                                                  ctx->pixbuf,
909                                                  state->affine[4] + x,
910                                                  state->affine[5] + y);
911       
912        g_object_unref (G_OBJECT (img));
913        g_free (rgb);
914}
Note: See TracBrowser for help on using the repository browser.