summary:     Bug 689707. Use lower precision bilinear interpolation. r=joe

diff --git a/gfx/cairo/libpixman/src/pixman-bits-image.c b/gfx/cairo/libpixman/src/pixman-bits-image.c
--- a/gfx/cairo/libpixman/src/pixman-bits-image.c
+++ b/gfx/cairo/libpixman/src/pixman-bits-image.c
@@ -124,18 +124,18 @@ bits_image_fetch_pixel_bilinear (bits_im
     int height = image->height;
     int x1, y1, x2, y2;
     uint32_t tl, tr, bl, br;
     int32_t distx, disty;
 
     x1 = x - pixman_fixed_1 / 2;
     y1 = y - pixman_fixed_1 / 2;
 
-    distx = (x1 >> 8) & 0xff;
-    disty = (y1 >> 8) & 0xff;
+    distx = interpolation_coord(x1);
+    disty = interpolation_coord(y1);
 
     x1 = pixman_fixed_to_int (x1);
     y1 = pixman_fixed_to_int (y1);
     x2 = x1 + 1;
     y2 = y1 + 1;
 
     if (repeat_mode != PIXMAN_REPEAT_NONE)
     {
@@ -190,17 +190,17 @@ bits_image_fetch_bilinear_no_repeat_8888
 
     if (!pixman_transform_point_3d (bits->common.transform, &v))
 	return;
 
     ux = ux_top = ux_bottom = bits->common.transform->matrix[0][0];
     x = x_top = x_bottom = v.vector[0] - pixman_fixed_1/2;
 
     y = v.vector[1] - pixman_fixed_1/2;
-    disty = (y >> 8) & 0xff;
+    disty = interpolation_coord(y);
 
     /* Load the pointers to the first and second lines from the source
      * image that bilinear code must read.
      *
      * The main trick in this code is about the check if any line are
      * outside of the image;
      *
      * When I realize that a line (any one) is outside, I change
@@ -299,17 +299,17 @@ bits_image_fetch_bilinear_no_repeat_8888
     while (buffer < end && x < 0)
     {
 	uint32_t tr, br;
 	int32_t distx;
 
 	tr = top_row[pixman_fixed_to_int (x_top) + 1] | top_mask;
 	br = bottom_row[pixman_fixed_to_int (x_bottom) + 1] | bottom_mask;
 
-	distx = (x >> 8) & 0xff;
+	distx = interpolation_coord(x);
 
 	*buffer++ = bilinear_interpolation (0, tr, 0, br, distx, disty);
 
 	x += ux;
 	x_top += ux_top;
 	x_bottom += ux_bottom;
 	mask += mask_inc;
     }
@@ -324,17 +324,17 @@ bits_image_fetch_bilinear_no_repeat_8888
 	    uint32_t tl, tr, bl, br;
 	    int32_t distx;
 
 	    tl = top_row [pixman_fixed_to_int (x_top)] | top_mask;
 	    tr = top_row [pixman_fixed_to_int (x_top) + 1] | top_mask;
 	    bl = bottom_row [pixman_fixed_to_int (x_bottom)] | bottom_mask;
 	    br = bottom_row [pixman_fixed_to_int (x_bottom) + 1] | bottom_mask;
 
-	    distx = (x >> 8) & 0xff;
+	    distx = interpolation_coord(x);
 
 	    *buffer = bilinear_interpolation (tl, tr, bl, br, distx, disty);
 	}
 
 	buffer++;
 	x += ux;
 	x_top += ux_top;
 	x_bottom += ux_bottom;
@@ -348,17 +348,17 @@ bits_image_fetch_bilinear_no_repeat_8888
 	if (*mask)
 	{
 	    uint32_t tl, bl;
 	    int32_t distx;
 
 	    tl = top_row [pixman_fixed_to_int (x_top)] | top_mask;
 	    bl = bottom_row [pixman_fixed_to_int (x_bottom)] | bottom_mask;
 
-	    distx = (x >> 8) & 0xff;
+	    distx = interpolation_coord(x);
 
 	    *buffer = bilinear_interpolation (tl, 0, bl, 0, distx, disty);
 	}
 
 	buffer++;
 	x += ux;
 	x_top += ux_top;
 	x_bottom += ux_bottom;
@@ -675,18 +675,18 @@ bits_image_fetch_bilinear_affine (pixman
 	const uint8_t *row2;
 
 	if (mask && !mask[i])
 	    goto next;
 
 	x1 = x - pixman_fixed_1 / 2;
 	y1 = y - pixman_fixed_1 / 2;
 
-	distx = (x1 >> 8) & 0xff;
-	disty = (y1 >> 8) & 0xff;
+	distx = interpolation_coord(x1);
+	disty = interpolation_coord(y1);
 
 	y1 = pixman_fixed_to_int (y1);
 	y2 = y1 + 1;
 	x1 = pixman_fixed_to_int (x1);
 	x2 = x1 + 1;
 
 	if (repeat_mode != PIXMAN_REPEAT_NONE)
 	{
diff --git a/gfx/cairo/libpixman/src/pixman-inlines.h b/gfx/cairo/libpixman/src/pixman-inlines.h
--- a/gfx/cairo/libpixman/src/pixman-inlines.h
+++ b/gfx/cairo/libpixman/src/pixman-inlines.h
@@ -76,16 +76,31 @@ repeat (pixman_repeat_t repeat, int *c, 
     {
 	*c = MOD (*c, size * 2);
 	if (*c >= size)
 	    *c = size * 2 - *c - 1;
     }
     return TRUE;
 }
 
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+#define LOW_QUALITY_INTERPOLATION
+#endif
+
+static force_inline int32_t
+interpolation_coord(pixman_fixed_t t)
+{
+#ifdef LOW_QUALITY_INTERPOLATION
+    return (t >> 12) & 0xf;
+#else
+    return (t >> 8) & 0xff;
+#endif
+}
+
+
 #if SIZEOF_LONG > 4
 
 static force_inline uint32_t
 bilinear_interpolation (uint32_t tl, uint32_t tr,
 			uint32_t bl, uint32_t br,
 			int distx, int disty)
 {
     uint64_t distxy, distxiy, distixy, distixiy;
@@ -122,16 +137,44 @@ bilinear_interpolation (uint32_t tl, uin
     f = tl64 * distixiy + tr64 * distxiy + bl64 * distixy + br64 * distxy;
     r |= ((f >> 16) & 0x000000ff00000000ull) | (f & 0xff000000ull);
 
     return (uint32_t)(r >> 16);
 }
 
 #else
 
+#ifdef LOW_QUALITY_INTERPOLATION
+/* Based on Filter_32_opaque_portable from Skia */
+static force_inline uint32_t
+bilinear_interpolation(uint32_t a00, uint32_t a01,
+		       uint32_t a10, uint32_t a11,
+		       int x, int y)
+{
+    int xy = x * y;
+    static const uint32_t mask = 0xff00ff;
+
+    int scale = 256 - 16*y - 16*x + xy;
+    uint32_t lo = (a00 & mask) * scale;
+    uint32_t hi = ((a00 >> 8) & mask) * scale;
+
+    scale = 16*x - xy;
+    lo += (a01 & mask) * scale;
+    hi += ((a01 >> 8) & mask) * scale;
+
+    scale = 16*y - xy;
+    lo += (a10 & mask) * scale;
+    hi += ((a10 >> 8) & mask) * scale;
+
+    lo += (a11 & mask) * xy;
+    hi += ((a11 >> 8) & mask) * xy;
+
+    return ((lo >> 8) & mask) | (hi & ~mask);
+}
+#else
 static force_inline uint32_t
 bilinear_interpolation (uint32_t tl, uint32_t tr,
 			uint32_t bl, uint32_t br,
 			int distx, int disty)
 {
     int distxy, distxiy, distixy, distixiy;
     uint32_t f, r;
 
@@ -164,17 +207,17 @@ bilinear_interpolation (uint32_t tl, uin
 
     /* Alpha */
     f = (tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
       + (bl & 0x0000ff00) * distixy  + (br & 0x0000ff00) * distxy;
     r |= f & 0xff000000;
 
     return r;
 }
-
+#endif
 #endif
 
 /*
  * For each scanline fetched from source image with PAD repeat:
  * - calculate how many pixels need to be padded on the left side
  * - calculate how many pixels need to be padded on the right side
  * - update width to only count pixels which are fetched from the image
  * All this information is returned via 'width', 'left_pad', 'right_pad'