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

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