[cairo] [PATCH 3/5] surface: Define private map/unmap functions

Andrea Canciani ranma42 at gmail.com
Sun Jan 15 05:59:06 PST 2012


Cairo backends often need to map/unmap to a raster surface but they
don't care about the pixel format, as Pixman will be doing the format
handling.

Cairo users cannot know how to access the raw data if the format is
invalid.

The two different scenarios call for different guarantees on the
returned surface.

The private map/unmap functions also makes it possible to simply
return the status upon unmapping.
---
 src/cairo-surface.c |  287 +++++++++++++++++++++++++++++++++++++-------------
 src/cairoint.h      |    8 ++
 2 files changed, 220 insertions(+), 75 deletions(-)

diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 2607f29..d470596 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -585,41 +585,84 @@ cairo_surface_create_similar_image (cairo_surface_t  *other,
 }
 slim_hidden_def (cairo_surface_create_similar_image);
 
+
+static const cairo_user_data_key_t clone_key;
+
+static cairo_surface_t *
+_cairo_surface_clone_subimage (cairo_surface_t             *surface,
+			       const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *image;
+    cairo_surface_pattern_t pattern;
+    cairo_status_t ignored;
+
+    image = cairo_surface_create_similar_image (surface,
+						_cairo_format_from_content (surface->content),
+						extents->width,
+						extents->height);
+    /* TODO: check me with non-identity device_transform. Should we
+     * clone the scaling, too? */
+    cairo_surface_set_device_offset (image,
+				     -extents->x,
+				     -extents->y);
+
+    _cairo_pattern_init_for_surface (&pattern, surface);
+    pattern.base.filter = CAIRO_FILTER_NEAREST;
+
+    ignored = _cairo_surface_paint (image,
+				    CAIRO_OPERATOR_SOURCE,
+				    &pattern.base,
+				    NULL);
+
+    _cairo_pattern_fini (&pattern.base);
+
+    cairo_surface_set_user_data (image, &clone_key, surface, NULL);
+
+    /* TODO: return cairo_surface_image_t * */
+
+    return image;
+}
+
 /**
- * cairo_surface_map_to_image:
+ * _cairo_surface_map_to_image:
  * @surface: an existing surface used to extract the image from
  * @extents: limit the extraction to an rectangular region
  *
  * Returns an image surface that is the most efficient mechanism for
- * modifying the backing store of the target surface. The region retrieved
- * may be limited to the @extents or %NULL for the whole surface
- *
- * Note, the use of the original surface as a target or source whilst it is
- * mapped is undefined. The result of mapping the surface multiple times is
- * undefined. Calling cairo_surface_destroy() or cairo_surface_finish() on the
- * resulting image surface results in undefined behavior.
- *
- * Return value: a pointer to the newly allocated image surface. The caller
- * must use cairo_surface_unmap_image() to destroy this image surface.
+ * modifying the backing store of the target surface. The region
+ * retrieved is limited to @extents.
+ *
+ * Note, the use of the original surface as a target or source whilst
+ * it is mapped is undefined. The result of mapping the surface
+ * multiple times is undefined. Calling cairo_surface_destroy() or
+ * cairo_surface_finish() on the resulting image surface results in
+ * undefined behavior. Changing the device transform of the image
+ * surface or of @surface before the image surface is unmapped results
+ * in undefined behavior.
+ *
+ * Assumes that @surface is valid (CAIRO_STATUS_SUCCESS,
+ * non-finished).
+ *
+ * Return value: a pointer to the newly allocated image surface. The
+ * caller must use _cairo_surface_unmap_image() to destroy this image
+ * surface.
  *
  * This function always returns a valid pointer, but it will return a
  * pointer to a "nil" surface if @other is already in an error state
  * or any other error occurs.
  *
+ * The returned image might have a %CAIRO_FORMAT_INVALID format.
+ *
  * Since: 1.12
  **/
 cairo_surface_t *
-cairo_surface_map_to_image (cairo_surface_t  *surface,
-			    const cairo_rectangle_int_t *extents)
+_cairo_surface_map_to_image (cairo_surface_t  *surface,
+			     const cairo_rectangle_int_t *extents)
 {
-    cairo_rectangle_int_t rect;
     cairo_surface_t *image;
+    cairo_rectangle_int_t rect;
 
-    if (unlikely (surface->status))
-	return _cairo_surface_create_in_error (surface->status);
-    if (unlikely (surface->finished))
-	return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
-
+    /* TODO: ensure that callers provide valid extents, move to the public API */
     if (extents == NULL) {
 	if (unlikely (! surface->backend->get_extents (surface, &rect)))
 	    return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
@@ -636,34 +679,163 @@ cairo_surface_map_to_image (cairo_surface_t  *surface,
 	}
     }
 
+    /* TODO: require map_to_image != NULL */
     image = NULL;
     if (surface->backend->map_to_image)
 	image = surface->backend->map_to_image (surface, extents);
 
-    if (image == NULL) {
-	cairo_surface_pattern_t pattern;
-	cairo_status_t status;
+    if (image == NULL)
+	image = _cairo_surface_clone_subimage (surface, extents);
 
-	image = cairo_surface_create_similar_image (surface,
-						    _cairo_format_from_content (surface->content),
-						    extents->width,
-						    extents->height);
-	cairo_surface_set_device_offset (image, -extents->y, -extents->y);
+    /* TODO: return cairo_image_surface_t * */
 
-	_cairo_pattern_init_for_surface (&pattern, surface);
+    return image;
+}
+
+/**
+ * _cairo_surface_unmap_image:
+ * @surface: the surface passed to _cairo_surface_map_to_image().
+ * @image: the currently mapped image
+ *
+ * Unmaps the image surface as returned from
+ * _cairo_surface_map_to_image().
+ *
+ * The content of the image will be uploaded to the target surface.
+ * Afterwards, the image is destroyed.
+ *
+ * Using an image surface which wasn't returned by
+ * _cairo_surface_map_to_image() results in undefined behavior.
+ *
+ * An image surface in error status can be passed to
+ * _cairo_surface_unmap_image().
+ *
+ * Return value: the unmap status.
+ *
+ * Even if the unmap status is not successful, @image is destroyed.
+ *
+ * Since: 1.12
+ **/
+cairo_int_status_t
+_cairo_surface_unmap_image (cairo_surface_t  *surface,
+			    cairo_surface_t *imagesurf)
+{
+    cairo_image_surface_t *image = (cairo_image_surface_t *) imagesurf;
+    cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
+
+    /* map_to_image can return error surfaces */
+    if (unlikely (image->base.status)) {
+	status = image->base.status;
+	goto destroy;
+    }
+
+    /* If the image is untouched just skip the update */
+    if (image->base.serial == 0) {
+	status = CAIRO_STATUS_SUCCESS;
+	goto destroy;
+    }
+
+    /* TODO: require unmap_image != NULL */
+    if (surface->backend->unmap_image &&
+	cairo_surface_get_user_data (&image->base, &clone_key) == NULL)
+    {
+	status = surface->backend->unmap_image (surface, image);
+    }
+
+    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+	cairo_surface_pattern_t pattern;
+	cairo_clip_t *clip;
+	cairo_rectangle_int_t extents;
+
+	_cairo_pattern_init_for_surface (&pattern, &image->base);
 	pattern.base.filter = CAIRO_FILTER_NEAREST;
 
-	status = _cairo_surface_paint (image,
+	/* We have to apply the translate from map_to_image's extents.x and .y */
+	cairo_matrix_init_translate (&pattern.base.matrix,
+				     image->base.device_transform.x0,
+				     image->base.device_transform.y0);
+
+	/* And we also have to clip the operation to the image's extents */
+	extents.x = image->base.device_transform_inverse.x0;
+	extents.y = image->base.device_transform_inverse.y0;
+	extents.width  = image->width;
+	extents.height = image->height;
+	clip = _cairo_clip_intersect_rectangle (NULL, &extents);
+
+	status = _cairo_surface_paint (surface,
 				       CAIRO_OPERATOR_SOURCE,
 				       &pattern.base,
-				       NULL);
+				       clip);
 
 	_cairo_pattern_fini (&pattern.base);
+	_cairo_clip_destroy (clip);
 
-	if (unlikely (status)) {
-	    cairo_surface_destroy (image);
-	    image = _cairo_surface_create_in_error (status);
-	}
+	goto destroy;
+    }
+
+destroy:
+    cairo_surface_finish (&image->base);
+    cairo_surface_destroy (&image->base);
+
+    return status;
+}
+
+/**
+ * cairo_surface_map_to_image:
+ * @surface: an existing surface used to extract the image from
+ * @extents: limit the extraction to an rectangular region
+ *
+ * Returns an image surface that is the most efficient mechanism for
+ * modifying the backing store of the target surface. The region retrieved
+ * may be limited to the @extents or %NULL for the whole surface
+ *
+ * Note, the use of the original surface as a target or source whilst
+ * it is mapped is undefined. The result of mapping the surface
+ * multiple times is undefined. Calling cairo_surface_destroy() or
+ * cairo_surface_finish() on the resulting image surface results in
+ * undefined behavior. Changing the device transform of the image
+ * surface or of @surface before the image surface is unmapped results
+ * in undefined behavior.
+ *
+ * Return value: a pointer to the newly allocated image surface. The caller
+ * must use cairo_surface_unmap_image() to destroy this image surface.
+ *
+ * This function always returns a valid pointer, but it will return a
+ * pointer to a "nil" surface if @other is already in an error state
+ * or any other error occurs. If the returned pointer does not have an
+ * error status, it is guaranteed to be an image surface whose format
+ * is not %CAIRO_FORMAT_INVALID.
+ *
+ * Since: 1.12
+ **/
+cairo_surface_t *
+cairo_surface_map_to_image (cairo_surface_t  *surface,
+			    const cairo_rectangle_int_t *extents)
+{
+    cairo_surface_t *image;
+    cairo_status_t status;
+
+    if (unlikely (surface->status))
+	return _cairo_surface_create_in_error (surface->status);
+    if (unlikely (surface->finished))
+	return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
+
+    image = _cairo_surface_map_to_image (surface, extents);
+
+    status = cairo_surface_status (image);
+    if (unlikely (status)) {
+	cairo_surface_destroy (image);
+	return _cairo_surface_create_in_error (status);
+    }
+
+    if (cairo_image_surface_get_format (image) == CAIRO_FORMAT_INVALID) {
+	cairo_surface_destroy (image);
+	image = _cairo_surface_clone_subimage (surface, extents);
+    }
+
+    status = cairo_surface_status (image);
+    if (unlikely (status)) {
+	cairo_surface_destroy (image);
+	return _cairo_surface_create_in_error (status);
     }
 
     return image;
@@ -689,7 +861,7 @@ void
 cairo_surface_unmap_image (cairo_surface_t *surface,
 			   cairo_surface_t *image)
 {
-    cairo_int_status_t status;
+    cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
 
     if (unlikely (surface->status)) {
 	status = surface->status;
@@ -699,7 +871,6 @@ cairo_surface_unmap_image (cairo_surface_t *surface,
 	status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
 	goto error;
     }
-
     if (unlikely (image->status)) {
 	status = image->status;
 	goto error;
@@ -713,50 +884,16 @@ cairo_surface_unmap_image (cairo_surface_t *surface,
 	goto error;
     }
 
-    /* If the image is untouched just skip the update */
-    if (image->serial == 0) {
-	status = CAIRO_STATUS_SUCCESS;
-	goto error;
-    }
-
-    status = CAIRO_INT_STATUS_UNSUPPORTED;
-    if (surface->backend->unmap_image)
-	status = surface->backend->unmap_image (surface, (cairo_image_surface_t *) image);
-    if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
-	cairo_image_surface_t *img = (cairo_image_surface_t *) image;
-	cairo_surface_pattern_t pattern;
-	cairo_clip_t *clip;
-	cairo_rectangle_int_t extents;
-
-	_cairo_pattern_init_for_surface (&pattern, image);
-	pattern.base.filter = CAIRO_FILTER_NEAREST;
-
-	/* We have to apply the translate from map_to_image's extents.x and .y */
-	cairo_matrix_init_translate (&pattern.base.matrix,
-				     image->device_transform.x0,
-				     image->device_transform.y0);
-
-	/* And we also have to clip the operation to the image's extents */
-	extents.x = image->device_transform_inverse.x0;
-	extents.y = image->device_transform_inverse.y0;
-	extents.width  = img->width;
-	extents.height = img->height;
-	clip = _cairo_clip_intersect_rectangle (NULL, &extents);
-
-	status = _cairo_surface_paint (surface,
-				       CAIRO_OPERATOR_SOURCE,
-				       &pattern.base,
-				       clip);
+    status = _cairo_surface_unmap_image (surface, image);
+    if (unlikely (status))
+	_cairo_surface_set_error (surface, status);
 
-	_cairo_pattern_fini (&pattern.base);
-	_cairo_clip_destroy (clip);
-    }
+    return;
 
 error:
+    _cairo_surface_set_error (surface, status);
     cairo_surface_finish (image);
     cairo_surface_destroy (image);
-    if (status)
-	_cairo_surface_set_error (surface, status);
 }
 slim_hidden_def (cairo_surface_unmap_image);
 
diff --git a/src/cairoint.h b/src/cairoint.h
index f08462c..15a26cd 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1299,6 +1299,14 @@ _cairo_surface_paint (cairo_surface_t	*surface,
 		      const cairo_pattern_t *source,
 		      const cairo_clip_t	    *clip);
 
+cairo_private cairo_surface_t *
+_cairo_surface_map_to_image (cairo_surface_t  *surface,
+			     const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_int_status_t
+_cairo_surface_unmap_image (cairo_surface_t *surface,
+			    cairo_surface_t *image);
+
 cairo_private cairo_status_t
 _cairo_surface_mask (cairo_surface_t	*surface,
 		     cairo_operator_t	 op,
-- 
1.7.5.4



More information about the cairo mailing list