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 <[email protected]>
---
 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

_______________________________________________
Pixman mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/pixman

Reply via email to