[librsvg/rustification] Radial gradients: fix the focus point so it lies within the gradient's circle
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg/rustification] Radial gradients: fix the focus point so it lies within the gradient's circle
- Date: Fri, 11 Nov 2016 23:19:59 +0000 (UTC)
commit ac4ca7e2edef0973620ff55fd57282cc45662c7f
Author: Federico Mena Quintero <federico gnome org>
Date: Fri Nov 11 13:23:47 2016 -0600
Radial gradients: fix the focus point so it lies within the gradient's circle
An SVG radial gradient is from a focus point (fx, fy) towards a
circle (cx, cy, r). The focus point is assumed to be inside the circle.
If the focus point is specified to be outside the circle, we need to
find the closest point to it in the circumference, i.e. the point in the
circumference that intersects the line that passes through (cx, cy)
and (fx, fy).
This computation makes us pass the reference test - regenerated the
corresponding image.
rsvg-cairo-draw.c | 80 ++++++++++++++++++-
.../reftests/svg1.1/pservers-grad-13-b-ref.png | Bin 98222 -> 93578 bytes
2 files changed, 75 insertions(+), 5 deletions(-)
---
diff --git a/rsvg-cairo-draw.c b/rsvg-cairo-draw.c
index 7ca45da..40294b0 100644
--- a/rsvg-cairo-draw.c
+++ b/rsvg-cairo-draw.c
@@ -132,6 +132,65 @@ _set_source_rsvg_linear_gradient (RsvgDrawingCtx * ctx,
cairo_pattern_destroy (pattern);
}
+/* SVG defines radial gradients as being inside a circle (cx, cy, radius). The
+ * gradient projects out from a focus point (fx, fy), which is assumed to be
+ * inside the circle, to the edge of the circle.
+ *
+ * The description of https://www.w3.org/TR/SVG/pservers.html#RadialGradientElement
+ * states:
+ *
+ * If the point defined by ‘fx’ and ‘fy’ lies outside the circle defined by
+ * ‘cx’, ‘cy’ and ‘r’, then the user agent shall set the focal point to the
+ * intersection of the line from (‘cx’, ‘cy’) to (‘fx’, ‘fy’) with the circle
+ * defined by ‘cx’, ‘cy’ and ‘r’.
+ *
+ * So, let's do that!
+ */
+static void
+fix_focus_point (double fx, double fy, double cx, double cy, double radius,
+ double *out_fx, double *out_fy)
+{
+ double vx, vy;
+ double mag, scale;
+
+ /* Easy case first: the focus point is inside the circle */
+
+ if ((fx - cx) * (fx - cx) + (fy - cy) * (fy - cy) <= radius * radius) {
+ *out_fx = fx;
+ *out_fy = fy;
+ return;
+ }
+
+ /* Hard case: focus point is outside the circle.
+ *
+ * First, translate everything to the origin.
+ */
+
+ fx -= cx;
+ fy -= cy;
+
+ /* Find the vector from the origin to (fx, fy) */
+
+ vx = fx;
+ vy = fy;
+
+ /* Find the vector's magnitude */
+
+ mag = sqrt (vx * vx + vy * vy);
+
+ /* Normalize the vector to have a magnitude equal to radius; (vx, vy) will now be on the edge of the
circle */
+
+ scale = mag / radius;
+
+ vx /= scale;
+ vy /= scale;
+
+ /* Translate back to (cx, cy) and we are done! */
+
+ *out_fx = vx + cx;
+ *out_fy = vy + cy;
+}
+
static void
_set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
RsvgRadialGradient * radial,
@@ -142,6 +201,10 @@ _set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
cairo_pattern_t *pattern;
cairo_matrix_t matrix;
RsvgRadialGradient statradial;
+ double fx, fy;
+ double cx, cy, radius;
+ double new_fx, new_fy;
+
statradial = *radial;
radial = &statradial;
rsvg_radial_gradient_fix_fallback (ctx, radial);
@@ -149,11 +212,18 @@ _set_source_rsvg_radial_gradient (RsvgDrawingCtx * ctx,
if (radial->obj_bbox)
_rsvg_push_view_box (ctx, 1., 1.);
- pattern = cairo_pattern_create_radial (_rsvg_css_normalize_length (&radial->fx, ctx),
- _rsvg_css_normalize_length (&radial->fy, ctx), 0.0,
- _rsvg_css_normalize_length (&radial->cx, ctx),
- _rsvg_css_normalize_length (&radial->cy, ctx),
- _rsvg_css_normalize_length (&radial->r, ctx));
+ fx = _rsvg_css_normalize_length (&radial->fx, ctx);
+ fy = _rsvg_css_normalize_length (&radial->fy, ctx);
+
+ cx = _rsvg_css_normalize_length (&radial->cx, ctx);
+ cy = _rsvg_css_normalize_length (&radial->cy, ctx);
+ radius = _rsvg_css_normalize_length (&radial->r, ctx);
+
+ fix_focus_point (fx, fy, cx, cy, radius, &new_fx, &new_fy);
+
+ pattern = cairo_pattern_create_radial (new_fx, new_fy, 0.0,
+ cx, cy, radius);
+
if (radial->obj_bbox)
_rsvg_pop_view_box (ctx);
diff --git a/tests/fixtures/reftests/svg1.1/pservers-grad-13-b-ref.png
b/tests/fixtures/reftests/svg1.1/pservers-grad-13-b-ref.png
index c24aea4..7383625 100644
Binary files a/tests/fixtures/reftests/svg1.1/pservers-grad-13-b-ref.png and
b/tests/fixtures/reftests/svg1.1/pservers-grad-13-b-ref.png differ
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]