gfx/cairo/pixman-dither.patch
author Brian Hackett <bhackett1024@gmail.com>
Thu, 12 Dec 2013 13:10:54 -0800
changeset 176470 2e5ff561425490570383d57abf4eff21619c865f
parent 103950 0c1f34eb5b9304e636789e4a23819cf2a8ca69d5
permissions -rw-r--r--
Bug 932982 - Trace type constraints and allow preserving jitcode in GCs without also marking all type information, r=billm, r=jandem

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++;