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