Allow pattern specifications to have a fallback color, per the spec

See here: https://www.w3.org/TR/SVG/painting.html#SpecifyingPaint

A paint specification can be a color, or "<funciri> [somecolor]".  If
the optional color is specified, it will be used in case the paint
server specified by the IRI is not valid (not found, doesn't have enough
parameters, or somehow doesn't manage to paint anything).
parent 0eb00115
...@@ -167,46 +167,65 @@ _set_source_rsvg_solid_color (RsvgDrawingCtx * ctx, ...@@ -167,46 +167,65 @@ _set_source_rsvg_solid_color (RsvgDrawingCtx * ctx,
cairo_set_source_rgba (cr, r, g, b, a); cairo_set_source_rgba (cr, r, g, b, a);
} }
static void static gboolean
_set_source_rsvg_pattern (RsvgDrawingCtx * ctx, _set_source_rsvg_pattern (RsvgDrawingCtx * ctx,
RsvgPattern * rsvg_pattern, RsvgBbox bbox) RsvgPattern * rsvg_pattern, RsvgBbox bbox)
{ {
Pattern *pattern; Pattern *pattern;
gboolean result;
pattern = rsvg_pattern_node_to_rust_pattern ((RsvgNode *) rsvg_pattern); pattern = rsvg_pattern_node_to_rust_pattern ((RsvgNode *) rsvg_pattern);
pattern_resolve_fallbacks_and_set_pattern (pattern, ctx, bbox); result = pattern_resolve_fallbacks_and_set_pattern (pattern, ctx, bbox);
pattern_destroy (pattern); pattern_destroy (pattern);
return result;
} }
/* note: _set_source_rsvg_paint_server does not change cairo's CTM */ /* note: _set_source_rsvg_paint_server does not change cairo's CTM */
static void static gboolean
_set_source_rsvg_paint_server (RsvgDrawingCtx * ctx, _set_source_rsvg_paint_server (RsvgDrawingCtx * ctx,
guint32 current_color_rgb, guint32 current_color_rgb,
RsvgPaintServer * ps, RsvgPaintServer * ps,
guint8 opacity, RsvgBbox bbox, guint32 current_color) guint8 opacity, RsvgBbox bbox, guint32 current_color)
{ {
RsvgNode *node; RsvgNode *node;
gboolean had_paint_server;
had_paint_server = FALSE;
switch (ps->type) { switch (ps->type) {
case RSVG_PAINT_SERVER_IRI: case RSVG_PAINT_SERVER_IRI:
node = rsvg_drawing_ctx_acquire_node (ctx, ps->core.iri); node = rsvg_drawing_ctx_acquire_node (ctx, ps->core.iri->iri_str);
if (node == NULL) if (node == NULL)
break; break;
else if (rsvg_node_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) else if (rsvg_node_type (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
_set_source_rsvg_linear_gradient (ctx, (RsvgLinearGradient *) node, opacity, bbox); _set_source_rsvg_linear_gradient (ctx, (RsvgLinearGradient *) node, opacity, bbox);
else if (rsvg_node_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) had_paint_server = TRUE;
} else if (rsvg_node_type (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
_set_source_rsvg_radial_gradient (ctx, (RsvgRadialGradient *) node, opacity, bbox); _set_source_rsvg_radial_gradient (ctx, (RsvgRadialGradient *) node, opacity, bbox);
else if (rsvg_node_type (node) == RSVG_NODE_TYPE_PATTERN) had_paint_server = TRUE;
_set_source_rsvg_pattern (ctx, (RsvgPattern *) node, bbox); } else if (rsvg_node_type (node) == RSVG_NODE_TYPE_PATTERN) {
if (_set_source_rsvg_pattern (ctx, (RsvgPattern *) node, bbox)) {
had_paint_server = TRUE;
} else {
if (ps->core.iri->has_alternate) {
_set_source_rsvg_solid_color (ctx, &ps->core.iri->alternate, opacity, current_color);
had_paint_server = TRUE;
}
}
}
rsvg_drawing_ctx_release_node (ctx, node); rsvg_drawing_ctx_release_node (ctx, node);
break; break;
case RSVG_PAINT_SERVER_SOLID: case RSVG_PAINT_SERVER_SOLID:
_set_source_rsvg_solid_color (ctx, ps->core.color, opacity, current_color); _set_source_rsvg_solid_color (ctx, ps->core.color, opacity, current_color);
had_paint_server = TRUE;
break; break;
} }
return had_paint_server;
} }
static void static void
...@@ -348,14 +367,17 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub ...@@ -348,14 +367,17 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
cairo_save (render->cr); cairo_save (render->cr);
cairo_move_to (render->cr, x, y); cairo_move_to (render->cr, x, y);
rsvg_bbox_insert (&render->bbox, &bbox); rsvg_bbox_insert (&render->bbox, &bbox);
_set_source_rsvg_paint_server (ctx,
state->current_color, if (_set_source_rsvg_paint_server (ctx,
state->fill, state->current_color,
state->fill_opacity, state->fill,
bbox, rsvg_current_state (ctx)->current_color); state->fill_opacity,
if (rotation != 0.) bbox, rsvg_current_state (ctx)->current_color)) {
cairo_rotate (render->cr, -rotation); if (rotation != 0.)
pango_cairo_show_layout (render->cr, layout); cairo_rotate (render->cr, -rotation);
pango_cairo_show_layout (render->cr, layout);
}
cairo_restore (render->cr); cairo_restore (render->cr);
} }
...@@ -364,19 +386,20 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub ...@@ -364,19 +386,20 @@ rsvg_cairo_render_pango_layout (RsvgDrawingCtx * ctx, PangoLayout * layout, doub
cairo_move_to (render->cr, x, y); cairo_move_to (render->cr, x, y);
rsvg_bbox_insert (&render->bbox, &bbox); rsvg_bbox_insert (&render->bbox, &bbox);
_set_source_rsvg_paint_server (ctx, if (_set_source_rsvg_paint_server (ctx,
state->current_color, state->current_color,
state->stroke, state->stroke,
state->stroke_opacity, state->stroke_opacity,
bbox, rsvg_current_state (ctx)->current_color); bbox, rsvg_current_state (ctx)->current_color)) {
if (rotation != 0.)
cairo_rotate (render->cr, -rotation);
pango_cairo_layout_path (render->cr, layout);
if (rotation != 0.) setup_cr_for_stroke (render->cr, ctx, state);
cairo_rotate (render->cr, -rotation);
pango_cairo_layout_path (render->cr, layout);
setup_cr_for_stroke (render->cr, ctx, state); cairo_stroke (render->cr);
}
cairo_stroke (render->cr);
cairo_restore (render->cr); cairo_restore (render->cr);
} }
} }
...@@ -452,27 +475,27 @@ rsvg_cairo_render_path_builder (RsvgDrawingCtx * ctx, RsvgPathBuilder *builder) ...@@ -452,27 +475,27 @@ rsvg_cairo_render_path_builder (RsvgDrawingCtx * ctx, RsvgPathBuilder *builder)
opacity = state->fill_opacity; opacity = state->fill_opacity;
_set_source_rsvg_paint_server (ctx, if (_set_source_rsvg_paint_server (ctx,
state->current_color, state->current_color,
state->fill, state->fill,
opacity, bbox, rsvg_current_state (ctx)->current_color); opacity, bbox, rsvg_current_state (ctx)->current_color)) {
if (state->stroke != NULL)
if (state->stroke != NULL) cairo_fill_preserve (cr);
cairo_fill_preserve (cr); else
else cairo_fill (cr);
cairo_fill (cr); }
} }
if (state->stroke != NULL) { if (state->stroke != NULL) {
int opacity; int opacity;
opacity = state->stroke_opacity; opacity = state->stroke_opacity;
_set_source_rsvg_paint_server (ctx, if (_set_source_rsvg_paint_server (ctx,
state->current_color, state->current_color,
state->stroke, state->stroke,
opacity, bbox, rsvg_current_state (ctx)->current_color); opacity, bbox, rsvg_current_state (ctx)->current_color)) {
cairo_stroke (cr);
cairo_stroke (cr); }
} }
cairo_new_path (cr); /* clear the path in case stroke == fill == NULL; otherwise we leave it around from computing the bounding box */ cairo_new_path (cr); /* clear the path in case stroke == fill == NULL; otherwise we leave it around from computing the bounding box */
......
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* vim: set sw=4 sts=4 ts=4 expandtab: */ /* vim: set sw=4 sts=4 ts=4 expandtab: */
/* /*
rsvg-paint-server.c: Implement the SVG paint server abstraction. rsvg-paint-server.c: Implement the SVG paint server abstraction.
Copyright (C) 2000 Eazel, Inc. Copyright (C) 2000 Eazel, Inc.
This program is free software; you can redistribute it and/or This program is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details. Library General Public License for more details.
You should have received a copy of the GNU Library General Public You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the License along with this program; if not, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330, Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. Boston, MA 02111-1307, USA.
Author: Raph Levien <raph@artofcode.com> Author: Raph Levien <raph@artofcode.com>
*/ */
...@@ -64,17 +64,37 @@ rsvg_paint_server_solid_current_color (void) ...@@ -64,17 +64,37 @@ rsvg_paint_server_solid_current_color (void)
} }
static RsvgPaintServer * static RsvgPaintServer *
rsvg_paint_server_iri (char *iri) rsvg_paint_server_iri (char *iri, gboolean has_alternate, RsvgSolidColor alternate)
{ {
RsvgPaintServer *result = g_new (RsvgPaintServer, 1); RsvgPaintServer *result = g_new (RsvgPaintServer, 1);
result->refcnt = 1; result->refcnt = 1;
result->type = RSVG_PAINT_SERVER_IRI; result->type = RSVG_PAINT_SERVER_IRI;
result->core.iri = iri; result->core.iri = g_new0 (RsvgPaintServerIri, 1);
result->core.iri->iri_str = iri;
result->core.iri->has_alternate = has_alternate;
result->core.iri->alternate = alternate;
return result; return result;
} }
static gboolean
parse_current_color_or_argb (const char *str, RsvgSolidColor *dest)
{
if (!strcmp (str, "currentColor")) {
dest->currentcolor = TRUE;
dest->argb = 0;
return TRUE;
} else {
gboolean parsed;
dest->currentcolor = FALSE;
dest->argb = rsvg_css_parse_color (str, &parsed);
return parsed;
}
}
/** /**
* rsvg_paint_server_parse: * rsvg_paint_server_parse:
* @str: The SVG paint specification string to parse. * @str: The SVG paint specification string to parse.
...@@ -86,21 +106,34 @@ rsvg_paint_server_iri (char *iri) ...@@ -86,21 +106,34 @@ rsvg_paint_server_iri (char *iri)
* on error. * on error.
**/ **/
RsvgPaintServer * RsvgPaintServer *
rsvg_paint_server_parse (gboolean * inherit, const char *str) rsvg_paint_server_parse (gboolean *inherit, const char *str)
{ {
char *name; char *name;
const char *rest;
guint32 argb; guint32 argb;
if (inherit != NULL) if (inherit != NULL)
*inherit = 1; *inherit = TRUE;
if (str == NULL || !strcmp (str, "none")) if (str == NULL || !strcmp (str, "none"))
return NULL; return NULL;
name = rsvg_get_url_string (str, NULL); name = rsvg_get_url_string (str, &rest);
if (name) { if (name) {
return rsvg_paint_server_iri (name); RsvgSolidColor alternate;
gboolean has_alternate;
while (*rest && g_ascii_isspace (*rest)) {
rest++;
}
has_alternate = parse_current_color_or_argb (rest, &alternate);
return rsvg_paint_server_iri (name, has_alternate, alternate);
} else if (!strcmp (str, "inherit")) { } else if (!strcmp (str, "inherit")) {
/* Do the fallback to black here; don't let the caller do it via inheritance */
if (inherit != NULL) if (inherit != NULL)
*inherit = 0; *inherit = FALSE;
return rsvg_paint_server_solid (0); return rsvg_paint_server_solid (0);
} else if (!strcmp (str, "currentColor")) { } else if (!strcmp (str, "currentColor")) {
RsvgPaintServer *ps; RsvgPaintServer *ps;
...@@ -140,8 +173,10 @@ rsvg_paint_server_unref (RsvgPaintServer * ps) ...@@ -140,8 +173,10 @@ rsvg_paint_server_unref (RsvgPaintServer * ps)
if (--ps->refcnt == 0) { if (--ps->refcnt == 0) {
if (ps->type == RSVG_PAINT_SERVER_SOLID) if (ps->type == RSVG_PAINT_SERVER_SOLID)
g_free (ps->core.color); g_free (ps->core.color);
else if (ps->type == RSVG_PAINT_SERVER_IRI) else if (ps->type == RSVG_PAINT_SERVER_IRI) {
g_free (ps->core.iri->iri_str);
g_free (ps->core.iri); g_free (ps->core.iri);
}
g_free (ps); g_free (ps);
} }
} }
......
...@@ -173,9 +173,9 @@ void pattern_destroy (Pattern *pattern); ...@@ -173,9 +173,9 @@ void pattern_destroy (Pattern *pattern);
/* Implemented in rust/src/pattern.rs */ /* Implemented in rust/src/pattern.rs */
G_GNUC_INTERNAL G_GNUC_INTERNAL
void pattern_resolve_fallbacks_and_set_pattern (Pattern *pattern, gboolean pattern_resolve_fallbacks_and_set_pattern (Pattern *pattern,
RsvgDrawingCtx *draw_ctx, RsvgDrawingCtx *draw_ctx,
RsvgBbox bbox); RsvgBbox bbox);
G_GNUC_INTERNAL G_GNUC_INTERNAL
Pattern *rsvg_pattern_node_to_rust_pattern (RsvgNode *node); Pattern *rsvg_pattern_node_to_rust_pattern (RsvgNode *node);
...@@ -192,10 +192,17 @@ struct _RsvgSolidColor { ...@@ -192,10 +192,17 @@ struct _RsvgSolidColor {
typedef struct _RsvgSolidColor RsvgPaintServerColor; typedef struct _RsvgSolidColor RsvgPaintServerColor;
typedef enum _RsvgPaintServerType RsvgPaintServerType; typedef enum _RsvgPaintServerType RsvgPaintServerType;
typedef union _RsvgPaintServerCore RsvgPaintServerCore; typedef union _RsvgPaintServerCore RsvgPaintServerCore;
typedef struct _RsvgPaintServerIri RsvgPaintServerIri;
struct _RsvgPaintServerIri {
char *iri_str;
gboolean has_alternate;
RsvgSolidColor alternate;
};
union _RsvgPaintServerCore { union _RsvgPaintServerCore {
RsvgSolidColor *color; RsvgSolidColor *color;
char *iri; RsvgPaintServerIri *iri;
}; };
enum _RsvgPaintServerType { enum _RsvgPaintServerType {
...@@ -211,7 +218,7 @@ struct _RsvgPaintServer { ...@@ -211,7 +218,7 @@ struct _RsvgPaintServer {
/* Create a new paint server based on a specification string. */ /* Create a new paint server based on a specification string. */
G_GNUC_INTERNAL G_GNUC_INTERNAL
RsvgPaintServer *rsvg_paint_server_parse (gboolean * inherit, const char *str); RsvgPaintServer *rsvg_paint_server_parse (gboolean *inherit, const char *str);
G_GNUC_INTERNAL G_GNUC_INTERNAL
void rsvg_paint_server_ref (RsvgPaintServer * ps); void rsvg_paint_server_ref (RsvgPaintServer * ps);
G_GNUC_INTERNAL G_GNUC_INTERNAL
......
...@@ -202,9 +202,13 @@ impl FallbackSource for NodeFallbackSource { ...@@ -202,9 +202,13 @@ impl FallbackSource for NodeFallbackSource {
fn set_pattern_on_draw_context (pattern: &Pattern, fn set_pattern_on_draw_context (pattern: &Pattern,
draw_ctx: *mut RsvgDrawingCtx, draw_ctx: *mut RsvgDrawingCtx,
bbox: &RsvgBbox) { bbox: &RsvgBbox) -> bool {
assert! (pattern.is_resolved ()); assert! (pattern.is_resolved ());
if !pattern_node_has_children (pattern.c_node) {
return false;
}
let obj_bbox = pattern.obj_bbox.unwrap (); let obj_bbox = pattern.obj_bbox.unwrap ();
let obj_cbbox = pattern.obj_cbbox.unwrap (); let obj_cbbox = pattern.obj_cbbox.unwrap ();
let pattern_affine = pattern.affine.unwrap (); let pattern_affine = pattern.affine.unwrap ();
...@@ -248,8 +252,9 @@ fn set_pattern_on_draw_context (pattern: &Pattern, ...@@ -248,8 +252,9 @@ fn set_pattern_on_draw_context (pattern: &Pattern,
let scaled_width = pattern_width * bbwscale; let scaled_width = pattern_width * bbwscale;
let scaled_height = pattern_height * bbhscale; let scaled_height = pattern_height * bbhscale;
if scaled_width.abs () < DBL_EPSILON || scaled_height.abs () < DBL_EPSILON { if scaled_width.abs () < DBL_EPSILON || scaled_height.abs () < DBL_EPSILON
return || pw < 1 || ph < 1 {
return false;
} }
scwscale = pw as f64 / scaled_width; scwscale = pw as f64 / scaled_width;
...@@ -356,6 +361,8 @@ fn set_pattern_on_draw_context (pattern: &Pattern, ...@@ -356,6 +361,8 @@ fn set_pattern_on_draw_context (pattern: &Pattern,
surface_pattern.set_filter (Filter::Best); surface_pattern.set_filter (Filter::Best);
cr_save.set_source (&surface_pattern); cr_save.set_source (&surface_pattern);
true
} }
#[no_mangle] #[no_mangle]
...@@ -416,7 +423,7 @@ pub unsafe extern fn pattern_destroy (raw_pattern: *mut Pattern) { ...@@ -416,7 +423,7 @@ pub unsafe extern fn pattern_destroy (raw_pattern: *mut Pattern) {
#[no_mangle] #[no_mangle]
pub extern fn pattern_resolve_fallbacks_and_set_pattern (raw_pattern: *mut Pattern, pub extern fn pattern_resolve_fallbacks_and_set_pattern (raw_pattern: *mut Pattern,
draw_ctx: *mut RsvgDrawingCtx, draw_ctx: *mut RsvgDrawingCtx,
bbox: RsvgBbox) { bbox: RsvgBbox) -> bool {
assert! (!raw_pattern.is_null ()); assert! (!raw_pattern.is_null ());
let pattern: &mut Pattern = unsafe { &mut (*raw_pattern) }; let pattern: &mut Pattern = unsafe { &mut (*raw_pattern) };
...@@ -426,5 +433,5 @@ pub extern fn pattern_resolve_fallbacks_and_set_pattern (raw_pattern: *mut Patte ...@@ -426,5 +433,5 @@ pub extern fn pattern_resolve_fallbacks_and_set_pattern (raw_pattern: *mut Patte
set_pattern_on_draw_context (&resolved, set_pattern_on_draw_context (&resolved,
draw_ctx, draw_ctx,
&bbox); &bbox)
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment