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
