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

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.
This commit is contained in:
Jeff Muizelaar 2012-05-10 22:12:52 -04:00
parent 5e7b552f26
commit 4a37a54063
5 changed files with 281 additions and 14 deletions

View File

@ -192,6 +192,8 @@ pixman-export.patch: use cairo_public for PIXMAN_EXPORT to make sure pixman symb
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.

View File

@ -129,8 +129,8 @@ bits_image_fetch_pixel_bilinear (bits_image_t *image,
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);
@ -195,7 +195,7 @@ bits_image_fetch_bilinear_no_repeat_8888 (pixman_image_t * ima,
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.
@ -304,7 +304,7 @@ bits_image_fetch_bilinear_no_repeat_8888 (pixman_image_t * ima,
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);
@ -329,7 +329,7 @@ bits_image_fetch_bilinear_no_repeat_8888 (pixman_image_t * ima,
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);
}
@ -353,7 +353,7 @@ bits_image_fetch_bilinear_no_repeat_8888 (pixman_image_t * ima,
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);
}
@ -680,8 +680,8 @@ bits_image_fetch_bilinear_affine (pixman_image_t * image,
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;

View File

@ -81,6 +81,21 @@ repeat (pixman_repeat_t repeat, int *c, int size)
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
@ -127,6 +142,34 @@ bilinear_interpolation (uint32_t tl, uint32_t tr,
#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,
@ -169,7 +212,7 @@ bilinear_interpolation (uint32_t tl, uint32_t tr,
return r;
}
#endif
#endif
/*

View File

@ -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'

View File

@ -10,7 +10,7 @@
-->
<!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);
@ -27,11 +27,11 @@
</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>