Bug 689707. Use lower precision bilinear interpolation. r=joe,a=akeybl
authorJeff Muizelaar <jmuizelaar@mozilla.com>
Thu, 10 May 2012 22:12:52 -0400
changeset 95793 63727a5fa570e0f0aa0ca125541d87805b8c5dad
parent 95792 ac685c4554a508a2a7acafb2db0ec0d24001cd90
child 95794 0fc9cddcb19aef6bf828780bba2acdb6a0511c68
push id886
push userlsblakk@mozilla.com
push dateMon, 04 Jun 2012 19:57:52 +0000
treeherdermozilla-beta@bbd8d5efd6d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjoe, akeybl
bugs689707
milestone14.0a2
Bug 689707. Use lower precision bilinear interpolation. r=joe,a=akeybl This takes the bilinear interpolation code from Skia. It uses 4 bits of precision instead of 8. This lets it interpolate two components at a time because the results fit in 16 bits. The lower precision code is only used in the fallback code and not in any of the specialized code for NEON. This means pixman gives different results depending on the cpu which isn't great. However, this was easiest and the NEON code doesn't gain as much from using lower precision. Skia actually uses even lower interpolation when working with 565 but that's harder to plug in right now, and this gives a reasonable improvement.
gfx/cairo/README
gfx/cairo/libpixman/src/pixman-bits-image.c
gfx/cairo/libpixman/src/pixman-inlines.h
gfx/cairo/pixman-lowres-interp.patch
layout/reftests/image-element/pattern-html-02.html
--- a/gfx/cairo/README
+++ b/gfx/cairo/README
@@ -187,11 +187,13 @@ pixman-android-cpu-detect.patch: Add CPU
 pixman-rename-and-endian.patch: include cairo-platform.h for renaming of external symbols and endian macros
 
 NOTE: we previously supported ARM assembler on MSVC, this has been removed because of the maintenance burden
 
 pixman-export.patch: use cairo_public for PIXMAN_EXPORT to make sure pixman symbols are not exported in libxul
 
 pixman-limits.patch: include limits.h for SIZE_MAX
 
+pixman-lowres-interp.patch: Use lower quality interpolation for more speed.
+
 ==== disable printing patch ====
 
 disable-printing.patch:  allows us to use NS_PRINTING to disable printing.
--- 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)
 	{
--- 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'
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/pixman-lowres-interp.patch
@@ -0,0 +1,222 @@
+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'
--- a/layout/reftests/image-element/pattern-html-02.html
+++ b/layout/reftests/image-element/pattern-html-02.html
@@ -5,36 +5,36 @@
      Test painting patterns with scaling by background-size and
      -moz-transform. background-repeat requires the construction
      of a surface with the pattern contents, which will then be
      scaled up by the transform / background-size. That's why we
      need a threshold filter again.
 -->
 <!DOCTYPE html>
 <html>
-<body style="margin:0; filter:url(#thresholdAt150);">
+<body style="margin:0; filter:url(#thresholdAt159);">
   <div style="background: white; overflow: hidden;">
     <div style="margin:20px 0px 20px 40px; width:40px; height:20px;
                 background:-moz-element(#p);
                 -moz-transform:scale(3);"></div>
     <div style="width:120px; height:60px;
                 background:-moz-element(#p);
                 background-size:300%;"></div>
   
     <svg>
       <pattern id="p" patternUnits="userSpaceOnUse"
                    x="0" y="0" width="20" height="20">
         <rect x="5" y="5" width="10" height="10" fill="black"></rect>
       </pattern> 
     </svg>
   
     <svg>
-      <filter id="thresholdAt150" color-interpolation-filters="sRGB">
+      <filter id="thresholdAt159" color-interpolation-filters="sRGB">
         <feComponentTransfer>
-          <feFuncR type="linear" slope="255" intercept="-150"/>
-          <feFuncG type="linear" slope="255" intercept="-150"/>
-          <feFuncB type="linear" slope="255" intercept="-150"/>
+          <feFuncR type="linear" slope="255" intercept="-159"/>
+          <feFuncG type="linear" slope="255" intercept="-159"/>
+          <feFuncB type="linear" slope="255" intercept="-159"/>
         </feComponentTransfer>
       </filter>
     </svg>
   </div>
 </body>
 </html>