Commit a69335a8 authored by Chris Wilson's avatar Chris Wilson 🤔
Browse files

API: map-to-image and create-similar-image



A common requirement is the fast upload of pixel data. In order to
allocate the most appropriate image buffer, we need knowledge of the
destination. The most obvious example is that we could use a
shared-memory region for the image to avoid the transfer cost of
uploading the pixels to the X server. Similarly, gl, win32, quartz...

The other side of the equation is that for manual modification of a
remote surface, it would be more efficient if we can create a similar
image to reduce the transfer costs. This strategy is already followed
for the destination fallbacks and this merely exposes the same
capability for the application fallbacks.
Signed-off-by: Chris Wilson's avatarChris Wilson <chris@chris-wilson.co.uk>
parent c6812c6a
......@@ -643,10 +643,14 @@ _cairo_analysis_surface_show_text_glyphs (void *abstract_surface,
static const cairo_surface_backend_t cairo_analysis_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_ANALYSIS,
_cairo_analysis_surface_finish,
NULL,
NULL, /* create_similar */
_cairo_analysis_surface_finish,
NULL, /* create_similar_image */
NULL, /* map_to_image */
NULL, /* unmap */
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
......@@ -842,10 +846,14 @@ typedef cairo_int_status_t
static const cairo_surface_backend_t cairo_null_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_NULL,
NULL, /* finish */
_cairo_default_context_create, /* XXX */
NULL, /* create_similar */
NULL, /* finish */
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image*/
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
......
......@@ -1829,10 +1829,15 @@ _cairo_directfb_surface_is_similar (void *surface_a, void *surface_b)
static cairo_surface_backend_t
_cairo_directfb_surface_backend = {
CAIRO_SURFACE_TYPE_DIRECTFB, /*type*/
_cairo_directfb_surface_finish, /*finish*/
_cairo_default_context_create,
_cairo_directfb_surface_create_similar,/*create_similar*/
_cairo_directfb_surface_finish, /*finish*/
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
_cairo_directfb_surface_acquire_source_image,/*acquire_source_image*/
_cairo_directfb_surface_release_source_image,/*release_source_image*/
_cairo_directfb_surface_acquire_dest_image,/*acquire_dest_image*/
......
......@@ -41,6 +41,8 @@
#include "cairo.h"
#include "cairo-compiler-private.h"
#include <assert.h>
CAIRO_BEGIN_DECLS
enum _cairo_int_status {
......@@ -100,6 +102,13 @@ enum _cairo_int_status {
#define _cairo_int_status_is_error(status) \
(status != CAIRO_INT_STATUS_SUCCESS && status <= CAIRO_INT_STATUS_LAST_STATUS)
static inline cairo_status_t
_cairo_public_status (cairo_int_status_t status)
{
assert (status <= CAIRO_INT_STATUS_LAST_STATUS);
return status;
}
cairo_private cairo_status_t
_cairo_error (cairo_status_t status);
......
......@@ -1764,10 +1764,13 @@ _cairo_gl_surface_fill (void *abstract_surface,
const cairo_surface_backend_t _cairo_gl_surface_backend = {
CAIRO_SURFACE_TYPE_GL,
_cairo_gl_surface_finish,
_cairo_default_context_create,
_cairo_gl_surface_create_similar,
_cairo_gl_surface_finish,
NULL, /* similar image */
NULL, /* map to image */
NULL, /* unmap image */
_cairo_gl_surface_acquire_source_image,
_cairo_gl_surface_release_source_image,
......
......@@ -728,6 +728,36 @@ _cairo_image_surface_create_similar (void *abstract_other,
width, height);
}
static cairo_surface_t *
_cairo_image_surface_map_to_image (void *abstract_other,
const cairo_rectangle_int_t *extents)
{
cairo_image_surface_t *other = abstract_other;
cairo_surface_t *surface;
uint8_t *data;
data = other->data;
data += extents->y * other->stride;
data += extents->x * PIXMAN_FORMAT_BPP (other->pixman_format)/ 8;
surface =
_cairo_image_surface_create_with_pixman_format (data,
other->pixman_format,
extents->width,
extents->height,
other->stride);
cairo_surface_set_device_offset (surface, -extents->x, -extents->y);
return surface;
}
static cairo_int_status_t
_cairo_image_surface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
return CAIRO_INT_STATUS_SUCCESS;
}
static cairo_status_t
_cairo_image_surface_finish (void *abstract_surface)
{
......@@ -4655,10 +4685,15 @@ _cairo_surface_is_image (const cairo_surface_t *surface)
const cairo_surface_backend_t _cairo_image_surface_backend = {
CAIRO_SURFACE_TYPE_IMAGE,
_cairo_image_surface_finish,
_cairo_default_context_create,
_cairo_image_surface_create_similar,
_cairo_image_surface_finish,
NULL, /* create similar image */
_cairo_image_surface_map_to_image,
_cairo_image_surface_unmap_image,
_cairo_image_surface_acquire_source_image,
_cairo_image_surface_release_source_image,
_cairo_image_surface_acquire_dest_image,
......
......@@ -1439,10 +1439,14 @@ _cairo_os2_surface_mark_dirty_rectangle (void *surface,
static const cairo_surface_backend_t cairo_os2_surface_backend = {
CAIRO_SURFACE_TYPE_OS2,
_cairo_os2_surface_finish,
_cairo_default_context_create,
NULL, /* create_similar */
_cairo_os2_surface_finish,
NULL, /* create_similar_image */
NULL, /* map_to_image */
NULL, /* unmap_image */
_cairo_os2_surface_acquire_source_image,
_cairo_os2_surface_release_source_image,
_cairo_os2_surface_acquire_dest_image,
......
......@@ -649,10 +649,15 @@ _cairo_paginated_context_create (void *target)
static const cairo_surface_backend_t cairo_paginated_surface_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_PAGINATED,
_cairo_paginated_surface_finish,
_cairo_paginated_context_create,
_cairo_paginated_surface_create_similar,
_cairo_paginated_surface_finish,
NULL, /* create simlar image */
NULL, /* map to image */
NULL, /* unmap image */
_cairo_paginated_surface_acquire_source_image,
_cairo_paginated_surface_release_source_image,
NULL, /* acquire_dest_image */
......
......@@ -6567,10 +6567,15 @@ _cairo_pdf_surface_set_paginated_mode (void *abstract_surface,
static const cairo_surface_backend_t cairo_pdf_surface_backend = {
CAIRO_SURFACE_TYPE_PDF,
_cairo_pdf_surface_finish,
_cairo_default_context_create,
NULL, /* create similar: handled by wrapper */
_cairo_pdf_surface_finish,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
......
......@@ -3932,10 +3932,15 @@ _cairo_ps_surface_supports_fine_grained_fallbacks (void *abstract_surface)
static const cairo_surface_backend_t cairo_ps_surface_backend = {
CAIRO_SURFACE_TYPE_PS,
_cairo_ps_surface_finish,
_cairo_default_context_create,
NULL, /* create similar: handled by wrapper */
_cairo_ps_surface_finish,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
......
......@@ -160,10 +160,15 @@ _cairo_quartz_image_surface_flush (void *asurface)
static const cairo_surface_backend_t cairo_quartz_image_surface_backend = {
CAIRO_SURFACE_TYPE_QUARTZ_IMAGE,
_cairo_quartz_image_surface_finish,
_cairo_default_context_create,
_cairo_quartz_image_surface_create_similar,
_cairo_quartz_image_surface_finish,
NULL, /* create_similar_image */
NULL, /* map_to_image */
NULL, /* unmap_image */
_cairo_quartz_image_surface_acquire_source_image,
NULL, /* release_source_image */
_cairo_quartz_image_surface_acquire_dest_image,
......
......@@ -805,10 +805,15 @@ _cairo_surface_is_recording (const cairo_surface_t *surface)
static const cairo_surface_backend_t cairo_recording_surface_backend = {
CAIRO_SURFACE_TYPE_RECORDING,
_cairo_recording_surface_finish,
_cairo_default_context_create,
_cairo_recording_surface_create_similar,
_cairo_recording_surface_finish,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
_cairo_recording_surface_acquire_source_image,
_cairo_recording_surface_release_source_image,
NULL, /* acquire_dest_image */
......
......@@ -298,3 +298,13 @@ _cairo_box_add_curve_to (cairo_box_t *extents,
assert (status == CAIRO_STATUS_SUCCESS);
}
}
void
_cairo_rectangle_int_from_double (cairo_rectangle_int_t *recti,
const cairo_rectangle_t *rectf)
{
recti->x = floor (rectf->x);
recti->y = floor (rectf->y);
recti->width = ceil (rectf->x + rectf->width) - floor (rectf->x);
recti->height = ceil (rectf->y + rectf->height) - floor (rectf->y);
}
......@@ -3409,10 +3409,15 @@ _cairo_script_surface_get_extents (void *abstract_surface,
static const cairo_surface_backend_t
_cairo_script_surface_backend = {
CAIRO_SURFACE_TYPE_SCRIPT,
_cairo_script_surface_finish,
_cairo_default_context_create,
_cairo_script_surface_create_similar,
_cairo_script_surface_finish,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
_cairo_script_surface_acquire_source_image,
_cairo_script_surface_release_source_image,
NULL, /* acquire_dest_image */
......
......@@ -89,10 +89,13 @@ _cairo_surface_snapshot_get_extents (void *abstract_surface,
static const cairo_surface_backend_t _cairo_surface_snapshot_backend = {
CAIRO_INTERNAL_SURFACE_TYPE_SNAPSHOT,
_cairo_surface_snapshot_finish,
NULL,
NULL, /* create similar */
_cairo_surface_snapshot_finish,
NULL, /* create similar image */
NULL, /* map to image */
NULL, /* unmap image */
_cairo_surface_snapshot_acquire_source_image,
_cairo_surface_snapshot_release_source_image,
......
......@@ -61,6 +61,48 @@ _cairo_surface_subsurface_create_similar (void *other,
return surface->target->backend->create_similar (surface->target, content, width, height);
}
static cairo_surface_t *
_cairo_surface_subsurface_create_similar_image (void *other,
cairo_format_t format,
int width, int height)
{
cairo_surface_subsurface_t *surface = other;
return surface->target->backend->create_similar_image (surface->target,
format,
width, height);
}
static cairo_surface_t *
_cairo_surface_subsurface_map_to_image (void *abstract_surface,
const cairo_rectangle_int_t *extents)
{
cairo_surface_subsurface_t *surface = abstract_surface;
cairo_rectangle_int_t target_extents;
if (surface->target->backend->map_to_image == NULL)
return NULL;
target_extents.x = extents->x + surface->extents.x;
target_extents.y = extents->y + surface->extents.y;
target_extents.width = extents->width;
target_extents.height = extents->height;
return surface->target->backend->map_to_image (surface->target,
&target_extents);
}
static cairo_int_status_t
_cairo_surface_subsurface_unmap_image (void *abstract_surface,
cairo_image_surface_t *image)
{
cairo_surface_subsurface_t *surface = abstract_surface;
if (surface->target->backend->unmap_image == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
return surface->target->backend->unmap_image (surface->target, image);
}
static cairo_int_status_t
_cairo_surface_subsurface_paint (void *abstract_surface,
cairo_operator_t op,
......@@ -426,10 +468,14 @@ _cairo_surface_subsurface_create_context(void *target)
static const cairo_surface_backend_t _cairo_surface_subsurface_backend = {
CAIRO_SURFACE_TYPE_SUBSURFACE,
_cairo_surface_subsurface_finish,
_cairo_surface_subsurface_create_context,
_cairo_surface_subsurface_create_similar,
_cairo_surface_subsurface_finish,
_cairo_surface_subsurface_create_similar_image,
_cairo_surface_subsurface_map_to_image,
_cairo_surface_subsurface_unmap_image,
_cairo_surface_subsurface_acquire_source_image,
_cairo_surface_subsurface_release_source_image,
......@@ -533,4 +579,36 @@ cairo_surface_create_for_rectangle (cairo_surface_t *target,
return &surface->base;
}
cairo_surface_t *
_cairo_surface_create_for_rectangle_int (cairo_surface_t *target,
cairo_rectangle_int_t *extents)
{
cairo_surface_subsurface_t *surface;
if (unlikely (target->status))
return _cairo_surface_create_in_error (target->status);
if (unlikely (target->finished))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
assert (target->backend->type != CAIRO_SURFACE_TYPE_SUBSURFACE);
surface = malloc (sizeof (cairo_surface_subsurface_t));
if (unlikely (surface == NULL))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY));
assert (_cairo_matrix_is_translation (&target->device_transform));
_cairo_surface_init (&surface->base,
&_cairo_surface_subsurface_backend,
NULL, /* device */
target->content);
surface->extents = *extents;
surface->extents.x += target->device_transform.x0;
surface->extents.y += target->device_transform.y0;
surface->target = cairo_surface_reference (target);
return &surface->base;
}
/* XXX observe mark-dirty */
......@@ -519,6 +519,188 @@ cairo_surface_create_similar (cairo_surface_t *other,
return surface;
}
/**
* cairo_surface_create_similar_image:
* @other: an existing surface used to select the preference of the new surface
* @format: the format for the new surface
* @width: width of the new surface, (in device-space units)
* @height: height of the new surface (in device-space units)
*
* Create a new image surface that is as compatible as possible for uploading
* to and the use in conjunction with an existing surface.
*
* Initially the surface contents are all 0 (transparent if contents
* have transparency, black otherwise.)
*
* Return value: a pointer to the newly allocated image surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* 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.
**/
cairo_surface_t *
cairo_surface_create_similar_image (cairo_surface_t *other,
cairo_format_t format,
int width,
int height)
{
if (other->status)
return _cairo_surface_create_in_error (other->status);
if (unlikely (other->finished))
return _cairo_surface_create_in_error (CAIRO_STATUS_SURFACE_FINISHED);
if (unlikely (width < 0 || height < 0))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
if (unlikely (! CAIRO_FORMAT_VALID (format)))
return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_FORMAT));
if (other->backend->create_similar_image)
return other->backend->create_similar_image (other,
format, width, height);
return cairo_image_surface_create (format, width, height);
}
/**
* 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.
*
* Return value: a pointer to the newly allocated image surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* 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.
**/
cairo_surface_t *
cairo_surface_map_to_image (cairo_surface_t *surface,
const cairo_rectangle_t *extents)
{
cairo_rectangle_int_t rect;
cairo_surface_t *image;
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);
if (extents == NULL) {
if (unlikely (! surface->backend->get_extents (surface, &rect)))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
} else {
_cairo_rectangle_int_from_double (&rect, extents);
}
image = NULL;
if (surface->backend->map_to_image)
image = surface->backend->map_to_image (surface, &rect);
if (image == NULL) {
cairo_surface_pattern_t pattern;
cairo_status_t status;
image = cairo_image_surface_create (_cairo_format_from_content (surface->content),
rect.width, rect.height);
cairo_surface_set_device_offset (image, -rect.y, -rect.y);
_cairo_pattern_init_for_surface (&pattern, surface);
pattern.base.filter = CAIRO_FILTER_NEAREST;
status = _cairo_surface_paint (image,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
if (unlikely (status)) {
cairo_surface_destroy (image);
image = _cairo_surface_create_in_error (status);
}
}
return image;
}
/**
* cairo_surface_unmap_image:
* @surface: an existing surface used to extract the image from
* @image: the currently mapped image
*
* Unmaps the image surface as returned from #cairo_surface_map_to_image().
* 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
*
* Return value: a pointer to the newly allocated image surface. The caller
* owns the surface and should call cairo_surface_destroy() when done
* with it.
*
* 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.
**/
void
cairo_surface_unmap_image (cairo_surface_t *surface,
cairo_surface_t *image)
{
cairo_int_status_t status;
if (unlikely (surface->status)) {
status = surface->status;
goto error;
}
if (unlikely (surface->finished)) {
status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
goto error;
}
if (unlikely (image->status)) {
status = image->status;
goto error;
}
if (unlikely (image->finished)) {
status = _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
goto error;
}
status = CAIRO_INT_STATUS_UNSUPPORTED;
if (surface->backend->unmap_image)
status = surface->backend->unmap_image (surface, image);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_surface_pattern_t pattern;
_cairo_pattern_init_for_surface (&pattern, image);
pattern.base.filter = CAIRO_FILTER_NEAREST;
status = _cairo_surface_paint (surface,
CAIRO_OPERATOR_SOURCE,
&pattern.base,
NULL);
_cairo_pattern_fini (&pattern.base);
}
error:
cairo_surface_finish (image);
cairo_surface_destroy (image);
if (status)
_cairo_status_set_error (&surface->status, status);
}
cairo_surface_t *
_cairo_surface_create_similar_solid (cairo_surface_t *other,
cairo_content_t content,
......
......@@ -2575,10 +2575,15 @@ _cairo_svg_surface_get_font_options (void *abstract_surface,
static const cairo_surface_backend_t cairo_svg_surface_backend = {
CAIRO_SURFACE_TYPE_SVG,
_cairo_svg_surface_finish,
_cairo_default_context_create,
NULL, /* create_similar: handled by wrapper */
_cairo_svg_surface_finish,
NULL, /* create_similar_image */
NULL, /* map to image */
NULL, /* unmap image */
NULL, /* acquire_source_image */
NULL, /* release_source_image */
NULL, /* acquire_dest_image */
......
......@@ -396,10 +396,15 @@ _cairo_tee_surface_show_text_glyphs (void *abstract_surface,
static const cairo_surface_backend_t cairo_tee_surface_backend = {
CAIRO_SURFACE_TYPE_TEE,
_cairo_tee_surface_finish,
_cairo_default_context_create, /* XXX */
_cairo_tee_surface_create_similar,
_cairo_tee_surface_finish,
NULL, /* create similar image */