[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