gecko-dev/gfx/cairo/pixman-dither.patch

311 lines
8.7 KiB
Diff

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 <config.h>
#endif
#include <stdlib.h>
#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 <config.h>
#endif
#include <stdlib.h>
#include <math.h>
#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++;