[cairo] [pixman] fix pixel offsets in rotated image sources

Bertram Felgenhauer bertram.felgenhauer at googlemail.com
Fri Sep 9 14:29:38 PDT 2005


Hi,

I've had a look at bug 2488 (pixel offsets in rotated image
patterns). As far as I can see, there are two separate issues here,
both connected to the exact coordinates of pixels in screen and image
space. The discussion below assumes that pixel 0,0 covers the area
[0,1]x[0,1] in the corresponding plane, which is consistent with cairos
rendering behaviour (is that documented anywhere?) ([0,1] denotes
the interval with ends 0 and 1)

1st issue. The rendering code uses (0, 0) as a reference point for the
pixel (0,0). This leads to rounding errors for transformations that are
close to the unit transformation - consider the identity transform and
a translation by (-epsilon, -epsilon) - and a bias to the upper left
of the pixel when rendering. A better choice seems to be to sample at
the center of the pixel, which has coordinates (1/2, 1/2).

2nd issue. The second issue lies in how bilinear interpolation is
done. The formulas used by pixman give the pixel 0,0 the maximum
influence at (0,0). For the same reasons as above, this maximum is
better put at (1/2,1/2). With this change, bilinear filtering with the
identity transformation is the same as truncating (nearest
filter). Note that the PIXMAN_FILTER_NEAREST implementation truncates
the coordinates and thus maps the whole [0,1]x[0,1] area to the pixel
(0,0) - this problem does not appear here.

There are two attachments to this mail, one that implements another
testcase for this problem (I implemented that before I found the
pixman-rotate testcase) and a proposed fix to pixman.

I've tested the fix - also with scaled source images (for which there
is no test case) - and got good results, in particular it produces no
new failures and solves the targeted test case. I also tested scaled
images and got the results I expected.

Comments are welcome.

With kind regards,

Bertram
-------------- next part --------------
? test/rotate-surface-pattern-ref.png
Index: test/.cvsignore
===================================================================
RCS file: /cvs/cairo/cairo/test/.cvsignore,v
retrieving revision 1.51
diff -u -r1.51 .cvsignore
--- test/.cvsignore	31 Aug 2005 16:00:02 -0000	1.51
+++ test/.cvsignore	9 Sep 2005 12:28:46 -0000
@@ -44,6 +44,7 @@
 pixman-rotate
 rectangle-rounding-error
 rel-path
+rotate-surface-pattern
 scale-source-surface-paint
 select-font-no-show-text
 self-copy
Index: test/Makefile.am
===================================================================
RCS file: /cvs/cairo/cairo/test/Makefile.am,v
retrieving revision 1.91
diff -u -r1.91 Makefile.am
--- test/Makefile.am	31 Aug 2005 16:00:02 -0000	1.91
+++ test/Makefile.am	9 Sep 2005 12:28:46 -0000
@@ -32,6 +32,7 @@
 path-data			\
 pixman-rotate			\
 rectangle-rounding-error	\
+rotate-surface-pattern		\
 scale-source-surface-paint	\
 select-font-no-show-text	\
 self-copy			\
@@ -106,6 +107,7 @@
 paint-with-alpha-ref.png		\
 path-data-ref.png			\
 pixman-rotate-ref.png			\
+rotate-surface-pattern.png		\
 rectangle-rounding-error-ref.png	\
 romedalen.png				\
 self-copy-ref.png			\
@@ -220,6 +222,7 @@
 pdf_clip_LDADD = $(LDADDS)
 ps_surface_LDADD = $(LDADDS)
 pixman_rotate_LDADD = $(LDADDS)
+rotate_surface_pattern_LDADD = $(LDADDS)
 rectangle_rounding_error_LDADD = $(LDADDS)
 scale_source_surface_paint_LDADD = $(LDADDS)
 select_font_no_show_text_LDADD = $(LDADDS)
Index: test/rotate-surface-pattern.c
===================================================================
RCS file: test/rotate-surface-pattern.c
diff -N test/rotate-surface-pattern.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ test/rotate-surface-pattern.c	9 Sep 2005 12:28:47 -0000
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2005 Bertram Felgenhauer
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of
+ * the author not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. The author makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE AUTHOR. BE LIABLE FOR ANY SPECIAL,
+ * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+ * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: Bertram Felgenhauer <int-e at gmx.de>
+ */
+
+#include "cairo-test.h"
+
+#define STEPS 16
+
+/* See also:
+ *      https://bugs.freedesktop.org/show_bug.cgi?id=2488
+ */                                                                             
+
+cairo_test_t test = {
+    "rotate-surface-pattern",
+    "This test demonstrates offsets introduced by rotating surface patterns.",
+    128, 128
+};
+
+static void
+draw_image(cairo_t *cr, cairo_surface_t *image, int width, int height)
+{
+    cairo_rectangle(cr, 0, 0, width, height);
+    /* first mark target area */
+    cairo_set_source_rgba(cr, 0, 0, 1, 0.2);
+    cairo_fill_preserve(cr);
+    cairo_set_source_surface(cr, image, 0, 0);
+    cairo_fill(cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    int i;
+    cairo_surface_t *image =
+        cairo_surface_create_similar(cairo_get_target(cr),
+                                     CAIRO_CONTENT_COLOR_ALPHA,
+                                     16, 16);
+    cairo_t *icr = cairo_create(image);
+
+    /* prepare image */
+    cairo_set_line_width(icr, 1);
+    cairo_set_source_rgb(icr, 0, 0, 0);
+    cairo_rectangle(icr, 0.5, 0.5, 15, 15);
+    cairo_stroke(icr);
+    cairo_destroy(icr);
+
+    cairo_rectangle(cr, 0, 0, 128, 128);
+    cairo_set_source_rgb(cr, 1, 1, 1);
+    cairo_fill(cr);
+    cairo_translate(cr, 64, 64);
+
+    /* draw it a few times */
+    for (i=0; i<16; i++) {
+        cairo_save(cr);
+        cairo_rotate(cr, 2*M_PI*i/16); 
+        cairo_translate(cr, 16, 16);
+        draw_image(cr, image, 16, 16);
+        cairo_translate(cr, 8.5, 8.5);
+        draw_image(cr, image, 16, 16);
+        cairo_restore(cr);
+    }
+
+    /* draw the two extreme cases again in the center */
+    for (i=0; i<2; i++) {
+        cairo_save(cr);
+        cairo_rotate(cr, 2*M_PI*i/2);
+        draw_image(cr, image, 16, 16);
+        cairo_restore(cr);
+    }
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test, draw);
+}
-------------- next part --------------
? test/rotate-surface-pattern-ref.png
? test/testsvg-diff
? test/testsvg-output
Index: pixman/src/fbcompose.c
===================================================================
RCS file: /cvs/cairo/cairo/pixman/src/fbcompose.c,v
retrieving revision 1.3
diff -u -r1.3 fbcompose.c
--- pixman/src/fbcompose.c	28 Aug 2005 02:32:57 -0000	1.3
+++ pixman/src/fbcompose.c	9 Sep 2005 12:58:18 -0000
@@ -2724,8 +2724,9 @@
         xFixed_32_32 l;
         xFixed_48_16 dx, dy, a, b, off;
 
-        v.vector[0] = IntToxFixed(x);
-        v.vector[1] = IntToxFixed(y);
+        /* reference point is the center of the pixel */
+        v.vector[0] = IntToxFixed(x) + xFixed1/2;
+        v.vector[1] = IntToxFixed(y) + xFixed1/2;
         v.vector[2] = xFixed1;
         if (pict->transform) {
             if (!PictureTransformPoint3d (pict->transform, &v))
@@ -2791,8 +2792,9 @@
 
         if (pict->transform) {
             PictVector v;
-            v.vector[0] = IntToxFixed(x);
-            v.vector[1] = IntToxFixed(y);
+            /* reference point is the center of the pixel */
+            v.vector[0] = IntToxFixed(x) + xFixed1/2;
+            v.vector[1] = IntToxFixed(y) + xFixed1/2;
             v.vector[2] = xFixed1;
             if (!PictureTransformPoint3d (pict->transform, &v))
                 return;
@@ -2913,8 +2915,9 @@
     x += xoff;
     y += yoff;
 
-    v.vector[0] = IntToxFixed(x);
-    v.vector[1] = IntToxFixed(y);
+    /* reference point is the center of the pixel */
+    v.vector[0] = IntToxFixed(x) + xFixed1/2;
+    v.vector[1] = IntToxFixed(y) + xFixed1/2;
     v.vector[2] = xFixed1;
 
     /* when using convolution filters one might get here without a transform */
@@ -3020,6 +3023,12 @@
             }
         }
     } else if (pict->filter == PIXMAN_FILTER_BILINEAR || pict->filter == PIXMAN_FILTER_GOOD || pict->filter == PIXMAN_FILTER_BEST) {
+        /* adjust vector for maximum contribution at 0.5, 0.5 of each texel. */
+        v.vector[0] -= v.vector[2]/2;
+        v.vector[1] -= v.vector[2]/2;
+        unit.vector[0] -= unit.vector[2]/2;
+        unit.vector[1] -= unit.vector[2]/2;
+
         if (pict->repeat == RepeatNormal) {
             if (PIXREGION_NUM_RECTS(pict->pCompositeClip) == 1) {
                 box = pict->pCompositeClip->extents;


More information about the cairo mailing list