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 (); |
* |
* /* Add a Coons patch */ |
* 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); |
* |
* /* Add a Gouraud-shaded triangle */ |
* 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 **) ¤t_patch); |
if (unlikely (status)) { |
_cairo_pattern_set_error (pattern, status); |
return; |
} |
|
mesh->current_patch = current_patch; |
mesh->current_side = -2; /* no current point */ |
|
for (i = 0; i < 4; i++) |
mesh->has_control_point[i] = FALSE; |
|
for (i = 0; i < 4; i++) |
mesh->has_color[i] = FALSE; |
} |
|
|
static void |
_calc_control_point (cairo_mesh_patch_t *patch, int control_point) |
{ |
/* The Coons patch is a special case of the Tensor Product patch |
* where the four control points are: |
* |
* P11 = S(1/3, 1/3) |
* P12 = S(1/3, 2/3) |
* P21 = S(2/3, 1/3) |
* P22 = S(2/3, 2/3) |
* |
* where S is the gradient surface. |
* |
* When one or more control points has not been specified |
* calculated the Coons patch control points are substituted. If |
* no control points are specified the gradient will be a Coons |
* patch. |
* |
* The equations below are defined in the ISO32000 standard. |
*/ |
cairo_point_double_t *p[3][3]; |
int cp_i, cp_j, i, j; |
|
cp_i = mesh_control_point_i[control_point]; |
cp_j = mesh_control_point_j[control_point]; |
|
for (i = 0; i < 3; i++) |
for (j = 0; j < 3; j++) |
p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j]; |
|
p[0][0]->x = (- 4 * p[1][1]->x |
+ 6 * (p[1][0]->x + p[0][1]->x) |
- 2 * (p[1][2]->x + p[2][1]->x) |
+ 3 * (p[2][0]->x + p[0][2]->x) |
- 1 * p[2][2]->x) * (1. / 9); |
|
p[0][0]->y = (- 4 * p[1][1]->y |
+ 6 * (p[1][0]->y + p[0][1]->y) |
- 2 * (p[1][2]->y + p[2][1]->y) |
+ 3 * (p[2][0]->y + p[0][2]->y) |
- 1 * p[2][2]->y) * (1. / 9); |
} |
|
/** |
* cairo_mesh_pattern_end_patch: |
* @pattern: a #cairo_pattern_t |
* |
* Indicates the end of the current patch in a mesh pattern. |
* |
* If the current patch has less than 4 sides, it is closed with a |
* straight line from the current point to the first point of the |
* patch as if cairo_mesh_pattern_line_to() was used. |
* |
* Note: If @pattern is not a mesh pattern then @pattern will be put |
* into an error status with a status of |
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current |
* patch or the current patch has no current point, @pattern will be |
* put into an error status with a status of |
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
* |
* Since: 1.12 |
**/ |
void |
cairo_mesh_pattern_end_patch (cairo_pattern_t *pattern) |
{ |
cairo_mesh_pattern_t *mesh; |
cairo_mesh_patch_t *current_patch; |
int i; |
|
if (unlikely (pattern->status)) |
return; |
|
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
return; |
} |
|
mesh = (cairo_mesh_pattern_t *) pattern; |
current_patch = mesh->current_patch; |
if (unlikely (!current_patch)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
return; |
} |
|
if (unlikely (mesh->current_side == -2)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
return; |
} |
|
while (mesh->current_side < 3) { |
int corner_num; |
|
cairo_mesh_pattern_line_to (pattern, |
current_patch->points[0][0].x, |
current_patch->points[0][0].y); |
|
corner_num = mesh->current_side + 1; |
if (corner_num < 4 && ! mesh->has_color[corner_num]) { |
current_patch->colors[corner_num] = current_patch->colors[0]; |
mesh->has_color[corner_num] = TRUE; |
} |
} |
|
for (i = 0; i < 4; i++) { |
if (! mesh->has_control_point[i]) |
_calc_control_point (current_patch, i); |
} |
|
for (i = 0; i < 4; i++) { |
if (! mesh->has_color[i]) |
current_patch->colors[i] = *CAIRO_COLOR_TRANSPARENT; |
} |
|
mesh->current_patch = NULL; |
} |
|
/** |
* cairo_mesh_pattern_curve_to: |
* @pattern: a #cairo_pattern_t |
* @x1: the X coordinate of the first control point |
* @y1: the Y coordinate of the first control point |
* @x2: the X coordinate of the second control point |
* @y2: the Y coordinate of the second control point |
* @x3: the X coordinate of the end of the curve |
* @y3: the Y coordinate of the end of the curve |
* |
* Adds a cubic Bézier spline to the current patch from the current |
* point to position (@x3, @y3) in pattern-space coordinates, using |
* (@x1, @y1) and (@x2, @y2) as the control points. |
* |
* If the current patch has no current point before the call to |
* cairo_mesh_pattern_curve_to(), this function will behave as if |
* preceded by a call to cairo_mesh_pattern_move_to(@pattern, @x1, |
* @y1). |
* |
* After this call the current point will be (@x3, @y3). |
* |
* Note: If @pattern is not a mesh pattern then @pattern will be put |
* into an error status with a status of |
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current |
* patch or the current patch already has 4 sides, @pattern will be |
* put into an error status with a status of |
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
* |
* Since: 1.12 |
**/ |
void |
cairo_mesh_pattern_curve_to (cairo_pattern_t *pattern, |
double x1, double y1, |
double x2, double y2, |
double x3, double y3) |
{ |
cairo_mesh_pattern_t *mesh; |
int current_point, i, j; |
|
if (unlikely (pattern->status)) |
return; |
|
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
return; |
} |
|
mesh = (cairo_mesh_pattern_t *) pattern; |
if (unlikely (!mesh->current_patch)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
return; |
} |
|
if (unlikely (mesh->current_side == 3)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
return; |
} |
|
if (mesh->current_side == -2) |
cairo_mesh_pattern_move_to (pattern, x1, y1); |
|
assert (mesh->current_side >= -1); |
assert (pattern->status == CAIRO_STATUS_SUCCESS); |
|
mesh->current_side++; |
|
current_point = 3 * mesh->current_side; |
|
current_point++; |
i = mesh_path_point_i[current_point]; |
j = mesh_path_point_j[current_point]; |
mesh->current_patch->points[i][j].x = x1; |
mesh->current_patch->points[i][j].y = y1; |
|
current_point++; |
i = mesh_path_point_i[current_point]; |
j = mesh_path_point_j[current_point]; |
mesh->current_patch->points[i][j].x = x2; |
mesh->current_patch->points[i][j].y = y2; |
|
current_point++; |
if (current_point < 12) { |
i = mesh_path_point_i[current_point]; |
j = mesh_path_point_j[current_point]; |
mesh->current_patch->points[i][j].x = x3; |
mesh->current_patch->points[i][j].y = y3; |
} |
} |
slim_hidden_def (cairo_mesh_pattern_curve_to); |
|
/** |
* cairo_mesh_pattern_line_to: |
* @pattern: a #cairo_pattern_t |
* @x: the X coordinate of the end of the new line |
* @y: the Y coordinate of the end of the new line |
* |
* Adds a line to the current patch from the current point to position |
* (@x, @y) in pattern-space coordinates. |
* |
* If there is no current point before the call to |
* cairo_mesh_pattern_line_to() this function will behave as |
* cairo_mesh_pattern_move_to(@pattern, @x, @y). |
* |
* After this call the current point will be (@x, @y). |
* |
* Note: If @pattern is not a mesh pattern then @pattern will be put |
* into an error status with a status of |
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current |
* patch or the current patch already has 4 sides, @pattern will be |
* put into an error status with a status of |
* %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION. |
* |
* Since: 1.12 |
**/ |
void |
cairo_mesh_pattern_line_to (cairo_pattern_t *pattern, |
double x, double y) |
{ |
cairo_mesh_pattern_t *mesh; |
cairo_point_double_t last_point; |
int last_point_idx, i, j; |
|
if (unlikely (pattern->status)) |
return; |
|
if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH); |
return; |
} |
|
mesh = (cairo_mesh_pattern_t *) pattern; |
if (unlikely (!mesh->current_patch)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
return; |
} |
|
if (unlikely (mesh->current_side == 3)) { |
_cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION); |
return; |
} |
|
if (mesh->current_side == -2) { |
cairo_mesh_pattern_move_to (pattern, x, y); |
return; |
} |
|
last_point_idx = 3 * (mesh->current_side + 1); |
i = mesh_path_point_i[last_point_idx]; |
j = mesh_path_point_j[last_point_idx]; |
|
last_point = mesh->current_patch->points[i][j]; |
|
cairo_mesh_pattern_curve_to (pattern, |
(2 * last_point.x + x) * (1. / 3), |
(2 * last_point.y + y) * (1. / 3), |
(last_point.x + 2 * x) * (1. / 3), |
(last_point.y + 2 * y) * (1. / 3), |
x, y); |
} |
slim_hidden_def (cairo_mesh_pattern_line_to); |
|
/** |
* cairo_mesh_pattern_move_to: |
* @pattern: a #cairo_pattern_t |
* @x: the X coordinate of the new position |
* @y: the Y coordinate of the new position |
* |
* Define the first point of the current patch in a mesh pattern. |
* |
* After this call the current point will be (@x, @y). |
* |
* Note: If @pattern is not a mesh pattern then @pattern will be put |
* into an error status with a status of |
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current |
* patch or the current patch already has at 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; |
} |
} |