[PATCH] [pattern] Tweak REFLECT HACK

Chris Wilson chris at chris-wilson.co.uk
Mon Oct 20 08:49:23 PDT 2008


In order to workaound a directfb bug, tweak the reflect->repeat pattern so
that it covers the destination rectangle. Although the number of paint()
increases, the number of read/written pixels remain the same so that
performance should not deteriorate, but instead be improved by using a
cloned source. The early return of the REFLECT surface is discarded so
that the latter optimisations for surface sources can be applied. One side
effect of this is that acquire_source_image() is removed due to its lax
reference counting which thereby exposes the ROI optimisations for image
destinations as well.
---
 src/cairo-matrix.c  |    9 ++-
 src/cairo-pattern.c |  273 ++++++++++++++++++++++++---------------------------
 src/cairoint.h      |    1 -
 3 files changed, 137 insertions(+), 146 deletions(-)

diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index ca18323..724facd 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -371,6 +371,13 @@ _cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
     double min_x, max_x;
     double min_y, max_y;
 
+    if (_cairo_matrix_is_identity (matrix)) {
+	if (is_tight)
+	    *is_tight = TRUE;
+
+	return;
+    }
+
     quad_x[0] = *x1;
     quad_y[0] = *y1;
     cairo_matrix_transform_point (matrix, &quad_x[0], &quad_y[0]);
@@ -406,7 +413,7 @@ _cairo_matrix_transform_bounding_box (const cairo_matrix_t *matrix,
     *y1 = min_y;
     *x2 = max_x;
     *y2 = max_y;
-    
+
     if (is_tight) {
         /* it's tight if and only if the four corner points form an axis-aligned
            rectangle.
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index d83142a..f3e05b8 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -1354,7 +1354,6 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern,
 	attr->matrix = matrix;
 	attr->extend = pattern->base.extend;
 	attr->filter = CAIRO_FILTER_NEAREST;
-	attr->acquired = FALSE;
 
 	*out = &image->base;
 
@@ -1445,7 +1444,6 @@ _cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern,
     cairo_matrix_init_identity (&attr->matrix);
     attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE;
     attr->filter = CAIRO_FILTER_NEAREST;
-    attr->acquired = FALSE;
 
     return status;
 }
@@ -1596,7 +1594,6 @@ NOCACHE:
     cairo_matrix_init_identity (&attribs->matrix);
     attribs->extend = CAIRO_EXTEND_REPEAT;
     attribs->filter = CAIRO_FILTER_NEAREST;
-    attribs->acquired = FALSE;
 
     status = CAIRO_STATUS_SUCCESS;
 
@@ -1773,31 +1770,32 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t   *pattern,
 					    cairo_surface_t	       **out,
 					    cairo_surface_attributes_t *attr)
 {
-    cairo_int_status_t status;
+    cairo_surface_t *surface;
+    cairo_rectangle_int_t extents;
+    cairo_rectangle_int_t sampled_area;
+    double x1, y1, x2, y2;
     int tx, ty;
     double pad;
+    cairo_int_status_t status;
 
-    attr->acquired = FALSE;
+    surface = cairo_surface_reference (pattern->surface);
 
+    attr->matrix = pattern->base.matrix;
     attr->extend = pattern->base.extend;
     attr->filter = _cairo_pattern_analyze_filter (pattern, &pad);
 
-    if (_cairo_matrix_is_integer_translation (&pattern->base.matrix,
-					      &tx, &ty))
-    {
+    attr->x_offset = attr->y_offset = tx = ty = 0;
+    if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) {
 	cairo_matrix_init_identity (&attr->matrix);
 	attr->x_offset = tx;
 	attr->y_offset = ty;
-    }
-    else if (attr->filter == CAIRO_FILTER_NEAREST)
-    {
+    } else if (attr->filter == CAIRO_FILTER_NEAREST) {
 	/*
 	 * For NEAREST, we can remove the fractional translation component
 	 * from the transformation - this ensures that the pattern will always
 	 * hit fast-paths in the backends for simple transformations that
 	 * become (almost) identity, without loss of quality.
 	 */
-	attr->matrix = pattern->base.matrix;
 	attr->matrix.x0 = 0;
 	attr->matrix.y0 = 0;
 	if (_cairo_matrix_is_pixel_exact (&attr->matrix)) {
@@ -1811,15 +1809,11 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t   *pattern,
 	    attr->matrix.y0 = pattern->base.matrix.y0;
 	}
 
-	attr->x_offset = attr->y_offset = 0;
-	tx = ty = 0;
-    }
-    else
-    {
-	attr->matrix = pattern->base.matrix;
-	attr->x_offset = attr->y_offset = 0;
-	tx = 0;
-	ty = 0;
+	if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) {
+	    cairo_matrix_init_identity (&attr->matrix);
+	    attr->x_offset = tx;
+	    attr->y_offset = ty;
+	}
     }
 
     /* XXX: Hack:
@@ -1828,153 +1822,159 @@ _cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t   *pattern,
      * an image twice bigger on each side, and create a pattern of four
      * images such that the new image, when repeated, has the same effect
      * of reflecting the original pattern.
-     *
-     * This is because the reflect support in pixman is broken and we
-     * pass repeat instead of reflect to pixman.  See
-     * _cairo_image_surface_set_attributes() for that.
      */
     if (attr->extend == CAIRO_EXTEND_REFLECT) {
 	cairo_t *cr;
-	int w,h;
+	cairo_surface_t *src;
+	int w, h;
 
-	cairo_rectangle_int_t extents;
-	status = _cairo_surface_get_extents (pattern->surface, &extents);
+	status = _cairo_surface_get_extents (surface, &extents);
 	if (status)
-	    return status;
+	    goto BAIL;
 
-	attr->extend = CAIRO_EXTEND_REPEAT;
+	status = _cairo_surface_clone_similar (dst, pattern->surface,
+					       extents.x, extents.y,
+					       extents.width, extents.height,
+					       &extents.x, &extents.y, &src);
+	if (status)
+	    goto BAIL;
 
-	/* TODO: Instead of rendering pattern->surface four times to
-	 * out, we should first copy pattern->surface to surface similar
-	 * to dst and then copy that four times to out.  This may cause
-	 * an extra copy in the case of image destination, but for X servers,
-	 * this will send pattern->surface just once over the wire instead
-	 * of current four.
-	 */
-	x = extents.x;
-	y = extents.y;
 	w = 2 * extents.width;
 	h = 2 * extents.height;
 
-	*out = cairo_surface_create_similar (dst, dst->content, w, h);
-	status = cairo_surface_status (*out);
-	if (status) {
-	    cairo_surface_destroy (*out);
-	    *out = NULL;
-	    return status;
+	if (_cairo_matrix_is_identity (&attr->matrix)) {
+	    attr->x_offset = -x;
+	    x += tx;
+	    while (x <= -w)
+		x += w;
+	    while (x >= w)
+		x -= w;
+	    extents.x += x;
+	    tx = x = 0;
+
+	    attr->y_offset = -y;
+	    y += ty;
+	    while (y <= -h)
+		y += h;
+	    while (y >= h)
+		y -= h;
+	    extents.y += y;
+	    ty = y = 0;
 	}
 
-	(*out)->device_transform = pattern->surface->device_transform;
-	(*out)->device_transform_inverse = pattern->surface->device_transform_inverse;
+	cairo_surface_destroy (surface);
+	surface = cairo_surface_create_similar (dst, dst->content, w, h);
+	if (surface->status) {
+	    cairo_surface_destroy (src);
+	    return surface->status;
+	}
 
-	cr = cairo_create (*out);
+	surface->device_transform = pattern->surface->device_transform;
+	surface->device_transform_inverse = pattern->surface->device_transform_inverse;
 
-	cairo_set_source_surface (cr, pattern->surface, -x, -y);
+	cr = cairo_create (surface);
+
+	cairo_set_source_surface (cr, src, -extents.x, -extents.y);
 	cairo_paint (cr);
 
 	cairo_scale (cr, -1, +1);
-	cairo_set_source_surface (cr, pattern->surface, x-w, -y);
+	cairo_set_source_surface (cr, src, extents.x-w, -extents.y);
+	cairo_paint (cr);
+	cairo_set_source_surface (cr, src, extents.x, -extents.y);
 	cairo_paint (cr);
 
 	cairo_scale (cr, +1, -1);
-	cairo_set_source_surface (cr, pattern->surface, x-w, y-h);
+	cairo_set_source_surface (cr, src, extents.x-w, extents.y-h);
+	cairo_paint (cr);
+	cairo_set_source_surface (cr, src, extents.x, extents.y-h);
+	cairo_paint (cr);
+	cairo_set_source_surface (cr, src, extents.x-w, extents.y);
+	cairo_paint (cr);
+	cairo_set_source_surface (cr, src, extents.x, extents.y);
 	cairo_paint (cr);
 
 	cairo_scale (cr, -1, +1);
-	cairo_set_source_surface (cr, pattern->surface, -x, y-h);
+	cairo_set_source_surface (cr, src, -extents.x, extents.y-h);
+	cairo_paint (cr);
+	cairo_set_source_surface (cr, src, -extents.x, extents.y);
 	cairo_paint (cr);
 
 	status = cairo_status (cr);
 	cairo_destroy (cr);
 
-	if (status) {
-	    cairo_surface_destroy (*out);
-	    *out = NULL;
-	}
-
-	return status;
-    }
-
-    if (_cairo_surface_is_image (dst)) {
-	cairo_image_surface_t *image;
+	cairo_surface_destroy (src);
 
-	status = _cairo_surface_acquire_source_image (pattern->surface,
-						      &image,
-						      &attr->extra);
 	if (status)
-	    return status;
+	    goto BAIL;
 
-	*out = &image->base;
-	attr->acquired = TRUE;
-    } else {
-	cairo_rectangle_int_t extents;
-
-	status = _cairo_surface_get_extents (pattern->surface, &extents);
-	if (status)
-	    return status;
+	attr->extend = CAIRO_EXTEND_REPEAT;
+    }
 
-	/* If we're repeating, we just play it safe and clone the
-	 * entire surface - i.e. we use the existing extents.
-	 */
-	if (attr->extend != CAIRO_EXTEND_REPEAT) {
-	    cairo_rectangle_int_t sampled_area;
+    status = _cairo_surface_get_extents (surface, &extents);
+    if (status)
+	goto BAIL;
 
-	    /* Otherwise, we first transform the rectangle to the
-	     * coordinate space of the source surface so that we can
-	     * clone only that portion of the surface that will be
-	     * read.
-	     */
-	    if (_cairo_matrix_is_identity (&attr->matrix)) {
-		sampled_area.x = x;
-		sampled_area.y = y;
-		sampled_area.width  = width;
-		sampled_area.height = height;
-	    } else {
-		double x1 = x;
-		double y1 = y;
-		double x2 = x + width;
-		double y2 = y + height;
-
-		_cairo_matrix_transform_bounding_box  (&attr->matrix,
-						       &x1, &y1, &x2, &y2,
-						       NULL);
-
-		sampled_area.x = floor (x1 - pad);
-		sampled_area.y = floor (y1 - pad);
-		sampled_area.width  = ceil (x2 + pad) - sampled_area.x;
-		sampled_area.height = ceil (y2 + pad) - sampled_area.y;
+    /* We first transform the rectangle to the coordinate space of the
+     * source surface so that we only need to clone that portion of the
+     * surface that will be read.
+     */
+    x1 = x;
+    y1 = y;
+    x2 = x + width;
+    y2 = y + height;
+    _cairo_matrix_transform_bounding_box  (&attr->matrix,
+					   &x1, &y1, &x2, &y2,
+					   NULL);
+
+    sampled_area.x = floor (x1 - pad);
+    sampled_area.y = floor (y1 - pad);
+    sampled_area.width  = ceil (x2 + pad) - sampled_area.x;
+    sampled_area.height = ceil (y2 + pad) - sampled_area.y;
+
+    sampled_area.x += tx;
+    sampled_area.y += ty;
+
+    if (attr->extend != CAIRO_EXTEND_REPEAT) {
+	/* Never acquire a larger area than the source itself */
+	_cairo_rectangle_intersect (&extents, &sampled_area);
+    } else {
+	if (sampled_area.x >= extents.x &&
+	    sampled_area.y >= extents.y &&
+	    sampled_area.x + sampled_area.width <= extents.x + extents.width &&
+	    sampled_area.y + sampled_area.height <= extents.y + extents.height)
+	{
+	    /* source is wholly contained within extents, drop the REPEAT */
+	    extents = sampled_area;
+	    attr->extend = CAIRO_EXTEND_NONE;
+	}
+    }
 
-	    }
+    status = _cairo_surface_clone_similar (dst, surface,
+					   extents.x, extents.y,
+					   extents.width, extents.height,
+					   &x, &y, out);
+    if (status)
+	goto BAIL;
 
-	    sampled_area.x += tx;
-	    sampled_area.y += ty;
+    if (x != 0 || y != 0) {
+	if (_cairo_matrix_is_identity (&attr->matrix)) {
+	    attr->x_offset -= x;
+	    attr->y_offset -= y;
+	} else {
+	    cairo_matrix_t m;
 
-	    /* Never acquire a larger area than the source itself */
-	    _cairo_rectangle_intersect (&extents, &sampled_area);
-	}
+	    x -= attr->x_offset;
+	    y -= attr->y_offset;
+	    attr->x_offset = 0;
+	    attr->y_offset = 0;
 
-	status = _cairo_surface_clone_similar (dst, pattern->surface,
-					       extents.x, extents.y,
-					       extents.width, extents.height,
-					       &x, &y, out);
-	if (status == CAIRO_STATUS_SUCCESS && (x != 0 || y != 0)) {
-	    if (_cairo_matrix_is_identity (&attr->matrix)) {
-		attr->x_offset -= x;
-		attr->y_offset -= y;
-	    } else {
-		cairo_matrix_t m;
-
-		x -= attr->x_offset;
-		y -= attr->y_offset;
-		attr->x_offset = 0;
-		attr->y_offset = 0;
-
-		cairo_matrix_init_translate (&m, -x, -y);
-		cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m);
-	    }
+	    cairo_matrix_init_translate (&m, -x, -y);
+	    cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m);
 	}
     }
 
+  BAIL:
+    cairo_surface_destroy (surface);
     return status;
 }
 
@@ -2012,7 +2012,6 @@ _cairo_pattern_acquire_surface (cairo_pattern_t		   *pattern,
 
     if (pattern->status) {
 	*surface_out = NULL;
-	attributes->acquired = FALSE;
 	return pattern->status;
     }
 
@@ -2095,21 +2094,7 @@ _cairo_pattern_release_surface (cairo_pattern_t		   *pattern,
 				cairo_surface_t		   *surface,
 				cairo_surface_attributes_t *attributes)
 {
-    if (attributes->acquired)
-    {
-	cairo_surface_pattern_t *surface_pattern;
-
-	assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
-	surface_pattern = (cairo_surface_pattern_t *) pattern;
-
-	_cairo_surface_release_source_image (surface_pattern->surface,
-					     (cairo_image_surface_t *) surface,
-					     attributes->extra);
-    }
-    else
-    {
-	cairo_surface_destroy (surface);
-    }
+    cairo_surface_destroy (surface);
 }
 
 cairo_int_status_t
diff --git a/src/cairoint.h b/src/cairoint.h
index cdf28b4..7c96576 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -906,7 +906,6 @@ typedef struct _cairo_surface_attributes {
     cairo_filter_t filter;
     int		   x_offset;
     int		   y_offset;
-    cairo_bool_t   acquired;
     void	   *extra;
 } cairo_surface_attributes_t;
 
-- 
1.5.6.3


--=-glTF3lOLWkFHT+I3aul5--



More information about the cairo mailing list