[cairo] [PATCH] spans: Correct handling of clips having mixed antialias

Taekyun Kim podain77 at gmail.com
Mon Oct 17 04:38:18 PDT 2011


From: Taekyun Kim <tkq.kim at samsung.com>

If clip paths in a clip have different antialias, the clip cannot
be reduced to a single polygon. So we have to first intersect each
clip paths having same antialias and then composite them to the
clip surface. If we know that any of them is reduced to a empty
polygon, we can skip remaining clip paths. Further work should be
detect all-clipped cases somewhat ealier and cheaply.
---
 src/cairo-spans-compositor.c |  223 ++++++++++++++++++++++++------------------
 1 files changed, 128 insertions(+), 95 deletions(-)

diff --git a/src/cairo-spans-compositor.c b/src/cairo-spans-compositor.c
index 468f338..d8bc836 100644
--- a/src/cairo-spans-compositor.c
+++ b/src/cairo-spans-compositor.c
@@ -77,19 +77,95 @@ clip_and_composite_polygon (const cairo_spans_compositor_t	*compositor,
 			    cairo_polygon_t			*polygon,
 			    cairo_fill_rule_t			 fill_rule,
 			    cairo_antialias_t			 antialias);
+
+static cairo_int_status_t
+get_clip_polygon (const cairo_clip_path_t *clip_path,
+		  cairo_antialias_t antialias,
+		  cairo_polygon_t *polygon,
+		  cairo_fill_rule_t *fill_rule)
+{
+    cairo_int_status_t status;
+    cairo_bool_t is_first_polygon = TRUE;
+
+    while (clip_path) {
+	if (clip_path->antialias == antialias) {
+	    if (is_first_polygon) {
+		status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+							    clip_path->tolerance,
+							    polygon);
+		*fill_rule = clip_path->fill_rule;
+		polygon->limits = NULL;
+		polygon->num_limits = 0;
+		is_first_polygon = FALSE;
+	    } else if (polygon->num_edges == 0) {
+		/* Intersecting with empty polygon will generate empty polygon. */
+		return CAIRO_INT_STATUS_NOTHING_TO_DO;
+	    } else {
+		cairo_polygon_t next;
+
+		_cairo_polygon_init (&next, NULL, 0);
+		status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
+							    clip_path->tolerance,
+							    &next);
+		if (likely (status == CAIRO_INT_STATUS_SUCCESS))
+		    status = _cairo_polygon_intersect (polygon, *fill_rule,
+						       &next, clip_path->fill_rule);
+		_cairo_polygon_fini (&next);
+		*fill_rule = CAIRO_FILL_RULE_WINDING;
+	    }
+
+	    if (unlikely (status))
+		return status;
+	}
+
+	clip_path = clip_path->prev;
+    }
+
+    if (polygon->num_edges == 0)
+	return CAIRO_INT_STATUS_NOTHING_TO_DO;
+
+    assert (is_first_polygon == FALSE);
+    return CAIRO_INT_STATUS_SUCCESS;
+}
+
+static cairo_int_status_t
+composite_clip_polygon (const cairo_spans_compositor_t *compositor,
+			cairo_surface_t *surface,
+			cairo_operator_t op,
+			cairo_polygon_t *polygon,
+			cairo_fill_rule_t fill_rule,
+			cairo_antialias_t antialias)
+{
+    cairo_int_status_t status;
+    cairo_composite_rectangles_t composite;
+
+    status = _cairo_composite_rectangles_init_for_polygon (&composite, surface, op,
+							   &_cairo_pattern_white.base,
+							   polygon,
+							   NULL);
+    if (unlikely (status))
+	return CAIRO_INT_STATUS_UNSUPPORTED;
+
+    status = composite_polygon (compositor, &composite,
+				polygon, fill_rule, antialias);
+    _cairo_composite_rectangles_fini (&composite);
+
+    return status;
+}
+
 static cairo_surface_t *
 get_clip_surface (const cairo_spans_compositor_t *compositor,
 		  cairo_surface_t *dst,
 		  const cairo_clip_t *clip,
 		  const cairo_rectangle_int_t *extents)
 {
-    cairo_composite_rectangles_t composite;
     cairo_surface_t *surface;
     cairo_box_t box;
-    cairo_polygon_t polygon;
-    const cairo_clip_path_t *clip_path;
+    cairo_polygon_t polygon0;
+    cairo_polygon_t polygon1;
+    cairo_fill_rule_t fill_rule0;
+    cairo_fill_rule_t fill_rule1;
     cairo_antialias_t antialias;
-    cairo_fill_rule_t fill_rule;
     cairo_int_status_t status;
 
     assert (clip->path);
@@ -100,118 +176,71 @@ get_clip_surface (const cairo_spans_compositor_t *compositor,
 						   extents->height,
 						   CAIRO_COLOR_TRANSPARENT);
 
-    _cairo_box_from_rectangle (&box, extents);
-    _cairo_polygon_init (&polygon, &box, 1);
+    if (! surface)
+	return _cairo_surface_create_in_error (CAIRO_INT_STATUS_NO_MEMORY);
 
-    clip_path = clip->path;
-    status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
-						clip_path->tolerance,
-						&polygon);
-    if (unlikely (status))
-	goto cleanup_polygon;
+    status = _cairo_clip_get_polygon (clip, &polygon0, &fill_rule0, &antialias);
 
-    polygon.limits = NULL;
-    polygon.num_limits = 0;
+    if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
+	if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+	    goto error;
 
-    antialias = clip_path->antialias;
-    fill_rule = clip_path->fill_rule;
-    clip_path = clip_path->prev;
-    while (clip_path) {
-	if (clip_path->antialias == antialias) {
-	    cairo_polygon_t next;
-
-	    _cairo_polygon_init (&next, NULL, 0);
-	    status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
-							clip_path->tolerance,
-							&next);
-	    if (likely (status == CAIRO_INT_STATUS_SUCCESS))
-		status = _cairo_polygon_intersect (&polygon, fill_rule,
-						   &next, clip_path->fill_rule);
-	    _cairo_polygon_fini (&next);
-	    if (unlikely (status))
-		goto cleanup_polygon;
+	_cairo_polygon_translate (&polygon0, -extents->x, -extents->y);
+	status = composite_clip_polygon (compositor, surface, CAIRO_OPERATOR_ADD,
+					 &polygon0, fill_rule0, antialias);
+	_cairo_polygon_fini (&polygon0);
 
-	    fill_rule = CAIRO_FILL_RULE_WINDING;
-	}
+	if (unlikely (status))
+	    goto error;
 
-	clip_path = clip_path->prev;
+	return surface;
     }
 
-    _cairo_polygon_translate (&polygon, -extents->x, -extents->y);
-    status = _cairo_composite_rectangles_init_for_polygon (&composite, surface,
-							   CAIRO_OPERATOR_ADD,
-							   &_cairo_pattern_white.base,
-							   &polygon,
-							   NULL);
-    if (unlikely (status))
-	goto cleanup_polygon;
+    /* Here, we can assume that clip paths have heterogeneous antialias,
+     * as this code is fallback for that kind of clip.
+     * This code might not work if all clip paths have equal antialias.
+     */
+
+    _cairo_box_from_rectangle (&box, extents);
+    _cairo_polygon_init (&polygon0, &box, 1);
+    _cairo_polygon_init (&polygon1, &box, 1);
+
+    status = get_clip_polygon (clip->path, CAIRO_ANTIALIAS_DEFAULT, &polygon0, &fill_rule0);
 
-    status = composite_polygon (compositor, &composite,
-				&polygon, fill_rule, antialias);
-    _cairo_composite_rectangles_fini (&composite);
-    _cairo_polygon_fini (&polygon);
     if (unlikely (status))
-	goto error;
+	goto cleanup_polygon;
 
-    _cairo_polygon_init (&polygon, &box, 1);
+    status = get_clip_polygon (clip->path, CAIRO_ANTIALIAS_NONE, &polygon1, &fill_rule1);
 
-    clip_path = clip->path;
-    antialias = clip_path->antialias == CAIRO_ANTIALIAS_DEFAULT ? CAIRO_ANTIALIAS_NONE : CAIRO_ANTIALIAS_DEFAULT;
-    clip_path = clip_path->prev;
-    while (clip_path) {
-	if (clip_path->antialias == antialias) {
-	    if (polygon.num_edges == 0) {
-		status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
-							    clip_path->tolerance,
-							    &polygon);
+    if (unlikely (status))
+	goto cleanup_polygon;
 
-		fill_rule = clip_path->fill_rule;
-		polygon.limits = NULL;
-		polygon.num_limits = 0;
-	    } else {
-		cairo_polygon_t next;
+    _cairo_polygon_translate (&polygon0, -extents->x, -extents->y);
+    status = composite_clip_polygon (compositor, surface, CAIRO_OPERATOR_ADD,
+				     &polygon0, fill_rule0, CAIRO_ANTIALIAS_DEFAULT);
 
-		_cairo_polygon_init (&next, NULL, 0);
-		status = _cairo_path_fixed_fill_to_polygon (&clip_path->path,
-							    clip_path->tolerance,
-							    &next);
-		if (likely (status == CAIRO_INT_STATUS_SUCCESS))
-		    status = _cairo_polygon_intersect (&polygon, fill_rule,
-						       &next, clip_path->fill_rule);
-		_cairo_polygon_fini (&next);
-		fill_rule = CAIRO_FILL_RULE_WINDING;
-	    }
-	    if (unlikely (status))
-		goto error;
-	}
+    if (unlikely (status))
+	goto cleanup_polygon;
 
-	clip_path = clip_path->prev;
-    }
+    _cairo_polygon_translate (&polygon1, -extents->x, -extents->y);
+    status = composite_clip_polygon (compositor, surface, CAIRO_OPERATOR_IN,
+				     &polygon1, fill_rule1, CAIRO_ANTIALIAS_NONE);
 
-    if (polygon.num_edges) {
-	_cairo_polygon_translate (&polygon, -extents->x, -extents->y);
-	status = _cairo_composite_rectangles_init_for_polygon (&composite, surface,
-							       CAIRO_OPERATOR_IN,
-							       &_cairo_pattern_white.base,
-							       &polygon,
-							       NULL);
-	if (unlikely (status))
-	    goto cleanup_polygon;
+    if (unlikely (status))
+	goto cleanup_polygon;
 
-	status = composite_polygon (compositor, &composite,
-				    &polygon, fill_rule, antialias);
-	_cairo_composite_rectangles_fini (&composite);
-	_cairo_polygon_fini (&polygon);
-	if (unlikely (status))
-	    goto error;
-    }
+    _cairo_polygon_fini (&polygon0);
+    _cairo_polygon_fini (&polygon1);
 
     return surface;
 
 cleanup_polygon:
-    _cairo_polygon_fini (&polygon);
+    _cairo_polygon_fini (&polygon0);
+    _cairo_polygon_fini (&polygon1);
+
 error:
     cairo_surface_destroy (surface);
+
     return _cairo_int_surface_create_in_error (status);
 }
 
@@ -519,8 +548,12 @@ composite_aligned_boxes (const cairo_spans_compositor_t		*compositor,
 	if (need_clip_mask) {
 	    mask = get_clip_surface (compositor, dst, extents->clip,
 				     &extents->bounded);
-	    if (unlikely (mask->status))
+	    if (unlikely (mask->status)) {
+		if ((cairo_int_status_t)mask->status == CAIRO_INT_STATUS_NOTHING_TO_DO)
+		    return CAIRO_STATUS_SUCCESS;
+
 		return mask->status;
+	    }
 
 	    mask_x = -extents->bounded.x;
 	    mask_y = -extents->bounded.y;
-- 
1.7.1



More information about the cairo mailing list