mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-02-06 03:30:28 +01:00
Track change in _cairo_pattern_release_surface and also pass the appropriate pattern for each acquired surface. The previous backend mismatch was causing memory leaks. Remove stale comment. Add missing fclose to keep valgrind happy about memory leaks.
1520 lines
42 KiB
C
1520 lines
42 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))
|
|
|
|
static const cairo_solid_pattern_t cairo_solid_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 */
|
|
{ 0.0, 0.0, 0.0, 1.0, /* solid black */
|
|
0x0, 0x0, 0x0, 0xffff }
|
|
};
|
|
|
|
static const cairo_surface_pattern_t cairo_surface_pattern_nil = {
|
|
{ CAIRO_PATTERN_SURFACE, /* 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 */
|
|
NULL /* surface */
|
|
};
|
|
|
|
static const cairo_linear_pattern_t cairo_linear_pattern_nil = {
|
|
{ { CAIRO_PATTERN_LINEAR, /* 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 */
|
|
NULL, /* stops */
|
|
0 }, /* n_stops */
|
|
{ 0., 0. }, { 1.0, 1.0 } /* point0, point1 */
|
|
};
|
|
|
|
static const cairo_radial_pattern_t cairo_radial_pattern_nil = {
|
|
{ { CAIRO_PATTERN_RADIAL, /* 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 */
|
|
NULL, /* stops */
|
|
0 }, /* n_stops */
|
|
{ 0., 0. }, { 0.0, 0.0 }, /* center0, center1 */
|
|
1.0, 1.0, /* radius0, radius1 */
|
|
};
|
|
|
|
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) {
|
|
if (other->base.type == CAIRO_PATTERN_LINEAR)
|
|
_cairo_gradient_pattern_init_copy (pattern, &cairo_linear_pattern_nil.base);
|
|
else
|
|
_cairo_gradient_pattern_init_copy (pattern, &cairo_radial_pattern_nil.base);
|
|
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)
|
|
{
|
|
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 *fini = (cairo_surface_pattern_t *) pattern;
|
|
|
|
cairo_surface_destroy (fini->surface);
|
|
} break;
|
|
case CAIRO_PATTERN_LINEAR:
|
|
case CAIRO_PATTERN_RADIAL: {
|
|
cairo_gradient_pattern_t *fini = (cairo_gradient_pattern_t *) pattern;
|
|
|
|
if (fini->n_stops)
|
|
free (fini->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)
|
|
{
|
|
_cairo_pattern_init (&pattern->base, CAIRO_PATTERN_SURFACE);
|
|
|
|
pattern->surface = 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_solid_pattern_nil.base;
|
|
|
|
_cairo_pattern_init_solid (pattern, color);
|
|
|
|
return &pattern->base;
|
|
}
|
|
|
|
/**
|
|
* _cairo_pattern_create_in_error:
|
|
* @status: an error status
|
|
*
|
|
* Create an empty #cairo_pattern_t object to hold an error
|
|
* status. This is useful for propagating status values from an
|
|
* existing object to a new #cairo_pattern_t.
|
|
*
|
|
* Return value: a (solid, black) #cairo_pattern_t object with status
|
|
* of @status. If there is insufficient memory a pointer to a special,
|
|
* static cairo_solid_pattern_nil will be returned instead with a
|
|
* status of CAIRO_STATUS_NO_MEMORY rather than @status.
|
|
*
|
|
* Return value:
|
|
**/
|
|
cairo_pattern_t *
|
|
_cairo_pattern_create_in_error (cairo_status_t status)
|
|
{
|
|
cairo_solid_pattern_t *pattern;
|
|
|
|
pattern = malloc (sizeof (cairo_solid_pattern_t));
|
|
if (pattern == NULL)
|
|
return (cairo_pattern_t *) &cairo_solid_pattern_nil.base;
|
|
|
|
_cairo_pattern_init_solid (pattern, CAIRO_COLOR_BLACK);
|
|
|
|
pattern->base.status = status;
|
|
|
|
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
|
|
*
|
|
* Create a new cairo_pattern_t corresponding to a 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_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);
|
|
|
|
return _cairo_pattern_create_solid (&color);
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*
|
|
* Create 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_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);
|
|
|
|
return _cairo_pattern_create_solid (&color);
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
|
|
pattern = malloc (sizeof (cairo_surface_pattern_t));
|
|
if (pattern == NULL)
|
|
return (cairo_pattern_t *)&cairo_surface_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().
|
|
*
|
|
* 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)
|
|
return (cairo_pattern_t *) &cairo_linear_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
|
|
*
|
|
* Create 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().
|
|
*
|
|
* 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)
|
|
return (cairo_pattern_t *) &cairo_radial_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.
|
|
**/
|
|
void
|
|
cairo_pattern_reference (cairo_pattern_t *pattern)
|
|
{
|
|
if (pattern == NULL)
|
|
return;
|
|
|
|
if (pattern->ref_count == (unsigned int)-1)
|
|
return;
|
|
|
|
pattern->ref_count++;
|
|
}
|
|
|
|
/**
|
|
* cairo_pattern_status:
|
|
* @pattern: a #cairo_pattern_t
|
|
*
|
|
* Checks whether an error has previously occurred for this
|
|
* pattern.
|
|
*
|
|
* Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY
|
|
**/
|
|
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) {
|
|
pattern->base.status = 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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
pattern->status = 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);
|
|
}
|
|
|
|
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)
|
|
{
|
|
pattern->status = 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);
|
|
}
|
|
|
|
void
|
|
cairo_pattern_set_matrix (cairo_pattern_t *pattern,
|
|
const cairo_matrix_t *matrix)
|
|
{
|
|
if (pattern->status)
|
|
return;
|
|
|
|
pattern->matrix = *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 = ((double) factor) / 65536.0;
|
|
|
|
factor = (cairo_fixed_t) (((exp (f * f) - 1.0) / E_MINUS_ONE) * 65536);
|
|
|
|
*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, factor * 65536, 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, factor * 65536, 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)
|
|
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 == NULL) {
|
|
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 == NULL)
|
|
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)
|
|
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 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 (&tmp.solid, &combined);
|
|
|
|
mask = NULL;
|
|
}
|
|
else
|
|
{
|
|
_cairo_pattern_init_copy (&tmp.base, src);
|
|
}
|
|
|
|
status = _cairo_pattern_acquire_surface (&tmp.base, dst,
|
|
src_x, src_y,
|
|
width, height,
|
|
src_out, src_attributes);
|
|
|
|
_cairo_pattern_fini (&tmp.base);
|
|
|
|
if (status)
|
|
return status;
|
|
|
|
if (mask)
|
|
{
|
|
_cairo_pattern_init_copy (&tmp.base, mask);
|
|
|
|
status = _cairo_pattern_acquire_surface (&tmp.base, dst,
|
|
mask_x, mask_y,
|
|
width, height,
|
|
mask_out, mask_attributes);
|
|
|
|
_cairo_pattern_fini (&tmp.base);
|
|
|
|
if (status)
|
|
{
|
|
_cairo_pattern_release_surface (dst, *src_out, src_attributes);
|
|
return status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*mask_out = NULL;
|
|
}
|
|
|
|
return CAIRO_STATUS_SUCCESS;
|
|
}
|