summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrea Canciani <ranma42@gmail.com>2012-01-08 15:30:03 +0100
committerAndrea Canciani <ranma42@gmail.com>2012-05-26 16:06:21 +0200
commit10c0a1c68c34677273d2f48c5d5a6c1c15a320c0 (patch)
tree7ba50c9b2f5250a086a8978cef8ab255d514b281
parent3e9895e95100ced3a6fcbedcba75e50ca450b4f0 (diff)
surface: Define private map/unmap functions
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.
-rw-r--r--src/cairo-image-surface-private.h7
-rw-r--r--src/cairo-image-surface.c41
-rw-r--r--src/cairo-surface.c239
-rw-r--r--src/cairoint.h8
4 files changed, 222 insertions, 73 deletions
diff --git a/src/cairo-image-surface-private.h b/src/cairo-image-surface-private.h
index 3388c649..6f5f05bf 100644
--- a/src/cairo-image-surface-private.h
+++ b/src/cairo-image-surface-private.h
@@ -216,6 +216,13 @@ _pixman_image_add_tristrip (pixman_image_t *image,
int dst_x, int dst_y,
cairo_tristrip_t *strip);
+cairo_private cairo_image_surface_t *
+_cairo_image_surface_clone_subimage (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents);
+
+cairo_private cairo_bool_t
+_cairo_image_surface_is_clone (cairo_image_surface_t *surface);
+
CAIRO_END_DECLS
#endif /* CAIRO_IMAGE_SURFACE_PRIVATE_H */
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 98f70c58..00369b61 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -1202,3 +1202,44 @@ _cairo_image_analyze_color (cairo_image_surface_t *image)
return image->color = CAIRO_IMAGE_IS_COLOR;
}
+
+static const cairo_user_data_key_t clone_key;
+
+cairo_image_surface_t *
+_cairo_image_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);
+
+ return (cairo_image_surface_t *) image;
+}
+
+cairo_bool_t
+_cairo_image_surface_is_clone (cairo_image_surface_t *image)
+{
+ return cairo_surface_get_user_data (&image->base, &clone_key) != NULL;
+}
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 35ac3916..99cbfbef 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -599,6 +599,135 @@ cairo_surface_create_similar_image (cairo_surface_t *other,
slim_hidden_def (cairo_surface_create_similar_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 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.
+ **/
+cairo_image_surface_t *
+_cairo_surface_map_to_image (cairo_surface_t *surface,
+ const cairo_rectangle_int_t *extents)
+{
+ cairo_image_surface_t *image = NULL;
+
+ assert (extents != NULL);
+
+ /* TODO: require map_to_image != NULL */
+ if (surface->backend->map_to_image)
+ image = (cairo_image_surface_t *) surface->backend->map_to_image (surface, extents);
+
+ if (image == NULL)
+ image = _cairo_image_surface_clone_subimage (surface, extents);
+
+ 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.
+ **/
+cairo_int_status_t
+_cairo_surface_unmap_image (cairo_surface_t *surface,
+ cairo_image_surface_t *image)
+{
+ cairo_surface_pattern_t pattern;
+ cairo_rectangle_int_t extents;
+ cairo_clip_t *clip;
+ cairo_int_status_t status;
+
+ /* 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_image_surface_is_clone (image))
+ {
+ status = surface->backend->unmap_image (surface, image);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ goto destroy;
+ }
+
+ _cairo_pattern_init_for_surface (&pattern, &image->base);
+ 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->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,
+ clip);
+
+ _cairo_pattern_fini (&pattern.base);
+ _cairo_clip_destroy (clip);
+
+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
@@ -607,17 +736,22 @@ slim_hidden_def (cairo_surface_create_similar_image);
* 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.
+ * 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.
+ * 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
**/
@@ -626,7 +760,9 @@ 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_surface_t *imagesurf;
+ cairo_image_surface_t *image;
+ cairo_status_t status;
if (unlikely (surface->status))
return _cairo_surface_create_in_error (surface->status);
@@ -649,37 +785,28 @@ cairo_surface_map_to_image (cairo_surface_t *surface,
}
}
- 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;
-
- image = cairo_surface_create_similar_image (surface,
- _cairo_format_from_content (surface->content),
- extents->width,
- extents->height);
- cairo_surface_set_device_offset (image, -extents->x, -extents->y);
-
- _cairo_pattern_init_for_surface (&pattern, surface);
- pattern.base.filter = CAIRO_FILTER_NEAREST;
+ image = _cairo_surface_map_to_image (surface, extents);
+ imagesurf = &image->base;
- status = _cairo_surface_paint (image,
- CAIRO_OPERATOR_SOURCE,
- &pattern.base,
- NULL);
+ status = cairo_surface_status (imagesurf);
+ if (unlikely (status)) {
+ cairo_surface_destroy (imagesurf);
+ return _cairo_surface_create_in_error (status);
+ }
- _cairo_pattern_fini (&pattern.base);
+ if (cairo_image_surface_get_format (imagesurf) == CAIRO_FORMAT_INVALID) {
+ cairo_surface_destroy (imagesurf);
+ image = _cairo_image_surface_clone_subimage (surface, extents);
+ imagesurf = &image->base;
+ }
- if (unlikely (status)) {
- cairo_surface_destroy (image);
- image = _cairo_surface_create_in_error (status);
- }
+ status = cairo_surface_status (imagesurf);
+ if (unlikely (status)) {
+ cairo_surface_destroy (imagesurf);
+ return _cairo_surface_create_in_error (status);
}
- return image;
+ return imagesurf;
}
slim_hidden_def (cairo_surface_map_to_image);
@@ -702,7 +829,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;
@@ -712,7 +839,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;
@@ -726,50 +852,17 @@ 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,
+ (cairo_image_surface_t *) 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 d8070e5f..5b3d653f 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1304,6 +1304,14 @@ _cairo_surface_paint (cairo_surface_t *surface,
const cairo_pattern_t *source,
const cairo_clip_t *clip);
+cairo_private cairo_image_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_image_surface_t *image);
+
cairo_private cairo_status_t
_cairo_surface_mask (cairo_surface_t *surface,
cairo_operator_t op,