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

Revision 18609, 21.5 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-styles.c: Handle SVG styles
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.h"
27#include "rsvg-css.h"
28#include "rsvg-styles.h"
29#include "rsvg-private.h"
30
31#include <libart_lgpl/art_rgba.h>
32#include <libart_lgpl/art_affine.h>
33
34/* Our default font */
35#define DEFAULT_FONT "Times Roman"
36
37gdouble
38rsvg_viewport_percentage (gdouble width, gdouble height)
39{
40        return ((width * width) + (height * height)) / M_SQRT2;
41}
42
43void
44rsvg_state_init (RsvgState *state)
45{
46        memset (state, 0, sizeof (*state));
47       
48        art_affine_identity (state->affine);
49       
50        state->opacity = 0xff;
51        state->fill = rsvg_paint_server_parse (NULL, "#000");
52        state->fill_opacity = 0xff;
53        state->stroke_opacity = 0xff;
54        state->stroke_width = 1;
55        state->miter_limit = 4;
56        state->cap = ART_PATH_STROKE_CAP_BUTT;
57        state->join = ART_PATH_STROKE_JOIN_MITER;
58        state->stop_opacity = 0xff;
59       
60        state->font_family  = g_strdup (DEFAULT_FONT);
61        state->font_size    = 12.0;
62        state->font_style   = PANGO_STYLE_NORMAL;
63        state->font_variant = PANGO_VARIANT_NORMAL;
64        state->font_weight  = PANGO_WEIGHT_NORMAL;
65        state->font_stretch = PANGO_STRETCH_NORMAL;
66
67        state->visible = TRUE;
68}
69
70void
71rsvg_state_clone (RsvgState *dst, const RsvgState *src)
72{
73        gint i;
74       
75        *dst = *src;
76        dst->font_family = g_strdup (src->font_family);
77        rsvg_paint_server_ref (dst->fill);
78        rsvg_paint_server_ref (dst->stroke);
79        dst->save_pixbuf = NULL;
80       
81        if (src->dash.n_dash > 0)
82                {
83                        dst->dash.dash = g_new (gdouble, src->dash.n_dash);
84                        for (i = 0; i < src->dash.n_dash; i++)
85                                dst->dash.dash[i] = src->dash.dash[i];
86                }
87}
88
89void
90rsvg_state_finalize (RsvgState *state)
91{
92        g_free (state->font_family);
93        rsvg_paint_server_unref (state->fill);
94        rsvg_paint_server_unref (state->stroke);
95       
96        if (state->dash.n_dash != 0)
97                g_free (state->dash.dash);
98}
99
100/* Parse a CSS2 style argument, setting the SVG context attributes. */
101static void
102rsvg_parse_style_arg (RsvgHandle *ctx, RsvgState *state, const char *str)
103{
104        RsvgState * parent_state = state; /* TODO: temporary hack */
105        int arg_off;
106       
107        arg_off = rsvg_css_param_arg_offset (str);
108        if (rsvg_css_param_match (str, "opacity"))
109                {
110                        state->opacity = rsvg_css_parse_opacity (str + arg_off);
111                }
112        else if (rsvg_css_param_match (str, "display"))
113                {
114                        if (!strcmp (str + arg_off, "none"))
115                                state->visible = FALSE;
116                        else if (strcmp (str + arg_off, "inherit") != 0)
117                                state->visible = TRUE;
118                        /* else inherit */
119                }
120        else if (rsvg_css_param_match (str, "visibility"))
121                {
122                        if (!strcmp (str + arg_off, "visable"))
123                                state->visible = TRUE;
124                        else if (strcmp (str + arg_off, "inherit") != 0)
125                                state->visible = FALSE; /* collapse or hidden */
126                        /* else inherit */
127                }
128        else if (rsvg_css_param_match (str, "fill"))
129                {
130                        rsvg_paint_server_unref (state->fill);
131                        state->fill = rsvg_paint_server_parse (ctx->defs, str + arg_off);
132                }
133        else if (rsvg_css_param_match (str, "fill-opacity"))
134                {
135                        state->fill_opacity = rsvg_css_parse_opacity (str + arg_off);
136                }
137        else if (rsvg_css_param_match (str, "stroke"))
138                {
139                        rsvg_paint_server_unref (state->stroke);
140                        state->stroke = rsvg_paint_server_parse (ctx->defs, str + arg_off);
141                }
142        else if (rsvg_css_param_match (str, "stroke-width"))
143                {
144                        state->stroke_width = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi,
145                                                                                                                                        (gdouble)ctx->height, state->font_size);
146                }
147        else if (rsvg_css_param_match (str, "stroke-linecap"))
148                {
149                        if (!strcmp (str + arg_off, "butt"))
150                                state->cap = ART_PATH_STROKE_CAP_BUTT;
151                        else if (!strcmp (str + arg_off, "round"))
152                                state->cap = ART_PATH_STROKE_CAP_ROUND;
153                        else if (!strcmp (str + arg_off, "square"))
154                                state->cap = ART_PATH_STROKE_CAP_SQUARE;
155                        else
156                                g_warning ("unknown line cap style %s", str + arg_off);
157                }
158        else if (rsvg_css_param_match (str, "stroke-opacity"))
159                {
160                        state->stroke_opacity = rsvg_css_parse_opacity (str + arg_off);
161                }
162        else if (rsvg_css_param_match (str, "stroke-linejoin"))
163                {
164                        if (!strcmp (str + arg_off, "miter"))
165                                state->join = ART_PATH_STROKE_JOIN_MITER;
166                        else if (!strcmp (str + arg_off, "round"))
167                                state->join = ART_PATH_STROKE_JOIN_ROUND;
168                        else if (!strcmp (str + arg_off, "bevel"))
169                                state->join = ART_PATH_STROKE_JOIN_BEVEL;
170                        else
171                                g_warning ("unknown line join style %s", str + arg_off);
172                }
173        else if (rsvg_css_param_match (str, "font-size"))
174                {
175                        state->font_size = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi,
176                                                                                                                                 (gdouble)ctx->height, state->font_size);
177                }
178        else if (rsvg_css_param_match (str, "font-family"))
179                {
180                        char * save = g_strdup (rsvg_css_parse_font_family (str + arg_off, parent_state->font_family));
181                        g_free (state->font_family);
182                        state->font_family = save;
183                }
184        else if (rsvg_css_param_match (str, "font-style"))
185                {
186                        state->font_style = rsvg_css_parse_font_style (str + arg_off, parent_state->font_style);
187                }
188        else if (rsvg_css_param_match (str, "font-variant"))
189                {
190                        state->font_variant = rsvg_css_parse_font_variant (str + arg_off, parent_state->font_variant);
191                }
192        else if (rsvg_css_param_match (str, "font-weight"))
193                {
194                        state->font_weight = rsvg_css_parse_font_weight (str + arg_off, parent_state->font_weight);
195                }
196        else if (rsvg_css_param_match (str, "font-stretch"))
197                {
198                        state->font_stretch = rsvg_css_parse_font_stretch (str + arg_off, parent_state->font_stretch);
199                }
200        else if (rsvg_css_param_match (str, "text-decoration"))
201                {
202                        if (!strcmp (str, "inherit"))
203                                state->font_decor = parent_state->font_decor;
204                        else
205                                {
206                                        if (strstr (str, "underline"))
207                                                state->font_decor |= TEXT_UNDERLINE;
208                                        if (strstr (str, "overline"))
209                                                state->font_decor |= TEXT_OVERLINE;
210                                        if (strstr (str, "strike") || strstr (str, "line-through")) /* strike though or line-through */
211                                                state->font_decor |= TEXT_STRIKE;
212                                }
213                }
214        else if (rsvg_css_param_match (str, "stop-color"))
215                {
216                        state->stop_color = rsvg_css_parse_color (str + arg_off);
217                }
218        else if (rsvg_css_param_match (str, "stop-opacity"))
219                {
220                        state->stop_opacity = rsvg_css_parse_opacity (str + arg_off);
221                }
222        else if (rsvg_css_param_match (str, "stroke-miterlimit"))
223                {
224                        state->miter_limit = g_ascii_strtod (str + arg_off, NULL);
225                }
226        else if (rsvg_css_param_match (str, "stroke-dashoffset"))
227                {
228                        state->dash.offset = rsvg_css_parse_normalized_length (str + arg_off, ctx->dpi,
229                                                                                                                                   rsvg_viewport_percentage((gdouble)ctx->width, (gdouble)ctx->height), state->font_size);
230                        if (state->dash.offset < 0.)
231                                state->dash.offset = 0.;
232                }
233        else if (rsvg_css_param_match (str, "stroke-dasharray"))
234                {
235                        if(!strcmp(str + arg_off, "none"))
236                                {
237                                        if (state->dash.n_dash != 0)
238                                                {
239                                                        /* free any cloned dash data */
240                                                        g_free (state->dash.dash);
241                                                        state->dash.n_dash = 0;
242                                                }
243                                }
244                        else
245                                {
246                                        gchar ** dashes = g_strsplit (str + arg_off, ",", -1);
247                                        if (NULL != dashes)
248                                                {
249                                                        gint n_dashes, i;
250                                                        gboolean is_even = FALSE ;
251                                                       
252                                                        /* count the #dashes */
253                                                        for (n_dashes = 0; dashes[n_dashes] != NULL; n_dashes++)
254                                                                ;
255                                                       
256                                                        is_even = (n_dashes % 2 == 0);
257                                                        state->dash.n_dash = (is_even ? n_dashes : n_dashes * 2);
258                                                        state->dash.dash = g_new (double, state->dash.n_dash);
259                                                       
260                                                        /* TODO: handle negative value == error case */
261                                                       
262                                                        /* the even and base case */
263                                                        for (i = 0; i < n_dashes; i++)
264                                                                state->dash.dash[i] = g_ascii_strtod (dashes[i], NULL);
265                                                       
266                                                        /* if an odd number of dashes is found, it gets repeated */
267                                                        if (!is_even)
268                                                                for (; i < state->dash.n_dash; i++)
269                                                                        state->dash.dash[i] = g_ascii_strtod (dashes[i - n_dashes], NULL);
270                                                       
271                                                        g_strfreev (dashes) ;
272                                                }
273                                }
274                }
275}
276
277/* tell whether @str is a supported style argument
278   whenever something gets added to parse_arg, please
279   remember to add it here too
280*/
281gboolean
282rsvg_is_style_arg(const char *str)
283{
284        static GHashTable *styles = NULL;
285        if (!styles)
286                {
287                        styles = g_hash_table_new (g_str_hash, g_str_equal);
288                       
289                        g_hash_table_insert (styles, "display",           GINT_TO_POINTER (TRUE));
290                        g_hash_table_insert (styles, "fill",              GINT_TO_POINTER (TRUE));
291                        g_hash_table_insert (styles, "fill-opacity",      GINT_TO_POINTER (TRUE));
292                        g_hash_table_insert (styles, "font-family",       GINT_TO_POINTER (TRUE));
293                        g_hash_table_insert (styles, "font-size",         GINT_TO_POINTER (TRUE));
294                        g_hash_table_insert (styles, "font-stretch",      GINT_TO_POINTER (TRUE));
295                        g_hash_table_insert (styles, "font-style",        GINT_TO_POINTER (TRUE));
296                        g_hash_table_insert (styles, "font-variant",      GINT_TO_POINTER (TRUE));
297                        g_hash_table_insert (styles, "font-weight",       GINT_TO_POINTER (TRUE));
298                        g_hash_table_insert (styles, "opacity",           GINT_TO_POINTER (TRUE));
299                        g_hash_table_insert (styles, "stop-color",        GINT_TO_POINTER (TRUE));
300                        g_hash_table_insert (styles, "stop-opacity",      GINT_TO_POINTER (TRUE));
301                        g_hash_table_insert (styles, "stroke",            GINT_TO_POINTER (TRUE));
302                        g_hash_table_insert (styles, "stroke-dasharray",  GINT_TO_POINTER (TRUE));
303                        g_hash_table_insert (styles, "stroke-dashoffset", GINT_TO_POINTER (TRUE));
304                        g_hash_table_insert (styles, "stroke-linecap",    GINT_TO_POINTER (TRUE));
305                        g_hash_table_insert (styles, "stroke-linejoin",   GINT_TO_POINTER (TRUE));
306                        g_hash_table_insert (styles, "stroke-miterlimit", GINT_TO_POINTER (TRUE));
307                        g_hash_table_insert (styles, "stroke-opacity",    GINT_TO_POINTER (TRUE));
308                        g_hash_table_insert (styles, "stroke-width",      GINT_TO_POINTER (TRUE));
309                        g_hash_table_insert (styles, "text-decoration",   GINT_TO_POINTER (TRUE));
310                        g_hash_table_insert (styles, "visibility",        GINT_TO_POINTER (TRUE));
311                }
312       
313        /* this will default to 0 (FALSE) on a failed lookup */
314        return GPOINTER_TO_INT (g_hash_table_lookup (styles, str));
315}
316
317/* take a pair of the form (fill="#ff00ff") and parse it as a style */
318void
319rsvg_parse_style_pair (RsvgHandle *ctx, RsvgState *state,
320                                           const char *key, const char *val)
321{
322        gchar * str = g_strdup_printf ("%s:%s", key, val);
323        rsvg_parse_style_arg (ctx, state, str);
324        g_free (str);
325}
326
327/* Split a CSS2 style into individual style arguments, setting attributes
328   in the SVG context.
329   
330   It's known that this is _way_ out of spec. A more complete CSS2
331   implementation will happen later.
332*/
333void
334rsvg_parse_style (RsvgHandle *ctx, RsvgState *state, const char *str)
335{
336        int start, end;
337        char *arg;
338       
339        start = 0;
340        while (str[start] != '\0')
341                {
342                        for (end = start; str[end] != '\0' && str[end] != ';'; end++);
343                        arg = g_new (char, 1 + end - start);
344                        memcpy (arg, str + start, end - start);
345                        arg[end - start] = '\0';
346                        rsvg_parse_style_arg (ctx, state, arg);
347                        g_free (arg);
348                        start = end;
349                        if (str[start] == ';') start++;
350                        while (str[start] == ' ') start++;
351                }
352}
353
354/*
355 * Extremely poor man's CSS parser. Not robust. Not compliant.
356 * Should work well enough for our needs ;-)
357 */
358void
359rsvg_parse_cssbuffer (RsvgHandle *ctx, const char * buff, size_t buflen)
360{
361        size_t loc = 0;
362       
363        while (loc < buflen)
364                {
365                        GString * style_name = g_string_new (NULL);
366                        GString * style_props = g_string_new (NULL);
367                       
368                        /* advance to the style's name */
369                        while (loc < buflen && g_ascii_isspace (buff[loc]))
370                                loc++;
371                       
372                        while (loc < buflen && !g_ascii_isspace (buff[loc]))
373                                g_string_append_c (style_name, buff[loc++]);
374                       
375                        /* advance to the first { that defines the style's properties */
376                        while (loc < buflen && buff[loc++] != '{' )
377                                ;
378                       
379                        while (loc < buflen && g_ascii_isspace (buff[loc]))
380                                loc++;
381                       
382                        while (buff[loc] != '}')
383                                {
384                                        /* suck in and append our property */
385                                        while (loc < buflen && buff[loc] != ';' && buff[loc] != '}' )
386                                                g_string_append_c (style_props, buff[loc++]);
387
388                                        if (buff[loc] == '}')
389                                                break;
390                                        else
391                                                {
392                                                        g_string_append_c (style_props, ';');
393                                                       
394                                                        /* advance to the next property */
395                                                        loc++;
396                                                        while (g_ascii_isspace (buff[loc]) && loc < buflen)
397                                                                loc++;
398                                                }
399                                }
400
401                        /* push name/style pair into HT */
402                        g_hash_table_insert (ctx->css_props, style_name->str, style_props->str);
403                       
404                        g_string_free (style_name, FALSE);
405                        g_string_free (style_props, FALSE);
406                       
407                        loc++;
408                        while (g_ascii_isspace (buff[loc]) && loc < buflen)
409                                loc++;
410                }
411}
412
413/* Parse an SVG transform string into an affine matrix. Reference: SVG
414   working draft dated 1999-07-06, section 8.5. Return TRUE on
415   success. */
416gboolean
417rsvg_parse_transform (double dst[6], const char *src)
418{
419        int idx;
420        char keyword[32];
421        double args[6];
422        int n_args;
423        guint key_len;
424        double tmp_affine[6];
425       
426        art_affine_identity (dst);
427       
428        idx = 0;
429        while (src[idx])
430                {
431                        /* skip initial whitespace */
432                        while (g_ascii_isspace (src[idx]))
433                                idx++;
434                       
435                        /* parse keyword */
436                        for (key_len = 0; key_len < sizeof (keyword); key_len++)
437                                {
438                                        char c;
439                                       
440                                        c = src[idx];
441                                        if (g_ascii_isalpha (c) || c == '-')
442                                                keyword[key_len] = src[idx++];
443                                        else
444                                                break;
445                                }
446                        if (key_len >= sizeof (keyword))
447                                return FALSE;
448                        keyword[key_len] = '\0';
449                       
450                        /* skip whitespace */
451                        while (g_ascii_isspace (src[idx]))
452                                idx++;
453                       
454                        if (src[idx] != '(')
455                                return FALSE;
456                        idx++;
457                       
458                        for (n_args = 0; ; n_args++)
459                                {
460                                        char c;
461                                        char *end_ptr;
462                                       
463                                        /* skip whitespace */
464                                        while (g_ascii_isspace (src[idx]))
465                                                idx++;
466                                        c = src[idx];
467                                        if (g_ascii_isdigit (c) || c == '+' || c == '-' || c == '.')
468                                                {
469                                                        if (n_args == sizeof(args) / sizeof(args[0]))
470                                                                return FALSE; /* too many args */
471                                                        args[n_args] = g_ascii_strtod (src + idx, &end_ptr);
472                                                        idx = end_ptr - src;
473                                                       
474                                                        while (g_ascii_isspace (src[idx]))
475                                                                idx++;
476                                                       
477                                                        /* skip optional comma */
478                                                        if (src[idx] == ',')
479                                                                idx++;
480                                                }
481                                        else if (c == ')')
482                                                break;
483                                        else
484                                                return FALSE;
485                                }
486                        idx++;
487                       
488                        /* ok, have parsed keyword and args, now modify the transform */
489                        if (!strcmp (keyword, "matrix"))
490                                {
491                                        if (n_args != 6)
492                                                return FALSE;
493                                        art_affine_multiply (dst, args, dst);
494                                }
495                        else if (!strcmp (keyword, "translate"))
496                                {
497                                        if (n_args == 1)
498                                                args[1] = 0;
499                                        else if (n_args != 2)
500                                                return FALSE;
501                                        art_affine_translate (tmp_affine, args[0], args[1]);
502                                        art_affine_multiply (dst, tmp_affine, dst);
503                                }
504                        else if (!strcmp (keyword, "scale"))
505                                {
506                                        if (n_args == 1)
507                                                args[1] = args[0];
508                                        else if (n_args != 2)
509                                                return FALSE;
510                                        art_affine_scale (tmp_affine, args[0], args[1]);
511                                        art_affine_multiply (dst, tmp_affine, dst);
512                                }
513                        else if (!strcmp (keyword, "rotate"))
514                                {
515                                        if (n_args != 1)
516                                                return FALSE;
517                                        art_affine_rotate (tmp_affine, args[0]);
518                                        art_affine_multiply (dst, tmp_affine, dst);
519                                }
520                        else if (!strcmp (keyword, "skewX"))
521                                {
522                                        if (n_args != 1)
523                                                return FALSE;
524                                        art_affine_shear (tmp_affine, args[0]);
525                                        art_affine_multiply (dst, tmp_affine, dst);
526                                }
527                        else if (!strcmp (keyword, "skewY"))
528                                {
529                                        if (n_args != 1)
530                                                return FALSE;
531                                        art_affine_shear (tmp_affine, args[0]);
532                                        /* transpose the affine, given that we know [1] is zero */
533                                        tmp_affine[1] = tmp_affine[2];
534                                        tmp_affine[2] = 0;
535                                        art_affine_multiply (dst, tmp_affine, dst);
536                                }
537                        else
538                                return FALSE; /* unknown keyword */
539                }
540        return TRUE;
541}
542
543/**
544 * rsvg_parse_transform_attr: Parse transform attribute and apply to state.
545 * @ctx: Rsvg context.
546 * @state: State in which to apply the transform.
547 * @str: String containing transform.
548 *
549 * Parses the transform attribute in @str and applies it to @state.
550 **/
551static void
552rsvg_parse_transform_attr (RsvgHandle *ctx, RsvgState *state, const char *str)
553{
554        double affine[6];
555       
556        if (rsvg_parse_transform (affine, str))
557                {
558                        art_affine_multiply (state->affine, affine, state->affine);
559                }
560        else
561                {
562                        /* parse error for transform attribute. todo: report */
563                }
564}
565
566static gboolean
567rsvg_lookup_apply_css_style (RsvgHandle *ctx, const char * target)
568{
569        const char * value = (const char *)g_hash_table_lookup (ctx->css_props, target);
570       
571        if (value != NULL)
572                {
573                        rsvg_parse_style (ctx, &ctx->state[ctx->n_state - 1],
574                                                          value);
575                        return TRUE;
576                }
577        return FALSE;
578}
579
580/**
581 * rsvg_parse_style_attrs: Parse style attribute.
582 * @ctx: Rsvg context.
583 * @tag: The SVG tag we're processing (eg: circle, ellipse), optionally %NULL
584 * @klazz: The space delimited class list, optionally %NULL
585 * @atts: Attributes in SAX style.
586 *
587 * Parses style and transform attributes and modifies state at top of
588 * stack.
589 **/
590void
591rsvg_parse_style_attrs (RsvgHandle *ctx,
592                                                const char * tag,
593                                                const char * klazz,
594                                                const char * id,
595                                                const xmlChar **atts)
596{
597        int i = 0, j = 0;
598        char * target = NULL;
599        gboolean found = FALSE;
600        GString * klazz_list = NULL;
601       
602        /* handle the all-encompassing "star" entry first, ignoring found-ness */
603        rsvg_lookup_apply_css_style (ctx, "*");
604
605        if (id != NULL)
606                {
607                        target = g_strdup_printf ("#%s", id);
608                        rsvg_lookup_apply_css_style (ctx, target);
609                        g_free (target);
610                }
611
612        /* todo: see if klazz or tag should accumulate onto tag.klazz */
613
614        if (tag != NULL && klazz != NULL)
615                {
616                        target = g_strdup_printf ("%s.%s", tag, klazz);
617                        found = rsvg_lookup_apply_css_style (ctx, target);
618                        g_free (target);
619                }
620       
621        if (found == FALSE)
622                {
623                        if (tag != NULL)
624                                rsvg_lookup_apply_css_style (ctx, tag);
625                       
626                        if (klazz != NULL)
627                                {
628                                        i = strlen (klazz);
629                                        while (j < i)
630                                                {
631                                                        klazz_list = g_string_new (".");
632                                                       
633                                                        while (j < i && g_ascii_isspace(klazz[j]))
634                                                                j++;
635                                                       
636                                                        while (j < i && !g_ascii_isspace(klazz[j]))
637                                                                g_string_append_c (klazz_list, klazz[j++]);
638                                                       
639                                                        rsvg_lookup_apply_css_style (ctx, klazz_list->str);
640                                                        g_string_free (klazz_list, TRUE);
641                                                }
642                                }
643                }
644       
645        if (atts != NULL)
646                {
647                        for (i = 0; atts[i] != NULL; i += 2)
648                                {
649                                        if (!strcmp ((char *)atts[i], "style"))
650                                                rsvg_parse_style (ctx, &ctx->state[ctx->n_state - 1],
651                                                                                  (char *)atts[i + 1]);
652                                        else if (!strcmp ((char *)atts[i], "transform"))
653                                                rsvg_parse_transform_attr (ctx, &ctx->state[ctx->n_state - 1],
654                                                                                                   (char *)atts[i + 1]);
655                                        else if (rsvg_is_style_arg ((char *)atts[i]))
656                                                rsvg_parse_style_pair (ctx, &ctx->state[ctx->n_state - 1],
657                                                                                           (char *)atts[i], (char *)atts[i + 1]);
658                                }
659                }
660}
661
662static void
663rsvg_pixmap_destroy (guchar *pixels, gpointer data)
664{
665  g_free (pixels);
666}
667
668/**
669 * rsvg_push_opacity_group: Begin a new transparency group.
670 * @ctx: Context in which to push.
671 *
672 * Pushes a new transparency group onto the stack. The top of the stack
673 * is stored in the context, while the "saved" value is in the state
674 * stack.
675 **/
676void
677rsvg_push_opacity_group (RsvgHandle *ctx)
678{
679        RsvgState *state;
680        GdkPixbuf *pixbuf;
681        art_u8 *pixels;
682        int width, height, rowstride;
683       
684        state = &ctx->state[ctx->n_state - 1];
685        pixbuf = ctx->pixbuf;
686       
687        state->save_pixbuf = pixbuf;
688       
689        if (pixbuf == NULL)
690                {
691                        /* FIXME: What warning/GError here? */
692                        return;
693                }
694
695        if (!gdk_pixbuf_get_has_alpha (pixbuf))
696    {
697                g_warning ("push/pop transparency group on non-alpha buffer nyi");
698                return;
699    }
700       
701        width = gdk_pixbuf_get_width (pixbuf);
702        height = gdk_pixbuf_get_height (pixbuf);
703        rowstride = gdk_pixbuf_get_rowstride (pixbuf);
704        pixels = g_new (art_u8, rowstride * height);
705        memset (pixels, 0, rowstride * height);
706       
707        pixbuf = gdk_pixbuf_new_from_data (pixels,
708                                                                           GDK_COLORSPACE_RGB,
709                                                                           TRUE,
710                                                                           gdk_pixbuf_get_bits_per_sample (pixbuf),
711                                                                           width,
712                                                                           height,
713                                                                           rowstride,
714                                                                           rsvg_pixmap_destroy,
715                                                                           NULL);
716        ctx->pixbuf = pixbuf;
717}
718
719/**
720 * rsvg_pop_opacity_group: End a transparency group.
721 * @ctx: Context in which to push.
722 * @opacity: Opacity for blending (0..255).
723 *
724 * Pops a new transparency group from the stack, recompositing with the
725 * next on stack.
726 **/
727void
728rsvg_pop_opacity_group (RsvgHandle *ctx, int opacity)
729{
730        RsvgState *state = &ctx->state[ctx->n_state - 1];
731        GdkPixbuf *tos, *nos;
732        art_u8 *tos_pixels, *nos_pixels;
733        int width;
734        int height;
735        int rowstride;
736        int x, y;
737        int tmp;
738       
739        tos = ctx->pixbuf;
740        nos = state->save_pixbuf;
741       
742        if (tos == NULL || nos == NULL)
743                {
744                        /* FIXME: What warning/GError here? */
745                        return;
746                }
747       
748        if (!gdk_pixbuf_get_has_alpha (nos))
749                {
750                        g_warning ("push/pop transparency group on non-alpha buffer nyi");
751                        return;
752                }
753       
754        width = gdk_pixbuf_get_width (tos);
755        height = gdk_pixbuf_get_height (tos);
756        rowstride = gdk_pixbuf_get_rowstride (tos);
757       
758        tos_pixels = gdk_pixbuf_get_pixels (tos);
759        nos_pixels = gdk_pixbuf_get_pixels (nos);
760       
761        for (y = 0; y < height; y++)
762                {
763                        for (x = 0; x < width; x++)
764                                {
765                                        art_u8 r, g, b, a;
766                                        a = tos_pixels[4 * x + 3];
767                                        if (a)
768                                                {
769                                                        r = tos_pixels[4 * x];
770                                                        g = tos_pixels[4 * x + 1];
771                                                        b = tos_pixels[4 * x + 2];
772                                                        tmp = a * opacity + 0x80;
773                                                        a = (tmp + (tmp >> 8)) >> 8;
774                                                        art_rgba_run_alpha (nos_pixels + 4 * x, r, g, b, a, 1);
775                                                }
776                                }
777                        tos_pixels += rowstride;
778                        nos_pixels += rowstride;
779                }
780       
781        g_object_unref (tos);
782        ctx->pixbuf = nos;
783}
Note: See TracBrowser for help on using the repository browser.