Hello
This is the second attempt at bitmap supersampling in Pixman.
Changes:
1. There is a new fast path flag called FAST_PATH_NO_SUPERSAMPLING
(self-explanatory)
2. No divisions in inner loops, but this causes a proliferation of
variables. Note: using float or double for transformation matrices
would both simplify code and fix the problems encountered by Inkscape
when zooming into a bitmap.
3. Automatic sampling rate: by default, when the filter is better than
nearest neighbor, the image's sampling rate is automatically adjusted
to match the transform. This can be turned off by explicitly setting a
sampling rate using pixman_image_set_sampling_rate. This way the
quality of downscaling is vastly improved without the need for any
further changes to Cairo.
Regards, Krzysztof
diff --git a/pixman/pixman-bits-image.c b/pixman/pixman-bits-image.c
index a32ebcc..eb5c40f 100644
--- a/pixman/pixman-bits-image.c
+++ b/pixman/pixman-bits-image.c
@@ -643,6 +643,154 @@ bits_image_fetch_affine_no_alpha (pixman_image_t * image,
}
}
+static void
+bits_image_fetch_affine_no_alpha_supersample (pixman_image_t * image,
+ int offset,
+ int line,
+ int width,
+ uint32_t * buffer,
+ const uint32_t * mask)
+{
+ pixman_fixed_t x, y;
+ pixman_fixed_t ux, uy, vx, vy;
+ pixman_fixed_t uxfrac, uyfrac, vxfrac, vyfrac;
+ pixman_fixed_t uxrem, uyrem, vxrem, vyrem;
+ pixman_fixed_t uxstart, uystart, vxstart, vystart;
+ pixman_vector_t v;
+ int rate_x, rate_y, samples;
+ int i, iy, ix;
+ uint32_t result_a, result_r, result_g, result_b;
+
+ /* reference point is the center of the pixel */
+ v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2;
+ v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2;
+ v.vector[2] = pixman_fixed_1;
+
+ if (image->common.transform)
+ {
+ if (!pixman_transform_point_3d (image->common.transform, &v))
+ return;
+
+ ux = image->common.transform->matrix[0][0];
+ uy = image->common.transform->matrix[1][0];
+ vx = image->common.transform->matrix[0][1];
+ vy = image->common.transform->matrix[1][1];
+ }
+ else
+ {
+ ux = pixman_fixed_1;
+ uy = 0;
+ vx = 0;
+ vy = pixman_fixed_1;
+ }
+
+ x = v.vector[0];
+ y = v.vector[1];
+
+ rate_x = image->common.sampling_rate_x;
+ rate_y = image->common.sampling_rate_y;
+ samples = rate_x * rate_y;
+
+ /* precompute subpixel grid increments */
+ uxfrac = ux / rate_x;
+ uxrem = ux % rate_x;
+ uyfrac = uy / rate_x;
+ uyrem = uy % rate_x;
+ vxfrac = vx / rate_y;
+ vxrem = vx % rate_y;
+ vyfrac = vy / rate_y;
+ vyrem = vy % rate_y;
+ uxstart = (ux * (rate_x-1) + rate_x) / (2*rate_x);
+ uystart = (uy * (rate_x-1) + rate_x) / (2*rate_x);
+ vxstart = (vx * (rate_y-1) + rate_y) / (2*rate_y);
+ vystart = (vy * (rate_y-1) + rate_y) / (2*rate_y);
+
+ for (i = 0; i < width; ++i)
+ {
+ if (!mask || mask[i])
+ {
+ pixman_fixed_t tpx, tpy, tpxrem, tpyrem;
+
+ result_a = 0;
+ result_r = 0;
+ result_g = 0;
+ result_b = 0;
+
+ tpx = x - vxstart;
+ tpy = y - vystart;
+ tpxrem = 0;
+ tpyrem = 0;
+
+ for (iy = 0; iy < rate_y; ++iy)
+ {
+ pixman_fixed_t spx, spy, spxrem, spyrem;
+
+ tpx += vxfrac;
+ tpy += vyfrac;
+ tpxrem += vxrem;
+ tpyrem += vyrem;
+
+ if (tpxrem >= rate_y)
+ {
+ tpxrem -= rate_y;
+ ++tpx;
+ }
+ if (tpyrem >= rate_y)
+ {
+ tpyrem -= rate_y;
+ ++tpy;
+ }
+
+ spx = tpx - uxstart;
+ spy = tpy - uystart;
+ spxrem = 0;
+ spyrem = 0;
+
+ for (ix = 0; ix < rate_x; ++ix)
+ {
+ uint32_t pixel;
+
+ spx += uxfrac;
+ spy += uyfrac;
+ spxrem += uxrem;
+ spyrem += uyrem;
+
+ if (spxrem >= rate_x)
+ {
+ spxrem -= rate_x;
+ ++spx;
+ }
+ if (spyrem >= rate_x)
+ {
+ spyrem -= rate_x;
+ ++spy;
+ }
+
+ pixel = bits_image_fetch_pixel_filtered (
+ &image->bits, spx, spy, fetch_pixel_no_alpha);
+
+ result_a += (pixel & 0xff000000) >> 24;
+ result_r += (pixel & 0x00ff0000) >> 16;
+ result_g += (pixel & 0x0000ff00) >> 8;
+ result_b += (pixel & 0x000000ff) >> 0;
+ }
+ }
+ result_a = (result_a + samples/2) / samples;
+ result_r = (result_r + samples/2) / samples;
+ result_g = (result_g + samples/2) / samples;
+ result_b = (result_b + samples/2) / samples;
+
+ buffer[i] = (result_a << 24)
+ | (result_r << 16)
+ | (result_g << 8)
+ | (result_b << 0);
+ }
+
+ x += ux;
+ y += uy;
+ }
+}
+
/* General fetcher */
static force_inline uint32_t
fetch_pixel_general (bits_image_t *image, int x, int y, pixman_bool_t check_bounds)
@@ -750,6 +898,196 @@ bits_image_fetch_general (pixman_image_t * image,
}
static void
+bits_image_fetch_general_supersample (pixman_image_t * image,
+ int offset,
+ int line,
+ int width,
+ uint32_t * buffer,
+ const uint32_t * mask)
+{
+ pixman_fixed_t x, y, w;
+ pixman_fixed_t ux, uy, uw, vx, vy, vw;
+ pixman_fixed_t uxfrac, uyfrac, uwfrac, vxfrac, vyfrac, vwfrac;
+ pixman_fixed_t uxrem, uyrem, uwrem, vxrem, vyrem, vwrem;
+ pixman_fixed_t uxstart, uystart, uwstart, vxstart, vystart, vwstart;
+ pixman_vector_t v;
+ int rate_x, rate_y, samples;
+ int i, ix, iy;
+ uint32_t result_a, result_r, result_g, result_b;
+
+ /* reference point is the center of the pixel */
+ v.vector[0] = pixman_int_to_fixed (offset) + pixman_fixed_1 / 2;
+ v.vector[1] = pixman_int_to_fixed (line) + pixman_fixed_1 / 2;
+ v.vector[2] = pixman_fixed_1;
+
+ if (image->common.transform)
+ {
+ if (!pixman_transform_point_3d (image->common.transform, &v))
+ return;
+
+ ux = image->common.transform->matrix[0][0];
+ uy = image->common.transform->matrix[1][0];
+ uw = image->common.transform->matrix[2][0];
+ vx = image->common.transform->matrix[0][1];
+ vy = image->common.transform->matrix[1][1];
+ vw = image->common.transform->matrix[2][1];
+ }
+ else
+ {
+ ux = pixman_fixed_1;
+ uy = 0;
+ uw = 0;
+ vx = 0;
+ vy = pixman_fixed_1;
+ vw = 0;
+ }
+
+ x = v.vector[0];
+ y = v.vector[1];
+ w = v.vector[2];
+
+ rate_x = image->common.sampling_rate_x;
+ rate_y = image->common.sampling_rate_y;
+ samples = rate_x * rate_y;
+
+ /* precompute subpixel grid increments */
+ uxfrac = ux / rate_x;
+ uxrem = ux % rate_x;
+ uyfrac = uy / rate_x;
+ uyrem = uy % rate_x;
+ uwfrac = uw / rate_x;
+ uwrem = uw % rate_x;
+ vxfrac = vx / rate_y;
+ vxrem = vx % rate_y;
+ vyfrac = vy / rate_y;
+ vyrem = vy % rate_y;
+ vwfrac = vw / rate_y;
+ vwrem = vw % rate_y;
+ uxstart = (ux * (rate_x-1) + rate_x) / (2*rate_x);
+ uystart = (uy * (rate_x-1) + rate_x) / (2*rate_x);
+ uwstart = (uw * (rate_x-1) + rate_x) / (2*rate_x);
+ vxstart = (vx * (rate_y-1) + rate_y) / (2*rate_y);
+ vystart = (vy * (rate_y-1) + rate_y) / (2*rate_y);
+ vwstart = (vw * (rate_y-1) + rate_y) / (2*rate_y);
+
+ for (i = 0; i < width; ++i)
+ {
+ if (!mask || mask[i])
+ {
+ pixman_fixed_t tpx, tpy, tpw, tpxrem, tpyrem, tpwrem;
+
+ result_a = 0;
+ result_r = 0;
+ result_g = 0;
+ result_b = 0;
+
+ tpx = x - vxstart;
+ tpy = y - vystart;
+ tpw = w - vwstart;
+ tpxrem = 0;
+ tpyrem = 0;
+ tpwrem = 0;
+
+ for (iy = 0; iy < rate_y; ++iy)
+ {
+ pixman_fixed_t spx, spy, spw, spxrem, spyrem, spwrem;
+
+ tpx += vxfrac;
+ tpy += vyfrac;
+ tpw += vwfrac;
+ tpxrem += vxrem;
+ tpyrem += vyrem;
+ tpwrem += vwrem;
+
+ if (tpxrem >= rate_y)
+ {
+ tpxrem -= rate_y;
+ ++tpx;
+ }
+ if (tpyrem >= rate_y)
+ {
+ tpyrem -= rate_y;
+ ++tpy;
+ }
+ if (tpwrem >= rate_y)
+ {
+ tpwrem -= rate_y;
+ ++tpw;
+ }
+
+ spx = tpx - uxstart;
+ spy = tpy - uystart;
+ spw = tpw - uwstart;
+ spxrem = 0;
+ spyrem = 0;
+ spwrem = 0;
+
+ for (ix = 0; ix < rate_x; ++ix)
+ {
+ pixman_fixed_t x0, y0;
+ uint32_t pixel;
+
+ spx += uxfrac;
+ spy += uyfrac;
+ spw += uwfrac;
+ spxrem += uxrem;
+ spyrem += uyrem;
+ spwrem += uwrem;
+
+ if (spxrem >= rate_x)
+ {
+ spxrem -= rate_x;
+ ++spx;
+ }
+ if (spyrem >= rate_x)
+ {
+ spyrem -= rate_x;
+ ++spy;
+ }
+ if (spwrem >= rate_x)
+ {
+ spwrem -= rate_x;
+ ++spw;
+ }
+
+ if (spw != 0)
+ {
+ x0 = ((pixman_fixed_48_16_t)spx << 16) / spw;
+ y0 = ((pixman_fixed_48_16_t)spy << 16) / spw;
+ }
+ else
+ {
+ x0 = 0;
+ y0 = 0;
+ }
+
+ pixel = bits_image_fetch_pixel_filtered (
+ &image->bits, x0, y0, fetch_pixel_general);
+
+ result_a += (pixel & 0xff000000) >> 24;
+ result_r += (pixel & 0x00ff0000) >> 16;
+ result_g += (pixel & 0x0000ff00) >> 8;
+ result_b += (pixel & 0x000000ff) >> 0;
+ }
+ }
+ result_a = (result_a + samples/2) / samples;
+ result_r = (result_r + samples/2) / samples;
+ result_g = (result_g + samples/2) / samples;
+ result_b = (result_b + samples/2) / samples;
+
+ buffer[i] = (result_a << 24)
+ | (result_r << 16)
+ | (result_g << 8)
+ | (result_b << 0);
+ }
+
+ x += ux;
+ y += uy;
+ w += uw;
+ }
+}
+
+static void
bits_image_fetch_solid_32 (pixman_image_t * image,
int x,
int y,
@@ -927,7 +1265,8 @@ static const fetcher_info_t fetcher_info[] =
FAST_PATH_ID_TRANSFORM |
FAST_PATH_NO_CONVOLUTION_FILTER |
FAST_PATH_NO_PAD_REPEAT |
- FAST_PATH_NO_REFLECT_REPEAT),
+ FAST_PATH_NO_REFLECT_REPEAT |
+ FAST_PATH_NO_SUPERSAMPLING),
bits_image_fetch_untransformed_32,
bits_image_fetch_untransformed_64
},
@@ -940,7 +1279,8 @@ static const fetcher_info_t fetcher_info[] =
FAST_PATH_X_UNIT_POSITIVE | \
FAST_PATH_Y_UNIT_ZERO | \
FAST_PATH_NONE_REPEAT | \
- FAST_PATH_BILINEAR_FILTER)
+ FAST_PATH_BILINEAR_FILTER | \
+ FAST_PATH_NO_SUPERSAMPLING)
{ PIXMAN_a8r8g8b8,
FAST_BILINEAR_FLAGS,
@@ -957,12 +1297,27 @@ static const fetcher_info_t fetcher_info[] =
{ PIXMAN_any,
(FAST_PATH_NO_ALPHA_MAP |
FAST_PATH_HAS_TRANSFORM |
- FAST_PATH_AFFINE_TRANSFORM),
+ FAST_PATH_AFFINE_TRANSFORM |
+ FAST_PATH_NO_SUPERSAMPLING),
bits_image_fetch_affine_no_alpha,
_pixman_image_get_scanline_generic_64
},
- { PIXMAN_any, 0, bits_image_fetch_general, _pixman_image_get_scanline_generic_64 },
+ { PIXMAN_any,
+ (FAST_PATH_NO_ALPHA_MAP |
+ FAST_PATH_HAS_TRANSFORM |
+ FAST_PATH_AFFINE_TRANSFORM),
+ bits_image_fetch_affine_no_alpha_supersample,
+ _pixman_image_get_scanline_generic_64
+ },
+
+ { PIXMAN_any,
+ FAST_PATH_NO_SUPERSAMPLING,
+ bits_image_fetch_general,
+ _pixman_image_get_scanline_generic_64
+ },
+
+ { PIXMAN_any, 0, bits_image_fetch_general_supersample, _pixman_image_get_scanline_generic_64 },
{ PIXMAN_null },
};
diff --git a/pixman/pixman-image.c b/pixman/pixman-image.c
index 269c3c1..653f638 100644
--- a/pixman/pixman-image.c
+++ b/pixman/pixman-image.c
@@ -116,6 +116,9 @@ _pixman_image_allocate (void)
common->destroy_func = NULL;
common->destroy_data = NULL;
common->dirty = TRUE;
+ common->sampling_rate_x = 1;
+ common->sampling_rate_y = 1;
+ common->sampling_rate_auto = TRUE;
}
return image;
@@ -380,6 +383,13 @@ compute_image_info (pixman_image_t *image)
flags |= (FAST_PATH_NO_ACCESSORS | FAST_PATH_NO_WIDE_FORMAT);
+ /* Supersampling */
+ if (image->common.sampling_rate_x <= 1 &&
+ image->common.sampling_rate_y <= 1)
+ {
+ flags |= FAST_PATH_NO_SUPERSAMPLING;
+ }
+
/* Type specific checks */
switch (image->type)
{
@@ -473,6 +483,33 @@ _pixman_image_validate (pixman_image_t *image)
{
if (image->common.dirty)
{
+ /* When supersampling is set to auto, update
+ * the sampling rate based on the transform.
+ * Do not supersample when filter is nearest neighbor.
+ */
+ if (image->common.sampling_rate_auto)
+ {
+ if (image->common.transform &&
+ image->common.filter != PIXMAN_FILTER_FAST &&
+ image->common.filter != PIXMAN_FILTER_NEAREST)
+ {
+ double exp_x, exp_y;
+ exp_x = hypot (
+ pixman_fixed_to_double(image->common.transform->matrix[0][0]),
+ pixman_fixed_to_double(image->common.transform->matrix[1][0]));
+ exp_y = hypot (
+ pixman_fixed_to_double(image->common.transform->matrix[0][1]),
+ pixman_fixed_to_double(image->common.transform->matrix[1][1]));
+
+ image->common.sampling_rate_x = MAX(1, ceil(exp_x));
+ image->common.sampling_rate_y = MAX(1, ceil(exp_y));
+ }
+ else
+ {
+ image->common.sampling_rate_x = 1;
+ image->common.sampling_rate_y = 1;
+ }
+ }
compute_image_info (image);
/* It is important that property_changed is
@@ -635,6 +672,40 @@ pixman_image_set_filter (pixman_image_t * image,
}
PIXMAN_EXPORT void
+pixman_image_set_sampling_rate (pixman_image_t *image,
+ int rate_x,
+ int rate_y)
+{
+ image_common_t *common = (image_common_t *)image;
+
+ if (rate_x == common->sampling_rate_x &&
+ rate_y == common->sampling_rate_y &&
+ !common->sampling_rate_auto)
+ {
+ return;
+ }
+
+ /* activate automatic adjustment of sampling rate
+ * when any of the parameters is less than one
+ */
+ if (rate_x < 1 || rate_y < 1)
+ {
+ if (common->sampling_rate_auto)
+ return;
+
+ common->sampling_rate_auto = TRUE;
+ }
+ else
+ {
+ common->sampling_rate_x = rate_x;
+ common->sampling_rate_y = rate_y;
+ common->sampling_rate_auto = FALSE;
+ }
+
+ image_property_changed (image);
+}
+
+PIXMAN_EXPORT void
pixman_image_set_source_clipping (pixman_image_t *image,
pixman_bool_t clip_sources)
{
diff --git a/pixman/pixman-private.h b/pixman/pixman-private.h
index 0629c42..e736ca9 100644
--- a/pixman/pixman-private.h
+++ b/pixman/pixman-private.h
@@ -87,15 +87,22 @@ struct image_common
* the image is used as a source
*/
pixman_bool_t dirty;
+
pixman_transform_t * transform;
pixman_repeat_t repeat;
pixman_filter_t filter;
pixman_fixed_t * filter_params;
int n_filter_params;
+
bits_image_t * alpha_map;
int alpha_origin_x;
int alpha_origin_y;
pixman_bool_t component_alpha;
+
+ int sampling_rate_x;
+ int sampling_rate_y;
+ pixman_bool_t sampling_rate_auto;
+
classify_func_t classify;
property_changed_func_t property_changed;
fetch_scanline_t get_scanline_32;
@@ -570,6 +577,7 @@ _pixman_choose_implementation (void);
#define FAST_PATH_BILINEAR_FILTER (1 << 20)
#define FAST_PATH_NO_NORMAL_REPEAT (1 << 21)
#define FAST_PATH_HAS_TRANSFORM (1 << 22)
+#define FAST_PATH_NO_SUPERSAMPLING (1 << 23)
#define FAST_PATH_PAD_REPEAT \
(FAST_PATH_NO_NONE_REPEAT | \
diff --git a/pixman/pixman.h b/pixman/pixman.h
index cfffa79..ff09b7e 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -772,6 +772,9 @@ pixman_bool_t pixman_image_set_filter (pixman_image_t
pixman_filter_t filter,
const pixman_fixed_t *filter_params,
int n_filter_params);
+void pixman_image_set_sampling_rate (pixman_image_t *image,
+ int rate_x,
+ int rate_y);
void pixman_image_set_source_clipping (pixman_image_t *image,
pixman_bool_t source_clipping);
void pixman_image_set_alpha_map (pixman_image_t *image,
_______________________________________________
Pixman mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/pixman