cairo/src/cairo-pattern.c
Billy Biggs 33599bc6b6 Fix for bug #4172:
Use _cairo_fixed_to_double and _cairo_double_to_fixed when converting between double and 16.16 fixed point. These functions round nicely, and solve some inconsistencies in rendering between my PPC and x86 machines. Thanks to Owen Taylor for suggesting the fix.
The above change does affect linear gradients slightly, but it seems worth it. Update the reference images to follow.
2005-08-21 14:10:39 +00:00

1649 lines
47 KiB
C

/* cairo - a vector graphics library with display and print output
*
* Copyright © 2004 David Reveman
* Copyright © 2005 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software
* and its documentation for any purpose is hereby granted without
* fee, provided that the above copyright notice appear in all copies
* and that both that copyright notice and this permission notice
* appear in supporting documentation, and that the name of David
* Reveman not be used in advertising or publicity pertaining to
* distribution of the software without specific, written prior
* permission. David Reveman makes no representations about the
* suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* DAVID REVEMAN DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL DAVID REVEMAN BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Author: David Reveman <davidr@novell.com>
*/
#include "cairoint.h"
typedef void (*cairo_shader_function_t) (unsigned char *color0,
unsigned char *color1,
cairo_fixed_t factor,
uint32_t *pixel);
typedef struct _cairo_shader_color_stop {
cairo_fixed_t offset;
cairo_fixed_48_16_t scale;
int id;
unsigned char color_char[4];
} cairo_shader_color_stop_t;
typedef struct _cairo_shader_op {
cairo_shader_color_stop_t *stops;
int n_stops;
cairo_extend_t extend;
cairo_shader_function_t shader_function;
} cairo_shader_op_t;
#define MULTIPLY_COLORCOMP(c1, c2) \
((unsigned char) \
((((unsigned char) (c1)) * (int) ((unsigned char) (c2))) / 0xff))
const cairo_solid_pattern_t cairo_pattern_nil = {
{ CAIRO_PATTERN_SOLID, /* type */
(unsigned int)-1, /* ref_count */
CAIRO_STATUS_NO_MEMORY, /* status */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_DEFAULT }, /* extend */
};
const cairo_solid_pattern_t cairo_pattern_nil_null_pointer = {
{ CAIRO_PATTERN_SOLID, /* type */
(unsigned int)-1, /* ref_count */
CAIRO_STATUS_NULL_POINTER,/* status */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_DEFAULT }, /* extend */
};
const cairo_solid_pattern_t cairo_pattern_nil_file_not_found = {
{ CAIRO_PATTERN_SOLID, /* type */
(unsigned int)-1, /* ref_count */
CAIRO_STATUS_FILE_NOT_FOUND, /* status */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_DEFAULT }, /* extend */
};
const cairo_solid_pattern_t cairo_pattern_nil_read_error = {
{ CAIRO_PATTERN_SOLID, /* type */
(unsigned int)-1, /* ref_count */
CAIRO_STATUS_READ_ERROR, /* status */
{ 1., 0., 0., 1., 0., 0., }, /* matrix */
CAIRO_FILTER_DEFAULT, /* filter */
CAIRO_EXTEND_DEFAULT }, /* extend */
};
static const cairo_pattern_t *
_cairo_pattern_nil_for_status (cairo_status_t status)
{
switch (status) {
case CAIRO_STATUS_NULL_POINTER:
return &cairo_pattern_nil_null_pointer.base;
case CAIRO_STATUS_FILE_NOT_FOUND:
return &cairo_pattern_nil_file_not_found.base;
case CAIRO_STATUS_READ_ERROR:
return &cairo_pattern_nil_read_error.base;
default:
case CAIRO_STATUS_NO_MEMORY:
return &cairo_pattern_nil.base;
}
}
/**
* _cairo_pattern_set_error:
* @pattern: a pattern
* @status: a status value indicating an error, (eg. not
* CAIRO_STATUS_SUCCESS)
*
* Sets pattern->status to @status and calls _cairo_error;
*
* All assignments of an error status to pattern->status should happen
* through _cairo_pattern_set_error() or else _cairo_error() should be
* called immediately after the assignment.
*
* 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 void
_cairo_pattern_set_error (cairo_pattern_t *pattern,
cairo_status_t status)
{
/* Don't overwrite an existing error. This preserves the first
* error, which is the most significant. It also avoids attempting
* to write to read-only data (eg. from a nil pattern). */
if (pattern->status == CAIRO_STATUS_SUCCESS)
pattern->status = status;
_cairo_error (status);
}
static void
_cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
{
pattern->type = type;
pattern->ref_count = 1;
pattern->status = CAIRO_STATUS_SUCCESS;
pattern->extend = CAIRO_EXTEND_DEFAULT;
pattern->filter = CAIRO_FILTER_DEFAULT;
cairo_matrix_init_identity (&pattern->matrix);
}
static void
_cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern,
const cairo_gradient_pattern_t *other)
{
if (other->base.type == CAIRO_PATTERN_LINEAR)
{
cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern;
cairo_linear_pattern_t *src = (cairo_linear_pattern_t *) other;
*dst = *src;
}
else
{
cairo_radial_pattern_t *dst = (cairo_radial_pattern_t *) pattern;
cairo_radial_pattern_t *src = (cairo_radial_pattern_t *) other;
*dst = *src;
}
if (other->n_stops)
{
pattern->stops = malloc (other->n_stops * sizeof (cairo_color_stop_t));
if (pattern->stops == NULL) {
_cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY);
return;
}
memcpy (pattern->stops, other->stops,
other->n_stops * sizeof (cairo_color_stop_t));
}
}
void
_cairo_pattern_init_copy (cairo_pattern_t *pattern,
const cairo_pattern_t *other)
{
if (other->status) {
_cairo_pattern_set_error (pattern, other->status);
return;
}
switch (other->type) {
case CAIRO_PATTERN_SOLID: {
cairo_solid_pattern_t *dst = (cairo_solid_pattern_t *) pattern;
cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) other;
*dst = *src;
} break;
case CAIRO_PATTERN_SURFACE: {
cairo_surface_pattern_t *dst = (cairo_surface_pattern_t *) pattern;
cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) other;
*dst = *src;
cairo_surface_reference (dst->surface);
} break;
case CAIRO_PATTERN_LINEAR:
case CAIRO_PATTERN_RADIAL: {
cairo_gradient_pattern_t *dst = (cairo_gradient_pattern_t *) pattern;
cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) other;
_cairo_gradient_pattern_init_copy (dst, src);
} break;
}
pattern->ref_count = 1;
}
void
_cairo_pattern_fini (cairo_pattern_t *pattern)
{
switch (pattern->type) {
case CAIRO_PATTERN_SOLID:
break;
case CAIRO_PATTERN_SURFACE: {
cairo_surface_pattern_t *surface_pattern =
(cairo_surface_pattern_t *) pattern;
cairo_surface_destroy (surface_pattern->surface);
} break;
case CAIRO_PATTERN_LINEAR:
case CAIRO_PATTERN_RADIAL: {
cairo_gradient_pattern_t *gradient =
(cairo_gradient_pattern_t *) pattern;
if (gradient->stops)
free (gradient->stops);
} break;
}
}
void
_cairo_pattern_init_solid (cairo_solid_pattern_t *pattern,
const cairo_color_t *color)
{
_cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SOLID);
pattern->color = *color;
}
void
_cairo_pattern_init_for_surface (cairo_surface_pattern_t *pattern,
cairo_surface_t *surface)
{
if (surface->status) {
/* Force to solid to simplify the pattern_fini process. */
pattern->base.type = CAIRO_PATTERN_SOLID;
_cairo_pattern_set_error (&pattern->base, surface->status);
return;
}
_cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SURFACE);
pattern->surface = cairo_surface_reference (surface);
}
static void
_cairo_pattern_init_gradient (cairo_gradient_pattern_t *pattern,
cairo_pattern_type_t type)
{
_cairo_pattern_init (&pattern->base, type);
pattern->stops = 0;
pattern->n_stops = 0;
}
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_LINEAR);
pattern->point0.x = x0;
pattern->point0.y = y0;
pattern->point1.x = x1;
pattern->point1.y = y1;
}
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_RADIAL);
pattern->center0.x = cx0;
pattern->center0.y = cy0;
pattern->radius0 = fabs (radius0);
pattern->center1.x = cx1;
pattern->center1.y = cy1;
pattern->radius1 = fabs (radius1);
}
cairo_pattern_t *
_cairo_pattern_create_solid (const cairo_color_t *color)
{
cairo_solid_pattern_t *pattern;
pattern = malloc (sizeof (cairo_solid_pattern_t));
if (pattern == NULL)
return (cairo_pattern_t *) &cairo_pattern_nil.base;
_cairo_pattern_init_solid (pattern, color);
return &pattern->base;
}
/**
* 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 succesful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
cairo_pattern_t *
cairo_pattern_create_rgb (double red, double green, double blue)
{
cairo_pattern_t *pattern;
cairo_color_t color;
_cairo_restrict_value (&red, 0.0, 1.0);
_cairo_restrict_value (&green, 0.0, 1.0);
_cairo_restrict_value (&blue, 0.0, 1.0);
_cairo_color_init_rgb (&color, red, green, blue);
pattern = _cairo_pattern_create_solid (&color);
if (pattern->status)
_cairo_error (pattern->status);
return pattern;
}
/**
* cairo_pattern_create_rgba:
* @red: red component of the color
* @green: green component of the color
* @blue: blue component of the color
* @alpha: alpha component of the color
*
* Creates a new cairo_pattern_t corresponding to a translucent color.
* The color components are floating point numbers in the range 0 to
* 1. If the values passed in are outside that range, they will be
* clamped.
*
* Return value: the newly created #cairo_pattern_t if succesful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
cairo_pattern_t *
cairo_pattern_create_rgba (double red, double green, double blue,
double alpha)
{
cairo_pattern_t *pattern;
cairo_color_t color;
_cairo_restrict_value (&red, 0.0, 1.0);
_cairo_restrict_value (&green, 0.0, 1.0);
_cairo_restrict_value (&blue, 0.0, 1.0);
_cairo_restrict_value (&alpha, 0.0, 1.0);
_cairo_color_init_rgba (&color, red, green, blue, alpha);
pattern = _cairo_pattern_create_solid (&color);
if (pattern->status)
_cairo_error (pattern->status);
return pattern;
}
/**
* 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 succesful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
cairo_pattern_t *
cairo_pattern_create_for_surface (cairo_surface_t *surface)
{
cairo_surface_pattern_t *pattern;
if (surface == NULL)
return (cairo_pattern_t*) _cairo_pattern_nil_for_status (CAIRO_STATUS_NULL_POINTER);
if (surface->status)
return (cairo_pattern_t*) _cairo_pattern_nil_for_status (surface->status);
pattern = malloc (sizeof (cairo_surface_pattern_t));
if (pattern == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_pattern_t *)&cairo_pattern_nil.base;
}
_cairo_pattern_init_for_surface (pattern, surface);
return &pattern->base;
}
/**
* 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 succesful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
cairo_pattern_t *
cairo_pattern_create_linear (double x0, double y0, double x1, double y1)
{
cairo_linear_pattern_t *pattern;
pattern = malloc (sizeof (cairo_linear_pattern_t));
if (pattern == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_pattern_t *) &cairo_pattern_nil.base;
}
_cairo_pattern_init_linear (pattern, x0, y0, x1, y1);
return &pattern->base.base;
}
/**
* cairo_pattern_create_radial:
* @cx0: x coordinate for the center of the start circle
* @cy0: y coordinate for the center of the start circle
* @radius0: radius of the start cirle
* @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 cirle
*
* Creates a new radial gradient cairo_pattern_t between the two
* circles defined by (x0, y0, c0) and (x1, y1, c0). 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 succesful, or
* an error pattern in case of no memory. The caller owns the
* returned object and should call cairo_pattern_destroy() when
* finished with it.
*
* This function will always return a valid pointer, but if an error
* occurred the pattern status will be set to an error. To inspect
* the status of a pattern use cairo_pattern_status().
**/
cairo_pattern_t *
cairo_pattern_create_radial (double cx0, double cy0, double radius0,
double cx1, double cy1, double radius1)
{
cairo_radial_pattern_t *pattern;
pattern = malloc (sizeof (cairo_radial_pattern_t));
if (pattern == NULL) {
_cairo_error (CAIRO_STATUS_NO_MEMORY);
return (cairo_pattern_t *) &cairo_pattern_nil.base;
}
_cairo_pattern_init_radial (pattern, cx0, cy0, radius0, cx1, cy1, radius1);
return &pattern->base.base;
}
/**
* cairo_pattern_reference:
* @pattern: a #cairo_pattern_t
*
* Increases the reference count on @pattern by one. This prevents
* @pattern from being destroyed until a matching call to
* cairo_pattern_destroy() is made.
*
* Return value: the referenced #cairo_pattern_t.
**/
cairo_pattern_t *
cairo_pattern_reference (cairo_pattern_t *pattern)
{
if (pattern == NULL)
return NULL;
if (pattern->ref_count == (unsigned int)-1)
return pattern;
pattern->ref_count++;
return pattern;
}
/**
* cairo_pattern_status:
* @pattern: a #cairo_pattern_t
*
* Checks whether an error has previously occurred for this
* pattern.
*
* Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_STATUS_NO_MEMORY, or
* %CAIRO_STATUS_PATTERN_TYPE_MISMATCH.
**/
cairo_status_t
cairo_pattern_status (cairo_pattern_t *pattern)
{
return pattern->status;
}
/**
* cairo_pattern_destroy:
* @pattern: a #cairo_pattern_t
*
* Decreases the reference count on @pattern by one. If the result is
* zero, then @pattern and all associated resources are freed. See
* cairo_pattern_reference().
**/
void
cairo_pattern_destroy (cairo_pattern_t *pattern)
{
if (pattern == NULL)
return;
if (pattern->ref_count == (unsigned int)-1)
return;
pattern->ref_count--;
if (pattern->ref_count)
return;
_cairo_pattern_fini (pattern);
free (pattern);
}
static void
_cairo_pattern_add_color_stop (cairo_gradient_pattern_t *pattern,
double offset,
cairo_color_t *color)
{
cairo_color_stop_t *stop;
cairo_color_stop_t *new_stops;
pattern->n_stops++;
new_stops = realloc (pattern->stops,
pattern->n_stops * sizeof (cairo_color_stop_t));
if (new_stops == NULL) {
_cairo_pattern_set_error (&pattern->base, CAIRO_STATUS_NO_MEMORY);
return;
}
pattern->stops = new_stops;
stop = &pattern->stops[pattern->n_stops - 1];
stop->offset = _cairo_fixed_from_double (offset);
stop->color = *color;
}
/**
* 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().
*
* 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_MISMATCH.
**/
void
cairo_pattern_add_color_stop_rgb (cairo_pattern_t *pattern,
double offset,
double red,
double green,
double blue)
{
cairo_color_t color;
if (pattern->status)
return;
if (pattern->type != CAIRO_PATTERN_LINEAR &&
pattern->type != CAIRO_PATTERN_RADIAL)
{
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
_cairo_restrict_value (&offset, 0.0, 1.0);
_cairo_restrict_value (&red, 0.0, 1.0);
_cairo_restrict_value (&green, 0.0, 1.0);
_cairo_restrict_value (&blue, 0.0, 1.0);
_cairo_color_init_rgb (&color, red, green, blue);
_cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
offset,
&color);
}
/**
* 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().
*
* 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_MISMATCH.
*/
void
cairo_pattern_add_color_stop_rgba (cairo_pattern_t *pattern,
double offset,
double red,
double green,
double blue,
double alpha)
{
cairo_color_t color;
if (pattern->status)
return;
if (pattern->type != CAIRO_PATTERN_LINEAR &&
pattern->type != CAIRO_PATTERN_RADIAL)
{
_cairo_pattern_set_error (pattern, CAIRO_STATUS_PATTERN_TYPE_MISMATCH);
return;
}
_cairo_restrict_value (&offset, 0.0, 1.0);
_cairo_restrict_value (&red, 0.0, 1.0);
_cairo_restrict_value (&green, 0.0, 1.0);
_cairo_restrict_value (&blue, 0.0, 1.0);
_cairo_restrict_value (&alpha, 0.0, 1.0);
_cairo_color_init_rgba (&color, red, green, blue, alpha);
_cairo_pattern_add_color_stop ((cairo_gradient_pattern_t *) pattern,
offset,
&color);
}
/**
* 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 (&amp;matrix, 0.5, 0.5);
* cairo_pattern_set_matrix (pattern, &amp;matrix);
* </programlisting></informalexample>
*
* Meanwhile, using values of 2.0 rather than 0.5 in the code above
* would cause the pattern to appear at half of its default size.
*
* Also, please note the discussion of the user-space locking
* semantics of cairo_set_source().
**/
void
cairo_pattern_set_matrix (cairo_pattern_t *pattern,
const cairo_matrix_t *matrix)
{
if (pattern->status)
return;
pattern->matrix = *matrix;
}
/**
* cairo_pattern_get_matrix:
* @pattern: a #cairo_pattern_t
* @matrix: return value for the matrix
*
* Stores the pattern's transformation matrix into @matrix.
**/
void
cairo_pattern_get_matrix (cairo_pattern_t *pattern, cairo_matrix_t *matrix)
{
*matrix = pattern->matrix;
}
void
cairo_pattern_set_filter (cairo_pattern_t *pattern, cairo_filter_t filter)
{
if (pattern->status)
return;
pattern->filter = filter;
}
cairo_filter_t
cairo_pattern_get_filter (cairo_pattern_t *pattern)
{
return pattern->filter;
}
void
cairo_pattern_set_extend (cairo_pattern_t *pattern, cairo_extend_t extend)
{
if (pattern->status)
return;
pattern->extend = extend;
}
cairo_extend_t
cairo_pattern_get_extend (cairo_pattern_t *pattern)
{
return pattern->extend;
}
void
_cairo_pattern_transform (cairo_pattern_t *pattern,
const cairo_matrix_t *ctm_inverse)
{
assert (pattern->status == CAIRO_STATUS_SUCCESS);
cairo_matrix_multiply (&pattern->matrix, ctm_inverse, &pattern->matrix);
}
#define INTERPOLATE_COLOR_NEAREST(c1, c2, factor) \
((factor < 32768)? c1: c2)
static void
_cairo_pattern_shader_nearest (unsigned char *color0,
unsigned char *color1,
cairo_fixed_t factor,
uint32_t *pixel)
{
*pixel =
((INTERPOLATE_COLOR_NEAREST (color0[3], color1[3], factor) << 24) |
(INTERPOLATE_COLOR_NEAREST (color0[0], color1[0], factor) << 16) |
(INTERPOLATE_COLOR_NEAREST (color0[1], color1[1], factor) << 8) |
(INTERPOLATE_COLOR_NEAREST (color0[2], color1[2], factor) << 0));
}
#undef INTERPOLATE_COLOR_NEAREST
#define INTERPOLATE_COLOR_LINEAR(c1, c2, factor) \
(((c2 * factor) + (c1 * (65536 - factor))) / 65536)
static void
_cairo_pattern_shader_linear (unsigned char *color0,
unsigned char *color1,
cairo_fixed_t factor,
uint32_t *pixel)
{
*pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) |
(INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) |
(INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor) << 8) |
(INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor) << 0));
}
#define E_MINUS_ONE 1.7182818284590452354
static void
_cairo_pattern_shader_gaussian (unsigned char *color0,
unsigned char *color1,
cairo_fixed_t factor,
uint32_t *pixel)
{
double f = _cairo_fixed_to_double (factor);
factor = _cairo_fixed_from_double ((exp (f * f) - 1.0) / E_MINUS_ONE);
*pixel = ((INTERPOLATE_COLOR_LINEAR (color0[3], color1[3], factor) << 24) |
(INTERPOLATE_COLOR_LINEAR (color0[0], color1[0], factor) << 16) |
(INTERPOLATE_COLOR_LINEAR (color0[1], color1[1], factor) << 8) |
(INTERPOLATE_COLOR_LINEAR (color0[2], color1[2], factor) << 0));
}
#undef INTERPOLATE_COLOR_LINEAR
static int
_cairo_shader_color_stop_compare (const void *elem1, const void *elem2)
{
cairo_shader_color_stop_t *s1 = (cairo_shader_color_stop_t *) elem1;
cairo_shader_color_stop_t *s2 = (cairo_shader_color_stop_t *) elem2;
return
(s1->offset == s2->offset) ?
/* equal offsets, sort on id */
((s1->id < s2->id) ? -1 : 1) :
/* sort on offset */
((s1->offset < s2->offset) ? -1 : 1);
}
static cairo_status_t
_cairo_pattern_shader_init (cairo_gradient_pattern_t *pattern,
cairo_shader_op_t *op)
{
int i;
op->stops = malloc (pattern->n_stops * sizeof (cairo_shader_color_stop_t));
if (!op->stops)
return CAIRO_STATUS_NO_MEMORY;
for (i = 0; i < pattern->n_stops; i++)
{
op->stops[i].color_char[0] = pattern->stops[i].color.red * 0xff;
op->stops[i].color_char[1] = pattern->stops[i].color.green * 0xff;
op->stops[i].color_char[2] = pattern->stops[i].color.blue * 0xff;
op->stops[i].color_char[3] = pattern->stops[i].color.alpha * 0xff;
op->stops[i].offset = pattern->stops[i].offset;
op->stops[i].id = i;
}
/* sort stops in ascending order */
qsort (op->stops, pattern->n_stops, sizeof (cairo_shader_color_stop_t),
_cairo_shader_color_stop_compare);
/* this scale value is used only when computing gradient values
* before the defined range, in which case stop 0 is used for both
* ends of the interpolation, making the value of 'scale' not
* actually matter, except that valgrind notices we're using
* an undefined value.
*/
op->stops[0].scale = 0;
for (i = 0; i < pattern->n_stops - 1; i++)
{
op->stops[i + 1].scale = op->stops[i + 1].offset - op->stops[i].offset;
if (op->stops[i + 1].scale == 65536)
op->stops[i + 1].scale = 0;
}
op->n_stops = pattern->n_stops;
op->extend = pattern->base.extend;
/* XXX: this is wrong, the filter should not be used for selecting
color stop interpolation function. function should always be 'linear'
and filter should be used for computing pixels. */
switch (pattern->base.filter) {
case CAIRO_FILTER_FAST:
case CAIRO_FILTER_NEAREST:
op->shader_function = _cairo_pattern_shader_nearest;
break;
case CAIRO_FILTER_GAUSSIAN:
op->shader_function = _cairo_pattern_shader_gaussian;
break;
case CAIRO_FILTER_GOOD:
case CAIRO_FILTER_BEST:
case CAIRO_FILTER_BILINEAR:
op->shader_function = _cairo_pattern_shader_linear;
break;
}
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_pattern_shader_fini (cairo_shader_op_t *op)
{
if (op->stops)
free (op->stops);
}
/* Find two color stops bounding the given offset. If the given offset
* is before the first or after the last stop offset, the nearest
* offset is returned twice.
*/
static void
_cairo_shader_op_find_color_stops (cairo_shader_op_t *op,
cairo_fixed_t offset,
cairo_shader_color_stop_t *stops[2])
{
int i;
/* Before first stop. */
if (offset <= op->stops[0].offset) {
stops[0] = &op->stops[0];
stops[1] = &op->stops[0];
return;
}
/* Between two stops. */
for (i = 0; i < op->n_stops - 1; i++) {
if (offset <= op->stops[i + 1].offset) {
stops[0] = &op->stops[i];
stops[1] = &op->stops[i + 1];
return;
}
}
/* After last stop. */
stops[0] = &op->stops[op->n_stops - 1];
stops[1] = &op->stops[op->n_stops - 1];
}
static void
_cairo_pattern_calc_color_at_pixel (cairo_shader_op_t *op,
cairo_fixed_t factor,
uint32_t *pixel)
{
cairo_shader_color_stop_t *stops[2];
switch (op->extend) {
case CAIRO_EXTEND_REPEAT:
factor -= factor & 0xffff0000;
break;
case CAIRO_EXTEND_REFLECT:
if (factor < 0 || factor > 65536) {
if ((factor >> 16) % 2)
factor = 65536 - (factor - (factor & 0xffff0000));
else
factor -= factor & 0xffff0000;
}
break;
case CAIRO_EXTEND_NONE:
break;
}
_cairo_shader_op_find_color_stops (op, factor, stops);
/* take offset as new 0 of coordinate system */
factor -= stops[0]->offset;
/* difference between two offsets == 0, abrubt change */
if (stops[1]->scale)
factor = ((cairo_fixed_48_16_t) factor << 16) /
stops[1]->scale;
op->shader_function (stops[0]->color_char,
stops[1]->color_char,
factor, pixel);
/* multiply alpha */
if (((unsigned char) (*pixel >> 24)) != 0xff) {
*pixel = (*pixel & 0xff000000) |
(MULTIPLY_COLORCOMP (*pixel >> 16, *pixel >> 24) << 16) |
(MULTIPLY_COLORCOMP (*pixel >> 8, *pixel >> 24) << 8) |
(MULTIPLY_COLORCOMP (*pixel >> 0, *pixel >> 24) << 0);
}
}
static cairo_status_t
_cairo_image_data_set_linear (cairo_linear_pattern_t *pattern,
double offset_x,
double offset_y,
uint32_t *pixels,
int width,
int height)
{
int x, y;
cairo_point_double_t point0, point1;
double a, b, c, d, tx, ty;
double scale, start, dx, dy, factor;
cairo_shader_op_t op;
cairo_status_t status;
status = _cairo_pattern_shader_init (&pattern->base, &op);
if (status)
return status;
/* We compute the position in the linear gradient for
* a point q as:
*
* [q . (p1 - p0) - p0 . (p1 - p0)] / (p1 - p0) ^ 2
*
* The computation is done in pattern space. The
* calculation could be heavily optimized by using the
* fact that 'factor' increases linearly in both
* directions.
*/
point0.x = pattern->point0.x;
point0.y = pattern->point0.y;
point1.x = pattern->point1.x;
point1.y = pattern->point1.y;
_cairo_matrix_get_affine (&pattern->base.base.matrix,
&a, &b, &c, &d, &tx, &ty);
dx = point1.x - point0.x;
dy = point1.y - point0.y;
scale = dx * dx + dy * dy;
scale = (scale) ? 1.0 / scale : 1.0;
start = dx * point0.x + dy * point0.y;
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
double qx_device = x + offset_x;
double qy_device = y + offset_y;
/* transform fragment into pattern space */
double qx = a * qx_device + c * qy_device + tx;
double qy = b * qx_device + d * qy_device + ty;
factor = ((dx * qx + dy * qy) - start) * scale;
_cairo_pattern_calc_color_at_pixel (&op, _cairo_fixed_from_double (factor), pixels++);
}
}
_cairo_pattern_shader_fini (&op);
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_linear_pattern_classify (cairo_linear_pattern_t *pattern,
double offset_x,
double offset_y,
int width,
int height,
cairo_bool_t *is_horizontal,
cairo_bool_t *is_vertical)
{
cairo_point_double_t point0, point1;
double a, b, c, d, tx, ty;
double scale, start, dx, dy;
cairo_fixed_t factors[3];
int i;
/* To classidy a pattern as horizontal or vertical, we first
* compute the (fixed point) factors at the corners of the
* pattern. We actually only need 3/4 corners, so we skip the
* fourth.
*/
point0.x = pattern->point0.x;
point0.y = pattern->point0.y;
point1.x = pattern->point1.x;
point1.y = pattern->point1.y;
_cairo_matrix_get_affine (&pattern->base.base.matrix,
&a, &b, &c, &d, &tx, &ty);
dx = point1.x - point0.x;
dy = point1.y - point0.y;
scale = dx * dx + dy * dy;
scale = (scale) ? 1.0 / scale : 1.0;
start = dx * point0.x + dy * point0.y;
for (i = 0; i < 3; i++) {
double qx_device = (i % 2) * (width - 1) + offset_x;
double qy_device = (i / 2) * (height - 1) + offset_y;
/* transform fragment into pattern space */
double qx = a * qx_device + c * qy_device + tx;
double qy = b * qx_device + d * qy_device + ty;
factors[i] = _cairo_fixed_from_double (((dx * qx + dy * qy) - start) * scale);
}
/* We consider a pattern to be vertical if the fixed point factor
* at the two upper corners is the same. We could accept a small
* change, but determining what change is acceptable would require
* sorting the stops in the pattern and looking at the differences.
*
* Horizontal works the same way with the two left corners.
*/
*is_vertical = factors[1] == factors[0];
*is_horizontal = factors[2] == factors[0];
}
static cairo_status_t
_cairo_image_data_set_radial (cairo_radial_pattern_t *pattern,
double offset_x,
double offset_y,
uint32_t *pixels,
int width,
int height)
{
int x, y, aligned_circles;
cairo_point_double_t c0, c1;
double px, py, ex, ey;
double a, b, c, d, tx, ty;
double r0, r1, c0_e_x, c0_e_y, c0_e, c1_e_x, c1_e_y, c1_e,
c0_c1_x, c0_c1_y, c0_c1, angle_c0, c1_y, y_x, c0_y, c0_x, r1_2,
denumerator, fraction, factor;
cairo_shader_op_t op;
cairo_status_t status;
status = _cairo_pattern_shader_init (&pattern->base, &op);
if (status)
return status;
c0.x = pattern->center0.x;
c0.y = pattern->center0.y;
r0 = pattern->radius0;
c1.x = pattern->center1.x;
c1.y = pattern->center1.y;
r1 = pattern->radius1;
if (c0.x != c1.x || c0.y != c1.y) {
aligned_circles = 0;
c0_c1_x = c1.x - c0.x;
c0_c1_y = c1.y - c0.y;
c0_c1 = sqrt (c0_c1_x * c0_c1_x + c0_c1_y * c0_c1_y);
r1_2 = r1 * r1;
} else {
aligned_circles = 1;
r1 = 1.0 / (r1 - r0);
r1_2 = c0_c1 = 0.0; /* shut up compiler */
}
_cairo_matrix_get_affine (&pattern->base.base.matrix,
&a, &b, &c, &d, &tx, &ty);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
px = x + offset_x;
py = y + offset_y;
/* transform fragment */
ex = a * px + c * py + tx;
ey = b * px + d * py + ty;
if (aligned_circles) {
ex = ex - c1.x;
ey = ey - c1.y;
factor = (sqrt (ex * ex + ey * ey) - r0) * r1;
} else {
/*
y (ex, ey)
c0 -------------------+---------- x
\ | __--
\ | __--
\ | __--
\ | __-- r1
\ | __--
c1 --
We need to calulate distance c0->x; the distance from
the inner circle center c0, through fragment position
(ex, ey) to point x where it crosses the outer circle.
From points c0, c1 and (ex, ey) we get angle C0. With
angle C0 we calculate distance c1->y and c0->y and by
knowing c1->y and r1, we also know y->x. Adding y->x to
c0->y gives us c0->x. The gradient offset can then be
calculated as:
offset = (c0->e - r0) / (c0->x - r0)
*/
c0_e_x = ex - c0.x;
c0_e_y = ey - c0.y;
c0_e = sqrt (c0_e_x * c0_e_x + c0_e_y * c0_e_y);
c1_e_x = ex - c1.x;
c1_e_y = ey - c1.y;
c1_e = sqrt (c1_e_x * c1_e_x + c1_e_y * c1_e_y);
denumerator = -2.0 * c0_e * c0_c1;
if (denumerator != 0.0) {
fraction = (c1_e * c1_e - c0_e * c0_e - c0_c1 * c0_c1) /
denumerator;
if (fraction > 1.0)
fraction = 1.0;
else if (fraction < -1.0)
fraction = -1.0;
angle_c0 = acos (fraction);
c0_y = cos (angle_c0) * c0_c1;
c1_y = sin (angle_c0) * c0_c1;
y_x = sqrt (r1_2 - c1_y * c1_y);
c0_x = y_x + c0_y;
factor = (c0_e - r0) / (c0_x - r0);
} else {
factor = -r0;
}
}
_cairo_pattern_calc_color_at_pixel (&op, _cairo_fixed_from_double (factor), pixels++);
}
}
_cairo_pattern_shader_fini (&op);
return CAIRO_STATUS_SUCCESS;
}
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_gradient (cairo_gradient_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **out,
cairo_surface_attributes_t *attr)
{
cairo_image_surface_t *image;
cairo_status_t status;
uint32_t *data;
cairo_bool_t repeat = FALSE;
if (pattern->base.type == CAIRO_PATTERN_LINEAR) {
cairo_bool_t is_horizontal;
cairo_bool_t is_vertical;
_cairo_linear_pattern_classify ((cairo_linear_pattern_t *)pattern,
x, y, width, height,
&is_horizontal, &is_vertical);
if (is_horizontal) {
height = 1;
repeat = TRUE;
}
if (is_vertical) {
width = 1;
repeat = TRUE;
}
}
data = malloc (width * height * 4);
if (data == NULL)
return CAIRO_STATUS_NO_MEMORY;
if (pattern->base.type == CAIRO_PATTERN_LINEAR)
{
cairo_linear_pattern_t *linear = (cairo_linear_pattern_t *) pattern;
status = _cairo_image_data_set_linear (linear, x, y, data,
width, height);
}
else
{
cairo_radial_pattern_t *radial = (cairo_radial_pattern_t *) pattern;
status = _cairo_image_data_set_radial (radial, x, y, data,
width, height);
}
if (status) {
free (data);
return status;
}
image = (cairo_image_surface_t *)
cairo_image_surface_create_for_data ((unsigned char *) data,
CAIRO_FORMAT_ARGB32,
width, height,
width * 4);
if (image->base.status) {
free (data);
return CAIRO_STATUS_NO_MEMORY;
}
_cairo_image_surface_assume_ownership_of_data (image);
status = _cairo_surface_clone_similar (dst, &image->base, out);
cairo_surface_destroy (&image->base);
attr->x_offset = -x;
attr->y_offset = -y;
cairo_matrix_init_identity (&attr->matrix);
attr->extend = repeat ? CAIRO_EXTEND_REPEAT : CAIRO_EXTEND_NONE;
attr->filter = CAIRO_FILTER_NEAREST;
attr->acquired = FALSE;
return status;
}
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_solid (cairo_solid_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **out,
cairo_surface_attributes_t *attribs)
{
*out = _cairo_surface_create_similar_solid (dst,
CAIRO_CONTENT_COLOR_ALPHA,
1, 1,
&pattern->color);
if ((*out)->status)
return CAIRO_STATUS_NO_MEMORY;
attribs->x_offset = attribs->y_offset = 0;
cairo_matrix_init_identity (&attribs->matrix);
attribs->extend = CAIRO_EXTEND_REPEAT;
attribs->filter = CAIRO_FILTER_NEAREST;
attribs->acquired = FALSE;
return CAIRO_STATUS_SUCCESS;
}
/**
* _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 (cairo_pattern_t *pattern)
{
cairo_solid_pattern_t *solid;
if (pattern->type != CAIRO_PATTERN_SOLID)
return FALSE;
solid = (cairo_solid_pattern_t *) pattern;
return CAIRO_ALPHA_IS_OPAQUE (solid->color.alpha);
}
static cairo_int_status_t
_cairo_pattern_acquire_surface_for_surface (cairo_surface_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_int_status_t status;
int tx, ty;
attr->acquired = FALSE;
if (_cairo_surface_is_image (dst))
{
cairo_image_surface_t *image;
status = _cairo_surface_acquire_source_image (pattern->surface,
&image,
&attr->extra);
if (status)
return status;
*out = &image->base;
attr->acquired = TRUE;
}
else
{
status = _cairo_surface_clone_similar (dst, pattern->surface, out);
}
attr->extend = pattern->base.extend;
attr->filter = pattern->base.filter;
if (_cairo_matrix_is_integer_translation (&pattern->base.matrix,
&tx, &ty))
{
cairo_matrix_init_identity (&attr->matrix);
attr->x_offset = tx;
attr->y_offset = ty;
attr->filter = CAIRO_FILTER_NEAREST;
}
else
{
attr->matrix = pattern->base.matrix;
attr->x_offset = attr->y_offset = 0;
}
return status;
}
/**
* _cairo_pattern_acquire_surface:
* @pattern: a #cairo_pattern_t
* @dst: destination surface
* @x: X coordinate in source corresponding to left side of destination area
* @y: Y coordinate in source corresponding to top side of destination area
* @width: width of destination area
* @height: height of destination area
* @surface_out: location to store a pointer to a surface
* @attributes: surface attributes that destination backend should apply to
* the returned surface
*
* A convenience function to obtain a surface to use as the source for
* drawing on @dst.
*
* Return value: %CAIRO_STATUS_SUCCESS if a surface was stored in @surface_out.
**/
cairo_int_status_t
_cairo_pattern_acquire_surface (cairo_pattern_t *pattern,
cairo_surface_t *dst,
int x,
int y,
unsigned int width,
unsigned int height,
cairo_surface_t **surface_out,
cairo_surface_attributes_t *attributes)
{
cairo_status_t status;
if (pattern->status) {
*surface_out = NULL;
attributes->acquired = FALSE;
return pattern->status;
}
switch (pattern->type) {
case CAIRO_PATTERN_SOLID: {
cairo_solid_pattern_t *src = (cairo_solid_pattern_t *) pattern;
status = _cairo_pattern_acquire_surface_for_solid (src, dst,
x, y, width, height,
surface_out,
attributes);
} break;
case CAIRO_PATTERN_LINEAR:
case CAIRO_PATTERN_RADIAL: {
cairo_gradient_pattern_t *src = (cairo_gradient_pattern_t *) pattern;
/* fast path for gradients with less than 2 color stops */
if (src->n_stops < 2)
{
const cairo_color_t *color;
cairo_solid_pattern_t solid;
if (src->n_stops)
color = &src->stops->color;
else
color = CAIRO_COLOR_TRANSPARENT;
_cairo_pattern_init_solid (&solid, color);
status = _cairo_pattern_acquire_surface_for_solid (&solid, dst,
x, y,
width, height,
surface_out,
attributes);
}
else
{
status = _cairo_pattern_acquire_surface_for_gradient (src, dst,
x, y,
width, height,
surface_out,
attributes);
}
} break;
case CAIRO_PATTERN_SURFACE: {
cairo_surface_pattern_t *src = (cairo_surface_pattern_t *) pattern;
status = _cairo_pattern_acquire_surface_for_surface (src, dst,
x, y, width, height,
surface_out,
attributes);
} break;
default:
status = CAIRO_INT_STATUS_UNSUPPORTED;
}
return status;
}
/**
* _cairo_pattern_release_surface:
* @pattern: a #cairo_pattern_t
* @surface: a surface obtained by _cairo_pattern_acquire_surface
* @attributes: attributes obtained by _cairo_pattern_acquire_surface
*
* Releases resources obtained by _cairo_pattern_acquire_surface.
**/
void
_cairo_pattern_release_surface (cairo_pattern_t *pattern,
cairo_surface_t *surface,
cairo_surface_attributes_t *attributes)
{
if (attributes->acquired)
{
cairo_surface_pattern_t *surface_pattern;
assert (pattern->type == CAIRO_PATTERN_SURFACE);
surface_pattern = (cairo_surface_pattern_t *) pattern;
_cairo_surface_release_source_image (surface_pattern->surface,
(cairo_image_surface_t *) surface,
attributes->extra);
}
else
{
cairo_surface_destroy (surface);
}
}
cairo_int_status_t
_cairo_pattern_acquire_surfaces (cairo_pattern_t *src,
cairo_pattern_t *mask,
cairo_surface_t *dst,
int src_x,
int src_y,
int mask_x,
int mask_y,
unsigned int width,
unsigned int height,
cairo_surface_t **src_out,
cairo_surface_t **mask_out,
cairo_surface_attributes_t *src_attributes,
cairo_surface_attributes_t *mask_attributes)
{
cairo_int_status_t status;
cairo_pattern_union_t src_tmp, mask_tmp;
if (src->status)
return src->status;
if (mask && mask->status)
return mask->status;
/* If src and mask are both solid, then the mask alpha can be
* combined into src and mask can be ignored. */
/* XXX: This optimization assumes that there is no color
* information in mask, so this will need to change when we
* support RENDER-style 4-channel masks. */
if (src->type == CAIRO_PATTERN_SOLID &&
mask && mask->type == CAIRO_PATTERN_SOLID)
{
cairo_color_t combined;
cairo_solid_pattern_t *src_solid = (cairo_solid_pattern_t *) src;
cairo_solid_pattern_t *mask_solid = (cairo_solid_pattern_t *) mask;
combined = src_solid->color;
_cairo_color_multiply_alpha (&combined, mask_solid->color.alpha);
_cairo_pattern_init_solid (&src_tmp.solid, &combined);
mask = NULL;
}
else
{
_cairo_pattern_init_copy (&src_tmp.base, src);
}
status = _cairo_pattern_acquire_surface (&src_tmp.base, dst,
src_x, src_y,
width, height,
src_out, src_attributes);
if (status) {
_cairo_pattern_fini (&src_tmp.base);
return status;
}
if (mask == NULL)
{
_cairo_pattern_fini (&src_tmp.base);
*mask_out = NULL;
return CAIRO_STATUS_SUCCESS;
}
_cairo_pattern_init_copy (&mask_tmp.base, mask);
status = _cairo_pattern_acquire_surface (&mask_tmp.base, dst,
mask_x, mask_y,
width, height,
mask_out, mask_attributes);
if (status)
_cairo_pattern_release_surface (&src_tmp.base,
*src_out, src_attributes);
_cairo_pattern_fini (&src_tmp.base);
_cairo_pattern_fini (&mask_tmp.base);
return status;
}