[cairo] Offset artifacts using push/pop group api along with clip

T Rowley tor at cs.brown.edu
Thu Aug 10 11:35:18 PDT 2006


In mozilla I'm implementing complex svg clipPaths by rendering both the 
geometry and clipPath in seperate push/pop groups, then combining them 
with cairo_mask_surface.  This works fine when the whole surface is 
being drawn, but when mozilla needs to do a partial redraw, I'm seeing 
offset artifacts.

The attached testcase reproduces the problem as far as I can tell in the 
cairo test framework.  It also adds a 
cairo_surface_pattern_get_surface() API to get access to a surface 
inside a pattern.
-------------- next part --------------
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index ebdb13638b18944b57d76b687a800e5dfd00d857..268e0faab03f5bdcd44c5d08716d76308f1885d1 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -778,6 +778,26 @@ cairo_pattern_get_matrix (cairo_pattern_
     *matrix = pattern->matrix;
 }
 
+/**
+ * cairo_surface_pattern_get_surface:
+ * @pattern: a #cairo_pattern_t
+ *
+ * Returns the underlying surface of a surface pattern, null if
+ * non-surface pattern.
+ **/
+cairo_surface_t *
+cairo_surface_pattern_get_surface (cairo_pattern_t *pattern)
+{
+    cairo_surface_t *surface;
+
+    if (pattern == NULL || pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
+	return NULL;
+
+    surface = ((cairo_surface_pattern_t *) pattern)->surface;
+    cairo_surface_reference(surface);
+    return surface;
+}
+
 void
 cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
 {
diff --git a/src/cairo.h b/src/cairo.h
index bac27fdb588048f812a2fd37f2f468474dba2423..a8c282c7395ace3a5e7a9cbde0b3a52cd78a5d3c 100644
--- a/src/cairo.h
+++ b/src/cairo.h
@@ -1508,6 +1508,9 @@ cairo_public void
 cairo_pattern_get_matrix (cairo_pattern_t *pattern,
 			  cairo_matrix_t  *matrix);
 
+cairo_public cairo_surface_t *
+cairo_surface_pattern_get_surface (cairo_pattern_t *pattern);
+
 /**
  * cairo_extend_t
  * @CAIRO_EXTEND_NONE: pixels outside of the source pattern
diff --git a/test/Makefile.am b/test/Makefile.am
index 968e0908b67e5a319bf9f8978e6c64818ce33848..21c208436fdd8b024e26cd666a6c7116dc4a144c 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -82,7 +82,8 @@ unbounded-operator		\
 user-data			\
 rel-path                        \
 push-group			\
-zero-alpha
+zero-alpha			\
+push-group-clip
 
 # Then we have a collection of tests that are only run if certain
 # features are compiled into cairo
@@ -268,6 +269,8 @@ push-group-ref.png					\
 push-group-rgb24-ref.png				\
 push-group-svg-argb32-ref.png				\
 push-group-svg-rgb24-ref.png				\
+push-group-clip-ref.png					\
+push-group-clip-rgb24-ref.png				\
 rectangle-rounding-error-ref.png			\
 rel-path-ref.png					\
 rel-path-rgb24-ref.png					\
diff --git a/test/push-group-clip-ref.png b/test/push-group-clip-ref.png
new file mode 100644
index 0000000000000000000000000000000000000000..d679ee5f279f962222f75e2ce7a1746af2cf9d82
GIT binary patch
literal 746
zc-rd>@N?(olHy`uVBq!ia0vp^DIm<j1|$m}O$`B3Y)RhkE)4%caKYZ?lYt`aC7!;n
z?AJKB1cihSUN?Kqz`)ew>EaktG3V`_y?MfpB5e=PKU?15vWUTDqMVk7z at mN;tEhL1
zss)TPRSVA8Ubw{AB&EppqKI*#KtO<s$RCS#EulpW=ii*Moc;B;$Gc6F&s3&bZYvLG
zVn7B7r(!-`I_^|_|Khg!m#?StS5DY_K6$a)iHnaX>;1~LRrd0BZrbsreb!ImyI+HU
zIZhF4W3#NXxvm?3*)FgxOZ13p{c7pDxcCkum(vP*Kc(+}*=(iYwf350%%|{MUoQXh
zn<8Zk)ZbrHe^Xxd)ch(|%PT$go!MWw9!;vh#(K|Y+AGN;s`oFJo$ME0T0SA(>f!Y-
z^E6Jsk_DQ(%6e)0n!G6;3fyP^ZYt~C)YAd9Ao=cU%bc%^Tv&gu at U9E8@BCyR2((If
z?+V+J)J12M>u*kryC#>VEv))U@{`~7c0I$t8WSDGX8o3|RX(-fg{|=9`d7}~scO=y
zUrb#lt;^Vc)X40r#zRN5E!VR&XD3bhppb2qv2%{Eu2;o`(zn~=E-e#Lwee8j{KkI4
z)DH^3qw at DYan4LzD}Gi;?ukRT?)2FqJerCp=9xw5n(maC)c01}`SiSK^NEI1wIxY6
zjbCdSc{KBj>sH6 at ZQa+l=i0LghnN4H^>O=&^b-yGD|D*;5*H=${NTF2DP2TM%j2mh
z-!$9#1%ifJ{>+=+++5PZ5mdNnPU?A8-}e%t%O^#B^9lOO7gVunYwCIB*z*#t)0HE>
z>9{Qn?>WR>rc=$g_ at cJQLDk%8w#|Vr1s6Y9<bV3!{})^O0t+Iyq`t465_L>tM`1(i
z`|2%GM>TeAZA*P$d8MsQW6#f?)c56I+Ky=Kv7May{%gbv&cH%u<J0%lQBnzmMC1K^
V1~M!A!~+#TTu)a&mvv4FO#nl*NNxZC

diff --git a/test/push-group-clip-rgb24-ref.png b/test/push-group-clip-rgb24-ref.png
new file mode 100644
index 0000000000000000000000000000000000000000..e7edd8141e15f5753ea94244e7315bd1341a8c05
GIT binary patch
literal 709
zc-rd>@N?(olHy`uVBq!ia0vp^DIm<j1SJ1AFfjuuwj^(N7l!{JxM1({$v_eI5>H=O
z_G=tmf<i(EubaJQU|>r3ba4!+nDh4bMn922kz*h8#g|S{-5B7s(KaMNz}de^cI|tI
zm39or<uoeUqpS=fq>~oP@;f9=Ih1tWDRHNmiAbB}_j|>~@6Och_c^{d<-Gmulkd*)
zIJlsID#!9O1~!M at 75}L!xjOH=Nvme^U3vQrTbd{T`_|PZ+F;GY_wk$Nzi%7EbJqNR
zEVABx#+P;BQEM1t9$e*)`Mx^5XbnTnv1d%nY+p?k)@FEqW7hHc`@Wp&5pAg5Q~%|?
z?5nBrulbty)yzB5FWNA_;%4{x)oL4=3R><iQ+ at kBRrIx3@#`PAKE{Os#d^%ULUZ4x
zN at TY^<kH)H=d?i>1K$U~?HlIq%}K63{z>>w$+qdaYZ!7K`|RD8y0`7w|3Yc`tJzCa
z_nx^Q8NWer=VIHvXOhnv)U at 5d`zBRRn<4v$*#4EQd*7UoWC$yi-0%77eW=PciEz)~
zOFqT9CiNQl-qkYwt|gQ$IJxI;Q0{uAjhlG)#2@%C+G%<)>DW8|hKRjF(XL6phF*80
z at -;=eN?Qvr?OWx|C|au_`d*0Z+0se--gT6-$$pOb7{BD`?#5|<Enf?7OPUs5Kd<V<
zpShx+AJ4Dwmp{2J<jna~k8G=KZb!W07JFn{E_68Pm3QJJ+Y+tA5xctAJhClhTOG9P
z^!G=$d2*{Gx}=?tRA0+cTPm_$rt|v(u}|Kbd1=D$HG7{N&)Vf1w8wqR<j7So*<wD<
zJ0Ie=a#{55qtzDKCzsBWlM;A;lFLK;>KBWyivCcy9JQm(evfQ}4Fi{o^k3-w?x3n^
m{8H|m$otF=6tF_-Kf`r{u1Bt)4}AxwLIzJ)KbLh*2~7Y6s7jas

diff --git a/test/push-group-clip.c b/test/push-group-clip.c
new file mode 100644
index 0000000000000000000000000000000000000000..d6aed8b8fd299e922a34cb344d6549b2099869c0
--- /dev/null
+++ b/test/push-group-clip.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2005 Mozilla Corporation
+ *
+ * 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
+ * Mozilla Corporation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission. Mozilla Corporation makes no representations about the
+ * suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * MOZILLA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL MOZILLA CORPORATION 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: Vladimir Vukicevic <vladimir at pobox.com>
+ */
+
+#include "cairo-test.h"
+
+#define UNIT_SIZE 100
+
+#define WIDTH UNIT_SIZE
+#define HEIGHT UNIT_SIZE
+
+static cairo_test_draw_function_t draw;
+
+cairo_test_t test = {
+    "push-group-clip",
+    "Verify that cairo_push_group works with clipping.",
+    WIDTH, HEIGHT,
+    draw
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_pattern_t *mask;
+    cairo_surface_t *maskSurface;
+
+    cairo_save (cr);
+
+    /* NOTE: #if 0 out the follow to see working mask clipping */
+#if 1
+    /* Mozilla style partial redraws are setup in this sort of fashion */
+    cairo_translate (cr, -UNIT_SIZE/2, 0);
+
+    cairo_rectangle (cr, UNIT_SIZE/2, 0, UNIT_SIZE/2, UNIT_SIZE);
+    cairo_clip (cr);
+    cairo_new_path (cr);
+#endif
+
+    /* start a group */
+    cairo_push_group (cr);
+
+    /* draw diamond */
+    cairo_move_to (cr, UNIT_SIZE / 2, 0);
+    cairo_line_to (cr, UNIT_SIZE    , UNIT_SIZE / 2);
+    cairo_line_to (cr, UNIT_SIZE / 2, UNIT_SIZE);
+    cairo_line_to (cr, 0            , UNIT_SIZE / 2);
+    cairo_close_path (cr);
+    cairo_set_source_rgba (cr, 0, 0, 1, 1);
+    cairo_fill (cr);
+
+    /* draw circle */
+    cairo_arc (cr,
+	       UNIT_SIZE / 2, UNIT_SIZE / 2,
+	       UNIT_SIZE / 3.5,
+	       0, M_PI * 2);
+    cairo_set_source_rgba (cr, 1, 0, 0, 1);
+    cairo_fill (cr);
+
+    cairo_pop_group_to_source (cr);
+
+    /* NOTE: toggle this #if to see normal clipping instead of masking */
+#if 1
+    /* start mask group */
+    cairo_push_group (cr);
+
+    /* draw rect - horizontal clipping bar*/
+    cairo_rectangle (cr,
+		     0, UNIT_SIZE/4,
+		     UNIT_SIZE, UNIT_SIZE/2);
+    cairo_set_source_rgba (cr, 1, 1, 1, 1);
+    cairo_fill (cr);
+
+    mask = cairo_pop_group (cr);
+    maskSurface = cairo_surface_pattern_get_surface(mask);
+
+    cairo_mask_surface (cr, maskSurface, 0, 0);
+
+    cairo_surface_destroy (maskSurface);
+    cairo_pattern_destroy (mask);
+#else
+    cairo_rectangle (cr,
+		     0, UNIT_SIZE/4,
+		     UNIT_SIZE, UNIT_SIZE/2);
+    cairo_clip (cr);
+    cairo_new_path (cr);
+
+    cairo_paint (cr);
+#endif
+
+    cairo_restore (cr);
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+    return cairo_test (&test);
+}


More information about the cairo mailing list