#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
/** * SECTION:cairo-pattern * @Title: cairo_pattern_t * @Short_Description: Sources for drawing * @See_Also: #cairo_t, #cairo_surface_t * * #cairo_pattern_t is the paint with which cairo draws. * The primary use of patterns is as the source for all cairo drawing * operations, although they can also be used as masks, that is, as the * brush too. * * A cairo pattern is created by using one of the many constructors, * of the form * <function>cairo_pattern_create_<emphasis>type</emphasis>()</function> * or implicitly through * <function>cairo_set_source_<emphasis>type</emphasis>()</function> * functions.
**/
/** * _cairo_pattern_set_error: * @pattern: a pattern * @status: a status value indicating an error * * Atomically sets pattern->status to @status and calls _cairo_error; * Does nothing if status is %CAIRO_STATUS_SUCCESS. * * All assignments of an error status to pattern->status should happen * through _cairo_pattern_set_error(). Note that due to the nature of * the atomic operation, it is not safe to call this function on the nil * objects. * * The purpose of this function is to allow the user to set a * breakpoint in _cairo_error() to generate a stack trace for when the * user causes cairo to detect an error.
**/ static cairo_status_t
_cairo_pattern_set_error (cairo_pattern_t *pattern,
cairo_status_t status)
{ if (status == CAIRO_STATUS_SUCCESS) return status;
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. */
_cairo_status_set_error (&pattern->status, status);
return _cairo_error (status);
}
void
_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
{ #if HAVE_VALGRIND switch (type) { case CAIRO_PATTERN_TYPE_SOLID:
VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_solid_pattern_t)); break; case CAIRO_PATTERN_TYPE_SURFACE:
VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_surface_pattern_t)); break; case CAIRO_PATTERN_TYPE_LINEAR:
VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_linear_pattern_t)); break; case CAIRO_PATTERN_TYPE_RADIAL:
VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t)); break; case CAIRO_PATTERN_TYPE_MESH:
VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)); break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE: break;
} #endif
/* Set the reference count to zero for on-stack patterns.
* Callers needs to explicitly increment the count for heap allocations. */
CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
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. */
CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 0);
_cairo_user_data_array_init (&pattern->user_data);
cairo_list_init (&pattern->observers);
return CAIRO_STATUS_SUCCESS;
}
void
_cairo_pattern_init_static_copy (cairo_pattern_t *pattern, const cairo_pattern_t *other)
{ int size;
assert (other->status == CAIRO_STATUS_SUCCESS);
switch (other->type) { default:
ASSERT_NOT_REACHED; case CAIRO_PATTERN_TYPE_SOLID:
size = sizeof (cairo_solid_pattern_t); break; case CAIRO_PATTERN_TYPE_SURFACE:
size = sizeof (cairo_surface_pattern_t); break; case CAIRO_PATTERN_TYPE_LINEAR:
size = sizeof (cairo_linear_pattern_t); break; case CAIRO_PATTERN_TYPE_RADIAL:
size = sizeof (cairo_radial_pattern_t); break; case CAIRO_PATTERN_TYPE_MESH:
size = sizeof (cairo_mesh_pattern_t); break; case CAIRO_PATTERN_TYPE_RASTER_SOURCE:
size = sizeof (cairo_raster_source_pattern_t); break;
}
/* We don't bother doing any fancy copy-on-write implementation
* for the pattern's data. It's generally quite tiny. */
status = _cairo_pattern_init_copy (pattern, other); if (unlikely (status)) return status;
/* But we do let the surface snapshot stuff be as fancy as it
* would like to be. */ if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
cairo_surface_pattern_t *surface_pattern =
(cairo_surface_pattern_t *) pattern;
cairo_surface_t *surface = surface_pattern->surface;
if (status == CAIRO_STATUS_NO_MEMORY) return (cairo_pattern_t *)&_cairo_pattern_nil.base;
CAIRO_MUTEX_INITIALIZE ();
pattern = _cairo_pattern_create_solid (CAIRO_COLOR_BLACK); if (pattern->status == CAIRO_STATUS_SUCCESS)
status = _cairo_pattern_set_error (pattern, status);
return pattern;
}
/** * cairo_pattern_create_rgb: * @red: red component of the color * @green: green component of the color * @blue: blue component of the color * * Creates a new #cairo_pattern_t corresponding to an opaque color. The * color components are floating point numbers in the range 0 to 1. * If the values passed in are outside that range, they will be * clamped. * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_rgb (double red, double green, double blue)
{ return cairo_pattern_create_rgba (red, green, blue, 1.0);
}
/** * cairo_pattern_create_rgba: * @red: red component of the color * @green: green component of the color * @blue: blue component of the color * @alpha: alpha component of the color * * Creates a new #cairo_pattern_t corresponding to a translucent color. * The color components are floating point numbers in the range 0 to * 1. If the values passed in are outside that range, they will be * clamped. * * The color is specified in the same way as in cairo_set_source_rgb(). * * 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.0
**/
cairo_pattern_t *
cairo_pattern_create_rgba (double red, double green, double blue, double alpha)
{
cairo_color_t color;
red = _cairo_restrict_value (red, 0.0, 1.0);
green = _cairo_restrict_value (green, 0.0, 1.0);
blue = _cairo_restrict_value (blue, 0.0, 1.0);
alpha = _cairo_restrict_value (alpha, 0.0, 1.0);
/** * cairo_pattern_create_for_surface: * @surface: the surface * * Create a new #cairo_pattern_t for the given surface. * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_for_surface (cairo_surface_t *surface)
{
cairo_surface_pattern_t *pattern;
/** * cairo_pattern_create_linear: * @x0: x coordinate of the start point * @y0: y coordinate of the start point * @x1: x coordinate of the end point * @y1: y coordinate of the end point * * Create a new linear gradient #cairo_pattern_t along the line defined * by (x0, y0) and (x1, y1). Before using the gradient pattern, a * number of color stops should be defined using * cairo_pattern_add_color_stop_rgb() or * cairo_pattern_add_color_stop_rgba(). * * Note: The coordinates here are in pattern space. For a new pattern, * pattern space is identical to user space, but the relationship * between the spaces can be changed with cairo_pattern_set_matrix(). * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
{
cairo_linear_pattern_t *pattern;
/** * cairo_pattern_create_radial: * @cx0: x coordinate for the center of the start circle * @cy0: y coordinate for the center of the start circle * @radius0: radius of the start circle * @cx1: x coordinate for the center of the end circle * @cy1: y coordinate for the center of the end circle * @radius1: radius of the end circle * * Creates a new radial gradient #cairo_pattern_t between the two * circles defined by (cx0, cy0, radius0) and (cx1, cy1, radius1). Before using the * gradient pattern, a number of color stops should be defined using * cairo_pattern_add_color_stop_rgb() or * cairo_pattern_add_color_stop_rgba(). * * Note: The coordinates here are in pattern space. For a new pattern, * pattern space is identical to user space, but the relationship * between the spaces can be changed with cairo_pattern_set_matrix(). * * Return value: the newly created #cairo_pattern_t if successful, or * an error pattern in case of no memory. The caller owns the * returned object and should call cairo_pattern_destroy() when * finished with it. * * This function will always return a valid pointer, but if an error * occurred the pattern status will be set to an error. To inspect * the status of a pattern use cairo_pattern_status(). * * Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_create_radial (double cx0, double cy0, double radius0, double cx1, double cy1, double radius1)
{
cairo_radial_pattern_t *pattern;
/* This order is specified in the diagram in the documentation for
* cairo_pattern_create_mesh() */ staticconstint mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 }; staticconstint mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 }; staticconstint mesh_control_point_i[4] = { 1, 1, 2, 2 }; staticconstint 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;
/** * cairo_pattern_reference: * @pattern: a #cairo_pattern_t * * Increases the reference count on @pattern by one. This prevents * @pattern from being destroyed until a matching call to * cairo_pattern_destroy() is made. * * Use cairo_pattern_get_reference_count() to get the number of * references to a #cairo_pattern_t. * * Return value: the referenced #cairo_pattern_t. * * Since: 1.0
**/
cairo_pattern_t *
cairo_pattern_reference (cairo_pattern_t *pattern)
{ if (pattern == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return pattern;
/** * cairo_pattern_get_type: * @pattern: a #cairo_pattern_t * * Get the pattern's type. See #cairo_pattern_type_t for available * types. * * Return value: The type of @pattern. * * Since: 1.2
**/
cairo_pattern_type_t
cairo_pattern_get_type (cairo_pattern_t *pattern)
{ return pattern->type;
}
/** * cairo_pattern_status: * @pattern: a #cairo_pattern_t * * Checks whether an error has previously occurred for this * pattern. * * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, * %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)
{ return pattern->status;
}
/** * cairo_pattern_destroy: * @pattern: a #cairo_pattern_t * * Decreases the reference count on @pattern by one. If the result is * zero, then @pattern and all associated resources are freed. See * cairo_pattern_reference(). * * Since: 1.0
**/ void
cairo_pattern_destroy (cairo_pattern_t *pattern)
{
cairo_pattern_type_t type;
if (pattern == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return;
if (! _cairo_reference_count_dec_and_test (&pattern->ref_count)) return;
type = pattern->type;
_cairo_pattern_fini (pattern);
/* maintain a small cache of freed patterns */ if (type < ARRAY_LENGTH (freed_pattern_pool))
_freed_pool_put (&freed_pattern_pool[type], pattern); else
free (pattern);
}
/** * cairo_pattern_get_reference_count: * @pattern: a #cairo_pattern_t * * Returns the current reference count of @pattern. * * Return value: the current reference count of @pattern. If the * object is a nil object, 0 will be returned. * * Since: 1.4
**/ unsignedint
cairo_pattern_get_reference_count (cairo_pattern_t *pattern)
{ if (pattern == NULL ||
CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return 0;
/** * cairo_pattern_get_user_data: * @pattern: a #cairo_pattern_t * @key: the address of the #cairo_user_data_key_t the user data was * attached to * * Return user data previously attached to @pattern using the * specified key. If no user data has been attached with the given * key this function returns %NULL. * * Return value: the user data previously attached or %NULL. * * Since: 1.4
**/ void *
cairo_pattern_get_user_data (cairo_pattern_t *pattern, const cairo_user_data_key_t *key)
{ return _cairo_user_data_array_get_data (&pattern->user_data,
key);
}
/** * cairo_pattern_set_user_data: * @pattern: a #cairo_pattern_t * @key: the address of a #cairo_user_data_key_t to attach the user data to * @user_data: the user data to attach to the #cairo_pattern_t * @destroy: a #cairo_destroy_func_t which will be called when the * #cairo_t is destroyed or when new user data is attached using the * same key. * * Attach user data to @pattern. To remove user data from a surface, * call this function with the key that was used to set it and %NULL * for @data. * * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY if a * slot could not be allocated for the user data. * * Since: 1.4
**/
cairo_status_t
cairo_pattern_set_user_data (cairo_pattern_t *pattern, const cairo_user_data_key_t *key, void *user_data,
cairo_destroy_func_t destroy)
{ if (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count)) return pattern->status;
/** * 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;
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;
}
staticvoid
_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;
/** * 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;
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;
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;
}
}
/** * 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;
/** * 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;
/** * 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, unsignedint point_num, double x, double y)
{
cairo_mesh_pattern_t *mesh; int i, j;
/* make room for at least one more color stop */ static cairo_status_t
_cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
{
cairo_gradient_stop_t *new_stops; int old_size = pattern->stops_size; int embedded_size = ARRAY_LENGTH (pattern->stops_embedded); int new_size = 2 * MAX (old_size, 4);
/* we have a local buffer at pattern->stops_embedded. try to fulfill the request
* from there. */ if (old_size < embedded_size) {
pattern->stops = pattern->stops_embedded;
pattern->stops_size = embedded_size; return CAIRO_STATUS_SUCCESS;
}
if (CAIRO_INJECT_FAULT ()) return _cairo_error (CAIRO_STATUS_NO_MEMORY);
/** * 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, unsignedint 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, unsignedint corner_num, double red, double green, double blue, double alpha)
{
cairo_mesh_pattern_t *mesh;
/** * cairo_pattern_add_color_stop_rgb: * @pattern: a #cairo_pattern_t * @offset: an offset in the range [0.0 .. 1.0] * @red: red component of color * @green: green component of color * @blue: blue component of color * * Adds an opaque color stop to a gradient pattern. The offset * specifies the location along the gradient's control vector. For * example, a linear gradient's control vector is from (x0,y0) to * (x1,y1) while a radial gradient's control vector is from any point * on the start circle to the corresponding point on the end circle. * * The color is specified in the same way as in cairo_set_source_rgb(). * * If two (or more) stops are specified with identical offset values, * they will be sorted according to the order in which the stops are * added, (stops added earlier will compare less than stops added * later). This can be useful for reliably making sharp color * transitions instead of the typical blend. * * * Note: If the pattern is not a gradient pattern, (eg. a linear or * radial pattern), then the pattern will be put into an error status * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. * * Since: 1.0
**/ void
cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern, double offset, double red, double green, double blue)
{
cairo_pattern_add_color_stop_rgba (pattern, offset, red, green, blue, 1.0);
}
/** * cairo_pattern_add_color_stop_rgba: * @pattern: a #cairo_pattern_t * @offset: an offset in the range [0.0 .. 1.0] * @red: red component of color * @green: green component of color * @blue: blue component of color * @alpha: alpha component of color * * Adds a translucent color stop to a gradient pattern. The offset * specifies the location along the gradient's control vector. For * example, a linear gradient's control vector is from (x0,y0) to * (x1,y1) while a radial gradient's control vector is from any point * on the start circle to the corresponding point on the end circle. * * The color is specified in the same way as in cairo_set_source_rgba(). * * If two (or more) stops are specified with identical offset values, * they will be sorted according to the order in which the stops are * added, (stops added earlier will compare less than stops added * later). This can be useful for reliably making sharp color * transitions instead of the typical blend. * * Note: If the pattern is not a gradient pattern, (eg. a linear or * radial pattern), then the pattern will be put into an error status * with a status of %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. * * Since: 1.0
**/ void
cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern, double offset, double red, double green, double blue, double alpha)
{ if (pattern->status) return;
/** * cairo_pattern_set_matrix: * @pattern: a #cairo_pattern_t * @matrix: a #cairo_matrix_t * * Sets the pattern's transformation matrix to @matrix. This matrix is * a transformation from user space to pattern space. * * When a pattern is first created it always has the identity matrix * for its transformation matrix, which means that pattern space is * initially identical to user space. * * Important: Please note that the direction of this transformation * matrix is from user space to pattern space. This means that if you * imagine the flow from a pattern to user space (and on to device * space), then coordinates in that flow will be transformed by the * inverse of the pattern matrix. * * For example, if you want to make a pattern appear twice as large as * it does by default the correct code to use is: * * <informalexample><programlisting> * cairo_matrix_init_scale (&matrix, 0.5, 0.5); * cairo_pattern_set_matrix (pattern, &matrix); * </programlisting></informalexample> * * Meanwhile, using values of 2.0 rather than 0.5 in the code above * would cause the pattern to appear at half of its default size. * * Also, please note the discussion of the user-space locking * semantics of cairo_set_source(). * * Since: 1.0
**/ void
cairo_pattern_set_matrix (cairo_pattern_t *pattern, const cairo_matrix_t *matrix)
{
cairo_matrix_t inverse;
cairo_status_t status;
if (pattern->status) return;
if (memcmp (&pattern->matrix, matrix, sizeof (cairo_matrix_t)) == 0) return;
inverse = *matrix;
status = cairo_matrix_invert (&inverse); if (unlikely (status))
status = _cairo_pattern_set_error (pattern, status);
}
/** * cairo_pattern_get_matrix: * @pattern: a #cairo_pattern_t * @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)
{
*matrix = pattern->matrix;
}
/** * cairo_pattern_set_filter: * @pattern: a #cairo_pattern_t * @filter: a #cairo_filter_t describing the filter to use for resizing * the pattern * * Sets the filter to be used for resizing when using this pattern. * See #cairo_filter_t for details on each filter. * * * Note that you might want to control filtering even when you do not * have an explicit #cairo_pattern_t object, (for example when using * cairo_set_source_surface()). In these cases, it is convenient to * use cairo_get_source() to get access to the pattern that cairo * creates implicitly. For example: * * <informalexample><programlisting> * cairo_set_source_surface (cr, image, x, y); * cairo_pattern_set_filter (cairo_get_source (cr), CAIRO_FILTER_NEAREST); * </programlisting></informalexample> * * Since: 1.0
**/ void
cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
{ if (pattern->status) return;
/** * cairo_pattern_get_filter: * @pattern: a #cairo_pattern_t * * Gets the current filter for a pattern. See #cairo_filter_t * for details on each filter. * * Return value: the current filter used for resizing the pattern. * * Since: 1.0
**/
cairo_filter_t
cairo_pattern_get_filter (cairo_pattern_t *pattern)
{ return pattern->filter;
}
/** * cairo_pattern_get_dither: * @pattern: a #cairo_pattern_t * * Gets the current dithering mode, as set by * cairo_pattern_set_dither(). * * Return value: the current dithering mode. * * Since: 1.18
**/
cairo_dither_t
cairo_pattern_get_dither (cairo_pattern_t *pattern)
{ return pattern->dither;
}
/** * cairo_pattern_set_dither: * @pattern: a #cairo_pattern_t * @dither: a #cairo_dither_t describing the new dithering mode * * Set the dithering mode of the rasterizer used for drawing shapes. * This value is a hint, and a particular backend may or may not support * a particular value. At the current time, only pixman is supported. * * Since: 1.18
**/ void
cairo_pattern_set_dither (cairo_pattern_t *pattern, cairo_dither_t dither)
{ if (pattern->status) return;
/** * cairo_pattern_set_extend: * @pattern: a #cairo_pattern_t * @extend: a #cairo_extend_t describing how the area outside of the * pattern will be drawn * * Sets the mode to be used for drawing outside the area of a pattern. * See #cairo_extend_t for details on the semantics of each extend * strategy. * * The default extend mode is %CAIRO_EXTEND_NONE for surface patterns * and %CAIRO_EXTEND_PAD for gradient patterns. * * Since: 1.0
**/ void
cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
{ if (pattern->status) return;
/** * cairo_pattern_get_extend: * @pattern: a #cairo_pattern_t * * Gets the current extend mode for a pattern. See #cairo_extend_t * for details on the semantics of each extend strategy. * * Return value: the current extend strategy used for drawing the * pattern. * * Since: 1.0
**/
cairo_extend_t
cairo_pattern_get_extend (cairo_pattern_t *pattern)
{ return pattern->extend;
}
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 ().
*/
/* * Linear gradients are othrogonal to the line passing through * their extremes. Because of convexity, the parameter range can * be computed as the convex hull (one the real line) of the * parameter values of the 4 corners of the box. * * The parameter value t for a point (x,y) can be computed as: * * t = (p2 - p1) . (x,y) / |p2 - p1|^2 * * t0 is the t value for the top left corner * tdx is the difference between left and right corners * tdy is the difference between top and bottom corners
*/
/* * 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.
*/
/* * _cairo_radial_pattern_focus_is_inside: * * 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. *
*/
cairo_bool_t
_cairo_radial_pattern_focus_is_inside (const cairo_radial_pattern_t *radial)
{ double cx, cy, cr, dx, dy, dr;
/* enlarge boundaries slightly to avoid rounding problems in the
* parameter range computation */
x0 -= DBL_EPSILON;
y0 -= DBL_EPSILON;
x1 += DBL_EPSILON;
y1 += DBL_EPSILON;
/* enlarge boundaries even more to avoid rounding problems when
* testing if a point belongs to the box */
minx = x0 - DBL_EPSILON;
miny = y0 - DBL_EPSILON;
maxx = x1 + DBL_EPSILON;
maxy = y1 + DBL_EPSILON;
/* we don't allow negative radiuses, so we will be checking that
* t*dr >= mindr to consider t valid */
mindr = -(cr + DBL_EPSILON);
/* * After the previous transformations, the start circle is * centered in the origin and has radius cr. A 1-unit change in * the t parameter corresponds to dx,dy,dr changes in the x,y,r of * the circle (center coordinates, radius). * * To compute the minimum range needed to correctly draw the * pattern, we start with an empty range and extend it to include * the circles touching the bounding box or within it.
*/
/* * Focus, the point where the circle has radius == 0. * * r = cr + t * dr = 0 * t = -cr / dr * * If the radius is constant (dr == 0) there is no focus (the * gradient represents a cylinder instead of a cone).
*/ if (fabs (dr) >= DBL_EPSILON) { double t_focus;
/* * Circles externally tangent to box edges. * * All circles have center in (dx, dy) * t * * If the circle is tangent to the line defined by the edge of the * box, then at least one of the following holds true: * * (dx*t) + (cr + dr*t) == x0 (left edge) * (dx*t) - (cr + dr*t) == x1 (right edge) * (dy*t) + (cr + dr*t) == y0 (top edge) * (dy*t) - (cr + dr*t) == y1 (bottom edge) * * The solution is only valid if the tangent point is actually on * the edge, i.e. if its y coordinate is in [y0,y1] for left/right * edges and if its x coordinate is in [x0,x1] for top/bottom * edges. * * For the first equation: * * (dx + dr) * t = x0 - cr * t = (x0 - cr) / (dx + dr) * y = dy * t * * in the code this becomes: * * t_edge = (num) / (den) * v = (delta) * t_edge * * If the denominator in t is 0, the pattern is tangent to a line * parallel to the edge under examination. The corner-case where * the boundary line is the same as the edge is handled by the * focus point case and/or by the a==0 case.
*/ #define T_EDGE(num,den,delta,lower,upper) \ if (fabs (den) >= DBL_EPSILON) { \ double t_edge, v; \
\
t_edge = (num) / (den); \
v = t_edge * (delta); \ if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) \
valid = _extend_range (range, t_edge, valid); \
}
/* * Circles passing through a corner. * * A circle passing through the point (x,y) satisfies: * * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2 * * If we set: * a = dx^2 + dy^2 - dr^2 * b = x*dx + y*dy + cr*dr * c = x^2 + y^2 - cr^2 * we have: * a*t^2 - 2*b*t + c == 0
*/
a = dx * dx + dy * dy - dr * dr; if (fabs (a) < DBL_EPSILON * DBL_EPSILON) { double b, maxd2;
/* Ensure that gradients with both a and dr small are * considered degenerate. * The floating point version of the degeneracy test implemented * in _radial_pattern_is_degenerate() is: * * 1) The circles are practically the same size: * |dr| < DBL_EPSILON * AND * 2a) The circles are both very small: * min (r0, r1) < DBL_EPSILON * OR * 2b) The circles are very close to each other: * max (|dx|, |dy|) < 2 * DBL_EPSILON * * Assuming that the gradient is not degenerate, we want to * show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON. * * If the gradient is not degenerate yet it has |dr| < * DBL_EPSILON, (2b) is false, thus: * * max (|dx|, |dy|) >= 2*DBL_EPSILON * which implies: * 4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2 * * From the definition of a, we get: * a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2 * dx^2 + dy^2 - DBL_EPSILON^2 < dr^2 * 3*DBL_EPSILON^2 < dr^2 * * which is inconsistent with the hypotheses, thus |dr| < * DBL_EPSILON is false or the gradient is degenerate.
*/
assert (fabs (dr) >= DBL_EPSILON);
/* * If a == 0, all the circles are tangent to a line in the * focus point. If this line is within the box extents, we * should add the circle with infinite radius, but this would * make the range unbounded, so we add the smallest circle whose * distance to the desired (degenerate) circle within the * bounding box does not exceed tolerance. * * The equation of the line is b==0, i.e.: * x*dx + y*dy + cr*dr == 0 * * We compute the intersection of the line with the box and * keep the intersection with maximum square distance (maxd2) * from the focus point. * * In the code the intersection is represented in another * coordinate system, whose origin is the focus point and * which has a u,v axes, which are respectively orthogonal and * parallel to the edge being intersected. * * The intersection is valid only if it belongs to the box, * otherwise it is ignored. * * For example: * * y = y0 * x*dx + y0*dy + cr*dr == 0 * x = -(y0*dy + cr*dr) / dx * * which in (u,v) is: * u = y0 - y_focus * v = -(y0*dy + cr*dr) / dx - x_focus * * In the code: * u = (edge) - (u_origin) * v = -((edge) * (delta) + cr*dr) / (den) - v_focus
*/ #define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin) \ if (fabs (den) >= DBL_EPSILON) { \ double v; \
\
v = -((edge) * (delta) + cr * dr) / (den); \ if ((lower) <= v && v <= (upper)) { \ double u, d2; \
\
u = (edge) - (u_origin); \
v -= (v_origin); \
d2 = u*u + v*v; \ if (maxd2 < d2) \
maxd2 = d2; \
} \
}
/* * The limit circle can be transformed rigidly to the y=0 line * and the circles tangent to it in (0,0) are: * * x^2 + (y-r)^2 = r^2 <=> x^2 + y^2 - 2*y*r = 0 * * y is the distance from the line, in our case tolerance; * x is the distance along the line, i.e. sqrt(maxd2), * so: * * r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance) * t = (r - cr) / dr = * (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr)
*/ if (maxd2 > 0) { double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr;
t_limit /= 2 * tolerance * dr;
valid = _extend_range (range, t_limit, valid);
}
/* * Nondegenerate, nonlimit circles passing through the corners. * * a == 0 && a*t^2 - 2*b*t + c == 0 * * t = c / (2*b) * * The b == 0 case has just been handled, so we only have to * compute this if b != 0.
*/ #define T_CORNER(x,y) \
b = (x) * dx + (y) * dy + cr * dr; \ if (fabs (b) >= DBL_EPSILON) { \ double t_corner; \ double x2 = (x) * (x); \ double y2 = (y) * (y); \ double cr2 = (cr) * (cr); \ double c = x2 + y2 - cr2; \
\
t_corner = 0.5 * c / b; \ if (t_corner * dr >= mindr) \
valid = _extend_range (range, t_corner, valid); \
}
/** * _cairo_gradient_pattern_box_to_parameter: * * Compute a interpolation range sufficient to draw (within the given * tolerance) the gradient in the given box getting the same result as * using the (-inf, +inf) range. * * Assumes that the pattern is not degenerate. This can be guaranteed * by simplifying it to a solid clear if _cairo_pattern_is_clear or to * a solid color if _cairo_gradient_pattern_is_solid. * * The range isn't guaranteed to be minimal, but it tries to.
**/ void
_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient, double x0, double y0, double x1, double y1, double tolerance, double out_range[2])
{
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
/** * _cairo_gradient_pattern_interpolate: * * Interpolate between the start and end objects of linear or radial * gradients. The interpolated object is stored in out_circle, with * the radius being zero in the linear gradient case.
**/ void
_cairo_gradient_pattern_interpolate (const cairo_gradient_pattern_t *gradient, double t,
cairo_circle_double_t *out_circle)
{
assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
/** * _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;
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;
dim = fabs (radial->cd1.center.x);
dim = MAX (dim, fabs (radial->cd1.center.y));
dim = MAX (dim, fabs (radial->cd1.radius));
dim = MAX (dim, fabs (radial->cd2.center.x));
dim = MAX (dim, fabs (radial->cd2.center.y));
dim = MAX (dim, fabs (radial->cd2.radius));
dim = MAX (dim, fabs (radial->cd1.center.x - radial->cd2.center.x));
dim = MAX (dim, fabs (radial->cd1.center.y - radial->cd2.center.y));
dim = MAX (dim, fabs (radial->cd1.radius - radial->cd2.radius));
}
if (unlikely (dim > max_value)) {
cairo_matrix_t scale;
if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) { /* degenerate radial gradients are clear */ if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient)) returnTRUE;
} elseif (gradient->base.extend == CAIRO_EXTEND_NONE) { /* EXTEND_NONE degenerate linear gradients are clear */ if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient)) returnTRUE;
}
/* 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];
switch (gradient->base.extend) { case CAIRO_EXTEND_REPEAT: /* * Sa, Sb and Sy, Sz are the first two and last two stops respectively. * The weight of the first and last stop can be computed as the area of * the following triangles (taken with height 1, since the whole [0-1] * will have total weight 1 this way): b*h/2 * * + + * / |\ / | \ * / | \ / | \ * / | \ / | \ * ~~~~~+---+---+---+~~~~~~~+-------+---+---+~~~~~ * -1+Sz 0 Sa Sb Sy Sz 1 1+Sa * * For the first stop: (Sb-(-1+Sz)/2 = (1+Sb-Sz)/2 * For the last stop: ((1+Sa)-Sy)/2 = (1+Sa-Sy)/2 * Halving the result is done after summing up all the areas.
*/
delta0 = 1.0 + gradient->stops[1].offset - gradient->stops[end].offset;
delta1 = 1.0 + gradient->stops[0].offset - gradient->stops[end-1].offset; break;
case CAIRO_EXTEND_REFLECT: /* * Sa, Sb and Sy, Sz are the first two and last two stops respectively. * The weight of the first and last stop can be computed as the area of * the following trapezoids (taken with height 1, since the whole [0-1] * will have total weight 1 this way): (b+B)*h/2 * * +-------+ +---+ * | |\ / | | * | | \ / | | * | | \ / | | * +-------+---+~~~~~~~+-------+---+ * 0 Sa Sb Sy Sz 1 * * For the first stop: (Sa+Sb)/2 * For the last stop: ((1-Sz) + (1-Sy))/2 = (2-Sy-Sz)/2 * Halving the result is done after summing up all the areas.
*/
delta0 = gradient->stops[0].offset + gradient->stops[1].offset;
delta1 = 2.0 - gradient->stops[end-1].offset - gradient->stops[end].offset; break;
case CAIRO_EXTEND_PAD: /* PAD is computed as the average of the first and last stop: * - take both of them with weight 1 (they will be halved * after the whole sum has been computed). * - avoid summing any of the inner stops.
*/
delta0 = delta1 = 1.0;
start = end; break;
r = delta0 * gradient->stops[0].color.red;
g = delta0 * gradient->stops[0].color.green;
b = delta0 * gradient->stops[0].color.blue;
a = delta0 * gradient->stops[0].color.alpha;
for (i = start; i < end; ++i) { /* Inner stops weight is the same as the area of the triangle they influence * (which goes from the stop before to the stop after), again with height 1 * since the whole must sum up to 1: b*h/2 * Halving is done after the whole sum has been computed.
*/ double delta = gradient->stops[i+1].offset - gradient->stops[i-1].offset;
r += delta * gradient->stops[i].color.red;
g += delta * gradient->stops[i].color.green;
b += delta * gradient->stops[i].color.blue;
a += delta * gradient->stops[i].color.alpha;
}
r += delta1 * gradient->stops[end].color.red;
g += delta1 * gradient->stops[end].color.green;
b += delta1 * gradient->stops[end].color.blue;
a += delta1 * gradient->stops[end].color.alpha;
_cairo_color_init_rgba (color, r * .5, g * .5, b * .5, a * .5);
}
/** * _cairo_pattern_alpha_range: * * Convenience function to determine the minimum and maximum alpha in * the drawn part of a pattern (i.e. ignoring clear parts caused by * extend modes and/or pattern shape). * * If not NULL, out_min and out_max will be set respectively to the * minimum and maximum alpha value of the pattern.
**/ void
_cairo_pattern_alpha_range (const cairo_pattern_t *pattern, double *out_min, double *out_max)
{ double alpha_min, alpha_max;
case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: { const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern; unsignedint 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; elseif (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); unsignedint 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; elseif (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 parameters to define * the coordinate 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; unsignedint num_patches, i, j, k; double x0, y0, x1, y1;
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);
}
}
}
/** * _cairo_gradient_pattern_is_solid: * * Convenience function to determine whether a gradient pattern is * a solid color within the given extents. In this case the color * argument is initialized to the color the pattern represents. * This functions doesn't handle completely transparent gradients, * thus it should be called only after _cairo_pattern_is_clear has * returned FALSE. * * Return value: %TRUE if the pattern is a solid color.
**/
cairo_bool_t
_cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient, const cairo_rectangle_int_t *extents,
cairo_color_t *color)
{ unsignedint i;
/** * _cairo_pattern_is_constant_alpha: * * Convenience function to determine whether a pattern has constant * alpha within the given extents. In this case the alpha argument is * initialized to the alpha within the extents. * * Return value: %TRUE if the pattern has constant alpha.
**/
cairo_bool_t
_cairo_pattern_is_constant_alpha (const cairo_pattern_t *abstract_pattern, const cairo_rectangle_int_t *extents, double *alpha)
{ const cairo_pattern_union_t *pattern;
cairo_color_t color;
if (_cairo_pattern_is_clear (abstract_pattern)) {
*alpha = 0.0; returnTRUE;
}
if (_cairo_pattern_is_opaque (abstract_pattern, extents)) {
*alpha = 1.0; returnTRUE;
}
case CAIRO_PATTERN_TYPE_LINEAR: case CAIRO_PATTERN_TYPE_RADIAL: if (_cairo_gradient_pattern_is_solid (&pattern->gradient.base, extents, &color)) {
*alpha = color.alpha; returnTRUE;
} else { returnFALSE;
}
/* TODO: need to test these as well */ case CAIRO_PATTERN_TYPE_SURFACE: case CAIRO_PATTERN_TYPE_RASTER_SOURCE: case CAIRO_PATTERN_TYPE_MESH: returnFALSE;
}
/** * _cairo_pattern_is_opaque_solid: * * Convenience function to determine whether a pattern is an opaque * (alpha==1.0) solid color pattern. This is done by testing whether * the pattern's alpha value when converted to a byte is 255, so if a * backend actually supported deep alpha channels this function might * not do the right thing. * * Return value: %TRUE if the pattern is an opaque, solid color.
**/
cairo_bool_t
_cairo_pattern_is_opaque_solid (const cairo_pattern_t *pattern)
{
cairo_solid_pattern_t *solid;
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) returnFALSE;
if (t[0] < 0.0 || t[1] > 1.0) returnFALSE;
}
} else returnFALSE; /* TODO: check actual intersection */
for (i = 0; i < gradient->n_stops; i++) if (! CAIRO_COLOR_IS_OPAQUE (&gradient->stops[i].color)) returnFALSE;
returnTRUE;
}
/** * _cairo_pattern_is_opaque: * * Convenience function to determine whether a pattern is an opaque * pattern (of any type). The same caveats that apply to * _cairo_pattern_is_opaque_solid apply here as well. * * Return value: %TRUE if the pattern is a opaque.
**/
cairo_bool_t
_cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern, const cairo_rectangle_int_t *sample)
{ const cairo_pattern_union_t *pattern;
if (abstract_pattern->has_component_alpha) returnFALSE;
pattern = (cairo_pattern_union_t *) abstract_pattern; switch (pattern->base.type) { case CAIRO_PATTERN_TYPE_SOLID: return _cairo_pattern_is_opaque_solid (abstract_pattern); case CAIRO_PATTERN_TYPE_SURFACE: return _surface_is_opaque (&pattern->surface, 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, sample); case CAIRO_PATTERN_TYPE_MESH: returnFALSE;
}
if (abstract_pattern->has_component_alpha) returnFALSE;
pattern = (cairo_pattern_union_t *) abstract_pattern; 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; returnFALSE;
}
/* * Will given row of back-translation matrix work with bilinear scale? * This is true for scales larger than 1. Also it was judged acceptable * for scales larger than .75. And if there is integer translation * then a scale of exactly .5 works.
*/ staticint
use_bilinear(double x, double y, double t)
{ /* This is the inverse matrix! */ double h = x*x + y*y; if (h < 1.0 / (0.75 * 0.75)) returnTRUE; /* scale > .75 */ if ((h > 3.99 && h < 4.01) /* scale is 1/2 */
&& !_cairo_fixed_from_double(x*y) /* parallel to an axis */
&& _cairo_fixed_is_integer (_cairo_fixed_from_double (t))) returnTRUE; returnFALSE;
}
/** * _cairo_pattern_analyze_filter: * @pattern: surface pattern * * Possibly optimize the filter to a simpler value depending on transformation * * Returns: the optimized #cairo_filter_t to use with @pattern.
**/
cairo_filter_t
_cairo_pattern_analyze_filter (const cairo_pattern_t *pattern)
{ switch (pattern->filter) { case CAIRO_FILTER_GOOD: case CAIRO_FILTER_BEST: case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_FAST: /* If source pixels map 1:1 onto destination pixels, we do * not need to filter (and do not want to filter, since it * will cause blurriness)
*/ if (_cairo_matrix_is_pixel_exact (&pattern->matrix)) { return CAIRO_FILTER_NEAREST;
} else { /* Use BILINEAR for any scale greater than .75 instead * of GOOD. For scales of 1 and larger this is identical, * for the smaller sizes it was judged that the artifacts * were not worse than the artifacts from a box filer. * BILINEAR can also be used if the scale is exactly .5 * and the translation in that direction is an integer.
*/ if (pattern->filter == CAIRO_FILTER_GOOD &&
use_bilinear (pattern->matrix.xx, pattern->matrix.xy,
pattern->matrix.x0) &&
use_bilinear (pattern->matrix.yx, pattern->matrix.yy,
pattern->matrix.y0)) return CAIRO_FILTER_BILINEAR;
} break;
case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_GAUSSIAN: default: break;
}
return pattern->filter;
}
/** * _cairo_hypot: * Returns: value similar to hypot(@x,@y) * * May want to replace this with Manhattan distance (abs(x)+abs(y)) if * hypot is too slow, as there is no need for accuracy here.
**/ staticinlinedouble
_cairo_hypot(double x, double y)
{ return hypot(x, y);
}
/** * _cairo_pattern_sampled_area: * * Return region of @pattern that will be sampled to fill @extents, * based on the transformation and filter. * * This does not include pixels that are mulitiplied by values very * close to zero by the ends of filters. This is so that transforms * that should be the identity or 90 degree rotations do not expand * the source unexpectedly. * * 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)
**/ void
_cairo_pattern_sampled_area (const cairo_pattern_t *pattern, const cairo_rectangle_int_t *extents,
cairo_rectangle_int_t *sample)
{ double x1, x2, y1, y2; double padx, pady;
/* Assume filters are interpolating, which means identity
cannot change the image */ if (_cairo_matrix_is_identity (&pattern->matrix)) {
*sample = *extents; return;
}
/* How far away from center will it actually sample? * This is the distance from a transformed pixel center to the * furthest sample of reasonable size.
*/ switch (pattern->filter) { case CAIRO_FILTER_NEAREST: case CAIRO_FILTER_FAST: /* Correct value is zero, but when the sample is on an integer * it is unknown if the backend will sample the pixel to the * left or right. This value makes it include both possible pixels.
*/
padx = pady = 0.004; break; case CAIRO_FILTER_BILINEAR: case CAIRO_FILTER_GAUSSIAN: default: /* Correct value is .5 */
padx = pady = 0.495; break; case CAIRO_FILTER_GOOD: /* Correct value is max(width,1)*.5 */
padx = _cairo_hypot (pattern->matrix.xx, pattern->matrix.xy); if (padx <= 1.0) padx = 0.495; elseif (padx >= 16.0) padx = 7.92; else padx *= 0.495;
pady = _cairo_hypot (pattern->matrix.yx, pattern->matrix.yy); if (pady <= 1.0) pady = 0.495; elseif (pady >= 16.0) pady = 7.92; else pady *= 0.495; break; case CAIRO_FILTER_BEST: /* Correct value is width*2 */
padx = _cairo_hypot (pattern->matrix.xx, pattern->matrix.xy) * 1.98; if (padx > 7.92) padx = 7.92;
pady = _cairo_hypot (pattern->matrix.yx, pattern->matrix.yy) * 1.98; if (pady > 7.92) pady = 7.92; break;
}
/* round furthest samples to edge of pixels */
x1 = floor (x1 - padx); if (x1 < CAIRO_RECT_INT_MIN) x1 = CAIRO_RECT_INT_MIN;
sample->x = x1;
/** * _cairo_pattern_get_extents: * * Return the "target-space" extents of @pattern in @extents. * * For unbounded patterns, the @extents will be initialized with * "infinite" extents, (minimum and maximum fixed-point values). * * When is_vector is TRUE, avoid rounding to zero widths or heights that * are less than 1 unit. * * XXX: Currently, bounded gradient patterns will also return * "infinite" extents, though it would be possible to optimize these * with a little more work.
**/ void
_cairo_pattern_get_extents (const cairo_pattern_t *pattern,
cairo_rectangle_int_t *extents,
cairo_bool_t is_vector)
{ double x1, y1, x2, y2; int ix1, ix2, iy1, iy2;
cairo_bool_t round_x = FALSE;
cairo_bool_t round_y = FALSE;
switch (pattern->type) { case CAIRO_PATTERN_TYPE_SOLID: goto UNBOUNDED;
if (_radial_pattern_is_degenerate (radial)) { /* cairo-gstate should have optimised degenerate * patterns to solid clear patterns, so we can ignore
* them here. */ goto EMPTY;
}
/* TODO: in some cases (focus outside/on the circle) it is
* half-bounded. */ if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED;
if (pattern->extend != CAIRO_EXTEND_NONE) goto UNBOUNDED;
if (_linear_pattern_is_degenerate (linear)) { /* cairo-gstate should have optimised degenerate * patterns to solid ones, so we can again ignore
* them here. */ goto EMPTY;
}
/* TODO: to get tight extents, use the matrix to transform
* the pattern instead of transforming the extents later. */ if (pattern->matrix.xy != 0. || pattern->matrix.yx != 0.) goto UNBOUNDED;
if (linear->pd1.x == linear->pd2.x) {
x1 = -HUGE_VAL;
x2 = HUGE_VAL;
y1 = MIN (linear->pd1.y, linear->pd2.y);
y2 = MAX (linear->pd1.y, linear->pd2.y);
} elseif (linear->pd1.y == linear->pd2.y) {
x1 = MIN (linear->pd1.x, linear->pd2.x);
x2 = MAX (linear->pd1.x, linear->pd2.x);
y1 = -HUGE_VAL;
y2 = HUGE_VAL;
} else { goto UNBOUNDED;
}
/* The current linear renderer just point-samples in the middle
of the pixels, similar to the NEAREST filter: */
round_x = round_y = TRUE;
} break;
if (a->has_component_alpha != b->has_component_alpha) returnFALSE;
if (a->type != CAIRO_PATTERN_TYPE_SOLID) { if (memcmp (&a->matrix, &b->matrix, sizeof (cairo_matrix_t))) returnFALSE;
if (a->filter != b->filter) returnFALSE;
if (a->extend != b->extend) returnFALSE;
}
switch (a->type) { case CAIRO_PATTERN_TYPE_SOLID: return _cairo_solid_pattern_equal ((cairo_solid_pattern_t *) a,
(cairo_solid_pattern_t *) b); case CAIRO_PATTERN_TYPE_LINEAR: return _cairo_linear_pattern_equal ((cairo_linear_pattern_t *) a,
(cairo_linear_pattern_t *) b); case CAIRO_PATTERN_TYPE_RADIAL: return _cairo_radial_pattern_equal ((cairo_radial_pattern_t *) a,
(cairo_radial_pattern_t *) b); case CAIRO_PATTERN_TYPE_MESH: return _cairo_mesh_pattern_equal ((cairo_mesh_pattern_t *) a,
(cairo_mesh_pattern_t *) b); case CAIRO_PATTERN_TYPE_SURFACE: return _cairo_surface_pattern_equal ((cairo_surface_pattern_t *) a,
(cairo_surface_pattern_t *) b); 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; returnFALSE;
}
}
/** * cairo_pattern_get_rgba: * @pattern: a #cairo_pattern_t * @red: return value for red component of color, or %NULL * @green: return value for green component of color, or %NULL * @blue: return value for blue component of color, or %NULL * @alpha: return value for alpha component of color, or %NULL * * Gets the solid color for a solid color pattern. * * Note that the color and alpha values are not premultiplied. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a solid * color pattern. * * Since: 1.4
**/
cairo_status_t
cairo_pattern_get_rgba (cairo_pattern_t *pattern, double *red, double *green, double *blue, double *alpha)
{
cairo_solid_pattern_t *solid = (cairo_solid_pattern_t*) pattern; double r0, g0, b0, a0;
if (pattern->status) return pattern->status;
if (pattern->type != CAIRO_PATTERN_TYPE_SOLID) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
if (red)
*red = r0; if (green)
*green = g0; if (blue)
*blue = b0; if (alpha)
*alpha = a0;
return CAIRO_STATUS_SUCCESS;
}
/** * cairo_pattern_get_surface: * @pattern: a #cairo_pattern_t * @surface: return value for surface of pattern, or %NULL * * Gets the surface of a surface pattern. The reference returned in * @surface is owned by the pattern; the caller should call * cairo_surface_reference() if the surface is to be retained. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if the pattern is not a surface * pattern. * * Since: 1.4
**/
cairo_status_t
cairo_pattern_get_surface (cairo_pattern_t *pattern,
cairo_surface_t **surface)
{
cairo_surface_pattern_t *spat = (cairo_surface_pattern_t*) pattern;
if (pattern->status) return pattern->status;
if (pattern->type != CAIRO_PATTERN_TYPE_SURFACE) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
if (surface)
*surface = spat->surface;
return CAIRO_STATUS_SUCCESS;
}
/** * cairo_pattern_get_color_stop_rgba: * @pattern: a #cairo_pattern_t * @index: index of the stop to return data for * @offset: return value for the offset of the stop, or %NULL * @red: return value for red component of color, or %NULL * @green: return value for green component of color, or %NULL * @blue: return value for blue component of color, or %NULL * @alpha: return value for alpha component of color, or %NULL * * Gets the color and offset information at the given @index for a * gradient pattern. Values of @index range from 0 to n-1 * where n is the number returned * by cairo_pattern_get_color_stop_count(). * * Note that the color and alpha values are not premultiplied. * * Return value: %CAIRO_STATUS_SUCCESS, or %CAIRO_STATUS_INVALID_INDEX * if @index is not valid for the given pattern. If the pattern is * not a gradient pattern, %CAIRO_STATUS_PATTERN_TYPE_MISMATCH is * returned. * * Since: 1.4
**/
cairo_status_t
cairo_pattern_get_color_stop_rgba (cairo_pattern_t *pattern, int index, double *offset, double *red, double *green, double *blue, double *alpha)
{
cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern;
if (index < 0 || (unsignedint) index >= gradient->n_stops) return _cairo_error (CAIRO_STATUS_INVALID_INDEX);
if (offset)
*offset = gradient->stops[index].offset; if (red)
*red = gradient->stops[index].color.red; if (green)
*green = gradient->stops[index].color.green; if (blue)
*blue = gradient->stops[index].color.blue; if (alpha)
*alpha = gradient->stops[index].color.alpha;
return CAIRO_STATUS_SUCCESS;
}
/** * cairo_pattern_get_color_stop_count: * @pattern: a #cairo_pattern_t * @count: return value for the number of color stops, or %NULL * * Gets the number of color stops specified in the given gradient * pattern. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a gradient * pattern. * * Since: 1.4
**/
cairo_status_t
cairo_pattern_get_color_stop_count (cairo_pattern_t *pattern, int *count)
{
cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t*) pattern;
/** * cairo_pattern_get_linear_points: * @pattern: a #cairo_pattern_t * @x0: return value for the x coordinate of the first point, or %NULL * @y0: return value for the y coordinate of the first point, or %NULL * @x1: return value for the x coordinate of the second point, or %NULL * @y1: return value for the y coordinate of the second point, or %NULL * * Gets the gradient endpoints for a linear gradient. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a linear * gradient pattern. * * Since: 1.4
**/
cairo_status_t
cairo_pattern_get_linear_points (cairo_pattern_t *pattern, double *x0, double *y0, double *x1, double *y1)
{
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t*) pattern;
if (pattern->status) return pattern->status;
if (pattern->type != CAIRO_PATTERN_TYPE_LINEAR) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
if (x0)
*x0 = linear->pd1.x; if (y0)
*y0 = linear->pd1.y; if (x1)
*x1 = linear->pd2.x; if (y1)
*y1 = linear->pd2.y;
return CAIRO_STATUS_SUCCESS;
}
/** * cairo_pattern_get_radial_circles: * @pattern: a #cairo_pattern_t * @x0: return value for the x coordinate of the center of the first circle, or %NULL * @y0: return value for the y coordinate of the center of the first circle, or %NULL * @r0: return value for the radius of the first circle, or %NULL * @x1: return value for the x coordinate of the center of the second circle, or %NULL * @y1: return value for the y coordinate of the center of the second circle, or %NULL * @r1: return value for the radius of the second circle, or %NULL * * Gets the gradient endpoint circles for a radial gradient, each * specified as a center coordinate and a radius. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a radial * gradient pattern. * * Since: 1.4
**/
cairo_status_t
cairo_pattern_get_radial_circles (cairo_pattern_t *pattern, double *x0, double *y0, double *r0, double *x1, double *y1, double *r1)
{
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t*) pattern;
if (pattern->status) return pattern->status;
if (pattern->type != CAIRO_PATTERN_TYPE_RADIAL) return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
if (x0)
*x0 = radial->cd1.center.x; if (y0)
*y0 = radial->cd1.center.y; if (r0)
*r0 = radial->cd1.radius; if (x1)
*x1 = radial->cd2.center.x; if (y1)
*y1 = radial->cd2.center.y; if (r1)
*r1 = radial->cd2.radius;
return CAIRO_STATUS_SUCCESS;
}
/** * cairo_mesh_pattern_get_patch_count: * @pattern: a #cairo_pattern_t * @count: return value for the number patches, or %NULL * * Gets the number of patches specified in the given mesh pattern. * * The number only includes patches which have been finished by * calling cairo_mesh_pattern_end_patch(). For example it will be 0 * during the definition of the first patch. * * Return value: %CAIRO_STATUS_SUCCESS, or * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH if @pattern is not a mesh * pattern. * * Since: 1.12
**/
cairo_status_t
cairo_mesh_pattern_get_patch_count (cairo_pattern_t *pattern, unsignedint *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;
}
/** * 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 from 0 to n-1 where n is 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, unsignedint 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; unsignedint 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));
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;
}
/** * 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 from 0 to n-1 where n is 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(). * * Note that the color and alpha values are not premultiplied. * * 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, unsignedint patch_num, unsignedint corner_num, double *red, double *green, double *blue, double *alpha)
{
cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; unsignedint 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);
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;
}
/** * 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 from 0 to n-1 where n is 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, unsignedint patch_num, unsignedint point_num, double *x, double *y)
{
cairo_mesh_pattern_t *mesh = (cairo_mesh_pattern_t *) pattern; const cairo_mesh_patch_t *patch; unsignedint 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);
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;
}
void
_cairo_pattern_reset_static_data (void)
{ int i;
for (i = 0; i < ARRAY_LENGTH (freed_pattern_pool); i++)
_freed_pool_reset (&freed_pattern_pool[i]);
}
staticvoid
_cairo_debug_print_surface_pattern (FILE *file, const cairo_surface_pattern_t *pattern)
{ constchar *s; switch (pattern->surface->type) { case CAIRO_SURFACE_TYPE_IMAGE: s = "image"; break; case CAIRO_SURFACE_TYPE_PDF: s = "pdf"; break; case CAIRO_SURFACE_TYPE_PS: s = "ps"; break; case CAIRO_SURFACE_TYPE_XLIB: s = "xlib"; break; case CAIRO_SURFACE_TYPE_XCB: s = "xcb"; break; case CAIRO_SURFACE_TYPE_GLITZ: s = "glitz"; break; case CAIRO_SURFACE_TYPE_QUARTZ: s = "quartz"; break; case CAIRO_SURFACE_TYPE_WIN32: s = "win32"; break; case CAIRO_SURFACE_TYPE_BEOS: s = "beos"; break; case CAIRO_SURFACE_TYPE_DIRECTFB: s = "directfb"; break; case CAIRO_SURFACE_TYPE_SVG: s = "svg"; break; case CAIRO_SURFACE_TYPE_OS2: s = "os2"; break; case CAIRO_SURFACE_TYPE_WIN32_PRINTING: s = "win32_printing"; break; case CAIRO_SURFACE_TYPE_QUARTZ_IMAGE: s = "quartz_image"; break; case CAIRO_SURFACE_TYPE_SCRIPT: s = "script"; break; case CAIRO_SURFACE_TYPE_QT: s = "qt"; break; case CAIRO_SURFACE_TYPE_RECORDING: s = "recording"; break; case CAIRO_SURFACE_TYPE_VG: s = "vg"; break; case CAIRO_SURFACE_TYPE_GL: s = "gl"; break; case CAIRO_SURFACE_TYPE_DRM: s = "drm"; break; case CAIRO_SURFACE_TYPE_TEE: s = "tee"; break; case CAIRO_SURFACE_TYPE_XML: s = "xml"; break; case CAIRO_SURFACE_TYPE_SKIA: s = "skia"; break; /* Deprecated */ case CAIRO_SURFACE_TYPE_SUBSURFACE: s = "subsurface"; break; case CAIRO_SURFACE_TYPE_COGL: s = "cogl"; break; default: s = "invalid"; ASSERT_NOT_REACHED; break;
}
fprintf (file, " surface type: %s\n", s);
}
void
_cairo_debug_print_pattern (FILE *file, const cairo_pattern_t *pattern)
{ constchar *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 = "gaussian"; 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;
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.166 Sekunden
(vorverarbeitet am 2026-04-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.