diff --git a/gfx/cairo/libpixman/src/pixman-dither.h b/gfx/cairo/libpixman/src/pixman-dither.h new file mode 100644 --- /dev/null +++ b/gfx/cairo/libpixman/src/pixman-dither.h @@ -0,0 +1,51 @@ +#define R16_BITS 5 +#define G16_BITS 6 +#define B16_BITS 5 + +#define R16_SHIFT (B16_BITS + G16_BITS) +#define G16_SHIFT (B16_BITS) +#define B16_SHIFT 0 + +#define MASK 0xff +#define ONE_HALF 0x80 + +#define A_SHIFT 8 * 3 +#define R_SHIFT 8 * 2 +#define G_SHIFT 8 +#define A_MASK 0xff000000 +#define R_MASK 0xff0000 +#define G_MASK 0xff00 + +#define RB_MASK 0xff00ff +#define AG_MASK 0xff00ff00 +#define RB_ONE_HALF 0x800080 +#define RB_MASK_PLUS_ONE 0x10000100 + +#define ALPHA_8(x) ((x) >> A_SHIFT) +#define RED_8(x) (((x) >> R_SHIFT) & MASK) +#define GREEN_8(x) (((x) >> G_SHIFT) & MASK) +#define BLUE_8(x) ((x) & MASK) + +// This uses the same dithering technique that Skia does. +// It is essentially preturbing the lower bit based on the +// high bit +static inline uint16_t dither_32_to_16(uint32_t c) +{ + uint8_t b = BLUE_8(c); + uint8_t g = GREEN_8(c); + uint8_t r = RED_8(c); + r = ((r << 1) - ((r >> (8 - R16_BITS) << (8 - R16_BITS)) | (r >> R16_BITS))) >> (8 - R16_BITS); + g = ((g << 1) - ((g >> (8 - G16_BITS) << (8 - G16_BITS)) | (g >> G16_BITS))) >> (8 - G16_BITS); + b = ((b << 1) - ((b >> (8 - B16_BITS) << (8 - B16_BITS)) | (b >> B16_BITS))) >> (8 - B16_BITS); + return ((r << R16_SHIFT) | (g << G16_SHIFT) | (b << B16_SHIFT)); +} + +static inline uint16_t dither_8888_to_0565(uint32_t color, pixman_bool_t toggle) +{ + // alternate between a preturbed truncation and a regular truncation + if (toggle) { + return dither_32_to_16(color); + } else { + return CONVERT_8888_TO_0565(color); + } +} diff --git a/gfx/cairo/libpixman/src/pixman-linear-gradient.c b/gfx/cairo/libpixman/src/pixman-linear-gradient.c --- a/gfx/cairo/libpixman/src/pixman-linear-gradient.c +++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c @@ -26,16 +26,18 @@ */ #ifdef HAVE_CONFIG_H #include #endif #include #include "pixman-private.h" +#include "pixman-dither.h" + static pixman_bool_t linear_gradient_is_horizontal (pixman_image_t *image, int x, int y, int width, int height) { linear_gradient_t *linear = (linear_gradient_t *)image; @@ -222,25 +224,28 @@ linear_get_scanline_narrow (pixman_iter_ return iter->buffer; } static uint16_t convert_8888_to_0565(uint32_t color) { return CONVERT_8888_TO_0565(color); } + + static uint32_t * linear_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask) { pixman_image_t *image = iter->image; int x = iter->x; int y = iter->y; int width = iter->width; uint16_t * buffer = (uint16_t*)iter->buffer; + pixman_bool_t toggle = ((x ^ y) & 1); pixman_vector_t v, unit; pixman_fixed_32_32_t l; pixman_fixed_48_16_t dx, dy; gradient_t *gradient = (gradient_t *)image; linear_gradient_t *linear = (linear_gradient_t *)image; uint16_t *end = buffer + width; pixman_gradient_walker_t walker; @@ -294,34 +299,47 @@ linear_get_scanline_16 (pixman_iter_t * t = ((dx * v.vector[0] + dy * v.vector[1]) - (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden; } next_inc = 0; if (((pixman_fixed_32_32_t )(inc * width)) == 0) { - register uint16_t color; + register uint32_t color; + uint16_t dither_diff; + uint16_t color16; + uint16_t color16b; - color = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t)); - while (buffer < end) - *buffer++ = color; + color = _pixman_gradient_walker_pixel (&walker, t); + color16 = dither_8888_to_0565(color, toggle); + color16b = dither_8888_to_0565(color, toggle^1); + // compute the difference + dither_diff = color16 ^ color16b; + while (buffer < end) { + *buffer++ = color16; + // use dither_diff to toggle between color16 and color16b + color16 ^= dither_diff; + toggle ^= 1; + } } else { int i; i = 0; while (buffer < end) { if (!mask || *mask++) { - *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, - t + next_inc)); + *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, + t + next_inc), + toggle); } + toggle ^= 1; i++; next_inc = inc * i; buffer++; } } } else { @@ -340,18 +358,20 @@ linear_get_scanline_16 (pixman_iter_t * invden = pixman_fixed_1 * (double) pixman_fixed_1 / (l * (double) v.vector[2]); v2 = v.vector[2] * (1. / pixman_fixed_1); t = ((dx * v.vector[0] + dy * v.vector[1]) - (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden; } - *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t)); + *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t), + toggle); } + toggle ^= 1; ++buffer; v.vector[0] += unit.vector[0]; v.vector[1] += unit.vector[1]; v.vector[2] += unit.vector[2]; } } @@ -369,17 +389,18 @@ linear_get_scanline_wide (pixman_iter_t pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width); return buffer; } void _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter) { - if (linear_gradient_is_horizontal ( + // XXX: we can't use this optimization when dithering + if (0 && linear_gradient_is_horizontal ( iter->image, iter->x, iter->y, iter->width, iter->height)) { if (iter->flags & ITER_16) linear_get_scanline_16 (iter, NULL); else if (iter->flags & ITER_NARROW) linear_get_scanline_narrow (iter, NULL); else linear_get_scanline_wide (iter, NULL); diff --git a/gfx/cairo/libpixman/src/pixman-radial-gradient.c b/gfx/cairo/libpixman/src/pixman-radial-gradient.c --- a/gfx/cairo/libpixman/src/pixman-radial-gradient.c +++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c @@ -29,16 +29,18 @@ #ifdef HAVE_CONFIG_H #include #endif #include #include #include "pixman-private.h" +#include "pixman-dither.h" + static inline pixman_fixed_32_32_t dot (pixman_fixed_48_16_t x1, pixman_fixed_48_16_t y1, pixman_fixed_48_16_t z1, pixman_fixed_48_16_t x2, pixman_fixed_48_16_t y2, pixman_fixed_48_16_t z2) { @@ -489,16 +491,17 @@ radial_get_scanline_16 (pixman_iter_t *i * <=> for every p, the radiuses associated with the two t solutions * have opposite sign */ pixman_image_t *image = iter->image; int x = iter->x; int y = iter->y; int width = iter->width; uint16_t *buffer = iter->buffer; + pixman_bool_t toggle = ((x ^ y) & 1); gradient_t *gradient = (gradient_t *)image; radial_gradient_t *radial = (radial_gradient_t *)image; uint16_t *end = buffer + width; pixman_gradient_walker_t walker; pixman_vector_t v, unit; /* reference point is the center of the pixel */ @@ -575,25 +578,27 @@ radial_get_scanline_16 (pixman_iter_t *i unit.vector[0], unit.vector[1], 0); ddc = 2 * dot (unit.vector[0], unit.vector[1], 0, unit.vector[0], unit.vector[1], 0); while (buffer < end) { if (!mask || *mask++) { - *buffer = convert_8888_to_0565( + *buffer = dither_8888_to_0565( radial_compute_color (radial->a, b, c, radial->inva, radial->delta.radius, radial->mindr, &walker, - image->common.repeat)); + image->common.repeat), + toggle); } + toggle ^= 1; b += db; c += dc; dc += ddc; ++buffer; } } else { @@ -621,31 +626,33 @@ radial_get_scanline_16 (pixman_iter_t *i radial->delta.x, radial->delta.y, radial->delta.radius); /* / pixman_fixed_1 / pixman_fixed_1 */ c = fdot (pdx, pdy, -radial->c1.radius, pdx, pdy, radial->c1.radius); /* / pixman_fixed_1 / pixman_fixed_1 */ - *buffer = convert_8888_to_0565 ( + *buffer = dither_8888_to_0565 ( radial_compute_color (radial->a, b, c, radial->inva, radial->delta.radius, radial->mindr, &walker, - image->common.repeat)); + image->common.repeat), + toggle); } else { *buffer = 0; } } ++buffer; + toggle ^= 1; v.vector[0] += unit.vector[0]; v.vector[1] += unit.vector[1]; v.vector[2] += unit.vector[2]; } } iter->y++;