cairo/src/cairo-pattern.c

2274 lines
66 KiB
C
Raw Normal View History

/* -*- Mode: c; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 8; -*- */
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 David Reveman
2005-04-14 14:42:26 +00:00
* Copyright © 2005 Red Hat, Inc.
2004-04-06 09:45:17 +00:00
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of David
* Reveman not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. David Reveman makes no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
2004-04-06 09:45:17 +00:00
*
* DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2004-04-06 09:45:17 +00:00
*
* Authors: David Reveman <davidr@novell.com>
* Keith Packard <keithp@keithp.com>
* Carl Worth <cworth@cworth.org>
2004-04-06 09:45:17 +00:00
*/
#include "cairoint.h"
const cairo_solid_pattern_t _cairo_pattern_nil = {
{ CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_REF_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_NO_MEMORY, /* status */
{ 0, 0, 0, NULL }, /* user_data */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
};
static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = {
{ CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_REF_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_NULL_POINTER,/* status */
{ 0, 0, 0, NULL }, /* user_data */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
};
const cairo_solid_pattern_t cairo_pattern_none = {
{ CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_REF_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_SUCCESS, /* status */
{ 0, 0, 0, NULL }, /* user_data */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
};
/**
* _cairo_pattern_set_error:
* @pattern: a pattern
* @status: a status value indicating an error, (eg. not
* CAIRO_STATUS_SUCCESS)
*
* Sets pattern->status to @status and calls _cairo_error;
*
* All assignments of an error status to pattern->status should happen
* through _cairo_pattern_set_error() or else _cairo_error() should be
* called immediately after the assignment.
*
* 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.
**/
static void
_cairo_pattern_set_error (cairo_pattern_t *pattern,
cairo_status_t status)
{
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. It also avoids attempting
* to write to read-only data (eg. from a nil pattern). */
if (pattern->status == CAIRO_STATUS_SUCCESS)
pattern->status = status;
_cairo_error (status);
}
static void
_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
2004-04-06 09:45:17 +00:00
{
CAIRO_MUTEX_INITIALIZE ();
pattern->type = type;
2004-04-06 09:45:17 +00:00
pattern->ref_count = 1;
pattern->status = CAIRO_STATUS_SUCCESS;
_cairo_user_data_array_init (&pattern->user_data);
if (type == CAIRO_PATTERN_TYPE_SURFACE)
pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT;
else
pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT;
pattern->filter = CAIRO_FILTER_DEFAULT;
2004-04-06 09:45:17 +00:00
cairo_matrix_init_identity (&pattern->matrix);
2004-04-06 09:45:17 +00:00
}
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
static cairo_status_t
_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern,
const cairo_gradient_pattern_t *other)
2004-04-06 09:45:17 +00:00
{
if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR)
{
cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern;
cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other;
*dst = *src;
}
else
{
cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern;
cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other;
*dst = *src;
}
2004-04-06 09:45:17 +00:00
if (other->stops == other->stops_embedded)
pattern->stops = pattern->stops_embedded;
else if (other->stops)
{
pattern->stops = _cairo_malloc_ab (other->stops_size,
sizeof (cairo_gradient_stop_t));
if (pattern->stops == NULL) {
pattern->stops_size = 0;
pattern->n_stops = 0;
_cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY);
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
return CAIRO_STATUS_NO_MEMORY;
}
2004-04-06 09:45:17 +00:00
memcpy (pattern->stops, other->stops,
other->n_stops * sizeof (cairo_gradient_stop_t));
2004-04-06 09:45:17 +00:00
}
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
return CAIRO_STATUS_SUCCESS;
}
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
cairo_status_t
_cairo_pattern_init_copy (cairo_pattern_t *pattern,
const cairo_pattern_t *other)
{
if (other->status) {
_cairo_pattern_set_error (pattern, other->status);
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
return other->status;
}
switch (other->type) {
case CAIRO_PATTERN_TYPE_SOLID: {
cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern;
cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other;
*dst = *src;
} break;
case CAIRO_PATTERN_TYPE_SURFACE: {
cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern;
cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other;
*dst = *src;
cairo_surface_reference (dst->surface);
} break;
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL: {
cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern;
cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other;
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
cairo_status_t status;
status = _cairo_gradient_pattern_init_copy (dst, src);
if (status)
return status;
} break;
}
/* The reference count and user_data array are unique to the copy. */
pattern->ref_count = 1;
_cairo_user_data_array_init (&pattern->user_data);
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
return CAIRO_STATUS_SUCCESS;
2004-04-06 09:45:17 +00:00
}
void
_cairo_pattern_fini (cairo_pattern_t *pattern)
{
_cairo_user_data_array_fini (&pattern->user_data);
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
break;
case CAIRO_PATTERN_TYPE_SURFACE: {
cairo_surface_pattern_t *surface_pattern =
(cairo_surface_pattern_t *) pattern;
cairo_surface_destroy (surface_pattern->surface);
} break;
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL: {
cairo_gradient_pattern_t *gradient =
(cairo_gradient_pattern_t *) pattern;
if (gradient->stops && gradient->stops != gradient->stops_embedded)
free (gradient->stops);
} break;
}
}
void
_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
const cairo_color_t *color,
cairo_content_t content)
{
_cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID);
2005-04-14 14:42:26 +00:00
pattern->color = *color;
pattern->content = content;
2004-04-06 09:45:17 +00:00
}
void
_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
cairo_surface_t *surface)
2004-04-06 09:45:17 +00:00
{
if (surface->status) {
/* Force to solid to simplify the pattern_fini process. */
_cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID);
_cairo_pattern_set_error (&pattern->base, surface->status);
return;
}
_cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SURFACE);
pattern->surface = cairo_surface_reference (surface);
}
static void
_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern,
cairo_pattern_type_t type)
{
_cairo_pattern_init (&pattern->base, type);
2004-04-06 09:45:17 +00:00
pattern->n_stops = 0;
pattern->stops_size = 0;
pattern->stops = NULL;
}
void
_cairo_pattern_init_linear (cairo_linear_pattern_t *pattern,
double x0, double y0, double x1, double y1)
{
_cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_LINEAR);
pattern->p1.x = _cairo_fixed_from_double (x0);
pattern->p1.y = _cairo_fixed_from_double (y0);
pattern->p2.x = _cairo_fixed_from_double (x1);
pattern->p2.y = _cairo_fixed_from_double (y1);
}
void
_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
double cx0, double cy0, double radius0,
double cx1, double cy1, double radius1)
{
_cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL);
pattern->c1.x = _cairo_fixed_from_double (cx0);
pattern->c1.y = _cairo_fixed_from_double (cy0);
pattern->r1 = _cairo_fixed_from_double (fabs (radius0));
pattern->c2.x = _cairo_fixed_from_double (cx1);
pattern->c2.y = _cairo_fixed_from_double (cy1);
pattern->r2 = _cairo_fixed_from_double (fabs (radius1));
2004-04-06 09:45:17 +00:00
}
/* We use a small freed pattern cache here, because we don't want to
* constantly reallocate simple colors. */
#define MAX_PATTERN_CACHE_SIZE 4
static struct {
cairo_solid_pattern_t *patterns[MAX_PATTERN_CACHE_SIZE];
int size;
} solid_pattern_cache;
cairo_pattern_t *
_cairo_pattern_create_solid (const cairo_color_t *color,
cairo_content_t content)
{
cairo_solid_pattern_t *pattern = NULL;
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock);
if (solid_pattern_cache.size) {
int i = --solid_pattern_cache.size %
ARRAY_LENGTH (solid_pattern_cache.patterns);
pattern = solid_pattern_cache.patterns[i];
solid_pattern_cache.patterns[i] = NULL;
}
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock);
if (pattern == NULL) {
/* None cached, need to create a new pattern. */
pattern = malloc (sizeof (cairo_solid_pattern_t));
}
if (pattern == NULL)
pattern = (cairo_solid_pattern_t *) &_cairo_pattern_nil;
else
_cairo_pattern_init_solid (pattern, color, content);
return &pattern->base;
}
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
static void
_cairo_pattern_reset_solid_pattern_cache (void)
{
int i;
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock);
for (i = 0; i < MIN (ARRAY_LENGTH (solid_pattern_cache.patterns), solid_pattern_cache.size); i++) {
free (solid_pattern_cache.patterns[i]);
solid_pattern_cache.patterns[i] = NULL;
}
solid_pattern_cache.size = 0;
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock);
}
static const cairo_pattern_t *
_cairo_pattern_create_in_error (cairo_status_t status)
{
cairo_pattern_t *pattern;
if (status == CAIRO_STATUS_NO_MEMORY)
return (cairo_pattern_t *)&_cairo_pattern_nil.base;
pattern = _cairo_pattern_create_solid (_cairo_stock_color (CAIRO_STOCK_BLACK),
CAIRO_CONTENT_COLOR);
/* no-op on a pattern already in error i.e the _cairo_pattern_nil */
_cairo_pattern_set_error (pattern, status);
return pattern;
}
/**
* cairo_pattern_create_rgb:
* @red: red component of the color
* @green: green component of the color
* @blue: blue component of the color
*
* Creates a new cairo_pattern_t corresponding to an opaque color. The
* color components are floating point numbers in the range 0 to 1.
* If the values passed in are outside that range, they will be
* clamped.
*
2007-01-07 02:08:15 -05:00
* Return value: the newly created #cairo_pattern_t if successful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
cairo_pattern_t *
cairo_pattern_create_rgb (double red, double green, double blue)
{
cairo_pattern_t *pattern;
cairo_color_t color;
_cairo_restrict_value (&red, 0.0, 1.0);
_cairo_restrict_value (&green, 0.0, 1.0);
_cairo_restrict_value (&blue, 0.0, 1.0);
_cairo_color_init_rgb (&color, red, green, blue);
pattern = _cairo_pattern_create_solid (&color,
CAIRO_CONTENT_COLOR);
if (pattern->status)
_cairo_error (pattern->status);
return pattern;
}
slim_hidden_def (cairo_pattern_create_rgb);
/**
* cairo_pattern_create_rgba:
* @red: red component of the color
* @green: green component of the color
* @blue: blue component of the color
* @alpha: alpha component of the color
*
* Creates a new cairo_pattern_t corresponding to a translucent color.
* The color components are floating point numbers in the range 0 to
* 1. If the values passed in are outside that range, they will be
* clamped.
*
2007-01-07 02:08:15 -05:00
* Return value: the newly created #cairo_pattern_t if successful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
cairo_pattern_t *
cairo_pattern_create_rgba (double red, double green, double blue,
double alpha)
{
cairo_pattern_t *pattern;
cairo_color_t color;
_cairo_restrict_value (&red, 0.0, 1.0);
_cairo_restrict_value (&green, 0.0, 1.0);
_cairo_restrict_value (&blue, 0.0, 1.0);
_cairo_restrict_value (&alpha, 0.0, 1.0);
_cairo_color_init_rgba (&color, red, green, blue, alpha);
pattern = _cairo_pattern_create_solid (&color,
CAIRO_CONTENT_COLOR_ALPHA);
if (pattern->status)
_cairo_error (pattern->status);
return pattern;
}
slim_hidden_def (cairo_pattern_create_rgba);
/**
* cairo_pattern_create_for_surface:
* @surface: the surface
*
* Create a new cairo_pattern_t for the given surface.
*
2007-01-07 02:08:15 -05:00
* Return value: the newly created #cairo_pattern_t if successful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
2004-04-06 09:45:17 +00:00
cairo_pattern_t *
cairo_pattern_create_for_surface (cairo_surface_t *surface)
{
cairo_surface_pattern_t *pattern;
2004-04-06 09:45:17 +00:00
if (surface == NULL)
return (cairo_pattern_t*) &_cairo_pattern_nil_null_pointer;
if (surface->status)
return (cairo_pattern_t*) _cairo_pattern_create_in_error (surface->status);
pattern = malloc (sizeof (cairo_surface_pattern_t));
if (pattern == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_pattern_t *)&_cairo_pattern_nil.base;
}
2004-04-06 09:45:17 +00:00
_cairo_pattern_init_for_surface (pattern, surface);
2004-04-06 09:45:17 +00:00
return &pattern->base;
2004-04-06 09:45:17 +00:00
}
slim_hidden_def (cairo_pattern_create_for_surface);
2004-04-06 09:45:17 +00:00
/**
* cairo_pattern_create_linear:
* @x0: x coordinate of the start point
* @y0: y coordinate of the start point
* @x1: x coordinate of the end point
* @y1: y coordinate of the end point
*
* Create a new linear gradient cairo_pattern_t along the line defined
* by (x0, y0) and (x1, y1). Before using the gradient pattern, a
* number of color stops should be defined using
* cairo_pattern_add_color_stop_rgb() or
* cairo_pattern_add_color_stop_rgba().
*
* Note: The coordinates here are in pattern space. For a new pattern,
* pattern space is identical to user space, but the relationship
* between the spaces can be changed with cairo_pattern_set_matrix().
*
2007-01-07 02:08:15 -05:00
* Return value: the newly created #cairo_pattern_t if successful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
2004-04-06 09:45:17 +00:00
cairo_pattern_t *
cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
{
cairo_linear_pattern_t *pattern;
2004-04-06 09:45:17 +00:00
pattern = malloc (sizeof (cairo_linear_pattern_t));
if (pattern == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_pattern_t *) &_cairo_pattern_nil.base;
}
2004-04-06 09:45:17 +00:00
_cairo_pattern_init_linear (pattern, x0, y0, x1, y1);
2004-04-06 09:45:17 +00:00
return &pattern->base.base;
2004-04-06 09:45:17 +00:00
}
/**
* cairo_pattern_create_radial:
* @cx0: x coordinate for the center of the start circle
* @cy0: y coordinate for the center of the start circle
* @radius0: radius of the start circle
* @cx1: x coordinate for the center of the end circle
* @cy1: y coordinate for the center of the end circle
* @radius1: radius of the end circle
*
* Creates a new radial gradient cairo_pattern_t between the two
2007-05-28 17:05:40 -04:00
* circles defined by (cx0, cy0, radius0) and (cx1, cy1, radius1). Before using the
* gradient pattern, a number of color stops should be defined using
* cairo_pattern_add_color_stop_rgb() or
* cairo_pattern_add_color_stop_rgba().
*
* Note: The coordinates here are in pattern space. For a new pattern,
* pattern space is identical to user space, but the relationship
* between the spaces can be changed with cairo_pattern_set_matrix().
*
2007-01-07 02:08:15 -05:00
* Return value: the newly created #cairo_pattern_t if successful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
2004-04-06 09:45:17 +00:00
cairo_pattern_t *
cairo_pattern_create_radial (double cx0, double cy0, double radius0,
double cx1, double cy1, double radius1)
{
cairo_radial_pattern_t *pattern;
pattern = malloc (sizeof (cairo_radial_pattern_t));
if (pattern == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_pattern_t *) &_cairo_pattern_nil.base;
}
2004-04-06 09:45:17 +00:00
_cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1);
2004-04-06 09:45:17 +00:00
return &pattern->base.base;
2004-04-06 09:45:17 +00:00
}
/**
* cairo_pattern_reference:
* @pattern: a #cairo_pattern_t
*
* Increases the reference count on @pattern by one. This prevents
* @pattern from being destroyed until a matching call to
* cairo_pattern_destroy() is made.
*
* The number of references to a #cairo_pattern_t can be get using
* cairo_pattern_get_reference_count().
*
* Return value: the referenced #cairo_pattern_t.
**/
cairo_pattern_t *
2004-04-06 09:45:17 +00:00
cairo_pattern_reference (cairo_pattern_t *pattern)
{
if (pattern == NULL || pattern->ref_count == CAIRO_REF_COUNT_INVALID)
return pattern;
assert (pattern->ref_count > 0);
2004-04-06 09:45:17 +00:00
pattern->ref_count++;
return pattern;
2004-04-06 09:45:17 +00:00
}
slim_hidden_def (cairo_pattern_reference);
2004-04-06 09:45:17 +00:00
/**
* cairo_pattern_get_type:
* @pattern: a #cairo_pattern_t
*
* This function returns the type a pattern.
* See #cairo_pattern_type_t for available types.
*
* Return value: The type of @pattern.
*
* Since: 1.2
**/
2006-02-27 23:12:43 -08:00
cairo_pattern_type_t
cairo_pattern_get_type (cairo_pattern_t *pattern)
{
return pattern->type;
}
slim_hidden_def (cairo_pattern_get_type);
2006-02-27 23:12:43 -08:00
/**
* cairo_pattern_status:
* @pattern: a #cairo_pattern_t
*
* Checks whether an error has previously occurred for this
* pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
**/
cairo_status_t
cairo_pattern_status (cairo_pattern_t *pattern)
{
return pattern->status;
}
slim_hidden_def (cairo_pattern_status);
/**
* cairo_pattern_destroy:
* @pattern: a #cairo_pattern_t
*
* Decreases the reference count on @pattern by one. If the result is
* zero, then @pattern and all associated resources are freed. See
* cairo_pattern_reference().
**/
2004-04-06 09:45:17 +00:00
void
cairo_pattern_destroy (cairo_pattern_t *pattern)
{
if (pattern == NULL || pattern->ref_count == CAIRO_REF_COUNT_INVALID)
return;
assert (pattern->ref_count > 0);
2004-04-06 09:45:17 +00:00
pattern->ref_count--;
if (pattern->ref_count)
return;
_cairo_pattern_fini (pattern);
/* maintain a small cache of freed patterns */
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID) {
int i;
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_pattern_cache_lock);
i = solid_pattern_cache.size++ %
ARRAY_LENGTH (solid_pattern_cache.patterns);
/* swap an old pattern for this 'cache-hot' pattern */
if (solid_pattern_cache.patterns[i])
free (solid_pattern_cache.patterns[i]);
solid_pattern_cache.patterns[i] = (cairo_solid_pattern_t *) pattern;
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_pattern_cache_lock);
} else {
free (pattern);
}
2004-04-06 09:45:17 +00:00
}
slim_hidden_def (cairo_pattern_destroy);
2004-04-06 09:45:17 +00:00
/**
* cairo_pattern_get_reference_count:
* @pattern: a #cairo_pattern_t
*
* Returns the current reference count of @pattern.
*
* Return value: the current reference count of @pattern. If the
* object is a nil object, 0 will be returned.
*
* Since: 1.4
**/
unsigned int
cairo_pattern_get_reference_count (cairo_pattern_t *pattern)
{
if (pattern == NULL || pattern->ref_count == CAIRO_REF_COUNT_INVALID)
return 0;
return pattern->ref_count;
}
/**
* cairo_pattern_get_user_data:
* @pattern: a #cairo_pattern_t
* @key: the address of the #cairo_user_data_key_t the user data was
* attached to
*
* Return user data previously attached to @pattern 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.4
**/
void *
cairo_pattern_get_user_data (cairo_pattern_t *pattern,
const cairo_user_data_key_t *key)
{
return _cairo_user_data_array_get_data (&pattern->user_data,
key);
}
/**
* cairo_pattern_set_user_data:
* @pattern: a #cairo_pattern_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 #cairo_pattern_t
* @destroy: a #cairo_destroy_func_t which will be called when the
* #cairo_t is destroyed or when new user data is attached using the
* same key.
*
* Attach user data to @pattern. 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.4
**/
cairo_status_t
cairo_pattern_set_user_data (cairo_pattern_t *pattern,
const cairo_user_data_key_t *key,
void *user_data,
cairo_destroy_func_t destroy)
{
if (pattern->ref_count == CAIRO_REF_COUNT_INVALID)
return CAIRO_STATUS_NO_MEMORY;
return _cairo_user_data_array_set_data (&pattern->user_data,
key, user_data, destroy);
}
/* make room for at least one more color stop */
static cairo_status_t
_cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
{
cairo_gradient_stop_t *new_stops;
int old_size = pattern->stops_size;
int embedded_size = ARRAY_LENGTH (pattern->stops_embedded);
int new_size = 2 * MAX (old_size, 4);
/* we have a local buffer at pattern->stops_embedded. try to fulfill the request
* from there. */
if (old_size < embedded_size) {
pattern->stops = pattern->stops_embedded;
pattern->stops_size = embedded_size;
return CAIRO_STATUS_SUCCESS;
}
assert (pattern->n_stops <= pattern->stops_size);
if (pattern->stops == pattern->stops_embedded) {
new_stops = _cairo_malloc_ab (new_size, sizeof (cairo_gradient_stop_t));
if (new_stops)
memcpy (new_stops, pattern->stops, old_size * sizeof (cairo_gradient_stop_t));
} else {
new_stops = realloc (pattern->stops, new_size * sizeof (cairo_gradient_stop_t));
}
if (new_stops == NULL) {
return CAIRO_STATUS_NO_MEMORY;
}
pattern->stops = new_stops;
pattern->stops_size = new_size;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
2005-04-14 14:42:26 +00:00
double offset,
double red,
double green,
double blue,
double alpha)
2004-04-06 09:45:17 +00:00
{
cairo_gradient_stop_t *stops;
cairo_fixed_t x;
unsigned int i;
2004-04-06 09:45:17 +00:00
if (pattern->n_stops >= pattern->stops_size) {
cairo_status_t status = _cairo_pattern_gradient_grow (pattern);
if (status) {
_cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY);
return;
}
2004-04-06 09:45:17 +00:00
}
stops = pattern->stops;
2004-04-06 09:45:17 +00:00
x = _cairo_fixed_from_double (offset);
for (i = 0; i < pattern->n_stops; i++)
{
if (x < stops[i].x)
{
memmove (&stops[i + 1], &stops[i],
sizeof (cairo_gradient_stop_t) * (pattern->n_stops - i));
break;
}
}
stops[i].x = x;
stops[i].color.red = red;
stops[i].color.green = green;
stops[i].color.blue = blue;
stops[i].color.alpha = alpha;
stops[i].color.red_short = _cairo_color_double_to_short (red);
stops[i].color.green_short = _cairo_color_double_to_short (green);
stops[i].color.blue_short = _cairo_color_double_to_short (blue);
stops[i].color.alpha_short = _cairo_color_double_to_short (alpha);
2004-04-06 09:45:17 +00:00
pattern->n_stops++;
}
/**
* cairo_pattern_add_color_stop_rgb:
* @pattern: a #cairo_pattern_t
* @offset: an offset in the range [0.0 .. 1.0]
* @red: red component of color
* @green: green component of color
* @blue: blue component of color
*
* Adds an opaque color stop to a gradient pattern. The offset
* specifies the location along the gradient's control vector. For
* example, a linear gradient's control vector is from (x0,y0) to
* (x1,y1) while a radial gradient's control vector is from any point
* on the start circle to the corresponding point on the end circle.
*
* The color is specified in the same way as in cairo_set_source_rgb().
*
* If two (or more) stops are specified with identical offset values,
* they will be sorted according to the order in which the stops are
* added, (stops added earlier will compare less than stops added
* later). This can be useful for reliably making sharp color
* transitions instead of the typical blend.
*
*
* Note: If the pattern is not a gradient pattern, (eg. a linear or
* radial pattern), then the pattern will be put into an error status
* with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
**/
void
2005-04-14 14:42:26 +00:00
cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
double offset,
double red,
double green,
double blue)
{
if (pattern->status)
return;
if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
2005-04-14 14:42:26 +00:00
{
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
2005-04-14 14:42:26 +00:00
}
_cairo_restrict_value (&offset, 0.0, 1.0);
_cairo_restrict_value (&red, 0.0, 1.0);
_cairo_restrict_value (&green, 0.0, 1.0);
_cairo_restrict_value (&blue, 0.0, 1.0);
_cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
offset, red, green, blue, 1.0);
2005-04-14 14:42:26 +00:00
}
/**
* cairo_pattern_add_color_stop_rgba:
* @pattern: a #cairo_pattern_t
* @offset: an offset in the range [0.0 .. 1.0]
* @red: red component of color
* @green: green component of color
* @blue: blue component of color
* @alpha: alpha component of color
*
* Adds a translucent color stop to a gradient pattern. The offset
* specifies the location along the gradient's control vector. For
* example, a linear gradient's control vector is from (x0,y0) to
* (x1,y1) while a radial gradient's control vector is from any point
* on the start circle to the corresponding point on the end circle.
*
* The color is specified in the same way as in cairo_set_source_rgba().
*
* If two (or more) stops are specified with identical offset values,
* they will be sorted according to the order in which the stops are
* added, (stops added earlier will compare less than stops added
* later). This can be useful for reliably making sharp color
* transitions instead of the typical blend.
*
* Note: If the pattern is not a gradient pattern, (eg. a linear or
* radial pattern), then the pattern will be put into an error status
* with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
*/
void
2005-04-14 14:42:26 +00:00
cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
double offset,
double red,
double green,
double blue,
double alpha)
{
if (pattern->status)
return;
if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
{
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
_cairo_restrict_value (&offset, 0.0, 1.0);
_cairo_restrict_value (&red, 0.0, 1.0);
_cairo_restrict_value (&green, 0.0, 1.0);
_cairo_restrict_value (&blue, 0.0, 1.0);
_cairo_restrict_value (&alpha, 0.0, 1.0);
_cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
offset, red, green, blue, alpha);
2004-04-06 09:45:17 +00:00
}
/**
* cairo_pattern_set_matrix:
* @pattern: a #cairo_pattern_t
* @matrix: a #cairo_matrix_t
*
* Sets the pattern's transformation matrix to @matrix. This matrix is
* a transformation from user space to pattern space.
*
* When a pattern is first created it always has the identity matrix
* for its transformation matrix, which means that pattern space is
* initially identical to user space.
*
* Important: Please note that the direction of this transformation
* matrix is from user space to pattern space. This means that if you
* imagine the flow from a pattern to user space (and on to device
* space), then coordinates in that flow will be transformed by the
* inverse of the pattern matrix.
*
* For example, if you want to make a pattern appear twice as large as
* it does by default the correct code to use is:
*
* <informalexample><programlisting>
* cairo_matrix_init_scale (&amp;matrix, 0.5, 0.5);
* cairo_pattern_set_matrix (pattern, &amp;matrix);
* </programlisting></informalexample>
*
* Meanwhile, using values of 2.0 rather than 0.5 in the code above
* would cause the pattern to appear at half of its default size.
*
* Also, please note the discussion of the user-space locking
* semantics of cairo_set_source().
**/
void
cairo_pattern_set_matrix (cairo_pattern_t *pattern,
const cairo_matrix_t *matrix)
2004-04-06 09:45:17 +00:00
{
cairo_matrix_t inverse;
cairo_status_t status;
if (pattern->status)
return;
pattern->matrix = *matrix;
inverse = *matrix;
status = cairo_matrix_invert (&inverse);
if (status)
_cairo_pattern_set_error (pattern, status);
2004-04-06 09:45:17 +00:00
}
slim_hidden_def (cairo_pattern_set_matrix);
2004-04-06 09:45:17 +00:00
/**
* cairo_pattern_get_matrix:
* @pattern: a #cairo_pattern_t
* @matrix: return value for the matrix
*
* Stores the pattern's transformation matrix into @matrix.
**/
void
2004-04-06 09:45:17 +00:00
cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix)
{
*matrix = pattern->matrix;
2004-04-06 09:45:17 +00:00
}
2007-03-02 22:01:15 -05:00
/**
* cairo_pattern_set_filter:
* @pattern: a #cairo_pattern_t
* @filter: a #cairo_filter_t describing the filter to use for resizing
* the pattern
*
* Sets the filter to be used for resizing when using this pattern.
* See #cairo_filter_t for details on each filter.
**/
void
2004-04-06 09:45:17 +00:00
cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
{
if (pattern->status)
return;
2004-04-06 09:45:17 +00:00
pattern->filter = filter;
2004-04-06 09:45:17 +00:00
}
2007-03-02 22:01:15 -05:00
/**
* cairo_pattern_get_filter:
* @pattern: a #cairo_pattern_t
*
* Gets the current filter for a pattern. See #cairo_filter_t
* for details on each filter.
*
* Return value: the current filter used for resizing the pattern.
**/
2004-04-06 09:45:17 +00:00
cairo_filter_t
cairo_pattern_get_filter (cairo_pattern_t *pattern)
{
return pattern->filter;
}
/**
* cairo_pattern_set_extend:
* @pattern: a #cairo_pattern_t
* @extend: a #cairo_extend_t describing how the area outside of the
* pattern will be drawn
*
* Sets the mode to be used for drawing outside the area of a pattern.
* See #cairo_extend_t for details on the semantics of each extend
* strategy.
**/
void
2004-04-06 09:45:17 +00:00
cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
{
if (pattern->status)
return;
2004-04-06 09:45:17 +00:00
pattern->extend = extend;
2004-04-06 09:45:17 +00:00
}
/**
* cairo_pattern_get_extend:
* @pattern: a #cairo_pattern_t
*
* Gets the current extend mode for a pattern. See #cairo_extend_t
* for details on the semantics of each extend strategy.
*
* Return value: the current extend strategy used for drawing the
* pattern.
**/
2004-04-06 09:45:17 +00:00
cairo_extend_t
cairo_pattern_get_extend (cairo_pattern_t *pattern)
{
return pattern->extend;
}
slim_hidden_def (cairo_pattern_get_extend);
2004-04-06 09:45:17 +00:00
void
_cairo_pattern_transform (cairo_pattern_t *pattern,
const cairo_matrix_t *ctm_inverse)
2004-04-06 09:45:17 +00:00
{
if (pattern->status)
return;
2004-04-29 20:26:56 +00:00
cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
2004-04-06 09:45:17 +00:00
}
static void
_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern,
double offset_x,
double offset_y,
int width,
int height,
cairo_bool_t *is_horizontal,
cairo_bool_t *is_vertical)
{
cairo_point_double_t point0, point1;
double a, b, c, d, tx, ty;
double scale, start, dx, dy;
cairo_fixed_t factors[3];
int i;
2007-05-18 13:23:04 -07:00
/* To classify a pattern as horizontal or vertical, we first
* compute the (fixed point) factors at the corners of the
* pattern. We actually only need 3/4 corners, so we skip the
* fourth.
*/
point0.x = _cairo_fixed_to_double (pattern->p1.x);
point0.y = _cairo_fixed_to_double (pattern->p1.y);
point1.x = _cairo_fixed_to_double (pattern->p2.x);
point1.y = _cairo_fixed_to_double (pattern->p2.y);
_cairo_matrix_get_affine (&pattern->base.base.matrix,
&a, &b, &c, &d, &tx, &ty);
dx = point1.x - point0.x;
dy = point1.y - point0.y;
scale = dx * dx + dy * dy;
scale = (scale) ? 1.0 / scale : 1.0;
start = dx * point0.x + dy * point0.y;
for (i = 0; i < 3; i++) {
double qx_device = (i % 2) * (width - 1) + offset_x;
double qy_device = (i / 2) * (height - 1) + offset_y;
/* transform fragment into pattern space */
double qx = a * qx_device + c * qy_device + tx;
double qy = b * qx_device + d * qy_device + ty;
factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale);
}
/* We consider a pattern to be vertical if the fixed point factor
* at the two upper corners is the same. We could accept a small
* change, but determining what change is acceptable would require
* sorting the stops in the pattern and looking at the differences.
*
* Horizontal works the same way with the two left corners.
*/
*is_vertical = factors[1] == factors[0];
*is_horizontal = factors[2] == factors[0];
}
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **out,
cairo_surface_attributes_t *attr)
{
cairo_image_surface_t *image;
pixman_image_t *pixman_image;
pixman_transform_t pixman_transform;
cairo_status_t status;
cairo_bool_t repeat = FALSE;
pixman_gradient_stop_t pixman_stops_static[2];
pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
unsigned int i;
if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) {
pixman_stops = _cairo_malloc_ab (pattern->n_stops, sizeof(pixman_gradient_stop_t));
if (pixman_stops == NULL)
return CAIRO_STATUS_NO_MEMORY;
}
for (i = 0; i < pattern->n_stops; i++) {
pixman_stops[i].x = _cairo_fixed_to_16_16 (pattern->stops[i].x);
pixman_stops[i].color.red = pattern->stops[i].color.red_short;
pixman_stops[i].color.green = pattern->stops[i].color.green_short;
pixman_stops[i].color.blue = pattern->stops[i].color.blue_short;
pixman_stops[i].color.alpha = pattern->stops[i].color.alpha_short;
}
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR)
{
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
pixman_point_fixed_t p1, p2;
p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
pixman_stops,
pattern->n_stops);
}
else
{
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
pixman_point_fixed_t c1, c2;
pixman_fixed_t r1, r2;
c1.x = _cairo_fixed_to_16_16 (radial->c1.x);
c1.y = _cairo_fixed_to_16_16 (radial->c1.y);
r1 = _cairo_fixed_to_16_16 (radial->r1);
c2.x = _cairo_fixed_to_16_16 (radial->c2.x);
c2.y = _cairo_fixed_to_16_16 (radial->c2.y);
r2 = _cairo_fixed_to_16_16 (radial->r2);
pixman_image = pixman_image_create_radial_gradient (&c1, &c2,
r1, r2,
pixman_stops,
pattern->n_stops);
}
if (pixman_stops != pixman_stops_static)
free (pixman_stops);
if (pixman_image == NULL)
return CAIRO_STATUS_NO_MEMORY;
if (_cairo_surface_is_image (dst))
{
image = (cairo_image_surface_t *)
_cairo_image_surface_create_for_pixman_image (pixman_image,
CAIRO_FORMAT_ARGB32);
if (image->base.status)
{
pixman_image_unref (pixman_image);
return CAIRO_STATUS_NO_MEMORY;
}
attr->x_offset = attr->y_offset = 0;
attr->matrix = pattern->base.matrix;
attr->extend = pattern->base.extend;
attr->filter = CAIRO_FILTER_NEAREST;
attr->acquired = FALSE;
*out = &image->base;
return CAIRO_STATUS_SUCCESS;
}
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_bool_t is_horizontal;
cairo_bool_t is_vertical;
_cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern,
x, y, width, height,
&is_horizontal, &is_vertical);
if (is_horizontal) {
height = 1;
repeat = TRUE;
}
/* width-1 repeating patterns are quite slow with scan-line based
* compositing code, so we use a wider strip and spend some extra
* expense in computing the gradient. It's possible that for narrow
* gradients we'd be better off using a 2 or 4 pixel strip; the
* wider the gradient, the more it's worth spending extra time
* computing a sample.
*/
if (is_vertical && width > 8) {
width = 8;
repeat = TRUE;
}
}
image = (cairo_image_surface_t *)
cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
if (image->base.status) {
pixman_image_unref (pixman_image);
return CAIRO_STATUS_NO_MEMORY;
}
2004-04-06 09:45:17 +00:00
pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR, NULL, 0);
_cairo_matrix_to_pixman_matrix (&pattern->base.matrix, &pixman_transform);
if (!pixman_image_set_transform (pixman_image, &pixman_transform)) {
cairo_surface_destroy (&image->base);
pixman_image_unref (pixman_image);
return CAIRO_STATUS_NO_MEMORY;
}
switch (pattern->base.extend) {
case CAIRO_EXTEND_NONE:
pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NONE);
break;
case CAIRO_EXTEND_REPEAT:
pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_NORMAL);
break;
case CAIRO_EXTEND_REFLECT:
pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_REFLECT);
break;
case CAIRO_EXTEND_PAD:
pixman_image_set_repeat (pixman_image, PIXMAN_REPEAT_PAD);
break;
}
pixman_image_composite (PIXMAN_OP_SRC,
pixman_image,
NULL,
image->pixman_image,
x, y,
0, 0,
0, 0,
width, height);
pixman_image_unref (pixman_image);
status = _cairo_surface_clone_similar (dst, &image->base,
0, 0, width, height, out);
cairo_surface_destroy (&image->base);
attr->x_offset = -x;
attr->y_offset = -y;
cairo_matrix_init_identity (&attr->matrix);
attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE;
attr->filter = CAIRO_FILTER_NEAREST;
attr->acquired = FALSE;
return status;
}
2004-04-06 09:45:17 +00:00
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
/* We maintain a small cache here, because we don't want to constantly
* recreate surfaces for simple solid colors. */
#define MAX_SURFACE_CACHE_SIZE 16
static struct {
struct _cairo_pattern_solid_surface_cache{
cairo_color_t color;
cairo_surface_t *surface;
} cache[MAX_SURFACE_CACHE_SIZE];
int size;
} solid_surface_cache;
static cairo_bool_t
_cairo_pattern_solid_surface_matches (
const struct _cairo_pattern_solid_surface_cache *cache,
const cairo_solid_pattern_t *pattern,
cairo_surface_t *dst)
{
if (cache->surface->ref_count != 1)
return FALSE;
if (! _cairo_color_equal (&cache->color, &pattern->color))
return FALSE;
if (! _cairo_surface_is_similar (cache->surface, dst, pattern->content))
return FALSE;
return TRUE;
}
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **out,
cairo_surface_attributes_t *attribs)
{
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
static int i;
cairo_surface_t *surface;
cairo_status_t status;
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
/* Check cache first */
if (i < solid_surface_cache.size &&
_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
pattern,
dst))
{
if (! _cairo_surface_reset (solid_surface_cache.cache[i].surface))
goto DONE;
}
for (i = 0 ; i < solid_surface_cache.size; i++) {
if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i],
pattern,
dst))
{
if (! _cairo_surface_reset (solid_surface_cache.cache[i].surface))
goto DONE;
}
}
/* Not cached, need to create new */
surface = _cairo_surface_create_similar_solid (dst,
pattern->content,
1, 1,
&pattern->color,
&pattern->base);
if (surface->status) {
status = surface->status;
goto UNLOCK;
}
if (! _cairo_surface_is_similar (surface, dst, pattern->content)) {
/* in the rare event of a substitute surface being returned (e.g.
* malloc failure) don't cache the fallback surface */
*out = surface;
goto NOCACHE;
}
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
/* Cache new */
if (solid_surface_cache.size < MAX_SURFACE_CACHE_SIZE) {
solid_surface_cache.size++;
} else {
i = rand () % MAX_SURFACE_CACHE_SIZE;
/* Evict old */
cairo_surface_destroy (solid_surface_cache.cache[i].surface);
}
solid_surface_cache.cache[i].color = pattern->color;
solid_surface_cache.cache[i].surface = surface;
DONE:
*out = cairo_surface_reference (solid_surface_cache.cache[i].surface);
NOCACHE:
attribs->x_offset = attribs->y_offset = 0;
cairo_matrix_init_identity (&attribs->matrix);
attribs->extend = CAIRO_EXTEND_REPEAT;
attribs->filter = CAIRO_FILTER_NEAREST;
attribs->acquired = FALSE;
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
status = CAIRO_STATUS_SUCCESS;
UNLOCK:
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
return status;
}
static void
_cairo_pattern_reset_solid_surface_cache (void)
{
int i;
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
for (i = 0; i < solid_surface_cache.size; i++)
cairo_surface_destroy (solid_surface_cache.cache[i].surface);
solid_surface_cache.size = 0;
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
}
/**
2005-04-14 14:42:26 +00:00
* _cairo_pattern_is_opaque_solid
*
2005-04-14 14:42:26 +00:00
* Convenience function to determine whether a pattern is an opaque
* (alpha==1.0) solid color pattern. This is done by testing whether
* the pattern's alpha value when converted to a byte is 255, so if a
* backend actually supported deep alpha channels this function might
* not do the right thing.
*
2005-04-14 14:42:26 +00:00
* Return value: %TRUE if the pattern is an opaque, solid color.
**/
cairo_bool_t
_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern)
{
2005-04-14 14:42:26 +00:00
cairo_solid_pattern_t *solid;
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
2005-04-14 14:42:26 +00:00
return FALSE;
solid = (cairo_solid_pattern_t *) pattern;
return CAIRO_COLOR_IS_OPAQUE (&solid->color);
}
static cairo_bool_t
_gradient_is_opaque (const cairo_gradient_pattern_t *gradient)
{
unsigned int i;
for (i = 0; i < gradient->n_stops; i++)
if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (gradient->stops[i].color.alpha))
return FALSE;
return TRUE;
}
/**
* _cairo_pattern_is_opaque
*
* Convenience function to determine whether a pattern is an opaque
* pattern (of any type). The same caveats that apply to
* _cairo_pattern_is_opaque_solid apply here as well.
*
* Return value: %TRUE if the pattern is a opaque.
**/
cairo_bool_t
_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern)
{
const cairo_pattern_union_t *pattern;
pattern = (cairo_pattern_union_t *) abstract_pattern;
switch (pattern->base.type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_pattern_is_opaque_solid (abstract_pattern);
case CAIRO_PATTERN_TYPE_SURFACE:
return cairo_surface_get_content (pattern->surface.surface) == CAIRO_CONTENT_COLOR;
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
return _gradient_is_opaque (&pattern->gradient.base);
}
ASSERT_NOT_REACHED;
return FALSE;
}
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_surface (cairo_surface_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **out,
cairo_surface_attributes_t *attr)
{
cairo_int_status_t status;
2005-04-14 14:42:26 +00:00
int tx, ty;
attr->acquired = FALSE;
attr->extend = pattern->base.extend;
attr->filter = pattern->base.filter;
if (_cairo_matrix_is_integer_translation (&pattern->base.matrix,
&tx, &ty))
{
cairo_matrix_init_identity (&attr->matrix);
attr->x_offset = tx;
attr->y_offset = ty;
attr->filter = CAIRO_FILTER_NEAREST;
}
else
{
attr->matrix = pattern->base.matrix;
attr->x_offset = attr->y_offset = 0;
tx = 0;
ty = 0;
}
/* XXX: Hack:
*
* The way we currently support CAIRO_EXTEND_REFLECT is to create
* an image twice bigger on each side, and create a pattern of four
* images such that the new image, when repeated, has the same effect
* of reflecting the original pattern.
*
* This is because the reflect support in pixman is broken and we
* pass repeat instead of reflect to pixman. See
* _cairo_image_surface_set_attributes() for that.
*/
if (attr->extend == CAIRO_EXTEND_REFLECT) {
cairo_t *cr;
int w,h;
cairo_rectangle_int_t extents;
status = _cairo_surface_get_extents (pattern->surface, &extents);
if (status)
return status;
attr->extend = CAIRO_EXTEND_REPEAT;
/* TODO: Instead of rendering pattern->surface four times to
* out, we should first copy pattern->surface to surface similar
* to dst and then copy that four times to out. This may cause
* an extra copy in the case of image destination, but for X servers,
* this will send pattern->surface just once over the wire instead
* of current four.
*/
x = extents.x;
y = extents.y;
w = 2 * extents.width;
h = 2 * extents.height;
*out = cairo_surface_create_similar (dst, dst->content, w, h);
status = cairo_surface_status (*out);
if (status) {
cairo_surface_destroy (*out);
*out = NULL;
return status;
}
(*out)->device_transform = pattern->surface->device_transform;
(*out)->device_transform_inverse = pattern->surface->device_transform_inverse;
cr = cairo_create (*out);
cairo_set_source_surface (cr, pattern->surface, -x, -y);
cairo_paint (cr);
cairo_scale (cr, -1, +1);
cairo_set_source_surface (cr, pattern->surface, x-w, -y);
cairo_paint (cr);
cairo_scale (cr, +1, -1);
cairo_set_source_surface (cr, pattern->surface, x-w, y-h);
cairo_paint (cr);
cairo_scale (cr, -1, +1);
cairo_set_source_surface (cr, pattern->surface, -x, y-h);
cairo_paint (cr);
status = cairo_status (cr);
cairo_destroy (cr);
if (status) {
cairo_surface_destroy (*out);
*out = NULL;
}
return status;
}
2005-04-14 14:42:26 +00:00
if (_cairo_surface_is_image (dst))
{
2005-04-14 14:42:26 +00:00
cairo_image_surface_t *image;
2005-04-14 14:42:26 +00:00
status = _cairo_surface_acquire_source_image (pattern->surface,
&image,
&attr->extra);
if (status)
return status;
*out = &image->base;
2005-04-14 14:42:26 +00:00
attr->acquired = TRUE;
}
else
{
cairo_rectangle_int_t extents;
status = _cairo_surface_get_extents (pattern->surface, &extents);
if (status)
return status;
/* If we're repeating, we just play it safe and clone the entire surface. */
/* If requested width and height are -1, clone the entire surface.
* This is relied on in the svg backend. */
if (attr->extend == CAIRO_EXTEND_REPEAT ||
(width == (unsigned int) -1 && height == (unsigned int) -1)) {
x = extents.x;
y = extents.y;
width = extents.width;
height = extents.height;
} else {
/* Otherwise, we first transform the rectangle to the
* coordinate space of the source surface so that we can
* clone only that portion of the surface that will be
* read. */
if (! _cairo_matrix_is_identity (&attr->matrix)) {
double x1 = x;
double y1 = y;
double x2 = x + width;
double y2 = y + height;
cairo_bool_t is_tight;
_cairo_matrix_transform_bounding_box (&attr->matrix,
&x1, &y1, &x2, &y2,
&is_tight);
/* The transform_bounding_box call may have resulted
* in a region larger than the surface, but we never
* want to clone more than the surface itself, (we
* know we're not repeating at this point due to the
* above.
*
* XXX: The one padding here is to account for filter
* radius. It's a workaround right now, until we get a
* proper fix. (see bug #10508)
*/
x = MAX (0, floor (x1) - 1);
y = MAX (0, floor (y1) - 1);
width = MIN (extents.width, ceil (x2) + 1) - x;
height = MIN (extents.height, ceil (y2) + 1) - y;
}
x += tx;
y += ty;
}
status = _cairo_surface_clone_similar (dst, pattern->surface,
x, y, width, height, out);
if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
cairo_t *cr;
*out = cairo_surface_create_similar (dst, dst->content,
width, height);
status = cairo_surface_status (*out);
if (status) {
cairo_surface_destroy (*out);
*out = NULL;
return status;
}
(*out)->device_transform = pattern->surface->device_transform;
(*out)->device_transform_inverse = pattern->surface->device_transform_inverse;
2007-02-23 16:14:37 -05:00
/* XXX Use _cairo_surface_composite directly */
cr = cairo_create (*out);
cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
cairo_set_source_surface (cr, pattern->surface, -x, -y);
cairo_paint (cr);
status = cairo_status (cr);
cairo_destroy (cr);
if (status) {
cairo_surface_destroy (*out);
*out = NULL;
}
}
}
return status;
}
/**
* _cairo_pattern_acquire_surface:
* @pattern: a #cairo_pattern_t
* @dst: destination surface
* @x: X coordinate in source corresponding to left side of destination area
* @y: Y coordinate in source corresponding to top side of destination area
* @width: width of destination area
* @height: height of destination area
* @surface_out: location to store a pointer to a surface
* @attributes: surface attributes that destination backend should apply to
* the returned surface
*
* A convenience function to obtain a surface to use as the source for
* drawing on @dst.
*
* Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out.
**/
cairo_int_status_t
_cairo_pattern_acquire_surface (cairo_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **surface_out,
cairo_surface_attributes_t *attributes)
{
cairo_status_t status;
if (pattern->status) {
*surface_out = NULL;
attributes->acquired = FALSE;
return pattern->status;
}
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID: {
cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern;
status = _cairo_pattern_acquire_surface_for_solid (src, dst,
x, y, width, height,
surface_out,
attributes);
} break;
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL: {
cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern;
/* fast path for gradients with less than 2 color stops */
if (src->n_stops < 2)
{
cairo_solid_pattern_t solid;
if (src->n_stops)
{
cairo_color_t color;
_cairo_color_init_rgba (&color,
src->stops->color.red,
src->stops->color.green,
src->stops->color.blue,
src->stops->color.alpha);
_cairo_pattern_init_solid (&solid, &color,
CAIRO_COLOR_IS_OPAQUE (&color) ?
CAIRO_CONTENT_COLOR :
CAIRO_CONTENT_COLOR_ALPHA);
}
else
{
const cairo_color_t *color;
2005-04-14 14:42:26 +00:00
color = _cairo_stock_color (CAIRO_STOCK_TRANSPARENT);
_cairo_pattern_init_solid (&solid, color,
CAIRO_CONTENT_ALPHA);
}
status = _cairo_pattern_acquire_surface_for_solid (&solid, dst,
x, y,
width, height,
surface_out,
attributes);
}
else
{
status = _cairo_pattern_acquire_surface_for_gradient (src, dst,
x, y,
width, height,
surface_out,
attributes);
}
} break;
case CAIRO_PATTERN_TYPE_SURFACE: {
cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern;
status = _cairo_pattern_acquire_surface_for_surface (src, dst,
x, y, width, height,
surface_out,
attributes);
} break;
default:
status = CAIRO_INT_STATUS_UNSUPPORTED;
}
return status;
}
/**
* _cairo_pattern_release_surface:
* @pattern: a #cairo_pattern_t
* @surface: a surface obtained by _cairo_pattern_acquire_surface
* @attributes: attributes obtained by _cairo_pattern_acquire_surface
*
* Releases resources obtained by _cairo_pattern_acquire_surface.
**/
void
_cairo_pattern_release_surface (cairo_pattern_t *pattern,
cairo_surface_t *surface,
cairo_surface_attributes_t *attributes)
{
if (attributes->acquired)
{
cairo_surface_pattern_t *surface_pattern;
assert (pattern->type == CAIRO_PATTERN_TYPE_SURFACE);
surface_pattern = (cairo_surface_pattern_t *) pattern;
_cairo_surface_release_source_image (surface_pattern->surface,
(cairo_image_surface_t *) surface,
attributes->extra);
}
else
{
cairo_surface_destroy (surface);
}
}
cairo_int_status_t
_cairo_pattern_acquire_surfaces (cairo_pattern_t *src,
cairo_pattern_t *mask,
cairo_surface_t *dst,
int src_x,
int src_y,
int mask_x,
int mask_y,
unsigned int width,
unsigned int height,
cairo_surface_t **src_out,
cairo_surface_t **mask_out,
cairo_surface_attributes_t *src_attributes,
cairo_surface_attributes_t *mask_attributes)
{
cairo_int_status_t status;
cairo_pattern_union_t src_tmp, mask_tmp;
if (src->status)
return src->status;
if (mask && mask->status)
return mask->status;
2005-04-14 14:42:26 +00:00
/* If src and mask are both solid, then the mask alpha can be
* combined into src and mask can be ignored. */
/* XXX: This optimization assumes that there is no color
* information in mask, so this will need to change when we
* support RENDER-style 4-channel masks. */
if (src->type == CAIRO_PATTERN_TYPE_SOLID &&
mask && mask->type == CAIRO_PATTERN_TYPE_SOLID)
{
2005-04-14 14:42:26 +00:00
cairo_color_t combined;
cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src;
cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask;
combined = src_solid->color;
_cairo_color_multiply_alpha (&combined, mask_solid->color.alpha);
_cairo_pattern_init_solid (&src_tmp.solid, &combined,
CAIRO_COLOR_IS_OPAQUE (&combined) ?
CAIRO_CONTENT_COLOR :
CAIRO_CONTENT_COLOR_ALPHA);
2005-04-14 14:42:26 +00:00
mask = NULL;
}
else
{
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
status = _cairo_pattern_init_copy (&src_tmp.base, src);
if (status)
return status;
}
status = _cairo_pattern_acquire_surface (&src_tmp.base, dst,
src_x, src_y,
width, height,
src_out, src_attributes);
if (status) {
_cairo_pattern_fini (&src_tmp.base);
return status;
}
if (mask == NULL)
{
_cairo_pattern_fini (&src_tmp.base);
*mask_out = NULL;
return CAIRO_STATUS_SUCCESS;
}
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
status = _cairo_pattern_init_copy (&mask_tmp.base, mask);
if (status)
goto CLEANUP_SOURCE;
status = _cairo_pattern_acquire_surface (&mask_tmp.base, dst,
mask_x, mask_y,
width, height,
mask_out, mask_attributes);
[cairo-pattern] Return status from _cairo_pattern_init_copy() During the copy, allocation of the gradient may fail and so the callers need to check for a pattern that returned in an error state. No callers did so and in order to force all callers to check the error status, the status return was added to _cairo_pattern_init_copy(). The early error checking may appear redundant for an object with an embedded structure, however it does fix an error where an uninitialised pattern was being used: ==1922== Process terminating with default action of signal 11 (SIGSEGV) ==1922== Access not within mapped region at address 0x55555555 ==1922== at 0x402CF6F: _cairo_array_index (cairo-array.c:208) ==1922== by 0x402D4F3: _cairo_user_data_array_fini (cairo-array.c:370) ==1922== by 0x4046464: _cairo_pattern_fini (cairo-pattern.c:188) ==1922== by 0x404992A: _cairo_meta_surface_paint (cairo-meta-surface.c:266) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109) ==1922== Injected fault at: ==1922== at 0x4020EA5: malloc (vg_replace_malloc.c:207) ==1922== by 0x404475C: _cairo_pattern_init_copy (cairo-pattern.c:136) ==1922== by 0x403F779: _cairo_surface_copy_pattern_for_destination (cairo-surface.c:2153) ==1922== by 0x403FCC1: _cairo_surface_paint (cairo-surface.c:1328) ==1922== by 0x405CB5E: _test_meta_surface_paint (test-meta-surface.c:195) ==1922== by 0x403FCE0: _cairo_surface_paint (cairo-surface.c:1331) ==1922== by 0x4032A60: _cairo_gstate_paint (cairo-gstate.c:822) ==1922== by 0x402B2D1: cairo_paint (cairo.c:1879) ==1922== by 0x804A4F7: draw (radial-gradient.c:73) ==1922== by 0x804AFA4: cairo_test_expecting (cairo-test.c:326) ==1922== by 0x804A57C: main (radial-gradient.c:109)
2007-05-08 16:13:08 +01:00
_cairo_pattern_fini (&mask_tmp.base);
CLEANUP_SOURCE:
if (status)
_cairo_pattern_release_surface (&src_tmp.base,
*src_out, src_attributes);
_cairo_pattern_fini (&src_tmp.base);
return status;
}
/**
* _cairo_pattern_get_extents:
*
* Return the "target-space" extents of @pattern in @extents.
*
* For unbounded patterns, the @extents will be initialized with
* "infinite" extents, (minimum and maximum fixed-point values).
*
* XXX: Currently, bounded gradient patterns will also return
* "infinite" extents, though it would be possible to optimize these
* with a little more work.
**/
cairo_status_t
_cairo_pattern_get_extents (cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents)
{
if (pattern->extend == CAIRO_EXTEND_NONE &&
pattern->type == CAIRO_PATTERN_TYPE_SURFACE)
{
cairo_status_t status;
cairo_rectangle_int_t surface_extents;
cairo_surface_pattern_t *surface_pattern =
(cairo_surface_pattern_t *) pattern;
cairo_surface_t *surface = surface_pattern->surface;
cairo_matrix_t imatrix;
double x, y;
/* Initialize to keep the compiler quiet. */
int left=0, right=0, top=0, bottom=0;
int lx, rx, ty, by;
int sx, sy;
cairo_bool_t set = FALSE;
status = _cairo_surface_get_extents (surface, &surface_extents);
if (status)
return status;
imatrix = pattern->matrix;
status = cairo_matrix_invert (&imatrix);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
2007-03-13 06:20:21 -04:00
/* XXX Use _cairo_matrix_transform_bounding_box here */
for (sy = 0; sy <= 1; sy++) {
for (sx = 0; sx <= 1; sx++) {
x = surface_extents.x + sx * surface_extents.width;
y = surface_extents.y + sy * surface_extents.height;
cairo_matrix_transform_point (&imatrix, &x, &y);
if (x < 0) x = 0;
if (x > CAIRO_RECT_INT_MAX) x = CAIRO_RECT_INT_MAX;
if (y < 0) y = 0;
if (y > CAIRO_RECT_INT_MAX) y = CAIRO_RECT_INT_MAX;
lx = floor (x); rx = ceil (x);
ty = floor (y); by = ceil (y);
if (!set) {
left = lx;
right = rx;
top = ty;
bottom = by;
set = TRUE;
} else {
if (lx < left) left = lx;
if (rx > right) right = rx;
if (ty < top) top = ty;
if (by > bottom) bottom = by;
}
}
}
extents->x = left; extents->width = right - left;
extents->y = top; extents->height = bottom - top;
return CAIRO_STATUS_SUCCESS;
}
/* XXX: We could optimize gradients with pattern->extend of NONE
* here in some cases, (eg. radial gradients and 1 axis of
* horizontal/vertical linear gradients).
*/
/* XXX: because extents are represented as x, y, w, h we can't
* actually have a rectangle that covers our entire valid
* coordinate space, since we'd need width/height to be 2*INT_MAX.
*/
extents->x = 0;
extents->y = 0;
extents->width = CAIRO_RECT_INT_MAX;
extents->height = CAIRO_RECT_INT_MAX;
return CAIRO_STATUS_SUCCESS;
}
/**
* cairo_pattern_get_rgba
* @pattern: a #cairo_pattern_t
* @red: return value for red component of color, or %NULL
* @green: return value for green component of color, or %NULL
* @blue: return value for blue component of color, or %NULL
* @alpha: return value for alpha component of color, or %NULL
*
* Gets the solid color for a solid color pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid
* color pattern.
*
* Since: 1.4
**/
cairo_status_t
cairo_pattern_get_rgba (cairo_pattern_t *pattern,
double *red, double *green,
double *blue, double *alpha)
{
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern;
double r0, g0, b0, a0;
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID)
return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
_cairo_color_get_rgba (&solid->color, &r0, &g0, &b0, &a0);
if (red)
*red = r0;
if (green)
*green = g0;
if (blue)
*blue = b0;
if (alpha)
*alpha = a0;
return CAIRO_STATUS_SUCCESS;
}
/**
* cairo_pattern_get_surface
* @pattern: a #cairo_pattern_t
* @surface: return value for surface of pattern, or %NULL
*
* Gets the surface of a surface pattern. The reference returned in
* @surface is owned by the pattern; the caller should call
* cairo_surface_reference() if the surface is to be retained.
*
* Return value: %CAIRO_STATUS_SUCCESS, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a surface
* pattern.
*
* Since: 1.4
**/
cairo_status_t
cairo_pattern_get_surface (cairo_pattern_t *pattern,
cairo_surface_t **surface)
{
cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern;
if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE)
return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
if (surface)
*surface = spat->surface;
return CAIRO_STATUS_SUCCESS;
}
/**
* cairo_pattern_get_color_stop_rgba
* @pattern: a #cairo_pattern_t
* @index: index of the stop to return data for
2007-03-02 22:01:15 -05:00
* @offset: return value for the offset of the stop, or %NULL
* @red: return value for red component of color, or %NULL
* @green: return value for green component of color, or %NULL
* @blue: return value for blue component of color, or %NULL
* @alpha: return value for alpha component of color, or %NULL
*
* Gets the color and offset information at the given @index for a
* gradient pattern. Values of @index are 0 to 1 less than the number
* returned by cairo_pattern_get_color_stop_count().
*
* Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX
* if @index is not valid for the given pattern. If the pattern is
* not a gradient pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is
* returned.
*
* Since: 1.4
**/
cairo_status_t
cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern,
int index, double *offset,
double *red, double *green,
double *blue, double *alpha)
{
cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern;
if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
2006-10-04 15:23:36 -07:00
if (index < 0 || (unsigned int) index >= gradient->n_stops)
return CAIRO_STATUS_INVALID_INDEX;
if (offset)
*offset = _cairo_fixed_to_double(gradient->stops[index].x);
if (red)
*red = gradient->stops[index].color.red;
if (green)
*green = gradient->stops[index].color.green;
if (blue)
*blue = gradient->stops[index].color.blue;
if (alpha)
*alpha = gradient->stops[index].color.alpha;
return CAIRO_STATUS_SUCCESS;
}
/**
* cairo_pattern_get_color_stop_count
* @pattern: a #cairo_pattern_t
* @count: return value for the number of color stops, or %NULL
*
* Gets the number of color stops specified in the given gradient
* pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a gradient
* pattern.
*
* Since: 1.4
*/
cairo_status_t
cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern,
int *count)
{
cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern;
if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR &&
pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
if (count)
*count = gradient->n_stops;
return CAIRO_STATUS_SUCCESS;
}
/**
* cairo_pattern_get_linear_points
* @pattern: a #cairo_pattern_t
* @x0: return value for the x coordinate of the first point, or %NULL
* @y0: return value for the y coordinate of the first point, or %NULL
* @x1: return value for the x coordinate of the second point, or %NULL
* @y1: return value for the y coordinate of the second point, or %NULL
*
* Gets the gradient endpoints for a linear gradient.
*
* Return value: %CAIRO_STATUS_SUCCESS, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a linear
* gradient pattern.
*
* Since: 1.4
**/
cairo_status_t
cairo_pattern_get_linear_points (cairo_pattern_t *pattern,
double *x0, double *y0,
double *x1, double *y1)
{
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t*) pattern;
if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR)
return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
if (x0)
*x0 = _cairo_fixed_to_double (linear->p1.x);
if (y0)
*y0 = _cairo_fixed_to_double (linear->p1.y);
if (x1)
*x1 = _cairo_fixed_to_double (linear->p2.x);
if (y1)
*y1 = _cairo_fixed_to_double (linear->p2.y);
return CAIRO_STATUS_SUCCESS;
}
/**
* cairo_pattern_get_radial_circles
* @pattern: a #cairo_pattern_t
* @x0: return value for the x coordinate of the center of the first circle, or %NULL
* @y0: return value for the y coordinate of the center of the first circle, or %NULL
* @r0: return value for the radius of the first circle, or %NULL
* @x1: return value for the x coordinate of the center of the second circle, or %NULL
* @y1: return value for the y coordinate of the center of the second circle, or %NULL
* @r1: return value for the radius of the second circle, or %NULL
*
* Gets the gradient endpoint circles for a radial gradient, each
* specified as a center coordinate and a radius.
*
* Return value: %CAIRO_STATUS_SUCCESS, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a radial
* gradient pattern.
*
* Since: 1.4
**/
cairo_status_t
cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
double *x0, double *y0, double *r0,
double *x1, double *y1, double *r1)
{
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern;
if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL)
return CAIRO_STATUS_PATTERN_TYPE_MISMATCH;
if (x0)
*x0 = _cairo_fixed_to_double (radial->c1.x);
if (y0)
*y0 = _cairo_fixed_to_double (radial->c1.y);
if (r0)
*r0 = _cairo_fixed_to_double (radial->r1);
if (x1)
*x1 = _cairo_fixed_to_double (radial->c2.x);
if (y1)
*y1 = _cairo_fixed_to_double (radial->c2.y);
if (r1)
*r1 = _cairo_fixed_to_double (radial->r2);
return CAIRO_STATUS_SUCCESS;
}
[cairo-pattern] Cache surface for solid patterns Original work by Jorn Baayen <jorn@openedhand.com>, 2715f2098127d04d2f9e304580a26cd0842c0e64 We use a small cache of size 16 for surfaces created for solid patterns. This mainly helps with the X backends where we don't have to create a pattern for every operation, so we save a lot on X traffic. Xft uses a similar cache, so cairo's text rendering traffic with the xlib backend now completely matches that of Xft. The cache uses an static index variable, which itself acts like a cache of size 1, remembering the most recently used solid pattern. So repeated lookups for the same pattern hit immediately. If that fails, the cache is searched linearly, and if that fails too, a new surface is created and a random member of the cache is evicted. A cached surface can only be reused if it is similar to the destination. In order to check for similar surfaces a new test is introduced for the backends to determine that the cached surface is as would be returned by a _create_similar() call for the destination and content. As surfaces are in general complex encapsulation of graphics state we only return unshared cached surfaces and reset them (to clear any error conditions and graphics state). In practice this makes little difference to the efficacy of the cache during various benchmarks. However, in order to transparently share solid surfaces it would be possible to implement a COW scheme. Cache hit rates: (hit same index + hit in cache) / lookups cairo-perf: (42346 + 28480) / 159600 = 44.38% gtk-theme-torturer: (3023 + 3502) / 6528 = 99.95% gtk-perf: (8270 + 3190) / 21504 = 53.29% This translates into a reduction of about 25% of the XRENDER traffic during cairo-perf.
2007-05-02 10:00:22 +01:00
void
_cairo_pattern_reset_static_data (void)
{
_cairo_pattern_reset_solid_pattern_cache ();
_cairo_pattern_reset_solid_surface_cache ();
}