| /* -*- 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 |
| * Copyright © 2005 Red Hat, Inc. |
| * |
| * 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. |
| * |
| * 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. |
| * |
| * Authors: David Reveman <davidr@novell.com> |
| * Keith Packard <keithp@keithp.com> |
| * Carl Worth <cworth@cworth.org> |
| */ |
| |
| #include "cairoint.h" |
| #include "cairo-error-private.h" |
| #include "cairo-freed-pool-private.h" |
| #include "cairo-path-private.h" |
| |
| #include <float.h> |
| |
| #define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ |
| |
| /** |
| * SECTION:cairo-pattern |
| * @Title: cairo_pattern_t |
| * @Short_Description: Sources for drawing |
| * @See_Also: #cairo_t, #cairo_surface_t |
| * |
| * #cairo_pattern_t is the paint with which cairo draws. |
| * The primary use of patterns is as the source for all cairo drawing |
| * operations, although they can also be used as masks, that is, as the |
| * brush too. |
| * |
| * A cairo pattern is created by using one of the many constructors, |
| * of the form |
| * <function>cairo_pattern_create_<emphasis>type</emphasis>()</function> |
| * or implicitly through |
| * <function>cairo_set_source_<emphasis>type</emphasis>()</function> |
| * functions. |
| */ |
| |
| #if HAS_FREED_POOL |
| static freed_pool_t freed_pattern_pool[5]; |
| #endif |
| |
| static const cairo_solid_pattern_t _cairo_pattern_nil = { |
| { CAIRO_PATTERN_TYPE_SOLID, /* type */ |
| CAIRO_REFERENCE_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_REFERENCE_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_black = { |
| { CAIRO_PATTERN_TYPE_SOLID, /* type */ |
| CAIRO_REFERENCE_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 */ |
| { 0., 0., 0., 1., 0, 0, 0, 0xffff },/* color (double rgba, short rgba) */ |
| }; |
| |
| const cairo_solid_pattern_t _cairo_pattern_clear = { |
| { CAIRO_PATTERN_TYPE_SOLID, /* type */ |
| CAIRO_REFERENCE_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 */ |
| { 0., 0., 0., 0., 0, 0, 0, 0 },/* color (double rgba, short rgba) */ |
| }; |
| |
| const cairo_solid_pattern_t _cairo_pattern_white = { |
| { CAIRO_PATTERN_TYPE_SOLID, /* type */ |
| CAIRO_REFERENCE_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 */ |
| { 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */ |
| }; |
| |
| /** |
| * _cairo_pattern_set_error: |
| * @pattern: a pattern |
| * @status: a status value indicating an error |
| * |
| * Atomically sets pattern->status to @status and calls _cairo_error; |
| * Does nothing if status is %CAIRO_STATUS_SUCCESS. |
| * |
| * All assignments of an error status to pattern->status should happen |
| * through _cairo_pattern_set_error(). Note that due to the nature of |
| * the atomic operation, it is not safe to call this function on the nil |
| * objects. |
| * |
| * The purpose of this function is to allow the user to set a |
| * breakpoint in _cairo_error() to generate a stack trace for when the |
| * user causes cairo to detect an error. |
| **/ |
| static cairo_status_t |
| _cairo_pattern_set_error (cairo_pattern_t *pattern, |
| cairo_status_t status) |
| { |
| if (status == CAIRO_STATUS_SUCCESS) |
| return status; |
| |
| /* Don't overwrite an existing error. This preserves the first |
| * error, which is the most significant. */ |
| _cairo_status_set_error (&pattern->status, status); |
| |
| return _cairo_error (status); |
| } |
| |
| static void |
| _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type) |
| { |
| #if HAVE_VALGRIND |
| switch (type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_MESH: |
| VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)); |
| break; |
| } |
| #endif |
| |
| pattern->type = type; |
| pattern->status = CAIRO_STATUS_SUCCESS; |
| |
| /* Set the reference count to zero for on-stack patterns. |
| * Callers needs to explicitly increment the count for heap allocations. */ |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); |
| |
| _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; |
| |
| pattern->has_component_alpha = FALSE; |
| |
| cairo_matrix_init_identity (&pattern->matrix); |
| } |
| |
| static cairo_status_t |
| _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, |
| const cairo_gradient_pattern_t *other) |
| { |
| if (CAIRO_INJECT_FAULT ()) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| 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; |
| } |
| |
| 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 (unlikely (pattern->stops == NULL)) { |
| pattern->stops_size = 0; |
| pattern->n_stops = 0; |
| return _cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY); |
| } |
| |
| memcpy (pattern->stops, other->stops, |
| other->n_stops * sizeof (cairo_gradient_stop_t)); |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static cairo_status_t |
| _cairo_mesh_pattern_init_copy (cairo_mesh_pattern_t *pattern, |
| const cairo_mesh_pattern_t *other) |
| { |
| *pattern = *other; |
| |
| _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t)); |
| |
| return _cairo_array_append_multiple (&pattern->patches, |
| _cairo_array_index_const (&other->patches, 0), |
| _cairo_array_num_elements (&other->patches)); |
| } |
| |
| cairo_status_t |
| _cairo_pattern_init_copy (cairo_pattern_t *pattern, |
| const cairo_pattern_t *other) |
| { |
| if (other->status) |
| return _cairo_pattern_set_error (pattern, 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; |
| |
| VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t))); |
| |
| *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; |
| |
| VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t))); |
| |
| *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_status_t status; |
| |
| if (other->type == CAIRO_PATTERN_TYPE_LINEAR) { |
| VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t))); |
| } else { |
| VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t))); |
| } |
| |
| status = _cairo_gradient_pattern_init_copy (dst, src); |
| if (unlikely (status)) |
| return status; |
| |
| } break; |
| case CAIRO_PATTERN_TYPE_MESH: { |
| cairo_mesh_pattern_t *dst = (cairo_mesh_pattern_t *) pattern; |
| cairo_mesh_pattern_t *src = (cairo_mesh_pattern_t *) other; |
| cairo_status_t status; |
| |
| VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t))); |
| |
| status = _cairo_mesh_pattern_init_copy (dst, src); |
| if (unlikely (status)) |
| return status; |
| |
| } break; |
| } |
| |
| /* The reference count and user_data array are unique to the copy. */ |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); |
| _cairo_user_data_array_init (&pattern->user_data); |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| void |
| _cairo_pattern_init_static_copy (cairo_pattern_t *pattern, |
| const cairo_pattern_t *other) |
| { |
| int size; |
| |
| assert (other->status == CAIRO_STATUS_SUCCESS); |
| |
| switch (other->type) { |
| default: |
| ASSERT_NOT_REACHED; |
| case CAIRO_PATTERN_TYPE_SOLID: |
| size = sizeof (cairo_solid_pattern_t); |
| break; |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| size = sizeof (cairo_surface_pattern_t); |
| break; |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| size = sizeof (cairo_linear_pattern_t); |
| break; |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| size = sizeof (cairo_radial_pattern_t); |
| break; |
| case CAIRO_PATTERN_TYPE_MESH: |
| size = sizeof (cairo_mesh_pattern_t); |
| break; |
| } |
| |
| memcpy (pattern, other, size); |
| |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0); |
| _cairo_user_data_array_init (&pattern->user_data); |
| } |
| |
| cairo_status_t |
| _cairo_pattern_init_snapshot (cairo_pattern_t *pattern, |
| const cairo_pattern_t *other) |
| { |
| cairo_status_t status; |
| |
| /* We don't bother doing any fancy copy-on-write implementation |
| * for the pattern's data. It's generally quite tiny. */ |
| status = _cairo_pattern_init_copy (pattern, other); |
| if (unlikely (status)) |
| return status; |
| |
| /* But we do let the surface snapshot stuff be as fancy as it |
| * would like to be. */ |
| if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) { |
| cairo_surface_pattern_t *surface_pattern = |
| (cairo_surface_pattern_t *) pattern; |
| cairo_surface_t *surface = surface_pattern->surface; |
| |
| surface_pattern->surface = _cairo_surface_snapshot (surface); |
| |
| cairo_surface_destroy (surface); |
| |
| if (surface_pattern->surface->status) |
| return surface_pattern->surface->status; |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| 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; |
| case CAIRO_PATTERN_TYPE_MESH: { |
| cairo_mesh_pattern_t *mesh = |
| (cairo_mesh_pattern_t *) pattern; |
| |
| _cairo_array_fini (&mesh->patches); |
| } break; |
| } |
| |
| #if HAVE_VALGRIND |
| switch (pattern->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_solid_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_surface_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_linear_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_MESH: |
| VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_mesh_pattern_t)); |
| break; |
| } |
| #endif |
| } |
| |
| cairo_status_t |
| _cairo_pattern_create_copy (cairo_pattern_t **pattern_out, |
| const cairo_pattern_t *other) |
| { |
| cairo_pattern_t *pattern; |
| cairo_status_t status; |
| |
| if (other->status) |
| return other->status; |
| |
| switch (other->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| pattern = malloc (sizeof (cairo_solid_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| pattern = malloc (sizeof (cairo_surface_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| pattern = malloc (sizeof (cairo_linear_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| pattern = malloc (sizeof (cairo_radial_pattern_t)); |
| break; |
| case CAIRO_PATTERN_TYPE_MESH: |
| pattern = malloc (sizeof (cairo_mesh_pattern_t)); |
| break; |
| default: |
| ASSERT_NOT_REACHED; |
| return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| } |
| if (unlikely (pattern == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| status = _cairo_pattern_init_copy (pattern, other); |
| if (unlikely (status)) { |
| free (pattern); |
| return status; |
| } |
| |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1); |
| *pattern_out = pattern; |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| |
| void |
| _cairo_pattern_init_solid (cairo_solid_pattern_t *pattern, |
| const cairo_color_t *color) |
| { |
| _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_SOLID); |
| pattern->color = *color; |
| } |
| |
| void |
| _cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern, |
| cairo_surface_t *surface) |
| { |
| 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); |
| |
| pattern->n_stops = 0; |
| pattern->stops_size = 0; |
| pattern->stops = NULL; |
| } |
| |
| static 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->pd1.x = x0; |
| pattern->pd1.y = y0; |
| pattern->pd2.x = x1; |
| pattern->pd2.y = y1; |
| } |
| |
| static 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->cd1.center.x = cx0; |
| pattern->cd1.center.y = cy0; |
| pattern->cd1.radius = fabs (radius0); |
| pattern->cd2.center.x = cx1; |
| pattern->cd2.center.y = cy1; |
| pattern->cd2.radius = fabs (radius1); |
| } |
| |
| cairo_pattern_t * |
| _cairo_pattern_create_solid (const cairo_color_t *color) |
| { |
| cairo_solid_pattern_t *pattern; |
| |
| pattern = |
| _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SOLID]); |
| if (unlikely (pattern == NULL)) { |
| /* None cached, need to create a new pattern. */ |
| pattern = malloc (sizeof (cairo_solid_pattern_t)); |
| if (unlikely (pattern == NULL)) { |
| _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); |
| return (cairo_pattern_t *) &_cairo_pattern_nil; |
| } |
| } |
| |
| _cairo_pattern_init_solid (pattern, color); |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); |
| |
| return &pattern->base; |
| } |
| |
| 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; |
| |
| CAIRO_MUTEX_INITIALIZE (); |
| |
| pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); |
| if (pattern->status == CAIRO_STATUS_SUCCESS) |
| status = _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. |
| * |
| * 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_color_t color; |
| |
| red = _cairo_restrict_value (red, 0.0, 1.0); |
| green = _cairo_restrict_value (green, 0.0, 1.0); |
| blue = _cairo_restrict_value (blue, 0.0, 1.0); |
| |
| _cairo_color_init_rgb (&color, red, green, blue); |
| |
| CAIRO_MUTEX_INITIALIZE (); |
| |
| return _cairo_pattern_create_solid (&color); |
| } |
| 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. |
| * |
| * 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_color_t color; |
| |
| red = _cairo_restrict_value (red, 0.0, 1.0); |
| green = _cairo_restrict_value (green, 0.0, 1.0); |
| blue = _cairo_restrict_value (blue, 0.0, 1.0); |
| alpha = _cairo_restrict_value (alpha, 0.0, 1.0); |
| |
| _cairo_color_init_rgba (&color, red, green, blue, alpha); |
| |
| CAIRO_MUTEX_INITIALIZE (); |
| |
| return _cairo_pattern_create_solid (&color); |
| } |
| 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. |
| * |
| * 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_for_surface (cairo_surface_t *surface) |
| { |
| cairo_surface_pattern_t *pattern; |
| |
| if (surface == NULL) { |
| _cairo_error_throw (CAIRO_STATUS_NULL_POINTER); |
| return (cairo_pattern_t*) &_cairo_pattern_nil_null_pointer; |
| } |
| |
| if (surface->status) |
| return _cairo_pattern_create_in_error (surface->status); |
| |
| pattern = |
| _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_SURFACE]); |
| if (unlikely (pattern == NULL)) { |
| pattern = malloc (sizeof (cairo_surface_pattern_t)); |
| if (unlikely (pattern == NULL)) { |
| _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); |
| return (cairo_pattern_t *)&_cairo_pattern_nil.base; |
| } |
| } |
| |
| CAIRO_MUTEX_INITIALIZE (); |
| |
| _cairo_pattern_init_for_surface (pattern, surface); |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); |
| |
| return &pattern->base; |
| } |
| slim_hidden_def (cairo_pattern_create_for_surface); |
| |
| /** |
| * 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(). |
| * |
| * 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_linear (double x0, double y0, double x1, double y1) |
| { |
| cairo_linear_pattern_t *pattern; |
| |
| pattern = |
| _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_LINEAR]); |
| if (unlikely (pattern == NULL)) { |
| pattern = malloc (sizeof (cairo_linear_pattern_t)); |
| if (unlikely (pattern == NULL)) { |
| _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); |
| return (cairo_pattern_t *) &_cairo_pattern_nil.base; |
| } |
| } |
| |
| CAIRO_MUTEX_INITIALIZE (); |
| |
| _cairo_pattern_init_linear (pattern, x0, y0, x1, y1); |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); |
| |
| return &pattern->base.base; |
| } |
| |
| /** |
| * 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 |
| * 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(). |
| * |
| * 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_radial (double cx0, double cy0, double radius0, |
| double cx1, double cy1, double radius1) |
| { |
| cairo_radial_pattern_t *pattern; |
| |
| pattern = |
| _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_RADIAL]); |
| if (unlikely (pattern == NULL)) { |
| pattern = malloc (sizeof (cairo_radial_pattern_t)); |
| if (unlikely (pattern == NULL)) { |
| _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); |
| return (cairo_pattern_t *) &_cairo_pattern_nil.base; |
| } |
| } |
| |
| CAIRO_MUTEX_INITIALIZE (); |
| |
| _cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1); |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->base.base.ref_count, 1); |
| |
| return &pattern->base.base; |
| } |
| |
| /* This order is specified in the diagram in the documentation for |
| * cairo_pattern_create_mesh() */ |
| static const int mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 }; |
| static const int mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 }; |
| static const int mesh_control_point_i[4] = { 1, 1, 2, 2 }; |
| static const int mesh_control_point_j[4] = { 1, 2, 2, 1 }; |
| |
| /** |
| * cairo_pattern_create_mesh: |
| * |
| * Create a new mesh pattern. |
| * |
| * Mesh patterns are tensor-product patch meshes (type 7 shadings in |
| * PDF). Mesh patterns may also be used to create other types of |
| * shadings that are special cases of tensor-product patch meshes such |
| * as Coons patch meshes (type 6 shading in PDF) and Gouraud-shaded |
| * triangle meshes (type 4 and 5 shadings in PDF). |
| * |
| * Mesh patterns consist of one or more tensor-product patches, which |
| * should be defined before using the mesh pattern. Using a mesh |
| * pattern with a partially defined patch as source or mask will put |
| * the context in an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * A tensor-product patch is defined by 4 Bézier curves (side 0, 1, 2, |
| * 3) and by 4 additional control points (P0, P1, P2, P3) that provide |
| * further control over the patch and complete the definition of the |
| * tensor-product patch. The corner C0 is the first point of the |
| * patch. |
| * |
| * Degenerate sides are permitted so straight lines may be used. A |
| * zero length line on one side may be used to create 3 sided patches. |
| * |
| * <informalexample><programlisting> |
| * C1 Side 1 C2 |
| * +---------------+ |
| * | | |
| * | P1 P2 | |
| * | | |
| * Side 0 | | Side 2 |
| * | | |
| * | | |
| * | P0 P3 | |
| * | | |
| * +---------------+ |
| * C0 Side 3 C3 |
| * </programlisting></informalexample> |
| * |
| * Each patch is constructed by first calling |
| * cairo_mesh_pattern_begin_patch(), then cairo_mesh_pattern_move_to() |
| * to specify the first point in the patch (C0). Then the sides are |
| * specified with calls to cairo_mesh_pattern_curve_to() and |
| * cairo_mesh_pattern_line_to(). |
| * |
| * The four additional control points (P0, P1, P2, P3) in a patch can |
| * be specified with cairo_mesh_pattern_set_control_point(). |
| * |
| * At each corner of the patch (C0, C1, C2, C3) a color may be |
| * specified with cairo_mesh_pattern_set_corner_color_rgb() or |
| * cairo_mesh_pattern_set_corner_color_rgba(). Any corner whose color |
| * is not explicitly specified defaults to transparent black. |
| * |
| * A Coons patch is a special case of the tensor-product patch where |
| * the control points are implicitly defined by the sides of the |
| * patch. The default value for any control point not specified is the |
| * implicit value for a Coons patch, i.e. if no control points are |
| * specified the patch is a Coons patch. |
| * |
| * A triangle is a special case of the tensor-product patch where the |
| * control points are implicitly defined by the sides of the patch, |
| * all the sides are lines and one of them has length 0, i.e. if the |
| * patch is specified using just 3 lines, it is a triangle. If the |
| * corners connected by the 0-length side have the same color, the |
| * patch is a Gouraud-shaded triangle. |
| * |
| * Patches may be oriented differently to the above diagram. For |
| * example the first point could be at the top left. The diagram only |
| * shows the relationship between the sides, corners and control |
| * points. Regardless of where the first point is located, when |
| * specifying colors, corner 0 will always be the first point, corner |
| * 1 the point between side 0 and side 1 etc. |
| * |
| * Calling cairo_mesh_pattern_end_patch() completes the current |
| * patch. If less than 4 sides have been defined, the first missing |
| * side is defined as a line from the current point to the first point |
| * of the patch (C0) and the other sides are degenerate lines from C0 |
| * to C0. The corners between the added sides will all be coincident |
| * with C0 of the patch and their color will be set to be the same as |
| * the color of C0. |
| * |
| * Additional patches may be added with additional calls to |
| * cairo_mesh_pattern_begin_patch()/cairo_mesh_pattern_end_patch(). |
| * |
| * <informalexample><programlisting> |
| * cairo_pattern_t *mesh = cairo_mesh_pattern_create_mesh (); |
| * |
| * /* Add a Coons patch */ |
| * cairo_mesh_pattern_begin_patch (mesh); |
| * cairo_mesh_pattern_move_to (pattern, 0, 0); |
| * cairo_mesh_pattern_curve_to (pattern, 30, -30, 60, 30, 100, 0); |
| * cairo_mesh_pattern_curve_to (pattern, 60, 30, 130, 60, 100, 100); |
| * cairo_mesh_pattern_curve_to (pattern, 60, 70, 30, 130, 0, 100); |
| * cairo_mesh_pattern_curve_to (pattern, 30, 70, -30, 30, 0, 0); |
| * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0); |
| * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0); |
| * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1); |
| * cairo_mesh_pattern_set_corner_color_rgb (pattern, 3, 1, 1, 0); |
| * cairo_mesh_pattern_end_patch (mesh); |
| * |
| * /* Add a Gouraud-shaded triangle */ |
| * cairo_mesh_pattern_begin_patch (mesh) |
| * cairo_mesh_pattern_move_to (pattern, 100, 100); |
| * cairo_mesh_pattern_line_to (pattern, 130, 130); |
| * cairo_mesh_pattern_line_to (pattern, 130, 70); |
| * cairo_mesh_pattern_set_corner_color_rgb (pattern, 0, 1, 0, 0); |
| * cairo_mesh_pattern_set_corner_color_rgb (pattern, 1, 0, 1, 0); |
| * cairo_mesh_pattern_set_corner_color_rgb (pattern, 2, 0, 0, 1); |
| * cairo_mesh_pattern_end_patch (mesh) |
| * </programlisting></informalexample> |
| * |
| * When two patches overlap, the last one that has been added is drawn |
| * over the first one. |
| * |
| * When a patch folds over itself, points are sorted depending on |
| * their parameter coordinates inside the patch. The v coordinate |
| * ranges from 0 to 1 when moving from side 3 to side 1; the u |
| * coordinate ranges from 0 to 1 when going from side 0 to side |
| * 2. Points with higher v coordinate hide points with lower v |
| * coordinate. When two points have the same v coordinate, the one |
| * with higher u coordinate is above. This means that points nearer to |
| * side 1 are above points nearer to side 3; when this is not |
| * sufficient to decide which point is above (for example when both |
| * points belong to side 1 or side 3) points nearer to side 2 are |
| * above points nearer to side 0. |
| * |
| * For a complete definition of tensor-product patches, see the PDF |
| * specification (ISO32000), which describes the parametrization in |
| * detail. |
| * |
| * Note: The coordinates are always 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(). |
| * |
| * 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(). |
| * |
| * Since: 1.12 |
| **/ |
| cairo_pattern_t * |
| cairo_pattern_create_mesh (void) |
| { |
| cairo_mesh_pattern_t *pattern; |
| |
| pattern = |
| _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_MESH]); |
| if (unlikely (pattern == NULL)) { |
| pattern = malloc (sizeof (cairo_mesh_pattern_t)); |
| if (unlikely (pattern == NULL)) { |
| _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); |
| return (cairo_pattern_t *) &_cairo_pattern_nil.base; |
| } |
| } |
| |
| CAIRO_MUTEX_INITIALIZE (); |
| |
| _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_MESH); |
| _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t)); |
| pattern->current_patch = NULL; |
| CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1); |
| |
| return &pattern->base; |
| } |
| |
| /** |
| * 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 * |
| cairo_pattern_reference (cairo_pattern_t *pattern) |
| { |
| if (pattern == NULL || |
| CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) |
| return pattern; |
| |
| assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); |
| |
| _cairo_reference_count_inc (&pattern->ref_count); |
| |
| return pattern; |
| } |
| slim_hidden_def (cairo_pattern_reference); |
| |
| /** |
| * 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 |
| **/ |
| cairo_pattern_type_t |
| cairo_pattern_get_type (cairo_pattern_t *pattern) |
| { |
| return pattern->type; |
| } |
| |
| /** |
| * 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; |
| } |
| |
| /** |
| * 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(). |
| **/ |
| void |
| cairo_pattern_destroy (cairo_pattern_t *pattern) |
| { |
| cairo_pattern_type_t type; |
| |
| if (pattern == NULL || |
| CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) |
| return; |
| |
| assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count)); |
| |
| if (! _cairo_reference_count_dec_and_test (&pattern->ref_count)) |
| return; |
| |
| type = pattern->type; |
| _cairo_pattern_fini (pattern); |
| |
| /* maintain a small cache of freed patterns */ |
| _freed_pool_put (&freed_pattern_pool[type], pattern); |
| } |
| slim_hidden_def (cairo_pattern_destroy); |
| |
| /** |
| * 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 || |
| CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) |
| return 0; |
| |
| return CAIRO_REFERENCE_COUNT_GET_VALUE (&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 (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) |
| return pattern->status; |
| |
| return _cairo_user_data_array_set_data (&pattern->user_data, |
| key, user_data, destroy); |
| } |
| |
| /** |
| * cairo_mesh_pattern_begin_patch: |
| * @pattern: a #cairo_pattern_t |
| * |
| * Begin a patch in a mesh pattern. |
| * |
| * After calling this function, the patch shape should be defined with |
| * cairo_mesh_pattern_move_to(), cairo_mesh_pattern_line_to() and |
| * cairo_mesh_pattern_curve_to(). |
| * |
| * After defining the patch, cairo_mesh_pattern_end_patch() must be |
| * called before using @pattern as a source or mask. |
| * |
| * Note: If @pattern is not a mesh pattern then @pattern will be put |
| * into an error status with a status of |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern already has a |
| * current patch, it will be put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_mesh_pattern_begin_patch (cairo_pattern_t *pattern) |
| { |
| cairo_mesh_pattern_t *mesh; |
| cairo_status_t status; |
| cairo_mesh_patch_t *current_patch; |
| int i; |
| |
| if (unlikely (pattern->status)) |
| return; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| return; |
| } |
| |
| mesh = (cairo_mesh_pattern_t *) pattern; |
| if (unlikely (mesh->current_patch)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| status = _cairo_array_allocate (&mesh->patches, 1, (void **) ¤t_patch); |
| if (unlikely (status)) { |
| _cairo_pattern_set_error (pattern, status); |
| return; |
| } |
| |
| mesh->current_patch = current_patch; |
| mesh->current_side = -2; /* no current point */ |
| |
| for (i = 0; i < 4; i++) |
| mesh->has_control_point[i] = FALSE; |
| |
| for (i = 0; i < 4; i++) |
| mesh->has_color[i] = FALSE; |
| } |
| |
| |
| static void |
| _calc_control_point (cairo_mesh_patch_t *patch, int control_point) |
| { |
| /* The Coons patch is a special case of the Tensor Product patch |
| * where the four control points are: |
| * |
| * P11 = S(1/3, 1/3) |
| * P12 = S(1/3, 2/3) |
| * P21 = S(2/3, 1/3) |
| * P22 = S(2/3, 2/3) |
| * |
| * where S is the gradient surface. |
| * |
| * When one or more control points has not been specified |
| * calculated the Coons patch control points are substituted. If |
| * no control points are specified the gradient will be a Coons |
| * patch. |
| * |
| * The equations below are defined in the ISO32000 standard. |
| */ |
| cairo_point_double_t *p[3][3]; |
| int cp_i, cp_j, i, j; |
| |
| cp_i = mesh_control_point_i[control_point]; |
| cp_j = mesh_control_point_j[control_point]; |
| |
| for (i = 0; i < 3; i++) |
| for (j = 0; j < 3; j++) |
| p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j]; |
| |
| p[0][0]->x = (- 4 * p[1][1]->x |
| + 6 * (p[1][0]->x + p[0][1]->x) |
| - 2 * (p[1][2]->x + p[2][1]->x) |
| + 3 * (p[2][0]->x + p[0][2]->x) |
| - 1 * p[2][2]->x) * (1. / 9); |
| |
| p[0][0]->y = (- 4 * p[1][1]->y |
| + 6 * (p[1][0]->y + p[0][1]->y) |
| - 2 * (p[1][2]->y + p[2][1]->y) |
| + 3 * (p[2][0]->y + p[0][2]->y) |
| - 1 * p[2][2]->y) * (1. / 9); |
| } |
| |
| /** |
| * cairo_mesh_pattern_end_patch: |
| * @pattern: a #cairo_pattern_t |
| * |
| * Indicates the end of the current patch in a mesh pattern. |
| * |
| * If the current patch has less than 4 sides, it is closed with a |
| * straight line from the current point to the first point of the |
| * patch as if cairo_mesh_pattern_line_to() was used. |
| * |
| * Note: If @pattern is not a mesh pattern then @pattern will be put |
| * into an error status with a status of |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current |
| * patch or the current patch has no current point, @pattern will be |
| * put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern) |
| { |
| cairo_mesh_pattern_t *mesh; |
| cairo_mesh_patch_t *current_patch; |
| int i; |
| |
| if (unlikely (pattern->status)) |
| return; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| return; |
| } |
| |
| mesh = (cairo_mesh_pattern_t *) pattern; |
| current_patch = mesh->current_patch; |
| if (unlikely (!current_patch)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| if (unlikely (mesh->current_side == -2)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| while (mesh->current_side < 3) { |
| int corner_num; |
| |
| cairo_mesh_pattern_line_to (pattern, |
| current_patch->points[0][0].x, |
| current_patch->points[0][0].y); |
| |
| corner_num = mesh->current_side + 1; |
| if (corner_num < 4 && ! mesh->has_color[corner_num]) { |
| current_patch->colors[corner_num] = current_patch->colors[0]; |
| mesh->has_color[corner_num] = TRUE; |
| } |
| } |
| |
| for (i = 0; i < 4; i++) { |
| if (! mesh->has_control_point[i]) |
| _calc_control_point (current_patch, i); |
| } |
| |
| for (i = 0; i < 4; i++) { |
| if (! mesh->has_color[i]) |
| current_patch->colors[i] = *CAIRO_COLOR_TRANSPARENT; |
| } |
| |
| mesh->current_patch = NULL; |
| } |
| |
| /** |
| * cairo_mesh_pattern_curve_to: |
| * @pattern: a #cairo_pattern_t |
| * @x1: the X coordinate of the first control point |
| * @y1: the Y coordinate of the first control point |
| * @x2: the X coordinate of the second control point |
| * @y2: the Y coordinate of the second control point |
| * @x3: the X coordinate of the end of the curve |
| * @y3: the Y coordinate of the end of the curve |
| * |
| * Adds a cubic Bézier spline to the current patch from the current |
| * point to position (@x3, @y3) in pattern-space coordinates, using |
| * (@x1, @y1) and (@x2, @y2) as the control points. |
| * |
| * If the current patch has no current point before the call to |
| * cairo_mesh_pattern_curve_to(), this function will behave as if |
| * preceded by a call to cairo_mesh_pattern_move_to(@pattern, @x1, |
| * @y1). |
| * |
| * After this call the current point will be (@x3, @y3). |
| * |
| * Note: If @pattern is not a mesh pattern then @pattern will be put |
| * into an error status with a status of |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current |
| * patch or the current patch already has 4 sides, @pattern will be |
| * put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern, |
| double x1, double y1, |
| double x2, double y2, |
| double x3, double y3) |
| { |
| cairo_mesh_pattern_t *mesh; |
| int current_point, i, j; |
| |
| if (unlikely (pattern->status)) |
| return; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| return; |
| } |
| |
| mesh = (cairo_mesh_pattern_t *) pattern; |
| if (unlikely (!mesh->current_patch)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| if (unlikely (mesh->current_side == 3)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| if (mesh->current_side == -2) |
| cairo_mesh_pattern_move_to (pattern, x1, y1); |
| |
| assert (mesh->current_side >= -1); |
| assert (pattern->status == CAIRO_STATUS_SUCCESS); |
| |
| mesh->current_side++; |
| |
| current_point = 3 * mesh->current_side; |
| |
| current_point++; |
| i = mesh_path_point_i[current_point]; |
| j = mesh_path_point_j[current_point]; |
| mesh->current_patch->points[i][j].x = x1; |
| mesh->current_patch->points[i][j].y = y1; |
| |
| current_point++; |
| i = mesh_path_point_i[current_point]; |
| j = mesh_path_point_j[current_point]; |
| mesh->current_patch->points[i][j].x = x2; |
| mesh->current_patch->points[i][j].y = y2; |
| |
| current_point++; |
| if (current_point < 12) { |
| i = mesh_path_point_i[current_point]; |
| j = mesh_path_point_j[current_point]; |
| mesh->current_patch->points[i][j].x = x3; |
| mesh->current_patch->points[i][j].y = y3; |
| } |
| } |
| slim_hidden_def (cairo_mesh_pattern_curve_to); |
| |
| /** |
| * cairo_mesh_pattern_line_to: |
| * @pattern: a #cairo_pattern_t |
| * @x: the X coordinate of the end of the new line |
| * @y: the Y coordinate of the end of the new line |
| * |
| * Adds a line to the current patch from the current point to position |
| * (@x, @y) in pattern-space coordinates. |
| * |
| * If there is no current point before the call to |
| * cairo_mesh_pattern_line_to() this function will behave as |
| * cairo_mesh_pattern_move_to(@pattern, @x, @y). |
| * |
| * After this call the current point will be (@x, @y). |
| * |
| * Note: If @pattern is not a mesh pattern then @pattern will be put |
| * into an error status with a status of |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current |
| * patch or the current patch already has 4 sides, @pattern will be |
| * put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_mesh_pattern_line_to (cairo_pattern_t *pattern, |
| double x, double y) |
| { |
| cairo_mesh_pattern_t *mesh; |
| cairo_point_double_t last_point; |
| int last_point_idx, i, j; |
| |
| if (unlikely (pattern->status)) |
| return; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| return; |
| } |
| |
| mesh = (cairo_mesh_pattern_t *) pattern; |
| if (unlikely (!mesh->current_patch)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| if (unlikely (mesh->current_side == 3)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| if (mesh->current_side == -2) { |
| cairo_mesh_pattern_move_to (pattern, x, y); |
| return; |
| } |
| |
| last_point_idx = 3 * (mesh->current_side + 1); |
| i = mesh_path_point_i[last_point_idx]; |
| j = mesh_path_point_j[last_point_idx]; |
| |
| last_point = mesh->current_patch->points[i][j]; |
| |
| cairo_mesh_pattern_curve_to (pattern, |
| (2 * last_point.x + x) * (1. / 3), |
| (2 * last_point.y + y) * (1. / 3), |
| (last_point.x + 2 * x) * (1. / 3), |
| (last_point.y + 2 * y) * (1. / 3), |
| x, y); |
| } |
| slim_hidden_def (cairo_mesh_pattern_line_to); |
| |
| /** |
| * cairo_mesh_pattern_move_to: |
| * @pattern: a #cairo_pattern_t |
| * @x: the X coordinate of the new position |
| * @y: the Y coordinate of the new position |
| * |
| * Define the first point of the current patch in a mesh pattern. |
| * |
| * After this call the current point will be (@x, @y). |
| * |
| * Note: If @pattern is not a mesh pattern then @pattern will be put |
| * into an error status with a status of |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current |
| * patch or the current patch already has at leas one side, @pattern |
| * will be put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_mesh_pattern_move_to (cairo_pattern_t *pattern, |
| double x, double y) |
| { |
| cairo_mesh_pattern_t *mesh; |
| |
| if (unlikely (pattern->status)) |
| return; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| return; |
| } |
| |
| mesh = (cairo_mesh_pattern_t *) pattern; |
| if (unlikely (!mesh->current_patch)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| if (unlikely (mesh->current_side >= 0)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| mesh->current_side = -1; |
| mesh->current_patch->points[0][0].x = x; |
| mesh->current_patch->points[0][0].y = y; |
| } |
| slim_hidden_def (cairo_mesh_pattern_move_to); |
| |
| /** |
| * cairo_mesh_pattern_set_control_point: |
| * @pattern: a #cairo_pattern_t |
| * @point_num: the control point to set the position for |
| * @x: the X coordinate of the control point |
| * @y: the Y coordinate of the control point |
| * |
| * Set an internal control point of the current patch. |
| * |
| * Valid values for @point_num are from 0 to 3 and identify the |
| * control points as explained in cairo_pattern_create_mesh(). |
| * |
| * Note: If @pattern is not a mesh pattern then @pattern will be put |
| * into an error status with a status of |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @point_num is not valid, |
| * @pattern will be put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, |
| * @pattern will be put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_mesh_pattern_set_control_point (cairo_pattern_t *pattern, |
| unsigned int point_num, |
| double x, |
| double y) |
| { |
| cairo_mesh_pattern_t *mesh; |
| int i, j; |
| |
| if (unlikely (pattern->status)) |
| return; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| return; |
| } |
| |
| if (unlikely (point_num > 3)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX); |
| return; |
| } |
| |
| mesh = (cairo_mesh_pattern_t *) pattern; |
| if (unlikely (!mesh->current_patch)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| i = mesh_control_point_i[point_num]; |
| j = mesh_control_point_j[point_num]; |
| |
| mesh->current_patch->points[i][j].x = x; |
| mesh->current_patch->points[i][j].y = y; |
| mesh->has_control_point[point_num] = TRUE; |
| } |
| |
| /* 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; |
| } |
| |
| if (CAIRO_INJECT_FAULT ()) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| 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 = _cairo_realloc_ab (pattern->stops, |
| new_size, |
| sizeof (cairo_gradient_stop_t)); |
| } |
| |
| if (unlikely (new_stops == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| pattern->stops = new_stops; |
| pattern->stops_size = new_size; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| static void |
| _cairo_mesh_pattern_set_corner_color (cairo_mesh_pattern_t *mesh, |
| unsigned int corner_num, |
| double red, double green, double blue, |
| double alpha) |
| { |
| cairo_color_t *color; |
| |
| assert (mesh->current_patch); |
| assert (corner_num <= 3); |
| |
| color = &mesh->current_patch->colors[corner_num]; |
| color->red = red; |
| color->green = green; |
| color->blue = blue; |
| color->alpha = alpha; |
| |
| color->red_short = _cairo_color_double_to_short (red); |
| color->green_short = _cairo_color_double_to_short (green); |
| color->blue_short = _cairo_color_double_to_short (blue); |
| color->alpha_short = _cairo_color_double_to_short (alpha); |
| |
| mesh->has_color[corner_num] = TRUE; |
| } |
| |
| /** |
| * cairo_mesh_pattern_set_corner_color_rgb: |
| * @pattern: a #cairo_pattern_t |
| * @corner_num: the corner to set the color for |
| * @red: red component of color |
| * @green: green component of color |
| * @blue: blue component of color |
| * |
| * Sets the color of a corner of the current patch in a mesh pattern. |
| * |
| * The color is specified in the same way as in cairo_set_source_rgb(). |
| * |
| * Valid values for @corner_num are from 0 to 3 and identify the |
| * corners as explained in cairo_pattern_create_mesh(). |
| * |
| * Note: If @pattern is not a mesh pattern then @pattern will be put |
| * into an error status with a status of |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid, |
| * @pattern will be put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, |
| * @pattern will be put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_mesh_pattern_set_corner_color_rgb (cairo_pattern_t *pattern, |
| unsigned int corner_num, |
| double red, double green, double blue) |
| { |
| cairo_mesh_pattern_set_corner_color_rgba (pattern, corner_num, red, green, blue, 1.0); |
| } |
| |
| /** |
| * cairo_mesh_pattern_set_corner_color_rgba: |
| * @pattern: a #cairo_pattern_t |
| * @corner_num: the corner to set the color for |
| * @red: red component of color |
| * @green: green component of color |
| * @blue: blue component of color |
| * @alpha: alpha component of color |
| * |
| * Sets the color of a corner of the current patch in a mesh pattern. |
| * |
| * The color is specified in the same way as in cairo_set_source_rgba(). |
| * |
| * Valid values for @corner_num are from 0 to 3 and identify the |
| * corners as explained in cairo_pattern_create_mesh(). |
| * |
| * Note: If @pattern is not a mesh pattern then @pattern will be put |
| * into an error status with a status of |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @corner_num is not valid, |
| * @pattern will be put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_INDEX. If @pattern has no current patch, |
| * @pattern will be put into an error status with a status of |
| * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
| * |
| * Since: 1.12 |
| **/ |
| void |
| cairo_mesh_pattern_set_corner_color_rgba (cairo_pattern_t *pattern, |
| unsigned int corner_num, |
| double red, double green, double blue, |
| double alpha) |
| { |
| cairo_mesh_pattern_t *mesh; |
| |
| if (unlikely (pattern->status)) |
| return; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| return; |
| } |
| |
| if (unlikely (corner_num > 3)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_INDEX); |
| return; |
| } |
| |
| mesh = (cairo_mesh_pattern_t *) pattern; |
| if (unlikely (!mesh->current_patch)) { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
| return; |
| } |
| |
| red = _cairo_restrict_value (red, 0.0, 1.0); |
| green = _cairo_restrict_value (green, 0.0, 1.0); |
| blue = _cairo_restrict_value (blue, 0.0, 1.0); |
| alpha = _cairo_restrict_value (alpha, 0.0, 1.0); |
| |
| _cairo_mesh_pattern_set_corner_color (mesh, corner_num, red, green, blue, alpha); |
| } |
| slim_hidden_def (cairo_mesh_pattern_set_corner_color_rgba); |
| |
| static void |
| _cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern, |
| double offset, |
| double red, |
| double green, |
| double blue, |
| double alpha) |
| { |
| cairo_gradient_stop_t *stops; |
| unsigned int i; |
| |
| if (pattern->n_stops >= pattern->stops_size) { |
| cairo_status_t status = _cairo_pattern_gradient_grow (pattern); |
| if (unlikely (status)) { |
| status = _cairo_pattern_set_error (&pattern->base, status); |
| return; |
| } |
| } |
| |
| stops = pattern->stops; |
| |
| for (i = 0; i < pattern->n_stops; i++) |
| { |
| if (offset < stops[i].offset) |
| { |
| memmove (&stops[i + 1], &stops[i], |
| sizeof (cairo_gradient_stop_t) * (pattern->n_stops - i)); |
| |
| break; |
| } |
| } |
| |
| stops[i].offset = offset; |
| |
| 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); |
| |
| 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 |
| 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) |
| { |
| _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| return; |
| } |
| |
| offset = _cairo_restrict_value (offset, 0.0, 1.0); |
| red = _cairo_restrict_value (red, 0.0, 1.0); |
| green = _cairo_restrict_value (green, 0.0, 1.0); |
| blue = _cairo_restrict_value (blue, 0.0, 1.0); |
| |
| _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, |
| offset, red, green, blue, 1.0); |
| } |
| |
| /** |
| * 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 |
| 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; |
| } |
| |
| offset = _cairo_restrict_value (offset, 0.0, 1.0); |
| red = _cairo_restrict_value (red, 0.0, 1.0); |
| green = _cairo_restrict_value (green, 0.0, 1.0); |
| blue = _cairo_restrict_value (blue, 0.0, 1.0); |
| alpha = _cairo_restrict_value (alpha, 0.0, 1.0); |
| |
| _cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern, |
| offset, red, green, blue, alpha); |
| } |
| |
| /** |
| * 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 (&matrix, 0.5, 0.5); |
| * cairo_pattern_set_matrix (pattern, &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) |
| { |
| cairo_matrix_t inverse; |
| cairo_status_t status; |
| |
| if (pattern->status) |
| return; |
| |
| if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0) |
| return; |
| |
| pattern->matrix = *matrix; |
| |
| inverse = *matrix; |
| status = cairo_matrix_invert (&inverse); |
| if (unlikely (status)) |
| status = _cairo_pattern_set_error (pattern, status); |
| } |
| slim_hidden_def (cairo_pattern_set_matrix); |
| |
| /** |
| * cairo_pattern_get_matrix: |
| * @pattern: a #cairo_pattern_t |
| * @matrix: return value for the matrix |
| * |
| * Stores the pattern's transformation matrix into @matrix. |
| **/ |
| void |
| cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix) |
| { |
| *matrix = pattern->matrix; |
| } |
| |
| /** |
| * 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. |
| * |
| * * Note that you might want to control filtering even when you do not |
| * have an explicit #cairo_pattern_t object, (for example when using |
| * cairo_set_source_surface()). In these cases, it is convenient to |
| * use cairo_get_source() to get access to the pattern that cairo |
| * creates implicitly. For example: |
| * |
| * <informalexample><programlisting> |
| * cairo_set_source_surface (cr, image, x, y); |
| * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST); |
| * </programlisting></informalexample> |
| **/ |
| void |
| cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter) |
| { |
| if (pattern->status) |
| return; |
| |
| pattern->filter = filter; |
| } |
| |
| /** |
| * 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. |
| **/ |
| 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. |
| * |
| * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns |
| * and %CAIRO_EXTEND_PAD for gradient patterns. |
| **/ |
| void |
| cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend) |
| { |
| if (pattern->status) |
| return; |
| |
| pattern->extend = extend; |
| } |
| |
| /** |
| * 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. |
| **/ |
| cairo_extend_t |
| cairo_pattern_get_extend (cairo_pattern_t *pattern) |
| { |
| return pattern->extend; |
| } |
| slim_hidden_def (cairo_pattern_get_extend); |
| |
| void |
| _cairo_pattern_transform (cairo_pattern_t *pattern, |
| const cairo_matrix_t *ctm_inverse) |
| { |
| if (pattern->status) |
| return; |
| |
| cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix); |
| } |
| |
| 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; |
| |
| /* 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 = pattern->pd1; |
| point1 = pattern->pd2; |
| |
| _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 (const 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_circle_double_t extremes[2]; |
| pixman_point_fixed_t p1, p2; |
| cairo_status_t status; |
| cairo_bool_t repeat = FALSE; |
| int ix, iy; |
| pixman_gradient_stop_t pixman_stops_static[2]; |
| pixman_gradient_stop_t *pixman_stops = pixman_stops_static; |
| unsigned int i; |
| int clone_offset_x, clone_offset_y; |
| cairo_matrix_t matrix = pattern->base.matrix; |
| |
| if (CAIRO_INJECT_FAULT ()) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { |
| pixman_stops = _cairo_malloc_ab (pattern->n_stops, |
| sizeof(pixman_gradient_stop_t)); |
| if (unlikely (pixman_stops == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| } |
| |
| for (i = 0; i < pattern->n_stops; i++) { |
| pixman_stops[i].x = _cairo_fixed_16_16_from_double (pattern->stops[i].offset); |
| 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; |
| } |
| |
| _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes); |
| |
| p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x); |
| p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y); |
| p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x); |
| p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y); |
| |
| if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) { |
| pixman_image = pixman_image_create_linear_gradient (&p1, &p2, |
| pixman_stops, |
| pattern->n_stops); |
| } else { |
| pixman_fixed_t r1, r2; |
| |
| r1 = _cairo_fixed_16_16_from_double (extremes[0].radius); |
| r2 = _cairo_fixed_16_16_from_double (extremes[1].radius); |
| |
| pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2, |
| pixman_stops, |
| pattern->n_stops); |
| } |
| |
| if (pixman_stops != pixman_stops_static) |
| free (pixman_stops); |
| |
| if (unlikely (pixman_image == NULL)) |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| |
| if (_cairo_surface_is_image (dst)) |
| { |
| image = (cairo_image_surface_t *) |
| _cairo_image_surface_create_for_pixman_image (pixman_image, |
| PIXMAN_a8r8g8b8); |
| if (image->base.status) |
| { |
| pixman_image_unref (pixman_image); |
| return image->base.status; |
| } |
| |
| attr->x_offset = attr->y_offset = 0; |
| attr->matrix = matrix; |
| attr->extend = pattern->base.extend; |
| attr->filter = CAIRO_FILTER_NEAREST; |
| attr->has_component_alpha = pattern->base.has_component_alpha; |
| |
| *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; |
| } |
| } |
| |
| if (! pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR, |
| NULL, 0)) |
| { |
| pixman_image_unref (pixman_image); |
| return _cairo_error (CAIRO_STATUS_NO_MEMORY); |
| } |
| |
| image = (cairo_image_surface_t *) |
| cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); |
| if (image->base.status) { |
| pixman_image_unref (pixman_image); |
| return image->base.status; |
| } |
| |
| ix = x; |
| iy = y; |
| status = _cairo_matrix_to_pixman_matrix_offset (&matrix, |
| pattern->base.filter, |
| width/2., height/2., |
| &pixman_transform, |
| &ix, &iy); |
| if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) { |
| if (unlikely (status != CAIRO_STATUS_SUCCESS) || |
| ! pixman_image_set_transform (pixman_image, &pixman_transform)) |
| { |
| cairo_surface_destroy (&image->base); |
| pixman_image_unref (pixman_image); |
| return _cairo_error (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_composite32 (PIXMAN_OP_SRC, |
| pixman_image, |
| NULL, |
| image->pixman_image, |
| ix, iy, |
| 0, 0, |
| 0, 0, |
| width, height); |
| |
| pixman_image_unref (pixman_image); |
| |
| _cairo_debug_check_image_surface_is_defined (&image->base); |
| |
| status = _cairo_surface_clone_similar (dst, &image->base, |
| 0, 0, width, height, |
| &clone_offset_x, |
| &clone_offset_y, |
| 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->has_component_alpha = pattern->base.has_component_alpha; |
| |
| return status; |
| } |
| |
| static cairo_int_status_t |
| _cairo_pattern_acquire_surface_for_mesh (const cairo_mesh_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_surface_t *image; |
| void *data; |
| cairo_status_t status; |
| int clone_offset_x, clone_offset_y; |
| int stride; |
| |
| image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); |
| if (unlikely (image->status)) |
| return image->status; |
| |
| stride = cairo_image_surface_get_stride (image); |
| data = cairo_image_surface_get_data (image); |
| |
| _cairo_mesh_pattern_rasterize (pattern, data, width, height, stride, -x, -y); |
| |
| attr->x_offset = -x; |
| attr->y_offset = -y; |
| attr->filter = CAIRO_FILTER_NEAREST; |
| attr->extend = pattern->base.extend; |
| cairo_matrix_init_identity (&attr->matrix); |
| attr->has_component_alpha = pattern->base.has_component_alpha; |
| |
| if (_cairo_surface_is_image (dst)) { |
| *out = image; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| status = _cairo_surface_clone_similar (dst, image, |
| 0, 0, width, height, |
| &clone_offset_x, |
| &clone_offset_y, out); |
| |
| cairo_surface_destroy (image); |
| |
| return status; |
| } |
| |
| /* 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 (cairo_surface_get_content (cache->surface) != _cairo_color_get_content (&pattern->color)) |
| return FALSE; |
| |
| if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1) |
| return FALSE; |
| |
| if (! _cairo_surface_is_similar (cache->surface, dst)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| static cairo_bool_t |
| _cairo_pattern_solid_surface_matches_color ( |
| const struct _cairo_pattern_solid_surface_cache *cache, |
| const cairo_solid_pattern_t *pattern, |
| cairo_surface_t *dst) |
| { |
| if (! _cairo_color_equal (&cache->color, &pattern->color)) |
| return FALSE; |
| |
| return _cairo_pattern_solid_surface_matches (cache, pattern, dst); |
| } |
| |
| static cairo_int_status_t |
| _cairo_pattern_acquire_surface_for_solid (const 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) |
| { |
| static int i; |
| |
| cairo_surface_t *surface, *to_destroy = NULL; |
| 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_color (&solid_surface_cache.cache[i], |
| pattern, |
| dst)) |
| { |
| goto DONE; |
| } |
| |
| for (i = 0 ; i < solid_surface_cache.size; i++) { |
| if (_cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i], |
| pattern, |
| dst)) |
| { |
| goto DONE; |
| } |
| } |
| |
| /* Choose a surface to repaint/evict */ |
| surface = NULL; |
| if (solid_surface_cache.size == MAX_SURFACE_CACHE_SIZE) { |
| i = rand () % MAX_SURFACE_CACHE_SIZE; |
| surface = solid_surface_cache.cache[i].surface; |
| |
| if (_cairo_pattern_solid_surface_matches (&solid_surface_cache.cache[i], |
| pattern, |
| dst)) |
| { |
| /* Reuse the surface instead of evicting */ |
| status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern); |
| if (unlikely (status)) |
| goto EVICT; |
| |
| cairo_surface_reference (surface); |
| } |
| else |
| { |
| EVICT: |
| surface = NULL; |
| } |
| } |
| |
| if (surface == NULL) { |
| /* Not cached, need to create new */ |
| surface = _cairo_surface_create_solid_pattern_surface (dst, pattern); |
| if (surface == NULL) { |
| status = CAIRO_INT_STATUS_UNSUPPORTED; |
| goto UNLOCK; |
| } |
| if (unlikely (surface->status)) { |
| status = surface->status; |
| goto UNLOCK; |
| } |
| |
| if (unlikely (! _cairo_surface_is_similar (surface, dst))) |
| { |
| /* In the rare event of a substitute surface being returned, |
| * don't cache the fallback. |
| */ |
| *out = surface; |
| goto NOCACHE; |
| } |
| } |
| |
| if (i == solid_surface_cache.size) |
| solid_surface_cache.size++; |
| |
| to_destroy = solid_surface_cache.cache[i].surface; |
| solid_surface_cache.cache[i].surface = surface; |
| solid_surface_cache.cache[i].color = pattern->color; |
| |
| 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->has_component_alpha = pattern->base.has_component_alpha; |
| |
| status = CAIRO_STATUS_SUCCESS; |
| |
| UNLOCK: |
| CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); |
| |
| if (to_destroy) |
| cairo_surface_destroy (to_destroy); |
| |
| return status; |
| } |
| |
| static void |
| _cairo_pattern_reset_solid_surface_cache (void) |
| { |
| CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); |
| |
| /* remove surfaces starting from the end so that solid_surface_cache.cache |
| * is always in a consistent state when we release the mutex. */ |
| while (solid_surface_cache.size) { |
| cairo_surface_t *surface; |
| |
| solid_surface_cache.size--; |
| surface = solid_surface_cache.cache[solid_surface_cache.size].surface; |
| solid_surface_cache.cache[solid_surface_cache.size].surface = NULL; |
| |
| /* release the lock to avoid the possibility of a recursive |
| * deadlock when the surface destroy closure gets called */ |
| CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); |
| cairo_surface_destroy (surface); |
| CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock); |
| } |
| |
| CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock); |
| } |
| |
| static cairo_bool_t |
| _linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear) |
| { |
| return fabs (linear->pd1.x - linear->pd2.x) < DBL_EPSILON && |
| fabs (linear->pd1.y - linear->pd2.y) < DBL_EPSILON; |
| } |
| |
| static cairo_bool_t |
| _radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial) |
| { |
| /* A radial pattern is considered degenerate if it can be |
| * represented as a solid or clear pattern. This corresponds to |
| * one of the two cases: |
| * |
| * 1) The radii are both very small: |
| * |dr| < DBL_EPSILON && min (r0, r1) < DBL_EPSILON |
| * |
| * 2) The two circles have about the same radius and are very |
| * close to each other (approximately a cylinder gradient that |
| * doesn't move with the parameter): |
| * |dr| < DBL_EPSILON && max (|dx|, |dy|) < 2 * DBL_EPSILON |
| * |
| * These checks are consistent with the assumptions used in |
| * _cairo_radial_pattern_box_to_parameter (). |
| */ |
| |
| return fabs (radial->cd1.radius - radial->cd2.radius) < DBL_EPSILON && |
| (MIN (radial->cd1.radius, radial->cd2.radius) < DBL_EPSILON || |
| MAX (fabs (radial->cd1.center.x - radial->cd2.center.x), |
| fabs (radial->cd1.center.y - radial->cd2.center.y)) < 2 * DBL_EPSILON); |
| } |
| |
| static void |
| _cairo_linear_pattern_box_to_parameter (const cairo_linear_pattern_t *linear, |
| double x0, double y0, |
| double x1, double y1, |
| double range[2]) |
| { |
| double t0, tdx, tdy; |
| double p1x, p1y, pdx, pdy, invsqnorm; |
| |
| assert (! _linear_pattern_is_degenerate (linear)); |
| |
| /* |
| * Linear gradients are othrogonal to the line passing through |
| * their extremes. Because of convexity, the parameter range can |
| * be computed as the convex hull (one the real line) of the |
| * parameter values of the 4 corners of the box. |
| * |
| * The parameter value t for a point (x,y) can be computed as: |
| * |
| * t = (p2 - p1) . (x,y) / |p2 - p1|^2 |
| * |
| * t0 is the t value for the top left corner |
| * tdx is the difference between left and right corners |
| * tdy is the difference between top and bottom corners |
| */ |
| |
| p1x = linear->pd1.x; |
| p1y = linear->pd1.y; |
| pdx = linear->pd2.x - p1x; |
| pdy = linear->pd2.y - p1y; |
| invsqnorm = 1.0 / (pdx * pdx + pdy * pdy); |
| pdx *= invsqnorm; |
| pdy *= invsqnorm; |
| |
| t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy; |
| tdx = (x1 - x0) * pdx; |
| tdy = (y1 - y0) * pdy; |
| |
| /* |
| * Because of the linearity of the t value, tdx can simply be |
| * added the t0 to move along the top edge. After this, range[0] |
| * and range[1] represent the parameter range for the top edge, so |
| * extending it to include the whole box simply requires adding |
| * tdy to the correct extreme. |
| */ |
| |
| range[0] = range[1] = t0; |
| if (tdx < 0) |
| range[0] += tdx; |
| else |
| range[1] += tdx; |
| |
| if (tdy < 0) |
| range[0] += tdy; |
| else |
| range[1] += tdy; |
| } |
| |
| static cairo_bool_t |
| _extend_range (double range[2], double value, cairo_bool_t valid) |
| { |
| if (!valid) |
| range[0] = range[1] = value; |
| else if (value < range[0]) |
| range[0] = value; |
| else if (value > range[1]) |
| range[1] = value; |
| |
| return TRUE; |
| } |
| |
| static void |
| _cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial, |
| double x0, double y0, |
| double x1, double y1, |
| double tolerance, |
| double range[2]) |
| { |
| double cx, cy, cr, dx, dy, dr; |
| double a, x_focus, y_focus; |
| double mindr, minx, miny, maxx, maxy; |
| cairo_bool_t valid; |
| |
| assert (! _radial_pattern_is_degenerate (radial)); |
| assert (x0 < x1); |
| assert (y0 < y1); |
| |
| tolerance = MAX (tolerance, DBL_EPSILON); |
| |
| range[0] = range[1] = 0; |
| valid = FALSE; |
| |
| x_focus = y_focus = 0; /* silence gcc */ |
| |
| cx = radial->cd1.center.x; |
| cy = radial->cd1.center.y; |
| cr = radial->cd1.radius; |
| dx = radial->cd2.center.x - cx; |
| dy = radial->cd2.center.y - cy; |
| dr = radial->cd2.radius - cr; |
| |
| /* translate by -(cx, cy) to simplify computations */ |
| x0 -= cx; |
| y0 -= cy; |
| x1 -= cx; |
| y1 -= cy; |
| |
| /* enlarge boundaries slightly to avoid rounding problems in the |
| * parameter range computation */ |
| x0 -= DBL_EPSILON; |
| y0 -= DBL_EPSILON; |
| x1 += DBL_EPSILON; |
| y1 += DBL_EPSILON; |
| |
| /* enlarge boundaries even more to avoid rounding problems when |
| * testing if a point belongs to the box */ |
| minx = x0 - DBL_EPSILON; |
| miny = y0 - DBL_EPSILON; |
| maxx = x1 + DBL_EPSILON; |
| maxy = y1 + DBL_EPSILON; |
| |
| /* we dont' allow negative radiuses, so we will be checking that |
| * t*dr >= mindr to consider t valid */ |
| mindr = -(cr + DBL_EPSILON); |
| |
| /* |
| * After the previous transformations, the start circle is |
| * centered in the origin and has radius cr. A 1-unit change in |
| * the t parameter corresponds to dx,dy,dr changes in the x,y,r of |
| * the circle (center coordinates, radius). |
| * |
| * To compute the minimum range needed to correctly draw the |
| * pattern, we start with an empty range and extend it to include |
| * the circles touching the bounding box or within it. |
| */ |
| |
| /* |
| * Focus, the point where the circle has radius == 0. |
| * |
| * r = cr + t * dr = 0 |
| * t = -cr / dr |
| * |
| * If the radius is constant (dr == 0) there is no focus (the |
| * gradient represents a cylinder instead of a cone). |
| */ |
| if (fabs (dr) >= DBL_EPSILON) { |
| double t_focus; |
| |
| t_focus = -cr / dr; |
| x_focus = t_focus * dx; |
| y_focus = t_focus * dy; |
| if (minx <= x_focus && x_focus <= maxx && |
| miny <= y_focus && y_focus <= maxy) |
| { |
| valid = _extend_range (range, t_focus, valid); |
| } |
| } |
| |
| /* |
| * Circles externally tangent to box edges. |
| * |
| * All circles have center in (dx, dy) * t |
| * |
| * If the circle is tangent to the line defined by the edge of the |
| * box, then at least one of the following holds true: |
| * |
| * (dx*t) + (cr + dr*t) == x0 (left edge) |
| * (dx*t) - (cr + dr*t) == x1 (right edge) |
| * (dy*t) + (cr + dr*t) == y0 (top edge) |
| * (dy*t) - (cr + dr*t) == y1 (bottom edge) |
| * |
| * The solution is only valid if the tangent point is actually on |
| * the edge, i.e. if its y coordinate is in [y0,y1] for left/right |
| * edges and if its x coordinate is in [x0,x1] for top/bottom |
| * edges. |
| * |
| * For the first equation: |
| * |
| * (dx + dr) * t = x0 - cr |
| * t = (x0 - cr) / (dx + dr) |
| * y = dy * t |
| * |
| * in the code this becomes: |
| * |
| * t_edge = (num) / (den) |
| * v = (delta) * t_edge |
| * |
| * If the denominator in t is 0, the pattern is tangent to a line |
| * parallel to the edge under examination. The corner-case where |
| * the boundary line is the same as the edge is handled by the |
| * focus point case and/or by the a==0 case. |
| */ |
| #define T_EDGE(num,den,delta,lower,upper) \ |
| if (fabs (den) >= DBL_EPSILON) { \ |
| double t_edge, v; \ |
| \ |
| t_edge = (num) / (den); \ |
| v = t_edge * (delta); \ |
| if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) \ |
| valid = _extend_range (range, t_edge, valid); \ |
| } |
| |
| /* circles tangent (externally) to left/right/top/bottom edge */ |
| T_EDGE (x0 - cr, dx + dr, dy, miny, maxy); |
| T_EDGE (x1 + cr, dx - dr, dy, miny, maxy); |
| T_EDGE (y0 - cr, dy + dr, dx, minx, maxx); |
| T_EDGE (y1 + cr, dy - dr, dx, minx, maxx); |
| |
| #undef T_EDGE |
| |
| /* |
| * Circles passing through a corner. |
| * |
| * A circle passing through the point (x,y) satisfies: |
| * |
| * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2 |
| * |
| * If we set: |
| * a = dx^2 + dy^2 - dr^2 |
| * b = x*dx + y*dy + cr*dr |
| * c = x^2 + y^2 - cr^2 |
| * we have: |
| * a*t^2 - 2*b*t + c == 0 |
| */ |
| a = dx * dx + dy * dy - dr * dr; |
| if (fabs (a) < DBL_EPSILON * DBL_EPSILON) { |
| double b, maxd2; |
| |
| /* Ensure that gradients with both a and dr small are |
| * considered degenerate. |
| * The floating point version of the degeneracy test implemented |
| * in _radial_pattern_is_degenerate() is: |
| * |
| * 1) The circles are practically the same size: |
| * |dr| < DBL_EPSILON |
| * AND |
| * 2a) The circles are both very small: |
| * min (r0, r1) < DBL_EPSILON |
| * OR |
| * 2b) The circles are very close to each other: |
| * max (|dx|, |dy|) < 2 * DBL_EPSILON |
| * |
| * Assuming that the gradient is not degenerate, we want to |
| * show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON. |
| * |
| * If the gradient is not degenerate yet it has |dr| < |
| * DBL_EPSILON, (2b) is false, thus: |
| * |
| * max (|dx|, |dy|) >= 2*DBL_EPSILON |
| * which implies: |
| * 4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2 |
| * |
| * From the definition of a, we get: |
| * a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2 |
| * dx^2 + dy^2 - DBL_EPSILON^2 < dr^2 |
| * 3*DBL_EPSILON^2 < dr^2 |
| * |
| * which is inconsistent with the hypotheses, thus |dr| < |
| * DBL_EPSILON is false or the gradient is degenerate. |
| */ |
| assert (fabs (dr) >= DBL_EPSILON); |
| |
| /* |
| * If a == 0, all the circles are tangent to a line in the |
| * focus point. If this line is within the box extents, we |
| * should add the circle with infinite radius, but this would |
| * make the range unbounded, so we add the smallest circle whose |
| * distance to the desired (degenerate) circle within the |
| * bounding box does not exceed tolerance. |
| * |
| * The equation of the line is b==0, i.e.: |
| * x*dx + y*dy + cr*dr == 0 |
| * |
| * We compute the intersection of the line with the box and |
| * keep the intersection with maximum square distance (maxd2) |
| * from the focus point. |
| * |
| * In the code the intersection is represented in another |
| * coordinate system, whose origin is the focus point and |
| * which has a u,v axes, which are respectively orthogonal and |
| * parallel to the edge being intersected. |
| * |
| * The intersection is valid only if it belongs to the box, |
| * otherwise it is ignored. |
| * |
| * For example: |
| * |
| * y = y0 |
| * x*dx + y0*dy + cr*dr == 0 |
| * x = -(y0*dy + cr*dr) / dx |
| * |
| * which in (u,v) is: |
| * u = y0 - y_focus |
| * v = -(y0*dy + cr*dr) / dx - x_focus |
| * |
| * In the code: |
| * u = (edge) - (u_origin) |
| * v = -((edge) * (delta) + cr*dr) / (den) - v_focus |
| */ |
| #define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin) \ |
| if (fabs (den) >= DBL_EPSILON) { \ |
| double v; \ |
| \ |
| v = -((edge) * (delta) + cr * dr) / (den); \ |
| if ((lower) <= v && v <= (upper)) { \ |
| double u, d2; \ |
| \ |
| u = (edge) - (u_origin); \ |
| v -= (v_origin); \ |
| d2 = u*u + v*v; \ |
| if (maxd2 < d2) \ |
| maxd2 = d2; \ |
| } \ |
| } |
| |
| maxd2 = 0; |
| |
| /* degenerate circles (lines) passing through each edge */ |
| T_EDGE (y0, dy, dx, minx, maxx, y_focus, x_focus); |
| T_EDGE (y1, dy, dx, minx, maxx, y_focus, x_focus); |
| T_EDGE (x0, dx, dy, miny, maxy, x_focus, y_focus); |
| T_EDGE (x1, dx, dy, miny, maxy, x_focus, y_focus); |
| |
| #undef T_EDGE |
| |
| /* |
| * The limit circle can be transformed rigidly to the y=0 line |
| * and the circles tangent to it in (0,0) are: |
| * |
| * x^2 + (y-r)^2 = r^2 <=> x^2 + y^2 - 2*y*r = 0 |
| * |
| * y is the distance from the line, in our case tolerance; |
| * x is the distance along the line, i.e. sqrt(maxd2), |
| * so: |
| * |
| * r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance) |
| * t = (r - cr) / dr = |
| * (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr) |
| */ |
| if (maxd2 > 0) { |
| double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr; |
| t_limit /= 2 * tolerance * dr; |
| valid = _extend_range (range, t_limit, valid); |
| } |
| |
| /* |
| * Nondegenerate, nonlimit circles passing through the corners. |
| * |
| * a == 0 && a*t^2 - 2*b*t + c == 0 |
| * |
| * t = c / (2*b) |
| * |
| * The b == 0 case has just been handled, so we only have to |
| * compute this if b != 0. |
| */ |
| #define T_CORNER(x,y) \ |
| b = (x) * dx + (y) * dy + cr * dr; \ |
| if (fabs (b) >= DBL_EPSILON) { \ |
| double t_corner; \ |
| double x2 = (x) * (x); \ |
| double y2 = (y) * (y); \ |
| double cr2 = (cr) * (cr); \ |
| double c = x2 + y2 - cr2; \ |
| \ |
| t_corner = 0.5 * c / b; \ |
| if (t_corner * dr >= mindr) \ |
| valid = _extend_range (range, t_corner, valid); \ |
| } |
| |
| /* circles touching each corner */ |
| T_CORNER (x0, y0); |
| T_CORNER (x0, y1); |
| T_CORNER (x1, y0); |
| T_CORNER (x1, y1); |
| |
| #undef T_CORNER |
| } else { |
| double inva, b, c, d; |
| |
| inva = 1 / a; |
| |
| /* |
| * Nondegenerate, nonlimit circles passing through the corners. |
| * |
| * a != 0 && a*t^2 - 2*b*t + c == 0 |
| * |
| * t = (b +- sqrt (b*b - a*c)) / a |
| * |
| * If the argument of sqrt() is negative, then no circle |
| * passes through the corner. |
| */ |
| #define T_CORNER(x,y) \ |
| b = (x) * dx + (y) * dy + cr * dr; \ |
| c = (x) * (x) + (y) * (y) - cr * cr; \ |
| d = b * b - a * c; \ |
| if (d >= 0) { \ |
| double t_corner; \ |
| \ |
| d = sqrt (d); \ |
| t_corner = (b + d) * inva; \ |
| if (t_corner * dr >= mindr) \ |
| valid = _extend_range (range, t_corner, valid); \ |
| t_corner = (b - d) * inva; \ |
| if (t_corner * dr >= mindr) \ |
| valid = _extend_range (range, t_corner, valid); \ |
| } |
| |
| /* circles touching each corner */ |
| T_CORNER (x0, y0); |
| T_CORNER (x0, y1); |
| T_CORNER (x1, y0); |
| T_CORNER (x1, y1); |
| |
| #undef T_CORNER |
| } |
| } |
| |
| /** |
| * _cairo_gradient_pattern_box_to_parameter |
| * |
| * Compute a interpolation range sufficient to draw (within the given |
| * tolerance) the gradient in the given box getting the same result as |
| * using the (-inf, +inf) range. |
| * |
| * Assumes that the pattern is not degenerate. This can be guaranteed |
| * by simplifying it to a solid clear if _cairo_pattern_is_clear or to |
| * a solid color if _cairo_gradient_pattern_is_solid. |
| * |
| * The range isn't guaranteed to be minimal, but it tries to. |
| **/ |
| void |
| _cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient, |
| double x0, double y0, |
| double x1, double y1, |
| double tolerance, |
| double out_range[2]) |
| { |
| assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || |
| gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); |
| |
| if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { |
| _cairo_linear_pattern_box_to_parameter ((cairo_linear_pattern_t *) gradient, |
| x0, y0, x1, y1, out_range); |
| } else { |
| _cairo_radial_pattern_box_to_parameter ((cairo_radial_pattern_t *) gradient, |
| x0, y0, x1, y1, tolerance, out_range); |
| } |
| } |
| |
| /** |
| * _cairo_gradient_pattern_interpolate |
| * |
| * Interpolate between the start and end objects of linear or radial |
| * gradients. The interpolated object is stored in out_circle, with |
| * the radius being zero in the linear gradient case. |
| **/ |
| void |
| _cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient, |
| double t, |
| cairo_circle_double_t *out_circle) |
| { |
| assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || |
| gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); |
| |
| #define lerp(a,b) (a)*(1-t) + (b)*t |
| |
| if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { |
| cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; |
| out_circle->center.x = lerp (linear->pd1.x, linear->pd2.x); |
| out_circle->center.y = lerp (linear->pd1.y, linear->pd2.y); |
| out_circle->radius = 0; |
| } else { |
| cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; |
| out_circle->center.x = lerp (radial->cd1.center.x, radial->cd2.center.x); |
| out_circle->center.y = lerp (radial->cd1.center.y, radial->cd2.center.y); |
| out_circle->radius = lerp (radial->cd1.radius , radial->cd2.radius); |
| } |
| |
| #undef lerp |
| } |
| |
| |
| /** |
| * _cairo_gradient_pattern_fit_to_range |
| * |
| * Scale the extremes of a gradient to guarantee that the coordinates |
| * and their deltas are within the range (-max_value, max_value). The |
| * new extremes are stored in out_circle. |
| * |
| * The pattern matrix is scaled to guarantee that the aspect of the |
| * gradient is the same and the result is stored in out_matrix. |
| * |
| **/ |
| void |
| _cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient, |
| double max_value, |
| cairo_matrix_t *out_matrix, |
| cairo_circle_double_t out_circle[2]) |
| { |
| double dim; |
| |
| assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || |
| gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); |
| |
| if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { |
| cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; |
| |
| out_circle[0].center = linear->pd1; |
| out_circle[0].radius = 0; |
| out_circle[1].center = linear->pd2; |
| out_circle[1].radius = 0; |
| |
| dim = fabs (linear->pd1.x); |
| dim = MAX (dim, fabs (linear->pd1.y)); |
| dim = MAX (dim, fabs (linear->pd2.x)); |
| dim = MAX (dim, fabs (linear->pd2.y)); |
| dim = MAX (dim, fabs (linear->pd1.x - linear->pd2.x)); |
| dim = MAX (dim, fabs (linear->pd1.y - linear->pd2.y)); |
| } else { |
| cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient; |
| |
| out_circle[0] = radial->cd1; |
| out_circle[1] = radial->cd2; |
| |
| dim = fabs (radial->cd1.center.x); |
| dim = MAX (dim, fabs (radial->cd1.center.y)); |
| dim = MAX (dim, fabs (radial->cd1.radius)); |
| dim = MAX (dim, fabs (radial->cd2.center.x)); |
| dim = MAX (dim, fabs (radial->cd2.center.y)); |
| dim = MAX (dim, fabs (radial->cd2.radius)); |
| dim = MAX (dim, fabs (radial->cd1.center.x - radial->cd2.center.x)); |
| dim = MAX (dim, fabs (radial->cd1.center.y - radial->cd2.center.y)); |
| dim = MAX (dim, fabs (radial->cd1.radius - radial->cd2.radius)); |
| } |
| |
| if (unlikely (dim > max_value)) { |
| cairo_matrix_t scale; |
| |
| dim = max_value / dim; |
| |
| out_circle[0].center.x *= dim; |
| out_circle[0].center.y *= dim; |
| out_circle[0].radius *= dim; |
| out_circle[1].center.x *= dim; |
| out_circle[1].center.y *= dim; |
| out_circle[1].radius *= dim; |
| |
| cairo_matrix_init_scale (&scale, dim, dim); |
| cairo_matrix_multiply (out_matrix, &gradient->base.matrix, &scale); |
| } else { |
| *out_matrix = gradient->base.matrix; |
| } |
| } |
| |
| static cairo_bool_t |
| _gradient_is_clear (const cairo_gradient_pattern_t *gradient, |
| const cairo_rectangle_int_t *extents) |
| { |
| unsigned int i; |
| |
| assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || |
| gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); |
| |
| if (gradient->n_stops == 0 || |
| (gradient->base.extend == CAIRO_EXTEND_NONE && |
| gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) |
| return TRUE; |
| |
| if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) { |
| /* degenerate radial gradients are clear */ |
| if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient)) |
| return TRUE; |
| } else if (gradient->base.extend == CAIRO_EXTEND_NONE) { |
| /* EXTEND_NONE degenerate linear gradients are clear */ |
| if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient)) |
| return TRUE; |
| } |
| |
| /* Check if the extents intersect the drawn part of the pattern. */ |
| if (extents != NULL && |
| (gradient->base.extend == CAIRO_EXTEND_NONE || |
| gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL)) |
| { |
| double t[2]; |
| |
| _cairo_gradient_pattern_box_to_parameter (gradient, |
| extents->x, |
| extents->y, |
| extents->x + extents->width, |
| extents->y + extents->height, |
| DBL_EPSILON, |
| t); |
| |
| if (gradient->base.extend == CAIRO_EXTEND_NONE && |
| (t[0] >= gradient->stops[gradient->n_stops - 1].offset || |
| t[1] <= gradient->stops[0].offset)) |
| { |
| return TRUE; |
| } |
| |
| if (t[0] == t[1]) |
| return TRUE; |
| } |
| |
| for (i = 0; i < gradient->n_stops; i++) |
| if (! CAIRO_COLOR_IS_CLEAR (&gradient->stops[i].color)) |
| return FALSE; |
| |
| return TRUE; |
| } |
| |
| static void |
| _gradient_color_average (const cairo_gradient_pattern_t *gradient, |
| cairo_color_t *color) |
| { |
| double delta0, delta1; |
| double r, g, b, a; |
| unsigned int i, start = 1, end; |
| |
| assert (gradient->n_stops > 0); |
| assert (gradient->base.extend != CAIRO_EXTEND_NONE); |
| |
| if (gradient->n_stops == 1) { |
| _cairo_color_init_rgba (color, |
| gradient->stops[0].color.red, |
| gradient->stops[0].color.green, |
| gradient->stops[0].color.blue, |
| gradient->stops[0].color.alpha); |
| return; |
| } |
| |
| end = gradient->n_stops - 1; |
| |
| switch (gradient->base.extend) { |
| case CAIRO_EXTEND_REPEAT: |
| /* |
| * Sa, Sb and Sy, Sz are the first two and last two stops respectively. |
| * The weight of the first and last stop can be computed as the area of |
| * the following triangles (taken with height 1, since the whole [0-1] |
| * will have total weight 1 this way): b*h/2 |
| * |
| * + + |
| * / |\ / | \ |
| * / | \ / | \ |
| * / | \ / | \ |
| * ~~~~~+---+---+---+~~~~~~~+-------+---+---+~~~~~ |
| * -1+Sz 0 Sa Sb Sy Sz 1 1+Sa |
| * |
| * For the first stop: (Sb-(-1+Sz)/2 = (1+Sb-Sz)/2 |
| * For the last stop: ((1+Sa)-Sy)/2 = (1+Sa-Sy)/2 |
| * Halving the result is done after summing up all the areas. |
| */ |
| delta0 = 1.0 + gradient->stops[1].offset - gradient->stops[end].offset; |
| delta1 = 1.0 + gradient->stops[0].offset - gradient->stops[end-1].offset; |
| break; |
| |
| case CAIRO_EXTEND_REFLECT: |
| /* |
| * Sa, Sb and Sy, Sz are the first two and last two stops respectively. |
| * The weight of the first and last stop can be computed as the area of |
| * the following trapezoids (taken with height 1, since the whole [0-1] |
| * will have total weight 1 this way): (b+B)*h/2 |
| * |
| * +-------+ +---+ |
| * | |\ / | | |
| * | | \ / | | |
| * | | \ / | | |
| * +-------+---+~~~~~~~+-------+---+ |
| * 0 Sa Sb Sy Sz 1 |
| * |
| * For the first stop: (Sa+Sb)/2 |
| * For the last stop: ((1-Sz) + (1-Sy))/2 = (2-Sy-Sz)/2 |
| * Halving the result is done after summing up all the areas. |
| */ |
| delta0 = gradient->stops[0].offset + gradient->stops[1].offset; |
| delta1 = 2.0 - gradient->stops[end-1].offset - gradient->stops[end].offset; |
| break; |
| |
| case CAIRO_EXTEND_PAD: |
| /* PAD is computed as the average of the first and last stop: |
| * - take both of them with weight 1 (they will be halved |
| * after the whole sum has been computed). |
| * - avoid summing any of the inner stops. |
| */ |
| delta0 = delta1 = 1.0; |
| start = end; |
| break; |
| |
| case CAIRO_EXTEND_NONE: |
| default: |
| ASSERT_NOT_REACHED; |
| _cairo_color_init_rgba (color, 0, 0, 0, 0); |
| return; |
| } |
| |
| r = delta0 * gradient->stops[0].color.red; |
| g = delta0 * gradient->stops[0].color.green; |
| b = delta0 * gradient->stops[0].color.blue; |
| a = delta0 * gradient->stops[0].color.alpha; |
| |
| for (i = start; i < end; ++i) { |
| /* Inner stops weight is the same as the area of the triangle they influence |
| * (which goes from the stop before to the stop after), again with height 1 |
| * since the whole must sum up to 1: b*h/2 |
| * Halving is done after the whole sum has been computed. |
| */ |
| double delta = gradient->stops[i+1].offset - gradient->stops[i-1].offset; |
| r += delta * gradient->stops[i].color.red; |
| g += delta * gradient->stops[i].color.green; |
| b += delta * gradient->stops[i].color.blue; |
| a += delta * gradient->stops[i].color.alpha; |
| } |
| |
| r += delta1 * gradient->stops[end].color.red; |
| g += delta1 * gradient->stops[end].color.green; |
| b += delta1 * gradient->stops[end].color.blue; |
| a += delta1 * gradient->stops[end].color.alpha; |
| |
| _cairo_color_init_rgba (color, r * .5, g * .5, b * .5, a * .5); |
| } |
| |
| /** |
| * _cairo_pattern_alpha_range |
| * |
| * Convenience function to determine the minimum and maximum alpha in |
| * the drawn part of a pattern (i.e. ignoring clear parts caused by |
| * extend modes and/or pattern shape). |
| * |
| * If not NULL, out_min and out_max will be set respectively to the |
| * minimum and maximum alpha value of the pattern. |
| **/ |
| void |
| _cairo_pattern_alpha_range (const cairo_pattern_t *pattern, |
| double *out_min, |
| double *out_max) |
| { |
| double alpha_min, alpha_max; |
| |
| switch (pattern->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: { |
| const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern; |
| alpha_min = alpha_max = solid->color.alpha; |
| break; |
| } |
| |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| case CAIRO_PATTERN_TYPE_RADIAL: { |
| const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; |
| unsigned int i; |
| |
| assert (gradient->n_stops >= 1); |
| |
| alpha_min = alpha_max = gradient->stops[0].color.alpha; |
| for (i = 1; i < gradient->n_stops; i++) { |
| if (alpha_min > gradient->stops[i].color.alpha) |
| alpha_min = gradient->stops[i].color.alpha; |
| else if (alpha_max < gradient->stops[i].color.alpha) |
| alpha_max = gradient->stops[i].color.alpha; |
| } |
| |
| break; |
| } |
| |
| case CAIRO_PATTERN_TYPE_MESH: { |
| const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern; |
| const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0); |
| unsigned int i, j, n = _cairo_array_num_elements (&mesh->patches); |
| |
| assert (n >= 1); |
| |
| alpha_min = alpha_max = patch[0].colors[0].alpha; |
| for (i = 0; i < n; i++) { |
| for (j = 0; j < 4; j++) { |
| if (patch[i].colors[j].alpha < alpha_min) |
| alpha_min = patch[i].colors[j].alpha; |
| else if (patch[i].colors[j].alpha > alpha_max) |
| alpha_max = patch[i].colors[j].alpha; |
| } |
| } |
| |
| break; |
| } |
| |
| default: |
| ASSERT_NOT_REACHED; |
| /* fall through */ |
| |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| alpha_min = 0; |
| alpha_max = 1; |
| break; |
| } |
| |
| if (out_min) |
| *out_min = alpha_min; |
| if (out_max) |
| *out_max = alpha_max; |
| } |
| |
| /** |
| * _cairo_mesh_pattern_coord_box |
| * |
| * Convenience function to determine the range of the coordinates of |
| * the points used to define the patches of the mesh. |
| * |
| * This is guaranteed to contain the pattern extents, but might not be |
| * tight, just like a Bezier curve is always inside the convex hull of |
| * the control points. |
| * |
| * This function cannot be used while the mesh is being constructed. |
| * |
| * The function returns TRUE and sets the output parametes to define |
| * the coodrinate range if the mesh pattern contains at least one |
| * patch, otherwise it returns FALSE. |
| **/ |
| cairo_bool_t |
| _cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh, |
| double *out_xmin, |
| double *out_ymin, |
| double *out_xmax, |
| double *out_ymax) |
| { |
| const cairo_mesh_patch_t *patch; |
| unsigned int num_patches, i, j, k; |
| double x0, y0, x1, y1; |
| |
| assert (mesh->current_patch == NULL); |
| |
| num_patches = _cairo_array_num_elements (&mesh->patches); |
| |
| if (num_patches == 0) |
| return FALSE; |
| |
| patch = _cairo_array_index_const (&mesh->patches, 0); |
| x0 = x1 = patch->points[0][0].x; |
| y0 = y1 = patch->points[0][0].y; |
| |
| for (i = 0; i < num_patches; i++) { |
| for (j = 0; j < 4; j++) { |
| for (k = 0; k < 4; k++) { |
| x0 = MIN (x0, patch[i].points[j][k].x); |
| y0 = MIN (y0, patch[i].points[j][k].y); |
| x1 = MAX (x1, patch[i].points[j][k].x); |
| y1 = MAX (y1, patch[i].points[j][k].y); |
| } |
| } |
| } |
| |
| *out_xmin = x0; |
| *out_ymin = y0; |
| *out_xmax = x1; |
| *out_ymax = y1; |
| |
| return TRUE; |
| } |
| |
| /** |
| * _cairo_gradient_pattern_is_solid |
| * |
| * Convenience function to determine whether a gradient pattern is |
| * a solid color within the given extents. In this case the color |
| * argument is initialized to the color the pattern represents. |
| * This functions doesn't handle completely transparent gradients, |
| * thus it should be called only after _cairo_pattern_is_clear has |
| * returned FALSE. |
| * |
| * Return value: %TRUE if the pattern is a solid color. |
| **/ |
| cairo_bool_t |
| _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, |
| const cairo_rectangle_int_t *extents, |
| cairo_color_t *color) |
| { |
| unsigned int i; |
| |
| assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || |
| gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); |
| |
| /* TODO: radial */ |
| if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { |
| cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; |
| if (_linear_pattern_is_degenerate (linear)) { |
| _gradient_color_average (gradient, color); |
| return TRUE; |
| } |
| |
| if (gradient->base.extend == CAIRO_EXTEND_NONE) { |
| double t[2]; |
| |
| /* We already know that the pattern is not clear, thus if some |
| * part of it is clear, the whole is not solid. |
| */ |
| |
| if (extents == NULL) |
| return FALSE; |
| |
| _cairo_linear_pattern_box_to_parameter (linear, |
| extents->x, |
| extents->y, |
| extents->x + extents->width, |
| extents->y + extents->height, |
| t); |
| |
| if (t[0] < 0.0 || t[1] > 1.0) |
| return FALSE; |
| } |
| } else |
| return FALSE; |
| |
| for (i = 1; i < gradient->n_stops; i++) |
| if (! _cairo_color_stop_equal (&gradient->stops[0].color, |
| &gradient->stops[i].color)) |
| return FALSE; |
| |
| _cairo_color_init_rgba (color, |
| gradient->stops[0].color.red, |
| gradient->stops[0].color.green, |
| gradient->stops[0].color.blue, |
| gradient->stops[0].color.alpha); |
| |
| return TRUE; |
| } |
| |
| static cairo_bool_t |
| _mesh_is_clear (const cairo_mesh_pattern_t *mesh) |
| { |
| double x1, y1, x2, y2; |
| cairo_bool_t is_valid; |
| |
| is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2); |
| if (!is_valid) |
| return TRUE; |
| |
| if (x2 - x1 < DBL_EPSILON || y2 - y1 < DBL_EPSILON) |
| return TRUE; |
| |
| return FALSE; |
| } |
| |
| /** |
| * _cairo_pattern_is_opaque_solid |
| * |
| * 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. |
| * |
| * Return value: %TRUE if the pattern is an opaque, solid color. |
| **/ |
| cairo_bool_t |
| _cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern) |
| { |
| cairo_solid_pattern_t *solid; |
| |
| if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) |
| return FALSE; |
| |
| solid = (cairo_solid_pattern_t *) pattern; |
| |
| return CAIRO_COLOR_IS_OPAQUE (&solid->color); |
| } |
| |
| static cairo_bool_t |
| _surface_is_opaque (const cairo_surface_pattern_t *pattern, |
| const cairo_rectangle_int_t *r) |
| { |
| if (pattern->surface->content & CAIRO_CONTENT_ALPHA) |
| return FALSE; |
| |
| if (pattern->base.extend != CAIRO_EXTEND_NONE) |
| return TRUE; |
| |
| if (r != NULL) { |
| cairo_rectangle_int_t extents; |
| |
| if (! _cairo_surface_get_extents (pattern->surface, &extents)) |
| return TRUE; |
| |
| if (r->x >= extents.x && |
| r->y >= extents.y && |
| r->x + r->width <= extents.x + extents.width && |
| r->y + r->height <= extents.y + extents.height) |
| { |
| return TRUE; |
| } |
| } |
| |
| return FALSE; |
| } |
| |
| static cairo_bool_t |
| _surface_is_clear (const cairo_surface_pattern_t *pattern) |
| { |
| cairo_rectangle_int_t extents; |
| |
| if (_cairo_surface_get_extents (pattern->surface, &extents) && |
| (extents.width == 0 || extents.height == 0)) |
| return TRUE; |
| |
| return pattern->surface->is_clear && |
| pattern->surface->content & CAIRO_CONTENT_ALPHA; |
| } |
| |
| static cairo_bool_t |
| _gradient_is_opaque (const cairo_gradient_pattern_t *gradient, |
| const cairo_rectangle_int_t *extents) |
| { |
| unsigned int i; |
| |
| assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR || |
| gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL); |
| |
| if (gradient->n_stops == 0 || |
| (gradient->base.extend == CAIRO_EXTEND_NONE && |
| gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset)) |
| return FALSE; |
| |
| if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) { |
| if (gradient->base.extend == CAIRO_EXTEND_NONE) { |
| double t[2]; |
| cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient; |
| |
| /* EXTEND_NONE degenerate radial gradients are clear */ |
| if (_linear_pattern_is_degenerate (linear)) |
| return FALSE; |
| |
| if (extents == NULL) |
| return FALSE; |
| |
| _cairo_linear_pattern_box_to_parameter (linear, |
| extents->x, |
| extents->y, |
| extents->x + extents->width, |
| extents->y + extents->height, |
| t); |
| |
| if (t[0] < 0.0 || t[1] > 1.0) |
| return FALSE; |
| } |
| } else |
| return FALSE; /* TODO: check actual intersection */ |
| |
| for (i = 0; i < gradient->n_stops; i++) |
| if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color)) |
| 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_rectangle_int_t *extents) |
| { |
| const cairo_pattern_union_t *pattern; |
| |
| if (abstract_pattern->has_component_alpha) |
| return FALSE; |
| |
| 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 _surface_is_opaque (&pattern->surface, extents); |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| return _gradient_is_opaque (&pattern->gradient.base, extents); |
| case CAIRO_PATTERN_TYPE_MESH: |
| return FALSE; |
| } |
| |
| ASSERT_NOT_REACHED; |
| return FALSE; |
| } |
| |
| cairo_bool_t |
| _cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern) |
| { |
| const cairo_pattern_union_t *pattern; |
| |
| if (abstract_pattern->has_component_alpha) |
| return FALSE; |
| |
| pattern = (cairo_pattern_union_t *) abstract_pattern; |
| switch (pattern->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| return CAIRO_COLOR_IS_CLEAR (&pattern->solid.color); |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| return _surface_is_clear (&pattern->surface); |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| return _gradient_is_clear (&pattern->gradient.base, NULL); |
| case CAIRO_PATTERN_TYPE_MESH: |
| return _mesh_is_clear (&pattern->mesh); |
| } |
| |
| ASSERT_NOT_REACHED; |
| return FALSE; |
| } |
| |
| /** |
| * _cairo_pattern_analyze_filter: |
| * @pattern: surface pattern |
| * @pad_out: location to store necessary padding in the source image, or %NULL |
| * Returns: the optimized #cairo_filter_t to use with @pattern. |
| * |
| * Analyze the filter to determine how much extra needs to be sampled |
| * from the source image to account for the filter radius and whether |
| * we can optimize the filter to a simpler value. |
| * |
| * XXX: We don't actually have any way of querying the backend for |
| * the filter radius, so we just guess base on what we know that |
| * backends do currently (see bug #10508) |
| */ |
| cairo_filter_t |
| _cairo_pattern_analyze_filter (const cairo_pattern_t *pattern, |
| double *pad_out) |
| { |
| double pad; |
| cairo_filter_t optimized_filter; |
| |
| switch (pattern->filter) { |
| case CAIRO_FILTER_GOOD: |
| case CAIRO_FILTER_BEST: |
| case CAIRO_FILTER_BILINEAR: |
| /* If source pixels map 1:1 onto destination pixels, we do |
| * not need to filter (and do not want to filter, since it |
| * will cause blurriness) |
| */ |
| if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) { |
| pad = 0.; |
| optimized_filter = CAIRO_FILTER_NEAREST; |
| } else { |
| /* 0.5 is enough for a bilinear filter. It's possible we |
| * should defensively use more for CAIRO_FILTER_BEST, but |
| * without a single example, it's hard to know how much |
| * more would be defensive... |
| */ |
| pad = 0.5; |
| optimized_filter = pattern->filter; |
| } |
| break; |
| |
| case CAIRO_FILTER_FAST: |
| case CAIRO_FILTER_NEAREST: |
| case CAIRO_FILTER_GAUSSIAN: |
| default: |
| pad = 0.; |
| optimized_filter = pattern->filter; |
| break; |
| } |
| |
| if (pad_out) |
| *pad_out = pad; |
| |
| return optimized_filter; |
| } |
| |
| |
| static double |
| _pixman_nearest_sample (double d) |
| { |
| return ceil (d - .5); |
| } |
| |
| static cairo_int_status_t |
| _cairo_pattern_acquire_surface_for_surface (const cairo_surface_pattern_t *pattern, |
| cairo_surface_t *dst, |
| int x, |
| int y, |
| unsigned int width, |
| unsigned int height, |
| unsigned int flags, |
| cairo_surface_t **out, |
| cairo_surface_attributes_t *attr) |
| { |
| cairo_surface_t *surface; |
| cairo_rectangle_int_t extents; |
| cairo_rectangle_int_t sampled_area; |
| double x1, y1, x2, y2; |
| int tx, ty; |
| double pad; |
| cairo_bool_t is_identity; |
| cairo_bool_t is_empty; |
| cairo_bool_t is_bounded; |
| cairo_int_status_t status; |
| |
| surface = cairo_surface_reference (pattern->surface); |
| |
| is_identity = FALSE; |
| attr->matrix = pattern->base.matrix; |
| attr->extend = pattern->base.extend; |
| attr->filter = _cairo_pattern_analyze_filter (&pattern->base, &pad); |
| attr->has_component_alpha = pattern->base.has_component_alpha; |
| |
| attr->x_offset = attr->y_offset = tx = ty = 0; |
| if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { |
| cairo_matrix_init_identity (&attr->matrix); |
| attr->x_offset = tx; |
| attr->y_offset = ty; |
| is_identity = TRUE; |
| } else if (attr->filter == CAIRO_FILTER_NEAREST) { |
| /* |
| * For NEAREST, we can remove the fractional translation component |
| * from the transformation - this ensures that the pattern will always |
| * hit fast-paths in the backends for simple transformations that |
| * become (almost) identity, without loss of quality. |
| */ |
| attr->matrix.x0 = 0; |
| attr->matrix.y0 = 0; |
| if (_cairo_matrix_is_pixel_exact (&attr->matrix)) { |
| /* The rounding here is rather peculiar as it needs to match the |
| * rounding performed on the sample coordinate used by pixman. |
| */ |
| attr->matrix.x0 = _pixman_nearest_sample (pattern->base.matrix.x0); |
| attr->matrix.y0 = _pixman_nearest_sample (pattern->base.matrix.y0); |
| } else { |
| attr->matrix.x0 = pattern->base.matrix.x0; |
| attr->matrix.y0 = pattern->base.matrix.y0; |
| } |
| |
| if (_cairo_matrix_is_integer_translation (&attr->matrix, &tx, &ty)) { |
| cairo_matrix_init_identity (&attr->matrix); |
| attr->x_offset = tx; |
| attr->y_offset = ty; |
| is_identity = TRUE; |
| } |
| } |
| |
| /* 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. |
| */ |
| if (flags & CAIRO_PATTERN_ACQUIRE_NO_REFLECT && |
| attr->extend == CAIRO_EXTEND_REFLECT) |
| { |
| cairo_t *cr; |
| cairo_surface_t *src; |
| int w, h; |
| |
| is_bounded = _cairo_surface_get_extents (surface, &extents); |
| assert (is_bounded); |
| |
| status = _cairo_surface_clone_similar (dst, surface, |
| extents.x, extents.y, |
| extents.width, extents.height, |
| &extents.x, &extents.y, &src); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| w = 2 * extents.width; |
| h = 2 * extents.height; |
| |
| if (is_identity) { |
| attr->x_offset = -x; |
| x += tx; |
| while (x <= -w) |
| x += w; |
| while (x >= w) |
| x -= w; |
| extents.x += x; |
| tx = x = 0; |
| |
| attr->y_offset = -y; |
| y += ty; |
| while (y <= -h) |
| y += h; |
| while (y >= h) |
| y -= h; |
| extents.y += y; |
| ty = y = 0; |
| } |
| |
| cairo_surface_destroy (surface); |
| surface = _cairo_surface_create_similar_solid (dst, |
| dst->content, w, h, |
| CAIRO_COLOR_TRANSPARENT, |
| FALSE); |
| if (surface == NULL) |
| return CAIRO_INT_STATUS_UNSUPPORTED; |
| if (unlikely (surface->status)) { |
| cairo_surface_destroy (src); |
| return surface->status; |
| } |
| |
| surface->device_transform = pattern->surface->device_transform; |
| surface->device_transform_inverse = pattern->surface->device_transform_inverse; |
| |
| cr = cairo_create (surface); |
| |
| cairo_set_source_surface (cr, src, -extents.x, -extents.y); |
| cairo_paint (cr); |
| |
| cairo_scale (cr, -1, +1); |
| cairo_set_source_surface (cr, src, extents.x-w, -extents.y); |
| cairo_paint (cr); |
| cairo_set_source_surface (cr, src, extents.x, -extents.y); |
| cairo_paint (cr); |
| |
| cairo_scale (cr, +1, -1); |
| cairo_set_source_surface (cr, src, extents.x-w, extents.y-h); |
| cairo_paint (cr); |
| cairo_set_source_surface (cr, src, extents.x, extents.y-h); |
| cairo_paint (cr); |
| cairo_set_source_surface (cr, src, extents.x-w, extents.y); |
| cairo_paint (cr); |
| cairo_set_source_surface (cr, src, extents.x, extents.y); |
| cairo_paint (cr); |
| |
| cairo_scale (cr, -1, +1); |
| cairo_set_source_surface (cr, src, -extents.x, extents.y-h); |
| cairo_paint (cr); |
| cairo_set_source_surface (cr, src, -extents.x, extents.y); |
| cairo_paint (cr); |
| |
| status = cairo_status (cr); |
| cairo_destroy (cr); |
| |
| cairo_surface_destroy (src); |
| |
| if (unlikely (status)) |
| goto BAIL; |
| |
| attr->extend = CAIRO_EXTEND_REPEAT; |
| } |
| |
| /* We first transform the rectangle to the coordinate space of the |
| * source surface so that we only need to clone that portion of the |
| * surface that will be read. |
| */ |
| x1 = x; |
| y1 = y; |
| x2 = x + (int) width; |
| y2 = y + (int) height; |
| if (! is_identity) { |
| _cairo_matrix_transform_bounding_box (&attr->matrix, |
| &x1, &y1, &x2, &y2, |
| NULL); |
| } |
| |
| sampled_area.x = floor (x1 - pad); |
| sampled_area.y = floor (y1 - pad); |
| sampled_area.width = ceil (x2 + pad) - sampled_area.x; |
| sampled_area.height = ceil (y2 + pad) - sampled_area.y; |
| |
| sampled_area.x += tx; |
| sampled_area.y += ty; |
| |
| if ( _cairo_surface_get_extents (surface, &extents)) { |
| if (attr->extend == CAIRO_EXTEND_NONE) { |
| /* Never acquire a larger area than the source itself */ |
| is_empty = _cairo_rectangle_intersect (&extents, &sampled_area); |
| } else { |
| int trim = 0; |
| |
| if (sampled_area.x >= extents.x && |
| sampled_area.x + (int) sampled_area.width <= extents.x + (int) extents.width) |
| { |
| /* source is horizontally contained within extents, trim */ |
| extents.x = sampled_area.x; |
| extents.width = sampled_area.width; |
| trim |= 0x1; |
| } |
| |
| if (sampled_area.y >= extents.y && |
| sampled_area.y + (int) sampled_area.height <= extents.y + (int) extents.height) |
| { |
| /* source is vertically contained within extents, trim */ |
| extents.y = sampled_area.y; |
| extents.height = sampled_area.height; |
| trim |= 0x2; |
| } |
| |
| if (trim == 0x3) { |
| /* source is wholly contained within extents, drop the REPEAT */ |
| attr->extend = CAIRO_EXTEND_NONE; |
| } |
| |
| is_empty = extents.width == 0 || extents.height == 0; |
| } |
| } |
| |
| /* XXX can we use is_empty? */ |
| |
| status = _cairo_surface_clone_similar (dst, surface, |
| extents.x, extents.y, |
| extents.width, extents.height, |
| &x, &y, out); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| if (x != 0 || y != 0) { |
| if (is_identity) { |
| attr->x_offset -= x; |
| attr->y_offset -= y; |
| } else { |
| cairo_matrix_t m; |
| |
| x -= attr->x_offset; |
| y -= attr->y_offset; |
| attr->x_offset = 0; |
| attr->y_offset = 0; |
| |
| cairo_matrix_init_translate (&m, -x, -y); |
| cairo_matrix_multiply (&attr->matrix, &attr->matrix, &m); |
| } |
| } |
| |
| /* reduce likelihood of range overflow with large downscaling */ |
| if (! is_identity) { |
| cairo_matrix_t m; |
| cairo_status_t invert_status; |
| |
| m = attr->matrix; |
| invert_status = cairo_matrix_invert (&m); |
| assert (invert_status == CAIRO_STATUS_SUCCESS); |
| |
| if (m.x0 != 0. || m.y0 != 0.) { |
| /* pixman also limits the [xy]_offset to 16 bits so evenly |
| * spread the bits between the two. |
| */ |
| x = floor (m.x0 / 2); |
| y = floor (m.y0 / 2); |
| attr->x_offset -= x; |
| attr->y_offset -= y; |
| cairo_matrix_init_translate (&m, x, y); |
| cairo_matrix_multiply (&attr->matrix, &m, &attr->matrix); |
| } |
| } |
| |
| BAIL: |
| cairo_surface_destroy (surface); |
| 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. |
| * |
| * Note that this function is only suitable for use when the destination |
| * surface is pixel based and 1 device unit maps to one pixel. |
| * |
| * Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out. |
| **/ |
| cairo_int_status_t |
| _cairo_pattern_acquire_surface (const cairo_pattern_t *pattern, |
| cairo_surface_t *dst, |
| int x, |
| int y, |
| unsigned int width, |
| unsigned int height, |
| unsigned int flags, |
| cairo_surface_t **surface_out, |
| cairo_surface_attributes_t *attributes) |
| { |
| if (unlikely (pattern->status)) { |
| *surface_out = NULL; |
| return pattern->status; |
| } |
| |
| switch (pattern->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| return _cairo_pattern_acquire_surface_for_solid ((cairo_solid_pattern_t *) pattern, |
| dst, x, y, width, height, |
| surface_out, |
| attributes); |
| |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| return _cairo_pattern_acquire_surface_for_gradient ((cairo_gradient_pattern_t *) pattern, |
| dst, x, y, width, height, |
| surface_out, |
| attributes); |
| |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| return _cairo_pattern_acquire_surface_for_surface ((cairo_surface_pattern_t *) pattern, |
| dst, x, y, width, height, |
| flags, |
| surface_out, |
| attributes); |
| |
| case CAIRO_PATTERN_TYPE_MESH: |
| return _cairo_pattern_acquire_surface_for_mesh ((cairo_mesh_pattern_t *) pattern, |
| dst, x, y, width, height, |
| surface_out, |
| attributes); |
| |
| default: |
| ASSERT_NOT_REACHED; |
| return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| } |
| } |
| |
| /** |
| * _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 (const cairo_pattern_t *pattern, |
| cairo_surface_t *surface, |
| cairo_surface_attributes_t *attributes) |
| { |
| cairo_surface_destroy (surface); |
| } |
| |
| cairo_int_status_t |
| _cairo_pattern_acquire_surfaces (const cairo_pattern_t *src, |
| const 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, |
| unsigned int flags, |
| 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; |
| |
| if (unlikely (src->status)) |
| return src->status; |
| if (unlikely (mask != NULL && mask->status)) |
| return mask->status; |
| |
| /* If src and mask are both solid, then the mask alpha can be |
| * combined into src and mask can be ignored. */ |
| |
| if (src->type == CAIRO_PATTERN_TYPE_SOLID && |
| mask && |
| ! mask->has_component_alpha && |
| mask->type == CAIRO_PATTERN_TYPE_SOLID) |
| { |
| 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); |
| |
| src = &src_tmp.base; |
| mask = NULL; |
| } |
| |
| status = _cairo_pattern_acquire_surface (src, dst, |
| src_x, src_y, |
| width, height, |
| flags, |
| src_out, src_attributes); |
| if (unlikely (status)) |
| goto BAIL; |
| |
| if (mask == NULL) { |
| *mask_out = NULL; |
| goto BAIL; |
| } |
| |
| status = _cairo_pattern_acquire_surface (mask, dst, |
| mask_x, mask_y, |
| width, height, |
| flags, |
| mask_out, mask_attributes); |
| if (unlikely (status)) |
| _cairo_pattern_release_surface (src, *src_out, src_attributes); |
| |
| BAIL: |
| if (src == &src_tmp.base) |
| _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. |
| **/ |
| void |
| _cairo_pattern_get_extents (const cairo_pattern_t *pattern, |
| cairo_rectangle_int_t *extents) |
| { |
| double x1, y1, x2, y2; |
| cairo_status_t status; |
| |
| switch (pattern->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| goto UNBOUNDED; |
| |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| { |
| cairo_rectangle_int_t surface_extents; |
| const cairo_surface_pattern_t *surface_pattern = |
| (const cairo_surface_pattern_t *) pattern; |
| cairo_surface_t *surface = surface_pattern->surface; |
| double pad; |
| |
| if (! _cairo_surface_get_extents (surface, &surface_extents)) |
| goto UNBOUNDED; |
| |
| if (surface_extents.width == 0 || surface_extents.height == 0) |
| goto EMPTY; |
| |
| if (pattern->extend != CAIRO_EXTEND_NONE) |
| goto UNBOUNDED; |
| |
| /* The filter can effectively enlarge the extents of the |
| * pattern, so extend as necessary. |
| */ |
| _cairo_pattern_analyze_filter (&surface_pattern->base, &pad); |
| x1 = surface_extents.x - pad; |
| y1 = surface_extents.y - pad; |
| x2 = surface_extents.x + (int) surface_extents.width + pad; |
| y2 = surface_extents.y + (int) surface_extents.height + pad; |
| } |
| break; |
| |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| { |
| const cairo_radial_pattern_t *radial = |
| (const cairo_radial_pattern_t *) pattern; |
| double cx1, cy1; |
| double cx2, cy2; |
| double r1, r2; |
| |
| if (_radial_pattern_is_degenerate (radial)) { |
| /* cairo-gstate should have optimised degenerate |
| * patterns to solid clear patterns, so we can ignore |
| * them here. */ |
| goto EMPTY; |
| } |
| |
| /* TODO: in some cases (focus outside/on the circle) it is |
| * half-bounded. */ |
| if (pattern->extend != CAIRO_EXTEND_NONE) |
| goto UNBOUNDED; |
| |
| cx1 = radial->cd1.center.x; |
| cy1 = radial->cd1.center.y; |
| r1 = radial->cd1.radius; |
| |
| cx2 = radial->cd2.center.x; |
| cy2 = radial->cd2.center.y; |
| r2 = radial->cd2.radius; |
| |
| x1 = MIN (cx1 - r1, cx2 - r2); |
| y1 = MIN (cy1 - r1, cy2 - r2); |
| x2 = MAX (cx1 + r1, cx2 + r2); |
| y2 = MAX (cy1 + r1, cy2 + r2); |
| } |
| break; |
| |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| { |
| const cairo_linear_pattern_t *linear = |
| (const cairo_linear_pattern_t *) pattern; |
| |
| if (pattern->extend != CAIRO_EXTEND_NONE) |
| goto UNBOUNDED; |
| |
| if (_linear_pattern_is_degenerate (linear)) { |
| /* cairo-gstate should have optimised degenerate |
| * patterns to solid ones, so we can again ignore |
| * them here. */ |
| goto EMPTY; |
| } |
| |
| /* TODO: to get tight extents, use the matrix to transform |
| * the pattern instead of transforming the extents later. */ |
| if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.) |
| goto UNBOUNDED; |
| |
| if (linear->pd1.x == linear->pd2.x) { |
| x1 = -HUGE_VAL; |
| x2 = HUGE_VAL; |
| y1 = MIN (linear->pd1.y, linear->pd2.y); |
| y2 = MAX (linear->pd1.y, linear->pd2.y); |
| } else if (linear->pd1.y == linear->pd2.y) { |
| x1 = MIN (linear->pd1.x, linear->pd2.x); |
| x2 = MAX (linear->pd1.x, linear->pd2.x); |
| y1 = -HUGE_VAL; |
| y2 = HUGE_VAL; |
| } else { |
| goto UNBOUNDED; |
| } |
| } |
| break; |
| |
| case CAIRO_PATTERN_TYPE_MESH: |
| { |
| const cairo_mesh_pattern_t *mesh = |
| (const cairo_mesh_pattern_t *) pattern; |
| double padx, pady; |
| cairo_bool_t is_valid; |
| |
| is_valid = _cairo_mesh_pattern_coord_box (mesh, &x1, &y1, &x2, &y2); |
| if (!is_valid) |
| goto EMPTY; |
| |
| padx = pady = 1.; |
| cairo_matrix_transform_distance (&pattern->matrix, &padx, &pady); |
| padx = fabs (padx); |
| pady = fabs (pady); |
| |
| x1 -= padx; |
| y1 -= pady; |
| x2 += padx; |
| y2 += pady; |
| } |
| break; |
| |
| default: |
| ASSERT_NOT_REACHED; |
| } |
| |
| if (_cairo_matrix_is_translation (&pattern->matrix)) { |
| x1 -= pattern->matrix.x0; x2 -= pattern->matrix.x0; |
| y1 -= pattern->matrix.y0; y2 -= pattern->matrix.y0; |
| } else { |
| cairo_matrix_t imatrix; |
| |
| imatrix = pattern->matrix; |
| status = cairo_matrix_invert (&imatrix); |
| /* cairo_pattern_set_matrix ensures the matrix is invertible */ |
| assert (status == CAIRO_STATUS_SUCCESS); |
| |
| _cairo_matrix_transform_bounding_box (&imatrix, |
| &x1, &y1, &x2, &y2, |
| NULL); |
| } |
| |
| x1 = floor (x1); |
| if (x1 < CAIRO_RECT_INT_MIN) |
| x1 = CAIRO_RECT_INT_MIN; |
| y1 = floor (y1); |
| if (y1 < CAIRO_RECT_INT_MIN) |
| y1 = CAIRO_RECT_INT_MIN; |
| |
| x2 = ceil (x2); |
| if (x2 > CAIRO_RECT_INT_MAX) |
| x2 = CAIRO_RECT_INT_MAX; |
| y2 = ceil (y2); |
| if (y2 > CAIRO_RECT_INT_MAX) |
| y2 = CAIRO_RECT_INT_MAX; |
| |
| extents->x = x1; extents->width = x2 - x1; |
| extents->y = y1; extents->height = y2 - y1; |
| return; |
| |
| UNBOUNDED: |
| /* unbounded patterns -> 'infinite' extents */ |
| _cairo_unbounded_rectangle_init (extents); |
| return; |
| |
| EMPTY: |
| extents->x = extents->y = 0; |
| extents->width = extents->height = 0; |
| return; |
| } |
| |
| |
| static unsigned long |
| _cairo_solid_pattern_hash (unsigned long hash, |
| const cairo_solid_pattern_t *solid) |
| { |
| hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color)); |
| |
| return hash; |
| } |
| |
| static unsigned long |
| _cairo_gradient_color_stops_hash (unsigned long hash, |
| const cairo_gradient_pattern_t *gradient) |
| { |
| unsigned int n; |
| |
| hash = _cairo_hash_bytes (hash, |
| &gradient->n_stops, |
| sizeof (gradient->n_stops)); |
| |
| for (n = 0; n < gradient->n_stops; n++) { |
| hash = _cairo_hash_bytes (hash, |
| &gradient->stops[n].offset, |
| sizeof (double)); |
| hash = _cairo_hash_bytes (hash, |
| &gradient->stops[n].color, |
| sizeof (cairo_color_stop_t)); |
| } |
| |
| return hash; |
| } |
| |
| unsigned long |
| _cairo_linear_pattern_hash (unsigned long hash, |
| const cairo_linear_pattern_t *linear) |
| { |
| hash = _cairo_hash_bytes (hash, &linear->pd1, sizeof (linear->pd1)); |
| hash = _cairo_hash_bytes (hash, &linear->pd2, sizeof (linear->pd2)); |
| |
| return _cairo_gradient_color_stops_hash (hash, &linear->base); |
| } |
| |
| unsigned long |
| _cairo_radial_pattern_hash (unsigned long hash, |
| const cairo_radial_pattern_t *radial) |
| { |
| hash = _cairo_hash_bytes (hash, &radial->cd1.center, sizeof (radial->cd1.center)); |
| hash = _cairo_hash_bytes (hash, &radial->cd1.radius, sizeof (radial->cd1.radius)); |
| hash = _cairo_hash_bytes (hash, &radial->cd2.center, sizeof (radial->cd2.center)); |
| hash = _cairo_hash_bytes (hash, &radial->cd2.radius, sizeof (radial->cd2.radius)); |
| |
| return _cairo_gradient_color_stops_hash (hash, &radial->base); |
| } |
| |
| static unsigned long |
| _cairo_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh) |
| { |
| const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0); |
| unsigned int i, n = _cairo_array_num_elements (&mesh->patches); |
| |
| for (i = 0; i < n; i++) |
| hash = _cairo_hash_bytes (hash, patch + i, sizeof (cairo_mesh_patch_t)); |
| |
| return hash; |
| } |
| |
| static unsigned long |
| _cairo_surface_pattern_hash (unsigned long hash, |
| const cairo_surface_pattern_t *surface) |
| { |
| hash ^= surface->surface->unique_id; |
| |
| return hash; |
| } |
| |
| unsigned long |
| _cairo_pattern_hash (const cairo_pattern_t *pattern) |
| { |
| unsigned long hash = _CAIRO_HASH_INIT_VALUE; |
| |
| if (pattern->status) |
| return 0; |
| |
| hash = _cairo_hash_bytes (hash, &pattern->type, sizeof (pattern->type)); |
| if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) { |
| hash = _cairo_hash_bytes (hash, |
| &pattern->matrix, sizeof (pattern->matrix)); |
| hash = _cairo_hash_bytes (hash, |
| &pattern->filter, sizeof (pattern->filter)); |
| hash = _cairo_hash_bytes (hash, |
| &pattern->extend, sizeof (pattern->extend)); |
| hash = _cairo_hash_bytes (hash, |
| &pattern->has_component_alpha, |
| sizeof (pattern->has_component_alpha)); |
| } |
| |
| switch (pattern->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| return _cairo_solid_pattern_hash (hash, (cairo_solid_pattern_t *) pattern); |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| return _cairo_linear_pattern_hash (hash, (cairo_linear_pattern_t *) pattern); |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| return _cairo_radial_pattern_hash (hash, (cairo_radial_pattern_t *) pattern); |
| case CAIRO_PATTERN_TYPE_MESH: |
| return _cairo_mesh_pattern_hash (hash, (cairo_mesh_pattern_t *) pattern); |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern); |
| default: |
| ASSERT_NOT_REACHED; |
| return FALSE; |
| } |
| } |
| |
| static cairo_bool_t |
| _cairo_solid_pattern_equal (const cairo_solid_pattern_t *a, |
| const cairo_solid_pattern_t *b) |
| { |
| return _cairo_color_equal (&a->color, &b->color); |
| } |
| |
| static cairo_bool_t |
| _cairo_gradient_color_stops_equal (const cairo_gradient_pattern_t *a, |
| const cairo_gradient_pattern_t *b) |
| { |
| unsigned int n; |
| |
| if (a->n_stops != b->n_stops) |
| return FALSE; |
| |
| for (n = 0; n < a->n_stops; n++) { |
| if (a->stops[n].offset != b->stops[n].offset) |
| return FALSE; |
| if (! _cairo_color_stop_equal (&a->stops[n].color, &b->stops[n].color)) |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| cairo_bool_t |
| _cairo_linear_pattern_equal (const cairo_linear_pattern_t *a, |
| const cairo_linear_pattern_t *b) |
| { |
| if (a->pd1.x != b->pd1.x) |
| return FALSE; |
| |
| if (a->pd1.y != b->pd1.y) |
| return FALSE; |
| |
| if (a->pd2.x != b->pd2.x) |
| return FALSE; |
| |
| if (a->pd2.y != b->pd2.y) |
| return FALSE; |
| |
| return _cairo_gradient_color_stops_equal (&a->base, &b->base); |
| } |
| |
| cairo_bool_t |
| _cairo_radial_pattern_equal (const cairo_radial_pattern_t *a, |
| const cairo_radial_pattern_t *b) |
| { |
| if (a->cd1.center.x != b->cd1.center.x) |
| return FALSE; |
| |
| if (a->cd1.center.y != b->cd1.center.y) |
| return FALSE; |
| |
| if (a->cd1.radius != b->cd1.radius) |
| return FALSE; |
| |
| if (a->cd2.center.x != b->cd2.center.x) |
| return FALSE; |
| |
| if (a->cd2.center.y != b->cd2.center.y) |
| return FALSE; |
| |
| if (a->cd2.radius != b->cd2.radius) |
| return FALSE; |
| |
| return _cairo_gradient_color_stops_equal (&a->base, &b->base); |
| } |
| |
| static cairo_bool_t |
| _cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a, |
| const cairo_mesh_pattern_t *b) |
| { |
| const cairo_mesh_patch_t *patch_a, *patch_b; |
| unsigned int i, num_patches_a, num_patches_b; |
| |
| num_patches_a = _cairo_array_num_elements (&a->patches); |
| num_patches_b = _cairo_array_num_elements (&b->patches); |
| |
| if (num_patches_a != num_patches_b) |
| return FALSE; |
| |
| for (i = 0; i < num_patches_a; i++) { |
| patch_a = _cairo_array_index_const (&a->patches, i); |
| patch_b = _cairo_array_index_const (&a->patches, i); |
| if (memcmp (patch_a, patch_b, sizeof(cairo_mesh_patch_t)) != 0) |
| return FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static cairo_bool_t |
| _cairo_surface_pattern_equal (const cairo_surface_pattern_t *a, |
| const cairo_surface_pattern_t *b) |
| { |
| return a->surface->unique_id == b->surface->unique_id; |
| } |
| |
| cairo_bool_t |
| _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b) |
| { |
| if (a->status || b->status) |
| return FALSE; |
| |
| if (a == b) |
| return TRUE; |
| |
| if (a->type != b->type) |
| return FALSE; |
| |
| if (a->has_component_alpha != b->has_component_alpha) |
| return FALSE; |
| |
| if (a->type != CAIRO_PATTERN_TYPE_SOLID) { |
| if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) |
| return FALSE; |
| |
| if (a->filter != b->filter) |
| return FALSE; |
| |
| if (a->extend != b->extend) |
| return FALSE; |
| } |
| |
| switch (a->type) { |
| case CAIRO_PATTERN_TYPE_SOLID: |
| return _cairo_solid_pattern_equal ((cairo_solid_pattern_t *) a, |
| (cairo_solid_pattern_t *) b); |
| case CAIRO_PATTERN_TYPE_LINEAR: |
| return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a, |
| (cairo_linear_pattern_t *) b); |
| case CAIRO_PATTERN_TYPE_RADIAL: |
| return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a, |
| (cairo_radial_pattern_t *) b); |
| case CAIRO_PATTERN_TYPE_MESH: |
| return _cairo_mesh_pattern_equal ((cairo_mesh_pattern_t *) a, |
| (cairo_mesh_pattern_t *) b); |
| case CAIRO_PATTERN_TYPE_SURFACE: |
| return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a, |
| (cairo_surface_pattern_t *) b); |
| default: |
| ASSERT_NOT_REACHED; |
| return FALSE; |
| } |
| } |
| |
| /** |
| * 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->status) |
| return pattern->status; |
| |
| if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) |
| return _cairo_error (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->status) |
| return pattern->status; |
| |
| if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) |
| return _cairo_error (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 |
| * @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->status) |
| return pattern->status; |
| |
| if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && |
| pattern->type != CAIRO_PATTERN_TYPE_RADIAL) |
| return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| |
| if (index < 0 || (unsigned int) index >= gradient->n_stops) |
| return _cairo_error (CAIRO_STATUS_INVALID_INDEX); |
| |
| if (offset) |
| *offset = gradient->stops[index].offset; |
| 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->status) |
| return pattern->status; |
| |
| if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR && |
| pattern->type != CAIRO_PATTERN_TYPE_RADIAL) |
| return _cairo_error (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->status) |
| return pattern->status; |
| |
| if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR) |
| return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| |
| if (x0) |
| *x0 = linear->pd1.x; |
| if (y0) |
| *y0 = linear->pd1.y; |
| if (x1) |
| *x1 = linear->pd2.x; |
| if (y1) |
| *y1 = linear->pd2.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->status) |
| return pattern->status; |
| |
| if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL) |
| return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| |
| if (x0) |
| *x0 = radial->cd1.center.x; |
| if (y0) |
| *y0 = radial->cd1.center.y; |
| if (r0) |
| *r0 = radial->cd1.radius; |
| if (x1) |
| *x1 = radial->cd2.center.x; |
| if (y1) |
| *y1 = radial->cd2.center.y; |
| if (r1) |
| *r1 = radial->cd2.radius; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| |
| /** |
| * cairo_mesh_pattern_get_patch_count |
| * @pattern: a #cairo_pattern_t |
| * @count: return value for the number patches, or %NULL |
| * |
| * Gets the number of patches specified in the given mesh pattern. |
| * |
| * The number only includes patches which have been finished by |
| * calling cairo_mesh_pattern_end_patch(). For example it will be 0 |
| * during the definition of the first patch. |
| * |
| * Return value: %CAIRO_STATUS_SUCCESS, or |
| * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a mesh |
| * pattern. |
| * |
| * Since: 1.12 |
| */ |
| cairo_status_t |
| cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern, |
| unsigned int *count) |
| { |
| cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; |
| |
| if (unlikely (pattern->status)) |
| return pattern->status; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) |
| return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| |
| if (count) { |
| *count = _cairo_array_num_elements (&mesh->patches); |
| if (mesh->current_patch) |
| *count -= 1; |
| } |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| slim_hidden_def (cairo_mesh_pattern_get_patch_count); |
| |
| /** |
| * cairo_mesh_pattern_get_path |
| * @pattern: a #cairo_pattern_t |
| * @patch_num: the patch number to return data for |
| * |
| * Gets path defining the patch @patch_num for a mesh |
| * pattern. |
| * |
| * @patch_num can range 0 to 1 less than the number returned by |
| * cairo_mesh_pattern_get_patch_count(). |
| * |
| * Return value: the path defining the patch, or a path with status |
| * %CAIRO_STATUS_INVALID_INDEX if @patch_num or @point_num is not |
| * valid for @pattern. If @pattern is not a mesh pattern, a path with |
| * status %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is returned. |
| * |
| * Since: 1.12 |
| */ |
| cairo_path_t * |
| cairo_mesh_pattern_get_path (cairo_pattern_t *pattern, |
| unsigned int patch_num) |
| { |
| cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; |
| const cairo_mesh_patch_t *patch; |
| cairo_path_t *path; |
| cairo_path_data_t *data; |
| unsigned int patch_count; |
| int l, current_point; |
| |
| if (unlikely (pattern->status)) |
| return _cairo_path_create_in_error (pattern->status); |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) |
| return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH)); |
| |
| patch_count = _cairo_array_num_elements (&mesh->patches); |
| if (mesh->current_patch) |
| patch_count--; |
| |
| if (unlikely (patch_num >= patch_count)) |
| return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_INVALID_INDEX)); |
| |
| patch = _cairo_array_index_const (&mesh->patches, patch_num); |
| |
| path = malloc (sizeof (cairo_path_t)); |
| if (path == NULL) |
| return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
| |
| path->num_data = 18; |
| path->data = _cairo_malloc_ab (path->num_data, |
| sizeof (cairo_path_data_t)); |
| if (path->data == NULL) { |
| free (path); |
| return _cairo_path_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); |
| } |
| |
| data = path->data; |
| data[0].header.type = CAIRO_PATH_MOVE_TO; |
| data[0].header.length = 2; |
| data[1].point.x = patch->points[0][0].x; |
| data[1].point.y = patch->points[0][0].y; |
| data += data[0].header.length; |
| |
| current_point = 0; |
| |
| for (l = 0; l < 4; l++) { |
| int i, j, k; |
| |
| data[0].header.type = CAIRO_PATH_CURVE_TO; |
| data[0].header.length = 4; |
| |
| for (k = 1; k < 4; k++) { |
| current_point = (current_point + 1) % 12; |
| i = mesh_path_point_i[current_point]; |
| j = mesh_path_point_j[current_point]; |
| data[k].point.x = patch->points[i][j].x; |
| data[k].point.y = patch->points[i][j].y; |
| } |
| |
| data += data[0].header.length; |
| } |
| |
| path->status = CAIRO_STATUS_SUCCESS; |
| |
| return path; |
| } |
| slim_hidden_def (cairo_mesh_pattern_get_path); |
| |
| /** |
| * cairo_mesh_pattern_get_corner_color_rgba |
| * @pattern: a #cairo_pattern_t |
| * @patch_num: the patch number to return data for |
| * @corner_num: the corner number to return data for |
| * @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 information in corner @corner_num of patch |
| * @patch_num for a mesh pattern. |
| * |
| * @patch_num can range 0 to 1 less than the number returned by |
| * cairo_mesh_pattern_get_patch_count(). |
| * |
| * Valid values for @corner_num are from 0 to 3 and identify the |
| * corners as explained in cairo_pattern_create_mesh(). |
| * |
| * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX |
| * if @patch_num or @corner_num is not valid for @pattern. If |
| * @pattern is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH |
| * is returned. |
| * |
| * Since: 1.12 |
| **/ |
| cairo_status_t |
| cairo_mesh_pattern_get_corner_color_rgba (cairo_pattern_t *pattern, |
| unsigned int patch_num, |
| unsigned int corner_num, |
| double *red, double *green, |
| double *blue, double *alpha) |
| { |
| cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; |
| unsigned int patch_count; |
| const cairo_mesh_patch_t *patch; |
| |
| if (unlikely (pattern->status)) |
| return pattern->status; |
| |
| if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) |
| return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| |
| if (unlikely (corner_num > 3)) |
| return _cairo_error (CAIRO_STATUS_INVALID_INDEX); |
| |
| patch_count = _cairo_array_num_elements (&mesh->patches); |
| if (mesh->current_patch) |
| patch_count--; |
| |
| if (unlikely (patch_num >= patch_count)) |
| return _cairo_error (CAIRO_STATUS_INVALID_INDEX); |
| |
| patch = _cairo_array_index_const (&mesh->patches, patch_num); |
| |
| if (red) |
| *red = patch->colors[corner_num].red; |
| if (green) |
| *green = patch->colors[corner_num].green; |
| if (blue) |
| *blue = patch->colors[corner_num].blue; |
| if (alpha) |
| *alpha = patch->colors[corner_num].alpha; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| slim_hidden_def (cairo_mesh_pattern_get_corner_color_rgba); |
| |
| /** |
| * cairo_mesh_pattern_get_control_point |
| * @pattern: a #cairo_pattern_t |
| * @patch_num: the patch number to return data for |
| * @point_num: the control point number to return data for |
| * @x: return value for the x coordinate of the control point, or %NULL |
| * @y: return value for the y coordinate of the control point, or %NULL |
| * |
| * Gets the control point @point_num of patch @patch_num for a mesh |
| * pattern. |
| * |
| * @patch_num can range 0 to 1 less than the number returned by |
| * cairo_mesh_pattern_get_patch_count(). |
| * |
| * Valid values for @point_num are from 0 to 3 and identify the |
| * control points as explained in cairo_pattern_create_mesh(). |
| * |
| * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX |
| * if @patch_num or @point_num is not valid for @pattern. If @pattern |
| * is not a mesh pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is |
| * returned. |
| * |
| * Since: 1.12 |
| **/ |
| cairo_status_t |
| cairo_mesh_pattern_get_control_point (cairo_pattern_t *pattern, |
| unsigned int patch_num, |
| unsigned int point_num, |
| double *x, double *y) |
| { |
| cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; |
| const cairo_mesh_patch_t *patch; |
| unsigned int patch_count; |
| int i, j; |
| |
| if (pattern->status) |
| return pattern->status; |
| |
| if (pattern->type != CAIRO_PATTERN_TYPE_MESH) |
| return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
| |
| if (point_num > 3) |
| return _cairo_error (CAIRO_STATUS_INVALID_INDEX); |
| |
| patch_count = _cairo_array_num_elements (&mesh->patches); |
| if (mesh->current_patch) |
| patch_count--; |
| |
| if (unlikely (patch_num >= patch_count)) |
| return _cairo_error (CAIRO_STATUS_INVALID_INDEX); |
| |
| patch = _cairo_array_index_const (&mesh->patches, patch_num); |
| |
| i = mesh_control_point_i[point_num]; |
| j = mesh_control_point_j[point_num]; |
| |
| if (x) |
| *x = patch->points[i][j].x; |
| if (y) |
| *y = patch->points[i][j].y; |
| |
| return CAIRO_STATUS_SUCCESS; |
| } |
| slim_hidden_def (cairo_mesh_pattern_get_control_point); |
| |
| void |
| _cairo_pattern_reset_static_data (void) |
| { |
| #if HAS_FREED_POOL |
| int i; |
| |
| for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++) |
| _freed_pool_reset (&freed_pattern_pool[i]); |
| #endif |
| |
| _cairo_pattern_reset_solid_surface_cache (); |
| } |