Bug 513395: Make sure the Cairo/Quartz backend returns the first stop for negative positions on the gradient line of a nonrepeating linear gradient. r=jrmuizel
authorRobert O'Callahan <roc@ocallahan.org>
Mon, 02 Nov 2009 09:20:40 -0800
changeset 34472 4e88b5896a2c1be3c6365c0efb7397fc62e06054
parent 34471 bde646815debbc0d2b8e21e4650fec709f74e28b
child 34473 4e4a74c88e7ded1f43fb206bbcffdac480b2fba3
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs513395
milestone1.9.3a1pre
Bug 513395: Make sure the Cairo/Quartz backend returns the first stop for negative positions on the gradient line of a nonrepeating linear gradient. r=jrmuizel
gfx/cairo/README
gfx/cairo/cairo/src/cairo-quartz-surface.c
gfx/cairo/quartz-first-stop.patch
--- a/gfx/cairo/README
+++ b/gfx/cairo/README
@@ -55,16 +55,18 @@ win32-raster.patch: bug 498689; use scan
 quartz-falback.patch: try to fix Quartz fallback-to-pixman path; possiby incorrect and obsoleted by Andrea Canciani patch
 
 quartz-repeating-radial-gradients.patch: use Quartz to render repeating radial gradients instead of falling back
 
 quartz-const-globals.patch: make some Quartz color function data const globals instead of local variables
 
 quartz-minimze-gradient-repeat.patch: reduce the number of gradient stop repetitions we use, to improve quality of Quartz's gradient rendering
 
+quartz-first-stop.patch: return the first stop for negative positions on the gradient line of a nonrepeating linear gradient
+
 ==== pixman patches ====
 
 pixman-neon.patch: add ARM NEON optimized compositing functions
 
 endian.patch: include cairo-platform.h for endian macros
 
 ==== disable printing patch ====
 
--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
+++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
@@ -690,31 +690,51 @@ ComputeGradientValue (void *info, const 
 }
 
 static const float gradient_output_value_ranges[8] = {
     0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
 };
 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 float nonrepeating_gradient_input_value_range[2] = { -0.001f, 1.f };
 
 static CGFunctionRef
 CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
 {
     cairo_pattern_t *pat;
-    float input_value_range[2] = { 0.f, 1.f };
 
     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,
+			     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,
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/quartz-first-stop.patch
@@ -0,0 +1,57 @@
+diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c
+--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c
++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c
+@@ -690,31 +690,51 @@ ComputeGradientValue (void *info, const 
+ }
+ 
+ static const float gradient_output_value_ranges[8] = {
+     0.f, 1.f, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f
+ };
+ 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 float nonrepeating_gradient_input_value_range[2] = { -0.001f, 1.f };
+ 
+ static CGFunctionRef
+ CreateGradientFunction (const cairo_gradient_pattern_t *gpat)
+ {
+     cairo_pattern_t *pat;
+-    float input_value_range[2] = { 0.f, 1.f };
+ 
+     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,
++			     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,