Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 1892 → Rev 3959

/programs/develop/libraries/cairo/src/cairo-pattern.c
29,9 → 29,21
*/
 
#include "cairoint.h"
 
#include "cairo-array-private.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
#include "cairo-image-surface-private.h"
#include "cairo-list-inline.h"
#include "cairo-path-private.h"
#include "cairo-pattern-private.h"
#include "cairo-recording-surface-inline.h"
#include "cairo-surface-snapshot-inline.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
44,68 → 56,108
* brush too.
*
* A cairo pattern is created by using one of the many constructors,
* of the form cairo_pattern_create_<emphasis>type</emphasis>()
* of the form
* <function>cairo_pattern_create_<emphasis>type</emphasis>()</function>
* or implicitly through
* cairo_set_source_<emphasis>type</emphasis>() functions.
*/
* <function>cairo_set_source_<emphasis>type</emphasis>()</function>
* functions.
**/
 
#if HAS_FREED_POOL
static freed_pool_t freed_pattern_pool[4];
#endif
static freed_pool_t freed_pattern_pool[5];
 
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 */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
1.0 /* opacity */
}
};
 
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 */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT }, /* extend */
1.0 /* opacity */
}
};
 
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 */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_NEAREST, /* filter */
CAIRO_EXTEND_REPEAT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
1.0 /* opacity */
},
{ 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 */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_NEAREST, /* filter */
CAIRO_EXTEND_REPEAT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
1.0 /* opacity */
},
{ 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 */
{ NULL, NULL }, /* observers */
 
CAIRO_PATTERN_TYPE_SOLID, /* type */
CAIRO_FILTER_NEAREST, /* filter */
CAIRO_EXTEND_REPEAT, /* extend */
FALSE, /* has component alpha */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_GRADIENT_DEFAULT}, /* extend */
1.0 /* opacity */
},
{ 1., 1., 1., 1., 0xffff, 0xffff, 0xffff, 0xffff },/* color (double rgba, short rgba) */
};
 
static void
_cairo_pattern_notify_observers (cairo_pattern_t *pattern,
unsigned int flags)
{
cairo_pattern_observer_t *pos;
 
cairo_list_foreach_entry (pos, cairo_pattern_observer_t, &pattern->observers, link)
pos->notify (pos, pattern, flags);
}
 
/**
* _cairo_pattern_set_error:
* @pattern: a pattern
137,7 → 189,7
return _cairo_error (status);
}
 
static void
void
_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
{
#if HAVE_VALGRIND
154,6 → 206,11
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;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
break;
}
#endif
 
166,16 → 223,20
 
_cairo_user_data_array_init (&pattern->user_data);
 
if (type == CAIRO_PATTERN_TYPE_SURFACE)
if (type == CAIRO_PATTERN_TYPE_SURFACE ||
type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
pattern->extend = CAIRO_EXTEND_SURFACE_DEFAULT;
else
pattern->extend = CAIRO_EXTEND_GRADIENT_DEFAULT;
 
pattern->filter = CAIRO_FILTER_DEFAULT;
pattern->opacity = 1.0;
 
pattern->has_component_alpha = FALSE;
 
cairo_matrix_init_identity (&pattern->matrix);
 
cairo_list_init (&pattern->observers);
}
 
static cairo_status_t
219,10 → 280,24
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)
{
cairo_status_t status;
 
if (other->status)
return _cairo_pattern_set_error (pattern, other->status);
 
248,7 → 323,6
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)));
261,6 → 335,23
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;
 
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;
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE: {
status = _cairo_raster_source_pattern_init_copy (pattern, other);
if (unlikely (status))
return status;
} break;
}
 
/* The reference count and user_data array are unique to the copy. */
293,6 → 384,12
case CAIRO_PATTERN_TYPE_RADIAL:
size = sizeof (cairo_radial_pattern_t);
break;
case CAIRO_PATTERN_TYPE_MESH:
size = sizeof (cairo_mesh_pattern_t);
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
size = sizeof (cairo_raster_source_pattern_t);
break;
}
 
memcpy (pattern, other, size);
324,11 → 421,11
 
cairo_surface_destroy (surface);
 
if (surface_pattern->surface->status)
return surface_pattern->surface->status;
}
status = surface_pattern->surface->status;
} else if (pattern->type == CAIRO_PATTERN_TYPE_RASTER_SOURCE)
status = _cairo_raster_source_pattern_snapshot (pattern);
 
return CAIRO_STATUS_SUCCESS;
return status;
}
 
void
353,6 → 450,15
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;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
_cairo_raster_source_pattern_finish (pattern);
break;
}
 
#if HAVE_VALGRIND
369,6 → 475,11
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;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
break;
}
#endif
}
396,6 → 507,12
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;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
pattern = malloc (sizeof (cairo_raster_source_pattern_t));
break;
default:
ASSERT_NOT_REACHED;
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
414,7 → 531,6
return CAIRO_STATUS_SUCCESS;
}
 
 
void
_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
const cairo_color_t *color)
450,19 → 566,19
pattern->stops = NULL;
}
 
void
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->p1.x = _cairo_fixed_from_double (x0);
pattern->p1.y = _cairo_fixed_from_double (y0);
pattern->p2.x = _cairo_fixed_from_double (x1);
pattern->p2.y = _cairo_fixed_from_double (y1);
pattern->pd1.x = x0;
pattern->pd1.y = y0;
pattern->pd2.x = x1;
pattern->pd2.y = y1;
}
 
void
static void
_cairo_pattern_init_radial (cairo_radial_pattern_t *pattern,
double cx0, double cy0, double radius0,
double cx1, double cy1, double radius1)
469,12 → 585,12
{
_cairo_pattern_init_gradient (&pattern->base, CAIRO_PATTERN_TYPE_RADIAL);
 
pattern->c1.x = _cairo_fixed_from_double (cx0);
pattern->c1.y = _cairo_fixed_from_double (cy0);
pattern->r1 = _cairo_fixed_from_double (fabs (radius0));
pattern->c2.x = _cairo_fixed_from_double (cx1);
pattern->c2.y = _cairo_fixed_from_double (cy1);
pattern->r2 = _cairo_fixed_from_double (fabs (radius1));
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 *
535,21 → 651,13
* 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.0
**/
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);
return cairo_pattern_create_rgba (red, green, blue, 1.0);
}
slim_hidden_def (cairo_pattern_create_rgb);
 
573,6 → 681,8
* 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.0
**/
cairo_pattern_t *
cairo_pattern_create_rgba (double red, double green, double blue,
607,6 → 717,8
* 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.0
**/
cairo_pattern_t *
cairo_pattern_create_for_surface (cairo_surface_t *surface)
665,6 → 777,8
* 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.0
**/
cairo_pattern_t *
cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
716,6 → 830,8
* 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.0
**/
cairo_pattern_t *
cairo_pattern_create_radial (double cx0, double cy0, double radius0,
741,7 → 857,187
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><screen>
* C1 Side 1 C2
* +---------------+
* | |
* | P1 P2 |
* | |
* Side 0 | | Side 2
* | |
* | |
* | P0 P3 |
* | |
* +---------------+
* C0 Side 3 C3
* </screen></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 *pattern = cairo_pattern_create_mesh ();
*
* /&ast; Add a Coons patch &ast;/
* cairo_mesh_pattern_begin_patch (pattern);
* 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 (pattern);
*
* /&ast; Add a Gouraud-shaded triangle &ast;/
* cairo_mesh_pattern_begin_patch (pattern)
* 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 (pattern)
* </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
*
753,6 → 1049,8
* cairo_pattern_get_reference_count().
*
* Return value: the referenced #cairo_pattern_t.
*
* Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_reference (cairo_pattern_t *pattern)
793,8 → 1091,11
* 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.
* Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY,
* %CAIRO_STATUS_INVALID_MATRIX, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH,
* or %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
*
* Since: 1.0
**/
cairo_status_t
cairo_pattern_status (cairo_pattern_t *pattern)
809,6 → 1110,8
* 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().
*
* Since: 1.0
**/
void
cairo_pattern_destroy (cairo_pattern_t *pattern)
828,7 → 1131,10
_cairo_pattern_fini (pattern);
 
/* maintain a small cache of freed patterns */
if (type < ARRAY_LENGTH (freed_pattern_pool))
_freed_pool_put (&freed_pattern_pool[type], pattern);
else
free (pattern);
}
slim_hidden_def (cairo_pattern_destroy);
 
906,6 → 1212,451
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 **) &current_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 least 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)
948,6 → 1699,126
}
 
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,
1020,6 → 1891,8
* 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.
*
* Since: 1.0
**/
void
cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
1028,25 → 1901,9
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;
cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, 1.0);
}
 
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
1073,7 → 1930,9
* 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.
*/
*
* Since: 1.0
**/
void
cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
double offset,
1101,6 → 1960,7
_cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
offset, red, green, blue, alpha);
}
slim_hidden_def (cairo_pattern_add_color_stop_rgba);
 
/**
* cairo_pattern_set_matrix:
1133,6 → 1993,8
*
* Also, please note the discussion of the user-space locking
* semantics of cairo_set_source().
*
* Since: 1.0
**/
void
cairo_pattern_set_matrix (cairo_pattern_t *pattern,
1148,6 → 2010,7
return;
 
pattern->matrix = *matrix;
_cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_MATRIX);
 
inverse = *matrix;
status = cairo_matrix_invert (&inverse);
1162,6 → 2025,8
* @matrix: return value for the matrix
*
* Stores the pattern's transformation matrix into @matrix.
*
* Since: 1.0
**/
void
cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix)
1188,6 → 2053,8
* cairo_set_source_surface (cr, image, x, y);
* cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST);
* </programlisting></informalexample>
*
* Since: 1.0
**/
void
cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
1196,6 → 2063,7
return;
 
pattern->filter = filter;
_cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_FILTER);
}
 
/**
1206,6 → 2074,8
* for details on each filter.
*
* Return value: the current filter used for resizing the pattern.
*
* Since: 1.0
**/
cairo_filter_t
cairo_pattern_get_filter (cairo_pattern_t *pattern)
1225,6 → 2095,8
*
* The default extend mode is %CAIRO_EXTEND_NONE for surface patterns
* and %CAIRO_EXTEND_PAD for gradient patterns.
*
* Since: 1.0
**/
void
cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
1233,6 → 2105,7
return;
 
pattern->extend = extend;
_cairo_pattern_notify_observers (pattern, CAIRO_PATTERN_NOTIFY_EXTEND);
}
 
/**
1244,6 → 2117,8
*
* Return value: the current extend strategy used for drawing the
* pattern.
*
* Since: 1.0
**/
cairo_extend_t
cairo_pattern_get_extend (cairo_pattern_t *pattern)
1262,517 → 2137,622
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)
static cairo_bool_t
_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
{
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;
return fabs (linear->pd1.x - linear->pd2.x) < DBL_EPSILON &&
fabs (linear->pd1.y - linear->pd2.y) < DBL_EPSILON;
}
 
/* 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.
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 ().
*/
point0.x = _cairo_fixed_to_double (pattern->p1.x);
point0.y = _cairo_fixed_to_double (pattern->p1.y);
point1.x = _cairo_fixed_to_double (pattern->p2.x);
point1.y = _cairo_fixed_to_double (pattern->p2.y);
 
_cairo_matrix_get_affine (&pattern->base.base.matrix,
&a, &b, &c, &d, &tx, &ty);
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);
}
 
dx = point1.x - point0.x;
dy = point1.y - point0.y;
scale = dx * dx + dy * dy;
scale = (scale) ? 1.0 / scale : 1.0;
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;
 
start = dx * point0.x + dy * point0.y;
assert (! _linear_pattern_is_degenerate (linear));
 
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.
/*
* 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.
*
* Horizontal works the same way with the two left corners.
* 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
*/
 
*is_vertical = factors[1] == factors[0];
*is_horizontal = factors[2] == factors[0];
}
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;
 
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_status_t status;
cairo_bool_t repeat = FALSE;
cairo_bool_t opaque = TRUE;
t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy;
tdx = (x1 - x0) * pdx;
tdy = (y1 - y0) * pdy;
 
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;
/*
* 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.
*/
 
if (CAIRO_INJECT_FAULT ())
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
range[0] = range[1] = t0;
if (tdx < 0)
range[0] += tdx;
else
range[1] += tdx;
 
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);
if (tdy < 0)
range[0] += tdy;
else
range[1] += tdy;
}
 
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;
if (! CAIRO_ALPHA_SHORT_IS_OPAQUE (pixman_stops[i].color.alpha))
opaque = FALSE;
}
 
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR)
static cairo_bool_t
_extend_range (double range[2], double value, cairo_bool_t valid)
{
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
pixman_point_fixed_t p1, p2;
cairo_fixed_t xdim, ydim;
if (!valid)
range[0] = range[1] = value;
else if (value < range[0])
range[0] = value;
else if (value > range[1])
range[1] = value;
 
xdim = linear->p2.x - linear->p1.x;
ydim = linear->p2.y - linear->p1.y;
return TRUE;
}
 
/*
* Transform the matrix to avoid overflow when converting between
* cairo_fixed_t and pixman_fixed_t (without incurring performance
* loss when the transformation is unnecessary).
* _cairo_radial_pattern_focus_is_inside:
*
* XXX: Consider converting out-of-range co-ordinates and transforms.
* Having a function to compute the required transformation to
* "normalize" a given bounding box would be generally useful -
* cf linear patterns, gradient patterns, surface patterns...
* Returns %TRUE if and only if the focus point exists and is
* contained in one of the two extreme circles. This condition is
* equivalent to one of the two extreme circles being completely
* contained in the other one.
*
* Note: if the focus is on the border of one of the two circles (in
* which case the circles are tangent in the focus point), it is not
* considered as contained in the circle, hence this function returns
* %FALSE.
*
*/
#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
if (_cairo_fixed_integer_ceil (xdim) > PIXMAN_MAX_INT ||
_cairo_fixed_integer_ceil (ydim) > PIXMAN_MAX_INT)
cairo_bool_t
_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial)
{
double sf;
double cx, cy, cr, dx, dy, dr;
 
if (xdim > ydim)
sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
else
sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
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;
 
p1.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.x) * sf);
p1.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p1.y) * sf);
p2.x = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.x) * sf);
p2.y = _cairo_fixed_16_16_from_double (_cairo_fixed_to_double (linear->p2.y) * sf);
 
cairo_matrix_scale (&matrix, sf, sf);
return dx*dx + dy*dy < dr*dr;
}
else
{
p1.x = _cairo_fixed_to_16_16 (linear->p1.x);
p1.y = _cairo_fixed_to_16_16 (linear->p1.y);
p2.x = _cairo_fixed_to_16_16 (linear->p2.x);
p2.y = _cairo_fixed_to_16_16 (linear->p2.y);
}
 
pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
pixman_stops,
pattern->n_stops);
}
else
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])
{
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
pixman_point_fixed_t c1, c2;
pixman_fixed_t r1, r2;
double cx, cy, cr, dx, dy, dr;
double a, x_focus, y_focus;
double mindr, minx, miny, maxx, maxy;
cairo_bool_t valid;
 
c1.x = _cairo_fixed_to_16_16 (radial->c1.x);
c1.y = _cairo_fixed_to_16_16 (radial->c1.y);
r1 = _cairo_fixed_to_16_16 (radial->r1);
assert (! _radial_pattern_is_degenerate (radial));
assert (x0 < x1);
assert (y0 < y1);
 
c2.x = _cairo_fixed_to_16_16 (radial->c2.x);
c2.y = _cairo_fixed_to_16_16 (radial->c2.y);
r2 = _cairo_fixed_to_16_16 (radial->r2);
tolerance = MAX (tolerance, DBL_EPSILON);
 
pixman_image = pixman_image_create_radial_gradient (&c1, &c2,
r1, r2,
pixman_stops,
pattern->n_stops);
}
range[0] = range[1] = 0;
valid = FALSE;
 
if (pixman_stops != pixman_stops_static)
free (pixman_stops);
x_focus = y_focus = 0; /* silence gcc */
 
if (unlikely (pixman_image == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
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;
 
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;
}
/* translate by -(cx, cy) to simplify computations */
x0 -= cx;
y0 -= cy;
x1 -= cx;
y1 -= cy;
 
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;
/* enlarge boundaries slightly to avoid rounding problems in the
* parameter range computation */
x0 -= DBL_EPSILON;
y0 -= DBL_EPSILON;
x1 += DBL_EPSILON;
y1 += DBL_EPSILON;
 
*out = &image->base;
/* 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;
 
return CAIRO_STATUS_SUCCESS;
}
/* we dont' allow negative radiuses, so we will be checking that
* t*dr >= mindr to consider t valid */
mindr = -(cr + DBL_EPSILON);
 
if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_bool_t is_horizontal;
cairo_bool_t is_vertical;
/*
* 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.
*/
 
_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.
/*
* 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 (is_vertical && width > 8) {
width = 8;
repeat = TRUE;
}
}
if (fabs (dr) >= DBL_EPSILON) {
double t_focus;
 
if (! pixman_image_set_filter (pixman_image, PIXMAN_FILTER_BILINEAR,
NULL, 0))
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)
{
pixman_image_unref (pixman_image);
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
valid = _extend_range (range, t_focus, valid);
}
 
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;
}
 
_cairo_matrix_to_pixman_matrix (&matrix, &pixman_transform,
width/2., height/2.);
if (!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);
/*
* 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); \
}
 
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;
}
/* 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);
 
pixman_image_composite32 (PIXMAN_OP_SRC,
pixman_image,
NULL,
image->pixman_image,
x, y,
0, 0,
0, 0,
width, height);
#undef T_EDGE
 
pixman_image_unref (pixman_image);
/*
* 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;
 
_cairo_debug_check_image_surface_is_defined (&image->base);
/* 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);
 
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;
/*
* 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; \
} \
}
 
/* 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;
maxd2 = 0;
 
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;
/* 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);
 
if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1)
return FALSE;
#undef T_EDGE
 
if (! _cairo_surface_is_similar (cache->surface, dst))
return FALSE;
 
return TRUE;
/*
* 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);
}
 
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);
/*
* 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); \
}
 
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;
/* circles touching each corner */
T_CORNER (x0, y0);
T_CORNER (x0, y1);
T_CORNER (x1, y0);
T_CORNER (x1, y1);
 
cairo_surface_t *surface, *to_destroy = NULL;
cairo_status_t status;
#undef T_CORNER
} else {
double inva, b, c, d;
 
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
inva = 1 / a;
 
/* Check cache first */
if (i < solid_surface_cache.size &&
_cairo_pattern_solid_surface_matches_color (&solid_surface_cache.cache[i],
pattern,
dst))
{
goto DONE;
/*
* 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); \
}
 
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;
/* circles touching each corner */
T_CORNER (x0, y0);
T_CORNER (x0, y1);
T_CORNER (x1, y0);
T_CORNER (x1, y1);
 
#undef T_CORNER
}
}
 
/* 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))
/**
* _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])
{
/* Reuse the surface instead of evicting */
status = _cairo_surface_repaint_solid_pattern_surface (dst, surface, pattern);
if (unlikely (status))
goto EVICT;
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
 
cairo_surface_reference (surface);
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);
}
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)))
/**
* _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)
{
/* In the rare event of a substitute surface being returned,
* don't cache the fallback.
*/
*out = surface;
goto NOCACHE;
}
}
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
 
if (i == solid_surface_cache.size)
solid_surface_cache.size++;
#define lerp(a,b) (a)*(1-t) + (b)*t
 
to_destroy = solid_surface_cache.cache[i].surface;
solid_surface_cache.cache[i].surface = surface;
solid_surface_cache.cache[i].color = pattern->color;
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);
}
 
DONE:
*out = cairo_surface_reference (solid_surface_cache.cache[i].surface);
#undef lerp
}
 
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;
/**
* _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;
 
UNLOCK:
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
 
if (to_destroy)
cairo_surface_destroy (to_destroy);
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
 
return status;
}
out_circle[0].center = linear->pd1;
out_circle[0].radius = 0;
out_circle[1].center = linear->pd2;
out_circle[1].radius = 0;
 
static void
_cairo_pattern_reset_solid_surface_cache (void)
{
CAIRO_MUTEX_LOCK (_cairo_pattern_solid_surface_cache_lock);
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;
 
/* 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;
out_circle[0] = radial->cd1;
out_circle[1] = radial->cd2;
 
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);
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));
}
 
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
}
if (unlikely (dim > max_value)) {
cairo_matrix_t scale;
 
static void
_extents_to_linear_parameter (const cairo_linear_pattern_t *linear,
const cairo_rectangle_int_t *extents,
double t[2])
{
double t0, tdx, tdy;
double p1x, p1y, pdx, pdy, invsqnorm;
dim = max_value / dim;
 
p1x = _cairo_fixed_to_double (linear->p1.x);
p1y = _cairo_fixed_to_double (linear->p1.y);
pdx = _cairo_fixed_to_double (linear->p2.x) - p1x;
pdy = _cairo_fixed_to_double (linear->p2.y) - p1y;
invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
pdx *= invsqnorm;
pdy *= invsqnorm;
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;
 
t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy;
tdx = extents->width * pdx;
tdy = extents->height * pdy;
 
t[0] = t[1] = t0;
if (tdx < 0)
t[0] += tdx;
else
t[1] += tdx;
 
if (tdy < 0)
t[0] += tdy;
else
t[1] += tdy;
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
_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
{
return linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y;
}
 
static cairo_bool_t
_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial)
{
return radial->r1 == radial->r2 &&
(radial->r1 == 0 /* && radial->r2 == 0 */ ||
(radial->c1.x == radial->c2.x && radial->c1.y == radial->c2.y));
}
 
static cairo_bool_t
_gradient_is_clear (const cairo_gradient_pattern_t *gradient,
const cairo_rectangle_int_t *extents)
{
1786,27 → 2766,40
gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset))
return TRUE;
 
/* Check if the extents intersect the drawn part of the pattern. */
if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
if (gradient->base.extend == CAIRO_EXTEND_NONE) {
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
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 (linear))
if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient))
return TRUE;
}
 
if (extents != NULL) {
/* 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];
_extents_to_linear_parameter (linear, extents, t);
if ((t[0] <= 0.0 && t[1] <= 0.0) || (t[0] >= 1.0 && t[1] >= 1.0))
 
_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;
}
}
} else {
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
/* degenerate radial gradients are clear */
if (_radial_pattern_is_degenerate (radial))
 
if (t[0] == t[1])
return TRUE;
/* TODO: check actual intersection */
}
 
for (i = 0; i < gradient->n_stops; i++)
1927,8 → 2920,144
}
 
/**
* _cairo_gradient_pattern_is_solid
* _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:
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
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.
1966,7 → 3095,13
if (extents == NULL)
return FALSE;
 
_extents_to_linear_parameter (linear, extents, t);
_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;
}
1987,8 → 3122,24
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
* _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
2013,8 → 3164,10
 
static cairo_bool_t
_surface_is_opaque (const cairo_surface_pattern_t *pattern,
const cairo_rectangle_int_t *r)
const cairo_rectangle_int_t *sample)
{
cairo_rectangle_int_t extents;
 
if (pattern->surface->content & CAIRO_CONTENT_ALPHA)
return FALSE;
 
2021,22 → 3174,29
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)
if (sample == NULL)
return FALSE;
 
return _cairo_rectangle_contains_rectangle (&extents, sample);
}
 
static cairo_bool_t
_raster_source_is_opaque (const cairo_raster_source_pattern_t *pattern,
const cairo_rectangle_int_t *sample)
{
if (pattern->content & CAIRO_CONTENT_ALPHA)
return FALSE;
 
if (pattern->base.extend != CAIRO_EXTEND_NONE)
return TRUE;
}
}
 
if (sample == NULL)
return FALSE;
 
return _cairo_rectangle_contains_rectangle (&pattern->extents, sample);
}
 
static cairo_bool_t
2053,8 → 3213,14
}
 
static cairo_bool_t
_raster_source_is_clear (const cairo_raster_source_pattern_t *pattern)
{
return pattern->extents.width == 0 || pattern->extents.height == 0;
}
 
static cairo_bool_t
_gradient_is_opaque (const cairo_gradient_pattern_t *gradient,
const cairo_rectangle_int_t *extents)
const cairo_rectangle_int_t *sample)
{
unsigned int i;
 
2075,10 → 3241,16
if (_linear_pattern_is_degenerate (linear))
return FALSE;
 
if (extents == NULL)
if (sample == NULL)
return FALSE;
 
_extents_to_linear_parameter (linear, extents, t);
_cairo_linear_pattern_box_to_parameter (linear,
sample->x,
sample->y,
sample->x + sample->width,
sample->y + sample->height,
t);
 
if (t[0] < 0.0 || t[1] > 1.0)
return FALSE;
}
2093,7 → 3265,7
}
 
/**
* _cairo_pattern_is_opaque
* _cairo_pattern_is_opaque:
*
* Convenience function to determine whether a pattern is an opaque
* pattern (of any type). The same caveats that apply to
2103,7 → 3275,7
**/
cairo_bool_t
_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern,
const cairo_rectangle_int_t *extents)
const cairo_rectangle_int_t *sample)
{
const cairo_pattern_union_t *pattern;
 
2115,10 → 3287,14
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);
return _surface_is_opaque (&pattern->surface, sample);
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _raster_source_is_opaque (&pattern->raster_source, sample);
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
return _gradient_is_opaque (&pattern->gradient.base, extents);
return _gradient_is_opaque (&pattern->gradient.base, sample);
case CAIRO_PATTERN_TYPE_MESH:
return FALSE;
}
 
ASSERT_NOT_REACHED;
2134,14 → 3310,18
return FALSE;
 
pattern = (cairo_pattern_union_t *) abstract_pattern;
switch (pattern->type) {
switch (abstract_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_RASTER_SOURCE:
return _raster_source_is_clear (&pattern->raster_source);
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;
2161,7 → 3341,7
* 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)
2206,439 → 3386,56
return optimized_filter;
}
 
 
static double
_pixman_nearest_sample (double d)
cairo_filter_t
_cairo_pattern_sampled_area (const cairo_pattern_t *pattern,
const cairo_rectangle_int_t *extents,
cairo_rectangle_int_t *sample)
{
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;
cairo_filter_t filter;
double x1, x2, y1, y2;
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;
filter = _cairo_pattern_analyze_filter (pattern, &pad);
if (pad == 0.0 && _cairo_matrix_is_identity (&pattern->matrix)) {
*sample = *extents;
return filter;
}
 
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;
}
}
x1 = extents->x;
y1 = extents->y;
x2 = extents->x + (int) extents->width;
y2 = extents->y + (int) extents->height;
 
/* 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,
_cairo_matrix_transform_bounding_box (&pattern->matrix,
&x1, &y1, &x2, &y2,
NULL);
}
if (x1 > CAIRO_RECT_INT_MIN)
sample->x = floor (x1 - pad);
else
sample->x = CAIRO_RECT_INT_MIN;
 
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;
if (y1 > CAIRO_RECT_INT_MIN)
sample->y = floor (y1 - pad);
else
sample->y = CAIRO_RECT_INT_MIN;
 
sampled_area.x += tx;
sampled_area.y += ty;
if (x2 < CAIRO_RECT_INT_MAX)
sample->width = ceil (x2 + pad);
else
sample->width = CAIRO_RECT_INT_MAX;
 
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 (y2 < CAIRO_RECT_INT_MAX)
sample->height = ceil (y2 + pad);
else
sample->height = CAIRO_RECT_INT_MAX;
 
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;
}
sample->width -= sample->x;
sample->height -= sample->y;
 
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;
return filter;
}
 
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);
 
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.
2689,6 → 3486,29
}
break;
 
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
{
const cairo_raster_source_pattern_t *raster =
(const cairo_raster_source_pattern_t *) pattern;
double pad;
 
if (raster->extents.width == 0 || raster->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 (pattern, &pad);
x1 = raster->extents.x - pad;
y1 = raster->extents.y - pad;
x2 = raster->extents.x + (int) raster->extents.width + pad;
y2 = raster->extents.y + (int) raster->extents.height + pad;
}
break;
 
case CAIRO_PATTERN_TYPE_RADIAL:
{
const cairo_radial_pattern_t *radial =
2695,40 → 3515,32
(const cairo_radial_pattern_t *) pattern;
double cx1, cy1;
double cx2, cy2;
double r, D;
double r1, r2;
 
if (radial->r1 == 0 && radial->r2 == 0)
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;
}
 
cx1 = _cairo_fixed_to_double (radial->c1.x);
cy1 = _cairo_fixed_to_double (radial->c1.y);
r = _cairo_fixed_to_double (radial->r1);
x1 = cx1 - r; x2 = cx1 + r;
y1 = cy1 - r; y2 = cy1 + r;
 
cx2 = _cairo_fixed_to_double (radial->c2.x);
cy2 = _cairo_fixed_to_double (radial->c2.y);
r = fabs (_cairo_fixed_to_double (radial->r2));
 
/* TODO: in some cases (focus outside/on the circle) it is
* half-bounded. */
if (pattern->extend != CAIRO_EXTEND_NONE)
goto UNBOUNDED;
 
/* We need to be careful, as if the circles are not
* self-contained, then the solution is actually unbounded.
*/
D = (cx1-cx2)*(cx1-cx2) + (cy1-cy2)*(cy1-cy2);
if (D > r*r - 1e-5)
goto UNBOUNDED;
cx1 = radial->cd1.center.x;
cy1 = radial->cd1.center.y;
r1 = radial->cd1.radius;
 
if (cx2 - r < x1)
x1 = cx2 - r;
if (cx2 + r > x2)
x2 = cx2 + r;
cx2 = radial->cd2.center.x;
cy2 = radial->cd2.center.y;
r2 = radial->cd2.radius;
 
if (cy2 - r < y1)
y1 = cy2 - r;
if (cy2 + r > y2)
y2 = cy2 + r;
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;
 
2740,20 → 3552,26
if (pattern->extend != CAIRO_EXTEND_NONE)
goto UNBOUNDED;
 
if (linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y)
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->p1.x == linear->p2.x) {
if (linear->pd1.x == linear->pd2.x) {
x1 = -HUGE_VAL;
x2 = HUGE_VAL;
y1 = _cairo_fixed_to_double (MIN (linear->p1.y, linear->p2.y));
y2 = _cairo_fixed_to_double (MAX (linear->p1.y, linear->p2.y));
} else if (linear->p1.y == linear->p2.y) {
x1 = _cairo_fixed_to_double (MIN (linear->p1.x, linear->p2.x));
x2 = _cairo_fixed_to_double (MAX (linear->p1.x, linear->p2.x));
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 {
2762,6 → 3580,29
}
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;
}
2811,13 → 3652,51
return;
}
 
/**
* _cairo_pattern_get_ink_extents:
*
* Return the "target-space" inked extents of @pattern in @extents.
**/
cairo_int_status_t
_cairo_pattern_get_ink_extents (const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents)
{
if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE &&
pattern->extend == CAIRO_EXTEND_NONE)
{
const cairo_surface_pattern_t *surface_pattern =
(const cairo_surface_pattern_t *) pattern;
cairo_surface_t *surface = surface_pattern->surface;
 
surface = _cairo_surface_get_source (surface, NULL);
if (_cairo_surface_is_recording (surface)) {
cairo_matrix_t imatrix;
cairo_box_t box;
cairo_status_t status;
 
imatrix = pattern->matrix;
status = cairo_matrix_invert (&imatrix);
/* cairo_pattern_set_matrix ensures the matrix is invertible */
assert (status == CAIRO_STATUS_SUCCESS);
 
status = _cairo_recording_surface_get_ink_bbox ((cairo_recording_surface_t *)surface,
&box, &imatrix);
if (unlikely (status))
return status;
 
_cairo_box_round_to_rectangle (&box, extents);
return CAIRO_STATUS_SUCCESS;
}
}
 
_cairo_pattern_get_extents (pattern, extents);
return CAIRO_STATUS_SUCCESS;
}
 
static unsigned long
_cairo_solid_pattern_hash (unsigned long hash,
const cairo_pattern_t *pattern)
const cairo_solid_pattern_t *solid)
{
const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
 
hash = _cairo_hash_bytes (hash, &solid->color, sizeof (solid->color));
 
return hash;
2839,7 → 3718,7
sizeof (double));
hash = _cairo_hash_bytes (hash,
&gradient->stops[n].color,
sizeof (cairo_color_t));
sizeof (cairo_color_stop_t));
}
 
return hash;
2849,8 → 3728,8
_cairo_linear_pattern_hash (unsigned long hash,
const cairo_linear_pattern_t *linear)
{
hash = _cairo_hash_bytes (hash, &linear->p1, sizeof (linear->p1));
hash = _cairo_hash_bytes (hash, &linear->p2, sizeof (linear->p2));
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);
}
2859,25 → 3738,44
_cairo_radial_pattern_hash (unsigned long hash,
const cairo_radial_pattern_t *radial)
{
hash = _cairo_hash_bytes (hash, &radial->c1, sizeof (radial->c1));
hash = _cairo_hash_bytes (hash, &radial->r1, sizeof (radial->r1));
hash = _cairo_hash_bytes (hash, &radial->c2, sizeof (radial->c2));
hash = _cairo_hash_bytes (hash, &radial->r2, sizeof (radial->r2));
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_pattern_t *pattern)
const cairo_surface_pattern_t *surface)
{
const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
 
hash ^= surface->surface->unique_id;
 
return hash;
}
 
static unsigned long
_cairo_raster_source_pattern_hash (unsigned long hash,
const cairo_raster_source_pattern_t *raster)
{
hash ^= (uintptr_t)raster->user_data;
 
return hash;
}
 
unsigned long
_cairo_pattern_hash (const cairo_pattern_t *pattern)
{
2901,13 → 3799,17
 
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_solid_pattern_hash (hash, pattern);
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, pattern);
return _cairo_surface_pattern_hash (hash, (cairo_surface_pattern_t *) pattern);
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _cairo_raster_source_pattern_hash (hash, (cairo_raster_source_pattern_t *) pattern);
default:
ASSERT_NOT_REACHED;
return FALSE;
2914,49 → 3816,10
}
}
 
static unsigned long
_cairo_gradient_pattern_color_stops_size (const cairo_pattern_t *pattern)
{
cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
 
return gradient->n_stops * (sizeof (double) + sizeof (cairo_color_t));
}
 
unsigned long
_cairo_pattern_size (const cairo_pattern_t *pattern)
{
if (pattern->status)
return 0;
 
/* XXX */
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return sizeof (cairo_solid_pattern_t);
break;
case CAIRO_PATTERN_TYPE_SURFACE:
return sizeof (cairo_surface_pattern_t);
break;
case CAIRO_PATTERN_TYPE_LINEAR:
return sizeof (cairo_linear_pattern_t) +
_cairo_gradient_pattern_color_stops_size (pattern);
break;
case CAIRO_PATTERN_TYPE_RADIAL:
return sizeof (cairo_radial_pattern_t) +
_cairo_gradient_pattern_color_stops_size (pattern);
default:
ASSERT_NOT_REACHED;
return 0;
}
}
 
 
static cairo_bool_t
_cairo_solid_pattern_equal (const cairo_pattern_t *A,
const cairo_pattern_t *B)
_cairo_solid_pattern_equal (const cairo_solid_pattern_t *a,
const cairo_solid_pattern_t *b)
{
const cairo_solid_pattern_t *a = (cairo_solid_pattern_t *) A;
const cairo_solid_pattern_t *b = (cairo_solid_pattern_t *) B;
 
return _cairo_color_equal (&a->color, &b->color);
}
 
2983,16 → 3846,16
_cairo_linear_pattern_equal (const cairo_linear_pattern_t *a,
const cairo_linear_pattern_t *b)
{
if (a->p1.x != b->p1.x)
if (a->pd1.x != b->pd1.x)
return FALSE;
 
if (a->p1.y != b->p1.y)
if (a->pd1.y != b->pd1.y)
return FALSE;
 
if (a->p2.x != b->p2.x)
if (a->pd2.x != b->pd2.x)
return FALSE;
 
if (a->p2.y != b->p2.y)
if (a->pd2.y != b->pd2.y)
return FALSE;
 
return _cairo_gradient_color_stops_equal (&a->base, &b->base);
3002,22 → 3865,22
_cairo_radial_pattern_equal (const cairo_radial_pattern_t *a,
const cairo_radial_pattern_t *b)
{
if (a->c1.x != b->c1.x)
if (a->cd1.center.x != b->cd1.center.x)
return FALSE;
 
if (a->c1.y != b->c1.y)
if (a->cd1.center.y != b->cd1.center.y)
return FALSE;
 
if (a->r1 != b->r1)
if (a->cd1.radius != b->cd1.radius)
return FALSE;
 
if (a->c2.x != b->c2.x)
if (a->cd2.center.x != b->cd2.center.x)
return FALSE;
 
if (a->c2.y != b->c2.y)
if (a->cd2.center.y != b->cd2.center.y)
return FALSE;
 
if (a->r2 != b->r2)
if (a->cd2.radius != b->cd2.radius)
return FALSE;
 
return _cairo_gradient_color_stops_equal (&a->base, &b->base);
3024,15 → 3887,42
}
 
static cairo_bool_t
_cairo_surface_pattern_equal (const cairo_pattern_t *A,
const cairo_pattern_t *B)
_cairo_mesh_pattern_equal (const cairo_mesh_pattern_t *a,
const cairo_mesh_pattern_t *b)
{
const cairo_surface_pattern_t *a = (cairo_surface_pattern_t *) A;
const cairo_surface_pattern_t *b = (cairo_surface_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;
}
 
static cairo_bool_t
_cairo_raster_source_pattern_equal (const cairo_raster_source_pattern_t *a,
const cairo_raster_source_pattern_t *b)
{
return a->user_data == b->user_data;
}
 
cairo_bool_t
_cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
{
3061,7 → 3951,8
 
switch (a->type) {
case CAIRO_PATTERN_TYPE_SOLID:
return _cairo_solid_pattern_equal (a, b);
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);
3068,8 → 3959,15
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 (a, b);
return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a,
(cairo_surface_pattern_t *) b);
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
return _cairo_raster_source_pattern_equal ((cairo_raster_source_pattern_t *) a,
(cairo_raster_source_pattern_t *) b);
default:
ASSERT_NOT_REACHED;
return FALSE;
3077,7 → 3975,7
}
 
/**
* cairo_pattern_get_rgba
* 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
3121,7 → 4019,7
}
 
/**
* cairo_pattern_get_surface
* cairo_pattern_get_surface:
* @pattern: a #cairo_pattern_t
* @surface: return value for surface of pattern, or %NULL
*
3154,7 → 4052,7
}
 
/**
* cairo_pattern_get_color_stop_rgba
* 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
3207,7 → 4105,7
}
 
/**
* cairo_pattern_get_color_stop_count
* cairo_pattern_get_color_stop_count:
* @pattern: a #cairo_pattern_t
* @count: return value for the number of color stops, or %NULL
*
3219,7 → 4117,7
* pattern.
*
* Since: 1.4
*/
**/
cairo_status_t
cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern,
int *count)
3240,7 → 4138,7
}
 
/**
* cairo_pattern_get_linear_points
* 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
3269,19 → 4167,19
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 
if (x0)
*x0 = _cairo_fixed_to_double (linear->p1.x);
*x0 = linear->pd1.x;
if (y0)
*y0 = _cairo_fixed_to_double (linear->p1.y);
*y0 = linear->pd1.y;
if (x1)
*x1 = _cairo_fixed_to_double (linear->p2.x);
*x1 = linear->pd2.x;
if (y1)
*y1 = _cairo_fixed_to_double (linear->p2.y);
*y1 = linear->pd2.y;
 
return CAIRO_STATUS_SUCCESS;
}
 
/**
* cairo_pattern_get_radial_circles
* 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
3313,30 → 4211,381
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
 
if (x0)
*x0 = _cairo_fixed_to_double (radial->c1.x);
*x0 = radial->cd1.center.x;
if (y0)
*y0 = _cairo_fixed_to_double (radial->c1.y);
*y0 = radial->cd1.center.y;
if (r0)
*r0 = _cairo_fixed_to_double (radial->r1);
*r0 = radial->cd1.radius;
if (x1)
*x1 = _cairo_fixed_to_double (radial->c2.x);
*x1 = radial->cd2.center.x;
if (y1)
*y1 = _cairo_fixed_to_double (radial->c2.y);
*y1 = radial->cd2.center.y;
if (r1)
*r1 = _cairo_fixed_to_double (radial->r2);
*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 ();
static void
_cairo_debug_print_surface_pattern (FILE *file,
const cairo_surface_pattern_t *pattern)
{
printf (" surface type: %d\n", pattern->surface->type);
}
 
static void
_cairo_debug_print_raster_source_pattern (FILE *file,
const cairo_raster_source_pattern_t *raster)
{
printf (" content: %x, size %dx%d\n", raster->content, raster->extents.width, raster->extents.height);
}
 
static void
_cairo_debug_print_linear_pattern (FILE *file,
const cairo_linear_pattern_t *pattern)
{
}
 
static void
_cairo_debug_print_radial_pattern (FILE *file,
const cairo_radial_pattern_t *pattern)
{
}
 
static void
_cairo_debug_print_mesh_pattern (FILE *file,
const cairo_mesh_pattern_t *pattern)
{
}
 
void
_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern)
{
const char *s;
switch (pattern->type) {
case CAIRO_PATTERN_TYPE_SOLID: s = "solid"; break;
case CAIRO_PATTERN_TYPE_SURFACE: s = "surface"; break;
case CAIRO_PATTERN_TYPE_LINEAR: s = "linear"; break;
case CAIRO_PATTERN_TYPE_RADIAL: s = "radial"; break;
case CAIRO_PATTERN_TYPE_MESH: s = "mesh"; break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE: s = "raster"; break;
default: s = "invalid"; ASSERT_NOT_REACHED; break;
}
 
fprintf (file, "pattern: %s\n", s);
if (pattern->type == CAIRO_PATTERN_TYPE_SOLID)
return;
 
switch (pattern->extend) {
case CAIRO_EXTEND_NONE: s = "none"; break;
case CAIRO_EXTEND_REPEAT: s = "repeat"; break;
case CAIRO_EXTEND_REFLECT: s = "reflect"; break;
case CAIRO_EXTEND_PAD: s = "pad"; break;
default: s = "invalid"; ASSERT_NOT_REACHED; break;
}
fprintf (file, " extend: %s\n", s);
 
switch (pattern->filter) {
case CAIRO_FILTER_FAST: s = "fast"; break;
case CAIRO_FILTER_GOOD: s = "good"; break;
case CAIRO_FILTER_BEST: s = "best"; break;
case CAIRO_FILTER_NEAREST: s = "nearest"; break;
case CAIRO_FILTER_BILINEAR: s = "bilinear"; break;
case CAIRO_FILTER_GAUSSIAN: s = "guassian"; break;
default: s = "invalid"; ASSERT_NOT_REACHED; break;
}
fprintf (file, " filter: %s\n", s);
fprintf (file, " matrix: [%g %g %g %g %g %g]\n",
pattern->matrix.xx, pattern->matrix.yx,
pattern->matrix.xy, pattern->matrix.yy,
pattern->matrix.x0, pattern->matrix.y0);
switch (pattern->type) {
default:
case CAIRO_PATTERN_TYPE_SOLID:
break;
case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
_cairo_debug_print_raster_source_pattern (file, (cairo_raster_source_pattern_t *)pattern);
break;
case CAIRO_PATTERN_TYPE_SURFACE:
_cairo_debug_print_surface_pattern (file, (cairo_surface_pattern_t *)pattern);
break;
case CAIRO_PATTERN_TYPE_LINEAR:
_cairo_debug_print_linear_pattern (file, (cairo_linear_pattern_t *)pattern);
break;
case CAIRO_PATTERN_TYPE_RADIAL:
_cairo_debug_print_radial_pattern (file, (cairo_radial_pattern_t *)pattern);
break;
case CAIRO_PATTERN_TYPE_MESH:
_cairo_debug_print_mesh_pattern (file, (cairo_mesh_pattern_t *)pattern);
break;
}
}