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

"Henry (Yu) Song - SISA" <[email protected]>
Newsgroups gmane.comp.lib.cairo
Message-ID <3955FA337689574EB32F94B12A7E6E9E16364AD5@sisaex01sj>
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 <[email protected]>
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
-- 
cairo mailing list
[email protected]
http://lists.cairographics.org/mailman/listinfo/cairo
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.