summaryrefslogtreecommitdiff
path: root/src/cairo-pattern.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cairo-pattern.c')
-rw-r--r--src/cairo-pattern.c2289
1 files changed, 2045 insertions, 244 deletions
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index 1b79011..82c2279 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -31,6 +31,11 @@
#include "cairoint.h"
#include "cairo-error-private.h"
#include "cairo-freed-pool-private.h"
+#include "cairo-path-private.h"
+
+#include <float.h>
+
+#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */
/**
* SECTION:cairo-pattern
@@ -44,13 +49,15 @@
* 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];
+static freed_pool_t freed_pattern_pool[5];
#endif
static const cairo_solid_pattern_t _cairo_pattern_nil = {
@@ -154,6 +161,9 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
case CAIRO_PATTERN_TYPE_RADIAL:
VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_radial_pattern_t));
break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t));
+ break;
}
#endif
@@ -219,6 +229,19 @@ _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern,
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)
@@ -261,6 +284,18 @@ _cairo_pattern_init_copy (cairo_pattern_t *pattern,
return status;
} break;
+ case CAIRO_PATTERN_TYPE_MESH: {
+ cairo_mesh_pattern_t *dst = (cairo_mesh_pattern_t *) pattern;
+ cairo_mesh_pattern_t *src = (cairo_mesh_pattern_t *) other;
+ cairo_status_t status;
+
+ VG (VALGRIND_MAKE_MEM_UNDEFINED (pattern, sizeof (cairo_mesh_pattern_t)));
+
+ status = _cairo_mesh_pattern_init_copy (dst, src);
+ if (unlikely (status))
+ return status;
+
+ } break;
}
/* The reference count and user_data array are unique to the copy. */
@@ -293,6 +328,9 @@ _cairo_pattern_init_static_copy (cairo_pattern_t *pattern,
case CAIRO_PATTERN_TYPE_RADIAL:
size = sizeof (cairo_radial_pattern_t);
break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ size = sizeof (cairo_mesh_pattern_t);
+ break;
}
memcpy (pattern, other, size);
@@ -353,6 +391,12 @@ _cairo_pattern_fini (cairo_pattern_t *pattern)
if (gradient->stops && gradient->stops != gradient->stops_embedded)
free (gradient->stops);
} break;
+ case CAIRO_PATTERN_TYPE_MESH: {
+ cairo_mesh_pattern_t *mesh =
+ (cairo_mesh_pattern_t *) pattern;
+
+ _cairo_array_fini (&mesh->patches);
+ } break;
}
#if HAVE_VALGRIND
@@ -369,6 +413,9 @@ _cairo_pattern_fini (cairo_pattern_t *pattern)
case CAIRO_PATTERN_TYPE_RADIAL:
VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_radial_pattern_t));
break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ VALGRIND_MAKE_MEM_NOACCESS (pattern, sizeof (cairo_mesh_pattern_t));
+ break;
}
#endif
}
@@ -396,6 +443,9 @@ _cairo_pattern_create_copy (cairo_pattern_t **pattern_out,
case CAIRO_PATTERN_TYPE_RADIAL:
pattern = malloc (sizeof (cairo_radial_pattern_t));
break;
+ case CAIRO_PATTERN_TYPE_MESH:
+ pattern = malloc (sizeof (cairo_mesh_pattern_t));
+ break;
default:
ASSERT_NOT_REACHED;
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
@@ -450,31 +500,31 @@ _cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern,
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)
{
_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 *
@@ -741,6 +791,186 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0,
return &pattern->base.base;
}
+/* This order is specified in the diagram in the documentation for
+ * cairo_pattern_create_mesh() */
+static const int mesh_path_point_i[12] = { 0, 0, 0, 0, 1, 2, 3, 3, 3, 3, 2, 1 };
+static const int mesh_path_point_j[12] = { 0, 1, 2, 3, 3, 3, 3, 2, 1, 0, 0, 0 };
+static const int mesh_control_point_i[4] = { 1, 1, 2, 2 };
+static const int mesh_control_point_j[4] = { 1, 2, 2, 1 };
+
+/**
+ * cairo_pattern_create_mesh:
+ *
+ * Create a new mesh pattern.
+ *
+ * Mesh patterns are tensor-product patch meshes (type 7 shadings in
+ * PDF). Mesh patterns may also be used to create other types of
+ * shadings that are special cases of tensor-product patch meshes such
+ * as Coons patch meshes (type 6 shading in PDF) and Gouraud-shaded
+ * triangle meshes (type 4 and 5 shadings in PDF).
+ *
+ * Mesh patterns consist of one or more tensor-product patches, which
+ * should be defined before using the mesh pattern. Using a mesh
+ * pattern with a partially defined patch as source or mask will put
+ * the context in an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * A tensor-product patch is defined by 4 Bézier curves (side 0, 1, 2,
+ * 3) and by 4 additional control points (P0, P1, P2, P3) that provide
+ * further control over the patch and complete the definition of the
+ * tensor-product patch. The corner C0 is the first point of the
+ * patch.
+ *
+ * Degenerate sides are permitted so straight lines may be used. A
+ * zero length line on one side may be used to create 3 sided patches.
+ *
+ * <informalexample><programlisting>
+ * C1 Side 1 C2
+ * +---------------+
+ * | |
+ * | P1 P2 |
+ * | |
+ * Side 0 | | Side 2
+ * | |
+ * | |
+ * | P0 P3 |
+ * | |
+ * +---------------+
+ * C0 Side 3 C3
+ * </programlisting></informalexample>
+ *
+ * Each patch is constructed by first calling
+ * cairo_pattern_mesh_begin_patch(), then cairo_pattern_mesh_move_to()
+ * to specify the first point in the patch (C0). Then the sides are
+ * specified with calls to cairo_pattern_mesh_curve_to() and
+ * cairo_pattern_mesh_line_to().
+ *
+ * The four additional control points (P0, P1, P2, P3) in a patch can
+ * be specified with cairo_pattern_mesh_set_control_point().
+ *
+ * At each corner of the patch (C0, C1, C2, C3) a color may be
+ * specified with cairo_pattern_mesh_set_corner_color_rgb() or
+ * cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_begin_patch()/cairo_pattern_mesh_end_patch().
+ *
+ * <informalexample><programlisting>
+ * cairo_pattern_t *mesh = cairo_pattern_mesh_create_mesh ();
+ *
+ * /&ast; Add a Coons patch &ast;/
+ * cairo_pattern_mesh_begin_patch (mesh);
+ * cairo_pattern_mesh_move_to (pattern, 0, 0);
+ * cairo_pattern_mesh_curve_to (pattern, 30, -30, 60, 30, 100, 0);
+ * cairo_pattern_mesh_curve_to (pattern, 60, 30, 130, 60, 100, 100);
+ * cairo_pattern_mesh_curve_to (pattern, 60, 70, 30, 130, 0, 100);
+ * cairo_pattern_mesh_curve_to (pattern, 30, 70, -30, 30, 0, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 3, 1, 1, 0);
+ * cairo_pattern_mesh_end_patch (mesh);
+ *
+ * /&ast; Add a Gouraud-shaded triangle &ast;/
+ * cairo_pattern_mesh_begin_patch (mesh)
+ * cairo_pattern_mesh_move_to (pattern, 100, 100);
+ * cairo_pattern_mesh_line_to (pattern, 130, 130);
+ * cairo_pattern_mesh_line_to (pattern, 130, 70);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 0, 1, 0, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 1, 0, 1, 0);
+ * cairo_pattern_mesh_set_corner_color_rgb (pattern, 2, 0, 0, 1);
+ * cairo_pattern_mesh_end_patch (mesh)
+ * </programlisting></informalexample>
+ *
+ * When two patches overlap, the last one that has been added is drawn
+ * over the first one.
+ *
+ * When a patch folds over itself, points are sorted depending on
+ * their parameter coordinates inside the patch. The v coordinate
+ * ranges from 0 to 1 when moving from side 3 to side 1; the u
+ * coordinate ranges from 0 to 1 when going from side 0 to side
+ * 2. Points with higher v coordinate hide points with lower v
+ * coordinate. When two points have the same v coordinate, the one
+ * with higher u coordinate is above. This means that points nearer to
+ * side 1 are above points nearer to side 3; when this is not
+ * sufficient to decide which point is above (for example when both
+ * points belong to side 1 or side 3) points nearer to side 2 are
+ * above points nearer to side 0.
+ *
+ * For a complete definition of tensor-product patches, see the PDF
+ * specification (ISO32000), which describes the parametrization in
+ * detail.
+ *
+ * Note: The coordinates are always in pattern space. For a new
+ * pattern, pattern space is identical to user space, but the
+ * relationship between the spaces can be changed with
+ * cairo_pattern_set_matrix().
+ *
+ * Return value: the newly created #cairo_pattern_t if successful, or
+ * an error pattern in case of no memory. The caller owns the returned
+ * object and should call cairo_pattern_destroy() when finished with
+ * it.
+ *
+ * This function will always return a valid pointer, but if an error
+ * occurred the pattern status will be set to an error. To inspect the
+ * status of a pattern use cairo_pattern_status().
+ *
+ * Since: 1.12
+ **/
+cairo_pattern_t *
+cairo_pattern_create_mesh (void)
+{
+ cairo_mesh_pattern_t *pattern;
+
+ pattern =
+ _freed_pool_get (&freed_pattern_pool[CAIRO_PATTERN_TYPE_MESH]);
+ if (unlikely (pattern == NULL)) {
+ pattern = malloc (sizeof (cairo_mesh_pattern_t));
+ if (unlikely (pattern == NULL)) {
+ _cairo_error_throw (CAIRO_STATUS_NO_MEMORY);
+ return (cairo_pattern_t *) &_cairo_pattern_nil.base;
+ }
+ }
+
+ CAIRO_MUTEX_INITIALIZE ();
+
+ _cairo_pattern_init (&pattern->base, CAIRO_PATTERN_TYPE_MESH);
+ _cairo_array_init (&pattern->patches, sizeof (cairo_mesh_patch_t));
+ pattern->current_patch = NULL;
+ CAIRO_REFERENCE_COUNT_INIT (&pattern->base.ref_count, 1);
+
+ return &pattern->base;
+}
+
/**
* cairo_pattern_reference:
* @pattern: a #cairo_pattern_t
@@ -906,6 +1136,451 @@ cairo_pattern_set_user_data (cairo_pattern_t *pattern,
key, user_data, destroy);
}
+/**
+ * cairo_pattern_mesh_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_pattern_mesh_move_to(), cairo_pattern_mesh_line_to() and
+ * cairo_pattern_mesh_curve_to().
+ *
+ * After defining the patch, cairo_pattern_mesh_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_pattern_mesh_begin_patch (cairo_pattern_t *pattern)
+{
+ cairo_mesh_pattern_t *mesh;
+ cairo_status_t status;
+ cairo_mesh_patch_t *current_patch;
+ int i;
+
+ if (unlikely (pattern->status))
+ return;
+
+ if (unlikely (pattern->type != CAIRO_PATTERN_TYPE_MESH)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
+ return;
+ }
+
+ mesh = (cairo_mesh_pattern_t *) pattern;
+ if (unlikely (mesh->current_patch)) {
+ _cairo_pattern_set_error (pattern, CAIRO_STATUS_INVALID_MESH_CONSTRUCTION);
+ return;
+ }
+
+ status = _cairo_array_allocate (&mesh->patches, 1, (void **) &current_patch);
+ if (unlikely (status)) {
+ _cairo_pattern_set_error (pattern, status);
+ return;
+ }
+
+ mesh->current_patch = current_patch;
+ mesh->current_side = -2; /* no current point */
+
+ for (i = 0; i < 4; i++)
+ mesh->has_control_point[i] = FALSE;
+
+ for (i = 0; i < 4; i++)
+ mesh->has_color[i] = FALSE;
+}
+
+
+static void
+_calc_control_point (cairo_mesh_patch_t *patch, int control_point)
+{
+ /* The Coons patch is a special case of the Tensor Product patch
+ * where the four control points are:
+ *
+ * P11 = S(1/3, 1/3)
+ * P12 = S(1/3, 2/3)
+ * P21 = S(2/3, 1/3)
+ * P22 = S(2/3, 2/3)
+ *
+ * where S is the gradient surface.
+ *
+ * When one or more control points has not been specified
+ * calculated the Coons patch control points are substituted. If
+ * no control points are specified the gradient will be a Coons
+ * patch.
+ *
+ * The equations below are defined in the ISO32000 standard.
+ */
+ cairo_point_double_t *p[3][3];
+ int cp_i, cp_j, i, j;
+
+ cp_i = mesh_control_point_i[control_point];
+ cp_j = mesh_control_point_j[control_point];
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ p[i][j] = &patch->points[cp_i ^ i][cp_j ^ j];
+
+ p[0][0]->x = (- 4 * p[1][1]->x
+ + 6 * (p[1][0]->x + p[0][1]->x)
+ - 2 * (p[1][2]->x + p[2][1]->x)
+ + 3 * (p[2][0]->x + p[0][2]->x)
+ - 1 * p[2][2]->x) * (1. / 9);
+
+ p[0][0]->y = (- 4 * p[1][1]->y
+ + 6 * (p[1][0]->y + p[0][1]->y)
+ - 2 * (p[1][2]->y + p[2][1]->y)
+ + 3 * (p[2][0]->y + p[0][2]->y)
+ - 1 * p[2][2]->y) * (1. / 9);
+}
+
+/**
+ * cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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_pattern_mesh_curve_to(), this function will behave as if
+ * preceded by a call to cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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_pattern_mesh_curve_to);
+
+/**
+ * cairo_pattern_mesh_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_pattern_mesh_line_to() this function will behave as
+ * cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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_pattern_mesh_line_to);
+
+/**
+ * cairo_pattern_mesh_move_to:
+ * @pattern: a #cairo_pattern_t
+ * @x: the X coordinate of the new position
+ * @y: the Y coordinate of the new position
+ *
+ * Define the first point of the current patch in a mesh pattern.
+ *
+ * After this call the current point will be (@x, @y).
+ *
+ * Note: If @pattern is not a mesh pattern then @pattern will be put
+ * into an error status with a status of
+ * %CAIRO_STATUS_PATTERN_TYPE_MISMATCH. If @pattern has no current
+ * patch or the current patch already has at leas one side, @pattern
+ * will be put into an error status with a status of
+ * %CAIRO_STATUS_INVALID_MESH_CONSTRUCTION.
+ *
+ * Since: 1.12
+ **/
+void
+cairo_pattern_mesh_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_pattern_mesh_move_to);
+
+/**
+ * cairo_pattern_mesh_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_pattern_mesh_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,7 +1623,127 @@ _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern)
}
static void
-_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
+_cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_set_corner_color_rgb (cairo_pattern_t *pattern,
+ unsigned int corner_num,
+ double red, double green, double blue)
+{
+ cairo_pattern_mesh_set_corner_color_rgba (pattern, corner_num, red, green, blue, 1.0);
+}
+
+/**
+ * cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_set_corner_color (mesh, corner_num, red, green, blue, alpha);
+}
+slim_hidden_def (cairo_pattern_mesh_set_corner_color_rgba);
+
+static void
+_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
double offset,
double red,
double green,
@@ -1282,10 +2077,8 @@ _cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern,
* pattern. We actually only need 3/4 corners, so we skip the
* fourth.
*/
- point0.x = _cairo_fixed_to_double (pattern->p1.x);
- point0.y = _cairo_fixed_to_double (pattern->p1.y);
- point1.x = _cairo_fixed_to_double (pattern->p2.x);
- point1.y = _cairo_fixed_to_double (pattern->p2.y);
+ point0 = pattern->pd1;
+ point1 = pattern->pd2;
_cairo_matrix_get_affine (&pattern->base.base.matrix,
&a, &b, &c, &d, &tx, &ty);
@@ -1333,10 +2126,11 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
cairo_image_surface_t *image;
pixman_image_t *pixman_image;
pixman_transform_t pixman_transform;
+ cairo_circle_double_t extremes[2];
+ pixman_point_fixed_t p1, p2;
cairo_status_t status;
cairo_bool_t repeat = FALSE;
- cairo_bool_t opaque = TRUE;
-
+ int ix, iy;
pixman_gradient_stop_t pixman_stops_static[2];
pixman_gradient_stop_t *pixman_stops = pixman_stops_static;
unsigned int i;
@@ -1359,75 +2153,26 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
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)
- {
- cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
- pixman_point_fixed_t p1, p2;
- cairo_fixed_t xdim, ydim;
-
- xdim = linear->p2.x - linear->p1.x;
- ydim = linear->p2.y - linear->p1.y;
-
- /*
- * 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).
- *
- * 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...
- */
-#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)
- {
- double sf;
-
- if (xdim > ydim)
- sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (xdim);
- else
- sf = PIXMAN_MAX_INT / _cairo_fixed_to_double (ydim);
+ _cairo_gradient_pattern_fit_to_range (pattern, PIXMAN_MAX_INT >> 1, &matrix, extremes);
- 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);
- }
- 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);
- }
+ p1.x = _cairo_fixed_16_16_from_double (extremes[0].center.x);
+ p1.y = _cairo_fixed_16_16_from_double (extremes[0].center.y);
+ p2.x = _cairo_fixed_16_16_from_double (extremes[1].center.x);
+ p2.y = _cairo_fixed_16_16_from_double (extremes[1].center.y);
+ if (pattern->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
pixman_image = pixman_image_create_linear_gradient (&p1, &p2,
pixman_stops,
pattern->n_stops);
- }
- else
- {
- cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
- pixman_point_fixed_t c1, c2;
+ } else {
pixman_fixed_t r1, r2;
- c1.x = _cairo_fixed_to_16_16 (radial->c1.x);
- c1.y = _cairo_fixed_to_16_16 (radial->c1.y);
- r1 = _cairo_fixed_to_16_16 (radial->r1);
+ r1 = _cairo_fixed_16_16_from_double (extremes[0].radius);
+ r2 = _cairo_fixed_16_16_from_double (extremes[1].radius);
- c2.x = _cairo_fixed_to_16_16 (radial->c2.x);
- c2.y = _cairo_fixed_to_16_16 (radial->c2.y);
- r2 = _cairo_fixed_to_16_16 (radial->r2);
-
- pixman_image = pixman_image_create_radial_gradient (&c1, &c2,
- r1, r2,
+ pixman_image = pixman_image_create_radial_gradient (&p1, &p2, r1, r2,
pixman_stops,
pattern->n_stops);
}
@@ -1498,12 +2243,21 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
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);
+ ix = x;
+ iy = y;
+ status = _cairo_matrix_to_pixman_matrix_offset (&matrix,
+ pattern->base.filter,
+ width/2., height/2.,
+ &pixman_transform,
+ &ix, &iy);
+ if (status != CAIRO_INT_STATUS_NOTHING_TO_DO) {
+ if (unlikely (status != CAIRO_STATUS_SUCCESS) ||
+ ! pixman_image_set_transform (pixman_image, &pixman_transform))
+ {
+ cairo_surface_destroy (&image->base);
+ pixman_image_unref (pixman_image);
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
}
switch (pattern->base.extend) {
@@ -1525,7 +2279,7 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
pixman_image,
NULL,
image->pixman_image,
- x, y,
+ ix, iy,
0, 0,
0, 0,
width, height);
@@ -1552,6 +2306,54 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat
return status;
}
+static cairo_int_status_t
+_cairo_pattern_acquire_surface_for_mesh (const cairo_mesh_pattern_t *pattern,
+ cairo_surface_t *dst,
+ int x,
+ int y,
+ unsigned int width,
+ unsigned int height,
+ cairo_surface_t **out,
+ cairo_surface_attributes_t *attr)
+{
+ cairo_surface_t *image;
+ void *data;
+ cairo_status_t status;
+ int clone_offset_x, clone_offset_y;
+ int stride;
+
+ image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height);
+ if (unlikely (image->status))
+ return image->status;
+
+ stride = cairo_image_surface_get_stride (image);
+ data = cairo_image_surface_get_data (image);
+
+ _cairo_mesh_pattern_rasterize (pattern, data, width, height, stride, -x, -y);
+
+ attr->x_offset = -x;
+ attr->y_offset = -y;
+ attr->filter = CAIRO_FILTER_NEAREST;
+ attr->extend = pattern->base.extend;
+ cairo_matrix_init_identity (&attr->matrix);
+ attr->has_component_alpha = pattern->base.has_component_alpha;
+
+ if (_cairo_surface_is_image (dst)) {
+ *out = image;
+
+ return CAIRO_STATUS_SUCCESS;
+ }
+
+ status = _cairo_surface_clone_similar (dst, image,
+ 0, 0, width, height,
+ &clone_offset_x,
+ &clone_offset_y, out);
+
+ cairo_surface_destroy (image);
+
+ return status;
+}
+
/* We maintain a small cache here, because we don't want to constantly
* recreate surfaces for simple solid colors. */
#define MAX_SURFACE_CACHE_SIZE 16
@@ -1726,50 +2528,590 @@ _cairo_pattern_reset_solid_surface_cache (void)
CAIRO_MUTEX_UNLOCK (_cairo_pattern_solid_surface_cache_lock);
}
+static cairo_bool_t
+_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
+{
+ return fabs (linear->pd1.x - linear->pd2.x) < DBL_EPSILON &&
+ fabs (linear->pd1.y - linear->pd2.y) < DBL_EPSILON;
+}
+
+static cairo_bool_t
+_radial_pattern_is_degenerate (const cairo_radial_pattern_t *radial)
+{
+ /* A radial pattern is considered degenerate if it can be
+ * represented as a solid or clear pattern. This corresponds to
+ * one of the two cases:
+ *
+ * 1) The radii are both very small:
+ * |dr| < DBL_EPSILON && min (r0, r1) < DBL_EPSILON
+ *
+ * 2) The two circles have about the same radius and are very
+ * close to each other (approximately a cylinder gradient that
+ * doesn't move with the parameter):
+ * |dr| < DBL_EPSILON && max (|dx|, |dy|) < 2 * DBL_EPSILON
+ *
+ * These checks are consistent with the assumptions used in
+ * _cairo_radial_pattern_box_to_parameter ().
+ */
+
+ return fabs (radial->cd1.radius - radial->cd2.radius) < DBL_EPSILON &&
+ (MIN (radial->cd1.radius, radial->cd2.radius) < DBL_EPSILON ||
+ MAX (fabs (radial->cd1.center.x - radial->cd2.center.x),
+ fabs (radial->cd1.center.y - radial->cd2.center.y)) < 2 * DBL_EPSILON);
+}
+
static void
-_extents_to_linear_parameter (const cairo_linear_pattern_t *linear,
- const cairo_rectangle_int_t *extents,
- double t[2])
+_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;
- 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;
+ assert (! _linear_pattern_is_degenerate (linear));
+
+ /*
+ * Linear gradients are othrogonal to the line passing through
+ * their extremes. Because of convexity, the parameter range can
+ * be computed as the convex hull (one the real line) of the
+ * parameter values of the 4 corners of the box.
+ *
+ * The parameter value t for a point (x,y) can be computed as:
+ *
+ * t = (p2 - p1) . (x,y) / |p2 - p1|^2
+ *
+ * t0 is the t value for the top left corner
+ * tdx is the difference between left and right corners
+ * tdy is the difference between top and bottom corners
+ */
+
+ p1x = linear->pd1.x;
+ p1y = linear->pd1.y;
+ pdx = linear->pd2.x - p1x;
+ pdy = linear->pd2.y - p1y;
invsqnorm = 1.0 / (pdx * pdx + pdy * pdy);
pdx *= invsqnorm;
pdy *= invsqnorm;
- t0 = (extents->x - p1x) * pdx + (extents->y - p1y) * pdy;
- tdx = extents->width * pdx;
- tdy = extents->height * pdy;
+ t0 = (x0 - p1x) * pdx + (y0 - p1y) * pdy;
+ tdx = (x1 - x0) * pdx;
+ tdy = (y1 - y0) * pdy;
+
+ /*
+ * Because of the linearity of the t value, tdx can simply be
+ * added the t0 to move along the top edge. After this, range[0]
+ * and range[1] represent the parameter range for the top edge, so
+ * extending it to include the whole box simply requires adding
+ * tdy to the correct extreme.
+ */
- t[0] = t[1] = t0;
+ range[0] = range[1] = t0;
if (tdx < 0)
- t[0] += tdx;
+ range[0] += tdx;
else
- t[1] += tdx;
+ range[1] += tdx;
if (tdy < 0)
- t[0] += tdy;
+ range[0] += tdy;
else
- t[1] += tdy;
+ range[1] += tdy;
}
static cairo_bool_t
-_linear_pattern_is_degenerate (const cairo_linear_pattern_t *linear)
+_extend_range (double range[2], double value, cairo_bool_t valid)
+{
+ if (!valid)
+ range[0] = range[1] = value;
+ else if (value < range[0])
+ range[0] = value;
+ else if (value > range[1])
+ range[1] = value;
+
+ return TRUE;
+}
+
+static void
+_cairo_radial_pattern_box_to_parameter (const cairo_radial_pattern_t *radial,
+ double x0, double y0,
+ double x1, double y1,
+ double tolerance,
+ double range[2])
+{
+ double cx, cy, cr, dx, dy, dr;
+ double a, x_focus, y_focus;
+ double mindr, minx, miny, maxx, maxy;
+ cairo_bool_t valid;
+
+ assert (! _radial_pattern_is_degenerate (radial));
+ assert (x0 < x1);
+ assert (y0 < y1);
+
+ tolerance = MAX (tolerance, DBL_EPSILON);
+
+ range[0] = range[1] = 0;
+ valid = FALSE;
+
+ x_focus = y_focus = 0; /* silence gcc */
+
+ cx = radial->cd1.center.x;
+ cy = radial->cd1.center.y;
+ cr = radial->cd1.radius;
+ dx = radial->cd2.center.x - cx;
+ dy = radial->cd2.center.y - cy;
+ dr = radial->cd2.radius - cr;
+
+ /* translate by -(cx, cy) to simplify computations */
+ x0 -= cx;
+ y0 -= cy;
+ x1 -= cx;
+ y1 -= cy;
+
+ /* enlarge boundaries slightly to avoid rounding problems in the
+ * parameter range computation */
+ x0 -= DBL_EPSILON;
+ y0 -= DBL_EPSILON;
+ x1 += DBL_EPSILON;
+ y1 += DBL_EPSILON;
+
+ /* enlarge boundaries even more to avoid rounding problems when
+ * testing if a point belongs to the box */
+ minx = x0 - DBL_EPSILON;
+ miny = y0 - DBL_EPSILON;
+ maxx = x1 + DBL_EPSILON;
+ maxy = y1 + DBL_EPSILON;
+
+ /* we dont' allow negative radiuses, so we will be checking that
+ * t*dr >= mindr to consider t valid */
+ mindr = -(cr + DBL_EPSILON);
+
+ /*
+ * After the previous transformations, the start circle is
+ * centered in the origin and has radius cr. A 1-unit change in
+ * the t parameter corresponds to dx,dy,dr changes in the x,y,r of
+ * the circle (center coordinates, radius).
+ *
+ * To compute the minimum range needed to correctly draw the
+ * pattern, we start with an empty range and extend it to include
+ * the circles touching the bounding box or within it.
+ */
+
+ /*
+ * Focus, the point where the circle has radius == 0.
+ *
+ * r = cr + t * dr = 0
+ * t = -cr / dr
+ *
+ * If the radius is constant (dr == 0) there is no focus (the
+ * gradient represents a cylinder instead of a cone).
+ */
+ if (fabs (dr) >= DBL_EPSILON) {
+ double t_focus;
+
+ t_focus = -cr / dr;
+ x_focus = t_focus * dx;
+ y_focus = t_focus * dy;
+ if (minx <= x_focus && x_focus <= maxx &&
+ miny <= y_focus && y_focus <= maxy)
+ {
+ valid = _extend_range (range, t_focus, valid);
+ }
+ }
+
+ /*
+ * Circles externally tangent to box edges.
+ *
+ * All circles have center in (dx, dy) * t
+ *
+ * If the circle is tangent to the line defined by the edge of the
+ * box, then at least one of the following holds true:
+ *
+ * (dx*t) + (cr + dr*t) == x0 (left edge)
+ * (dx*t) - (cr + dr*t) == x1 (right edge)
+ * (dy*t) + (cr + dr*t) == y0 (top edge)
+ * (dy*t) - (cr + dr*t) == y1 (bottom edge)
+ *
+ * The solution is only valid if the tangent point is actually on
+ * the edge, i.e. if its y coordinate is in [y0,y1] for left/right
+ * edges and if its x coordinate is in [x0,x1] for top/bottom
+ * edges.
+ *
+ * For the first equation:
+ *
+ * (dx + dr) * t = x0 - cr
+ * t = (x0 - cr) / (dx + dr)
+ * y = dy * t
+ *
+ * in the code this becomes:
+ *
+ * t_edge = (num) / (den)
+ * v = (delta) * t_edge
+ *
+ * If the denominator in t is 0, the pattern is tangent to a line
+ * parallel to the edge under examination. The corner-case where
+ * the boundary line is the same as the edge is handled by the
+ * focus point case and/or by the a==0 case.
+ */
+#define T_EDGE(num,den,delta,lower,upper) \
+ if (fabs (den) >= DBL_EPSILON) { \
+ double t_edge, v; \
+ \
+ t_edge = (num) / (den); \
+ v = t_edge * (delta); \
+ if (t_edge * dr >= mindr && (lower) <= v && v <= (upper)) \
+ valid = _extend_range (range, t_edge, valid); \
+ }
+
+ /* circles tangent (externally) to left/right/top/bottom edge */
+ T_EDGE (x0 - cr, dx + dr, dy, miny, maxy);
+ T_EDGE (x1 + cr, dx - dr, dy, miny, maxy);
+ T_EDGE (y0 - cr, dy + dr, dx, minx, maxx);
+ T_EDGE (y1 + cr, dy - dr, dx, minx, maxx);
+
+#undef T_EDGE
+
+ /*
+ * Circles passing through a corner.
+ *
+ * A circle passing through the point (x,y) satisfies:
+ *
+ * (x-t*dx)^2 + (y-t*dy)^2 == (cr + t*dr)^2
+ *
+ * If we set:
+ * a = dx^2 + dy^2 - dr^2
+ * b = x*dx + y*dy + cr*dr
+ * c = x^2 + y^2 - cr^2
+ * we have:
+ * a*t^2 - 2*b*t + c == 0
+ */
+ a = dx * dx + dy * dy - dr * dr;
+ if (fabs (a) < DBL_EPSILON * DBL_EPSILON) {
+ double b, maxd2;
+
+ /* Ensure that gradients with both a and dr small are
+ * considered degenerate.
+ * The floating point version of the degeneracy test implemented
+ * in _radial_pattern_is_degenerate() is:
+ *
+ * 1) The circles are practically the same size:
+ * |dr| < DBL_EPSILON
+ * AND
+ * 2a) The circles are both very small:
+ * min (r0, r1) < DBL_EPSILON
+ * OR
+ * 2b) The circles are very close to each other:
+ * max (|dx|, |dy|) < 2 * DBL_EPSILON
+ *
+ * Assuming that the gradient is not degenerate, we want to
+ * show that |a| < DBL_EPSILON^2 implies |dr| >= DBL_EPSILON.
+ *
+ * If the gradient is not degenerate yet it has |dr| <
+ * DBL_EPSILON, (2b) is false, thus:
+ *
+ * max (|dx|, |dy|) >= 2*DBL_EPSILON
+ * which implies:
+ * 4*DBL_EPSILON^2 <= max (|dx|, |dy|)^2 <= dx^2 + dy^2
+ *
+ * From the definition of a, we get:
+ * a = dx^2 + dy^2 - dr^2 < DBL_EPSILON^2
+ * dx^2 + dy^2 - DBL_EPSILON^2 < dr^2
+ * 3*DBL_EPSILON^2 < dr^2
+ *
+ * which is inconsistent with the hypotheses, thus |dr| <
+ * DBL_EPSILON is false or the gradient is degenerate.
+ */
+ assert (fabs (dr) >= DBL_EPSILON);
+
+ /*
+ * If a == 0, all the circles are tangent to a line in the
+ * focus point. If this line is within the box extents, we
+ * should add the circle with infinite radius, but this would
+ * make the range unbounded, so we add the smallest circle whose
+ * distance to the desired (degenerate) circle within the
+ * bounding box does not exceed tolerance.
+ *
+ * The equation of the line is b==0, i.e.:
+ * x*dx + y*dy + cr*dr == 0
+ *
+ * We compute the intersection of the line with the box and
+ * keep the intersection with maximum square distance (maxd2)
+ * from the focus point.
+ *
+ * In the code the intersection is represented in another
+ * coordinate system, whose origin is the focus point and
+ * which has a u,v axes, which are respectively orthogonal and
+ * parallel to the edge being intersected.
+ *
+ * The intersection is valid only if it belongs to the box,
+ * otherwise it is ignored.
+ *
+ * For example:
+ *
+ * y = y0
+ * x*dx + y0*dy + cr*dr == 0
+ * x = -(y0*dy + cr*dr) / dx
+ *
+ * which in (u,v) is:
+ * u = y0 - y_focus
+ * v = -(y0*dy + cr*dr) / dx - x_focus
+ *
+ * In the code:
+ * u = (edge) - (u_origin)
+ * v = -((edge) * (delta) + cr*dr) / (den) - v_focus
+ */
+#define T_EDGE(edge,delta,den,lower,upper,u_origin,v_origin) \
+ if (fabs (den) >= DBL_EPSILON) { \
+ double v; \
+ \
+ v = -((edge) * (delta) + cr * dr) / (den); \
+ if ((lower) <= v && v <= (upper)) { \
+ double u, d2; \
+ \
+ u = (edge) - (u_origin); \
+ v -= (v_origin); \
+ d2 = u*u + v*v; \
+ if (maxd2 < d2) \
+ maxd2 = d2; \
+ } \
+ }
+
+ maxd2 = 0;
+
+ /* degenerate circles (lines) passing through each edge */
+ T_EDGE (y0, dy, dx, minx, maxx, y_focus, x_focus);
+ T_EDGE (y1, dy, dx, minx, maxx, y_focus, x_focus);
+ T_EDGE (x0, dx, dy, miny, maxy, x_focus, y_focus);
+ T_EDGE (x1, dx, dy, miny, maxy, x_focus, y_focus);
+
+#undef T_EDGE
+
+ /*
+ * The limit circle can be transformed rigidly to the y=0 line
+ * and the circles tangent to it in (0,0) are:
+ *
+ * x^2 + (y-r)^2 = r^2 <=> x^2 + y^2 - 2*y*r = 0
+ *
+ * y is the distance from the line, in our case tolerance;
+ * x is the distance along the line, i.e. sqrt(maxd2),
+ * so:
+ *
+ * r = cr + dr * t = (maxd2 + tolerance^2) / (2*tolerance)
+ * t = (r - cr) / dr =
+ * (maxd2 + tolerance^2 - 2*tolerance*cr) / (2*tolerance*dr)
+ */
+ if (maxd2 > 0) {
+ double t_limit = maxd2 + tolerance*tolerance - 2*tolerance*cr;
+ t_limit /= 2 * tolerance * dr;
+ valid = _extend_range (range, t_limit, valid);
+ }
+
+ /*
+ * Nondegenerate, nonlimit circles passing through the corners.
+ *
+ * a == 0 && a*t^2 - 2*b*t + c == 0
+ *
+ * t = c / (2*b)
+ *
+ * The b == 0 case has just been handled, so we only have to
+ * compute this if b != 0.
+ */
+#define T_CORNER(x,y) \
+ b = (x) * dx + (y) * dy + cr * dr; \
+ if (fabs (b) >= DBL_EPSILON) { \
+ double t_corner; \
+ double x2 = (x) * (x); \
+ double y2 = (y) * (y); \
+ double cr2 = (cr) * (cr); \
+ double c = x2 + y2 - cr2; \
+ \
+ t_corner = 0.5 * c / b; \
+ if (t_corner * dr >= mindr) \
+ valid = _extend_range (range, t_corner, valid); \
+ }
+
+ /* circles touching each corner */
+ T_CORNER (x0, y0);
+ T_CORNER (x0, y1);
+ T_CORNER (x1, y0);
+ T_CORNER (x1, y1);
+
+#undef T_CORNER
+ } else {
+ double inva, b, c, d;
+
+ inva = 1 / a;
+
+ /*
+ * Nondegenerate, nonlimit circles passing through the corners.
+ *
+ * a != 0 && a*t^2 - 2*b*t + c == 0
+ *
+ * t = (b +- sqrt (b*b - a*c)) / a
+ *
+ * If the argument of sqrt() is negative, then no circle
+ * passes through the corner.
+ */
+#define T_CORNER(x,y) \
+ b = (x) * dx + (y) * dy + cr * dr; \
+ c = (x) * (x) + (y) * (y) - cr * cr; \
+ d = b * b - a * c; \
+ if (d >= 0) { \
+ double t_corner; \
+ \
+ d = sqrt (d); \
+ t_corner = (b + d) * inva; \
+ if (t_corner * dr >= mindr) \
+ valid = _extend_range (range, t_corner, valid); \
+ t_corner = (b - d) * inva; \
+ if (t_corner * dr >= mindr) \
+ valid = _extend_range (range, t_corner, valid); \
+ }
+
+ /* circles touching each corner */
+ T_CORNER (x0, y0);
+ T_CORNER (x0, y1);
+ T_CORNER (x1, y0);
+ T_CORNER (x1, y1);
+
+#undef T_CORNER
+ }
+}
+
+/**
+ * _cairo_gradient_pattern_box_to_parameter
+ *
+ * Compute a interpolation range sufficient to draw (within the given
+ * tolerance) the gradient in the given box getting the same result as
+ * using the (-inf, +inf) range.
+ *
+ * Assumes that the pattern is not degenerate. This can be guaranteed
+ * by simplifying it to a solid clear if _cairo_pattern_is_clear or to
+ * a solid color if _cairo_gradient_pattern_is_solid.
+ *
+ * The range isn't guaranteed to be minimal, but it tries to.
+ **/
+void
+_cairo_gradient_pattern_box_to_parameter (const cairo_gradient_pattern_t *gradient,
+ double x0, double y0,
+ double x1, double y1,
+ double tolerance,
+ double out_range[2])
{
- return linear->p1.x == linear->p2.x && linear->p1.y == linear->p2.y;
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ _cairo_linear_pattern_box_to_parameter ((cairo_linear_pattern_t *) gradient,
+ x0, y0, x1, y1, out_range);
+ } else {
+ _cairo_radial_pattern_box_to_parameter ((cairo_radial_pattern_t *) gradient,
+ x0, y0, x1, y1, tolerance, out_range);
+ }
}
-static cairo_bool_t
-_radial_pattern_is_degenerate (const cairo_radial_pattern_t *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)
{
- return radial->r1 == radial->r2 &&
- (radial->r1 == 0 /* && radial->r2 == 0 */ ||
- (radial->c1.x == radial->c2.x && radial->c1.y == radial->c2.y));
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+#define lerp(a,b) (a)*(1-t) + (b)*t
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+ out_circle->center.x = lerp (linear->pd1.x, linear->pd2.x);
+ out_circle->center.y = lerp (linear->pd1.y, linear->pd2.y);
+ out_circle->radius = 0;
+ } else {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+ out_circle->center.x = lerp (radial->cd1.center.x, radial->cd2.center.x);
+ out_circle->center.y = lerp (radial->cd1.center.y, radial->cd2.center.y);
+ out_circle->radius = lerp (radial->cd1.radius , radial->cd2.radius);
+ }
+
+#undef lerp
+}
+
+
+/**
+ * _cairo_gradient_pattern_fit_to_range
+ *
+ * Scale the extremes of a gradient to guarantee that the coordinates
+ * and their deltas are within the range (-max_value, max_value). The
+ * new extremes are stored in out_circle.
+ *
+ * The pattern matrix is scaled to guarantee that the aspect of the
+ * gradient is the same and the result is stored in out_matrix.
+ *
+ **/
+void
+_cairo_gradient_pattern_fit_to_range (const cairo_gradient_pattern_t *gradient,
+ double max_value,
+ cairo_matrix_t *out_matrix,
+ cairo_circle_double_t out_circle[2])
+{
+ double dim;
+
+ assert (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL);
+
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
+ cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
+
+ out_circle[0].center = linear->pd1;
+ out_circle[0].radius = 0;
+ out_circle[1].center = linear->pd2;
+ out_circle[1].radius = 0;
+
+ dim = fabs (linear->pd1.x);
+ dim = MAX (dim, fabs (linear->pd1.y));
+ dim = MAX (dim, fabs (linear->pd2.x));
+ dim = MAX (dim, fabs (linear->pd2.y));
+ dim = MAX (dim, fabs (linear->pd1.x - linear->pd2.x));
+ dim = MAX (dim, fabs (linear->pd1.y - linear->pd2.y));
+ } else {
+ cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) gradient;
+
+ out_circle[0] = radial->cd1;
+ out_circle[1] = radial->cd2;
+
+ dim = fabs (radial->cd1.center.x);
+ dim = MAX (dim, fabs (radial->cd1.center.y));
+ dim = MAX (dim, fabs (radial->cd1.radius));
+ dim = MAX (dim, fabs (radial->cd2.center.x));
+ dim = MAX (dim, fabs (radial->cd2.center.y));
+ dim = MAX (dim, fabs (radial->cd2.radius));
+ dim = MAX (dim, fabs (radial->cd1.center.x - radial->cd2.center.x));
+ dim = MAX (dim, fabs (radial->cd1.center.y - radial->cd2.center.y));
+ dim = MAX (dim, fabs (radial->cd1.radius - radial->cd2.radius));
+ }
+
+ if (unlikely (dim > max_value)) {
+ cairo_matrix_t scale;
+
+ dim = max_value / dim;
+
+ out_circle[0].center.x *= dim;
+ out_circle[0].center.y *= dim;
+ out_circle[0].radius *= dim;
+ out_circle[1].center.x *= dim;
+ out_circle[1].center.y *= dim;
+ out_circle[1].radius *= dim;
+
+ cairo_matrix_init_scale (&scale, dim, dim);
+ cairo_matrix_multiply (out_matrix, &gradient->base.matrix, &scale);
+ } else {
+ *out_matrix = gradient->base.matrix;
+ }
}
static cairo_bool_t
@@ -1786,27 +3128,40 @@ _gradient_is_clear (const cairo_gradient_pattern_t *gradient,
gradient->stops[0].offset == gradient->stops[gradient->n_stops - 1].offset))
return TRUE;
+ if (gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL) {
+ /* degenerate radial gradients are clear */
+ if (_radial_pattern_is_degenerate ((cairo_radial_pattern_t *) gradient))
+ return TRUE;
+ } else if (gradient->base.extend == CAIRO_EXTEND_NONE) {
+ /* EXTEND_NONE degenerate linear gradients are clear */
+ if (_linear_pattern_is_degenerate ((cairo_linear_pattern_t *) gradient))
+ return TRUE;
+ }
+
/* Check if the extents intersect the drawn part of the pattern. */
- if (gradient->base.type == CAIRO_PATTERN_TYPE_LINEAR) {
- if (gradient->base.extend == CAIRO_EXTEND_NONE) {
- cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) gradient;
- /* EXTEND_NONE degenerate linear gradients are clear */
- if (_linear_pattern_is_degenerate (linear))
+ if (extents != NULL &&
+ (gradient->base.extend == CAIRO_EXTEND_NONE ||
+ gradient->base.type == CAIRO_PATTERN_TYPE_RADIAL))
+ {
+ double t[2];
+
+ _cairo_gradient_pattern_box_to_parameter (gradient,
+ extents->x,
+ extents->y,
+ extents->x + extents->width,
+ extents->y + extents->height,
+ DBL_EPSILON,
+ t);
+
+ if (gradient->base.extend == CAIRO_EXTEND_NONE &&
+ (t[0] >= gradient->stops[gradient->n_stops - 1].offset ||
+ t[1] <= gradient->stops[0].offset))
+ {
return TRUE;
-
- if (extents != NULL) {
- 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))
- 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,6 +3282,141 @@ _gradient_color_average (const cairo_gradient_pattern_t *gradient,
}
/**
+ * _cairo_pattern_alpha_range
+ *
+ * Convenience function to determine the minimum and maximum alpha in
+ * the drawn part of a pattern (i.e. ignoring clear parts caused by
+ * extend modes and/or pattern shape).
+ *
+ * If not NULL, out_min and out_max will be set respectively to the
+ * minimum and maximum alpha value of the pattern.
+ **/
+void
+_cairo_pattern_alpha_range (const cairo_pattern_t *pattern,
+ double *out_min,
+ double *out_max)
+{
+ double alpha_min, alpha_max;
+
+ switch (pattern->type) {
+ case CAIRO_PATTERN_TYPE_SOLID: {
+ const cairo_solid_pattern_t *solid = (cairo_solid_pattern_t *) pattern;
+ alpha_min = alpha_max = solid->color.alpha;
+ break;
+ }
+
+ case CAIRO_PATTERN_TYPE_LINEAR:
+ case CAIRO_PATTERN_TYPE_RADIAL: {
+ const cairo_gradient_pattern_t *gradient = (cairo_gradient_pattern_t *) pattern;
+ unsigned int i;
+
+ assert (gradient->n_stops >= 1);
+
+ alpha_min = alpha_max = gradient->stops[0].color.alpha;
+ for (i = 1; i < gradient->n_stops; i++) {
+ if (alpha_min > gradient->stops[i].color.alpha)
+ alpha_min = gradient->stops[i].color.alpha;
+ else if (alpha_max < gradient->stops[i].color.alpha)
+ alpha_max = gradient->stops[i].color.alpha;
+ }
+
+ break;
+ }
+
+ case CAIRO_PATTERN_TYPE_MESH: {
+ const cairo_mesh_pattern_t *mesh = (const cairo_mesh_pattern_t *) pattern;
+ const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
+ unsigned int i, j, n = _cairo_array_num_elements (&mesh->patches);
+
+ assert (n >= 1);
+
+ alpha_min = alpha_max = patch[0].colors[0].alpha;
+ for (i = 0; i < n; i++) {
+ for (j = 0; j < 4; j++) {
+ if (patch[i].colors[j].alpha < alpha_min)
+ alpha_min = patch[i].colors[j].alpha;
+ else if (patch[i].colors[j].alpha > alpha_max)
+ alpha_max = patch[i].colors[j].alpha;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ ASSERT_NOT_REACHED;
+ /* fall through */
+
+ case CAIRO_PATTERN_TYPE_SURFACE:
+ alpha_min = 0;
+ alpha_max = 1;
+ break;
+ }
+
+ if (out_min)
+ *out_min = alpha_min;
+ if (out_max)
+ *out_max = alpha_max;
+}
+
+/**
+ * _cairo_mesh_pattern_coord_box
+ *
+ * Convenience function to determine the range of the coordinates of
+ * the points used to define the patches of the mesh.
+ *
+ * This is guaranteed to contain the pattern extents, but might not be
+ * tight, just like a Bezier curve is always inside the convex hull of
+ * the control points.
+ *
+ * This function cannot be used while the mesh is being constructed.
+ *
+ * The function returns TRUE and sets the output parametes to define
+ * the coodrinate range if the mesh pattern contains at least one
+ * patch, otherwise it returns FALSE.
+ **/
+cairo_bool_t
+_cairo_mesh_pattern_coord_box (const cairo_mesh_pattern_t *mesh,
+ double *out_xmin,
+ double *out_ymin,
+ double *out_xmax,
+ double *out_ymax)
+{
+ const cairo_mesh_patch_t *patch;
+ unsigned int num_patches, i, j, k;
+ double x0, y0, x1, y1;
+
+ assert (mesh->current_patch == NULL);
+
+ num_patches = _cairo_array_num_elements (&mesh->patches);
+
+ if (num_patches == 0)
+ return FALSE;
+
+ patch = _cairo_array_index_const (&mesh->patches, 0);
+ x0 = x1 = patch->points[0][0].x;
+ y0 = y1 = patch->points[0][0].y;
+
+ for (i = 0; i < num_patches; i++) {
+ for (j = 0; j < 4; j++) {
+ for (k = 0; k < 4; k++) {
+ x0 = MIN (x0, patch[i].points[j][k].x);
+ y0 = MIN (y0, patch[i].points[j][k].y);
+ x1 = MAX (x1, patch[i].points[j][k].x);
+ y1 = MAX (y1, patch[i].points[j][k].y);
+ }
+ }
+ }
+
+ *out_xmin = x0;
+ *out_ymin = y0;
+ *out_xmax = x1;
+ *out_ymax = y1;
+
+ return TRUE;
+}
+
+/**
* _cairo_gradient_pattern_is_solid
*
* Convenience function to determine whether a gradient pattern is
@@ -1966,7 +3456,13 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
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,6 +3483,22 @@ _cairo_gradient_pattern_is_solid (const cairo_gradient_pattern_t *gradient,
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
*
@@ -2078,7 +3590,13 @@ _gradient_is_opaque (const cairo_gradient_pattern_t *gradient,
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;
}
@@ -2119,6 +3637,8 @@ _cairo_pattern_is_opaque (const cairo_pattern_t *abstract_pattern,
case CAIRO_PATTERN_TYPE_LINEAR:
case CAIRO_PATTERN_TYPE_RADIAL:
return _gradient_is_opaque (&pattern->gradient.base, extents);
+ case CAIRO_PATTERN_TYPE_MESH:
+ return FALSE;
}
ASSERT_NOT_REACHED;
@@ -2142,6 +3662,8 @@ _cairo_pattern_is_clear (const cairo_pattern_t *abstract_pattern)
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;
@@ -2543,6 +4065,12 @@ _cairo_pattern_acquire_surface (const cairo_pattern_t *pattern,
surface_out,
attributes);
+ case CAIRO_PATTERN_TYPE_MESH:
+ return _cairo_pattern_acquire_surface_for_mesh ((cairo_mesh_pattern_t *) pattern,
+ dst, x, y, width, height,
+ surface_out,
+ attributes);
+
default:
ASSERT_NOT_REACHED;
return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
@@ -2695,40 +4223,32 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern,
(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 +4260,26 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern,
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 +4288,29 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern,
}
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;
}
@@ -2814,10 +4363,8 @@ _cairo_pattern_get_extents (const cairo_pattern_t *pattern,
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 +4386,7 @@ _cairo_gradient_color_stops_hash (unsigned long hash,
sizeof (double));
hash = _cairo_hash_bytes (hash,
&gradient->stops[n].color,
- sizeof (cairo_color_t));
+ sizeof (cairo_color_stop_t));
}
return hash;
@@ -2849,8 +4396,8 @@ unsigned long
_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,20 +4406,30 @@ unsigned long
_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_surface_pattern_hash (unsigned long hash,
- const cairo_pattern_t *pattern)
+_cairo_mesh_pattern_hash (unsigned long hash, const cairo_mesh_pattern_t *mesh)
{
- const cairo_surface_pattern_t *surface = (cairo_surface_pattern_t *) pattern;
+ const cairo_mesh_patch_t *patch = _cairo_array_index_const (&mesh->patches, 0);
+ unsigned int i, n = _cairo_array_num_elements (&mesh->patches);
+
+ for (i = 0; i < n; i++)
+ hash = _cairo_hash_bytes (hash, patch + i, sizeof (cairo_mesh_patch_t));
+
+ return hash;
+}
+static unsigned long
+_cairo_surface_pattern_hash (unsigned long hash,
+ const cairo_surface_pattern_t *surface)
+{
hash ^= surface->surface->unique_id;
return hash;
@@ -2901,62 +4458,25 @@ _cairo_pattern_hash (const cairo_pattern_t *pattern)
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);
default:
ASSERT_NOT_REACHED;
return FALSE;
}
}
-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 +4503,16 @@ cairo_bool_t
_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,34 +4522,54 @@ cairo_bool_t
_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);
}
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;
}
@@ -3061,15 +4601,20 @@ _cairo_pattern_equal (const cairo_pattern_t *a, const cairo_pattern_t *b)
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);
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);
default:
ASSERT_NOT_REACHED;
return FALSE;
@@ -3269,13 +4814,13 @@ cairo_pattern_get_linear_points (cairo_pattern_t *pattern,
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;
}
@@ -3313,17 +4858,273 @@ cairo_pattern_get_radial_circles (cairo_pattern_t *pattern,
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_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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;
+}
+
+/**
+ * cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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;
+}
+
+/**
+ * cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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;
+}
+
+/**
+ * cairo_pattern_mesh_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_pattern_mesh_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_pattern_mesh_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;
}