RsvgGradientStop: store whether the stop's values are valid

The spec mandates that values for stop offsets are only plain numbers or
percentages.  We used to handle this more or less implicitly by calling
_rsvg_css_hand_normalize_length(), using the resulting value, and
dropping the units.  Now we actually ensure that we get plain numbers
or percentages, and eliminate this one use of _rsvg_css_hand_normalize_length().

Now, RsvgGradientStop has an is_valid field that says whether the stop
has valid units.  At rendering time, we can decide what to do about
gradients with invalid stops:  currently we don't add invalid stops to
the gradient, but we still render it up to the last valid stop -
hopefully to make it easier to debug SVG files.

Also, we now ensure that stop offsets are actually in nondecreasing
order, per the spec.

(I really want to get rid of _rsvg_css_hand_normalize_length(), but
found these things along the way...)
parent 38927a65
......@@ -41,14 +41,25 @@
#include <pango/pangocairo.h>
static void
_pattern_add_rsvg_color_stops (cairo_pattern_t * pattern,
GPtrArray * stops, guint8 opacity)
/* Adds the color stops from the array of child nodes (of type RSVG_NODE_TYPE_STOP)
* to a cairo_pattern_t.
*
* Returns true if the stops are all valid and conform to the SVG spec; false
* otherwise. It is up to the caller to decide whether to render the gradient
* in this last case: it will have color stops added only up to the first invalid
* stop.
*/
static gboolean
add_color_stops_for_gradient (cairo_pattern_t * pattern,
GPtrArray * stops, guint8 opacity)
{
gsize i;
RsvgGradientStop *stop;
RsvgNode *node;
guint32 rgba;
double last_offset;
last_offset = 0.0;
for (i = 0; i < stops->len; i++) {
node = (RsvgNode *) g_ptr_array_index (stops, i);
......@@ -56,6 +67,15 @@ _pattern_add_rsvg_color_stops (cairo_pattern_t * pattern,
continue;
stop = (RsvgGradientStop *) node;
if (!stop->is_valid)
return FALSE;
if (stop->offset < last_offset)
return FALSE;
last_offset = stop->offset;
rgba = stop->rgba;
cairo_pattern_add_color_stop_rgba (pattern, stop->offset,
((rgba >> 24) & 0xff) / 255.0,
......@@ -63,6 +83,8 @@ _pattern_add_rsvg_color_stops (cairo_pattern_t * pattern,
((rgba >> 8) & 0xff) / 255.0,
(((rgba >> 0) & 0xff) * opacity) / 255.0 / 255.0);
}
return TRUE;
}
static void
......@@ -100,7 +122,11 @@ _set_source_rsvg_linear_gradient (RsvgDrawingCtx * ctx,
cairo_pattern_set_matrix (pattern, &matrix);
cairo_pattern_set_extend (pattern, linear->spread);
_pattern_add_rsvg_color_stops (pattern, linear->super.children, opacity);
/* We ignore the return value of add_color_stops_for_gradient(), which is
* whether the stops are conformant to the spec. This is so that we can
* render a partially-generated gradient if some of the stops are invalid.
*/
add_color_stops_for_gradient (pattern, linear->super.children, opacity);
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
......@@ -143,7 +169,11 @@ _set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
cairo_pattern_set_matrix (pattern, &matrix);
cairo_pattern_set_extend (pattern, radial->spread);
_pattern_add_rsvg_color_stops (pattern, radial->super.children, opacity);
/* We ignore the return value of add_color_stops_for_gradient(), which is
* whether the stops are conformant to the spec. This is so that we can
* render a partially-generated gradient if some of the stops are invalid.
*/
add_color_stops_for_gradient (pattern, radial->super.children, opacity);
cairo_set_source (cr, pattern);
cairo_pattern_destroy (pattern);
......
......@@ -149,7 +149,6 @@ rsvg_paint_server_unref (RsvgPaintServer * ps)
static void
rsvg_stop_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
{
double offset = 0;
gboolean is_current_color = FALSE;
const char *value;
RsvgGradientStop *stop;
......@@ -161,13 +160,23 @@ rsvg_stop_set_atts (RsvgNode * self, RsvgHandle * ctx, RsvgPropertyBag * atts)
if ((value = rsvg_property_bag_lookup (atts, "offset"))) {
/* either a number [0,1] or a percentage */
RsvgLength length = rsvg_length_parse (value, LENGTH_DIR_BOTH);
offset = _rsvg_css_hand_normalize_length (&length, rsvg_dpi_percentage (ctx), 1., 0.);
if (offset < 0.)
offset = 0.;
else if (offset > 1.)
offset = 1.;
stop->offset = offset;
if (length.unit == LENGTH_UNIT_DEFAULT || length.unit == LENGTH_UNIT_PERCENT) {
double offset;
offset = length.length;
if (offset < 0.0)
offset = 0.0;
else if (offset > 1.0)
offset = 1.0;
stop->offset = offset;
stop->is_valid = TRUE;
} else {
/* Only default and percent values are allowed */
stop->is_valid = FALSE;
}
}
if ((value = rsvg_property_bag_lookup (atts, "style")))
rsvg_parse_style (ctx, self->state, value);
......@@ -198,6 +207,7 @@ rsvg_new_stop (void)
stop->super.set_atts = rsvg_stop_set_atts;
stop->offset = 0;
stop->rgba = 0;
stop->is_valid = FALSE;
return &stop->super;
}
......
......@@ -48,6 +48,7 @@ struct _RsvgGradientStop {
RsvgNode super;
double offset;
guint32 rgba;
gboolean is_valid;
};
struct _RsvgLinearGradient {
......
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