Subject: [PATCH] clip: Unexpected behavier when we Mixing the even-odd and the winding on the small surface.

From: Gwang Yoon Hwang <[email-masked]>
Date: Tue, 11 Apr 2017 17:46:50 +0200

Whenever we are going to draw a text box with decorations, WebKit clips out the
in side of the text box to prevent bleeding while filling patterns.

However, Cairo doesn't apply clips properly if the follwing conditions are satisfied:

(1) Creates and apply a path for the clipping out which is not rectanglar.
(2) Creates and apply a path for the cliping in after the clip out operation.
(3) The filling area of the clipping out and the filling area of the clipping in
does not intersect on the target surface.
---
 test/Makefile.sources                              |   2 +
 test/clip-complex-shape-with-mixed-antialias.c     |  91 +++++++++++++++++++++
 test/clip-in-and-out-without-intersect.c           |  91 +++++++++++++++++++++
 ...clip-complex-shape-with-mixed-antialias.ref.png | Bin 0 -> 143 bytes
 .../clip-in-and-out-without-intersect.ref.png      | Bin 0 -> 149 bytes
 5 files changed, 184 insertions(+)
 create mode 100644 test/clip-complex-shape-with-mixed-antialias.c
 create mode 100644 test/clip-in-and-out-without-intersect.c
 create mode 100644 test/reference/clip-complex-shape-with-mixed-antialias.ref.png
 create mode 100644 test/reference/clip-in-and-out-without-intersect.ref.png

diff --git a/test/Makefile.sources b/test/Makefile.sources
index 5ead231..7026e49 100644
--- a/test/Makefile.sources
+++ b/test/Makefile.sources
@@ -45,6 +45,7 @@ test_sources = \
 	clip-all.c					\
 	clip-complex-bug61592.c				\
 	clip-complex-shape.c				\
+	clip-complex-shape-with-mixed-antialias.c	\
 	clip-contexts.c					\
 	clip-disjoint.c					\
 	clip-disjoint-hatching.c			\
@@ -61,6 +62,7 @@ test_sources = \
 	clip-fill-rule-pixel-aligned.c			\
 	clip-group-shapes.c				\
 	clip-image.c					\
+	clip-in-and-out-without-intersect.c		\
 	clip-intersect.c				\
 	clip-mixed-antialias.c				\
 	clip-nesting.c					\
diff --git a/test/clip-complex-shape-with-mixed-antialias.c b/test/clip-complex-shape-with-mixed-antialias.c
new file mode 100644
index 0000000..3c880f1
--- /dev/null
+++ b/test/clip-complex-shape-with-mixed-antialias.c
@@ -0,0 +1,91 @@
+#include "cairo-test.h"
+#include <cairo.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+static void create_tropezoid_path(cairo_t *cr, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
+{
+    cairo_move_to(cr, x1, y1);
+    cairo_line_to(cr, x2, y2);
+    cairo_line_to(cr, x3, y3);
+    cairo_line_to(cr, x4, y4);
+    cairo_move_to(cr, x1, y1);
+    cairo_close_path(cr);
+}
+
+static void create_rounded_rect_path(cairo_t *cr, double x, double y, double width, double height, double radius)
+{
+    double degrees = M_PI / 180.0;
+
+    cairo_move_to(cr, x, y);
+    cairo_arc(cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
+    cairo_arc(cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
+    cairo_arc(cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees);
+    cairo_arc(cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
+    cairo_close_path(cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_fill_rule_t saved_fill_rule;
+    cairo_set_line_width(cr, 1);
+
+    // Fill Background
+    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb(cr, 1, 1, 1);
+    cairo_paint(cr);
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+    // Base Clip
+    //cairo_rectangle(cr, -3, -3, 106, 42);
+    create_rounded_rect_path(cr, -3, -3, 106, 42, 2);
+    saved_fill_rule = cairo_get_fill_rule(cr);
+    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+    cairo_clip(cr);
+    cairo_set_fill_rule(cr, saved_fill_rule);
+
+    // Start of the clip 1 -- Clip out
+    double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+    cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+    cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
+
+    cairo_new_sub_path(cr);
+    create_rounded_rect_path(cr, -2, -2, 104, 40, 1);
+
+    saved_fill_rule = cairo_get_fill_rule(cr);
+    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_clip(cr);
+    cairo_set_fill_rule(cr, saved_fill_rule);
+    // End of the Clip 1
+
+    // Start of the clip 2
+    cairo_save(cr);
+    create_tropezoid_path(cr, -3, -3, 26.89, 26.89, 73.11, 26.89, 103, -3);
+
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+    saved_fill_rule = cairo_get_fill_rule(cr);
+    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+    cairo_clip(cr);
+    cairo_set_fill_rule(cr, saved_fill_rule);
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
+
+    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb(cr, 0, 0, 0);
+    cairo_rectangle(cr, -3, -3, 106, 42);
+    cairo_fill(cr);
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+    cairo_restore(cr);
+    // End of the clip 2
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_complex_shape_with_mixed_antialias,
+            "Test drawing through through an mixture of non rectanguler clips and antialiasing",
+            "clip", /* keywords */
+            "target=raster", /* requirements */
+            100, 18,
+            NULL, draw)
diff --git a/test/clip-in-and-out-without-intersect.c b/test/clip-in-and-out-without-intersect.c
new file mode 100644
index 0000000..c8d45dc
--- /dev/null
+++ b/test/clip-in-and-out-without-intersect.c
@@ -0,0 +1,91 @@
+#include "cairo-test.h"
+#include <cairo.h>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+static void create_tropezoid_path(cairo_t *cr, double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
+{
+    cairo_move_to(cr, x1, y1);
+    cairo_line_to(cr, x2, y2);
+    cairo_line_to(cr, x3, y3);
+    cairo_line_to(cr, x4, y4);
+    cairo_move_to(cr, x1, y1);
+    cairo_close_path(cr);
+}
+
+static void create_rounded_rect_path(cairo_t *cr, double x, double y, double width, double height, double radius)
+{
+    double degrees = M_PI / 180.0;
+
+    cairo_move_to(cr, x, y);
+    cairo_arc(cr, x + width - radius, y + radius, radius, -90 * degrees, 0 * degrees);
+    cairo_arc(cr, x + width - radius, y + height - radius, radius, 0 * degrees, 90 * degrees);
+    cairo_arc(cr, x + radius, y + height - radius, radius, 90 * degrees, 180 * degrees);
+    cairo_arc(cr, x + radius, y + radius, radius, 180 * degrees, 270 * degrees);
+    cairo_close_path(cr);
+}
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+    cairo_fill_rule_t saved_fill_rule;
+    cairo_set_line_width(cr, 1);
+    cairo_translate(cr, -11, -10);
+
+    // Fill Background
+    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb(cr, 1, 1, 1);
+    cairo_paint(cr);
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+
+    // Base Clip
+    create_rounded_rect_path(cr, 8, 10, 106, 42, 2);
+    saved_fill_rule = cairo_get_fill_rule(cr);
+    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+    cairo_clip(cr);
+    cairo_set_fill_rule(cr, saved_fill_rule);
+
+    // Start of the clip 1 -- Clip out
+    double x1 = 0, y1 = 0, x2 = 0, y2 = 0;
+    cairo_clip_extents(cr, &x1, &y1, &x2, &y2);
+    cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
+
+    cairo_new_sub_path(cr);
+    create_rounded_rect_path(cr, 9, 11, 105, 40, 1);
+
+    saved_fill_rule = cairo_get_fill_rule(cr);
+    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_EVEN_ODD);
+    cairo_clip(cr);
+    cairo_set_fill_rule(cr, saved_fill_rule);
+    // End of the Clip 1
+
+    // Start of the clip 2
+    cairo_save(cr);
+    create_tropezoid_path(cr, 8, 52, 37.89, 22.11, 84.11, 22.11, 114, 52);
+
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_NONE);
+    saved_fill_rule = cairo_get_fill_rule(cr);
+    cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
+    cairo_clip(cr);
+    cairo_set_fill_rule(cr, saved_fill_rule);
+    cairo_set_antialias(cr, CAIRO_ANTIALIAS_DEFAULT);
+
+    cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+    cairo_set_source_rgb(cr, 0, 0, 0);
+    cairo_rectangle(cr, 11, 10, 85, 41);
+    cairo_fill(cr);
+    cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+    cairo_restore(cr);
+    // End of the clip 2
+
+    return CAIRO_TEST_SUCCESS;
+}
+
+CAIRO_TEST (clip_in_and_out_without_intersect,
+            "Test drawing with a non rectanguler clipping out and a clip which does not intersect on the surface",
+            "clip", /* keywords */
+            "target=raster", /* requirements */
+            85, 41,
+            NULL, draw)
diff --git a/test/reference/clip-complex-shape-with-mixed-antialias.ref.png b/test/reference/clip-complex-shape-with-mixed-antialias.ref.png
new file mode 100644
index 0000000000000000000000000000000000000000..099a64aa87e17ed2416723f1737eaacbfe591620
GIT binary patch
literal 143
zcmeAS@N?(olHy`uVBq!ia0vp^DL^d5!2~3ge!sa7q}Y<Y-CY>|gW!U_%O?XxTs&PI
zLn2z=UO32mKtbTZhD(3T?ZOoFTT=W5QqP__KJ$Os0q-+h_@UXkG-kyhuLXaB<}rA>
L`njxgN@xNAyX-Qp

literal 0
HcmV?d00001

diff --git a/test/reference/clip-in-and-out-without-intersect.ref.png b/test/reference/clip-in-and-out-without-intersect.ref.png
new file mode 100644
index 0000000000000000000000000000000000000000..7559a2ac1ef30bac4b7baa09af5692fec6aa1ebd
GIT binary patch
literal 149
zcmeAS@N?(olHy`uVBq!ia0vp^p+Kz3!2~2vmw(z0q}Y<Y-CY>|gW!U_%O?XxygXeT
zLoyoQo;PG<P~bUWaA5y)#bkFW*0*zKGFRVbJpF6?*ZkM_ukBx3AN_yZzudpKaR@B?
ZU+*xL+5cy>**l=A44$rjF6*2UngFDpL6iUh

literal 0
HcmV?d00001

-- 
2.9.3