[Pixman] [PATCH] pixman: Use maximum precision for gradients
Maarten Lankhorst
maarten.lankhorst at linux.intel.com
Mon Dec 3 14:00:31 UTC 2018
The high precision gradient path was falling back to the 8-bit path,
then expanding it to float. This removed any advantage of the high
precision path, so make sure our gradients are computed as floats
by adding a separate path.
Signed-off-by: Maarten Lankhorst <maarten.lankhorst at linux.intel.com>
---
pixman/pixman-conical-gradient.c | 38 +++++++----
pixman/pixman-gradient-walker.c | 73 ++++++++++++--------
pixman/pixman-linear-gradient.c | 59 +++++++++++-----
pixman/pixman-private.h | 5 ++
pixman/pixman-radial-gradient.c | 111 +++++++++++++++++++++----------
5 files changed, 192 insertions(+), 94 deletions(-)
diff --git a/pixman/pixman-conical-gradient.c b/pixman/pixman-conical-gradient.c
index 8bb46aecdcab..f1d8cb4ffceb 100644
--- a/pixman/pixman-conical-gradient.c
+++ b/pixman/pixman-conical-gradient.c
@@ -51,7 +51,7 @@ coordinates_to_parameter (double x, double y, double angle)
}
static uint32_t *
-conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
+conical_get_scanline (pixman_iter_t *iter, const uint32_t *mask, pixman_bool_t wide)
{
pixman_image_t *image = iter->image;
int x = iter->x;
@@ -61,7 +61,7 @@ conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
gradient_t *gradient = (gradient_t *)image;
conical_gradient_t *conical = (conical_gradient_t *)image;
- uint32_t *end = buffer + width;
+ uint32_t *end = buffer + (wide ? 4 * width : width);
pixman_gradient_walker_t walker;
pixman_bool_t affine = TRUE;
double cx = 1.;
@@ -109,11 +109,15 @@ conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
{
double t = coordinates_to_parameter (rx, ry, conical->angle);
- *buffer = _pixman_gradient_walker_pixel (
- &walker, (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (&walker, (argb_t *)buffer,
+ (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
+ else
+ *buffer = _pixman_gradient_walker_pixel (&walker,
+ (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
}
- ++buffer;
+ buffer += wide ? 4 : 1;
rx += cx;
ry += cy;
@@ -144,11 +148,15 @@ conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
t = coordinates_to_parameter (x, y, conical->angle);
- *buffer = _pixman_gradient_walker_pixel (
- &walker, (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (&walker, (argb_t *)buffer,
+ (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
+ else
+ *buffer = _pixman_gradient_walker_pixel (&walker,
+ (pixman_fixed_48_16_t)pixman_double_to_fixed (t));
}
- ++buffer;
+ buffer += wide ? 4 : 1;
rx += cx;
ry += cy;
@@ -160,15 +168,17 @@ conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
return iter->buffer;
}
+
static uint32_t *
-conical_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+conical_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
{
- uint32_t *buffer = conical_get_scanline_narrow (iter, NULL);
-
- pixman_expand_to_float (
- (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+ return conical_get_scanline (iter, mask, FALSE);
+}
- return buffer;
+static uint32_t *
+conical_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+ return conical_get_scanline (iter, mask, TRUE);
}
void
diff --git a/pixman/pixman-gradient-walker.c b/pixman/pixman-gradient-walker.c
index 822f8e62bae7..f0a82dc8178c 100644
--- a/pixman/pixman-gradient-walker.c
+++ b/pixman/pixman-gradient-walker.c
@@ -122,45 +122,44 @@ gradient_walker_reset (pixman_gradient_walker_t *walker,
left_c = right_c;
}
- /* The alpha channel is scaled to be in the [0, 255] interval,
- * and the red/green/blue channels are scaled to be in [0, 1].
- * This ensures that after premultiplication all channels will
- * be in the [0, 255] interval.
- */
- la = (left_c->alpha * (1.0f/257.0f));
- lr = (left_c->red * (1.0f/257.0f));
- lg = (left_c->green * (1.0f/257.0f));
- lb = (left_c->blue * (1.0f/257.0f));
-
- ra = (right_c->alpha * (1.0f/257.0f));
- rr = (right_c->red * (1.0f/257.0f));
- rg = (right_c->green * (1.0f/257.0f));
- rb = (right_c->blue * (1.0f/257.0f));
-
+ la = left_c->alpha * (1.0f / 65535.f);
+ lr = left_c->red * (1.0f / 65535.f);
+ lg = left_c->green * (1.0f / 65535.f);
+ lb = left_c->blue* (1.0f / 65535.f);
+
+ ra = right_c->alpha * (1.0f / 65535.f);
+ rr = right_c->red * (1.0f / 65535.f);
+ rg = right_c->green * (1.0f / 65535.f);
+ rb = right_c->blue * (1.0f / 65535.f);
+
lx = left_x * (1.0f/65536.0f);
rx = right_x * (1.0f/65536.0f);
-
+
if (FLOAT_IS_ZERO (rx - lx) || left_x == INT32_MIN || right_x == INT32_MAX)
{
+ float factor = 1.0f / 2.0f;
+
walker->a_s = walker->r_s = walker->g_s = walker->b_s = 0.0f;
- walker->a_b = (la + ra) / 2.0f;
- walker->r_b = (lr + rr) / 510.0f;
- walker->g_b = (lg + rg) / 510.0f;
- walker->b_b = (lb + rb) / 510.0f;
+ walker->a_b = (la + ra) * factor;
+
+ walker->r_b = (lr + rr) / 2.0f;
+ walker->g_b = (lg + rg) / 2.0f;
+ walker->b_b = (lb + rb) / 2.0f;
}
else
{
float w_rec = 1.0f / (rx - lx);
walker->a_b = (la * rx - ra * lx) * w_rec;
- walker->r_b = (lr * rx - rr * lx) * w_rec * (1.0f/255.0f);
- walker->g_b = (lg * rx - rg * lx) * w_rec * (1.0f/255.0f);
- walker->b_b = (lb * rx - rb * lx) * w_rec * (1.0f/255.0f);
-
walker->a_s = (ra - la) * w_rec;
- walker->r_s = (rr - lr) * w_rec * (1.0f/255.0f);
- walker->g_s = (rg - lg) * w_rec * (1.0f/255.0f);
- walker->b_s = (rb - lb) * w_rec * (1.0f/255.0f);
+
+ walker->r_b = (lr * rx - rr * lx) * w_rec;
+ walker->g_b = (lg * rx - rg * lx) * w_rec;
+ walker->b_b = (lb * rx - rb * lx) * w_rec;
+
+ walker->r_s = (rr - lr) * w_rec;
+ walker->g_s = (rg - lg) * w_rec;
+ walker->b_s = (rb - lb) * w_rec;
}
walker->left_x = left_x;
@@ -183,7 +182,7 @@ _pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
y = x * (1.0f / 65536.0f);
- a = walker->a_s * y + walker->a_b;
+ a = (walker->a_s * y + walker->a_b) * 255.f;
r = a * (walker->r_s * y + walker->r_b);
g = a * (walker->g_s * y + walker->g_b);
b = a * (walker->b_s * y + walker->b_b);
@@ -200,3 +199,21 @@ _pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
return v;
}
+
+void
+_pixman_gradient_walker_pixel_wide (pixman_gradient_walker_t *walker,
+ argb_t *buffer,
+ pixman_fixed_48_16_t x)
+{
+ float a, y;
+
+ if (walker->need_reset || x < walker->left_x || x >= walker->right_x)
+ gradient_walker_reset (walker, x);
+
+ y = x * (1.0f / 65536.0f);
+
+ buffer->a = a = walker->a_s * y + walker->a_b;
+ buffer->r = a * (walker->r_s * y + walker->r_b);
+ buffer->g = a * (walker->g_s * y + walker->g_b);
+ buffer->b = a * (walker->b_s * y + walker->b_b);
+}
diff --git a/pixman/pixman-linear-gradient.c b/pixman/pixman-linear-gradient.c
index 40c8c9f37dbe..f8e095bbcf88 100644
--- a/pixman/pixman-linear-gradient.c
+++ b/pixman/pixman-linear-gradient.c
@@ -89,8 +89,9 @@ linear_gradient_is_horizontal (pixman_image_t *image,
}
static uint32_t *
-linear_get_scanline_narrow (pixman_iter_t *iter,
- const uint32_t *mask)
+linear_get_scanline (pixman_iter_t *iter,
+ const uint32_t *mask,
+ pixman_bool_t wide)
{
pixman_image_t *image = iter->image;
int x = iter->x;
@@ -103,7 +104,7 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
pixman_fixed_48_16_t dx, dy;
gradient_t *gradient = (gradient_t *)image;
linear_gradient_t *linear = (linear_gradient_t *)image;
- uint32_t *end = buffer + width;
+ uint32_t *end = buffer + (wide ? 4 * width : width);
pixman_gradient_walker_t walker;
_pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
@@ -160,11 +161,23 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
if (((pixman_fixed_32_32_t )(inc * width)) == 0)
{
- register uint32_t color;
+ if (wide)
+ {
+ argb_t color;
+ _pixman_gradient_walker_pixel_wide (&walker, &color, t);
- color = _pixman_gradient_walker_pixel (&walker, t);
- while (buffer < end)
- *buffer++ = color;
+ while (buffer < end)
+ {
+ *(argb_t *)buffer = color;
+ buffer += 4;
+ }
+ }
+ else
+ {
+ register uint32_t color = _pixman_gradient_walker_pixel (&walker, t);
+ while (buffer < end)
+ *buffer++ = color;
+ }
}
else
{
@@ -175,12 +188,17 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
{
if (!mask || *mask++)
{
- *buffer = _pixman_gradient_walker_pixel (&walker,
- t + next_inc);
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (&walker,
+ (argb_t *)buffer,
+ t + next_inc);
+ else
+ *buffer = _pixman_gradient_walker_pixel (&walker,
+ t + next_inc);
}
i++;
next_inc = inc * i;
- buffer++;
+ buffer += wide ? 4 : 1;
}
}
}
@@ -206,10 +224,14 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
(dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
}
- *buffer = _pixman_gradient_walker_pixel (&walker, t);
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (&walker,
+ (argb_t *)buffer, t);
+ else
+ *buffer = _pixman_gradient_walker_pixel (&walker, t);
}
- ++buffer;
+ buffer += wide ? 4 : 1;
v.vector[0] += unit.vector[0];
v.vector[1] += unit.vector[1];
@@ -223,14 +245,15 @@ linear_get_scanline_narrow (pixman_iter_t *iter,
}
static uint32_t *
-linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+linear_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
{
- uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
-
- pixman_expand_to_float (
- (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+ return linear_get_scanline(iter, mask, FALSE);
+}
- return buffer;
+static uint32_t *
+linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+ return linear_get_scanline(iter, mask, TRUE);
}
void
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index 73a54146ddb1..016ef51e0d3e 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -367,6 +367,11 @@ uint32_t
_pixman_gradient_walker_pixel (pixman_gradient_walker_t *walker,
pixman_fixed_48_16_t x);
+void
+_pixman_gradient_walker_pixel_wide (pixman_gradient_walker_t *walker,
+ argb_t *buffer,
+ pixman_fixed_48_16_t x);
+
/*
* Edges
*/
diff --git a/pixman/pixman-radial-gradient.c b/pixman/pixman-radial-gradient.c
index 6a217963da18..232a02a017dd 100644
--- a/pixman/pixman-radial-gradient.c
+++ b/pixman/pixman-radial-gradient.c
@@ -66,7 +66,7 @@ fdot (double x1,
return x1 * x2 + y1 * y2 + z1 * z2;
}
-static uint32_t
+static void
radial_compute_color (double a,
double b,
double c,
@@ -74,7 +74,9 @@ radial_compute_color (double a,
double dr,
double mindr,
pixman_gradient_walker_t *walker,
- pixman_repeat_t repeat)
+ pixman_repeat_t repeat,
+ void *pixel,
+ pixman_bool_t wide)
{
/*
* In this function error propagation can lead to bad results:
@@ -93,27 +95,40 @@ radial_compute_color (double a,
* - the above problems are worse if a is small (as inva becomes bigger)
*/
double discr;
+ uint32_t *ret = pixel;
if (a == 0)
{
double t;
if (b == 0)
- return 0;
+ goto zero;
t = pixman_fixed_1 / 2 * c / b;
if (repeat == PIXMAN_REPEAT_NONE)
{
if (0 <= t && t <= pixman_fixed_1)
- return _pixman_gradient_walker_pixel (walker, t);
+ {
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (walker, pixel, t);
+ else
+ *ret = _pixman_gradient_walker_pixel (walker, t);
+ return;
+ }
}
else
{
if (t * dr >= mindr)
- return _pixman_gradient_walker_pixel (walker, t);
+ {
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (walker, pixel, t);
+ else
+ *ret = _pixman_gradient_walker_pixel (walker, t);
+ return;
+ }
}
- return 0;
+ goto zero;
}
discr = fdot (b, a, 0, b, -c, 0);
@@ -139,24 +154,49 @@ radial_compute_color (double a,
if (repeat == PIXMAN_REPEAT_NONE)
{
if (0 <= t0 && t0 <= pixman_fixed_1)
- return _pixman_gradient_walker_pixel (walker, t0);
+ {
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (walker, pixel, t0);
+ else
+ *ret = _pixman_gradient_walker_pixel (walker, t0);
+ return;
+ }
else if (0 <= t1 && t1 <= pixman_fixed_1)
- return _pixman_gradient_walker_pixel (walker, t1);
+ {
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (walker, pixel, t1);
+ else
+ *ret = _pixman_gradient_walker_pixel (walker, t1);
+ return;
+ }
}
else
{
if (t0 * dr >= mindr)
- return _pixman_gradient_walker_pixel (walker, t0);
+ {
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (walker, pixel, t0);
+ else
+ *ret = _pixman_gradient_walker_pixel (walker, t0);
+ return;
+ }
else if (t1 * dr >= mindr)
- return _pixman_gradient_walker_pixel (walker, t1);
+ {
+ if (wide)
+ _pixman_gradient_walker_pixel_wide (walker, pixel, t1);
+ else
+ *ret = _pixman_gradient_walker_pixel (walker, t1);
+ return;
+ }
}
}
- return 0;
+zero:
+ memset(pixel, 0, wide ? sizeof(argb_t) : 4);
}
static uint32_t *
-radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
+radial_get_scanline (pixman_iter_t *iter, const uint32_t *mask, pixman_bool_t wide)
{
/*
* Implementation of radial gradients following the PDF specification.
@@ -247,7 +287,7 @@ radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
gradient_t *gradient = (gradient_t *)image;
radial_gradient_t *radial = (radial_gradient_t *)image;
- uint32_t *end = buffer + width;
+ uint32_t *end = buffer + (wide ? width * 4 : width);
pixman_gradient_walker_t walker;
pixman_vector_t v, unit;
@@ -330,18 +370,19 @@ radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
{
if (!mask || *mask++)
{
- *buffer = radial_compute_color (radial->a, b, c,
- radial->inva,
- radial->delta.radius,
- radial->mindr,
- &walker,
- image->common.repeat);
+ radial_compute_color (radial->a, b, c,
+ radial->inva,
+ radial->delta.radius,
+ radial->mindr,
+ &walker,
+ image->common.repeat,
+ buffer, wide);
}
b += db;
c += dc;
dc += ddc;
- ++buffer;
+ buffer += wide ? 4 : 1;
}
}
else
@@ -375,20 +416,21 @@ radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
pdx, pdy, radial->c1.radius);
/* / pixman_fixed_1 / pixman_fixed_1 */
- *buffer = radial_compute_color (radial->a, b, c,
- radial->inva,
- radial->delta.radius,
- radial->mindr,
- &walker,
- image->common.repeat);
+ radial_compute_color (radial->a, b, c,
+ radial->inva,
+ radial->delta.radius,
+ radial->mindr,
+ &walker,
+ image->common.repeat,
+ buffer, wide);
}
else
{
- *buffer = 0;
+ memset(buffer, 0, wide ? sizeof(argb_t) : 4);
}
}
- ++buffer;
+ buffer += wide ? 4 : 1;
v.vector[0] += unit.vector[0];
v.vector[1] += unit.vector[1];
@@ -401,14 +443,15 @@ radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
}
static uint32_t *
-radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+radial_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
{
- uint32_t *buffer = radial_get_scanline_narrow (iter, NULL);
-
- pixman_expand_to_float (
- (argb_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+ return radial_get_scanline (iter, mask, FALSE);
+}
- return buffer;
+static uint32_t *
+radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+{
+ return radial_get_scanline (iter, mask, TRUE);
}
void
--
2.19.2
More information about the Pixman
mailing list