[cairo-commit] 14 commits - src/cairoint.h src/cairo-pattern.c src/cairo-pdf-surface.c src/cairo-ps-surface.c src/cairo-quartz-surface.c src/cairo-types-private.h test/clip-operator.pdf.argb32.ref.png test/clip-operator.pdf.rgb24.ref.png test/clip-operator.quartz.argb32.ref.png test/clip-operator.quartz.rgb24.ref.png test/huge-linear.pdf.ref.png test/huge-linear.quartz.ref.png test/linear-gradient-large.quartz.ref.png test/linear-gradient-large.ref.png test/linear-gradient.quartz.ref.png test/linear-gradient-subset.quartz.ref.png test/Makefile.am test/mask.quartz.argb32.ref.png test/operator-source.quartz.argb32.ref.png test/operator-source.quartz.rgb24.ref.png test/push-group-color.ps2.ref.png test/radial-gradient-extend.ps3.ref.png test/radial-gradient-mask.pdf.argb32.xfail.png test/radial-gradient-mask.pdf.rgb24.xfail.png test/radial-gradient-mask.quartz.argb32.ref.png test/radial-gradient-mask.quartz.rgb24.ref.png test/radial-gradient-mask-source.pdf.argb32.xfail.png test/radia l-gradient-mask-source.pdf.rgb24.xfail.png test/radial-gradient-mask-source.quartz.argb32.ref.png test/radial-gradient-mask-source.quartz.rgb24.ref.png test/radial-gradient-one-stop.ps3.argb32.ref.png test/radial-gradient-one-stop.ps3.rgb24.ref.png test/radial-gradient.pdf.ref.png test/radial-gradient.pdf.xfail.png test/radial-gradient.quartz.ref.png test/radial-gradient-source.pdf.argb32.ref.png test/radial-gradient-source.pdf.argb32.xfail.png test/radial-gradient-source.pdf.rgb24.xfail.png test/radial-gradient-source.quartz.argb32.ref.png test/radial-gradient-source.quartz.rgb24.ref.png test/smask-mask.quartz.ref.png test/smask-paint.quartz.ref.png test/smask.quartz.ref.png test/trap-clip.quartz.argb32.ref.png test/trap-clip.quartz.rgb24.ref.png

Andrea Canciani ranma42 at kemper.freedesktop.org
Mon Dec 13 01:54:06 PST 2010


 dev/null                                               |binary
 src/cairo-pattern.c                                    |  702 ++++++++++++++---
 src/cairo-pdf-surface.c                                |  504 ++++--------
 src/cairo-ps-surface.c                                 |  340 +++-----
 src/cairo-quartz-surface.c                             |  400 +--------
 src/cairo-types-private.h                              |    5 
 src/cairoint.h                                         |   16 
 test/Makefile.am                                       |   22 
 test/clip-operator.pdf.argb32.ref.png                  |binary
 test/clip-operator.pdf.rgb24.ref.png                   |binary
 test/clip-operator.quartz.argb32.ref.png               |binary
 test/clip-operator.quartz.rgb24.ref.png                |binary
 test/huge-linear.pdf.ref.png                           |binary
 test/huge-linear.quartz.ref.png                        |binary
 test/linear-gradient-large.quartz.ref.png              |binary
 test/linear-gradient-large.ref.png                     |binary
 test/linear-gradient-subset.quartz.ref.png             |binary
 test/linear-gradient.quartz.ref.png                    |binary
 test/mask.quartz.argb32.ref.png                        |binary
 test/operator-source.quartz.argb32.ref.png             |binary
 test/operator-source.quartz.rgb24.ref.png              |binary
 test/push-group-color.ps2.ref.png                      |binary
 test/radial-gradient-extend.ps3.ref.png                |binary
 test/radial-gradient-mask-source.pdf.argb32.xfail.png  |binary
 test/radial-gradient-mask-source.pdf.rgb24.xfail.png   |binary
 test/radial-gradient-mask-source.quartz.argb32.ref.png |binary
 test/radial-gradient-mask-source.quartz.rgb24.ref.png  |binary
 test/radial-gradient-mask.pdf.argb32.xfail.png         |binary
 test/radial-gradient-mask.pdf.rgb24.xfail.png          |binary
 test/radial-gradient-mask.quartz.argb32.ref.png        |binary
 test/radial-gradient-mask.quartz.rgb24.ref.png         |binary
 test/radial-gradient-one-stop.ps3.argb32.ref.png       |binary
 test/radial-gradient-one-stop.ps3.rgb24.ref.png        |binary
 test/radial-gradient-source.pdf.argb32.xfail.png       |binary
 test/radial-gradient-source.pdf.rgb24.xfail.png        |binary
 test/radial-gradient-source.quartz.argb32.ref.png      |binary
 test/radial-gradient-source.quartz.rgb24.ref.png       |binary
 test/radial-gradient.pdf.xfail.png                     |binary
 test/radial-gradient.quartz.ref.png                    |binary
 test/smask-mask.quartz.ref.png                         |binary
 test/smask-paint.quartz.ref.png                        |binary
 test/smask.quartz.ref.png                              |binary
 test/trap-clip.quartz.argb32.ref.png                   |binary
 test/trap-clip.quartz.rgb24.ref.png                    |binary
 44 files changed, 1084 insertions(+), 905 deletions(-)

New commits:
commit c05135a3480a2d5c0e1d594ab0d111dac648fe4d
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Dec 12 23:40:37 2010 +0100

    test: Update ref images
    
    The recent (and not-so-recent) changes in gradient code changed the
    results of some tests involving gradients.
    
    radial-gradient-* tests are marked XFAIL for pdf because poppler is
    not sampling the color function with a sufficient frequency, but they
    look correct in Adobe Reader.

diff --git a/test/Makefile.am b/test/Makefile.am
index 9be8d63..1d7e66d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -717,6 +717,7 @@ REFERENCE_IMAGES = \
 	huge-linear.image16.ref.png \
 	huge-linear.pdf.ref.png \
 	huge-linear.ps3.ref.png \
+	huge-linear.quartz.ref.png \
 	huge-linear.ref.png \
 	huge-radial.image16.ref.png \
 	huge-radial.pdf.argb32.ref.png \
@@ -790,6 +791,7 @@ REFERENCE_IMAGES = \
 	linear-gradient.quartz.ref.png \
 	linear-gradient.ref.png \
 	linear-gradient.xlib.ref.png \
+	linear-gradient-large.quartz.ref.png \
 	linear-gradient-large.ref.png \
 	linear-gradient-one-stop.argb32.ref.png \
 	linear-gradient-one-stop.rgb24.ref.png \
@@ -1026,6 +1028,7 @@ REFERENCE_IMAGES = \
 	push-group-color.image16.ref.png \
 	push-group-color.quartz.ref.png \
 	push-group-color.ref.png \
+	push-group-color.ps2.ref.png \
 	push-group-color.ps3.ref.png \
 	push-group-color.xlib.ref.png \
 	quartz-surface-source.rgb24.ref.png \
@@ -1033,22 +1036,37 @@ REFERENCE_IMAGES = \
 	quartz-surface-source.ps2.ref.png \
 	quartz-surface-source.ps3.ref.png \
 	radial-gradient.image16.ref.png \
-	radial-gradient.pdf.ref.png \
+	radial-gradient.pdf.xfail.png \
+	radial-gradient.quartz.ref.png \
 	radial-gradient.ref.png \
 	radial-gradient.svg.xfail.png \
+	radial-gradient-extend.ps3.ref.png \
 	radial-gradient-extend.ref.png \
 	radial-gradient-source.image16.ref.png \
 	radial-gradient-source.argb32.ref.png \
 	radial-gradient-source.rgb24.ref.png \
-	radial-gradient-source.pdf.argb32.ref.png \
+	radial-gradient-source.pdf.argb32.xfail.png \
+	radial-gradient-source.pdf.rgb24.xfail.png \
+	radial-gradient-source.quartz.argb32.ref.png \
+	radial-gradient-source.quartz.rgb24.ref.png \
 	radial-gradient-mask.argb32.ref.png \
 	radial-gradient-mask.rgb24.ref.png \
 	radial-gradient-mask.image16.ref.png \
+	radial-gradient-mask.pdf.argb32.xfail.png \
+	radial-gradient-mask.pdf.rgb24.xfail.png \
+	radial-gradient-mask.quartz.argb32.ref.png \
+	radial-gradient-mask.quartz.rgb24.ref.png \
 	radial-gradient-mask-source.argb32.ref.png \
 	radial-gradient-mask-source.rgb24.ref.png \
 	radial-gradient-mask-source.image16.ref.png \
+	radial-gradient-mask-source.pdf.argb32.xfail.png \
+	radial-gradient-mask-source.pdf.rgb24.xfail.png \
+	radial-gradient-mask-source.quartz.argb32.ref.png \
+	radial-gradient-mask-source.quartz.rgb24.ref.png \
 	radial-gradient-one-stop.argb32.ref.png \
 	radial-gradient-one-stop.rgb24.ref.png \
+	radial-gradient-one-stop.ps3.argb32.ref.png \
+	radial-gradient-one-stop.ps3.rgb24.ref.png \
 	random-intersections-eo.image16.ref.png \
 	random-intersections-eo.ps.ref.png \
 	random-intersections-eo.quartz.ref.png \
diff --git a/test/clip-operator.pdf.argb32.ref.png b/test/clip-operator.pdf.argb32.ref.png
index 79600af..7f8c93e 100644
Binary files a/test/clip-operator.pdf.argb32.ref.png and b/test/clip-operator.pdf.argb32.ref.png differ
diff --git a/test/clip-operator.pdf.rgb24.ref.png b/test/clip-operator.pdf.rgb24.ref.png
index 163d54c..fc4f431 100644
Binary files a/test/clip-operator.pdf.rgb24.ref.png and b/test/clip-operator.pdf.rgb24.ref.png differ
diff --git a/test/clip-operator.quartz.argb32.ref.png b/test/clip-operator.quartz.argb32.ref.png
index bf71993..53634bd 100644
Binary files a/test/clip-operator.quartz.argb32.ref.png and b/test/clip-operator.quartz.argb32.ref.png differ
diff --git a/test/clip-operator.quartz.rgb24.ref.png b/test/clip-operator.quartz.rgb24.ref.png
index 51a30b0..79562e7 100644
Binary files a/test/clip-operator.quartz.rgb24.ref.png and b/test/clip-operator.quartz.rgb24.ref.png differ
diff --git a/test/huge-linear.pdf.ref.png b/test/huge-linear.pdf.ref.png
index cf1f8fc..8313470 100644
Binary files a/test/huge-linear.pdf.ref.png and b/test/huge-linear.pdf.ref.png differ
diff --git a/test/huge-linear.quartz.ref.png b/test/huge-linear.quartz.ref.png
new file mode 100644
index 0000000..3d12f7b
Binary files /dev/null and b/test/huge-linear.quartz.ref.png differ
diff --git a/test/linear-gradient-large.quartz.ref.png b/test/linear-gradient-large.quartz.ref.png
new file mode 100644
index 0000000..68f0829
Binary files /dev/null and b/test/linear-gradient-large.quartz.ref.png differ
diff --git a/test/linear-gradient-large.ref.png b/test/linear-gradient-large.ref.png
index 753fe2a..f1f37ab 100644
Binary files a/test/linear-gradient-large.ref.png and b/test/linear-gradient-large.ref.png differ
diff --git a/test/linear-gradient-subset.quartz.ref.png b/test/linear-gradient-subset.quartz.ref.png
index 8183411..2bec9fe 100644
Binary files a/test/linear-gradient-subset.quartz.ref.png and b/test/linear-gradient-subset.quartz.ref.png differ
diff --git a/test/linear-gradient.quartz.ref.png b/test/linear-gradient.quartz.ref.png
index 47332d3..7eeabe6 100644
Binary files a/test/linear-gradient.quartz.ref.png and b/test/linear-gradient.quartz.ref.png differ
diff --git a/test/mask.quartz.argb32.ref.png b/test/mask.quartz.argb32.ref.png
index 53d034a..d7feaf9 100644
Binary files a/test/mask.quartz.argb32.ref.png and b/test/mask.quartz.argb32.ref.png differ
diff --git a/test/operator-source.quartz.argb32.ref.png b/test/operator-source.quartz.argb32.ref.png
index 4e15991..6b79166 100644
Binary files a/test/operator-source.quartz.argb32.ref.png and b/test/operator-source.quartz.argb32.ref.png differ
diff --git a/test/operator-source.quartz.rgb24.ref.png b/test/operator-source.quartz.rgb24.ref.png
index c40c242..a88ecb5 100644
Binary files a/test/operator-source.quartz.rgb24.ref.png and b/test/operator-source.quartz.rgb24.ref.png differ
diff --git a/test/push-group-color.ps2.ref.png b/test/push-group-color.ps2.ref.png
new file mode 100644
index 0000000..daf827e
Binary files /dev/null and b/test/push-group-color.ps2.ref.png differ
diff --git a/test/radial-gradient-extend.ps3.ref.png b/test/radial-gradient-extend.ps3.ref.png
new file mode 100644
index 0000000..e84041e
Binary files /dev/null and b/test/radial-gradient-extend.ps3.ref.png differ
diff --git a/test/radial-gradient-mask-source.pdf.argb32.xfail.png b/test/radial-gradient-mask-source.pdf.argb32.xfail.png
new file mode 100644
index 0000000..0cb4330
Binary files /dev/null and b/test/radial-gradient-mask-source.pdf.argb32.xfail.png differ
diff --git a/test/radial-gradient-mask-source.pdf.rgb24.xfail.png b/test/radial-gradient-mask-source.pdf.rgb24.xfail.png
new file mode 100644
index 0000000..8c096b6
Binary files /dev/null and b/test/radial-gradient-mask-source.pdf.rgb24.xfail.png differ
diff --git a/test/radial-gradient-mask-source.quartz.argb32.ref.png b/test/radial-gradient-mask-source.quartz.argb32.ref.png
new file mode 100644
index 0000000..9cb1caa
Binary files /dev/null and b/test/radial-gradient-mask-source.quartz.argb32.ref.png differ
diff --git a/test/radial-gradient-mask-source.quartz.rgb24.ref.png b/test/radial-gradient-mask-source.quartz.rgb24.ref.png
new file mode 100644
index 0000000..303033b
Binary files /dev/null and b/test/radial-gradient-mask-source.quartz.rgb24.ref.png differ
diff --git a/test/radial-gradient-mask.pdf.argb32.xfail.png b/test/radial-gradient-mask.pdf.argb32.xfail.png
new file mode 100644
index 0000000..0cb4330
Binary files /dev/null and b/test/radial-gradient-mask.pdf.argb32.xfail.png differ
diff --git a/test/radial-gradient-mask.pdf.rgb24.xfail.png b/test/radial-gradient-mask.pdf.rgb24.xfail.png
new file mode 100644
index 0000000..8c096b6
Binary files /dev/null and b/test/radial-gradient-mask.pdf.rgb24.xfail.png differ
diff --git a/test/radial-gradient-mask.quartz.argb32.ref.png b/test/radial-gradient-mask.quartz.argb32.ref.png
new file mode 100644
index 0000000..9cb1caa
Binary files /dev/null and b/test/radial-gradient-mask.quartz.argb32.ref.png differ
diff --git a/test/radial-gradient-mask.quartz.rgb24.ref.png b/test/radial-gradient-mask.quartz.rgb24.ref.png
new file mode 100644
index 0000000..303033b
Binary files /dev/null and b/test/radial-gradient-mask.quartz.rgb24.ref.png differ
diff --git a/test/radial-gradient-one-stop.ps3.argb32.ref.png b/test/radial-gradient-one-stop.ps3.argb32.ref.png
new file mode 100644
index 0000000..0d21508
Binary files /dev/null and b/test/radial-gradient-one-stop.ps3.argb32.ref.png differ
diff --git a/test/radial-gradient-one-stop.ps3.rgb24.ref.png b/test/radial-gradient-one-stop.ps3.rgb24.ref.png
new file mode 100644
index 0000000..0d965cc
Binary files /dev/null and b/test/radial-gradient-one-stop.ps3.rgb24.ref.png differ
diff --git a/test/radial-gradient-source.pdf.argb32.ref.png b/test/radial-gradient-source.pdf.argb32.ref.png
deleted file mode 100644
index 7c32d81..0000000
Binary files a/test/radial-gradient-source.pdf.argb32.ref.png and /dev/null differ
diff --git a/test/radial-gradient-source.pdf.argb32.xfail.png b/test/radial-gradient-source.pdf.argb32.xfail.png
new file mode 100644
index 0000000..e450448
Binary files /dev/null and b/test/radial-gradient-source.pdf.argb32.xfail.png differ
diff --git a/test/radial-gradient-source.pdf.rgb24.xfail.png b/test/radial-gradient-source.pdf.rgb24.xfail.png
new file mode 100644
index 0000000..fc0174d
Binary files /dev/null and b/test/radial-gradient-source.pdf.rgb24.xfail.png differ
diff --git a/test/radial-gradient-source.quartz.argb32.ref.png b/test/radial-gradient-source.quartz.argb32.ref.png
new file mode 100644
index 0000000..77caab8
Binary files /dev/null and b/test/radial-gradient-source.quartz.argb32.ref.png differ
diff --git a/test/radial-gradient-source.quartz.rgb24.ref.png b/test/radial-gradient-source.quartz.rgb24.ref.png
new file mode 100644
index 0000000..2870f5e
Binary files /dev/null and b/test/radial-gradient-source.quartz.rgb24.ref.png differ
diff --git a/test/radial-gradient.pdf.ref.png b/test/radial-gradient.pdf.ref.png
deleted file mode 100644
index 1c288b4..0000000
Binary files a/test/radial-gradient.pdf.ref.png and /dev/null differ
diff --git a/test/radial-gradient.pdf.xfail.png b/test/radial-gradient.pdf.xfail.png
new file mode 100644
index 0000000..f7e68fb
Binary files /dev/null and b/test/radial-gradient.pdf.xfail.png differ
diff --git a/test/radial-gradient.quartz.ref.png b/test/radial-gradient.quartz.ref.png
new file mode 100644
index 0000000..21b6b46
Binary files /dev/null and b/test/radial-gradient.quartz.ref.png differ
diff --git a/test/smask-mask.quartz.ref.png b/test/smask-mask.quartz.ref.png
index 97359b4..98ba299 100644
Binary files a/test/smask-mask.quartz.ref.png and b/test/smask-mask.quartz.ref.png differ
diff --git a/test/smask-paint.quartz.ref.png b/test/smask-paint.quartz.ref.png
index 1d56558..4ee25fd 100644
Binary files a/test/smask-paint.quartz.ref.png and b/test/smask-paint.quartz.ref.png differ
diff --git a/test/smask.quartz.ref.png b/test/smask.quartz.ref.png
index b8dd1af..f9ab00f 100644
Binary files a/test/smask.quartz.ref.png and b/test/smask.quartz.ref.png differ
diff --git a/test/trap-clip.quartz.argb32.ref.png b/test/trap-clip.quartz.argb32.ref.png
index 8ad99ce..c5096df 100644
Binary files a/test/trap-clip.quartz.argb32.ref.png and b/test/trap-clip.quartz.argb32.ref.png differ
diff --git a/test/trap-clip.quartz.rgb24.ref.png b/test/trap-clip.quartz.rgb24.ref.png
index 110ac6e..7c2a15e 100644
Binary files a/test/trap-clip.quartz.rgb24.ref.png and b/test/trap-clip.quartz.rgb24.ref.png differ
commit 3cbe82fe586f64dd11a0b2bc9f58619968965369
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sun Dec 12 22:29:56 2010 +0000

    ps,pdf: Deal with empty domain gradients.
    
    If all the stops of the gradient have the same offset and the
    pattern's extend mode is EXTEND_PAD, then we cannot use the stops'
    domain as the interpolation parameter range because this would produce
    a gradient with the same start and end objects.  Such ranges tickle
    bad behaviour in rasterisers.
    
    We replace the color function with an appropriate step function
    defined on [0 1].
    
    Fixes radial-gradient-one-stop for pdf and ps3.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 97394ec..e5c0cca 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -2903,7 +2903,50 @@ _cairo_pdf_surface_emit_pattern_stops (cairo_pdf_surface_t      *surface,
 	stops[n_stops-1].offset = 1.0;
     }
 
-    if (n_stops <= 2) {
+    if (stops[0].offset == stops[n_stops - 1].offset) {
+	/*
+	 * The first and the last stops have the same offset, but we
+	 * don't want a function with an empty domain, because that
+	 * would provoke underdefined behaviour from rasterisers.
+	 * This can only happen with EXTEND_PAD, because EXTEND_NONE
+	 * is optimised into a clear pattern in cairo-gstate, and
+	 * REFLECT/REPEAT are always transformed to have the first
+	 * stop at t=0 and the last stop at t=1.  Thus we want a step
+	 * function going from the first color to the last one.
+	 *
+	 * This can be accomplished by stitching three functions:
+	 *  - a constant first color function,
+	 *  - a step from the first color to the last color (with empty domain)
+	 *  - a constant last color function
+	 */
+	cairo_pdf_color_stop_t pad_stops[4];
+
+	assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+
+	pad_stops[0] = pad_stops[1] = stops[0];
+	pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
+
+	pad_stops[0].offset = 0;
+	pad_stops[3].offset = 1;
+
+        status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+                                                                 4,
+                                                                 pad_stops,
+                                                                 FALSE,
+                                                                 color_function);
+        if (unlikely (status))
+            goto BAIL;
+
+        if (emit_alpha) {
+            status = _cairo_pdf_surface_emit_stitched_colorgradient (surface,
+                                                                     4,
+                                                                     pad_stops,
+                                                                     TRUE,
+                                                                     alpha_function);
+            if (unlikely (status))
+                goto BAIL;
+        }
+    } else if (n_stops == 2) {
         /* no need for stitched function */
         status = cairo_pdf_surface_emit_rgb_linear_function (surface,
                                                              &stops[0],
@@ -3200,6 +3243,21 @@ _cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t    *surface,
 						  bounds_x1, bounds_y1,
 						  bounds_x2, bounds_y2,
 						  tolerance, domain);
+    } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
+	/*
+	 * If the first and the last stop offset are the same, then
+	 * the color function is a step function.
+	 * _cairo_ps_surface_emit_pattern_stops emits it as a stitched
+	 * function no matter how many stops the pattern has.  The
+	 * domain of the stitched function will be [0 1] in this case.
+	 *
+	 * This is done to avoid emitting degenerate gradients for
+	 * EXTEND_PAD patterns having a step color function.
+	 */
+	domain[0] = 0.0;
+	domain[1] = 1.0;
+
+	assert (pattern->base.extend == CAIRO_EXTEND_PAD);
     } else {
 	domain[0] = pattern->stops[0].offset;
 	domain[1] = pattern->stops[pattern->n_stops - 1].offset;
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 35a48eb..6296ee1 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -3112,7 +3112,8 @@ _cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t       *surface,
     }
 
     if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
-	pattern->base.extend == CAIRO_EXTEND_REFLECT) {
+	pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
 	if (stops[0].offset > COLOR_STOP_EPSILON) {
 	    if (pattern->base.extend == CAIRO_EXTEND_REFLECT)
 		memcpy (allstops, stops, sizeof (cairo_ps_color_stop_t));
@@ -3154,16 +3155,40 @@ _cairo_ps_surface_emit_pattern_stops (cairo_ps_surface_t       *surface,
 
     _cairo_output_stream_printf (surface->stream,
 				 "/CairoFunction\n");
-    if (n_stops == 1) {
-	/* work around single stop gradients */
-	_cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[0]);
+    if (stops[0].offset == stops[n_stops - 1].offset) {
+	/*
+	 * The first and the last stops have the same offset, but we
+	 * don't want a function with an empty domain, because that
+	 * would provoke underdefined behaviour from rasterisers.
+	 * This can only happen with EXTEND_PAD, because EXTEND_NONE
+	 * is optimised into a clear pattern in cairo-gstate, and
+	 * REFLECT/REPEAT are always transformed to have the first
+	 * stop at t=0 and the last stop at t=1.  Thus we want a step
+	 * function going from the first color to the last one.
+	 *
+	 * This can be accomplished by stitching three functions:
+	 *  - a constant first color function,
+	 *  - a step from the first color to the last color (with empty domain)
+	 *  - a constant last color function
+	 */
+	cairo_ps_color_stop_t pad_stops[4];
+
+	assert (pattern->base.extend == CAIRO_EXTEND_PAD);
+
+	pad_stops[0] = pad_stops[1] = stops[0];
+	pad_stops[2] = pad_stops[3] = stops[n_stops - 1];
+
+	pad_stops[0].offset = 0;
+	pad_stops[3].offset = 1;
+
+	_cairo_ps_surface_emit_stitched_colorgradient (surface, 4, pad_stops);
     } else if (n_stops == 2) {
 	/* no need for stitched function */
 	_cairo_ps_surface_emit_linear_colorgradient (surface, &stops[0], &stops[1]);
     } else {
 	/* multiple stops: stitch. XXX possible optimization: regulary spaced
 	 * stops do not require stitching. XXX */
-	_cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops,stops);
+	_cairo_ps_surface_emit_stitched_colorgradient (surface, n_stops, stops);
     }
     _cairo_output_stream_printf (surface->stream,
 				 "def\n");
@@ -3250,6 +3275,21 @@ _cairo_ps_surface_emit_gradient (cairo_ps_surface_t       *surface,
 						  bounds_x1, bounds_y1,
 						  bounds_x2, bounds_y2,
 						  tolerance, domain);
+    } else if (pattern->stops[0].offset == pattern->stops[pattern->n_stops - 1].offset) {
+	/*
+	 * If the first and the last stop offset are the same, then
+	 * the color function is a step function.
+	 * _cairo_ps_surface_emit_pattern_stops emits it as a stitched
+	 * function no matter how many stops the pattern has.  The
+	 * domain of the stitched function will be [0 1] in this case.
+	 *
+	 * This is done to avoid emitting degenerate gradients for
+	 * EXTEND_PAD patterns having a step color function.
+	 */
+	domain[0] = 0.0;
+	domain[1] = 1.0;
+
+	assert (pattern->base.extend == CAIRO_EXTEND_PAD);
     } else {
 	domain[0] = pattern->stops[0].offset;
 	domain[1] = pattern->stops[pattern->n_stops - 1].offset;
commit 36e58aea51c3de1e2621e083bb82add7f25ddfef
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Nov 19 20:16:08 2010 +0100

    ps: Unify gradient emitters and support all extend modes.
    
    To draw repeated gradients in ps, which only supports none and pad
    extended gradients, we need an appropriate reparametrization of the
    gradients that will cover the whole clip region without needing
    repeats.
    
    This commit adds support for the drawing of reflect/repeat-extended
    radial gradients through native ps patterns, using pad-extension and
    no fallbacks.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index b53157e..35a48eb 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1726,7 +1726,6 @@ _gradient_pattern_supported (cairo_ps_surface_t    *surface,
 			     const cairo_pattern_t *pattern)
 {
     double min_alpha, max_alpha;
-    cairo_extend_t extend;
 
     if (surface->ps_level == CAIRO_PS_LEVEL_2)
 	return FALSE;
@@ -1737,17 +1736,6 @@ _gradient_pattern_supported (cairo_ps_surface_t    *surface,
     if (min_alpha != max_alpha)
 	return FALSE;
 
-    extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern);
-
-    /* Radial gradients are currently only supported with EXTEND_NONE
-     * and EXTEND_PAD. */
-    if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
-	if (extend == CAIRO_EXTEND_REPEAT ||
-	    extend == CAIRO_EXTEND_REFLECT) {
-	    return FALSE;
-	}
-    }
-
     surface->ps_level_used = CAIRO_PS_LEVEL_3;
 
     return TRUE;
@@ -3219,171 +3207,111 @@ _cairo_ps_surface_emit_repeating_function (cairo_ps_surface_t       *surface,
 }
 
 static cairo_status_t
-_cairo_ps_surface_emit_linear_pattern (cairo_ps_surface_t     *surface,
-				       cairo_linear_pattern_t *pattern)
+_cairo_ps_surface_emit_gradient (cairo_ps_surface_t       *surface,
+				 cairo_gradient_pattern_t *pattern)
 {
-    double x1, y1, x2, y2;
-    double _x1, _y1, _x2, _y2;
     cairo_matrix_t pat_to_ps;
-    cairo_extend_t extend;
+    cairo_circle_double_t start, end;
+    double domain[2];
     cairo_status_t status;
-    cairo_gradient_pattern_t *gradient = &pattern->base;
-    double first_stop, last_stop;
-    int repeat_begin = 0, repeat_end = 1;
 
-    extend = cairo_pattern_get_extend (&pattern->base.base);
+    assert (pattern->n_stops != 0);
 
-    pat_to_ps = pattern->base.base.matrix;
+    status = _cairo_ps_surface_emit_pattern_stops (surface, pattern);
+    if (unlikely (status))
+	return status;
+
+    pat_to_ps = pattern->base.matrix;
     status = cairo_matrix_invert (&pat_to_ps);
     /* cairo_pattern_set_matrix ensures the matrix is invertible */
     assert (status == CAIRO_STATUS_SUCCESS);
-
     cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
-    first_stop = gradient->stops[0].offset;
-    last_stop = gradient->stops[gradient->n_stops - 1].offset;
-
-    if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT ||
-	pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
-	double dx, dy;
-	int x_rep = 0, y_rep = 0;
-
-	x1 = _cairo_fixed_to_double (pattern->p1.x);
-	y1 = _cairo_fixed_to_double (pattern->p1.y);
-	cairo_matrix_transform_point (&pat_to_ps, &x1, &y1);
-
-	x2 = _cairo_fixed_to_double (pattern->p2.x);
-	y2 = _cairo_fixed_to_double (pattern->p2.y);
-	cairo_matrix_transform_point (&pat_to_ps, &x2, &y2);
-
-	dx = fabs (x2 - x1);
-	dy = fabs (y2 - y1);
-	if (dx > 1e-6)
-	    x_rep = ceil (surface->width/dx);
-	if (dy > 1e-6)
-	    y_rep = ceil (surface->height/dy);
-
-	repeat_end = MAX (x_rep, y_rep);
-	repeat_begin = -repeat_end;
-	first_stop = repeat_begin;
-	last_stop = repeat_end;
-    }
-
-    /* PS requires the first and last stop to be the same as the line
-     * coordinates. For repeating patterns this moves the line
-     * coordinates out to the begin/end of the repeating function. For
-     * non repeating patterns this may move the line coordinates in if
-     * there are not stops at offset 0 and 1. */
-    x1 = _cairo_fixed_to_double (pattern->p1.x);
-    y1 = _cairo_fixed_to_double (pattern->p1.y);
-    x2 = _cairo_fixed_to_double (pattern->p2.x);
-    y2 = _cairo_fixed_to_double (pattern->p2.y);
-
-    _x1 = x1 + (x2 - x1)*first_stop;
-    _y1 = y1 + (y2 - y1)*first_stop;
-    _x2 = x1 + (x2 - x1)*last_stop;
-    _y2 = y1 + (y2 - y1)*last_stop;
-
-    x1 = _x1;
-    x2 = _x2;
-    y1 = _y1;
-    y2 = _y2;
-
-    /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
-     * Type 2 function is used by itself without a stitching
-     * function. Type 2 functions always have the domain [0 1] */
-    if ((pattern->base.base.extend == CAIRO_EXTEND_NONE ||
-	 pattern->base.base.extend == CAIRO_EXTEND_PAD) &&
-	gradient->n_stops == 2) {
-	first_stop = 0.0;
-	last_stop = 1.0;
-    }
-
-    status = _cairo_ps_surface_emit_pattern_stops (surface,
-						   &pattern->base);
-    if (unlikely (status))
-	return status;
 
-    if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT ||
-	pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+	pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+	double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+	double tolerance;
+
+	/* TODO: use tighter extents */
+	bounds_x1 = 0;
+	bounds_y1 = 0;
+	bounds_x2 = surface->width;
+	bounds_y2 = surface->height;
+	_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+					      &bounds_x1, &bounds_y1,
+					      &bounds_x2, &bounds_y2,
+					      NULL);
+
+	tolerance = 1. / MAX (surface->base.x_fallback_resolution,
+			      surface->base.y_fallback_resolution);
+
+	_cairo_gradient_pattern_box_to_parameter (pattern,
+						  bounds_x1, bounds_y1,
+						  bounds_x2, bounds_y2,
+						  tolerance, domain);
+    } else {
+	domain[0] = pattern->stops[0].offset;
+	domain[1] = pattern->stops[pattern->n_stops - 1].offset;
+    }
+
+    /* PS requires the first and last stop to be the same as the
+     * extreme coordinates. For repeating patterns this moves the
+     * extreme coordinates out to the begin/end of the repeating
+     * function. For non repeating patterns this may move the extreme
+     * coordinates in if there are not stops at offset 0 and 1. */
+    _cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
+    _cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
+
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+	pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+	int repeat_begin, repeat_end;
+
+	repeat_begin = floor (domain[0]);
+	repeat_end = ceil (domain[1]);
+
 	status = _cairo_ps_surface_emit_repeating_function (surface,
-							    &pattern->base,
+							    pattern,
 							    repeat_begin,
 							    repeat_end);
 	if (unlikely (status))
 	    return status;
+    } else if (pattern->n_stops <= 2) {
+	/* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
+	 * Type 2 function is used by itself without a stitching
+	 * function. Type 2 functions always have the domain [0 1] */
+	domain[0] = 0.0;
+	domain[1] = 1.0;
     }
 
-    _cairo_output_stream_printf (surface->stream,
-				 "<< /PatternType 2\n"
-				 "   /Shading\n"
-				 "   << /ShadingType 2\n"
-				 "      /ColorSpace /DeviceRGB\n"
-				 "      /Coords [ %f %f %f %f ]\n"
-                                 "      /Domain [ %f %f ]\n"
-				 "      /Function CairoFunction\n",
-				 x1, y1, x2, y2,
-				 first_stop, last_stop);
-
-    if (extend == CAIRO_EXTEND_PAD) {
+    if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
 	_cairo_output_stream_printf (surface->stream,
-                                     "      /Extend [ true true ]\n");
+				     "<< /PatternType 2\n"
+				     "   /Shading\n"
+				     "   << /ShadingType 2\n"
+				     "      /ColorSpace /DeviceRGB\n"
+				     "      /Coords [ %f %f %f %f ]\n",
+				     start.center.x, start.center.y,
+				     end.center.x, end.center.y);
     } else {
 	_cairo_output_stream_printf (surface->stream,
-                                     "      /Extend [ false false ]\n");
+				     "<< /PatternType 2\n"
+				     "   /Shading\n"
+				     "   << /ShadingType 3\n"
+				     "      /ColorSpace /DeviceRGB\n"
+				     "      /Coords [ %f %f %f %f %f %f ]\n",
+				     start.center.x, start.center.y,
+				     MAX (start.radius, 0),
+				     end.center.x, end.center.y,
+				     MAX (end.radius, 0));
     }
 
     _cairo_output_stream_printf (surface->stream,
-				 "   >>\n"
-				 ">>\n");
-    _cairo_output_stream_printf (surface->stream,
-				 "[ %f %f %f %f %f %f ]\n",
-                                 pat_to_ps.xx, pat_to_ps.yx,
-                                 pat_to_ps.xy, pat_to_ps.yy,
-                                 pat_to_ps.x0, pat_to_ps.y0);
-    _cairo_output_stream_printf (surface->stream,
-				 "makepattern setpattern\n");
-
-    return status;
-}
-
-static cairo_status_t
-_cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t     *surface,
-				       cairo_radial_pattern_t *pattern)
-{
-    double x1, y1, x2, y2, r1, r2;
-    cairo_matrix_t pat_to_ps;
-    cairo_extend_t extend;
-    cairo_status_t status;
-
-    extend = cairo_pattern_get_extend (&pattern->base.base);
-
-    pat_to_ps = pattern->base.base.matrix;
-    status = cairo_matrix_invert (&pat_to_ps);
-    /* cairo_pattern_set_matrix ensures the matrix is invertible */
-    assert (status == CAIRO_STATUS_SUCCESS);
+				 "      /Domain [ %f %f ]\n",
+				 domain[0], domain[1]);
 
-    cairo_matrix_multiply (&pat_to_ps, &pat_to_ps, &surface->cairo_to_ps);
-    x1 = _cairo_fixed_to_double (pattern->c1.x);
-    y1 = _cairo_fixed_to_double (pattern->c1.y);
-    r1 = _cairo_fixed_to_double (pattern->r1);
-    x2 = _cairo_fixed_to_double (pattern->c2.x);
-    y2 = _cairo_fixed_to_double (pattern->c2.y);
-    r2 = _cairo_fixed_to_double (pattern->r2);
-
-   status = _cairo_ps_surface_emit_pattern_stops (surface, &pattern->base);
-   if (unlikely (status))
-      return status;
-
-   _cairo_output_stream_printf (surface->stream,
-				 "<< /PatternType 2\n"
-				 "   /Shading\n"
-				 "   << /ShadingType 3\n"
-				 "      /ColorSpace /DeviceRGB\n"
-				 "      /Coords [ %f %f %f %f %f %f ]\n"
-				 "      /Function CairoFunction\n",
-				 x1, y1, r1, x2, y2, r2);
-
-    if (extend == CAIRO_EXTEND_PAD) {
+    if (pattern->base.extend != CAIRO_EXTEND_NONE) {
 	_cairo_output_stream_printf (surface->stream,
                                      "      /Extend [ true true ]\n");
     } else {
@@ -3392,16 +3320,14 @@ _cairo_ps_surface_emit_radial_pattern (cairo_ps_surface_t     *surface,
     }
 
     _cairo_output_stream_printf (surface->stream,
+				 "      /Function CairoFunction\n"
 				 "   >>\n"
-				 ">>\n");
-
-    _cairo_output_stream_printf (surface->stream,
-				 "[ %f %f %f %f %f %f ]\n",
-				 pat_to_ps.xx, pat_to_ps.yx,
+				 ">>\n"
+				 "[ %f %f %f %f %f %f ]\n"
+				 "makepattern setpattern\n",
+                                 pat_to_ps.xx, pat_to_ps.yx,
                                  pat_to_ps.xy, pat_to_ps.yy,
                                  pat_to_ps.x0, pat_to_ps.y0);
-    _cairo_output_stream_printf (surface->stream,
-				 "makepattern setpattern\n");
 
     return status;
 }
@@ -3458,15 +3384,9 @@ _cairo_ps_surface_emit_pattern (cairo_ps_surface_t *surface,
 	break;
 
     case CAIRO_PATTERN_TYPE_LINEAR:
-	status = _cairo_ps_surface_emit_linear_pattern (surface,
-					  (cairo_linear_pattern_t *) pattern);
-	if (unlikely (status))
-	    return status;
-	break;
-
     case CAIRO_PATTERN_TYPE_RADIAL:
-	status = _cairo_ps_surface_emit_radial_pattern (surface,
-					  (cairo_radial_pattern_t *) pattern);
+	status = _cairo_ps_surface_emit_gradient (surface,
+					  (cairo_gradient_pattern_t *) pattern);
 	if (unlikely (status))
 	    return status;
 	break;
commit 20ef062511e224ef098671c923a4cd4d461139c2
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Nov 19 20:16:16 2010 +0100

    pdf: Unify gradient emitters and support all extend modes.
    
    To draw repeated gradients in pdf, which only supports none and pad
    extended gradients, we need an appropriate reparametrization of the
    gradients that will cover the whole clip region without needing
    repeats.
    
    This commit adds support for the drawing of reflect/repeat-extended
    radial gradients through native pdf patterns using pad-extension and
    no fallbacks.
    
    This fixes https://bugs.freedesktop.org/show_bug.cgi?id=28870
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index 99e9d0c..97394ec 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -3089,139 +3089,53 @@ cairo_pdf_surface_emit_transparency_group (cairo_pdf_surface_t  *surface,
     return _cairo_output_stream_get_status (surface->output);
 }
 
-static cairo_status_t
-_cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t    *surface,
-					cairo_pdf_pattern_t    *pdf_pattern)
+static void
+_cairo_pdf_surface_output_gradient (cairo_pdf_surface_t        *surface,
+				    const cairo_pdf_pattern_t  *pdf_pattern,
+				    cairo_pdf_resource_t        pattern_resource,
+				    const cairo_matrix_t       *pat_to_pdf,
+				    const cairo_circle_double_t*start,
+				    const cairo_circle_double_t*end,
+				    const double               *domain,
+				    const char                 *colorspace,
+				    cairo_pdf_resource_t        color_function)
 {
-    cairo_linear_pattern_t *pattern = (cairo_linear_pattern_t *) pdf_pattern->pattern;
-    cairo_pdf_resource_t color_function, alpha_function;
-    double x1, y1, x2, y2;
-    double _x1, _y1, _x2, _y2;
-    cairo_matrix_t pat_to_pdf;
-    cairo_extend_t extend;
-    cairo_status_t status;
-    cairo_gradient_pattern_t *gradient = &pattern->base;
-    double first_stop, last_stop;
-    int repeat_begin = 0, repeat_end = 1;
-
-    assert (pattern->base.n_stops != 0);
-
-    extend = cairo_pattern_get_extend (pdf_pattern->pattern);
-
-    pat_to_pdf = pattern->base.base.matrix;
-    status = cairo_matrix_invert (&pat_to_pdf);
-    /* cairo_pattern_set_matrix ensures the matrix is invertible */
-    assert (status == CAIRO_STATUS_SUCCESS);
-
-    cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
-    first_stop = gradient->stops[0].offset;
-    last_stop = gradient->stops[gradient->n_stops - 1].offset;
-
-    if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT ||
-	pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
-	double dx, dy;
-	int x_rep = 0, y_rep = 0;
-
-	x1 = _cairo_fixed_to_double (pattern->p1.x);
-	y1 = _cairo_fixed_to_double (pattern->p1.y);
-	cairo_matrix_transform_point (&pat_to_pdf, &x1, &y1);
-
-	x2 = _cairo_fixed_to_double (pattern->p2.x);
-	y2 = _cairo_fixed_to_double (pattern->p2.y);
-	cairo_matrix_transform_point (&pat_to_pdf, &x2, &y2);
-
-	dx = fabs (x2 - x1);
-	dy = fabs (y2 - y1);
-	if (dx > 1e-6)
-	    x_rep = ceil (surface->width/dx);
-	if (dy > 1e-6)
-	    y_rep = ceil (surface->height/dy);
-
-	repeat_end = MAX (x_rep, y_rep);
-	repeat_begin = -repeat_end;
-	first_stop = repeat_begin;
-	last_stop = repeat_end;
-    }
-
-    /* PDF requires the first and last stop to be the same as the line
-     * coordinates. For repeating patterns this moves the line
-     * coordinates out to the begin/end of the repeating function. For
-     * non repeating patterns this may move the line coordinates in if
-     * there are not stops at offset 0 and 1. */
-    x1 = _cairo_fixed_to_double (pattern->p1.x);
-    y1 = _cairo_fixed_to_double (pattern->p1.y);
-    x2 = _cairo_fixed_to_double (pattern->p2.x);
-    y2 = _cairo_fixed_to_double (pattern->p2.y);
-
-    _x1 = x1 + (x2 - x1)*first_stop;
-    _y1 = y1 + (y2 - y1)*first_stop;
-    _x2 = x1 + (x2 - x1)*last_stop;
-    _y2 = y1 + (y2 - y1)*last_stop;
-
-    x1 = _x1;
-    x2 = _x2;
-    y1 = _y1;
-    y2 = _y2;
-
-    /* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
-     * Type 2 function is used by itself without a stitching
-     * function. Type 2 functions always have the domain [0 1] */
-    if ((pattern->base.base.extend == CAIRO_EXTEND_NONE ||
-	 pattern->base.base.extend == CAIRO_EXTEND_PAD) &&
-	gradient->n_stops == 2) {
-	first_stop = 0.0;
-	last_stop = 1.0;
-    }
-
-    status = _cairo_pdf_surface_emit_pattern_stops (surface,
-                                                    &pattern->base,
-                                                    &color_function,
-                                                    &alpha_function);
-    if (unlikely (status))
-	return status;
-
-    if (pattern->base.base.extend == CAIRO_EXTEND_REPEAT ||
-	pattern->base.base.extend == CAIRO_EXTEND_REFLECT) {
-	status = _cairo_pdf_surface_emit_repeating_function (surface,
-							     &pattern->base,
-							     &color_function,
-							     repeat_begin,
-							     repeat_end);
-	if (unlikely (status))
-	    return status;
-
-	if (alpha_function.id != 0) {
-	    status = _cairo_pdf_surface_emit_repeating_function (surface,
-								 &pattern->base,
-								 &alpha_function,
-								 repeat_begin,
-								 repeat_end);
-	    if (unlikely (status))
-		return status;
-	}
-    }
-
-    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
     _cairo_output_stream_printf (surface->output,
                                  "%d 0 obj\n"
                                  "<< /Type /Pattern\n"
                                  "   /PatternType 2\n"
                                  "   /Matrix [ %f %f %f %f %f %f ]\n"
-                                 "   /Shading\n"
-                                 "      << /ShadingType 2\n"
-                                 "         /ColorSpace /DeviceRGB\n"
-                                 "         /Coords [ %f %f %f %f ]\n"
-                                 "         /Domain [ %f %f ]\n"
-                                 "         /Function %d 0 R\n",
-				 pdf_pattern->pattern_res.id,
-                                 pat_to_pdf.xx, pat_to_pdf.yx,
-                                 pat_to_pdf.xy, pat_to_pdf.yy,
-                                 pat_to_pdf.x0, pat_to_pdf.y0,
-                                 x1, y1, x2, y2,
-                                 first_stop, last_stop,
-                                 color_function.id);
-
-    if (extend == CAIRO_EXTEND_PAD) {
+                                 "   /Shading\n",
+				 pattern_resource.id,
+                                 pat_to_pdf->xx, pat_to_pdf->yx,
+                                 pat_to_pdf->xy, pat_to_pdf->yy,
+                                 pat_to_pdf->x0, pat_to_pdf->y0);
+
+    if (pdf_pattern->pattern->type == CAIRO_PATTERN_TYPE_LINEAR) {
+	_cairo_output_stream_printf (surface->output,
+				     "      << /ShadingType 2\n"
+				     "         /ColorSpace %s\n"
+				     "         /Coords [ %f %f %f %f ]\n",
+				     colorspace,
+				     start->center.x, start->center.y,
+				     end->center.x, end->center.y);
+    } else {
+	_cairo_output_stream_printf (surface->output,
+				     "      << /ShadingType 3\n"
+				     "         /ColorSpace %s\n"
+				     "         /Coords [ %f %f %f %f %f %f ]\n",
+				     colorspace,
+				     start->center.x, start->center.y,
+				     MAX (start->radius, 0),
+				     end->center.x, end->center.y,
+				     MAX (end->radius, 0));
+    }
+
+    _cairo_output_stream_printf (surface->output,
+				 "         /Domain [ %f %f ]\n",
+				 domain[0], domain[1]);
+
+    if (pdf_pattern->pattern->extend != CAIRO_EXTEND_NONE) {
         _cairo_output_stream_printf (surface->output,
                                      "         /Extend [ true true ]\n");
     } else {
@@ -3230,171 +3144,132 @@ _cairo_pdf_surface_emit_linear_pattern (cairo_pdf_surface_t    *surface,
     }
 
     _cairo_output_stream_printf (surface->output,
+				 "         /Function %d 0 R\n"
                                  "      >>\n"
                                  ">>\n"
-                                 "endobj\n");
-
-    if (alpha_function.id != 0) {
-	cairo_pdf_resource_t mask_resource;
-
-	assert (pdf_pattern->gstate_res.id != 0);
-
-        /* Create pattern for SMask. */
-        mask_resource = _cairo_pdf_surface_new_object (surface);
-	if (mask_resource.id == 0)
-	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
-        _cairo_output_stream_printf (surface->output,
-                                     "%d 0 obj\n"
-                                     "<< /Type /Pattern\n"
-                                     "   /PatternType 2\n"
-                                     "   /Matrix [ %f %f %f %f %f %f ]\n"
-                                     "   /Shading\n"
-                                     "      << /ShadingType 2\n"
-                                     "         /ColorSpace /DeviceGray\n"
-                                     "         /Coords [ %f %f %f %f ]\n"
-				     "         /Domain [ %f %f ]\n"
-                                     "         /Function %d 0 R\n",
-                                     mask_resource.id,
-                                     pat_to_pdf.xx, pat_to_pdf.yx,
-                                     pat_to_pdf.xy, pat_to_pdf.yy,
-                                     pat_to_pdf.x0, pat_to_pdf.y0,
-                                     x1, y1, x2, y2,
-				     first_stop, last_stop,
-                                     alpha_function.id);
-
-        if (extend == CAIRO_EXTEND_PAD) {
-            _cairo_output_stream_printf (surface->output,
-                                         "         /Extend [ true true ]\n");
-        } else {
-            _cairo_output_stream_printf (surface->output,
-                                         "         /Extend [ false false ]\n");
-        }
-
-        _cairo_output_stream_printf (surface->output,
-                                     "      >>\n"
-                                     ">>\n"
-                                     "endobj\n");
-        status = _cairo_pdf_surface_add_pattern (surface, mask_resource);
-        if (unlikely (status))
-            return status;
-
-	status = cairo_pdf_surface_emit_transparency_group (surface,
-						            pdf_pattern->gstate_res,
-							    mask_resource);
-	if (unlikely (status))
-	    return status;
-    }
-
-    return _cairo_output_stream_get_status (surface->output);
+                                 "endobj\n",
+				 color_function.id);
 }
 
 static cairo_status_t
-_cairo_pdf_surface_emit_radial_pattern (cairo_pdf_surface_t    *surface,
-					cairo_pdf_pattern_t    *pdf_pattern)
+_cairo_pdf_surface_emit_gradient (cairo_pdf_surface_t    *surface,
+				  cairo_pdf_pattern_t    *pdf_pattern)
 {
+    cairo_gradient_pattern_t *pattern = (cairo_gradient_pattern_t *) pdf_pattern->pattern;
     cairo_pdf_resource_t color_function, alpha_function;
-    double x1, y1, x2, y2, r1, r2;
     cairo_matrix_t pat_to_pdf;
-    cairo_extend_t extend;
+    cairo_circle_double_t start, end;
+    double domain[2];
     cairo_status_t status;
-    cairo_radial_pattern_t *pattern = (cairo_radial_pattern_t *) pdf_pattern->pattern;
 
-    assert (pattern->base.n_stops != 0);
-
-    extend = cairo_pattern_get_extend (pdf_pattern->pattern);
+    assert (pattern->n_stops != 0);
 
     status = _cairo_pdf_surface_emit_pattern_stops (surface,
-                                                    &pattern->base,
+                                                    pattern,
                                                     &color_function,
                                                     &alpha_function);
     if (unlikely (status))
 	return status;
 
-    pat_to_pdf = pattern->base.base.matrix;
+    pat_to_pdf = pattern->base.matrix;
     status = cairo_matrix_invert (&pat_to_pdf);
     /* cairo_pattern_set_matrix ensures the matrix is invertible */
     assert (status == CAIRO_STATUS_SUCCESS);
-
     cairo_matrix_multiply (&pat_to_pdf, &pat_to_pdf, &surface->cairo_to_pdf);
-    x1 = _cairo_fixed_to_double (pattern->c1.x);
-    y1 = _cairo_fixed_to_double (pattern->c1.y);
-    r1 = _cairo_fixed_to_double (pattern->r1);
-    x2 = _cairo_fixed_to_double (pattern->c2.x);
-    y2 = _cairo_fixed_to_double (pattern->c2.y);
-    r2 = _cairo_fixed_to_double (pattern->r2);
 
-    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+	pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+	double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+	double tolerance;
+
+	/* TODO: use tighter extents */
+	bounds_x1 = 0;
+	bounds_y1 = 0;
+	bounds_x2 = surface->width;
+	bounds_y2 = surface->height;
+	_cairo_matrix_transform_bounding_box (&pattern->base.matrix,
+					      &bounds_x1, &bounds_y1,
+					      &bounds_x2, &bounds_y2,
+					      NULL);
 
-    _cairo_output_stream_printf (surface->output,
-                                 "%d 0 obj\n"
-                                 "<< /Type /Pattern\n"
-                                 "   /PatternType 2\n"
-                                 "   /Matrix [ %f %f %f %f %f %f ]\n"
-                                 "   /Shading\n"
-                                 "      << /ShadingType 3\n"
-                                 "         /ColorSpace /DeviceRGB\n"
-                                 "         /Coords [ %f %f %f %f %f %f ]\n"
-                                 "         /Function %d 0 R\n",
-				 pdf_pattern->pattern_res.id,
-                                 pat_to_pdf.xx, pat_to_pdf.yx,
-                                 pat_to_pdf.xy, pat_to_pdf.yy,
-                                 pat_to_pdf.x0, pat_to_pdf.y0,
-                                 x1, y1, r1, x2, y2, r2,
-                                 color_function.id);
-
-    if (extend == CAIRO_EXTEND_PAD) {
-        _cairo_output_stream_printf (surface->output,
-                                     "         /Extend [ true true ]\n");
+	tolerance = 1. / MAX (surface->base.x_fallback_resolution,
+			      surface->base.y_fallback_resolution);
+
+	_cairo_gradient_pattern_box_to_parameter (pattern,
+						  bounds_x1, bounds_y1,
+						  bounds_x2, bounds_y2,
+						  tolerance, domain);
     } else {
-        _cairo_output_stream_printf (surface->output,
-                                     "         /Extend [ false false ]\n");
+	domain[0] = pattern->stops[0].offset;
+	domain[1] = pattern->stops[pattern->n_stops - 1].offset;
     }
 
-    _cairo_output_stream_printf (surface->output,
-                                 "      >>\n"
-                                 ">>\n"
-                                 "endobj\n");
+    /* PDF requires the first and last stop to be the same as the
+     * extreme coordinates. For repeating patterns this moves the
+     * extreme coordinates out to the begin/end of the repeating
+     * function. For non repeating patterns this may move the extreme
+     * coordinates in if there are not stops at offset 0 and 1. */
+    _cairo_gradient_pattern_interpolate (pattern, domain[0], &start);
+    _cairo_gradient_pattern_interpolate (pattern, domain[1], &end);
+
+    if (pattern->base.extend == CAIRO_EXTEND_REPEAT ||
+	pattern->base.extend == CAIRO_EXTEND_REFLECT)
+    {
+	int repeat_begin, repeat_end;
+
+	repeat_begin = floor (domain[0]);
+	repeat_end = ceil (domain[1]);
+
+	status = _cairo_pdf_surface_emit_repeating_function (surface,
+							     pattern,
+							     &color_function,
+							     repeat_begin,
+							     repeat_end);
+	if (unlikely (status))
+	    return status;
+
+	if (alpha_function.id != 0) {
+	    status = _cairo_pdf_surface_emit_repeating_function (surface,
+								 pattern,
+								 &alpha_function,
+								 repeat_begin,
+								 repeat_end);
+	    if (unlikely (status))
+		return status;
+	}
+    } else if (pattern->n_stops <= 2) {
+	/* For EXTEND_NONE and EXTEND_PAD if there are only two stops a
+	 * Type 2 function is used by itself without a stitching
+	 * function. Type 2 functions always have the domain [0 1] */
+	domain[0] = 0.0;
+	domain[1] = 1.0;
+    }
+
+    _cairo_pdf_surface_update_object (surface, pdf_pattern->pattern_res);
+    _cairo_pdf_surface_output_gradient (surface, pdf_pattern,
+					pdf_pattern->pattern_res,
+					&pat_to_pdf, &start, &end, domain,
+					"/DeviceRGB", color_function);
 
     if (alpha_function.id != 0) {
 	cairo_pdf_resource_t mask_resource;
 
 	assert (pdf_pattern->gstate_res.id != 0);
 
-	/* Create pattern for SMask. */
+        /* Create pattern for SMask. */
         mask_resource = _cairo_pdf_surface_new_object (surface);
 	if (mask_resource.id == 0)
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
 
-        _cairo_output_stream_printf (surface->output,
-                                     "%d 0 obj\n"
-                                     "<< /Type /Pattern\n"
-                                     "   /PatternType 2\n"
-                                     "   /Matrix [ %f %f %f %f %f %f ]\n"
-                                     "   /Shading\n"
-                                     "      << /ShadingType 3\n"
-                                     "         /ColorSpace /DeviceGray\n"
-                                     "         /Coords [ %f %f %f %f %f %f ]\n"
-                                     "         /Function %d 0 R\n",
-                                     mask_resource.id,
-                                     pat_to_pdf.xx, pat_to_pdf.yx,
-                                     pat_to_pdf.xy, pat_to_pdf.yy,
-                                     pat_to_pdf.x0, pat_to_pdf.y0,
-                                     x1, y1, r1, x2, y2, r2,
-                                     alpha_function.id);
-
-        if (extend == CAIRO_EXTEND_PAD) {
-            _cairo_output_stream_printf (surface->output,
-                                         "         /Extend [ true true ]\n");
-        } else {
-            _cairo_output_stream_printf (surface->output,
-                                         "         /Extend [ false false ]\n");
-        }
+	_cairo_pdf_surface_output_gradient (surface, pdf_pattern,
+					    mask_resource,
+					    &pat_to_pdf, &start, &end, domain,
+					    "/DeviceGray", alpha_function);
 
-        _cairo_output_stream_printf (surface->output,
-                                     "      >>\n"
-                                     ">>\n"
-                                     "endobj\n");
+        status = _cairo_pdf_surface_add_pattern (surface, mask_resource);
+        if (unlikely (status))
+            return status;
 
 	status = cairo_pdf_surface_emit_transparency_group (surface,
 						            pdf_pattern->gstate_res,
@@ -3429,11 +3304,8 @@ _cairo_pdf_surface_emit_pattern (cairo_pdf_surface_t *surface, cairo_pdf_pattern
 	break;
 
     case CAIRO_PATTERN_TYPE_LINEAR:
-	status = _cairo_pdf_surface_emit_linear_pattern (surface, pdf_pattern);
-	break;
-
     case CAIRO_PATTERN_TYPE_RADIAL:
-	status = _cairo_pdf_surface_emit_radial_pattern (surface, pdf_pattern);
+	status = _cairo_pdf_surface_emit_gradient (surface, pdf_pattern);
 	break;
 
     default:
@@ -5443,35 +5315,13 @@ _surface_pattern_supported (cairo_surface_pattern_t *pattern)
 }
 
 static cairo_bool_t
-_gradient_pattern_supported (const cairo_pattern_t *pattern)
-{
-    cairo_extend_t extend;
-
-    extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern);
-
-
-    /* Radial gradients are currently only supported with EXTEND_NONE
-     * and EXTEND_PAD. */
-    if (pattern->type == CAIRO_PATTERN_TYPE_RADIAL) {
-	if (extend == CAIRO_EXTEND_REPEAT ||
-	    extend == CAIRO_EXTEND_REFLECT) {
-	    return FALSE;
-	}
-    }
-
-    return TRUE;
-}
-
-static cairo_bool_t
 _pattern_supported (const cairo_pattern_t *pattern)
 {
     switch (pattern->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
-	return TRUE;
-
     case CAIRO_PATTERN_TYPE_LINEAR:
     case CAIRO_PATTERN_TYPE_RADIAL:
-	return _gradient_pattern_supported (pattern);
+	return TRUE;
 
     case CAIRO_PATTERN_TYPE_SURFACE:
 	return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
commit ca7f141dd7931041887dc96a542c2a47da25e12f
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Nov 17 22:07:09 2010 +0100

    quartz: Unify gradient construction and fix radial gradients.
    
    Share code between linear and radial gradients, using
    _cairo_gradient_pattern_box_to_parameter() instead of open coding the
    parameter range computation.
    
    As a side effect this fixes parameter range computation for radial
    gradients, because the previous code assumed that the focal point was
    inside the circles.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-quartz-surface.c b/src/cairo-quartz-surface.c
index a340865..4a22221 100644
--- a/src/cairo-quartz-surface.c
+++ b/src/cairo-quartz-surface.c
@@ -811,258 +811,45 @@ static const cairo_quartz_float_t gradient_output_value_ranges[8] = {
 static const CGFunctionCallbacks gradient_callbacks = {
     0, ComputeGradientValue, (CGFunctionReleaseInfoCallback) cairo_pattern_destroy
 };
-/* Quartz will clamp input values to the input range.
-
-   Our stops are all in the range 0.0 to 1.0. However, the color before the
-   beginning of the gradient line is obtained by Quartz computing a negative
-   position on the gradient line, clamping it to the input range we specified
-   for our color function, and then calling our color function (actually it
-   pre-samples the color function into an array, but that doesn't matter just
-   here). Therefore if we set the lower bound to 0.0, a negative position
-   on the gradient line will pass 0.0 to ComputeGradientValue, which will
-   select the last color stop with position 0, although it should select
-   the first color stop (this matters when there are multiple color stops with
-   position 0).
-
-   Therefore we pass a small negative number as the lower bound of the input
-   range, so this value gets passed into ComputeGradientValue, which will
-   return the color of the first stop. The number should be small because
-   as far as I can tell, Quartz pre-samples the entire input range of the color
-   function into an array of fixed size, so if the input range is larger
-   than needed, the resolution of the gradient will be unnecessarily low.
-*/
-static const cairo_quartz_float_t nonrepeating_gradient_input_value_range[2] = { 0., 1. };
-
-static CGFunctionRef
-CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
-{
-    cairo_pattern_t *pat;
-
-    if (_cairo_pattern_create_copy (&pat, &gpat->base))
-	/* quartz doesn't deal very well with malloc failing, so there's
-	 * not much point in us trying either */
-	return NULL;
-
-    return CGFunctionCreate (pat,
-			     1,
-			     nonrepeating_gradient_input_value_range,
-			     4,
-			     gradient_output_value_ranges,
-			     &gradient_callbacks);
-}
-
-static void
-UpdateLinearParametersToIncludePoint (double *min_t, double *max_t, CGPoint *start,
-				      double dx, double dy,
-				      double x, double y)
-{
-    /* Compute a parameter t such that a line perpendicular to the (dx,dy)
-       vector, passing through (start->x + dx*t, start->y + dy*t), also
-       passes through (x,y).
-
-       Let px = x - start->x, py = y - start->y.
-       t is given by
-         (px - dx*t)*dx + (py - dy*t)*dy = 0
-
-       Solving for t we get
-         numerator = dx*px + dy*py
-         denominator = dx^2 + dy^2
-         t = numerator/denominator
-
-       In CreateRepeatingLinearGradientFunction we know the length of (dx,dy)
-       is not zero. (This is checked in _cairo_quartz_setup_linear_source.)
-    */
-    double px = x - start->x;
-    double py = y - start->y;
-    double numerator = dx*px + dy*py;
-    double denominator = dx*dx + dy*dy;
-    double t = numerator/denominator;
-
-    if (*min_t > t)
-        *min_t = t;
-
-    if (*max_t < t)
-        *max_t = t;
-}
 
 static CGFunctionRef
-CreateRepeatingLinearGradientFunction (const cairo_gradient_pattern_t *gpat,
-				       CGPoint *start, CGPoint *end,
-				       const cairo_rectangle_int_t *extents)
+_cairo_quartz_create_gradient_function (const cairo_gradient_pattern_t *gradient,
+					const cairo_rectangle_int_t *extents,
+					cairo_circle_double_t       *start,
+					cairo_circle_double_t       *end)
 {
     cairo_pattern_t *pat;
     cairo_quartz_float_t input_value_range[2];
-    double t_min = 0.;
-    double t_max = 0.;
-    double dx = end->x - start->x;
-    double dy = end->y - start->y;
-    double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
-
-    bounds_x1 = extents->x;
-    bounds_y1 = extents->y;
-    bounds_x2 = extents->x + extents->width;
-    bounds_y2 = extents->y + extents->height;
-    _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
-                                          &bounds_x1, &bounds_y1,
-                                          &bounds_x2, &bounds_y2,
-                                          NULL);
-
-    UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
-					  bounds_x1, bounds_y1);
-    UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
-					  bounds_x2, bounds_y1);
-    UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
-					  bounds_x2, bounds_y2);
-    UpdateLinearParametersToIncludePoint (&t_min, &t_max, start, dx, dy,
-					  bounds_x1, bounds_y2);
-
-    end->x = start->x + dx*t_max;
-    end->y = start->y + dy*t_max;
-    start->x = start->x + dx*t_min;
-    start->y = start->y + dy*t_min;
-
-    // set the input range for the function -- the function knows how to
-    // map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT.
-    input_value_range[0] = t_min;
-    input_value_range[1] = t_max;
-
-    if (_cairo_pattern_create_copy (&pat, &gpat->base))
-	/* quartz doesn't deal very well with malloc failing, so there's
-	 * not much point in us trying either */
-	return NULL;
-
-    return CGFunctionCreate (pat,
-			     1,
-			     input_value_range,
-			     4,
-			     gradient_output_value_ranges,
-			     &gradient_callbacks);
-}
 
-static void
-UpdateRadialParameterToIncludePoint (double *max_t, CGPoint *center,
-				     double dr, double dx, double dy,
-				     double x, double y)
-{
-    /* Compute a parameter t such that a circle centered at
-       (center->x + dx*t, center->y + dy*t) with radius dr*t contains the
-       point (x,y).
-
-       Let px = x - center->x, py = y - center->y.
-       Parameter values for which t is on the circle are given by
-         (px - dx*t)^2 + (py - dy*t)^2 = (t*dr)^2
-
-       Solving for t using the quadratic formula, and simplifying, we get
-         numerator = dx*px + dy*py +-
-                     sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
-         denominator = dx^2 + dy^2 - dr^2
-         t = numerator/denominator
-
-       In CreateRepeatingRadialGradientFunction we know the outer circle
-       contains the inner circle. Therefore the distance between the circle
-       centers plus the radius of the inner circle is less than the radius of
-       the outer circle. (This is checked in _cairo_quartz_setup_radial_source.)
-       Therefore
-         dx^2 + dy^2 < dr^2
-       So the denominator is negative and the larger solution for t is given by
-         numerator = dx*px + dy*py -
-                     sqrt( dr^2*(px^2 + py^2) - (dx*py - dy*px)^2 )
-         denominator = dx^2 + dy^2 - dr^2
-         t = numerator/denominator
-       dx^2 + dy^2 < dr^2 also ensures that the operand of sqrt is positive.
-    */
-    double px = x - center->x;
-    double py = y - center->y;
-    double dx_py_minus_dy_px = dx*py - dy*px;
-    double numerator = dx*px + dy*py -
-        sqrt (dr*dr*(px*px + py*py) - dx_py_minus_dy_px*dx_py_minus_dy_px);
-    double denominator = dx*dx + dy*dy - dr*dr;
-    double t = numerator/denominator;
-
-    if (*max_t < t)
-        *max_t = t;
-}
-
-/* This must only be called when one of the circles properly contains the other */
-static CGFunctionRef
-CreateRepeatingRadialGradientFunction (const cairo_gradient_pattern_t *gpat,
-                                       CGPoint *start, double *start_radius,
-                                       CGPoint *end, double *end_radius,
-                                       const cairo_rectangle_int_t *extents)
-{
-    cairo_pattern_t *pat;
-    cairo_quartz_float_t input_value_range[2];
-    CGPoint *inner;
-    double *inner_radius;
-    CGPoint *outer;
-    double *outer_radius;
-    /* minimum and maximum t-parameter values that will make our gradient
-       cover the clipBox */
-    double t_min, t_max, t_temp;
-    /* outer minus inner */
-    double dr, dx, dy;
-    double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
-
-    bounds_x1 = extents->x;
-    bounds_y1 = extents->y;
-    bounds_x2 = extents->x + extents->width;
-    bounds_y2 = extents->y + extents->height;
-    _cairo_matrix_transform_bounding_box (&gpat->base.matrix,
-                                          &bounds_x1, &bounds_y1,
-                                          &bounds_x2, &bounds_y2,
-                                          NULL);
-
-    if (*start_radius < *end_radius) {
-        /* end circle contains start circle */
-        inner = start;
-        outer = end;
-        inner_radius = start_radius;
-        outer_radius = end_radius;
+    if (gradient->base.extend != CAIRO_EXTEND_NONE) {
+	double bounds_x1, bounds_x2, bounds_y1, bounds_y2;
+	double t[2];
+
+	bounds_x1 = extents->x;
+	bounds_y1 = extents->y;
+	bounds_x2 = extents->x + extents->width;
+	bounds_y2 = extents->y + extents->height;
+	_cairo_matrix_transform_bounding_box (&gradient->base.matrix,
+					      &bounds_x1, &bounds_y1,
+					      &bounds_x2, &bounds_y2,
+					      NULL);
+
+	_cairo_gradient_pattern_box_to_parameter (gradient, bounds_x1, bounds_y1,
+						  bounds_x2, bounds_y2, 1, t);
+
+	/* set the input range for the function -- the function knows how
+	   to map values outside of 0.0 .. 1.0 to the correct color */
+	input_value_range[0] = t[0];
+	input_value_range[1] = t[1];
     } else {
-        /* start circle contains end circle */
-        inner = end;
-        outer = start;
-        inner_radius = end_radius;
-        outer_radius = start_radius;
-    }
-
-    dr = *outer_radius - *inner_radius;
-    dx = outer->x - inner->x;
-    dy = outer->y - inner->y;
-
-    /* We can't round or fudge t_min here, it has to be as accurate as possible. */
-    t_min = -(*inner_radius/dr);
-    inner->x += t_min*dx;
-    inner->y += t_min*dy;
-    *inner_radius = 0.;
-
-    t_temp = 0.;
-    UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
-					 bounds_x1, bounds_y1);
-    UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
-					 bounds_x2, bounds_y1);
-    UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
-					 bounds_x2, bounds_y2);
-    UpdateRadialParameterToIncludePoint (&t_temp, inner, dr, dx, dy,
-					 bounds_x1, bounds_y2);
-    /* UpdateRadialParameterToIncludePoint assumes t=0 means radius 0.
-       But for the parameter values we use with Quartz, t_min means radius 0. */
-    t_max = t_min + t_temp;
-    outer->x = inner->x + t_temp*dx;
-    outer->y = inner->y + t_temp*dy;
-    *outer_radius = t_temp*dr;
-
-    /* set the input range for the function -- the function knows how to
-       map values outside of 0.0 .. 1.0 to that range for REPEAT/REFLECT. */
-    if (*start_radius < *end_radius) {
-        input_value_range[0] = t_min;
-        input_value_range[1] = t_max;
-    } else {
-        input_value_range[0] = 1 - t_max;
-        input_value_range[1] = 1 - t_min;
+	input_value_range[0] = 0;
+	input_value_range[1] = 1;
     }
 
-    if (_cairo_pattern_create_copy (&pat, &gpat->base))
+    _cairo_gradient_pattern_interpolate (gradient, input_value_range[0], start);
+    _cairo_gradient_pattern_interpolate (gradient, input_value_range[1], end);
+
+    if (_cairo_pattern_create_copy (&pat, &gradient->base))
 	/* quartz doesn't deal very well with malloc failing, so there's
 	 * not much point in us trying either */
 	return NULL;
@@ -1324,100 +1111,52 @@ minimize the number of repetitions since Quartz seems to sample our color
 function across the entire range, even if part of that range is not needed
 for the visible area of the gradient, and it samples with some fixed resolution,
 so if the gradient range is too large it samples with very low resolution and
-the gradient is very coarse. CreateRepeatingLinearGradientFunction and
-CreateRepeatingRadialGradientFunction compute the number of repetitions needed
-based on the extents of the object (the clip region cannot be used here since
-we don't want the rasterization of the entire gradient to depend on the
-clip region).
+the gradient is very coarse. _cairo_quartz_create_gradient_function computes
+the number of repetitions needed based on the extents.
 */
 static cairo_int_status_t
-_cairo_quartz_setup_linear_source (cairo_quartz_drawing_state_t *state,
-				   const cairo_linear_pattern_t *lpat,
-				   const cairo_rectangle_int_t *extents)
+_cairo_quartz_setup_gradient_source (cairo_quartz_drawing_state_t *state,
+				     const cairo_gradient_pattern_t *gradient,
+				     const cairo_rectangle_int_t *extents)
 {
-    const cairo_pattern_t *abspat = &lpat->base.base;
     cairo_matrix_t mat;
-    CGPoint start, end;
+    cairo_circle_double_t start, end;
     CGFunctionRef gradFunc;
     CGColorSpaceRef rgb;
-    bool extend = abspat->extend != CAIRO_EXTEND_NONE;
+    bool extend = gradient->base.extend != CAIRO_EXTEND_NONE;
 
-    assert (lpat->base.n_stops > 0);
+    assert (gradient->n_stops > 0);
 
-    mat = abspat->matrix;
+    mat = gradient->base.matrix;
     cairo_matrix_invert (&mat);
     _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
 
     rgb = CGColorSpaceCreateDeviceRGB ();
 
-    start = CGPointMake (_cairo_fixed_to_double (lpat->p1.x),
-			 _cairo_fixed_to_double (lpat->p1.y));
-    end = CGPointMake (_cairo_fixed_to_double (lpat->p2.x),
-		       _cairo_fixed_to_double (lpat->p2.y));
-
-    if (!extend)
-	gradFunc = CreateGradientFunction (&lpat->base);
-    else
-	gradFunc = CreateRepeatingLinearGradientFunction (&lpat->base,
-						          &start, &end,
-						          extents);
-
-    state->shading = CGShadingCreateAxial (rgb,
-					   start, end,
-					   gradFunc,
-					   extend, extend);
-
-    CGColorSpaceRelease (rgb);
-    CGFunctionRelease (gradFunc);
-
-    state->action = DO_SHADING;
-    return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_int_status_t
-_cairo_quartz_setup_radial_source (cairo_quartz_drawing_state_t *state,
-				   const cairo_radial_pattern_t *rpat,
-				   const cairo_rectangle_int_t *extents)
-{
-    const cairo_pattern_t *abspat = &rpat->base.base;
-    cairo_matrix_t mat;
-    CGPoint start, end;
-    CGFunctionRef gradFunc;
-    CGColorSpaceRef rgb;
-    bool extend = abspat->extend != CAIRO_EXTEND_NONE;
-    double c1x = _cairo_fixed_to_double (rpat->c1.x);
-    double c1y = _cairo_fixed_to_double (rpat->c1.y);
-    double c2x = _cairo_fixed_to_double (rpat->c2.x);
-    double c2y = _cairo_fixed_to_double (rpat->c2.y);
-    double r1 = _cairo_fixed_to_double (rpat->r1);
-    double r2 = _cairo_fixed_to_double (rpat->r2);
-
-    assert (rpat->base.n_stops > 0);
-
-    mat = abspat->matrix;
-    cairo_matrix_invert (&mat);
-    _cairo_quartz_cairo_matrix_to_quartz (&mat, &state->transform);
-
-    rgb = CGColorSpaceCreateDeviceRGB ();
-
-    start = CGPointMake (c1x, c1y);
-    end = CGPointMake (c2x, c2y);
-
-    if (!extend)
-	gradFunc = CreateGradientFunction (&rpat->base);
-    else
-	gradFunc = CreateRepeatingRadialGradientFunction (&rpat->base,
-						          &start, &r1,
-						          &end, &r2,
-						          extents);
-
-    state->shading = CGShadingCreateRadial (rgb,
-					    start,
-					    r1,
-					    end,
-					    r2,
-					    gradFunc,
-					    extend, extend);
+    gradFunc = _cairo_quartz_create_gradient_function (gradient,
+						       extents,
+						       &start,
+						       &end);
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+	state->shading = CGShadingCreateAxial (rgb,
+					       CGPointMake (start.center.x,
+							    start.center.y),
+					       CGPointMake (end.center.x,
+							    end.center.y),
+					       gradFunc,
+					       extend, extend);
+    } else {
+	state->shading = CGShadingCreateRadial (rgb,
+						CGPointMake (start.center.x,
+							     start.center.y),
+						MAX (start.radius, 0),
+						CGPointMake (end.center.x,
+							     end.center.y),
+						MAX (end.radius, 0),
+						gradFunc,
+						extend, extend);
+    }
 
     CGColorSpaceRelease (rgb);
     CGFunctionRelease (gradFunc);
@@ -1469,14 +1208,11 @@ _cairo_quartz_setup_source (cairo_quartz_drawing_state_t *state,
 	return CAIRO_STATUS_SUCCESS;
     }
 
-    if (source->type == CAIRO_PATTERN_TYPE_LINEAR) {
-	const cairo_linear_pattern_t *lpat = (const cairo_linear_pattern_t *)source;
-	return _cairo_quartz_setup_linear_source (state, lpat, extents);
-    }
-
-    if (source->type == CAIRO_PATTERN_TYPE_RADIAL) {
-	const cairo_radial_pattern_t *rpat = (const cairo_radial_pattern_t *)source;
-	return _cairo_quartz_setup_radial_source (state, rpat, extents);
+    if (source->type == CAIRO_PATTERN_TYPE_LINEAR ||
+	source->type == CAIRO_PATTERN_TYPE_RADIAL)
+    {
+	const cairo_gradient_pattern_t *gpat = (const cairo_gradient_pattern_t *)source;
+	return _cairo_quartz_setup_gradient_source (state, gpat, extents);
     }
 
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
commit 341e5b3246f785a4791606ea62873cfb180efae6
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Tue Nov 23 17:49:50 2010 +0100

    pattern: Add a function to interpolate gradient objects.
    
    This will be a common function used by the quartz, ps, and pdf
    backends when rewriting EXTEND_REFLECT/REPEAT gradients in terms
    of EXTEND_PAD gradients.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index e783207..c67eac3 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -2203,6 +2203,38 @@ _cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradie
     }
 }
 
+/**
+ * _cairo_gradient_pattern_interpolate
+ *
+ * Interpolate between the start and end objects of linear or radial
+ * gradients.  The interpolated object is stored in out_circle, with
+ * the radius being zero in the linear gradient case.
+ **/
+void
+_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient,
+				     double			     t,
+				     cairo_circle_double_t	    *out_circle)
+{
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+	    gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+#define lerp(a,b) _cairo_fixed_to_double(a)*(1-t) + _cairo_fixed_to_double(b)*t
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+	cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+	out_circle->center.x = lerp(linear->p1.x, linear->p2.x);
+	out_circle->center.y = lerp(linear->p1.y, linear->p2.y);
+	out_circle->radius = 0;
+    } else {
+	cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+	out_circle->center.x = lerp(radial->c1.x, radial->c2.x);
+	out_circle->center.y = lerp(radial->c1.y, radial->c2.y);
+	out_circle->radius   = lerp(radial->r1, radial->r2);
+    }
+
+#undef lerp
+}
+
 static cairo_bool_t
 _gradient_is_clear (const cairo_gradient_pattern_t *gradient,
 		    const cairo_rectangle_int_t *extents)
diff --git a/src/cairo-types-private.h b/src/cairo-types-private.h
index 85f5d51..1282b3f 100644
--- a/src/cairo-types-private.h
+++ b/src/cairo-types-private.h
@@ -252,6 +252,11 @@ typedef struct _cairo_point_double {
     double y;
 } cairo_point_double_t;
 
+typedef struct _cairo_circle_double {
+    cairo_point_double_t center;
+    double               radius;
+} cairo_circle_double_t;
+
 typedef struct _cairo_distance_double {
     double dx;
     double dy;
diff --git a/src/cairoint.h b/src/cairoint.h
index 4e8ef09..db2836c 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2229,6 +2229,11 @@ _cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradie
 					  double tolerance,
 					  double out_range[2]);
 
+cairo_private void
+_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient,
+                                     double			     t,
+                                     cairo_circle_double_t	    *out_circle);
+
 cairo_private cairo_bool_t
 _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
 
commit 08cb6db5206203fda919e3d258ce7fdbb3e3c9d8
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Tue Nov 23 19:31:40 2010 +0100

    pattern: Use pattern parameter range when analysing all gradients.
    
    This patch adds support for analysing the transparency of a
    radial gradient within some area of interest.  Before the code
    would ignore the extents for radial gradients.  Linear gradients
    now use _cairo_linear_pattern_box_to_parameter() allowing us
    to remove the superfluous _extents_to_linear_parameter().
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 1ac50b6..e783207 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1728,38 +1728,6 @@ _cairo_pattern_reset_solid_surface_cache (void)
     CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
 }
 
-static void
-_extents_to_linear_parameter (const cairo_linear_pattern_t *linear,
-			      const cairo_rectangle_int_t *extents,
-			      double t[2])
-{
-    double t0, tdx, tdy;
-    double p1x, p1y, pdx, pdy, invsqnorm;
-
-    p1x = _cairo_fixed_to_double (linear->p1.x);
-    p1y = _cairo_fixed_to_double (linear->p1.y);
-    pdx = _cairo_fixed_to_double (linear->p2.x) - p1x;
-    pdy = _cairo_fixed_to_double (linear->p2.y) - p1y;
-    invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
-    pdx *= invsqnorm;
-    pdy *= invsqnorm;
-
-    t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy;
-    tdx = extents->width * pdx;
-    tdy = extents->height * pdy;
-
-    t[0] = t[1] = t0;
-    if (tdx < 0)
-	t[0] += tdx;
-    else
-	t[1] += tdx;
-
-    if (tdy < 0)
-	t[0] += tdy;
-    else
-	t[1] += tdy;
-}
-
 static cairo_bool_t
 _linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
 {
@@ -2249,27 +2217,40 @@ _gradient_is_clear (const cairo_gradient_pattern_t *gradient,
 	 gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset))
 	return TRUE;
 
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) {
+	/* degenerate radial gradients are clear */
+	if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient))
+	    return TRUE;
+    } else if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+	/* EXTEND_NONE degenerate linear gradients are clear */
+	if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient))
+	    return TRUE;
+    }
+
     /* Check if the extents intersect the drawn part of the pattern. */
-    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
-	if (gradient->base.extend == CAIRO_EXTEND_NONE) {
-	    cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
-	    /* EXTEND_NONE degenerate linear gradients are clear */
-	    if (_linear_pattern_is_degenerate (linear))
+    if (extents != NULL &&
+	(gradient->base.extend == CAIRO_EXTEND_NONE ||
+	 gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL))
+    {
+	double t[2];
+
+	_cairo_gradient_pattern_box_to_parameter (gradient,
+						  extents->x,
+						  extents->y,
+						  extents->x + extents->width,
+						  extents->y + extents->height,
+						  DBL_EPSILON,
+						  t);
+
+	if (gradient->base.extend == CAIRO_EXTEND_NONE &&
+	    (t[0] >= gradient->stops[gradient->n_stops - 1].offset ||
+	     t[1] <= gradient->stops[0].offset))
+	{
 		return TRUE;
-
-	    if (extents != NULL) {
-		double t[2];
-		_extents_to_linear_parameter (linear, extents, t);
-		if ((t[0] <= 0.0 && t[1] <= 0.0) || (t[0] >= 1.0 && t[1] >= 1.0))
-		    return TRUE;
-	    }
 	}
-    } else {
-	cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
-	/* degenerate radial gradients are clear */
-	if (_radial_pattern_is_degenerate (radial))
+
+	if (t[0] == t[1])
 	    return TRUE;
-	/* TODO: check actual intersection */
     }
 
     for (i = 0; i < gradient->n_stops; i++)
@@ -2487,7 +2468,13 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
 	    if (extents == NULL)
 		return FALSE;
 
-	    _extents_to_linear_parameter (linear, extents, t);
+	    _cairo_linear_pattern_box_to_parameter (linear,
+						    extents->x,
+						    extents->y,
+						    extents->x + extents->width,
+						    extents->y + extents->height,
+						    t);
+
 	    if (t[0] < 0.0 || t[1] > 1.0)
 		return FALSE;
 	}
@@ -2599,7 +2586,13 @@ _gradient_is_opaque (const cairo_gradient_pattern_t *gradient,
 	    if (extents == NULL)
 		return FALSE;
 
-	    _extents_to_linear_parameter (linear, extents, t);
+	    _cairo_linear_pattern_box_to_parameter (linear,
+						    extents->x,
+						    extents->y,
+						    extents->x + extents->width,
+						    extents->y + extents->height,
+						    t);
+
 	    if (t[0] < 0.0 || t[1] > 1.0)
 		return FALSE;
 	}
commit 790837ac68e51bdd55f13b70d54ba32917cebb45
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Tue Nov 23 19:31:26 2010 +0100

    pattern: Compute a covering parameter range of a gradient for a box.
    
    This makes it possible to compute the interpolation range needed to
    correctly draw a gradient so that it covers an area of interest.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index d45a2cf..1ac50b6 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -32,6 +32,8 @@
 #include "cairo-error-private.h"
 #include "cairo-freed-pool-private.h"
 
+#include <float.h>
+
 /**
  * SECTION:cairo-pattern
  * @Title: cairo_pattern_t
@@ -1767,11 +1769,472 @@ _linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
 static cairo_bool_t
 _radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial)
 {
+    /* A radial pattern is considered degenerate if it can be
+     * represented as a solid or clear pattern.  This corresponds to
+     * one of the two cases:
+     *
+     * 1) The radii are both zero.
+     *
+     * 2) The two circles have same radius and are at the same point.
+     *    (Cylinder gradient that doesn't move with the parameter.)
+     *
+     * These checks are made in fixed point, so they're implicitly
+     * using an epsilon that is larger than the epsilons we're using
+     * in the floating point tests.
+     */
     return radial->r1 == radial->r2 &&
 	(radial->r1 == 0 /* && radial->r2 == 0 */ ||
 	 (radial->c1.x == radial->c2.x && radial->c1.y == radial->c2.y));
 }
 
+static void
+_cairo_linear_pattern_box_to_parameter (const cairo_linear_pattern_t *linear,
+					double x0, double y0,
+					double x1, double y1,
+					double range[2])
+{
+    double t0, tdx, tdy;
+    double p1x, p1y, pdx, pdy, invsqnorm;
+
+    assert (! _linear_pattern_is_degenerate (linear));
+
+    /*
+     * Linear gradients are othrogonal to the line passing through
+     * their extremes. Because of convexity, the parameter range can
+     * be computed as the convex hull (one the real line) of the
+     * parameter values of the 4 corners of the box.
+     *
+     * The parameter value t for a point (x,y) can be computed as:
+     *
+     *   t = (p2 - p1) . (x,y) / |p2 - p1|^2
+     *
+     * t0  is the t value for the top left corner
+     * tdx is the difference between left and right corners
+     * tdy is the difference between top and bottom corners
+     */
+
+    p1x = _cairo_fixed_to_double (linear->p1.x);
+    p1y = _cairo_fixed_to_double (linear->p1.y);
+    pdx = _cairo_fixed_to_double (linear->p2.x) - p1x;
+    pdy = _cairo_fixed_to_double (linear->p2.y) - p1y;
+    invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
+    pdx *= invsqnorm;
+    pdy *= invsqnorm;
+
+    t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy;
+    tdx = (x1 - x0) * pdx;
+    tdy = (y1 - y0) * pdy;
+
+    /*
+     * Because of the linearity of the t value, tdx can simply be
+     * added the t0 to move along the top edge. After this, range[0]
+     * and range[1] represent the parameter range for the top edge, so
+     * extending it to include the whole box simply requires adding
+     * tdy to the correct extreme.
+     */
+
+    range[0] = range[1] = t0;
+    if (tdx < 0)
+	range[0] += tdx;
+    else
+	range[1] += tdx;
+
+    if (tdy < 0)
+	range[0] += tdy;
+    else
+	range[1] += tdy;
+}
+
+static cairo_bool_t
+_extend_range (double range[2], double value, cairo_bool_t valid)
+{
+    if (!valid)
+	range[0] = range[1] = value;
+    else if (value < range[0])
+	range[0] = value;
+    else if (value > range[1])
+	range[1] = value;
+
+    return TRUE;
+}
+
+static void
+_cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial,
+					double x0, double y0,
+					double x1, double y1,
+					double tolerance,
+					double range[2])
+{
+    double cx, cy, cr, dx, dy, dr;
+    double a, x_focus, y_focus;
+    double mindr, minx, miny, maxx, maxy;
+    cairo_bool_t valid;
+
+    assert (! _radial_pattern_is_degenerate (radial));
+    assert (tolerance > 0);
+    assert (x0 < x1);
+    assert (y0 < y1);
+
+    range[0] = range[1] = 0;
+    valid = FALSE;
+
+    x_focus = y_focus = 0; /* silence gcc */
+
+    cx = _cairo_fixed_to_double (radial->c1.x);
+    cy = _cairo_fixed_to_double (radial->c1.y);
+    cr = _cairo_fixed_to_double (radial->r1);
+    dx = _cairo_fixed_to_double (radial->c2.x) - cx;
+    dy = _cairo_fixed_to_double (radial->c2.y) - cy;
+    dr = _cairo_fixed_to_double (radial->r2)   - cr;
+
+    /* translate by -(cx, cy) to simplify computations */
+    x0 -= cx;
+    y0 -= cy;
+    x1 -= cx;
+    y1 -= cy;
+
+    /* enlarge boundaries slightly to avoid rounding problems in the
+     * parameter range computation */
+    x0 -= DBL_EPSILON;
+    y0 -= DBL_EPSILON;
+    x1 += DBL_EPSILON;
+    y1 += DBL_EPSILON;
+
+    /* enlarge boundaries even more to avoid rounding problems when
+     * testing if a point belongs to the box */
+    minx = x0 - DBL_EPSILON;
+    miny = y0 - DBL_EPSILON;
+    maxx = x1 + DBL_EPSILON;
+    maxy = y1 + DBL_EPSILON;
+
+    /* we dont' allow negative radiuses, so we will be checking that
+     * t*dr >= mindr to consider t valid */
+    mindr = -(cr + DBL_EPSILON);
+
+    /*
+     * After the previous transformations, the start circle is
+     * centered in the origin and has radius cr. A 1-unit change in
+     * the t parameter corresponds to dx,dy,dr changes in the x,y,r of
+     * the circle (center coordinates, radius).
+     *
+     * To compute the minimum range needed to correctly draw the
+     * pattern, we start with an empty range and extend it to include
+     * the circles touching the bounding box or within it.
+     */
+
+    /*
+     * Focus, the point where the circle has radius == 0.
+     *
+     * r = cr + t * dr = 0
+     * t = -cr / dr
+     *
+     * If the radius is constant (dr == 0) there is no focus (the
+     * gradient represents a cylinder instead of a cone).
+     */
+    if (fabs (dr) >= DBL_EPSILON) {
+	double t_focus;
+
+	t_focus = -cr / dr;
+	x_focus = t_focus * dx;
+	y_focus = t_focus * dy;
+	if (minx <= x_focus && x_focus <= maxx &&
+	    miny <= y_focus && y_focus <= maxy)
+	{
+	    valid = _extend_range (range, t_focus, valid);
+	}
+    }
+
+    /*
+     * Circles externally tangent to box edges.
+     *
+     * All circles have center in (dx, dy) * t
+     *
+     * If the circle is tangent to the line defined by the edge of the
+     * box, then at least one of the following holds true:
+     *
+     *   (dx*t) + (cr + dr*t) == x0 (left   edge)
+     *   (dx*t) - (cr + dr*t) == x1 (right  edge)
+     *   (dy*t) + (cr + dr*t) == y0 (top    edge)
+     *   (dy*t) - (cr + dr*t) == y1 (bottom edge)
+     *
+     * The solution is only valid if the tangent point is actually on
+     * the edge, i.e. if its y coordinate is in [y0,y1] for left/right
+     * edges and if its x coordinate is in [x0,x1] for top/bottom
+     * edges.
+     *
+     * For the first equation:
+     *
+     *   (dx + dr) * t = x0 - cr
+     *   t = (x0 - cr) / (dx + dr)
+     *   y = dy * t
+     *
+     * in the code this becomes:
+     *
+     *   t_edge = (num) / (den)
+     *   v = (delta) * t_edge
+     *
+     * If the denominator in t is 0, the pattern is tangent to a line
+     * parallel to the edge under examination. The corner-case where
+     * the boundary line is the same as the edge is handled by the
+     * focus point case and/or by the a==0 case.
+     */
+#define T_EDGE(num,den,delta,lower,upper)				\
+    if (fabs (den) >= DBL_EPSILON) {					\
+	double t_edge, v;						\
+									\
+	t_edge = (num) / (den);						\
+	v = t_edge * (delta);						\
+	if (t_edge * dr >= mindr && (lower) <= v && v <= (upper))	\
+	    valid = _extend_range (range, t_edge, valid);		\
+    }
+
+    /* circles tangent (externally) to left/right/top/bottom edge */
+    T_EDGE (x0 - cr, dx + dr, dy, miny, maxy);
+    T_EDGE (x1 + cr, dx - dr, dy, miny, maxy);
+    T_EDGE (y0 - cr, dy + dr, dx, minx, maxx);
+    T_EDGE (y1 + cr, dy - dr, dx, minx, maxx);
+
+#undef T_EDGE
+
+    /*
+     * Circles passing through a corner.
+     *
+     * A circle passing through the point (x,y) satisfies:
+     *
+     * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
+     *
+     * If we set:
+     *   a = dx^2 + dy^2 - dr^2
+     *   b = x*dx + y*dy + cr*dr
+     *   c = x^2 + y^2 - cr^2
+     * we have:
+     *   a*t^2 - 2*b*t + c == 0
+     */
+    a = dx * dx + dy * dy - dr * dr;
+    if (fabs (a) < DBL_EPSILON * DBL_EPSILON) {
+	double b, maxd2;
+
+	/* Ensure that gradients with both a and dr small are
+	 * considered degenerate.
+	 * The floating point version of the degeneracy test implemented
+	 * in _radial_pattern_is_degenerate() is:
+	 *
+	 *  1) The circles are practically the same size:
+	 *     |dr| < DBL_EPSILON
+	 *  AND
+	 *  2a) The circles are both very small:
+	 *      min (r0, r1) < DBL_EPSILON
+	 *   OR
+	 *  2b) The circles are very close to each other:
+	 *      max (|dx|, |dy|) < 2 * DBL_EPSILON
+	 *
+	 * Assuming that the gradient is not degenerate, we want to
+	 * show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON.
+	 *
+	 * If the gradient is not degenerate yet it has |dr| <
+	 * DBL_EPSILON, (2b) is false, thus:
+	 *
+	 *   max (|dx|, |dy|) >= 2*DBL_EPSILON
+	 * which implies:
+	 *   4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
+	 *
+	 * From the definition of a, we get:
+	 *   a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2
+	 *   dx^2 + dy^2 - DBL_EPSILON^2 < dr^2
+	 *   3*DBL_EPSILON^2 < dr^2
+	 *
+	 * which is inconsistent with the hypotheses, thus |dr| <
+	 * DBL_EPSILON is false or the gradient is degenerate.
+	 */
+	assert (fabs (dr) >= DBL_EPSILON);
+
+	/*
+	 * If a == 0, all the circles are tangent to a line in the
+	 * focus point. If this line is within the box extents, we
+	 * should add the circle with infinite radius, but this would
+	 * make the range unbounded, so we add the smallest circle whose
+	 * distance to the desired (degenerate) circle within the
+	 * bounding box does not exceed tolerance.
+	 *
+	 * The equation of the line is b==0, i.e.:
+	 *   x*dx + y*dy + cr*dr == 0
+	 *
+	 * We compute the intersection of the line with the box and
+	 * keep the intersection with maximum square distance (maxd2)
+	 * from the focus point.
+	 *
+	 * In the code the intersection is represented in another
+	 * coordinate system, whose origin is the focus point and
+	 * which has a u,v axes, which are respectively orthogonal and
+	 * parallel to the edge being intersected.
+	 *
+	 * The intersection is valid only if it belongs to the box,
+	 * otherwise it is ignored.
+	 *
+	 * For example:
+	 *
+	 *   y = y0
+	 *   x*dx + y0*dy + cr*dr == 0
+	 *   x = -(y0*dy + cr*dr) / dx
+	 *
+	 * which in (u,v) is:
+	 *   u = y0 - y_focus
+	 *   v = -(y0*dy + cr*dr) / dx - x_focus
+	 *
+	 * In the code:
+	 *   u = (edge) - (u_origin)
+	 *   v = -((edge) * (delta) + cr*dr) / (den) - v_focus
+	 */
+#define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin)	\
+	if (fabs (den) >= DBL_EPSILON) {			\
+	    double v;						\
+								\
+	    v = -((edge) * (delta) + cr * dr) / (den);		\
+	    if ((lower) <= v && v <= (upper)) {			\
+		double u, d2;					\
+								\
+		u = (edge) - (u_origin);			\
+		v -= (v_origin);				\
+		d2 = u*u + v*v;					\
+		if (maxd2 < d2)					\
+		    maxd2 = d2;					\
+	    }							\
+	}
+
+	maxd2 = 0;
+
+	/* degenerate circles (lines) passing through each edge */
+	T_EDGE (y0, dy, dx, minx, maxx, y_focus, x_focus);
+	T_EDGE (y1, dy, dx, minx, maxx, y_focus, x_focus);
+	T_EDGE (x0, dx, dy, miny, maxy, x_focus, y_focus);
+	T_EDGE (x1, dx, dy, miny, maxy, x_focus, y_focus);
+
+#undef T_EDGE
+
+	/*
+	 * The limit circle can be transformed rigidly to the y=0 line
+	 * and the circles tangent to it in (0,0) are:
+	 *
+	 *   x^2 + (y-r)^2 = r^2  <=>  x^2 + y^2 - 2*y*r = 0
+	 *
+	 * y is the distance from the line, in our case tolerance;
+	 * x is the distance along the line, i.e. sqrt(maxd2),
+	 * so:
+	 *
+	 *   r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance)
+	 *   t = (r - cr) / dr =
+	 *       (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr)
+	 */
+	if (maxd2 > 0) {
+	    double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr;
+	    t_limit /= 2 * tolerance * dr;
+	    valid = _extend_range (range, t_limit, valid);
+	}
+
+	/*
+	 * Nondegenerate, nonlimit circles passing through the corners.
+	 *
+	 * a == 0 && a*t^2 - 2*b*t + c == 0
+	 *
+	 * t = c / (2*b)
+	 *
+	 * The b == 0 case has just been handled, so we only have to
+	 * compute this if b != 0.
+	 */
+#define T_CORNER(x,y)							\
+	b = (x) * dx + (y) * dy + cr * dr;				\
+	if (fabs (b) >= DBL_EPSILON) {					\
+	    double t_corner;						\
+	    double x2 = (x) * (x);					\
+	    double y2 = (y) * (y);					\
+	    double cr2 = (cr) * (cr);					\
+	    double c = x2 + y2 - cr2;					\
+	    								\
+	    t_corner = 0.5 * c / b;					\
+	    if (t_corner * dr >= mindr)					\
+		valid = _extend_range (range, t_corner, valid);		\
+	}
+
+	/* circles touching each corner */
+	T_CORNER (x0, y0);
+	T_CORNER (x0, y1);
+	T_CORNER (x1, y0);
+	T_CORNER (x1, y1);
+
+#undef T_CORNER
+    } else {
+	double inva, b, c, d;
+
+	inva = 1 / a;
+
+	/*
+	 * Nondegenerate, nonlimit circles passing through the corners.
+	 *
+	 * a != 0 && a*t^2 - 2*b*t + c == 0
+	 *
+	 * t = (b +- sqrt (b*b - a*c)) / a
+	 *
+	 * If the argument of sqrt() is negative, then no circle
+	 * passes through the corner.
+	 */
+#define T_CORNER(x,y)							\
+	b = (x) * dx + (y) * dy + cr * dr;				\
+	c = (x) * (x) + (y) * (y) - cr * cr;				\
+	d = b * b - a * c;						\
+	if (d >= 0) {							\
+	    double t_corner;						\
+									\
+	    d = sqrt (d);						\
+	    t_corner = (b + d) * inva;					\
+	    if (t_corner * dr >= mindr)					\
+		valid = _extend_range (range, t_corner, valid);		\
+	    t_corner = (b - d) * inva;					\
+	    if (t_corner * dr >= mindr)					\
+		valid = _extend_range (range, t_corner, valid);		\
+	}
+
+	/* circles touching each corner */
+	T_CORNER (x0, y0);
+	T_CORNER (x0, y1);
+	T_CORNER (x1, y0);
+	T_CORNER (x1, y1);
+
+#undef T_CORNER
+    }
+}
+
+/**
+ * _cairo_gradient_pattern_box_to_parameter
+ *
+ * Compute a interpolation range sufficient to draw (within the given
+ * tolerance) the gradient in the given box getting the same result as
+ * using the (-inf, +inf) range.
+ *
+ * Assumes that the pattern is not degenerate. This can be guaranteed
+ * by simplifying it to a solid clear if _cairo_pattern_is_clear or to
+ * a solid color if _cairo_gradient_pattern_is_solid.
+ *
+ * The range isn't guaranteed to be minimal, but it tries to.
+ **/
+void
+_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
+					  double x0, double y0,
+					  double x1, double y1,
+					  double tolerance,
+					  double out_range[2])
+{
+    assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+	    gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+    if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+	_cairo_linear_pattern_box_to_parameter ((cairo_linear_pattern_t *) gradient,
+						x0, y0, x1, y1, out_range);
+    } else {
+	_cairo_radial_pattern_box_to_parameter ((cairo_radial_pattern_t *) gradient,
+						x0, y0, x1, y1, tolerance, out_range);
+    }
+}
+
 static cairo_bool_t
 _gradient_is_clear (const cairo_gradient_pattern_t *gradient,
 		    const cairo_rectangle_int_t *extents)
diff --git a/src/cairoint.h b/src/cairoint.h
index f9fb521..4e8ef09 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2222,6 +2222,13 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
 				  const cairo_rectangle_int_t *extents,
 				  cairo_color_t *color);
 
+cairo_private void
+_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
+					  double x0, double y0,
+					  double x1, double y1,
+					  double tolerance,
+					  double out_range[2]);
+
 cairo_private cairo_bool_t
 _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern);
 
commit d1e9bdf7f15fd2ba7d42c6fe18650618d29c4942
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Sat Dec 4 14:58:52 2010 +0100

    ps: Avoid unneeded fallbacks for gradients with opaque stops.
    
    _cairo_pattern_is_opaque() returns false for none-extended linear
    gradients and for radial gradients, but fallback is only needed if
    they have non-opaque stops.
    This can be tested using _cairo_pattern_alpha_range(), which only
    analyses the part of the pattern which is drawn.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index 784e889..b53157e 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1779,6 +1779,8 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t    *surface,
 				     const cairo_pattern_t       *pattern,
 				     const cairo_rectangle_int_t *extents)
 {
+    double min_alpha;
+
     if (surface->force_fallbacks &&
 	surface->paginated_mode == CAIRO_PAGINATED_MODE_ANALYZE)
     {
@@ -1823,7 +1825,11 @@ _cairo_ps_surface_analyze_operation (cairo_ps_surface_t    *surface,
 								       surface_pattern);
     }
 
-    if (_cairo_pattern_is_opaque (pattern, extents))
+    /* Patterns whose drawn part is opaque are directly supported;
+       those whose drawn part is partially transparent can be
+       supported by flattening the alpha. */
+    _cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
+    if (CAIRO_ALPHA_IS_OPAQUE (min_alpha))
 	return CAIRO_STATUS_SUCCESS;
 
     return CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY;
commit ec4c085624d12cef51e583c18306fd4f4c2b6aaa
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Fri Nov 19 19:18:53 2010 +0100

    ps, pdf, pattern: Implement _cairo_pattern_alpha_range to analyse patterns.
    
    Both the ps and pdf backends are open coding analyses of the
    range of pattern alphas.  This patch factors out a new function
    _cairo_pattern_alpha_range() to do that for them.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 51473c8..d45a2cf 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1927,6 +1927,64 @@ _gradient_color_average (const cairo_gradient_pattern_t *gradient,
 }
 
 /**
+ * _cairo_pattern_alpha_range
+ *
+ * Convenience function to determine the minimum and maximum alpha in
+ * the drawn part of a pattern (i.e. ignoring clear parts caused by
+ * extend modes and/or pattern shape).
+ *
+ * If not NULL, out_min and out_max will be set respectively to the
+ * minimum and maximum alpha value of the pattern.
+ **/
+void
+_cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
+			    double                *out_min,
+			    double                *out_max)
+{
+    double alpha_min, alpha_max;
+
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID: {
+	const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+	alpha_min = alpha_max = solid->color.alpha;
+	break;
+    }
+
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL: {
+	const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+	unsigned int i;
+
+	assert (gradient->n_stops >= 1);
+
+	alpha_min = alpha_max = gradient->stops[0].color.alpha;
+	for (i = 1; i < gradient->n_stops; i++) {
+	    if (alpha_min > gradient->stops[i].color.alpha)
+		alpha_min = gradient->stops[i].color.alpha;
+	    else if (alpha_max < gradient->stops[i].color.alpha)
+		alpha_max = gradient->stops[i].color.alpha;
+	}
+
+	break;
+    }
+
+    default:
+	ASSERT_NOT_REACHED;
+	/* fall through */
+
+    case CAIRO_PATTERN_TYPE_SURFACE:
+	alpha_min = 0;
+	alpha_max = 1;
+	break;
+    }
+
+    if (out_min)
+	*out_min = alpha_min;
+    if (out_max)
+	*out_max = alpha_max;
+}
+
+/**
  * _cairo_gradient_pattern_is_solid
  *
  * Convenience function to determine whether a gradient pattern is
diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index d25dd91..99e9d0c 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -1244,18 +1244,6 @@ _cairo_pdf_surface_add_source_surface (cairo_pdf_surface_t	*surface,
     return status;
 }
 
-static cairo_bool_t
-_gradient_stops_are_opaque (const cairo_gradient_pattern_t *gradient)
-{
-    unsigned int i;
-
-    for (i = 0; i < gradient->n_stops; i++)
-	if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color))
-	    return FALSE;
-
-    return TRUE;
-}
-
 static cairo_status_t
 _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t		*surface,
 				    const cairo_pattern_t	*pattern,
@@ -1289,8 +1277,10 @@ _cairo_pdf_surface_add_pdf_pattern (cairo_pdf_surface_t		*surface,
     if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
         pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
     {
-	cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
-	if (! _gradient_stops_are_opaque (gradient)) {
+	double min_alpha;
+
+	_cairo_pattern_alpha_range (pattern, &min_alpha, NULL);
+	if (! CAIRO_ALPHA_IS_OPAQUE (min_alpha)) {
             pdf_pattern.gstate_res = _cairo_pdf_surface_new_object (surface);
 	    if (pdf_pattern.gstate_res.id == 0) {
 		cairo_pattern_destroy (pdf_pattern.pattern);
diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index fe633ba..784e889 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1725,21 +1725,17 @@ static cairo_bool_t
 _gradient_pattern_supported (cairo_ps_surface_t    *surface,
 			     const cairo_pattern_t *pattern)
 {
-    const cairo_gradient_pattern_t *gradient = (const cairo_gradient_pattern_t *) pattern;
-    uint16_t alpha;
+    double min_alpha, max_alpha;
     cairo_extend_t extend;
-    unsigned int i;
 
     if (surface->ps_level == CAIRO_PS_LEVEL_2)
 	return FALSE;
 
     /* Alpha gradients are only supported (by flattening the alpha)
      * if there is no variation in the alpha across the gradient. */
-    alpha = gradient->stops[0].color.alpha_short;
-    for (i = 0; i < gradient->n_stops; i++) {
-	if (gradient->stops[i].color.alpha_short != alpha)
-	    return FALSE;
-    }
+    _cairo_pattern_alpha_range (pattern, &min_alpha, &max_alpha);
+    if (min_alpha != max_alpha)
+	return FALSE;
 
     extend = cairo_pattern_get_extend ((cairo_pattern_t *) pattern);
 
diff --git a/src/cairoint.h b/src/cairoint.h
index 0ee4477..f9fb521 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -2213,6 +2213,10 @@ cairo_private void
 _cairo_pattern_transform (cairo_pattern_t      *pattern,
 			  const cairo_matrix_t *ctm_inverse);
 
+cairo_private void
+_cairo_pattern_alpha_range (const cairo_pattern_t *gradient,
+			    double *out_min, double *out_max);
+
 cairo_private cairo_bool_t
 _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
 				  const cairo_rectangle_int_t *extents,
commit 6579bf728f802e13b45292b11e3782db1844316f
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Thu Nov 18 13:53:51 2010 +0100

    pattern: Improve extents computation of radial gradients.
    
    Use the tests for degeneracy and new radial gradient definition
    when computing pattern extents.  Degenerate gradients are optimised
    away by cairo-gstate into solid or clear patterns, and
    the radial gradients semantics have changed to match PDF semantics.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 7750b3e..51473c8 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -2695,40 +2695,32 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 		(const cairo_radial_pattern_t *) pattern;
 	    double cx1, cy1;
 	    double cx2, cy2;
-	    double r, D;
+	    double r1, r2;
 
-	    if (radial->r1 == 0 && radial->r2 == 0)
+	    if (_radial_pattern_is_degenerate (radial)) {
+		/* cairo-gstate should have optimised degenerate
+		 * patterns to solid clear patterns, so we can ignore
+		 * them here. */
 		goto EMPTY;
+	    }
+
+	    /* TODO: in some cases (focus outside/on the circle) it is
+	     * half-bounded. */
+	    if (pattern->extend != CAIRO_EXTEND_NONE)
+		goto UNBOUNDED;
 
 	    cx1 = _cairo_fixed_to_double (radial->c1.x);
 	    cy1 = _cairo_fixed_to_double (radial->c1.y);
-	    r = _cairo_fixed_to_double (radial->r1);
-	    x1 = cx1 - r; x2 = cx1 + r;
-	    y1 = cy1 - r; y2 = cy1 + r;
+	    r1 = _cairo_fixed_to_double (radial->r1);
 
 	    cx2 = _cairo_fixed_to_double (radial->c2.x);
 	    cy2 = _cairo_fixed_to_double (radial->c2.y);
-	    r = fabs (_cairo_fixed_to_double (radial->r2));
-
-	    if (pattern->extend != CAIRO_EXTEND_NONE)
-		goto UNBOUNDED;
-
-	    /* We need to be careful, as if the circles are not
-	     * self-contained, then the solution is actually unbounded.
-	     */
-	    D = (cx1-cx2)*(cx1-cx2) + (cy1-cy2)*(cy1-cy2);
-	    if (D > r*r - 1e-5)
-		goto UNBOUNDED;
-
-	    if (cx2 - r < x1)
-		x1 = cx2 - r;
-	    if (cx2 + r > x2)
-		x2 = cx2 + r;
+	    r2 = _cairo_fixed_to_double (radial->r2);
 
-	    if (cy2 - r < y1)
-		y1 = cy2 - r;
-	    if (cy2 + r > y2)
-		y2 = cy2 + r;
+	    x1 = MIN (cx1 - r1, cx2 - r2);
+	    y1 = MIN (cy1 - r1, cy2 - r2);
+	    x2 = MAX (cx1 + r1, cx2 + r2);
+	    y2 = MAX (cy1 + r1, cy2 + r2);
 	}
 	break;
 
@@ -2740,9 +2732,15 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 	    if (pattern->extend != CAIRO_EXTEND_NONE)
 		goto UNBOUNDED;
 
-	    if (linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y)
+	    if (_linear_pattern_is_degenerate (linear)) {
+		/* cairo-gstate should have optimised degenerate
+		 * patterns to solid ones, so we can again ignore
+		 * them here. */
 		goto EMPTY;
+	    }
 
+	    /* TODO: to get tight extents, use the matrix to transform
+	     * the pattern instead of transforming the extents later. */
 	    if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.)
 		goto UNBOUNDED;
 
commit 86695aced9fc3210766abae7141c89b2c2c1a273
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Nov 17 21:31:07 2010 +0100

    pattern: Specialise signatures of pattern specific functions
    
    Change the signature of type-specific functions to make them only
    accept the correct pattern type instead of the abstract cairo_pattern_t.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 1b79011..7750b3e 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -2814,10 +2814,8 @@ _cairo_pattern_get_extents (const cairo_pattern_t         *pattern,
 
 static unsigned long
 _cairo_solid_pattern_hash (unsigned long hash,
-			   const cairo_pattern_t *pattern)
+			   const cairo_solid_pattern_t *solid)
 {
-    const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
-
     hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color));
 
     return hash;
@@ -2869,10 +2867,8 @@ _cairo_radial_pattern_hash (unsigned long hash,
 
 static unsigned long
 _cairo_surface_pattern_hash (unsigned long hash,
-			     const cairo_pattern_t *pattern)
+			     const cairo_surface_pattern_t *surface)
 {
-    const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
-
     hash ^= surface->surface->unique_id;
 
     return hash;
@@ -2901,13 +2897,13 @@ _cairo_pattern_hash (const cairo_pattern_t *pattern)
 
     switch (pattern->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
-	return _cairo_solid_pattern_hash (hash, pattern);
+	return _cairo_solid_pattern_hash (hash, (cairo_solid_pattern_t *) pattern);
     case CAIRO_PATTERN_TYPE_LINEAR:
 	return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern);
     case CAIRO_PATTERN_TYPE_RADIAL:
 	return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern);
     case CAIRO_PATTERN_TYPE_SURFACE:
-	return _cairo_surface_pattern_hash (hash, pattern);
+	return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern);
     default:
 	ASSERT_NOT_REACHED;
 	return FALSE;
@@ -2951,12 +2947,9 @@ _cairo_pattern_size (const cairo_pattern_t *pattern)
 
 
 static cairo_bool_t
-_cairo_solid_pattern_equal (const cairo_pattern_t *A,
-			    const cairo_pattern_t *B)
+_cairo_solid_pattern_equal (const cairo_solid_pattern_t *a,
+			    const cairo_solid_pattern_t *b)
 {
-    const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A;
-    const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B;
-
     return _cairo_color_equal (&a->color, &b->color);
 }
 
@@ -3024,12 +3017,9 @@ _cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
 }
 
 static cairo_bool_t
-_cairo_surface_pattern_equal (const cairo_pattern_t *A,
-			      const cairo_pattern_t *B)
+_cairo_surface_pattern_equal (const cairo_surface_pattern_t *a,
+			      const cairo_surface_pattern_t *b)
 {
-    const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A;
-    const cairo_surface_pattern_t *b = (cairo_surface_pattern_t *) B;
-
     return a->surface->unique_id == b->surface->unique_id;
 }
 
@@ -3061,7 +3051,8 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
 
     switch (a->type) {
     case CAIRO_PATTERN_TYPE_SOLID:
-	return _cairo_solid_pattern_equal (a, b);
+	return _cairo_solid_pattern_equal ((cairo_solid_pattern_t *) a,
+					   (cairo_solid_pattern_t *) b);
     case CAIRO_PATTERN_TYPE_LINEAR:
 	return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a,
 					    (cairo_linear_pattern_t *) b);
@@ -3069,7 +3060,8 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
 	return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a,
 					    (cairo_radial_pattern_t *) b);
     case CAIRO_PATTERN_TYPE_SURFACE:
-	return _cairo_surface_pattern_equal (a, b);
+	return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a,
+					     (cairo_surface_pattern_t *) b);
     default:
 	ASSERT_NOT_REACHED;
 	return FALSE;
commit ac9ed746b03e7c01c5997702d0694faa0938268d
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Nov 17 19:09:04 2010 +0100

    ps: Use switch instead of multiple if's
    
    This ensures that the compiler is able to automatically point out any
    unhandled pattern type.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-ps-surface.c b/src/cairo-ps-surface.c
index fc02003..fe633ba 100644
--- a/src/cairo-ps-surface.c
+++ b/src/cairo-ps-surface.c
@@ -1760,17 +1760,21 @@ _gradient_pattern_supported (cairo_ps_surface_t    *surface,
 static cairo_bool_t
 pattern_supported (cairo_ps_surface_t *surface, const cairo_pattern_t *pattern)
 {
-    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
 	return TRUE;
 
-    if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
-	pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
 	return _gradient_pattern_supported (surface, pattern);
 
-    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
+    case CAIRO_PATTERN_TYPE_SURFACE:
 	return surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
 
-    return FALSE;
+    default:
+	ASSERT_NOT_REACHED;
+	return FALSE;
+    }
 }
 
 static cairo_int_status_t
commit e6ab2e6821301301873ab329af53939807a004b3
Author: Andrea Canciani <ranma42 at gmail.com>
Date:   Wed Nov 17 19:05:58 2010 +0100

    pdf: Use switch instead of multiple if's
    
    This ensures that the compiler is able to automatically point out any
    unhandled pattern type.
    
    Reviewed-by: M Joonas Pihlaja <jpihlaja at cc.helsinki.fi>

diff --git a/src/cairo-pdf-surface.c b/src/cairo-pdf-surface.c
index cb7f7ba..d25dd91 100644
--- a/src/cairo-pdf-surface.c
+++ b/src/cairo-pdf-surface.c
@@ -5475,17 +5475,21 @@ _gradient_pattern_supported (const cairo_pattern_t *pattern)
 static cairo_bool_t
 _pattern_supported (const cairo_pattern_t *pattern)
 {
-    if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
+    switch (pattern->type) {
+    case CAIRO_PATTERN_TYPE_SOLID:
 	return TRUE;
 
-    if (pattern->type == CAIRO_PATTERN_TYPE_LINEAR ||
-	pattern->type == CAIRO_PATTERN_TYPE_RADIAL)
+    case CAIRO_PATTERN_TYPE_LINEAR:
+    case CAIRO_PATTERN_TYPE_RADIAL:
 	return _gradient_pattern_supported (pattern);
 
-    if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
+    case CAIRO_PATTERN_TYPE_SURFACE:
 	return _surface_pattern_supported ((cairo_surface_pattern_t *) pattern);
 
-    return FALSE;
+    default:
+	ASSERT_NOT_REACHED;
+	return FALSE;
+    }
 }
 
 static cairo_bool_t


More information about the cairo-commit mailing list