[cairo] gl: fix gradient color generation where two stops have same offset

Henry (Yu) Song - SISA hsong at sisa.samsung.com
Thu Jul 26 10:02:16 PDT 2012


Hi, Chris

Based on your comments, I am re-working the patch.  The first patch is about fixing gradient color generation when multiple stops have a same offset but different color.  This patch brings test case linear-gradient-large closer to reference image

Thanks

Henry

>From 29f4412c0a2454c36828b4dce826d9f29e6b0739 Mon Sep 17 00:00:00 2001
From: Henry Song <henry.song at samsung.com>
Date: Wed, 25 Jul 2012 15:34:52 -0700
Subject: [PATCH] gl: fix gradient color generation for GL in cases where
 there are stops that have same offset but different color.

bring test case: linear-gradient-large closer to reference image
---
 src/cairo-gl-gradient.c |   51 ++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 48 insertions(+), 3 deletions(-)

diff --git a/src/cairo-gl-gradient.c b/src/cairo-gl-gradient.c
index ffb5468..a66f26d 100644
--- a/src/cairo-gl-gradient.c
+++ b/src/cairo-gl-gradient.c
@@ -44,6 +44,7 @@
 #include "cairo-gl-gradient-private.h"
 #include "cairo-gl-private.h"
 
+#define OFFSET_TOLERANCE 0.00001
 
 static int
 _cairo_gl_gradient_sample_width (unsigned int                 n_stops,
@@ -57,9 +58,10 @@ _cairo_gl_gradient_sample_width (unsigned int                 n_stops,
 	double dx = stops[n].offset - stops[n-1].offset;
 	double delta, max;
 	int ramp;
-
+	
+	/* in case two stops offsets are same, and colors for different. */
 	if (dx == 0)
-	    continue;
+	    dx = 0.01;
 
 	max = stops[n].color.red - stops[n-1].color.red;
 
@@ -97,6 +99,8 @@ _cairo_gl_gradient_render (const cairo_gl_context_t    *ctx,
     pixman_point_fixed_t p1, p2;
     unsigned int i;
     pixman_format_code_t gradient_pixman_format;
+    double *offset_stops;
+    double min_offset, max_offset;
 
     /*
      * Ensure that the order of the gradient's components in memory is BGRA.
@@ -116,14 +120,55 @@ _cairo_gl_gradient_render (const cairo_gl_context_t    *ctx,
 	    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
     }
 
+    /* due to a "bug" in pixman,  when there are two stops that have
+     * a same offset, pixman always uses the right side of the offset
+     * color.  This causes problem in the following situation:
+     *
+     * two stops (0, 1, 1, 1, 1) and (0, 0, 0, 0, 1) - both offset at 0,
+     * pixman picks the back color, the correct color should be white.
+     *
+     * To fix this, we need to increment the second offset a little.
+     * however, if these two offset are at 1, then we should decrement
+     * the first offset.  This implementation assumes the stops are in
+     * ascending order
+     */
+    offset_stops = _cairo_malloc_ab (n_stops, sizeof (double));
+    min_offset = stops[0].offset;
+    max_offset = stops[n_stops - 1].offset;
+    offset_stops[0] = stops[0].offset;
+    /* from left to right, increment offset if two stops have same offset */
+    for (i = 1; i < n_stops; i++) {
+	if (offset_stops[i-1] == stops[i].offset) {
+	    if (stops[i].offset == max_offset || 
+		stops[i].offset >= max_offset - OFFSET_TOLERANCE) 
+		offset_stops[i] = stops[i].offset;
+	    else
+		offset_stops[i] = stops[i].offset + OFFSET_TOLERANCE;
+	}
+	else 
+	    offset_stops[i] = stops[i].offset;
+    }
+    /* from right to left */
+    i = n_stops - 1;
+    while (i != 0) {
+	if (offset_stops[i-1] == offset_stops[i]) {
+	    if (offset_stops[i] != min_offset &&
+		offset_stops[i] >= min_offset + OFFSET_TOLERANCE)
+		offset_stops[i] -= OFFSET_TOLERANCE;
+	}
+	i--;
+    }
+
     for (i = 0; i < n_stops; i++) {
-	pixman_stops[i].x = _cairo_fixed_16_16_from_double (stops[i].offset);
+	pixman_stops[i].x = _cairo_fixed_16_16_from_double (offset_stops[i]);
 	pixman_stops[i].color.red   = stops[i].color.red_short;
 	pixman_stops[i].color.green = stops[i].color.green_short;
 	pixman_stops[i].color.blue  = stops[i].color.blue_short;
 	pixman_stops[i].color.alpha = stops[i].color.alpha_short;
     }
 
+    free (offset_stops);
+
     p1.x = _cairo_fixed_16_16_from_double (0.5);
     p1.y = 0;
     p2.x = _cairo_fixed_16_16_from_double (width - 0.5);
-- 
1.7.9.5


More information about the cairo mailing list