[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