[cairo] pixman box filtering code prototype

Jeff Muizelaar jeff at infidigm.net
Tue Oct 7 15:12:49 PDT 2008


It works by projecting each pixel of the destination surface to a
parallelogram on the source surface.  All of the pixels in the
parallelogram are summed to produce the value of the destination pixel.

I've attached a patch against pixman revision
6449782f8a4bea0274a30f86d56214c2c73c0303. It does not merge without
conflicts so it will need some manual merging.

I've also attached a test program that generates the output something
like: http://people.freedesktop.org/~jrmuizel/box-filter.png vs.  the
existing bilinear code at:
http://people.freedesktop.org/~jrmuizel/bilinear.png

This example isn't the best at showcasing the code because it's scaling
down a simple image and not scaling too much, however it was a very
useful test case for getting things correct.

-Jeff
-------------- next part --------------
A non-text attachment was scrubbed...
Name: scale.c
Type: text/x-csrc
Size: 6612 bytes
Desc: not available
Url : http://lists.cairographics.org/archives/cairo/attachments/20081007/e81a2e9d/attachment.c 
-------------- next part --------------
diff --git a/pixman/pixman-compose.c b/pixman/pixman-compose.c
index faf2523..5880f4e 100644
--- a/pixman/pixman-compose.c
+++ b/pixman/pixman-compose.c
@@ -33,6 +33,8 @@
 #include <assert.h>
 #include <limits.h>
 
+#include <stdio.h>
+
 #include "pixman-private.h"
 
 /*
@@ -3682,6 +3684,12 @@ static void pixmanFetchSourcePict(source_image_t * pict, int x, int y, int width
  * Fetch from region strategies
  */
 typedef FASTCALL uint32_t (*fetchFromRegionProc)(bits_image_t *pict, int x, int y, uint32_t *buffer, fetchPixelProc fetch, pixman_box16_t *box);
+/* Note: the DIV macro returns the integer part of the division */
+static inline pixman_fixed_t pixman_fixed_div(pixman_fixed_t a, pixman_fixed_t b) {
+    return pixman_double_to_fixed(
+		pixman_fixed_to_double(a)/
+		pixman_fixed_to_double(b));
+}
 
 static inline uint32_t
 fbFetchFromNoRegion(bits_image_t *pict, int x, int y, uint32_t *buffer, fetchPixelProc fetch, pixman_box16_t *box)
@@ -4250,6 +4258,127 @@ fbFetchTransformed(bits_image_t * pict, int x, int y, int width, uint32_t *buffe
         {
             fbFetchTransformed_Nearest_General(pict, width, buffer, mask, maskBits, affine, v, unit);
         }
+    } else if (pict->common.filter == PIXMAN_FILTER_POSTSCRIPT)
+    {
+	//printf("filter postscript - for (%d %d) - %d\n", x, y, width);
+	/* use the whole pixel */
+	int i;
+	pixman_vector_t	v0, v1, v2;
+	v0.vector[0] = pixman_int_to_fixed(x);
+	v0.vector[1] = pixman_int_to_fixed(y);
+	v0.vector[2] = pixman_fixed_1;
+
+	v1.vector[0] = pixman_int_to_fixed(x+1);
+	v1.vector[1] = pixman_int_to_fixed(y);
+	v1.vector[2] = pixman_fixed_1;
+
+	v2.vector[0] = pixman_int_to_fixed(x);
+	v2.vector[1] = pixman_int_to_fixed(y+1);
+	v2.vector[2] = pixman_fixed_1;
+	fetchPixelProc   fetch;
+	fetchFromRegionProc fetchFromRegion;
+	pixman_box16_t *box = NULL;
+
+	/* initialize the two function pointers */
+	fetch = fetchPixelProcForPicture(pict);
+
+	assert(pict->common.transform);
+
+	pixman_transform_point_3d (pict->common.transform, &v0);
+	pixman_transform_point_3d (pict->common.transform, &v1);
+	pixman_transform_point_3d (pict->common.transform, &v2);
+	/*printf("initial %d %d, %d %d, %d %d\n",
+					v0.vector[0] >> 16,
+					v0.vector[1] >> 16,
+					v1.vector[0] >> 16,
+					v1.vector[1] >> 16,
+					v2.vector[0] >> 16,
+					v2.vector[1] >> 16
+
+					);
+	*/
+	//XXX: not strictly useful if they are both true, but that should only happen with a degenerate matrix
+	//printf("%x %x %x\n", v0.vector[1], v1.vector[1], v2.vector[1]);
+	/* Setup the transformed vectors so that v0 is always at the top
+	   and v2 is to the left of v1. */
+	if (v0.vector[1] >= v1.vector[1] && v0.vector[1] >= v2.vector[1]) {
+	    //printf("flip\n");
+	    // flip v0 vector
+	    // XXX: elaborate
+	    v0.vector[1] = v2.vector[1] - (v0.vector[1] - v1.vector[1]);
+	    v0.vector[0] = v2.vector[0] - (v0.vector[0] - v1.vector[0]);
+	} else if (v0.vector[1] > v1.vector[1]) {
+	    //printf("swap one\n");
+	    pixman_vector_t tmp = v1;
+	    v1 = v0;
+	    v0 = tmp;
+	    // adjust the location of the other point
+	    v2.vector[0] += v0.vector[0] - v1.vector[0];
+	    v2.vector[1] += v0.vector[1] - v1.vector[1];
+	} else if (v0.vector[1] > v2.vector[1]) {
+	    //printf("swap two\n");
+	    pixman_vector_t tmp = v2;
+	    v2 = v0;
+	    v0 = tmp;
+	    // adjust the location of the other point
+	    v1.vector[0] += v0.vector[0] - v2.vector[0];
+	    v1.vector[1] += v0.vector[1] - v2.vector[1];
+	}
+
+	if (v1.vector[0] < v2.vector[0]) {
+	    //XXX this check is not sufficient
+	    pixman_vector_t tmp = v2;
+	    v2 = v1;
+	    v1 = tmp;
+	}
+
+	/* these deltas are not used elsewhere */
+	pixman_fixed_t deltax = v1.vector[0] - v0.vector[0];
+	pixman_fixed_t deltay = v1.vector[1] - v0.vector[1];
+	pixman_fixed_t deltax2 = v2.vector[0] - v0.vector[0];
+	pixman_fixed_t deltay2 = v2.vector[1] - v0.vector[1];
+
+	pixman_fixed_t slope = pixman_fixed_div(deltax, deltay);
+	pixman_fixed_t slope2 = pixman_fixed_div(deltax2, deltay2);
+
+	/* we don't handle non-affine transformations at all */
+	assert(affine);
+
+	if (pict->common.repeat == PIXMAN_REPEAT_NORMAL) {
+	    if(pixman_region_n_rects (pict->common.src_clip) == 1)
+		fetchFromRegion = fbFetchFromNoRegion;
+	    else
+		fetchFromRegion = fbFetchFromNRectangles;
+
+#define FETCH					int ix = MOD(x, pict->width); \
+						int iy = MOD(y, pict->height); \
+						tl = fetchFromRegion(pict, ix, iy, buffer, fetch, box);
+#include "postscript-resize.c"
+#undef FETCH
+	} else if (pict->common.repeat == PIXMAN_REPEAT_PAD) {
+	    if(pixman_region_n_rects (pict->common.src_clip) == 1)
+		fetchFromRegion = fbFetchFromNoRegion;
+	    else
+		fetchFromRegion = fbFetchFromNRectangles;
+
+#define FETCH					int ix = CLIP(x, 0, pict->width-1); \
+						int iy = CLIP(y, 0, pict->height-1); \
+						tl = fetchFromRegion(pict, ix, iy, buffer, fetch, box);
+#include "postscript-resize.c"
+#undef FETCH
+
+        } else {
+	    if(pixman_region_n_rects (pict->common.src_clip) == 1) {
+		box = &(pict->common.src_clip->extents);
+		fetchFromRegion = fbFetchFromOneRectangle;
+	    } else {
+		fetchFromRegion = fbFetchFromNRectangles;
+	    }
+
+#define FETCH	tl = fetchFromRegion(pict, x, y, buffer, fetch, box);
+#include "postscript-resize.c"
+#undef FETCH
+        }
     } else if (pict->common.filter == PIXMAN_FILTER_BILINEAR	||
 	       pict->common.filter == PIXMAN_FILTER_GOOD	||
 	       pict->common.filter == PIXMAN_FILTER_BEST)
diff --git a/pixman/pixman.h b/pixman/pixman.h
index 2965acd..7634fa8 100644
--- a/pixman/pixman.h
+++ b/pixman/pixman.h
@@ -185,7 +185,8 @@ typedef enum
     PIXMAN_FILTER_BEST,
     PIXMAN_FILTER_NEAREST,
     PIXMAN_FILTER_BILINEAR,
-    PIXMAN_FILTER_CONVOLUTION
+    PIXMAN_FILTER_CONVOLUTION,
+    PIXMAN_FILTER_POSTSCRIPT
 } pixman_filter_t;
 
 typedef enum
diff --git a/pixman/postscript-resize.c b/pixman/postscript-resize.c
new file mode 100644
index 0000000..cea2ceb
--- /dev/null
+++ b/pixman/postscript-resize.c
@@ -0,0 +1,134 @@
+#define dprintf(a, ...)
+#define fixed_mul(a, b) ((int32_t)((((int64_t)a)*(b))>>32))
+	    for (i = 0; i < width; ++i) {
+		if (!mask || mask[i] & maskBits)
+		    {
+			if (!v.vector[2]) {
+			    *(buffer + i) = 0;
+			} else {
+
+			    /* assert(v2.vector[1] + v1.vector[1] > 2 * v0.vector[1]); */
+			    int y1 = (v2.vector[1] - (v0.vector[1] - v1.vector[1])) >> 16;
+			    int y0 = (v0.vector[1]) >> 16;
+			    /* claim(y1 > y0);
+			     * proof:
+			     * v2.vector[1] + v1.vector[1] > 2 * v0.vector[1]
+			     * v2.vector[1] + v1.vector[1] - v0.vector[1] > v0.vector[1] */
+
+			    int ry = v1.vector[1]>>16;
+			    int ly = v2.vector[1]>>16;
+
+			    pixman_fixed_t rx_start_y = v0.vector[1] & 0xffff;
+			    pixman_fixed_t rx_start_x = v0.vector[0];
+			    pixman_fixed_t rx_slope = slope;
+			    pixman_fixed_t lx_start_y = v0.vector[1] & 0xffff;
+			    pixman_fixed_t lx_start_x = v0.vector[0];
+			    pixman_fixed_t lx_slope = slope2;
+
+			    dprintf("**--YY: %d: %d, %d  %d %d\n",y1-y0, y0, y1, ry, ly);
+			    dprintf("v0: %x %x\n", v0.vector[0], v0.vector[1]);
+			    dprintf("v1: %x %x\n", v1.vector[0], v1.vector[1]);
+			    dprintf("v2: %x %x\n", v2.vector[0], v2.vector[1]);
+			    dprintf("slope,: %x %x\n", slope, slope2);
+
+			    uint32_t c1_accum, c2_accum, c3_accum, c4_accum;
+				/* note watch for including pixels in more than one sample.
+				 * i.e. use a one-sided interval */
+			    c1_accum = c2_accum = c3_accum = c4_accum = 0;
+			    int y; // this y shadows
+			    int total = 0;
+			    dprintf("%d %d, %d %d, %d %d\n",
+				    v0.vector[0] >> 16,
+				    v0.vector[1] >> 16,
+				    v1.vector[0] >> 16,
+				    v1.vector[1] >> 16,
+				    v2.vector[0] >> 16,
+				    v2.vector[1] >> 16
+
+					);
+			    dprintf("y1: %d %d\n", y0, y1);
+			    dprintf("%x %x\n", slope, slope2);
+			    for (y = y0; y <= y1; y++) {
+				int x;
+				pixman_fixed_t rx, lx;
+
+				if (y == ry) {
+				    rx_start_x = v1.vector[0];
+				    rx_start_y = v1.vector[1] & 0xffff;
+				    rx_slope = slope2;
+				}
+
+				if (y == ly) {
+				    lx_start_x = v2.vector[0];
+				    lx_start_y = v2.vector[1] & 0xffff;
+				    lx_slope = slope;
+				}
+
+				/* it may be possible to move these
+				 * multiplications into the conditions above
+				 * and use an addition the rest of the time */
+				rx = rx_start_x + fixed_mul(rx_start_y, rx_slope);
+				rx_start_y += (1 << 16);
+
+				lx = lx_start_x + fixed_mul(lx_start_y, lx_slope);
+				lx_start_y += (1 << 16);
+
+				for (x = (lx >> 16); x <= (rx >> 16); x++) {
+				    uint32_t tl;
+				    FETCH
+				    dprintf("x,y -> %d %d %x\n", x, y, tl);
+				    c1_accum += FbGet8(tl,24);
+				    c2_accum += FbGet8(tl,16);
+				    c3_accum += FbGet8(tl,8);
+				    c4_accum += FbGet8(tl,0);
+				    total++;
+				}
+			    }
+			    dprintf("total: %d\n", total);
+			    if (!total) {
+				printf("y0: %d, y1: %d\n", y0, y1);
+				printf("%x %x, %x %x, %x %x\n",
+					v0.vector[0] >> 16,
+					v0.vector[1] >> 16,
+					v1.vector[0] >> 16,
+					v1.vector[1] >> 16,
+					v2.vector[0] >> 16,
+					v2.vector[1] >> 16
+
+					);
+				{
+				    int i,j;
+				    for (i=0; i<3; i++) {
+					for (j=0; j<3; j++) {
+					    printf("%x ", pict->common.transform->matrix[i][j]);
+					}
+					printf("\n");
+				    }
+				}
+
+				assert(total);
+			    }
+			    /* use component numbers instead of names because we don't know
+			     * which will actually be red etc. */
+			    c1_accum /= total;
+			    c2_accum /= total;
+			    c3_accum /= total;
+			    c4_accum /= total;
+			    dprintf("total %d\n", total);
+			    uint32_t r = (c1_accum << 24) | ((c2_accum<<16)&0xff0000) | ((c3_accum<<8)&0xff00) | (c4_accum&0xff);
+
+			    *(buffer + i) = r;
+			}
+		    }
+
+                    v0.vector[0] += unit.vector[0];
+                    v0.vector[1] += unit.vector[1];
+                    v0.vector[2] += unit.vector[2];
+		    v1.vector[0] += unit.vector[0];
+                    v1.vector[1] += unit.vector[1];
+                    v1.vector[2] += unit.vector[2];
+                    v2.vector[0] += unit.vector[0];
+                    v2.vector[1] += unit.vector[1];
+                    v2.vector[2] += unit.vector[2];
+                }
+


More information about the cairo mailing list