Skip to content
Permalink
747cab741c
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
2755 lines (2400 sloc) 83.1 KB
/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2002 University of Southern California
* Copyright © 2005 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it either under the terms of the GNU Lesser General Public
* License version 2.1 as published by the Free Software Foundation
* (the "LGPL") or, at your option, under the terms of the Mozilla
* Public License Version 1.1 (the "MPL"). If you do not alter this
* notice, a recipient may use your version of this file under either
* the MPL or the LGPL.
*
* You should have received a copy of the LGPL along with this library
* in the file COPYING-LGPL-2.1; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
* You should have received a copy of the MPL along with this library
* in the file COPYING-MPL-1.1
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
* OF ANY KIND, either express or implied. See the LGPL or the MPL for
* the specific language governing rights and limitations.
*
* The Original Code is the cairo graphics library.
*
* The Initial Developer of the Original Code is University of Southern
* California.
*
* Contributor(s):
* Carl D. Worth <cworth@cworth.org>
*/
#include "cairoint.h"
#include "cairo-array-private.h"
#include "cairo-clip-inline.h"
#include "cairo-clip-private.h"
#include "cairo-damage-private.h"
#include "cairo-device-private.h"
#include "cairo-error-private.h"
#include "cairo-list-inline.h"
#include "cairo-image-surface-inline.h"
#include "cairo-recording-surface-private.h"
#include "cairo-region-private.h"
#include "cairo-surface-inline.h"
#include "cairo-tee-surface-private.h"
/**
* SECTION:cairo-surface
* @Title: cairo_surface_t
* @Short_Description: Base class for surfaces
* @See_Also: #cairo_t, #cairo_pattern_t
*
* #cairo_surface_t is the abstract type representing all different drawing
* targets that cairo can render to. The actual drawings are
* performed using a cairo <firstterm>context</firstterm>.
*
* A cairo surface is created by using <firstterm>backend</firstterm>-specific
* constructors, typically of the form
* <function>cairo_<emphasis>backend</emphasis>_surface_create(<!-- -->)</function>.
*
* Most surface types allow accessing the surface without using Cairo
* functions. If you do this, keep in mind that it is mandatory that you call
* cairo_surface_flush() before reading from or writing to the surface and that
* you must use cairo_surface_mark_dirty() after modifying it.
* <example>
* <title>Directly modifying an image surface</title>
* <programlisting>
* void
* modify_image_surface (cairo_surface_t *surface)
* {
* unsigned char *data;
* int width, height, stride;
*
* // flush to ensure all writing to the image was done
* cairo_surface_flush (surface);
*
* // modify the image
* data = cairo_image_surface_get_data (surface);
* width = cairo_image_surface_get_width (surface);
* height = cairo_image_surface_get_height (surface);
* stride = cairo_image_surface_get_stride (surface);
* modify_image_data (data, width, height, stride);
*
* // mark the image dirty so Cairo clears its caches.
* cairo_surface_mark_dirty (surface);
* }
* </programlisting>
* </example>
* Note that for other surface types it might be necessary to acquire the
* surface's device first. See cairo_device_acquire() for a discussion of
* devices.
**/
#define DEFINE_NIL_SURFACE(status, name) \
const cairo_surface_t name = { \
NULL, /* backend */ \
NULL, /* device */ \
CAIRO_SURFACE_TYPE_IMAGE, /* type */ \
CAIRO_CONTENT_COLOR, /* content */ \
CAIRO_REFERENCE_COUNT_INVALID, /* ref_count */ \
status, /* status */ \
0, /* unique id */ \
0, /* serial */ \
NULL, /* damage */ \
FALSE, /* _finishing */ \
FALSE, /* finished */ \
TRUE, /* is_clear */ \
FALSE, /* has_font_options */ \
FALSE, /* owns_device */ \
FALSE, /* is_vector */ \
{ 0, 0, 0, NULL, }, /* user_data */ \
{ 0, 0, 0, NULL, }, /* mime_data */ \
{ 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform */ \
{ 1.0, 0.0, 0.0, 1.0, 0.0, 0.0 }, /* device_transform_inverse */ \
{ NULL, NULL }, /* device_transform_observers */ \
0.0, /* x_resolution */ \
0.0, /* y_resolution */ \
0.0, /* x_fallback_resolution */ \
0.0, /* y_fallback_resolution */ \
NULL, /* snapshot_of */ \
NULL, /* snapshot_detach */ \
{ NULL, NULL }, /* snapshots */ \
{ NULL, NULL }, /* snapshot */ \
{ CAIRO_ANTIALIAS_DEFAULT, /* antialias */ \
CAIRO_SUBPIXEL_ORDER_DEFAULT, /* subpixel_order */ \
CAIRO_LCD_FILTER_DEFAULT, /* lcd_filter */ \
CAIRO_HINT_STYLE_DEFAULT, /* hint_style */ \
CAIRO_HINT_METRICS_DEFAULT, /* hint_metrics */ \
CAIRO_ROUND_GLYPH_POS_DEFAULT /* round_glyph_positions */ \
} /* font_options */ \
}
/* XXX error object! */
static DEFINE_NIL_SURFACE(CAIRO_STATUS_NO_MEMORY, _cairo_surface_nil);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_SURFACE_TYPE_MISMATCH, _cairo_surface_nil_surface_type_mismatch);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STATUS, _cairo_surface_nil_invalid_status);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_CONTENT, _cairo_surface_nil_invalid_content);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_FORMAT, _cairo_surface_nil_invalid_format);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_VISUAL, _cairo_surface_nil_invalid_visual);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_FILE_NOT_FOUND, _cairo_surface_nil_file_not_found);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_TEMP_FILE_ERROR, _cairo_surface_nil_temp_file_error);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_READ_ERROR, _cairo_surface_nil_read_error);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_WRITE_ERROR, _cairo_surface_nil_write_error);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_STRIDE, _cairo_surface_nil_invalid_stride);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_INVALID_SIZE, _cairo_surface_nil_invalid_size);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_TYPE_MISMATCH, _cairo_surface_nil_device_type_mismatch);
static DEFINE_NIL_SURFACE(CAIRO_STATUS_DEVICE_ERROR, _cairo_surface_nil_device_error);
static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_UNSUPPORTED, _cairo_surface_nil_unsupported);
static DEFINE_NIL_SURFACE(CAIRO_INT_STATUS_NOTHING_TO_DO, _cairo_surface_nil_nothing_to_do);
static void _cairo_surface_finish_snapshots (cairo_surface_t *surface);
static void _cairo_surface_finish (cairo_surface_t *surface);
/**
* _cairo_surface_set_error:
* @surface: a surface
* @status: a status value indicating an error
*
* Atomically sets surface->status to @status and calls _cairo_error;
* Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal
* status values.
*
* All assignments of an error status to surface->status should happen
* through _cairo_surface_set_error(). Note that due to the nature of
* the atomic operation, it is not safe to call this function on the
* nil objects.
*
* The purpose of this function is to allow the user to set a
* breakpoint in _cairo_error() to generate a stack trace for when the
* user causes cairo to detect an error.
*
* Return value: the error status.
**/
cairo_int_status_t
_cairo_surface_set_error (cairo_surface_t *surface,
cairo_int_status_t status)
{
/* NOTHING_TO_DO is magic. We use it to break out of the inner-most
* surface function, but anything higher just sees "success".
*/
if (status == CAIRO_INT_STATUS_NOTHING_TO_DO)
status = CAIRO_INT_STATUS_SUCCESS;
if (status == CAIRO_INT_STATUS_SUCCESS ||
status >= (int)CAIRO_INT_STATUS_LAST_STATUS)
return status;
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. */
_cairo_status_set_error (&surface->status, (cairo_status_t)status);
return _cairo_error (status);
}
/**
* cairo_surface_get_type:
* @surface: a #cairo_surface_t
*
* This function returns the type of the backend used to create
* a surface. See #cairo_surface_type_t for available types.
*
* Return value: The type of @surface.
*
* Since: 1.2
**/
cairo_surface_type_t
cairo_surface_get_type (cairo_surface_t *surface)
{
/* We don't use surface->backend->type here so that some of the
* special "wrapper" surfaces such as cairo_paginated_surface_t
* can override surface->type with the type of the "child"
* surface. */
return surface->type;
}
/**
* cairo_surface_get_content:
* @surface: a #cairo_surface_t
*
* This function returns the content type of @surface which indicates
* whether the surface contains color and/or alpha information. See
* #cairo_content_t.
*
* Return value: The content type of @surface.
*
* Since: 1.2
**/
cairo_content_t
cairo_surface_get_content (cairo_surface_t *surface)
{
return surface->content;
}
/**
* cairo_surface_status:
* @surface: a #cairo_surface_t
*
* Checks whether an error has previously occurred for this
* surface.
*
* Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NULL_POINTER,
* %CAIRO_STATUS_NO_MEMORY, %CAIRO_STATUS_READ_ERROR,
* %CAIRO_STATUS_INVALID_CONTENT, %CAIRO_STATUS_INVALID_FORMAT, or
* %CAIRO_STATUS_INVALID_VISUAL.
*
* Since: 1.0
**/
cairo_status_t
cairo_surface_status (cairo_surface_t *surface)
{
return surface->status;
}
slim_hidden_def (cairo_surface_status);
static unsigned int
_cairo_surface_allocate_unique_id (void)
{
static cairo_atomic_int_t unique_id;
#if CAIRO_NO_MUTEX
if (++unique_id == 0)
unique_id = 1;
return unique_id;
#else
cairo_atomic_int_t old, id;
do {
old = _cairo_atomic_uint_get (&unique_id);
id = old + 1;
if (id == 0)
id = 1;
} while (! _cairo_atomic_uint_cmpxchg (&unique_id, old, id));
return id;
#endif
}
/**
* cairo_surface_get_device:
* @surface: a #cairo_surface_t
*
* This function returns the device for a @surface.
* See #cairo_device_t.
*
* Return value: The device for @surface or %NULL if the surface does
* not have an associated device.
*
* Since: 1.10
**/
cairo_device_t *
cairo_surface_get_device (cairo_surface_t *surface)
{
if (unlikely (surface->status))
return _cairo_device_create_in_error (surface->status);
return surface->device;
}
static cairo_bool_t
_cairo_surface_has_snapshots (cairo_surface_t *surface)
{
return ! cairo_list_is_empty (&surface->snapshots);
}
static cairo_bool_t
_cairo_surface_has_mime_data (cairo_surface_t *surface)
{
return surface->mime_data.num_elements != 0;
}
static void
_cairo_surface_detach_mime_data (cairo_surface_t *surface)
{
if (! _cairo_surface_has_mime_data (surface))
return;
_cairo_user_data_array_fini (&surface->mime_data);
_cairo_user_data_array_init (&surface->mime_data);
}
static void
_cairo_surface_detach_snapshots (cairo_surface_t *surface)
{
while (_cairo_surface_has_snapshots (surface)) {
_cairo_surface_detach_snapshot (cairo_list_first_entry (&surface->snapshots,
cairo_surface_t,
snapshot));
}
}
void
_cairo_surface_detach_snapshot (cairo_surface_t *snapshot)
{
assert (snapshot->snapshot_of != NULL);
snapshot->snapshot_of = NULL;
cairo_list_del (&snapshot->snapshot);
if (snapshot->snapshot_detach != NULL)
snapshot->snapshot_detach (snapshot);
cairo_surface_destroy (snapshot);
}
void
_cairo_surface_attach_snapshot (cairo_surface_t *surface,
cairo_surface_t *snapshot,
cairo_surface_func_t detach_func)
{
assert (surface != snapshot);
assert (snapshot->snapshot_of != surface);
cairo_surface_reference (snapshot);
if (snapshot->snapshot_of != NULL)
_cairo_surface_detach_snapshot (snapshot);
snapshot->snapshot_of = surface;
snapshot->snapshot_detach = detach_func;
cairo_list_add (&snapshot->snapshot, &surface->snapshots);
assert (_cairo_surface_has_snapshot (surface, snapshot->backend) == snapshot);
}
cairo_surface_t *
_cairo_surface_has_snapshot (cairo_surface_t *surface,
const cairo_surface_backend_t *backend)
{
cairo_surface_t *snapshot;
cairo_list_foreach_entry (snapshot, cairo_surface_t,
&surface->snapshots, snapshot)
{
if (snapshot->backend == backend)
return snapshot;
}
return NULL;
}
cairo_status_t
_cairo_surface_begin_modification (cairo_surface_t *surface)
{
assert (surface->status == CAIRO_STATUS_SUCCESS);
assert (! surface->finished);
return _cairo_surface_flush (surface, 1);
}
void
_cairo_surface_init (cairo_surface_t *surface,
const cairo_surface_backend_t *backend,
cairo_device_t *device,
cairo_content_t content,
cairo_bool_t is_vector)
{
CAIRO_MUTEX_INITIALIZE ();
surface->backend = backend;
surface->device = cairo_device_reference (device);
surface->content = content;
surface->type = backend->type;
surface->is_vector = is_vector;
CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1);
surface->status = CAIRO_STATUS_SUCCESS;
surface->unique_id = _cairo_surface_allocate_unique_id ();
surface->finished = FALSE;
surface->_finishing = FALSE;
surface->is_clear = FALSE;
surface->serial = 0;
surface->damage = NULL;
surface->owns_device = (device != NULL);
_cairo_user_data_array_init (&surface->user_data);
_cairo_user_data_array_init (&surface->mime_data);
cairo_matrix_init_identity (&surface->device_transform);
cairo_matrix_init_identity (&surface->device_transform_inverse);
cairo_list_init (&surface->device_transform_observers);
surface->x_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT;
surface->y_resolution = CAIRO_SURFACE_RESOLUTION_DEFAULT;
surface->x_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT;
surface->y_fallback_resolution = CAIRO_SURFACE_FALLBACK_RESOLUTION_DEFAULT;
cairo_list_init (&surface->snapshots);
surface->snapshot_of = NULL;
surface->has_font_options = FALSE;
}
static void
_cairo_surface_copy_similar_properties (cairo_surface_t *surface,
cairo_surface_t *other)
{
if (other->has_font_options || other->backend != surface->backend) {
cairo_font_options_t options;
cairo_surface_get_font_options (other, &options);
_cairo_surface_set_font_options (surface, &options);
}
cairo_surface_set_fallback_resolution (surface,
other->x_fallback_resolution,
other->y_fallback_resolution);
}
/**
* cairo_surface_create_similar:
* @other: an existing surface used to select the backend of the new surface
* @content: the content 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 surface that is as compatible as possible with an
* existing surface. For example the new surface will have the same
* fallback resolution and font options as @other. Generally, the new
* surface will also use the same backend as @other, unless that is
* not possible for some reason. The type of the returned surface may
* be examined with cairo_surface_get_type().
*
* Initially the surface contents are all 0 (transparent if contents
* have transparency, black otherwise.)
*
* Use cairo_surface_create_similar_image() if you need an image surface
* which can be painted quickly to the target surface.
*
* Return value: a pointer to the newly allocated 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.
*
* Since: 1.0
**/
cairo_surface_t *
cairo_surface_create_similar (cairo_surface_t *other,
cairo_content_t content,
int width,
int height)
{
cairo_surface_t *surface;
cairo_status_t status;
cairo_solid_pattern_t pattern;
if (unlikely (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_CONTENT_VALID (content)))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_CONTENT);
if (unlikely (other->status))
return _cairo_surface_create_in_error (other->status);
/* We inherit the device scale, so create a larger surface */
width = width * other->device_transform.xx;
height = height * other->device_transform.yy;
surface = NULL;
if (other->backend->create_similar)
surface = other->backend->create_similar (other, content, width, height);
if (surface == NULL)
surface = cairo_surface_create_similar_image (other,
_cairo_format_from_content (content),
width, height);
if (unlikely (surface->status))
return surface;
_cairo_surface_copy_similar_properties (surface, other);
cairo_surface_set_device_scale (surface,
other->device_transform.xx,
other->device_transform.yy);
if (unlikely (surface->status))
return surface;
_cairo_pattern_init_solid (&pattern, CAIRO_COLOR_TRANSPARENT);
status = _cairo_surface_paint (surface,
CAIRO_OPERATOR_CLEAR,
&pattern.base, NULL);
if (unlikely (status)) {
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
}
assert (surface->is_clear);
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. However, this surface
* can still be used like any normal image surface.
*
* Initially the surface contents are all 0 (transparent if contents
* have transparency, black otherwise.)
*
* Use cairo_surface_create_similar() if you don't need an image 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.
*
* Since: 1.12
**/
cairo_surface_t *
cairo_surface_create_similar_image (cairo_surface_t *other,
cairo_format_t format,
int width,
int height)
{
cairo_surface_t *image;
if (unlikely (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_STATUS_INVALID_FORMAT);
image = NULL;
if (other->backend->create_similar_image)
image = other->backend->create_similar_image (other,
format, width, height);
if (image == NULL)
image = cairo_image_surface_create (format, width, height);
assert (image->is_clear);
return image;
}
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 = 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)
return status;
}
_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
*
* 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_rectangle_int_t rect;
cairo_image_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);
if (extents == NULL) {
if (unlikely (! surface->backend->get_extents (surface, &rect)))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
extents = &rect;
} else {
cairo_rectangle_int_t surface_extents;
/* If this surface is bounded, we can't map parts
* that are outside of it. */
if (likely (surface->backend->get_extents (surface, &surface_extents))) {
if (unlikely (! _cairo_rectangle_contains_rectangle (&surface_extents, extents)))
return _cairo_surface_create_in_error (CAIRO_STATUS_INVALID_SIZE);
}
}
image = _cairo_surface_map_to_image (surface, extents);
status = image->base.status;
if (unlikely (status)) {
cairo_surface_destroy (&image->base);
return _cairo_surface_create_in_error (status);
}
if (image->format == CAIRO_FORMAT_INVALID) {
cairo_surface_destroy (&image->base);
image = _cairo_image_surface_clone_subimage (surface, extents);
}
return &image->base;
}
/**
* 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.
*
* Since: 1.12
**/
void
cairo_surface_unmap_image (cairo_surface_t *surface,
cairo_surface_t *image)
{
cairo_int_status_t status = CAIRO_STATUS_SUCCESS;
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;
}
if (unlikely (! _cairo_surface_is_image (image))) {
status = _cairo_error (CAIRO_STATUS_SURFACE_TYPE_MISMATCH);
goto error;
}
status = _cairo_surface_unmap_image (surface,
(cairo_image_surface_t *) image);
if (unlikely (status))
_cairo_surface_set_error (surface, status);
return;
error:
_cairo_surface_set_error (surface, status);
cairo_surface_finish (image);
cairo_surface_destroy (image);
}
cairo_surface_t *
_cairo_surface_create_scratch (cairo_surface_t *other,
cairo_content_t content,
int width,
int height,
const cairo_color_t *color)
{
cairo_surface_t *surface;
cairo_status_t status;
cairo_solid_pattern_t pattern;
if (unlikely (other->status))
return _cairo_surface_create_in_error (other->status);
surface = NULL;
if (other->backend->create_similar)
surface = other->backend->create_similar (other, content, width, height);
if (surface == NULL)
surface = cairo_surface_create_similar_image (other,
_cairo_format_from_content (content),
width, height);
if (unlikely (surface->status))
return surface;
_cairo_surface_copy_similar_properties (surface, other);
if (unlikely (surface->status))
return surface;
if (color) {
_cairo_pattern_init_solid (&pattern, color);
status = _cairo_surface_paint (surface,
color == CAIRO_COLOR_TRANSPARENT ?
CAIRO_OPERATOR_CLEAR : CAIRO_OPERATOR_SOURCE,
&pattern.base, NULL);
if (unlikely (status)) {
cairo_surface_destroy (surface);
surface = _cairo_surface_create_in_error (status);
}
}
return surface;
}
/**
* cairo_surface_reference:
* @surface: a #cairo_surface_t
*
* Increases the reference count on @surface by one. This prevents
* @surface from being destroyed until a matching call to
* cairo_surface_destroy() is made.
*
* The number of references to a #cairo_surface_t can be get using
* cairo_surface_get_reference_count().
*
* Return value: the referenced #cairo_surface_t.
*
* Since: 1.0
**/
cairo_surface_t *
cairo_surface_reference (cairo_surface_t *surface)
{
if (surface == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
return surface;
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
_cairo_reference_count_inc (&surface->ref_count);
return surface;
}
slim_hidden_def (cairo_surface_reference);
/**
* cairo_surface_destroy:
* @surface: a #cairo_surface_t
*
* Decreases the reference count on @surface by one. If the result is
* zero, then @surface and all associated resources are freed. See
* cairo_surface_reference().
*
* Since: 1.0
**/
void
cairo_surface_destroy (cairo_surface_t *surface)
{
if (surface == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
return;
assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
if (! _cairo_reference_count_dec_and_test (&surface->ref_count))
return;
assert (surface->snapshot_of == NULL);
if (! surface->finished) {
_cairo_surface_finish_snapshots (surface);
/* We may have been referenced by a snapshot prior to have
* detaching it with the copy-on-write.
*/
if (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count))
return;
_cairo_surface_finish (surface);
}
if (surface->damage)
_cairo_damage_destroy (surface->damage);
_cairo_user_data_array_fini (&surface->user_data);
_cairo_user_data_array_fini (&surface->mime_data);
if (surface->owns_device)
cairo_device_destroy (surface->device);
assert (surface->snapshot_of == NULL);
assert (! _cairo_surface_has_snapshots (surface));
/* paranoid check that nobody took a reference whilst finishing */
assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
free (surface);
}
slim_hidden_def(cairo_surface_destroy);
/**
* cairo_surface_get_reference_count:
* @surface: a #cairo_surface_t
*
* Returns the current reference count of @surface.
*
* Return value: the current reference count of @surface. If the
* object is a nil object, 0 will be returned.
*
* Since: 1.4
**/
unsigned int
cairo_surface_get_reference_count (cairo_surface_t *surface)
{
if (surface == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
return 0;
return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count);
}
static void
_cairo_surface_finish_snapshots (cairo_surface_t *surface)
{
cairo_status_t status;
/* update the snapshots *before* we declare the surface as finished */
surface->_finishing = TRUE;
status = _cairo_surface_flush (surface, 0);
(void) status;
}
static void
_cairo_surface_finish (cairo_surface_t *surface)
{
cairo_status_t status;
surface->finished = TRUE;
/* call finish even if in error mode */
if (surface->backend->finish) {
status = surface->backend->finish (surface);
if (unlikely (status))
_cairo_surface_set_error (surface, status);
}
assert (surface->snapshot_of == NULL);
assert (!_cairo_surface_has_snapshots (surface));
}
/**
* cairo_surface_finish:
* @surface: the #cairo_surface_t to finish
*
* This function finishes the surface and drops all references to
* external resources. For example, for the Xlib backend it means
* that cairo will no longer access the drawable, which can be freed.
* After calling cairo_surface_finish() the only valid operations on a
* surface are getting and setting user, referencing and
* destroying, and flushing and finishing it.
* Further drawing to the surface will not affect the
* surface but will instead trigger a %CAIRO_STATUS_SURFACE_FINISHED
* error.
*
* When the last call to cairo_surface_destroy() decreases the
* reference count to zero, cairo will call cairo_surface_finish() if
* it hasn't been called already, before freeing the resources
* associated with the surface.
*
* Since: 1.0
**/
void
cairo_surface_finish (cairo_surface_t *surface)
{
if (surface == NULL)
return;
if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
return;
if (surface->finished)
return;
/* We have to be careful when decoupling potential reference cycles */
cairo_surface_reference (surface);
_cairo_surface_finish_snapshots (surface);
/* XXX need to block and wait for snapshot references */
_cairo_surface_finish (surface);
cairo_surface_destroy (surface);
}
slim_hidden_def (cairo_surface_finish);
/**
* _cairo_surface_release_device_reference:
* @surface: a #cairo_surface_t
*
* This function makes @surface release the reference to its device. The
* function is intended to be used for avoiding cycling references for
* surfaces that are owned by their device, for example cache surfaces.
* Note that the @surface will still assume that the device is available.
* So it is the caller's responsibility to ensure the device stays around
* until the @surface is destroyed. Just calling cairo_surface_finish() is
* not enough.
**/
void
_cairo_surface_release_device_reference (cairo_surface_t *surface)
{
assert (surface->owns_device);
cairo_device_destroy (surface->device);
surface->owns_device = FALSE;
}
/**
* cairo_surface_get_user_data:
* @surface: a #cairo_surface_t
* @key: the address of the #cairo_user_data_key_t the user data was
* attached to
*
* Return user data previously attached to @surface using the specified
* key. If no user data has been attached with the given key this
* function returns %NULL.
*
* Return value: the user data previously attached or %NULL.
*
* Since: 1.0
**/
void *
cairo_surface_get_user_data (cairo_surface_t *surface,
const cairo_user_data_key_t *key)
{
/* Prevent reads of the array during teardown */
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return NULL;
return _cairo_user_data_array_get_data (&surface->user_data, key);
}
/**
* cairo_surface_set_user_data:
* @surface: a #cairo_surface_t
* @key: the address of a #cairo_user_data_key_t to attach the user data to
* @user_data: the user data to attach to the surface
* @destroy: a #cairo_destroy_func_t which will be called when the
* surface is destroyed or when new user data is attached using the
* same key.
*
* Attach user data to @surface. To remove user data from a surface,
* call this function with the key that was used to set it and %NULL
* for @data.
*
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
* slot could not be allocated for the user data.
*
* Since: 1.0
**/
cairo_status_t
cairo_surface_set_user_data (cairo_surface_t *surface,
const cairo_user_data_key_t *key,
void *user_data,
cairo_destroy_func_t destroy)
{
if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
return surface->status;
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
return _cairo_user_data_array_set_data (&surface->user_data,
key, user_data, destroy);
}
/**
* cairo_surface_get_mime_data:
* @surface: a #cairo_surface_t
* @mime_type: the mime type of the image data
* @data: the image data to attached to the surface
* @length: the length of the image data
*
* Return mime data previously attached to @surface using the
* specified mime type. If no data has been attached with the given
* mime type, @data is set %NULL.
*
* Since: 1.10
**/
void
cairo_surface_get_mime_data (cairo_surface_t *surface,
const char *mime_type,
const unsigned char **data,
unsigned long *length)
{
cairo_user_data_slot_t *slots;
int i, num_slots;
*data = NULL;
*length = 0;
/* Prevent reads of the array during teardown */
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return;
/* The number of mime-types attached to a surface is usually small,
* typically zero. Therefore it is quicker to do a strcmp() against
* each key than it is to intern the string (i.e. compute a hash,
* search the hash table, and do a final strcmp).
*/
num_slots = surface->mime_data.num_elements;
slots = _cairo_array_index (&surface->mime_data, 0);
for (i = 0; i < num_slots; i++) {
if (slots[i].key != NULL && strcmp ((char *) slots[i].key, mime_type) == 0) {
cairo_mime_data_t *mime_data = slots[i].user_data;
*data = mime_data->data;
*length = mime_data->length;
return;
}
}
}
slim_hidden_def (cairo_surface_get_mime_data);
static void
_cairo_mime_data_destroy (void *ptr)
{
cairo_mime_data_t *mime_data = ptr;
if (! _cairo_reference_count_dec_and_test (&mime_data->ref_count))
return;
if (mime_data->destroy && mime_data->closure)
mime_data->destroy (mime_data->closure);
free (mime_data);
}
/**
* CAIRO_MIME_TYPE_JBIG2:
*
* Joint Bi-level Image Experts Group image coding standard (ISO/IEC 11544).
*
* Since: 1.14
**/
/**
* CAIRO_MIME_TYPE_JBIG2_GLOBAL:
*
* Joint Bi-level Image Experts Group image coding standard (ISO/IEC 11544) global segment.
*
* Since: 1.14
**/
/**
* CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID:
*
* An unique identifier shared by a JBIG2 global segment and all JBIG2 images
* that depend on the global segment.
*
* Since: 1.14
**/
/**
* CAIRO_MIME_TYPE_JP2:
*
* The Joint Photographic Experts Group (JPEG) 2000 image coding standard (ISO/IEC 15444-1).
*
* Since: 1.10
**/
/**
* CAIRO_MIME_TYPE_JPEG:
*
* The Joint Photographic Experts Group (JPEG) image coding standard (ISO/IEC 10918-1).
*
* Since: 1.10
**/
/**
* CAIRO_MIME_TYPE_PNG:
*
* The Portable Network Graphics image file format (ISO/IEC 15948).
*
* Since: 1.10
**/
/**
* CAIRO_MIME_TYPE_URI:
*
* URI for an image file (unofficial MIME type).
*
* Since: 1.10
**/
/**
* CAIRO_MIME_TYPE_UNIQUE_ID:
*
* Unique identifier for a surface (cairo specific MIME type). All surfaces with
* the same unique identifier will only be embedded once.
*
* Since: 1.12
**/
/**
* cairo_surface_set_mime_data:
* @surface: a #cairo_surface_t
* @mime_type: the MIME type of the image data
* @data: the image data to attach to the surface
* @length: the length of the image data
* @destroy: a #cairo_destroy_func_t which will be called when the
* surface is destroyed or when new image data is attached using the
* same mime type.
* @closure: the data to be passed to the @destroy notifier
*
* Attach an image in the format @mime_type to @surface. To remove
* the data from a surface, call this function with same mime type
* and %NULL for @data.
*
* The attached image (or filename) data can later be used by backends
* which support it (currently: PDF, PS, SVG and Win32 Printing
* surfaces) to emit this data instead of making a snapshot of the
* @surface. This approach tends to be faster and requires less
* memory and disk space.
*
* The recognized MIME types are the following: %CAIRO_MIME_TYPE_JPEG,
* %CAIRO_MIME_TYPE_PNG, %CAIRO_MIME_TYPE_JP2, %CAIRO_MIME_TYPE_URI,
* %CAIRO_MIME_TYPE_UNIQUE_ID, %CAIRO_MIME_TYPE_JBIG2,
* %CAIRO_MIME_TYPE_JBIG2_GLOBAL, %CAIRO_MIME_TYPE_JBIG2_GLOBAL_ID.
*
* See corresponding backend surface docs for details about which MIME
* types it can handle. Caution: the associated MIME data will be
* discarded if you draw on the surface afterwards. Use this function
* with care.
*
* Even if a backend supports a MIME type, that does not mean cairo
* will always be able to use the attached MIME data. For example, if
* the backend does not natively support the compositing operation used
* to apply the MIME data to the backend. In that case, the MIME data
* will be ignored. Therefore, to apply an image in all cases, it is best
* to create an image surface which contains the decoded image data and
* then attach the MIME data to that. This ensures the image will always
* be used while still allowing the MIME data to be used whenever
* possible.
*
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a
* slot could not be allocated for the user data.
*
* Since: 1.10
**/
cairo_status_t
cairo_surface_set_mime_data (cairo_surface_t *surface,
const char *mime_type,
const unsigned char *data,
unsigned long length,
cairo_destroy_func_t destroy,
void *closure)
{
cairo_status_t status;
cairo_mime_data_t *mime_data;
if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
return surface->status;
if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
status = _cairo_intern_string (&mime_type, -1);
if (unlikely (status))
return _cairo_surface_set_error (surface, status);
if (data != NULL) {
mime_data = malloc (sizeof (cairo_mime_data_t));
if (unlikely (mime_data == NULL))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_NO_MEMORY));
CAIRO_REFERENCE_COUNT_INIT (&mime_data->ref_count, 1);
mime_data->data = (unsigned char *) data;
mime_data->length = length;
mime_data->destroy = destroy;
mime_data->closure = closure;
} else
mime_data = NULL;
status = _cairo_user_data_array_set_data (&surface->mime_data,
(cairo_user_data_key_t *) mime_type,
mime_data,
_cairo_mime_data_destroy);
if (unlikely (status)) {
free (mime_data);
return _cairo_surface_set_error (surface, status);
}
return CAIRO_STATUS_SUCCESS;
}
slim_hidden_def (cairo_surface_set_mime_data);
/**
* cairo_surface_supports_mime_type:
* @surface: a #cairo_surface_t
* @mime_type: the mime type
*
* Return whether @surface supports @mime_type.
*
* Return value: %TRUE if @surface supports
* @mime_type, %FALSE otherwise
*
* Since: 1.12
**/
cairo_bool_t
cairo_surface_supports_mime_type (cairo_surface_t *surface,
const char *mime_type)
{
const char **types;
if (unlikely (surface->status))
return FALSE;
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return FALSE;
}
if (surface->backend->get_supported_mime_types) {
types = surface->backend->get_supported_mime_types (surface);
if (types) {
while (*types) {
if (strcmp (*types, mime_type) == 0)
return TRUE;
types++;
}
}
}
return FALSE;
}
slim_hidden_def (cairo_surface_supports_mime_type);
static void
_cairo_mime_data_reference (const void *key, void *elt, void *closure)
{
cairo_mime_data_t *mime_data = elt;
_cairo_reference_count_inc (&mime_data->ref_count);
}
cairo_status_t
_cairo_surface_copy_mime_data (cairo_surface_t *dst,
cairo_surface_t *src)
{
cairo_status_t status;
if (dst->status)
return dst->status;
if (src->status)
return _cairo_surface_set_error (dst, src->status);
/* first copy the mime-data, discarding any already set on dst */
status = _cairo_user_data_array_copy (&dst->mime_data, &src->mime_data);
if (unlikely (status))
return _cairo_surface_set_error (dst, status);
/* now increment the reference counters for the copies */
_cairo_user_data_array_foreach (&dst->mime_data,
_cairo_mime_data_reference,
NULL);
return CAIRO_STATUS_SUCCESS;
}
/**
* _cairo_surface_set_font_options:
* @surface: a #cairo_surface_t
* @options: a #cairo_font_options_t object that contains the
* options to use for this surface instead of backend's default
* font options.
*
* Sets the default font rendering options for the surface.
* This is useful to correctly propagate default font options when
* falling back to an image surface in a backend implementation.
* This affects the options returned in cairo_surface_get_font_options().
*
* If @options is %NULL the surface options are reset to those of
* the backend default.
**/
void
_cairo_surface_set_font_options (cairo_surface_t *surface,
cairo_font_options_t *options)
{
if (surface->status)
return;
assert (surface->snapshot_of == NULL);
if (surface->finished) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
if (options) {
surface->has_font_options = TRUE;
_cairo_font_options_init_copy (&surface->font_options, options);
} else {
surface->has_font_options = FALSE;
}
}
/**
* cairo_surface_get_font_options:
* @surface: a #cairo_surface_t
* @options: a #cairo_font_options_t object into which to store
* the retrieved options. All existing values are overwritten
*
* Retrieves the default font rendering options for the surface.
* This allows display surfaces to report the correct subpixel order
* for rendering on them, print surfaces to disable hinting of
* metrics and so forth. The result can then be used with
* cairo_scaled_font_create().
*
* Since: 1.0
**/
void
cairo_surface_get_font_options (cairo_surface_t *surface,
cairo_font_options_t *options)
{
if (cairo_font_options_status (options))
return;
if (surface->status) {
_cairo_font_options_init_default (options);
return;
}
if (! surface->has_font_options) {
surface->has_font_options = TRUE;
_cairo_font_options_init_default (&surface->font_options);
if (!surface->finished && surface->backend->get_font_options) {
surface->backend->get_font_options (surface, &surface->font_options);
}
}
_cairo_font_options_init_copy (options, &surface->font_options);
}
slim_hidden_def (cairo_surface_get_font_options);
cairo_status_t
_cairo_surface_flush (cairo_surface_t *surface, unsigned flags)
{
/* update the current snapshots *before* the user updates the surface */
_cairo_surface_detach_snapshots (surface);
if (surface->snapshot_of != NULL)
_cairo_surface_detach_snapshot (surface);
_cairo_surface_detach_mime_data (surface);
return __cairo_surface_flush (surface, flags);
}
/**
* cairo_surface_flush:
* @surface: a #cairo_surface_t
*
* Do any pending drawing for the surface and also restore any temporary
* modifications cairo has made to the surface's state. This function
* must be called before switching from drawing on the surface with
* cairo to drawing on it directly with native APIs, or accessing its
* memory outside of Cairo. If the surface doesn't support direct
* access, then this function does nothing.
*
* Since: 1.0
**/
void
cairo_surface_flush (cairo_surface_t *surface)
{
cairo_status_t status;
if (surface->status)
return;
if (surface->finished)
return;
status = _cairo_surface_flush (surface, 0);
if (unlikely (status))
_cairo_surface_set_error (surface, status);
}
slim_hidden_def (cairo_surface_flush);
/**
* cairo_surface_mark_dirty:
* @surface: a #cairo_surface_t
*
* Tells cairo that drawing has been done to surface using means other
* than cairo, and that cairo should reread any cached areas. Note
* that you must call cairo_surface_flush() before doing such drawing.
*
* Since: 1.0
**/
void
cairo_surface_mark_dirty (cairo_surface_t *surface)
{
cairo_rectangle_int_t extents;
if (unlikely (surface->status))
return;
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
_cairo_surface_get_extents (surface, &extents);
cairo_surface_mark_dirty_rectangle (surface,
extents.x, extents.y,
extents.width, extents.height);
}
slim_hidden_def (cairo_surface_mark_dirty);
/**
* cairo_surface_mark_dirty_rectangle:
* @surface: a #cairo_surface_t
* @x: X coordinate of dirty rectangle
* @y: Y coordinate of dirty rectangle
* @width: width of dirty rectangle
* @height: height of dirty rectangle
*
* Like cairo_surface_mark_dirty(), but drawing has been done only to
* the specified rectangle, so that cairo can retain cached contents
* for other parts of the surface.
*
* Any cached clip set on the surface will be reset by this function,
* to make sure that future cairo calls have the clip set that they
* expect.
*
* Since: 1.0
**/
void
cairo_surface_mark_dirty_rectangle (cairo_surface_t *surface,
int x,
int y,
int width,
int height)
{
cairo_status_t status;
if (unlikely (surface->status))
return;
assert (surface->snapshot_of == NULL);
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
/* The application *should* have called cairo_surface_flush() before
* modifying the surface independently of cairo (and thus having to
* call mark_dirty()). */
assert (! _cairo_surface_has_snapshots (surface));
assert (! _cairo_surface_has_mime_data (surface));
surface->is_clear = FALSE;
surface->serial++;
if (surface->damage) {
cairo_box_t box;
box.p1.x = x;
box.p1.y = y;
box.p2.x = x + width;
box.p2.y = y + height;
surface->damage = _cairo_damage_add_box (surface->damage, &box);
}
if (surface->backend->mark_dirty_rectangle != NULL) {
/* XXX: FRAGILE: We're ignoring the scaling component of
* device_transform here. I don't know what the right thing to
* do would actually be if there were some scaling here, but
* we avoid this since device_transfom scaling is not exported
* publicly and mark_dirty is not used internally. */
status = surface->backend->mark_dirty_rectangle (surface,
x + surface->device_transform.x0,
y + surface->device_transform.y0,
width, height);
if (unlikely (status))
_cairo_surface_set_error (surface, status);
}
}
slim_hidden_def (cairo_surface_mark_dirty_rectangle);
/**
* cairo_surface_set_device_scale:
* @surface: a #cairo_surface_t
* @x_scale: a scale factor in the X direction
* @y_scale: a scale factor in the Y direction
*
* Sets a scale that is multiplied to the device coordinates determined
* by the CTM when drawing to @surface. One common use for this is to
* render to very high resolution display devices at a scale factor, so
* that code that assumes 1 pixel will be a certain size will still work.
* Setting a transformation via cairo_translate() isn't
* sufficient to do this, since functions like
* cairo_device_to_user() will expose the hidden scale.
*
* Note that the scale affects drawing to the surface as well as
* using the surface in a source pattern.
*
* Since: 1.14
**/
void
cairo_surface_set_device_scale (cairo_surface_t *surface,
double x_scale,
double y_scale)
{
cairo_status_t status;
if (unlikely (surface->status))
return;
assert (surface->snapshot_of == NULL);
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
status = _cairo_surface_begin_modification (surface);
if (unlikely (status)) {
_cairo_surface_set_error (surface, status);
return;
}
surface->device_transform.xx = x_scale;
surface->device_transform.yy = y_scale;
surface->device_transform.xy = 0.0;
surface->device_transform.yx = 0.0;
surface->device_transform_inverse = surface->device_transform;
status = cairo_matrix_invert (&surface->device_transform_inverse);
/* should always be invertible unless given pathological input */
assert (status == CAIRO_STATUS_SUCCESS);
_cairo_observers_notify (&surface->device_transform_observers, surface);
}
slim_hidden_def (cairo_surface_set_device_scale);
/**
* cairo_surface_get_device_scale:
* @surface: a #cairo_surface_t
* @x_scale: the scale in the X direction, in device units
* @y_scale: the scale in the Y direction, in device units
*
* This function returns the previous device offset set by
* cairo_surface_set_device_scale().
*
* Since: 1.14
**/
void
cairo_surface_get_device_scale (cairo_surface_t *surface,
double *x_scale,
double *y_scale)
{
if (x_scale)
*x_scale = surface->device_transform.xx;
if (y_scale)
*y_scale = surface->device_transform.yy;
}
slim_hidden_def (cairo_surface_get_device_scale);
/**
* cairo_surface_set_device_offset:
* @surface: a #cairo_surface_t
* @x_offset: the offset in the X direction, in device units
* @y_offset: the offset in the Y direction, in device units
*
* Sets an offset that is added to the device coordinates determined
* by the CTM when drawing to @surface. One use case for this function
* is when we want to create a #cairo_surface_t that redirects drawing
* for a portion of an onscreen surface to an offscreen surface in a
* way that is completely invisible to the user of the cairo
* API. Setting a transformation via cairo_translate() isn't
* sufficient to do this, since functions like
* cairo_device_to_user() will expose the hidden offset.
*
* Note that the offset affects drawing to the surface as well as
* using the surface in a source pattern.
*
* Since: 1.0
**/
void
cairo_surface_set_device_offset (cairo_surface_t *surface,
double x_offset,
double y_offset)
{
cairo_status_t status;
if (unlikely (surface->status))
return;
assert (surface->snapshot_of == NULL);
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
status = _cairo_surface_begin_modification (surface);
if (unlikely (status)) {
_cairo_surface_set_error (surface, status);
return;
}
surface->device_transform.x0 = x_offset;
surface->device_transform.y0 = y_offset;
surface->device_transform_inverse = surface->device_transform;
status = cairo_matrix_invert (&surface->device_transform_inverse);
/* should always be invertible unless given pathological input */
assert (status == CAIRO_STATUS_SUCCESS);
_cairo_observers_notify (&surface->device_transform_observers, surface);
}
slim_hidden_def (cairo_surface_set_device_offset);
/**
* cairo_surface_get_device_offset:
* @surface: a #cairo_surface_t
* @x_offset: the offset in the X direction, in device units
* @y_offset: the offset in the Y direction, in device units
*
* This function returns the previous device offset set by
* cairo_surface_set_device_offset().
*
* Since: 1.2
**/
void
cairo_surface_get_device_offset (cairo_surface_t *surface,
double *x_offset,
double *y_offset)
{
if (x_offset)
*x_offset = surface->device_transform.x0;
if (y_offset)
*y_offset = surface->device_transform.y0;
}
slim_hidden_def (cairo_surface_get_device_offset);
/**
* cairo_surface_set_fallback_resolution:
* @surface: a #cairo_surface_t
* @x_pixels_per_inch: horizontal setting for pixels per inch
* @y_pixels_per_inch: vertical setting for pixels per inch
*
* Set the horizontal and vertical resolution for image fallbacks.
*
* When certain operations aren't supported natively by a backend,
* cairo will fallback by rendering operations to an image and then
* overlaying that image onto the output. For backends that are
* natively vector-oriented, this function can be used to set the
* resolution used for these image fallbacks, (larger values will
* result in more detailed images, but also larger file sizes).
*
* Some examples of natively vector-oriented backends are the ps, pdf,
* and svg backends.
*
* For backends that are natively raster-oriented, image fallbacks are
* still possible, but they are always performed at the native
* device resolution. So this function has no effect on those
* backends.
*
* Note: The fallback resolution only takes effect at the time of
* completing a page (with cairo_show_page() or cairo_copy_page()) so
* there is currently no way to have more than one fallback resolution
* in effect on a single page.
*
* The default fallback resoultion is 300 pixels per inch in both
* dimensions.
*
* Since: 1.2
**/
void
cairo_surface_set_fallback_resolution (cairo_surface_t *surface,
double x_pixels_per_inch,
double y_pixels_per_inch)
{
cairo_status_t status;
if (unlikely (surface->status))
return;
assert (surface->snapshot_of == NULL);
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
return;
}
if (x_pixels_per_inch <= 0 || y_pixels_per_inch <= 0) {
/* XXX Could delay raising the error until we fallback, but throwing
* the error here means that we can catch the real culprit.
*/
_cairo_surface_set_error (surface, CAIRO_STATUS_INVALID_MATRIX);
return;
}
status = _cairo_surface_begin_modification (surface);
if (unlikely (status)) {
_cairo_surface_set_error (surface, status);
return;
}
surface->x_fallback_resolution = x_pixels_per_inch;
surface->y_fallback_resolution = y_pixels_per_inch;
}
slim_hidden_def (cairo_surface_set_fallback_resolution);
/**
* cairo_surface_get_fallback_resolution:
* @surface: a #cairo_surface_t
* @x_pixels_per_inch: horizontal pixels per inch
* @y_pixels_per_inch: vertical pixels per inch
*
* This function returns the previous fallback resolution set by
* cairo_surface_set_fallback_resolution(), or default fallback
* resolution if never set.
*
* Since: 1.8
**/
void
cairo_surface_get_fallback_resolution (cairo_surface_t *surface,
double *x_pixels_per_inch,
double *y_pixels_per_inch)
{
if (x_pixels_per_inch)
*x_pixels_per_inch = surface->x_fallback_resolution;
if (y_pixels_per_inch)
*y_pixels_per_inch = surface->y_fallback_resolution;
}
cairo_bool_t
_cairo_surface_has_device_transform (cairo_surface_t *surface)
{
return ! _cairo_matrix_is_identity (&surface->device_transform);
}
/**
* _cairo_surface_acquire_source_image:
* @surface: a #cairo_surface_t
* @image_out: location to store a pointer to an image surface that
* has identical contents to @surface. This surface could be @surface
* itself, a surface held internal to @surface, or it could be a new
* surface with a copy of the relevant portion of @surface.
* @image_extra: location to store image specific backend data
*
* Gets an image surface to use when drawing as a fallback when drawing with
* @surface as a source. _cairo_surface_release_source_image() must be called
* when finished.
*
* Return value: %CAIRO_STATUS_SUCCESS if an image was stored in @image_out.
* %CAIRO_INT_STATUS_UNSUPPORTED if an image cannot be retrieved for the specified
* surface. Or %CAIRO_STATUS_NO_MEMORY.
**/
cairo_status_t
_cairo_surface_acquire_source_image (cairo_surface_t *surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_status_t status;
if (unlikely (surface->status))
return surface->status;
assert (!surface->finished);
if (surface->backend->acquire_source_image == NULL)
return CAIRO_INT_STATUS_UNSUPPORTED;
status = surface->backend->acquire_source_image (surface,
image_out, image_extra);
if (unlikely (status))
return _cairo_surface_set_error (surface, status);
_cairo_debug_check_image_surface_is_defined (&(*image_out)->base);
return CAIRO_STATUS_SUCCESS;
}
cairo_status_t
_cairo_surface_default_acquire_source_image (void *_surface,
cairo_image_surface_t **image_out,
void **image_extra)
{
cairo_surface_t *surface = _surface;
cairo_rectangle_int_t extents;
if (unlikely (! surface->backend->get_extents (surface, &extents)))
return _cairo_error (CAIRO_STATUS_INVALID_SIZE);
*image_out = _cairo_surface_map_to_image (surface, &extents);
*image_extra = NULL;
return (*image_out)->base.status;
}
/**
* _cairo_surface_release_source_image:
* @surface: a #cairo_surface_t
* @image_extra: same as return from the matching _cairo_surface_acquire_source_image()
*
* Releases any resources obtained with _cairo_surface_acquire_source_image()
**/
void
_cairo_surface_release_source_image (cairo_surface_t *surface,
cairo_image_surface_t *image,
void *image_extra)
{
assert (!surface->finished);
if (surface->backend->release_source_image)
surface->backend->release_source_image (surface, image, image_extra);
}
void
_cairo_surface_default_release_source_image (void *surface,
cairo_image_surface_t *image,
void *image_extra)
{
cairo_status_t ignored;
ignored = _cairo_surface_unmap_image (surface, image);
(void)ignored;
}
cairo_surface_t *
_cairo_surface_get_source (cairo_surface_t *surface,
cairo_rectangle_int_t *extents)
{
assert (surface->backend->source);
return surface->backend->source (surface, extents);
}
cairo_surface_t *
_cairo_surface_default_source (void *surface,
cairo_rectangle_int_t *extents)
{
if (extents)
_cairo_surface_get_extents(surface, extents);
return surface;
}
static cairo_status_t
_pattern_has_error (const cairo_pattern_t *pattern)
{
const cairo_surface_pattern_t *spattern;
if (unlikely (pattern->status))
return pattern->status;
if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
return CAIRO_STATUS_SUCCESS;
spattern = (const cairo_surface_pattern_t *) pattern;
if (unlikely (spattern->surface->status))
return spattern->surface->status;
if (unlikely (spattern->surface->finished))
return _cairo_error (CAIRO_STATUS_SURFACE_FINISHED);
return CAIRO_STATUS_SUCCESS;
}
static cairo_bool_t
nothing_to_do (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source)
{
if (_cairo_pattern_is_clear (source)) {
if (op == CAIRO_OPERATOR_OVER || op == CAIRO_OPERATOR_ADD)
return TRUE;
if (op == CAIRO_OPERATOR_SOURCE)
op = CAIRO_OPERATOR_CLEAR;
}
if (op == CAIRO_OPERATOR_CLEAR && surface->is_clear)
return TRUE;
if (op == CAIRO_OPERATOR_ATOP && (surface->content & CAIRO_CONTENT_COLOR) ==0)
return TRUE;
return FALSE;
}
cairo_status_t
_cairo_surface_paint (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_clip_t *clip)
{
cairo_int_status_t status;
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
status = _pattern_has_error (source);
if (unlikely (status))
return status;
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
status = surface->backend->paint (surface, op, source, clip);
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = op == CAIRO_OPERATOR_CLEAR && clip == NULL;
surface->serial++;
}
return _cairo_surface_set_error (surface, status);
}
cairo_status_t
_cairo_surface_mask (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_pattern_t *mask,
const cairo_clip_t *clip)
{
cairo_int_status_t status;
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
/* If the mask is blank, this is just an expensive no-op */
if (_cairo_pattern_is_clear (mask) &&
_cairo_operator_bounded_by_mask (op))
{
return CAIRO_STATUS_SUCCESS;
}
status = _pattern_has_error (source);
if (unlikely (status))
return status;
status = _pattern_has_error (mask);
if (unlikely (status))
return status;
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
status = surface->backend->mask (surface, op, source, mask, clip);
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
return _cairo_surface_set_error (surface, status);
}
cairo_status_t
_cairo_surface_fill_stroke (cairo_surface_t *surface,
cairo_operator_t fill_op,
const cairo_pattern_t *fill_source,
cairo_fill_rule_t fill_rule,
double fill_tolerance,
cairo_antialias_t fill_antialias,
cairo_path_fixed_t *path,
cairo_operator_t stroke_op,
const cairo_pattern_t *stroke_source,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *stroke_ctm,
const cairo_matrix_t *stroke_ctm_inverse,
double stroke_tolerance,
cairo_antialias_t stroke_antialias,
const cairo_clip_t *clip)
{
cairo_int_status_t status;
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
if (surface->is_clear &&
fill_op == CAIRO_OPERATOR_CLEAR &&
stroke_op == CAIRO_OPERATOR_CLEAR)
{
return CAIRO_STATUS_SUCCESS;
}
status = _pattern_has_error (fill_source);
if (unlikely (status))
return status;
status = _pattern_has_error (stroke_source);
if (unlikely (status))
return status;
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
if (surface->backend->fill_stroke) {
cairo_matrix_t dev_ctm = *stroke_ctm;
cairo_matrix_t dev_ctm_inverse = *stroke_ctm_inverse;
status = surface->backend->fill_stroke (surface,
fill_op, fill_source, fill_rule,
fill_tolerance, fill_antialias,
path,
stroke_op, stroke_source,
stroke_style,
&dev_ctm, &dev_ctm_inverse,
stroke_tolerance, stroke_antialias,
clip);
if (status != CAIRO_INT_STATUS_UNSUPPORTED)
goto FINISH;
}
status = _cairo_surface_fill (surface, fill_op, fill_source, path,
fill_rule, fill_tolerance, fill_antialias,
clip);
if (unlikely (status))
goto FINISH;
status = _cairo_surface_stroke (surface, stroke_op, stroke_source, path,
stroke_style, stroke_ctm, stroke_ctm_inverse,
stroke_tolerance, stroke_antialias,
clip);
if (unlikely (status))
goto FINISH;
FINISH:
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
return _cairo_surface_set_error (surface, status);
}
cairo_status_t
_cairo_surface_stroke (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_int_status_t status;
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
status = _pattern_has_error (source);
if (unlikely (status))
return status;
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
status = surface->backend->stroke (surface, op, source,
path, stroke_style,
ctm, ctm_inverse,
tolerance, antialias,
clip);
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
return _cairo_surface_set_error (surface, status);
}
cairo_status_t
_cairo_surface_fill (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
double tolerance,
cairo_antialias_t antialias,
const cairo_clip_t *clip)
{
cairo_int_status_t status;
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
status = _pattern_has_error (source);
if (unlikely (status))
return status;
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
status = surface->backend->fill (surface, op, source,
path, fill_rule,
tolerance, antialias,
clip);
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
return _cairo_surface_set_error (surface, status);
}
/**
* cairo_surface_copy_page:
* @surface: a #cairo_surface_t
*
* Emits the current page for backends that support multiple pages,
* but doesn't clear it, so that the contents of the current page will
* be retained for the next page. Use cairo_surface_show_page() if you
* want to get an empty page after the emission.
*
* There is a convenience function for this that takes a #cairo_t,
* namely cairo_copy_page().
*
* Since: 1.6
**/
void
cairo_surface_copy_page (cairo_surface_t *surface)
{
if (unlikely (surface->status))
return;
assert (surface->snapshot_of == NULL);
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
return;
}
/* It's fine if some backends don't implement copy_page */
if (surface->backend->copy_page == NULL)
return;
_cairo_surface_set_error (surface, surface->backend->copy_page (surface));
}
slim_hidden_def (cairo_surface_copy_page);
/**
* cairo_surface_show_page:
* @surface: a #cairo_Surface_t
*
* Emits and clears the current page for backends that support multiple
* pages. Use cairo_surface_copy_page() if you don't want to clear the page.
*
* There is a convenience function for this that takes a #cairo_t,
* namely cairo_show_page().
*
* Since: 1.6
**/
void
cairo_surface_show_page (cairo_surface_t *surface)
{
cairo_status_t status;
if (unlikely (surface->status))
return;
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
return;
}
status = _cairo_surface_begin_modification (surface);
if (unlikely (status)) {
_cairo_surface_set_error (surface, status);
return;
}
/* It's fine if some backends don't implement show_page */
if (surface->backend->show_page == NULL)
return;
_cairo_surface_set_error (surface, surface->backend->show_page (surface));
}
slim_hidden_def (cairo_surface_show_page);
/**
* _cairo_surface_get_extents:
* @surface: the #cairo_surface_t to fetch extents for
*
* This function returns a bounding box for the surface. The surface
* bounds are defined as a region beyond which no rendering will
* possibly be recorded, in other words, it is the maximum extent of
* potentially usable coordinates.
*
* For vector surfaces, (PDF, PS, SVG and recording-surfaces), the surface
* might be conceived as unbounded, but we force the user to provide a
* maximum size at the time of surface_create. So get_extents uses
* that size.
*
* Note: The coordinates returned are in "backend" space rather than
* "surface" space. That is, they are relative to the true (0,0)
* origin rather than the device_transform origin. This might seem a
* bit inconsistent with other #cairo_surface_t interfaces, but all
* current callers are within the surface layer where backend space is
* desired.
*
* This behavior would have to be changed is we ever exported a public
* variant of this function.
**/
cairo_bool_t
_cairo_surface_get_extents (cairo_surface_t *surface,
cairo_rectangle_int_t *extents)
{
cairo_bool_t bounded;
if (unlikely (surface->status))
goto zero_extents;
if (unlikely (surface->finished)) {
_cairo_surface_set_error(surface, CAIRO_STATUS_SURFACE_FINISHED);
goto zero_extents;
}
bounded = FALSE;
if (surface->backend->get_extents != NULL)
bounded = surface->backend->get_extents (surface, extents);
if (! bounded)
_cairo_unbounded_rectangle_init (extents);
return bounded;
zero_extents:
extents->x = extents->y = 0;
extents->width = extents->height = 0;
return TRUE;
}
/**
* cairo_surface_has_show_text_glyphs:
* @surface: a #cairo_surface_t
*
* Returns whether the surface supports
* sophisticated cairo_show_text_glyphs() operations. That is,
* whether it actually uses the provided text and cluster data
* to a cairo_show_text_glyphs() call.
*
* Note: Even if this function returns %FALSE, a
* cairo_show_text_glyphs() operation targeted at @surface will
* still succeed. It just will
* act like a cairo_show_glyphs() operation. Users can use this
* function to avoid computing UTF-8 text and cluster mapping if the
* target surface does not use it.
*
* Return value: %TRUE if @surface supports
* cairo_show_text_glyphs(), %FALSE otherwise
*
* Since: 1.8
**/
cairo_bool_t
cairo_surface_has_show_text_glyphs (cairo_surface_t *surface)
{
if (unlikely (surface->status))
return FALSE;
if (unlikely (surface->finished)) {
_cairo_surface_set_error (surface, CAIRO_STATUS_SURFACE_FINISHED);
return FALSE;
}
if (surface->backend->has_show_text_glyphs)
return surface->backend->has_show_text_glyphs (surface);
else
return surface->backend->show_text_glyphs != NULL;
}
slim_hidden_def (cairo_surface_has_show_text_glyphs);
/* Note: the backends may modify the contents of the glyph array as long as
* they do not return %CAIRO_INT_STATUS_UNSUPPORTED. This makes it possible to
* avoid copying the array again and again, and edit it in-place.
* Backends are in fact free to use the array as a generic buffer as they
* see fit.
*
* For show_glyphs backend method, and NOT for show_text_glyphs method,
* when they do return UNSUPPORTED, they may adjust remaining_glyphs to notify
* that they have successfully rendered some of the glyphs (from the beginning
* of the array), but not all. If they don't touch remaining_glyphs, it
* defaults to all glyphs.
*
* See commits 5a9642c5746fd677aed35ce620ce90b1029b1a0c and
* 1781e6018c17909311295a9cc74b70500c6b4d0a for the rationale.
*/
cairo_status_t
_cairo_surface_show_text_glyphs (cairo_surface_t *surface,
cairo_operator_t op,
const cairo_pattern_t *source,
const char *utf8,
int utf8_len,
cairo_glyph_t *glyphs,
int num_glyphs,
const cairo_text_cluster_t *clusters,
int num_clusters,
cairo_text_cluster_flags_t cluster_flags,
cairo_scaled_font_t *scaled_font,
const cairo_clip_t *clip)
{
cairo_int_status_t status;
TRACE ((stderr, "%s\n", __FUNCTION__));
if (unlikely (surface->status))
return surface->status;
if (unlikely (surface->finished))
return _cairo_surface_set_error (surface, _cairo_error (CAIRO_STATUS_SURFACE_FINISHED));
if (num_glyphs == 0 && utf8_len == 0)
return CAIRO_STATUS_SUCCESS;
if (_cairo_clip_is_all_clipped (clip))
return CAIRO_STATUS_SUCCESS;
status = _pattern_has_error (source);
if (unlikely (status))
return status;
if (nothing_to_do (surface, op, source))
return CAIRO_STATUS_SUCCESS;
status = _cairo_surface_begin_modification (surface);
if (unlikely (status))
return status;
status = CAIRO_INT_STATUS_UNSUPPORTED;
/* The logic here is duplicated in _cairo_analysis_surface show_glyphs and
* show_text_glyphs. Keep in synch. */
if (clusters) {
/* A real show_text_glyphs call. Try show_text_glyphs backend
* method first */
if (surface->backend->show_text_glyphs != NULL) {
status = surface->backend->show_text_glyphs (surface, op,
source,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters, cluster_flags,
scaled_font,
clip);
}
if (status == CAIRO_INT_STATUS_UNSUPPORTED &&
surface->backend->show_glyphs)
{
status = surface->backend->show_glyphs (surface, op,
source,
glyphs, num_glyphs,
scaled_font,
clip);
}
} else {
/* A mere show_glyphs call. Try show_glyphs backend method first */
if (surface->backend->show_glyphs != NULL) {
status = surface->backend->show_glyphs (surface, op,
source,
glyphs, num_glyphs,
scaled_font,
clip);
} else if (surface->backend->show_text_glyphs != NULL) {
/* Intentionally only try show_text_glyphs method for show_glyphs
* calls if backend does not have show_glyphs. If backend has
* both methods implemented, we don't fallback from show_glyphs to
* show_text_glyphs, and hence the backend can assume in its
* show_text_glyphs call that clusters is not NULL (which also
* implies that UTF-8 is not NULL, unless the text is
* zero-length).
*/
status = surface->backend->show_text_glyphs (surface, op,
source,
utf8, utf8_len,
glyphs, num_glyphs,
clusters, num_clusters, cluster_flags,
scaled_font,
clip);
}
}
if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
surface->is_clear = FALSE;
surface->serial++;
}
return _cairo_surface_set_error (surface, status);
}
/**
* _cairo_surface_set_resolution:
* @surface: the surface
* @x_res: x resolution, in dpi
* @y_res: y resolution, in dpi
*
* Set the actual surface resolution of @surface to the given x and y DPI.
* Mainly used for correctly computing the scale factor when fallback
* rendering needs to take place in the paginated surface.
**/
void
_cairo_surface_set_resolution (cairo_surface_t *surface,
double x_res,
double y_res)
{
if (surface->status)
return;
surface->x_resolution = x_res;
surface->y_resolution = y_res;
}
/**
* _cairo_surface_create_in_error:
* @status: the error status
*
* Return an appropriate static error surface for the error status.
* On error, surface creation functions should always return a surface
* created with _cairo_surface_create_in_error() instead of a new surface
* in an error state. This simplifies internal code as no refcounting has
* to be done.
**/
cairo_surface_t *
_cairo_surface_create_in_error (cairo_status_t status)
{
assert (status < CAIRO_STATUS_LAST_STATUS);
switch (status) {
case CAIRO_STATUS_NO_MEMORY:
return (cairo_surface_t *) &_cairo_surface_nil;
case CAIRO_STATUS_SURFACE_TYPE_MISMATCH:
return (cairo_surface_t *) &_cairo_surface_nil_surface_type_mismatch;
case CAIRO_STATUS_INVALID_STATUS:
return (cairo_surface_t *) &_cairo_surface_nil_invalid_status;
case CAIRO_STATUS_INVALID_CONTENT:
return (cairo_surface_t *) &_cairo_surface_nil_invalid_content;
case CAIRO_STATUS_INVALID_FORMAT:
return (cairo_surface_t *) &_cairo_surface_nil_invalid_format;
case CAIRO_STATUS_INVALID_VISUAL:
return (cairo_surface_t *) &_cairo_surface_nil_invalid_visual;
case CAIRO_STATUS_READ_ERROR:
return (cairo_surface_t *) &_cairo_surface_nil_read_error;
case CAIRO_STATUS_WRITE_ERROR:
return (cairo_surface_t *) &_cairo_surface_nil_write_error;
case CAIRO_STATUS_FILE_NOT_FOUND:
return (cairo_surface_t *) &_cairo_surface_nil_file_not_found;
case CAIRO_STATUS_TEMP_FILE_ERROR:
return (cairo_surface_t *) &_cairo_surface_nil_temp_file_error;
case CAIRO_STATUS_INVALID_STRIDE:
return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride;
case CAIRO_STATUS_INVALID_SIZE:
return (cairo_surface_t *) &_cairo_surface_nil_invalid_size;
case CAIRO_STATUS_DEVICE_TYPE_MISMATCH:
return (cairo_surface_t *) &_cairo_surface_nil_device_type_mismatch;
case CAIRO_STATUS_DEVICE_ERROR:
return (cairo_surface_t *) &_cairo_surface_nil_device_error;
case CAIRO_STATUS_SUCCESS:
case CAIRO_STATUS_LAST_STATUS:
ASSERT_NOT_REACHED;
/* fall-through */
case CAIRO_STATUS_INVALID_RESTORE:
case CAIRO_STATUS_INVALID_POP_GROUP:
case CAIRO_STATUS_NO_CURRENT_POINT:
case CAIRO_STATUS_INVALID_MATRIX:
case CAIRO_STATUS_NULL_POINTER:
case CAIRO_STATUS_INVALID_STRING:
case CAIRO_STATUS_INVALID_PATH_DATA:
case CAIRO_STATUS_SURFACE_FINISHED:
case CAIRO_STATUS_PATTERN_TYPE_MISMATCH:
case CAIRO_STATUS_INVALID_DASH:
case CAIRO_STATUS_INVALID_DSC_COMMENT:
case CAIRO_STATUS_INVALID_INDEX:
case CAIRO_STATUS_CLIP_NOT_REPRESENTABLE:
case CAIRO_STATUS_FONT_TYPE_MISMATCH:
case CAIRO_STATUS_USER_FONT_IMMUTABLE:
case CAIRO_STATUS_USER_FONT_ERROR:
case CAIRO_STATUS_NEGATIVE_COUNT:
case CAIRO_STATUS_INVALID_CLUSTERS:
case CAIRO_STATUS_INVALID_SLANT:
case CAIRO_STATUS_INVALID_WEIGHT:
case CAIRO_STATUS_USER_FONT_NOT_IMPLEMENTED:
case CAIRO_STATUS_INVALID_MESH_CONSTRUCTION:
case CAIRO_STATUS_DEVICE_FINISHED:
case CAIRO_STATUS_JBIG2_GLOBAL_MISSING:
case CAIRO_STATUS_PNG_ERROR:
case CAIRO_STATUS_FREETYPE_ERROR:
case CAIRO_STATUS_WIN32_GDI_ERROR:
default:
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_surface_t *) &_cairo_surface_nil;
}
}
cairo_surface_t *
_cairo_int_surface_create_in_error (cairo_int_status_t status)
{
if (status < CAIRO_INT_STATUS_LAST_STATUS)
return _cairo_surface_create_in_error (status);
switch ((int)status) {
case CAIRO_INT_STATUS_UNSUPPORTED:
return (cairo_surface_t *) &_cairo_surface_nil_unsupported;
case CAIRO_INT_STATUS_NOTHING_TO_DO:
return (cairo_surface_t *) &_cairo_surface_nil_nothing_to_do;
default:
_cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
return (cairo_surface_t *) &_cairo_surface_nil;
}
}
/* LocalWords: rasterized
*/