[cairo-commit] 6 commits - pixman/src src/cairo-pattern.c src/cairo-pdf-surface.c src/cairo-svg-surface.c test/.gitignore test/Makefile.am test/radial-gradient.c test/radial-gradient-ref.png

Carl Worth cworth at kemper.freedesktop.org
Thu Mar 1 17:09:37 PST 2007


 pixman/src/fbcompose.c       |  213 ++++++++++++++++++++++++++++++++-----------
 pixman/src/icimage.c         |   22 +---
 pixman/src/icimage.h         |   13 +-
 pixman/src/icint.h           |    1 
 pixman/src/pixman.h          |    4 
 src/cairo-pattern.c          |   36 +++----
 src/cairo-pdf-surface.c      |   12 +-
 src/cairo-svg-surface.c      |   43 ++++++--
 test/.gitignore              |    1 
 test/Makefile.am             |    1 
 test/radial-gradient-ref.png |binary
 test/radial-gradient.c       |  110 ++++++++++++++++++++++
 12 files changed, 346 insertions(+), 110 deletions(-)

New commits:
diff-tree df2d42ac7fb71997abd406fb5716c0bd85037c04 (from parents)
Merge: ec11ca33a7ebcc752218e63c57f861de6e23a86a 97dbbae62182c2e40f5f98278e08c92e14b92731
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Mar 1 17:08:03 2007 -0800

    Merge branch 'radial-gradient-fixes' into cairo

diff-tree 97dbbae62182c2e40f5f98278e08c92e14b92731 (from fe68e8aa32cc5a9f4d9f9302644ca66d8b691d65)
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Mar 1 15:17:19 2007 -0800

    SVG: Support CAIRO_EXTEND_NONE for radial gradients.
    
    This is as simple as adding transparent color stops on either end.

diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index fefe2d4..b9c9f60 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -1280,11 +1280,14 @@ emit_radial_pattern (cairo_svg_surface_t
 
     /* SVG doesn't have a start radius, so computing now SVG focal coordinates
      * and emulating start radius by translating color stops.
-     * FIXME: We also need to emulate cairo behaviour inside start circle when
-     * extend != CAIRO_EXTEND_NONE.
      * FIXME: Handle radius1 <= radius0 */
-    fx = (r1 * x0 - r0 * x1) / (r1 - r0);
-    fy = (r1 * y0 - r0 * y1) / (r1 - r0);
+    if (pattern->base.base.extend == CAIRO_EXTEND_NONE) {
+	fx = x0;
+	fy = y0;
+    } else {
+	fx = (r1 * x0 - r0 * x1) / (r1 - r0);
+	fy = (r1 * y0 - r0 * y1) / (r1 - r0);
+    }
 
     _cairo_output_stream_printf (document->xml_node_defs,
 				 "<radialGradient id=\"radial%d\" "
@@ -1300,7 +1303,27 @@ emit_radial_pattern (cairo_svg_surface_t
     cairo_matrix_invert (&p2u);
     emit_transform (document->xml_node_defs, "gradientTransform", ">\n", &p2u);
 
+    /* To support cairo's EXTEND_NONE, (for which SVG has no similar
+     * notion), we add transparent color stops on either end of the
+     * user-provided stops. */
+    if (pattern->base.base.extend == CAIRO_EXTEND_NONE) {
+	_cairo_output_stream_printf (document->xml_node_defs,
+				     "<stop offset=\"0\" style=\""
+				     "stop-color: rgb(0%%,0%%,0%%); "
+				     "stop-opacity: 0;\"/>\n");
+	if (r0 != 0.0)
+	    _cairo_output_stream_printf (document->xml_node_defs,
+					 "<stop offset=\"%f\" style=\""
+					 "stop-color: rgb(0%%,0%%,0%%); "
+					 "stop-opacity: 0;\"/>\n",
+					 r0 / r1);
+    }
     emit_pattern_stops (document->xml_node_defs, &pattern->base, r0 / r1);
+    if (pattern->base.base.extend == CAIRO_EXTEND_NONE)
+	_cairo_output_stream_printf (document->xml_node_defs,
+				     "<stop offset=\"1.0\" style=\""
+				     "stop-color: rgb(0%%,0%%,0%%); "
+				     "stop-opacity: 0;\"/>\n");
 
     _cairo_output_stream_printf (document->xml_node_defs,
 				 "</radialGradient>\n");
diff-tree fe68e8aa32cc5a9f4d9f9302644ca66d8b691d65 (from 9a5dfe1415dce9d726c93cd08c532f26512a70ee)
Author: Carl Worth <cworth at cworth.org>
Date:   Wed Feb 28 00:41:47 2007 -0800

    Fix gradient walker to not reset needlessly
    
    Previously the gradient walker was doing excessive resets, (such
    as on every pixel in constant-colored regions or outside the
    gradient with CAIRO_EXTEND_NONE). Don't do that.

diff --git a/pixman/src/fbcompose.c b/pixman/src/fbcompose.c
index 8d45ea2..233b90c 100644
--- a/pixman/src/fbcompose.c
+++ b/pixman/src/fbcompose.c
@@ -2740,12 +2740,13 @@ typedef struct
     CARD32        right_rb;
     int32_t       left_x;
     int32_t       right_x;
-    int32_t       width_x;
     int32_t       stepper;
 
     pixman_gradient_stop_t  *stops;
     int                      num_stops;
     unsigned int             spread;
+
+    int           need_reset;
 } GradientWalker;
 
 static void
@@ -2757,13 +2758,14 @@ _gradient_walker_init (GradientWalker  *
     walker->stops     = pGradient->gradient.stops;
     walker->left_x    = 0;
     walker->right_x   = 0x10000;
-    walker->width_x   = 0;  /* will force a reset */
     walker->stepper   = 0;
     walker->left_ag   = 0;
     walker->left_rb   = 0;
     walker->right_ag  = 0;
     walker->right_rb  = 0;
     walker->spread    = spread;
+
+    walker->need_reset = TRUE;
 }
 
 static void
@@ -2895,27 +2897,28 @@ _gradient_walker_reset (GradientWalker  
 
     walker->left_x   = left_x;
     walker->right_x  = right_x;
-    walker->width_x  = right_x - left_x;
     walker->left_ag  = ((left_c->alpha >> 8) << 16)   | (left_c->green >> 8);
     walker->left_rb  = ((left_c->red & 0xff00) << 8)  | (left_c->blue >> 8);
     walker->right_ag = ((right_c->alpha >> 8) << 16)  | (right_c->green >> 8);
     walker->right_rb = ((right_c->red & 0xff00) << 8) | (right_c->blue >> 8);
 
-    if ( walker->width_x == 0                      ||
+    if ( walker->left_x == walker->right_x           ||
 	 ( walker->left_ag == walker->right_ag &&
 	   walker->left_rb == walker->right_rb )   )
     {
-	walker->width_x = 1;
 	walker->stepper = 0;
     }
     else
     {
-	walker->stepper = ((1 << 24) + walker->width_x/2)/walker->width_x;
+	int32_t width = right_x - left_x;
+	walker->stepper = ((1 << 24) + width/2)/width;
     }
+
+    walker->need_reset = FALSE;
 }
 
 #define  GRADIENT_WALKER_NEED_RESET(w,x)  \
-   ( (x) < (w)->left_x || (x) - (w)->left_x >= (w)->width_x )
+   ( (w)->need_reset || (x) < (w)->left_x || (x) >= (w)->right_x)
 
 /* the following assumes that GRADIENT_WALKER_NEED_RESET(w,x) is FALSE */
 static CARD32
diff-tree 9a5dfe1415dce9d726c93cd08c532f26512a70ee (from 0439f4c4f7e7374b4f9699f664fc0e157628b190)
Author: Carl Worth <cworth at cworth.org>
Date:   Thu Mar 1 14:53:48 2007 -0800

    Fix implementation of radial gradients for inner radius != 0
    
    The previous implementation fell apart quite badly when neither radius
    value was equal to 0.0. I derived the math from scratch, (much thanks to
    Vincent Torri <vtorri at univ-evry.fr> for guiding me to a simpler derivation
    than I was doing originally), and it's working much better now without
    being any slower, (in fact, cairo-perf shows speedup of 1.05x to 1.58x on
    my laptop here).
    
    This work also provides groundwork for defining the behavior of radial
    gradients where neither circle is wholly contained within the other, (though
    we haven't done that definition yet---it will require a new test case and a
    very little bit of work on the implementation).
    
    This is a fix for the following bug report:
    
    	Radial Gradients with nonzero inner radius misplace stops
    	https://bugs.freedesktop.org/show_bug.cgi?id=7685

diff --git a/pixman/src/fbcompose.c b/pixman/src/fbcompose.c
index 7d7ac00..8d45ea2 100644
--- a/pixman/src/fbcompose.c
+++ b/pixman/src/fbcompose.c
@@ -37,6 +37,7 @@
 #define _USE_MATH_DEFINES
 #endif
 
+#include <assert.h>
 #include <math.h>
 
 #ifndef M_PI
@@ -2949,8 +2950,6 @@ _gradient_walker_pixel (GradientWalker  
     return (color | (t1 & 0xff00ff) | (t2 & 0xff00));
 }
 
-
-
 static void fbFetchSourcePict(PicturePtr pict, int x, int y, int width, CARD32 *buffer, CARD32 *mask, CARD32 maskBits)
 {
     SourcePictPtr   pGradient = pict->pSourcePict;
@@ -3082,13 +3081,128 @@ static void fbFetchSourcePict(PicturePtr
 	    }
         }
     } else {
+
+/*
+ * In the radial gradient problem we are given two circles (c₁,r₁) and
+ * (câ‚‚,râ‚‚) that define the gradient itself. Then, for any point p, we
+ * must compute the value(s) of t within [0.0, 1.0] representing the
+ * circle(s) that would color the point.
+ *
+ * There are potentially two values of t since the point p can be
+ * colored by both sides of the circle, (which happens whenever one
+ * circle is not entirely contained within the other).
+ *
+ * If we solve for a value of t that is outside of [0.0, 1.0] then we
+ * use the extend mode (NONE, REPEAT, REFLECT, or PAD) to map to a
+ * value within [0.0, 1.0].
+ *
+ * Here is an illustration of the problem:
+ *
+ *              pâ‚‚
+ *           p  •
+ *           •   ╲
+ *        ·       ╲r₂
+ *  p₁ ·           ╲
+ *  •              θ╲
+ *   ╲             ╌╌•
+ *    ╲r₁        ·   c₂
+ *    θ╲    ·
+ *    ╌╌•
+ *      c₁
+ *
+ * Given (c₁,r₁), (c₂,r₂) and p, we must find an angle θ such that two
+ * points p₁ and p₂ on the two circles are collinear with p. Then, the
+ * desired value of t is the ratio of the length of p₁p to the length
+ * of p₁p₂.
+ *
+ * So, we have six unknown values: (p₁x, p₁y), (p₂x, p₂y), θ and t.
+ * We can also write six equations that constrain the problem:
+ *
+ * Point p₁ is a distance r₁ from c₁ at an angle of θ:
+ *
+ *	1. p₁x = c₁x + r₁·cos θ
+ *	2. p₁y = c₁y + r₁·sin θ
+ *
+ * Point p₂ is a distance r₂ from c₂ at an angle of θ:
+ *
+ *	3. p₂x = c₂x + r2·cos θ
+ *	4. p₂y = c₂y + r2·sin θ
+ *
+ * Point p lies at a fraction t along the line segment p₁p₂:
+ *
+ *	5. px = t·p₂x + (1-t)·p₁x
+ *	6. py = t·p₂y + (1-t)·p₁y
+ *
+ * To solve, first subtitute 1-4 into 5 and 6:
+ *
+ * px = t·(c₂x + r₂·cos θ) + (1-t)·(c₁x + r₁·cos θ)
+ * py = t·(c₂y + r₂·sin θ) + (1-t)·(c₁y + r₁·sin θ)
+ *
+ * Then solve each for cos θ and sin θ expressed as a function of t:
+ *
+ * cos θ = (-(c₂x - c₁x)·t + (px - c₁x)) / ((r₂-r₁)·t + r₁)
+ * sin θ = (-(c₂y - c₁y)·t + (py - c₁y)) / ((r₂-r₁)·t + r₁)
+ *
+ * To simplify this a bit, we define new variables for several of the
+ * common terms as shown below:
+ *
+ *              pâ‚‚
+ *           p  •
+ *           •   ╲
+ *        ·  ┆    ╲r₂
+ *  p₁ ·     ┆     ╲
+ *  •     pdy┆      ╲
+ *   ╲       ┆       •c₂
+ *    ╲r₁    ┆   ·   ┆
+ *     ╲    ·┆       ┆cdy
+ *      •╌╌╌╌┴╌╌╌╌╌╌╌┘
+ *    c₁  pdx   cdx
+ *
+ * cdx = (c₂x - c₁x)
+ * cdy = (c₂y - c₁y)
+ *  dr =  r₂-r₁
+ * pdx =  px - c₁x
+ * pdy =  py - c₁y
+ *
+ * Note that cdx, cdy, and dr do not depend on point p at all, so can
+ * be pre-computed for the entire gradient. The simplifed equations
+ * are now:
+ *
+ * cos θ = (-cdx·t + pdx) / (dr·t + r₁)
+ * sin θ = (-cdy·t + pdy) / (dr·t + r₁)
+ *
+ * Finally, to get a single function of t and eliminate the last
+ * unknown θ, we use the identity sin²θ + cos²θ = 1. First, square
+ * each equation, (we knew a quadratic was coming since it must be
+ * possible to obtain two solutions in some cases):
+ *
+ * cos²θ = (cdx²t² - 2·cdx·pdx·t + pdx²) / (dr²·t² + 2·r₁·dr·t + r₁²)
+ * sin²θ = (cdy²t² - 2·cdy·pdy·t + pdy²) / (dr²·t² + 2·r₁·dr·t + r₁²)
+ *
+ * Then add both together, set the result equal to 1, and express as a
+ * standard quadratic equation in t of the form At² + Bt + C = 0
+ *
+ * (cdx² + cdy² - dr²)·t² - 2·(cdx·pdx + cdy·pdy + r₁·dr)·t + (pdx² + pdy² - r₁²) = 0
+ *
+ * In other words:
+ *
+ * A = cdx² + cdy² - dr²
+ * B = -2·(pdx·cdx + pdy·cdy + r₁·dr)
+ * C = pdx² + pdy² - r₁²
+ *
+ * And again, notice that A does not depend on p, so can be
+ * precomputed. From here we just use the quadratic formula to solve
+ * for t:
+ *
+ * t = (-2·B ± ⎷(B² - 4·A·C)) / 2·A
+ */
         /* radial or conical */
         Bool projective = FALSE;
         double cx = 1.;
         double cy = 0.;
         double cz = 0.;
-        double rx = x;
-        double ry = y;
+        double rx = x + 0.5;
+        double ry = y + 0.5;
         double rz = 1.;
 
         if (pict->transform) {
@@ -3110,23 +3224,36 @@ static void fbFetchSourcePict(PicturePtr
         }
 
         if (pGradient->type == SourcePictTypeRadial) {
+	    pixman_radial_gradient_image_t *radial;
+	    radial = &pGradient->radial;
             if (!projective) {
-                rx -= pGradient->radial.fx;
-                ry -= pGradient->radial.fy;
-
                 while (buffer < end) {
-                    double b, c, det, s;
-
                     if (!mask || *mask++ & maskBits)
                     {
-                        xFixed_48_16  t;
-
-                        b = 2*(rx*pGradient->radial.dx + ry*pGradient->radial.dy);
-                        c = -(rx*rx + ry*ry);
-                        det = (b * b) - (4 * pGradient->radial.a * c);
-                        s = (-b + sqrt(det))/(2. * pGradient->radial.a);
-
-                        t = (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536);
+			double pdx, pdy;
+			double B, C;
+			double det;
+			double c1x = xFixedToDouble (radial->c1.x);
+			double c1y = xFixedToDouble (radial->c1.y);
+			double r1  = xFixedToDouble (radial->c1.radius);
+                        xFixed_48_16 t;
+
+			pdx = rx - c1x;
+			pdy = ry - c1y;
+
+			B = -2 * (  pdx * radial->cdx
+				  + pdy * radial->cdy
+				  + r1 * radial->dr);
+			C = (pdx * pdx + pdy * pdy - r1 * r1);
+
+                        det = (B * B) - (4 * radial->A * C);
+			if (det < 0.0)
+			    det = 0.0;
+
+			if (radial->A < 0)
+			    t = (xFixed_48_16) ((- B - sqrt(det)) / (2.0 * radial->A) * 65536);
+			else
+			    t = (xFixed_48_16) ((- B + sqrt(det)) / (2.0 * radial->A) * 65536);
 
                         *buffer = _gradient_walker_pixel (&walker, t);
                     }
@@ -3135,35 +3262,12 @@ static void fbFetchSourcePict(PicturePtr
                     ry += cy;
                 }
             } else {
-                while (buffer < end) {
-                    double x, y;
-                    double b, c, det, s;
-
-                    if (!mask || *mask++ & maskBits)
-                    {
-                        xFixed_48_16  t;
-
-                        if (rz != 0) {
-                            x = rx/rz;
-                            y = ry/rz;
-                        } else {
-                            x = y = 0.;
-                        }
-                        x -= pGradient->radial.fx;
-                        y -= pGradient->radial.fy;
-                        b = 2*(x*pGradient->radial.dx + y*pGradient->radial.dy);
-                        c = -(x*x + y*y);
-                        det = (b * b) - (4 * pGradient->radial.a * c);
-                        s = (-b + sqrt(det))/(2. * pGradient->radial.a);
-                        t = (xFixed_48_16)((s*pGradient->radial.m + pGradient->radial.b)*65536);
-
-                        *buffer = _gradient_walker_pixel (&walker, t);
-                    }
-                    ++buffer;
-                    rx += cx;
-                    ry += cy;
-                    rz += cz;
-                }
+		/* In cairo, we don't have projective transformed
+		 * radial gradients---so I'm not going to bother
+		 * implementing something untested and broken
+		 * here. Instead, someone trying to get this code into
+		 * shape for use in the X server can fix this here. */
+		assert (0);
             }
         } else /* SourcePictTypeConical */ {
             double a = pGradient->conical.angle/(180.*65536);
diff --git a/pixman/src/icimage.c b/pixman/src/icimage.c
index e97b4c8..4d957a0 100644
--- a/pixman/src/icimage.c
+++ b/pixman/src/icimage.c
@@ -247,7 +247,6 @@ pixman_image_create_radial_gradient (con
 {
     pixman_radial_gradient_image_t *radial;
     pixman_image_t		   *image;
-    double			   x;
 
     if (n_stops < 2)
 	return NULL;
@@ -270,19 +269,14 @@ pixman_image_create_radial_gradient (con
     memcpy (radial->stops, stops, sizeof (pixman_gradient_stop_t) * n_stops);
 
     radial->type = SourcePictTypeRadial;
-    x = (double) gradient->c1.radius / (double) gradient->c2.radius;
-    radial->dx = (gradient->c2.x - gradient->c1.x);
-    radial->dy = (gradient->c2.y - gradient->c1.y);
-    radial->fx = (gradient->c1.x) - x * radial->dx;
-    radial->fy = (gradient->c1.y) - x * radial->dy;
-    radial->m = 1. / (1 + x);
-    radial->b = -x * radial->m;
-    radial->dx /= 65536.;
-    radial->dy /= 65536.;
-    radial->fx /= 65536.;
-    radial->fy /= 65536.;
-    x = gradient->c2.radius / 65536.;
-    radial->a = x * x - radial->dx * radial->dx - radial->dy * radial->dy;
+    radial->c1 = gradient->c1;
+    radial->c2 = gradient->c2;
+    radial->cdx = xFixedToDouble (gradient->c2.x - gradient->c1.x);
+    radial->cdy = xFixedToDouble (gradient->c2.y - gradient->c1.y);
+    radial->dr = xFixedToDouble (gradient->c2.radius - gradient->c1.radius);
+    radial->A = (  radial->cdx * radial->cdx
+		 + radial->cdy * radial->cdy
+		 - radial->dr  * radial->dr);
 
     image->pSourcePict = (pixman_source_image_t *) radial;
 
diff --git a/pixman/src/icimage.h b/pixman/src/icimage.h
index 82202a7..bbf41b9 100644
--- a/pixman/src/icimage.h
+++ b/pixman/src/icimage.h
@@ -101,13 +101,12 @@ typedef struct _pixman_radial_gradient_i
     int			   stopRange;
     uint32_t		   *colorTable;
     int			   colorTableSize;
-    double		   fx;
-    double		   fy;
-    double		   dx;
-    double		   dy;
-    double		   a;
-    double		   m;
-    double		   b;
+    pixman_circle_t	   c1;
+    pixman_circle_t	   c2;
+    double		   cdx;
+    double		   cdy;
+    double		   dr;
+    double		   A;
 } pixman_radial_gradient_image_t;
 
 typedef struct _pixman_conical_gradient_image {
diff --git a/pixman/src/icint.h b/pixman/src/icint.h
index 93795b0..47a2220 100644
--- a/pixman/src/icint.h
+++ b/pixman/src/icint.h
@@ -1026,6 +1026,7 @@ typedef	xFixed_16_16	xFixed;
 #define IntToxFixed(i)	((xFixed) (i) << XFIXED_BITS)
 #define xFixedE		((xFixed) 1)
 #define xFixed1		(IntToxFixed(1))
+#define xFixedToDouble(f) (double) ((f) / (double) xFixed1)
 #define xFixed1MinusE	(xFixed1 - xFixedE)
 #define xFixedFrac(f)	((f) & xFixed1MinusE)
 #define xFixedFloor(f)	((f) & ~xFixed1MinusE)
diff-tree 0439f4c4f7e7374b4f9699f664fc0e157628b190 (from d65455ed3800f9ec3115bbed96a5b2328ee60b57)
Author: Carl Worth <cworth at cworth.org>
Date:   Sun Feb 25 16:01:10 2007 +0100

    Rename radial gradient inner/outer to c1/c2
    
    The inner/outer names were totally bogus. It is quite
    legitimate to have the first circle's radius be larger than
    that of the second.

diff --git a/pixman/src/icimage.c b/pixman/src/icimage.c
index 44c4c8b..e97b4c8 100644
--- a/pixman/src/icimage.c
+++ b/pixman/src/icimage.c
@@ -270,18 +270,18 @@ pixman_image_create_radial_gradient (con
     memcpy (radial->stops, stops, sizeof (pixman_gradient_stop_t) * n_stops);
 
     radial->type = SourcePictTypeRadial;
-    x = (double) gradient->inner.radius / (double) gradient->outer.radius;
-    radial->dx = (gradient->outer.x - gradient->inner.x);
-    radial->dy = (gradient->outer.y - gradient->inner.y);
-    radial->fx = (gradient->inner.x) - x * radial->dx;
-    radial->fy = (gradient->inner.y) - x * radial->dy;
+    x = (double) gradient->c1.radius / (double) gradient->c2.radius;
+    radial->dx = (gradient->c2.x - gradient->c1.x);
+    radial->dy = (gradient->c2.y - gradient->c1.y);
+    radial->fx = (gradient->c1.x) - x * radial->dx;
+    radial->fy = (gradient->c1.y) - x * radial->dy;
     radial->m = 1. / (1 + x);
     radial->b = -x * radial->m;
     radial->dx /= 65536.;
     radial->dy /= 65536.;
     radial->fx /= 65536.;
     radial->fy /= 65536.;
-    x = gradient->outer.radius / 65536.;
+    x = gradient->c2.radius / 65536.;
     radial->a = x * x - radial->dx * radial->dx - radial->dy * radial->dy;
 
     image->pSourcePict = (pixman_source_image_t *) radial;
diff --git a/pixman/src/pixman.h b/pixman/src/pixman.h
index 309e5e6..7f0ef32 100644
--- a/pixman/src/pixman.h
+++ b/pixman/src/pixman.h
@@ -364,8 +364,8 @@ typedef struct pixman_linear_gradient {
 } pixman_linear_gradient_t;
 
 typedef struct pixman_radial_gradient {
-    pixman_circle_t inner;
-    pixman_circle_t outer;
+    pixman_circle_t c1;
+    pixman_circle_t c2;
 } pixman_radial_gradient_t;
 
 typedef enum {
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index ae271f8..f613f0e 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -238,12 +238,12 @@ _cairo_pattern_init_radial (cairo_radial
 {
     _cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL);
 
-    pattern->gradient.inner.x	   = _cairo_fixed_from_double (cx0);
-    pattern->gradient.inner.y	   = _cairo_fixed_from_double (cy0);
-    pattern->gradient.inner.radius = _cairo_fixed_from_double (fabs (radius0));
-    pattern->gradient.outer.x	   = _cairo_fixed_from_double (cx1);
-    pattern->gradient.outer.y	   = _cairo_fixed_from_double (cy1);
-    pattern->gradient.outer.radius = _cairo_fixed_from_double (fabs (radius1));
+    pattern->gradient.c1.x	   = _cairo_fixed_from_double (cx0);
+    pattern->gradient.c1.y	   = _cairo_fixed_from_double (cy0);
+    pattern->gradient.c1.radius = _cairo_fixed_from_double (fabs (radius0));
+    pattern->gradient.c2.x	   = _cairo_fixed_from_double (cx1);
+    pattern->gradient.c2.y	   = _cairo_fixed_from_double (cy1);
+    pattern->gradient.c2.radius = _cairo_fixed_from_double (fabs (radius1));
 }
 
 cairo_pattern_t *
@@ -1771,12 +1771,12 @@ cairo_pattern_get_linear_points (cairo_p
 /**
  * cairo_pattern_get_radial_circles
  * @pattern: a #cairo_pattern_t
- * @x0: return value for the x coordinate of the center of the first (inner) circle, or %NULL
- * @y0: return value for the y coordinate of the center of the first (inner) circle, or %NULL
- * @r0: return value for the radius of the first (inner) circle, or %NULL
- * @x1: return value for the x coordinate of the center of the second (outer) circle, or %NULL
- * @y1: return value for the y coordinate of the center of the second (outer) circle, or %NULL
- * @r1: return value for the radius of the second (outer) circle, or %NULL
+ * @x0: return value for the x coordinate of the center of the first circle, or %NULL
+ * @y0: return value for the y coordinate of the center of the first circle, or %NULL
+ * @r0: return value for the radius of the first circle, or %NULL
+ * @x1: return value for the x coordinate of the center of the second circle, or %NULL
+ * @y1: return value for the y coordinate of the center of the second circle, or %NULL
+ * @r1: return value for the radius of the second circle, or %NULL
  *
  * Gets the gradient endpoint circles for a radial gradient, each
  * specified as a center coordinate and a radius.
@@ -1798,17 +1798,17 @@ cairo_pattern_get_radial_circles (cairo_
 	return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
 
     if (x0)
-	*x0 = _cairo_fixed_to_double (radial->gradient.inner.x);
+	*x0 = _cairo_fixed_to_double (radial->gradient.c1.x);
     if (y0)
-	*y0 = _cairo_fixed_to_double (radial->gradient.inner.y);
+	*y0 = _cairo_fixed_to_double (radial->gradient.c1.y);
     if (r0)
-	*r0 = _cairo_fixed_to_double (radial->gradient.inner.radius);
+	*r0 = _cairo_fixed_to_double (radial->gradient.c1.radius);
     if (x1)
-	*x1 = _cairo_fixed_to_double (radial->gradient.outer.x);
+	*x1 = _cairo_fixed_to_double (radial->gradient.c2.x);
     if (y1)
-	*y1 = _cairo_fixed_to_double (radial->gradient.outer.y);
+	*y1 = _cairo_fixed_to_double (radial->gradient.c2.y);
     if (r1)
-	*r1 = _cairo_fixed_to_double (radial->gradient.outer.radius);
+	*r1 = _cairo_fixed_to_double (radial->gradient.c2.radius);
 
     return CAIRO_STATUS_SUCCESS;
 }
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index fa6c830..104c339 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -1257,13 +1257,13 @@ emit_radial_pattern (cairo_pdf_surface_t
     p2u = pattern->base.base.matrix;
     cairo_matrix_invert (&p2u);
 
-    x0 = _cairo_fixed_to_double (pattern->gradient.inner.x);
-    y0 = _cairo_fixed_to_double (pattern->gradient.inner.y);
-    r0 = _cairo_fixed_to_double (pattern->gradient.inner.radius);
+    x0 = _cairo_fixed_to_double (pattern->gradient.c1.x);
+    y0 = _cairo_fixed_to_double (pattern->gradient.c1.y);
+    r0 = _cairo_fixed_to_double (pattern->gradient.c1.radius);
     cairo_matrix_transform_point (&p2u, &x0, &y0);
-    x1 = _cairo_fixed_to_double (pattern->gradient.outer.x);
-    y1 = _cairo_fixed_to_double (pattern->gradient.outer.y);
-    r1 = _cairo_fixed_to_double (pattern->gradient.outer.radius);
+    x1 = _cairo_fixed_to_double (pattern->gradient.c2.x);
+    y1 = _cairo_fixed_to_double (pattern->gradient.c2.y);
+    r1 = _cairo_fixed_to_double (pattern->gradient.c2.radius);
     cairo_matrix_transform_point (&p2u, &x1, &y1);
 
     /* FIXME: This is surely crack, but how should you scale a radius
diff --git a/src/cairo-svg-surface.c b/src/cairo-svg-surface.c
index 1788a99..fefe2d4 100644
--- a/src/cairo-svg-surface.c
+++ b/src/cairo-svg-surface.c
@@ -1271,12 +1271,12 @@ emit_radial_pattern (cairo_svg_surface_t
     double x0, y0, x1, y1, r0, r1;
     double fx, fy;
 
-    x0 = _cairo_fixed_to_double (pattern->gradient.inner.x);
-    y0 = _cairo_fixed_to_double (pattern->gradient.inner.y);
-    r0 = _cairo_fixed_to_double (pattern->gradient.inner.radius);
-    x1 = _cairo_fixed_to_double (pattern->gradient.outer.x);
-    y1 = _cairo_fixed_to_double (pattern->gradient.outer.y);
-    r1 = _cairo_fixed_to_double (pattern->gradient.outer.radius);
+    x0 = _cairo_fixed_to_double (pattern->gradient.c1.x);
+    y0 = _cairo_fixed_to_double (pattern->gradient.c1.y);
+    r0 = _cairo_fixed_to_double (pattern->gradient.c1.radius);
+    x1 = _cairo_fixed_to_double (pattern->gradient.c2.x);
+    y1 = _cairo_fixed_to_double (pattern->gradient.c2.y);
+    r1 = _cairo_fixed_to_double (pattern->gradient.c2.radius);
 
     /* SVG doesn't have a start radius, so computing now SVG focal coordinates
      * and emulating start radius by translating color stops.
diff-tree d65455ed3800f9ec3115bbed96a5b2328ee60b57 (from 1bc1f8ef46a27aa21f8d29b39e645ee5a0e95cda)
Author: Carl Worth <cworth at cworth.org>
Date:   Fri Feb 23 07:12:03 2007 +0100

    Add radial-gradient test case
    
    Embarrassingly enough, the test suite previously never called
    into cairo_pattern_create_radial at all. Unsurprisingly, this
    has led to bugs creeping into the radial gradient implementation.

diff --git a/test/.gitignore b/test/.gitignore
index 3bac973..0b5ae98 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -96,6 +96,7 @@ png-flatten
 ps-features
 ps-features.ps
 push-group
+radial-gradient
 random-intersections
 svg2png
 svg-clip
diff --git a/test/Makefile.am b/test/Makefile.am
index 3382cf4..9462e30 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -71,6 +71,7 @@ paint-with-alpha		\
 pattern-get-type		\
 pattern-getters			\
 pixman-rotate			\
+radial-gradient			\
 random-intersections		\
 rectangle-rounding-error	\
 rectilinear-stroke		\
diff --git a/test/radial-gradient-ref.png b/test/radial-gradient-ref.png
new file mode 100644
index 0000000..ab10858
Binary files /dev/null and b/test/radial-gradient-ref.png differ
diff --git a/test/radial-gradient.c b/test/radial-gradient.c
new file mode 100644
index 0000000..1207943
--- /dev/null
+++ b/test/radial-gradient.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2005, 2007 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ */
+
+#include "cairo-test.h"
+
+static cairo_test_draw_function_t draw;
+
+#define NUM_GRADIENTS 4
+#define NUM_EXTEND 4
+#define SIZE 60
+#define WIDTH (SIZE * NUM_GRADIENTS)
+#define HEIGHT (SIZE * NUM_EXTEND)
+
+cairo_test_t test = {
+    "radial-gradient",
+    "Simple test of radial gradients",
+    WIDTH, HEIGHT,
+    draw
+};
+
+static void
+draw_gradient (cairo_t 		*cr,
+	       int		x,
+	       int		y,
+	       int		size,
+	       double		offset,
+	       double		inner_radius,
+	       cairo_extend_t	extend)
+{
+    cairo_pattern_t *pattern;
+
+    cairo_save (cr);
+
+    pattern = cairo_pattern_create_radial (x + size/2.0 + offset,
+					   y + size/2.0 + offset, inner_radius,
+					   x + size/2.0,
+					   y + size/2.0, size/3.0);
+    cairo_pattern_add_color_stop_rgba (pattern, 0.0,
+				       1.0, 0.0, 0.0, 1.0);
+    cairo_pattern_add_color_stop_rgba (pattern, sqrt (1.0 / 2.0),
+				       0.0, 1.0, 0.0, 0.0);
+    cairo_pattern_add_color_stop_rgba (pattern, 1.0,
+				       0.0, 0.0, 1.0, 0.5);
+    cairo_pattern_set_extend (pattern, extend);
+
+    cairo_rectangle (cr, x, y, size, size);
+    cairo_clip (cr);
+
+    cairo_set_source (cr, pattern);
+    cairo_paint (cr);
+
+    cairo_pattern_destroy (pattern);
+
+    cairo_restore (cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i, j;
+    double inner_radius, offset;
+    cairo_extend_t extend[NUM_EXTEND] = {
+	CAIRO_EXTEND_NONE,
+	CAIRO_EXTEND_REPEAT,
+	CAIRO_EXTEND_REFLECT,
+	CAIRO_EXTEND_PAD
+    };
+
+    cairo_test_paint_checkered (cr);
+
+    for (j = 0; j < NUM_EXTEND; j++) {
+	for (i = 0; i < NUM_GRADIENTS; i++) {
+	    offset = i % 2 ? SIZE / 12.0 : 0.0;
+	    inner_radius = i >= NUM_EXTEND / 2 ? SIZE / 6.0 : 0.0;
+	    draw_gradient (cr, i * SIZE, j * SIZE, SIZE,
+			   offset, inner_radius, extend[j]);
+	}
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test);
+}


More information about the cairo-commit mailing list