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

Reply via email to