Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
cairo/src/cairo-gstate.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2404 lines (1915 sloc)
62.8 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* cairo - a vector graphics library with display and print output | |
* | |
* Copyright © 2002 University of Southern California | |
* | |
* This library is free software; you can redistribute it and/or | |
* modify it either under the terms of the GNU Lesser General Public | |
* License version 2.1 as published by the Free Software Foundation | |
* (the "LGPL") or, at your option, under the terms of the Mozilla | |
* Public License Version 1.1 (the "MPL"). If you do not alter this | |
* notice, a recipient may use your version of this file under either | |
* the MPL or the LGPL. | |
* | |
* You should have received a copy of the LGPL along with this library | |
* in the file COPYING-LGPL-2.1; if not, write to the Free Software | |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
* You should have received a copy of the MPL along with this library | |
* in the file COPYING-MPL-1.1 | |
* | |
* The contents of this file are subject to the Mozilla Public License | |
* Version 1.1 (the "License"); you may not use this file except in | |
* compliance with the License. You may obtain a copy of the License at | |
* http://www.mozilla.org/MPL/ | |
* | |
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY | |
* OF ANY KIND, either express or implied. See the LGPL or the MPL for | |
* the specific language governing rights and limitations. | |
* | |
* The Original Code is the cairo graphics library. | |
* | |
* The Initial Developer of the Original Code is University of Southern | |
* California. | |
* | |
* Contributor(s): | |
* Carl D. Worth <cworth@isi.edu> | |
*/ | |
#include <stdlib.h> | |
#include <math.h> | |
#include "cairoint.h" | |
static cairo_status_t | |
_cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, | |
cairo_pattern_t *src, | |
cairo_operator_t operator, | |
cairo_surface_t *dst, | |
cairo_traps_t *traps); | |
cairo_gstate_t * | |
_cairo_gstate_create () | |
{ | |
cairo_gstate_t *gstate; | |
gstate = malloc (sizeof (cairo_gstate_t)); | |
if (gstate) | |
_cairo_gstate_init (gstate); | |
return gstate; | |
} | |
void | |
_cairo_gstate_init (cairo_gstate_t *gstate) | |
{ | |
gstate->operator = CAIRO_GSTATE_OPERATOR_DEFAULT; | |
gstate->tolerance = CAIRO_GSTATE_TOLERANCE_DEFAULT; | |
gstate->line_width = CAIRO_GSTATE_LINE_WIDTH_DEFAULT; | |
gstate->line_cap = CAIRO_GSTATE_LINE_CAP_DEFAULT; | |
gstate->line_join = CAIRO_GSTATE_LINE_JOIN_DEFAULT; | |
gstate->miter_limit = CAIRO_GSTATE_MITER_LIMIT_DEFAULT; | |
gstate->fill_rule = CAIRO_GSTATE_FILL_RULE_DEFAULT; | |
gstate->dash = NULL; | |
gstate->num_dashes = 0; | |
gstate->dash_offset = 0.0; | |
gstate->font = _cairo_font_create (CAIRO_FONT_FAMILY_DEFAULT, | |
CAIRO_FONT_SLANT_DEFAULT, | |
CAIRO_FONT_WEIGHT_DEFAULT); | |
gstate->surface = NULL; | |
gstate->clip.region = NULL; | |
gstate->clip.surface = NULL; | |
gstate->pattern = _cairo_pattern_create_solid (1.0, 1.0, 1.0); | |
gstate->alpha = 1.0; | |
gstate->pixels_per_inch = CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT; | |
_cairo_gstate_default_matrix (gstate); | |
_cairo_path_init (&gstate->path); | |
_cairo_pen_init_empty (&gstate->pen_regular); | |
gstate->next = NULL; | |
} | |
cairo_status_t | |
_cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other) | |
{ | |
cairo_status_t status; | |
cairo_gstate_t *next; | |
/* Copy all members, but don't smash the next pointer */ | |
next = gstate->next; | |
*gstate = *other; | |
gstate->next = next; | |
/* Now fix up pointer data that needs to be cloned/referenced */ | |
if (other->dash) { | |
gstate->dash = malloc (other->num_dashes * sizeof (double)); | |
if (gstate->dash == NULL) | |
return CAIRO_STATUS_NO_MEMORY; | |
memcpy (gstate->dash, other->dash, other->num_dashes * sizeof (double)); | |
} | |
if (other->font) { | |
gstate->font = _cairo_font_copy (other->font); | |
if (!gstate->font) { | |
status = CAIRO_STATUS_NO_MEMORY; | |
goto CLEANUP_DASHES; | |
} | |
} | |
if (other->clip.region) | |
{ | |
gstate->clip.region = pixman_region_create (); | |
pixman_region_copy (gstate->clip.region, other->clip.region); | |
} | |
cairo_surface_reference (gstate->surface); | |
cairo_surface_reference (gstate->clip.surface); | |
cairo_pattern_reference (gstate->pattern); | |
status = _cairo_path_init_copy (&gstate->path, &other->path); | |
if (status) | |
goto CLEANUP_FONT; | |
status = _cairo_pen_init_copy (&gstate->pen_regular, &other->pen_regular); | |
if (status) | |
goto CLEANUP_PATH; | |
return status; | |
CLEANUP_PATH: | |
_cairo_path_fini (&gstate->path); | |
CLEANUP_FONT: | |
cairo_font_destroy (gstate->font); | |
CLEANUP_DASHES: | |
free (gstate->dash); | |
gstate->dash = NULL; | |
return status; | |
} | |
void | |
_cairo_gstate_fini (cairo_gstate_t *gstate) | |
{ | |
cairo_font_destroy (gstate->font); | |
if (gstate->surface) | |
cairo_surface_destroy (gstate->surface); | |
gstate->surface = NULL; | |
if (gstate->clip.surface) | |
cairo_surface_destroy (gstate->clip.surface); | |
gstate->clip.surface = NULL; | |
if (gstate->clip.region) | |
pixman_region_destroy (gstate->clip.region); | |
gstate->clip.region = NULL; | |
cairo_pattern_destroy (gstate->pattern); | |
_cairo_matrix_fini (&gstate->ctm); | |
_cairo_matrix_fini (&gstate->ctm_inverse); | |
_cairo_path_fini (&gstate->path); | |
_cairo_pen_fini (&gstate->pen_regular); | |
if (gstate->dash) { | |
free (gstate->dash); | |
gstate->dash = NULL; | |
} | |
} | |
void | |
_cairo_gstate_destroy (cairo_gstate_t *gstate) | |
{ | |
_cairo_gstate_fini (gstate); | |
free (gstate); | |
} | |
cairo_gstate_t* | |
_cairo_gstate_clone (cairo_gstate_t *gstate) | |
{ | |
cairo_status_t status; | |
cairo_gstate_t *clone; | |
clone = malloc (sizeof (cairo_gstate_t)); | |
if (clone) { | |
status = _cairo_gstate_init_copy (clone, gstate); | |
if (status) { | |
free (clone); | |
return NULL; | |
} | |
} | |
clone->next = NULL; | |
return clone; | |
} | |
cairo_status_t | |
_cairo_gstate_copy (cairo_gstate_t *dest, cairo_gstate_t *src) | |
{ | |
cairo_status_t status; | |
cairo_gstate_t *next; | |
/* Preserve next pointer over fini/init */ | |
next = dest->next; | |
_cairo_gstate_fini (dest); | |
status = _cairo_gstate_init_copy (dest, src); | |
dest->next = next; | |
return status; | |
} | |
/* Push rendering off to an off-screen group. */ | |
/* XXX: Rethinking this API | |
cairo_status_t | |
_cairo_gstate_begin_group (cairo_gstate_t *gstate) | |
{ | |
Pixmap pix; | |
cairo_color_t clear; | |
unsigned int width, height; | |
gstate->parent_surface = gstate->surface; | |
width = _cairo_surface_get_width (gstate->surface); | |
height = _cairo_surface_get_height (gstate->surface); | |
pix = XCreatePixmap (gstate->dpy, | |
_cairo_surface_get_drawable (gstate->surface), | |
width, height, | |
_cairo_surface_get_depth (gstate->surface)); | |
if (pix == 0) | |
return CAIRO_STATUS_NO_MEMORY; | |
gstate->surface = cairo_surface_create (gstate->dpy); | |
if (gstate->surface == NULL) | |
return CAIRO_STATUS_NO_MEMORY; | |
_cairo_surface_set_drawableWH (gstate->surface, pix, width, height); | |
_cairo_color_init (&clear); | |
_cairo_color_set_alpha (&clear, 0); | |
status = _cairo_surface_fill_rectangle (gstate->surface, | |
CAIRO_OPERATOR_SRC, | |
&clear, | |
0, 0, | |
_cairo_surface_get_width (gstate->surface), | |
_cairo_surface_get_height (gstate->surface)); | |
if (status) | |
return status; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
*/ | |
/* Complete the current offscreen group, composing its contents onto the parent surface. */ | |
/* XXX: Rethinking this API | |
cairo_status_t | |
_cairo_gstate_end_group (cairo_gstate_t *gstate) | |
{ | |
Pixmap pix; | |
cairo_color_t mask_color; | |
cairo_surface_t mask; | |
if (gstate->parent_surface == NULL) | |
return CAIRO_STATUS_INVALID_POP_GROUP; | |
_cairo_surface_init (&mask, gstate->dpy); | |
_cairo_color_init (&mask_color); | |
_cairo_color_set_alpha (&mask_color, gstate->alpha); | |
_cairo_surface_set_solid_color (&mask, &mask_color); | |
* XXX: This could be made much more efficient by using | |
_cairo_surface_get_damaged_width/Height if cairo_surface_t actually kept | |
track of such informaton. * | |
_cairo_surface_composite (gstate->operator, | |
gstate->surface, | |
mask, | |
gstate->parent_surface, | |
0, 0, | |
0, 0, | |
0, 0, | |
_cairo_surface_get_width (gstate->surface), | |
_cairo_surface_get_height (gstate->surface)); | |
_cairo_surface_fini (&mask); | |
pix = _cairo_surface_get_drawable (gstate->surface); | |
XFreePixmap (gstate->dpy, pix); | |
cairo_surface_destroy (gstate->surface); | |
gstate->surface = gstate->parent_surface; | |
gstate->parent_surface = NULL; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
*/ | |
cairo_status_t | |
_cairo_gstate_set_target_surface (cairo_gstate_t *gstate, cairo_surface_t *surface) | |
{ | |
double scale; | |
if (gstate->surface) | |
cairo_surface_destroy (gstate->surface); | |
gstate->surface = surface; | |
/* Sometimes the user wants to return to having no target surface, | |
* (just like after cairo_create). This can be useful for forcing | |
* the old surface to be destroyed. */ | |
if (surface == NULL) | |
return CAIRO_STATUS_SUCCESS; | |
cairo_surface_reference (gstate->surface); | |
scale = _cairo_surface_pixels_per_inch (surface) / gstate->pixels_per_inch; | |
_cairo_gstate_scale (gstate, scale, scale); | |
gstate->pixels_per_inch = _cairo_surface_pixels_per_inch (surface); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
/* XXX: Need to decide the memory mangement semantics of this | |
function. Should it reference the surface again? */ | |
cairo_surface_t * | |
_cairo_gstate_current_target_surface (cairo_gstate_t *gstate) | |
{ | |
if (gstate == NULL) | |
return NULL; | |
/* XXX: Do we want this? | |
if (gstate->surface) | |
_cairo_surface_reference (gstate->surface); | |
*/ | |
return gstate->surface; | |
} | |
cairo_status_t | |
_cairo_gstate_set_pattern (cairo_gstate_t *gstate, cairo_pattern_t *pattern) | |
{ | |
if (pattern == NULL) | |
return CAIRO_STATUS_NULL_POINTER; | |
if (gstate->pattern) | |
cairo_pattern_destroy (gstate->pattern); | |
gstate->pattern = pattern; | |
cairo_pattern_reference (pattern); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_pattern_t * | |
_cairo_gstate_current_pattern (cairo_gstate_t *gstate) | |
{ | |
if (gstate == NULL) | |
return NULL; | |
/* XXX: Do we want this? | |
cairo_pattern_reference (gstate->pattern); | |
*/ | |
return gstate->pattern; | |
} | |
cairo_status_t | |
_cairo_gstate_set_operator (cairo_gstate_t *gstate, cairo_operator_t operator) | |
{ | |
gstate->operator = operator; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_operator_t | |
_cairo_gstate_current_operator (cairo_gstate_t *gstate) | |
{ | |
return gstate->operator; | |
} | |
cairo_status_t | |
_cairo_gstate_set_rgb_color (cairo_gstate_t *gstate, double red, double green, double blue) | |
{ | |
cairo_pattern_destroy (gstate->pattern); | |
gstate->pattern = _cairo_pattern_create_solid (red, green, blue); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_current_rgb_color (cairo_gstate_t *gstate, double *red, double *green, double *blue) | |
{ | |
return _cairo_pattern_get_rgb (gstate->pattern, red, green, blue); | |
} | |
cairo_status_t | |
_cairo_gstate_set_tolerance (cairo_gstate_t *gstate, double tolerance) | |
{ | |
gstate->tolerance = tolerance; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
double | |
_cairo_gstate_current_tolerance (cairo_gstate_t *gstate) | |
{ | |
return gstate->tolerance; | |
} | |
cairo_status_t | |
_cairo_gstate_set_alpha (cairo_gstate_t *gstate, double alpha) | |
{ | |
gstate->alpha = alpha; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
double | |
_cairo_gstate_current_alpha (cairo_gstate_t *gstate) | |
{ | |
return gstate->alpha; | |
} | |
cairo_status_t | |
_cairo_gstate_set_fill_rule (cairo_gstate_t *gstate, cairo_fill_rule_t fill_rule) | |
{ | |
gstate->fill_rule = fill_rule; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_fill_rule_t | |
_cairo_gstate_current_fill_rule (cairo_gstate_t *gstate) | |
{ | |
return gstate->fill_rule; | |
} | |
cairo_status_t | |
_cairo_gstate_set_line_width (cairo_gstate_t *gstate, double width) | |
{ | |
gstate->line_width = width; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
double | |
_cairo_gstate_current_line_width (cairo_gstate_t *gstate) | |
{ | |
return gstate->line_width; | |
} | |
cairo_status_t | |
_cairo_gstate_set_line_cap (cairo_gstate_t *gstate, cairo_line_cap_t line_cap) | |
{ | |
gstate->line_cap = line_cap; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_line_cap_t | |
_cairo_gstate_current_line_cap (cairo_gstate_t *gstate) | |
{ | |
return gstate->line_cap; | |
} | |
cairo_status_t | |
_cairo_gstate_set_line_join (cairo_gstate_t *gstate, cairo_line_join_t line_join) | |
{ | |
gstate->line_join = line_join; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_line_join_t | |
_cairo_gstate_current_line_join (cairo_gstate_t *gstate) | |
{ | |
return gstate->line_join; | |
} | |
cairo_status_t | |
_cairo_gstate_set_dash (cairo_gstate_t *gstate, double *dash, int num_dashes, double offset) | |
{ | |
if (gstate->dash) { | |
free (gstate->dash); | |
gstate->dash = NULL; | |
} | |
gstate->num_dashes = num_dashes; | |
if (gstate->num_dashes) { | |
gstate->dash = malloc (gstate->num_dashes * sizeof (double)); | |
if (gstate->dash == NULL) { | |
gstate->num_dashes = 0; | |
return CAIRO_STATUS_NO_MEMORY; | |
} | |
} | |
memcpy (gstate->dash, dash, gstate->num_dashes * sizeof (double)); | |
gstate->dash_offset = offset; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_set_miter_limit (cairo_gstate_t *gstate, double limit) | |
{ | |
gstate->miter_limit = limit; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
double | |
_cairo_gstate_current_miter_limit (cairo_gstate_t *gstate) | |
{ | |
return gstate->miter_limit; | |
} | |
void | |
_cairo_gstate_current_matrix (cairo_gstate_t *gstate, cairo_matrix_t *matrix) | |
{ | |
cairo_matrix_copy (matrix, &gstate->ctm); | |
} | |
cairo_status_t | |
_cairo_gstate_translate (cairo_gstate_t *gstate, double tx, double ty) | |
{ | |
cairo_matrix_t tmp; | |
_cairo_matrix_set_translate (&tmp, tx, ty); | |
cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); | |
_cairo_matrix_set_translate (&tmp, -tx, -ty); | |
cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_scale (cairo_gstate_t *gstate, double sx, double sy) | |
{ | |
cairo_matrix_t tmp; | |
if (sx == 0 || sy == 0) | |
return CAIRO_STATUS_INVALID_MATRIX; | |
_cairo_matrix_set_scale (&tmp, sx, sy); | |
cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); | |
_cairo_matrix_set_scale (&tmp, 1/sx, 1/sy); | |
cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_rotate (cairo_gstate_t *gstate, double angle) | |
{ | |
cairo_matrix_t tmp; | |
_cairo_matrix_set_rotate (&tmp, angle); | |
cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); | |
_cairo_matrix_set_rotate (&tmp, -angle); | |
cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_concat_matrix (cairo_gstate_t *gstate, | |
cairo_matrix_t *matrix) | |
{ | |
cairo_matrix_t tmp; | |
cairo_matrix_copy (&tmp, matrix); | |
cairo_matrix_multiply (&gstate->ctm, &tmp, &gstate->ctm); | |
cairo_matrix_invert (&tmp); | |
cairo_matrix_multiply (&gstate->ctm_inverse, &gstate->ctm_inverse, &tmp); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_set_matrix (cairo_gstate_t *gstate, | |
cairo_matrix_t *matrix) | |
{ | |
cairo_status_t status; | |
cairo_matrix_copy (&gstate->ctm, matrix); | |
cairo_matrix_copy (&gstate->ctm_inverse, matrix); | |
status = cairo_matrix_invert (&gstate->ctm_inverse); | |
if (status) | |
return status; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_default_matrix (cairo_gstate_t *gstate) | |
{ | |
int scale = gstate->pixels_per_inch / CAIRO_GSTATE_PIXELS_PER_INCH_DEFAULT + 0.5; | |
if (scale == 0) | |
scale = 1; | |
cairo_matrix_set_identity (&gstate->ctm); | |
cairo_matrix_scale (&gstate->ctm, scale, scale); | |
cairo_matrix_copy (&gstate->ctm_inverse, &gstate->ctm); | |
cairo_matrix_invert (&gstate->ctm_inverse); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_identity_matrix (cairo_gstate_t *gstate) | |
{ | |
cairo_matrix_set_identity (&gstate->ctm); | |
cairo_matrix_set_identity (&gstate->ctm_inverse); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_transform_point (cairo_gstate_t *gstate, double *x, double *y) | |
{ | |
cairo_matrix_transform_point (&gstate->ctm, x, y); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy) | |
{ | |
cairo_matrix_transform_distance (&gstate->ctm, dx, dy); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_inverse_transform_point (cairo_gstate_t *gstate, double *x, double *y) | |
{ | |
cairo_matrix_transform_point (&gstate->ctm_inverse, x, y); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_inverse_transform_distance (cairo_gstate_t *gstate, double *dx, double *dy) | |
{ | |
cairo_matrix_transform_distance (&gstate->ctm_inverse, dx, dy); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_new_path (cairo_gstate_t *gstate) | |
{ | |
_cairo_path_fini (&gstate->path); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_move_to (cairo_gstate_t *gstate, double x, double y) | |
{ | |
cairo_point_t point; | |
cairo_matrix_transform_point (&gstate->ctm, &x, &y); | |
point.x = _cairo_fixed_from_double (x); | |
point.y = _cairo_fixed_from_double (y); | |
return _cairo_path_move_to (&gstate->path, &point); | |
} | |
cairo_status_t | |
_cairo_gstate_line_to (cairo_gstate_t *gstate, double x, double y) | |
{ | |
cairo_point_t point; | |
cairo_matrix_transform_point (&gstate->ctm, &x, &y); | |
point.x = _cairo_fixed_from_double (x); | |
point.y = _cairo_fixed_from_double (y); | |
return _cairo_path_line_to (&gstate->path, &point); | |
} | |
cairo_status_t | |
_cairo_gstate_curve_to (cairo_gstate_t *gstate, | |
double x0, double y0, | |
double x1, double y1, | |
double x2, double y2) | |
{ | |
cairo_point_t p0, p1, p2; | |
cairo_matrix_transform_point (&gstate->ctm, &x0, &y0); | |
cairo_matrix_transform_point (&gstate->ctm, &x1, &y1); | |
cairo_matrix_transform_point (&gstate->ctm, &x2, &y2); | |
p0.x = _cairo_fixed_from_double (x0); | |
p0.y = _cairo_fixed_from_double (y0); | |
p1.x = _cairo_fixed_from_double (x1); | |
p1.y = _cairo_fixed_from_double (y1); | |
p2.x = _cairo_fixed_from_double (x2); | |
p2.y = _cairo_fixed_from_double (y2); | |
return _cairo_path_curve_to (&gstate->path, &p0, &p1, &p2); | |
} | |
/* Spline deviation from the circle in radius would be given by: | |
error = sqrt (x**2 + y**2) - 1 | |
A simpler error function to work with is: | |
e = x**2 + y**2 - 1 | |
From "Good approximation of circles by curvature-continuous Bezier | |
curves", Tor Dokken and Morten Daehlen, Computer Aided Geometric | |
Design 8 (1990) 22-41, we learn: | |
abs (max(e)) = 4/27 * sin**6(angle/4) / cos**2(angle/4) | |
and | |
abs (error) =~ 1/2 * e | |
Of course, this error value applies only for the particular spline | |
approximation that is used in _cairo_gstate_arc_segment. | |
*/ | |
static double | |
_arc_error_normalized (double angle) | |
{ | |
return 2.0/27.0 * pow (sin (angle / 4), 6) / pow (cos (angle / 4), 2); | |
} | |
static double | |
_arc_max_angle_for_tolerance_normalized (double tolerance) | |
{ | |
double angle, error; | |
int i; | |
/* Use table lookup to reduce search time in most cases. */ | |
struct { | |
double angle; | |
double error; | |
} table[] = { | |
{ M_PI / 1.0, 0.0185185185185185036127 }, | |
{ M_PI / 2.0, 0.000272567143730179811158 }, | |
{ M_PI / 3.0, 2.38647043651461047433e-05 }, | |
{ M_PI / 4.0, 4.2455377443222443279e-06 }, | |
{ M_PI / 5.0, 1.11281001494389081528e-06 }, | |
{ M_PI / 6.0, 3.72662000942734705475e-07 }, | |
{ M_PI / 7.0, 1.47783685574284411325e-07 }, | |
{ M_PI / 8.0, 6.63240432022601149057e-08 }, | |
{ M_PI / 9.0, 3.2715520137536980553e-08 }, | |
{ M_PI / 10.0, 1.73863223499021216974e-08 }, | |
{ M_PI / 11.0, 9.81410988043554039085e-09 }, | |
}; | |
int table_size = (sizeof (table) / sizeof (table[0])); | |
for (i = 0; i < table_size; i++) | |
if (table[i].error < tolerance) | |
return table[i].angle; | |
++i; | |
do { | |
angle = M_PI / i++; | |
error = _arc_error_normalized (angle); | |
} while (error > tolerance); | |
return angle; | |
} | |
static int | |
_cairo_gstate_arc_segments_needed (cairo_gstate_t *gstate, | |
double angle, | |
double radius) | |
{ | |
double l1, l2, lmax; | |
double max_angle; | |
_cairo_matrix_compute_eigen_values (&gstate->ctm, &l1, &l2); | |
l1 = fabs (l1); | |
l2 = fabs (l2); | |
if (l1 > l2) | |
lmax = l1; | |
else | |
lmax = l2; | |
max_angle = _arc_max_angle_for_tolerance_normalized (gstate->tolerance / (radius * lmax)); | |
return (int) ceil (angle / max_angle); | |
} | |
/* We want to draw a single spline approximating a circular arc radius | |
R from angle A to angle B. Since we want a symmetric spline that | |
matches the endpoints of the arc in position and slope, we know | |
that the spline control points must be: | |
(R * cos(A), R * sin(A)) | |
(R * cos(A) - h * sin(A), R * sin(A) + h * cos (A)) | |
(R * cos(B) + h * sin(B), R * sin(B) - h * cos (B)) | |
(R * cos(B), R * sin(B)) | |
for some value of h. | |
"Approximation of circular arcs by cubic poynomials", Michael | |
Goldapp, Computer Aided Geometric Design 8 (1991) 227-238, provides | |
various values of h along with error analysis for each. | |
From that paper, a very practical value of h is: | |
h = 4/3 * tan(angle/4) | |
This value does not give the spline with minimal error, but it does | |
provide a very good approximation, (6th-order convergence), and the | |
error expression is quite simple, (see the comment for | |
_arc_error_normalized). | |
*/ | |
static cairo_status_t | |
_cairo_gstate_arc_segment (cairo_gstate_t *gstate, | |
double xc, double yc, | |
double radius, | |
double angle_A, double angle_B) | |
{ | |
cairo_status_t status; | |
double r_sin_A, r_cos_A; | |
double r_sin_B, r_cos_B; | |
double h; | |
r_sin_A = radius * sin (angle_A); | |
r_cos_A = radius * cos (angle_A); | |
r_sin_B = radius * sin (angle_B); | |
r_cos_B = radius * cos (angle_B); | |
h = 4.0/3.0 * tan ((angle_B - angle_A) / 4.0); | |
status = _cairo_gstate_curve_to (gstate, | |
xc + r_cos_A - h * r_sin_A, yc + r_sin_A + h * r_cos_A, | |
xc + r_cos_B + h * r_sin_B, yc + r_sin_B - h * r_cos_B, | |
xc + r_cos_B, yc + r_sin_B); | |
if (status) | |
return status; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
static cairo_status_t | |
_cairo_gstate_arc_dir (cairo_gstate_t *gstate, | |
double xc, double yc, | |
double radius, | |
double angle_min, | |
double angle_max, | |
cairo_direction_t dir) | |
{ | |
cairo_status_t status; | |
while (angle_max - angle_min > 4 * M_PI) | |
angle_max -= 2 * M_PI; | |
/* Recurse if drawing arc larger than pi */ | |
if (angle_max - angle_min > M_PI) { | |
double angle_mid = angle_min + (angle_max - angle_min) / 2.0; | |
/* XXX: Something tells me this block could be condensed. */ | |
if (dir == CAIRO_DIRECTION_FORWARD) { | |
status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, | |
angle_min, angle_mid, dir); | |
if (status) | |
return status; | |
status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, | |
angle_mid, angle_max, dir); | |
if (status) | |
return status; | |
} else { | |
status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, | |
angle_mid, angle_max, dir); | |
if (status) | |
return status; | |
status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, | |
angle_min, angle_mid, dir); | |
if (status) | |
return status; | |
} | |
} else { | |
int i, segments; | |
double angle, angle_step; | |
segments = _cairo_gstate_arc_segments_needed (gstate, | |
angle_max - angle_min, | |
radius); | |
angle_step = (angle_max - angle_min) / (double) segments; | |
if (dir == CAIRO_DIRECTION_FORWARD) { | |
angle = angle_min; | |
} else { | |
angle = angle_max; | |
angle_step = - angle_step; | |
} | |
for (i = 0; i < segments; i++, angle += angle_step) { | |
_cairo_gstate_arc_segment (gstate, | |
xc, yc, | |
radius, | |
angle, | |
angle + angle_step); | |
} | |
} | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_arc (cairo_gstate_t *gstate, | |
double xc, double yc, | |
double radius, | |
double angle1, double angle2) | |
{ | |
cairo_status_t status; | |
if (radius <= 0.0) | |
return CAIRO_STATUS_SUCCESS; | |
while (angle2 < angle1) | |
angle2 += 2 * M_PI; | |
status = _cairo_gstate_line_to (gstate, | |
xc + radius * cos (angle1), | |
yc + radius * sin (angle1)); | |
if (status) | |
return status; | |
status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, | |
angle1, angle2, CAIRO_DIRECTION_FORWARD); | |
if (status) | |
return status; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_arc_negative (cairo_gstate_t *gstate, | |
double xc, double yc, | |
double radius, | |
double angle1, double angle2) | |
{ | |
cairo_status_t status; | |
if (radius <= 0.0) | |
return CAIRO_STATUS_SUCCESS; | |
while (angle2 > angle1) | |
angle2 -= 2 * M_PI; | |
status = _cairo_gstate_line_to (gstate, | |
xc + radius * cos (angle1), | |
yc + radius * sin (angle1)); | |
if (status) | |
return status; | |
status = _cairo_gstate_arc_dir (gstate, xc, yc, radius, | |
angle2, angle1, CAIRO_DIRECTION_REVERSE); | |
if (status) | |
return status; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
/* XXX: NYI | |
cairo_status_t | |
_cairo_gstate_arc_to (cairo_gstate_t *gstate, | |
double x1, double y1, | |
double x2, double y2, | |
double radius) | |
{ | |
} | |
*/ | |
cairo_status_t | |
_cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy) | |
{ | |
cairo_distance_t distance; | |
cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); | |
distance.dx = _cairo_fixed_from_double (dx); | |
distance.dy = _cairo_fixed_from_double (dy); | |
return _cairo_path_rel_move_to (&gstate->path, &distance); | |
} | |
cairo_status_t | |
_cairo_gstate_rel_line_to (cairo_gstate_t *gstate, double dx, double dy) | |
{ | |
cairo_distance_t distance; | |
cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); | |
distance.dx = _cairo_fixed_from_double (dx); | |
distance.dy = _cairo_fixed_from_double (dy); | |
return _cairo_path_rel_line_to (&gstate->path, &distance); | |
} | |
cairo_status_t | |
_cairo_gstate_rel_curve_to (cairo_gstate_t *gstate, | |
double dx0, double dy0, | |
double dx1, double dy1, | |
double dx2, double dy2) | |
{ | |
cairo_distance_t distance[3]; | |
cairo_matrix_transform_distance (&gstate->ctm, &dx0, &dy0); | |
cairo_matrix_transform_distance (&gstate->ctm, &dx1, &dy1); | |
cairo_matrix_transform_distance (&gstate->ctm, &dx2, &dy2); | |
distance[0].dx = _cairo_fixed_from_double (dx0); | |
distance[0].dy = _cairo_fixed_from_double (dy0); | |
distance[1].dx = _cairo_fixed_from_double (dx1); | |
distance[1].dy = _cairo_fixed_from_double (dy1); | |
distance[2].dx = _cairo_fixed_from_double (dx2); | |
distance[2].dy = _cairo_fixed_from_double (dy2); | |
return _cairo_path_rel_curve_to (&gstate->path, | |
&distance[0], | |
&distance[1], | |
&distance[2]); | |
} | |
/* XXX: NYI | |
cairo_status_t | |
_cairo_gstate_stroke_path (cairo_gstate_t *gstate) | |
{ | |
cairo_status_t status; | |
_cairo_pen_init (&gstate); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
*/ | |
cairo_status_t | |
_cairo_gstate_close_path (cairo_gstate_t *gstate) | |
{ | |
return _cairo_path_close_path (&gstate->path); | |
} | |
cairo_status_t | |
_cairo_gstate_current_point (cairo_gstate_t *gstate, double *x_ret, double *y_ret) | |
{ | |
cairo_status_t status; | |
cairo_point_t point; | |
double x, y; | |
status = _cairo_path_current_point (&gstate->path, &point); | |
if (status == CAIRO_STATUS_NO_CURRENT_POINT) { | |
x = 0.0; | |
y = 0.0; | |
} else { | |
x = _cairo_fixed_to_double (point.x); | |
y = _cairo_fixed_to_double (point.y); | |
cairo_matrix_transform_point (&gstate->ctm_inverse, &x, &y); | |
} | |
*x_ret = x; | |
*y_ret = y; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
typedef struct gstate_path_interpreter { | |
cairo_matrix_t ctm_inverse; | |
double tolerance; | |
cairo_point_t current_point; | |
cairo_move_to_func_t *move_to; | |
cairo_line_to_func_t *line_to; | |
cairo_curve_to_func_t *curve_to; | |
cairo_close_path_func_t *close_path; | |
void *closure; | |
} gpi_t; | |
static cairo_status_t | |
_gpi_move_to (void *closure, cairo_point_t *point) | |
{ | |
gpi_t *gpi = closure; | |
double x, y; | |
x = _cairo_fixed_to_double (point->x); | |
y = _cairo_fixed_to_double (point->y); | |
cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); | |
gpi->move_to (gpi->closure, x, y); | |
gpi->current_point = *point; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
static cairo_status_t | |
_gpi_line_to (void *closure, cairo_point_t *point) | |
{ | |
gpi_t *gpi = closure; | |
double x, y; | |
x = _cairo_fixed_to_double (point->x); | |
y = _cairo_fixed_to_double (point->y); | |
cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); | |
gpi->line_to (gpi->closure, x, y); | |
gpi->current_point = *point; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
static cairo_status_t | |
_gpi_curve_to (void *closure, | |
cairo_point_t *p1, | |
cairo_point_t *p2, | |
cairo_point_t *p3) | |
{ | |
gpi_t *gpi = closure; | |
cairo_status_t status; | |
cairo_spline_t spline; | |
double x1, y1, x2, y2, x3, y3; | |
if (gpi->curve_to) { | |
x1 = _cairo_fixed_to_double (p1->x); | |
y1 = _cairo_fixed_to_double (p1->y); | |
cairo_matrix_transform_point (&gpi->ctm_inverse, &x1, &y1); | |
x2 = _cairo_fixed_to_double (p2->x); | |
y2 = _cairo_fixed_to_double (p2->y); | |
cairo_matrix_transform_point (&gpi->ctm_inverse, &x2, &y2); | |
x3 = _cairo_fixed_to_double (p3->x); | |
y3 = _cairo_fixed_to_double (p3->y); | |
cairo_matrix_transform_point (&gpi->ctm_inverse, &x3, &y3); | |
gpi->curve_to (gpi->closure, x1, y1, x2, y2, x3, y3); | |
} else { | |
cairo_point_t *p0 = &gpi->current_point; | |
int i; | |
double x, y; | |
status = _cairo_spline_init (&spline, p0, p1, p2, p3); | |
if (status == CAIRO_INT_STATUS_DEGENERATE) | |
return CAIRO_STATUS_SUCCESS; | |
status = _cairo_spline_decompose (&spline, gpi->tolerance); | |
if (status) | |
return status; | |
for (i=1; i < spline.num_points; i++) { | |
x = _cairo_fixed_to_double (spline.points[i].x); | |
y = _cairo_fixed_to_double (spline.points[i].y); | |
cairo_matrix_transform_point (&gpi->ctm_inverse, &x, &y); | |
gpi->line_to (gpi->closure, x, y); | |
} | |
} | |
gpi->current_point = *p3; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
static cairo_status_t | |
_gpi_close_path (void *closure) | |
{ | |
gpi_t *gpi = closure; | |
gpi->close_path (gpi->closure); | |
gpi->current_point.x = 0; | |
gpi->current_point.y = 0; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
/* It's OK for curve_path to be NULL. In that case, all curves in the | |
path will be decomposed into one or more calls to the line_to | |
function, (according to the current tolerance). */ | |
cairo_status_t | |
_cairo_gstate_interpret_path (cairo_gstate_t *gstate, | |
cairo_move_to_func_t *move_to, | |
cairo_line_to_func_t *line_to, | |
cairo_curve_to_func_t *curve_to, | |
cairo_close_path_func_t *close_path, | |
void *closure) | |
{ | |
cairo_path_t path; | |
gpi_t gpi; | |
/* Anything we want from gstate must be copied. We must not retain | |
pointers into gstate. */ | |
_cairo_path_init_copy (&path, &gstate->path); | |
cairo_matrix_copy (&gpi.ctm_inverse, &gstate->ctm_inverse); | |
gpi.tolerance = gstate->tolerance; | |
gpi.move_to = move_to; | |
gpi.line_to = line_to; | |
gpi.curve_to = curve_to; | |
gpi.close_path = close_path; | |
gpi.closure = closure; | |
gpi.current_point.x = 0; | |
gpi.current_point.y = 0; | |
return _cairo_path_interpret (&path, | |
CAIRO_DIRECTION_FORWARD, | |
_gpi_move_to, | |
_gpi_line_to, | |
_gpi_curve_to, | |
_gpi_close_path, | |
&gpi); | |
} | |
/* This function modifies the pattern and the state of the pattern surface it | |
may contain. The pattern surface will be restored to its orignal state | |
when the pattern is destroyed. The appropriate way is to pass a copy of | |
the original pattern to this function just before the pattern should be | |
used and destroy the copy when done. */ | |
static cairo_status_t | |
_cairo_gstate_create_pattern (cairo_gstate_t *gstate, | |
cairo_pattern_t *pattern, | |
cairo_box_t *extents) | |
{ | |
cairo_int_status_t status; | |
if (gstate->surface == NULL) { | |
_cairo_pattern_fini (pattern); | |
return CAIRO_STATUS_NO_TARGET_SURFACE; | |
} | |
if (pattern->type == CAIRO_PATTERN_LINEAR || | |
pattern->type == CAIRO_PATTERN_RADIAL) { | |
if (pattern->n_stops < 2) { | |
pattern->type = CAIRO_PATTERN_SOLID; | |
if (pattern->n_stops) { | |
cairo_color_stop_t *stop = pattern->stops; | |
_cairo_color_set_rgb (&pattern->color, | |
(double) stop->color_char[0] / 0xff, | |
(double) stop->color_char[1] / 0xff, | |
(double) stop->color_char[2] / 0xff); | |
_cairo_color_set_alpha (&pattern->color, | |
(double) stop->color_char[3] / 0xff); | |
} | |
} | |
} | |
_cairo_pattern_set_alpha (pattern, gstate->alpha); | |
_cairo_pattern_transform (pattern, &gstate->ctm_inverse); | |
status = _cairo_surface_create_pattern (gstate->surface, pattern, extents); | |
if (status) { | |
_cairo_pattern_fini (pattern); | |
return status; | |
} | |
if (pattern->type == CAIRO_PATTERN_SURFACE) | |
_cairo_pattern_prepare_surface (pattern); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_stroke (cairo_gstate_t *gstate) | |
{ | |
cairo_status_t status; | |
cairo_traps_t traps; | |
if (gstate->line_width <= 0.0) | |
return CAIRO_STATUS_SUCCESS; | |
_cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate); | |
_cairo_traps_init (&traps); | |
status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); | |
if (status) { | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
_cairo_gstate_clip_and_composite_trapezoids (gstate, | |
gstate->pattern, | |
gstate->operator, | |
gstate->surface, | |
&traps); | |
_cairo_traps_fini (&traps); | |
_cairo_gstate_new_path (gstate); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_in_stroke (cairo_gstate_t *gstate, | |
double x, | |
double y, | |
int *inside_ret) | |
{ | |
cairo_status_t status = CAIRO_STATUS_SUCCESS; | |
cairo_traps_t traps; | |
cairo_matrix_transform_point (&gstate->ctm, &x, &y); | |
_cairo_pen_init (&gstate->pen_regular, gstate->line_width / 2.0, gstate); | |
_cairo_traps_init (&traps); | |
status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); | |
if (status) | |
goto BAIL; | |
*inside_ret = _cairo_traps_contain (&traps, x, y); | |
BAIL: | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
/* Warning: This call modifies the coordinates of traps */ | |
static cairo_status_t | |
_cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate, | |
cairo_pattern_t *src, | |
cairo_operator_t operator, | |
cairo_surface_t *dst, | |
cairo_traps_t *traps) | |
{ | |
cairo_status_t status; | |
cairo_pattern_t pattern; | |
cairo_box_t extents; | |
int x_src, y_src; | |
if (traps->num_traps == 0) | |
return CAIRO_STATUS_SUCCESS; | |
if (gstate->clip.surface) { | |
cairo_fixed_t xoff, yoff; | |
cairo_trapezoid_t *t; | |
int i; | |
cairo_surface_t *intermediate; | |
cairo_color_t empty_color; | |
_cairo_color_init (&empty_color); | |
_cairo_color_set_alpha (&empty_color, 0.); | |
intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, | |
CAIRO_FORMAT_A8, | |
gstate->clip.width, | |
gstate->clip.height, | |
&empty_color); | |
if (intermediate == NULL) { | |
status = CAIRO_STATUS_NO_MEMORY; | |
goto BAIL0; | |
} | |
/* Ugh. The cairo_composite/(Render) interface doesn't allow | |
an offset for the trapezoids. Need to manually shift all | |
the coordinates to align with the offset origin of the clip | |
surface. */ | |
xoff = _cairo_fixed_from_double (gstate->clip.x); | |
yoff = _cairo_fixed_from_double (gstate->clip.y); | |
for (i=0, t=traps->traps; i < traps->num_traps; i++, t++) { | |
t->top -= yoff; | |
t->bottom -= yoff; | |
t->left.p1.x -= xoff; | |
t->left.p1.y -= yoff; | |
t->left.p2.x -= xoff; | |
t->left.p2.y -= yoff; | |
t->right.p1.x -= xoff; | |
t->right.p1.y -= yoff; | |
t->right.p2.x -= xoff; | |
t->right.p2.y -= yoff; | |
} | |
if (traps->traps[0].left.p1.y < traps->traps[0].left.p2.y) { | |
x_src = _cairo_fixed_to_double (traps->traps[0].left.p1.x); | |
y_src = _cairo_fixed_to_double (traps->traps[0].left.p1.y); | |
} else { | |
x_src = _cairo_fixed_to_double (traps->traps[0].left.p2.x); | |
y_src = _cairo_fixed_to_double (traps->traps[0].left.p2.y); | |
} | |
_cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); | |
_cairo_pattern_set_alpha (&pattern, 1.0); | |
_cairo_traps_extents (traps, &extents); | |
status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); | |
if (status) | |
goto BAIL1; | |
status = _cairo_surface_composite_trapezoids (CAIRO_OPERATOR_ADD, | |
pattern.source, intermediate, | |
x_src, | |
y_src, | |
traps->traps, | |
traps->num_traps); | |
if (status) | |
goto BAIL2; | |
status = _cairo_surface_composite (CAIRO_OPERATOR_IN, | |
gstate->clip.surface, | |
NULL, | |
intermediate, | |
0, 0, 0, 0, 0, 0, | |
gstate->clip.width, gstate->clip.height); | |
if (status) | |
goto BAIL2; | |
_cairo_pattern_fini (&pattern); | |
_cairo_pattern_init_copy (&pattern, src); | |
extents.p1.x = _cairo_fixed_from_int (gstate->clip.x); | |
extents.p1.y = _cairo_fixed_from_int (gstate->clip.y); | |
extents.p2.x = | |
_cairo_fixed_from_int (gstate->clip.x + gstate->clip.width); | |
extents.p2.y = | |
_cairo_fixed_from_int (gstate->clip.y + gstate->clip.height); | |
status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); | |
if (status) | |
goto BAIL2; | |
if (dst == gstate->clip.surface) | |
xoff = yoff = 0; | |
status = _cairo_surface_composite (operator, | |
pattern.source, intermediate, dst, | |
0, 0, | |
0, 0, | |
xoff >> 16, | |
yoff >> 16, | |
gstate->clip.width, | |
gstate->clip.height); | |
BAIL2: | |
cairo_surface_destroy (intermediate); | |
BAIL1: | |
_cairo_pattern_fini (&pattern); | |
BAIL0: | |
if (status) | |
return status; | |
} else { | |
if (traps->traps[0].left.p1.y < traps->traps[0].left.p2.y) { | |
x_src = _cairo_fixed_to_double (traps->traps[0].left.p1.x); | |
y_src = _cairo_fixed_to_double (traps->traps[0].left.p1.y); | |
} else { | |
x_src = _cairo_fixed_to_double (traps->traps[0].left.p2.x); | |
y_src = _cairo_fixed_to_double (traps->traps[0].left.p2.y); | |
} | |
_cairo_pattern_init_copy (&pattern, src); | |
_cairo_traps_extents (traps, &extents); | |
status = _cairo_gstate_create_pattern (gstate, &pattern, &extents); | |
if (status) | |
return status; | |
status = _cairo_surface_composite_trapezoids (gstate->operator, | |
pattern.source, dst, | |
x_src - pattern.source_offset.x, | |
y_src - pattern.source_offset.y, | |
traps->traps, | |
traps->num_traps); | |
_cairo_pattern_fini (&pattern); | |
if (status) | |
return status; | |
} | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_fill (cairo_gstate_t *gstate) | |
{ | |
cairo_status_t status; | |
cairo_traps_t traps; | |
_cairo_traps_init (&traps); | |
status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); | |
if (status) { | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
_cairo_gstate_clip_and_composite_trapezoids (gstate, | |
gstate->pattern, | |
gstate->operator, | |
gstate->surface, | |
&traps); | |
_cairo_traps_fini (&traps); | |
_cairo_gstate_new_path (gstate); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_in_fill (cairo_gstate_t *gstate, | |
double x, | |
double y, | |
int *inside_ret) | |
{ | |
cairo_status_t status = CAIRO_STATUS_SUCCESS; | |
cairo_traps_t traps; | |
cairo_matrix_transform_point (&gstate->ctm, &x, &y); | |
_cairo_traps_init (&traps); | |
status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); | |
if (status) | |
goto BAIL; | |
*inside_ret = _cairo_traps_contain (&traps, x, y); | |
BAIL: | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_copy_page (cairo_gstate_t *gstate) | |
{ | |
if (gstate->surface == NULL) | |
return CAIRO_STATUS_NO_TARGET_SURFACE; | |
return _cairo_surface_copy_page (gstate->surface); | |
} | |
cairo_status_t | |
_cairo_gstate_show_page (cairo_gstate_t *gstate) | |
{ | |
if (gstate->surface == NULL) | |
return CAIRO_STATUS_NO_TARGET_SURFACE; | |
return _cairo_surface_show_page (gstate->surface); | |
} | |
cairo_status_t | |
_cairo_gstate_stroke_extents (cairo_gstate_t *gstate, | |
double *x1, double *y1, | |
double *x2, double *y2) | |
{ | |
cairo_status_t status; | |
cairo_traps_t traps; | |
cairo_box_t extents; | |
_cairo_traps_init (&traps); | |
status = _cairo_path_stroke_to_traps (&gstate->path, gstate, &traps); | |
if (status) | |
goto BAIL; | |
_cairo_traps_extents (&traps, &extents); | |
*x1 = _cairo_fixed_to_double (extents.p1.x); | |
*y1 = _cairo_fixed_to_double (extents.p1.y); | |
*x2 = _cairo_fixed_to_double (extents.p2.x); | |
*y2 = _cairo_fixed_to_double (extents.p2.y); | |
cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); | |
cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); | |
BAIL: | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_fill_extents (cairo_gstate_t *gstate, | |
double *x1, double *y1, | |
double *x2, double *y2) | |
{ | |
cairo_status_t status; | |
cairo_traps_t traps; | |
cairo_box_t extents; | |
_cairo_traps_init (&traps); | |
status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); | |
if (status) | |
goto BAIL; | |
_cairo_traps_extents (&traps, &extents); | |
*x1 = _cairo_fixed_to_double (extents.p1.x); | |
*y1 = _cairo_fixed_to_double (extents.p1.y); | |
*x2 = _cairo_fixed_to_double (extents.p2.x); | |
*y2 = _cairo_fixed_to_double (extents.p2.y); | |
cairo_matrix_transform_point (&gstate->ctm_inverse, x1, y1); | |
cairo_matrix_transform_point (&gstate->ctm_inverse, x2, y2); | |
BAIL: | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_init_clip (cairo_gstate_t *gstate) | |
{ | |
/* destroy any existing clip-region artifacts */ | |
if (gstate->clip.surface) | |
cairo_surface_destroy (gstate->clip.surface); | |
gstate->clip.surface = NULL; | |
if (gstate->clip.region) | |
pixman_region_destroy (gstate->clip.region); | |
gstate->clip.region = NULL; | |
/* reset the surface's clip to the whole surface */ | |
if (gstate->surface) | |
_cairo_surface_set_clip_region (gstate->surface, | |
gstate->clip.region); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
static int | |
extract_transformed_rectangle(cairo_matrix_t *mat, | |
cairo_traps_t *tr, | |
pixman_box16_t *box) | |
{ | |
double a, b, c, d, tx, ty; | |
cairo_status_t st; | |
/* XXX: Something in the rectangle-based clipping support is | |
* broken. See cairo_snippets/xxx_clip_rectangle which | |
* demonstrates no clipping at all. | |
* | |
* For now, I'm am disabling this optimization completely until it | |
* can be fixed. | |
*/ | |
return 0; | |
st = cairo_matrix_get_affine (mat, &a, &b, &c, &d, &tx, &ty); | |
if (!(st == CAIRO_STATUS_SUCCESS && b == 0. && c == 0.)) | |
return 0; | |
if (tr->num_traps == 1 | |
&& tr->traps[0].left.p1.x == tr->traps[0].left.p2.x | |
&& tr->traps[0].right.p1.x == tr->traps[0].right.p2.x | |
&& tr->traps[0].left.p1.y == tr->traps[0].right.p1.y | |
&& tr->traps[0].left.p2.y == tr->traps[0].right.p2.y | |
&& _cairo_fixed_is_integer(tr->traps[0].left.p1.x) | |
&& _cairo_fixed_is_integer(tr->traps[0].left.p1.y) | |
&& _cairo_fixed_is_integer(tr->traps[0].left.p2.x) | |
&& _cairo_fixed_is_integer(tr->traps[0].left.p2.y) | |
&& _cairo_fixed_is_integer(tr->traps[0].right.p1.x) | |
&& _cairo_fixed_is_integer(tr->traps[0].right.p1.y) | |
&& _cairo_fixed_is_integer(tr->traps[0].right.p2.x) | |
&& _cairo_fixed_is_integer(tr->traps[0].right.p2.y)) { | |
box->x1 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p1.x); | |
box->x2 = (short) _cairo_fixed_integer_part(tr->traps[0].right.p1.x); | |
box->y1 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p1.y); | |
box->y2 = (short) _cairo_fixed_integer_part(tr->traps[0].left.p2.y); | |
return 1; | |
} | |
return 0; | |
} | |
cairo_status_t | |
_cairo_gstate_clip (cairo_gstate_t *gstate) | |
{ | |
cairo_status_t status; | |
cairo_pattern_t pattern; | |
cairo_traps_t traps; | |
cairo_color_t white_color; | |
pixman_box16_t box; | |
/* Fill the clip region as traps. */ | |
_cairo_traps_init (&traps); | |
status = _cairo_path_fill_to_traps (&gstate->path, gstate, &traps); | |
if (status) { | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
/* Check to see if we can represent these traps as a PixRegion. */ | |
if (extract_transformed_rectangle (&gstate->ctm, &traps, &box)) { | |
pixman_region16_t *rect = NULL; | |
pixman_region16_t *intersection = NULL; | |
status = CAIRO_STATUS_SUCCESS; | |
rect = pixman_region_create_simple (&box); | |
if (rect == NULL) { | |
status = CAIRO_STATUS_NO_MEMORY; | |
} else { | |
if (gstate->clip.region == NULL) { | |
gstate->clip.region = rect; | |
} else { | |
intersection = pixman_region_create(); | |
if (pixman_region_intersect (intersection, | |
gstate->clip.region, rect) | |
== PIXMAN_REGION_STATUS_SUCCESS) { | |
pixman_region_destroy (gstate->clip.region); | |
gstate->clip.region = intersection; | |
} else { | |
status = CAIRO_STATUS_NO_MEMORY; | |
} | |
pixman_region_destroy (rect); | |
} | |
if (!status) | |
status = _cairo_surface_set_clip_region (gstate->surface, | |
gstate->clip.region); | |
} | |
if (status != CAIRO_INT_STATUS_UNSUPPORTED) { | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
} | |
/* Otherwise represent the clip as a mask surface. */ | |
_cairo_color_init (&white_color); | |
if (gstate->clip.surface == NULL) { | |
cairo_box_t extents; | |
_cairo_traps_extents (&traps, &extents); | |
gstate->clip.x = extents.p1.x >> 16; | |
gstate->clip.y = extents.p1.y >> 16; | |
gstate->clip.width = ((extents.p2.x + 65535) >> 16) - gstate->clip.x; | |
gstate->clip.height = ((extents.p2.y + 65535) >> 16) - gstate->clip.y; | |
gstate->clip.surface = | |
_cairo_surface_create_similar_solid (gstate->surface, | |
CAIRO_FORMAT_A8, | |
gstate->clip.width, | |
gstate->clip.height, | |
&white_color); | |
if (gstate->clip.surface == NULL) | |
return CAIRO_STATUS_NO_MEMORY; | |
} | |
_cairo_pattern_init_solid (&pattern, 1.0, 1.0, 1.0); | |
_cairo_pattern_set_alpha (&pattern, 1.0); | |
_cairo_gstate_clip_and_composite_trapezoids (gstate, | |
&pattern, | |
CAIRO_OPERATOR_IN, | |
gstate->clip.surface, | |
&traps); | |
_cairo_pattern_fini (&pattern); | |
_cairo_traps_fini (&traps); | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_show_surface (cairo_gstate_t *gstate, | |
cairo_surface_t *surface, | |
int width, | |
int height) | |
{ | |
/* We are dealing with 5 coordinate spaces in this function. this makes | |
* it ugly. | |
* | |
* - "Image" space is the space of the surface we're reading pixels from. | |
* it is the surface argument to this function. The surface has a | |
* matrix attached to it which maps "user" space (see below) into | |
* image space. | |
* | |
* - "Device" space is the space of the surface we're ultimately writing | |
* pixels to. It is the current surface of the gstate argument to | |
* this function. | |
* | |
* - "User" space is an arbitrary space defined by the user, defined | |
* implicitly by the gstate's CTM. The CTM maps from user space to | |
* device space. The CTM inverse (which is also kept at all times) | |
* maps from device space to user space. | |
* | |
* - "Clip" space is the space of the surface being used to clip pixels | |
* during compositing. Space-wise, it is a bounding box (offset+size) | |
* within device space. This surface is usually smaller than the device | |
* surface (and possibly the image surface too) and logically occupies | |
* a bounding box around the "clip path", situated somewhere in device | |
* space. The clip path is already painted on the clip surface. | |
* | |
* - "Pattern" space is another arbitrary space defined in the pattern | |
* element of gstate. As pixels are read from image space, they are | |
* combined with pixels being read from pattern space and pixels | |
* already existing in device space. User coordinates are converted | |
* to pattern space, similarly, using a matrix attached to the pattern. | |
* (in fact, there is a 6th space in here, which is the space of the | |
* surface acting as a source for the pattern) | |
* | |
* To composite these spaces, we temporarily change the image surface | |
* so that it can be read and written in device coordinates; in a sense | |
* this makes it "spatially compatible" with the clip and device spaces. | |
* | |
* | |
* There is also some confusion about the interaction between a clip and | |
* a pattern; it is assumed that in this "show surface" operation a pattern | |
* is to be used as an auxiliary alpha mask. this might be wrong, but it's | |
* what we're doing now. | |
* | |
* so, to follow the operations below, remember that in the compositing | |
* model, each operation is always of the form ((src IN mask) OP dst). | |
* that's the basic operation. | |
* | |
* so the compositing we are trying to do here, in general, involves 2 | |
* steps, going via a temporary surface: | |
* | |
* - combining clip and pattern pixels together into a mask channel. | |
* this will be ((pattern IN clip) SRC temporary). it ignores the | |
* pixels already in the temporary, overwriting it with the | |
* pattern, clipped to the clip mask. | |
* | |
* - combining temporary and "image" pixels with "device" pixels, | |
* with a user-provided porter/duff operator. this will be | |
* ((image IN temporary) OP device). | |
* | |
* if there is no clip, the degenerate case is just the second step | |
* with pattern standing in for temporary. | |
* | |
*/ | |
cairo_status_t status; | |
cairo_matrix_t user_to_image, image_to_user; | |
cairo_matrix_t image_to_device, device_to_image; | |
double device_x, device_y; | |
double device_width, device_height; | |
cairo_pattern_t pattern; | |
cairo_box_t pattern_extents; | |
cairo_surface_get_matrix (surface, &user_to_image); | |
cairo_matrix_multiply (&device_to_image, &gstate->ctm_inverse, &user_to_image); | |
cairo_surface_set_matrix (surface, &device_to_image); | |
image_to_user = user_to_image; | |
cairo_matrix_invert (&image_to_user); | |
cairo_matrix_multiply (&image_to_device, &image_to_user, &gstate->ctm); | |
_cairo_gstate_current_point (gstate, &device_x, &device_y); | |
device_width = width; | |
device_height = height; | |
_cairo_matrix_transform_bounding_box (&image_to_device, | |
&device_x, &device_y, | |
&device_width, &device_height); | |
_cairo_pattern_init (&pattern); | |
if ((gstate->pattern->type != CAIRO_PATTERN_SOLID) || | |
(gstate->alpha != 1.0)) { | |
/* I'm allowing any type of pattern for the mask right now. | |
Maybe this is bad. Will allow for some cool effects though. */ | |
_cairo_pattern_init_copy (&pattern, gstate->pattern); | |
pattern_extents.p1.x = _cairo_fixed_from_double (device_x); | |
pattern_extents.p1.y = _cairo_fixed_from_double (device_y); | |
pattern_extents.p2.x = _cairo_fixed_from_double (device_x + device_width); | |
pattern_extents.p2.y = _cairo_fixed_from_double (device_y + device_height); | |
status = _cairo_gstate_create_pattern (gstate, &pattern, &pattern_extents); | |
if (status) | |
return status; | |
} | |
if (gstate->clip.surface) | |
{ | |
cairo_surface_t *intermediate; | |
cairo_color_t empty_color; | |
_cairo_color_init (&empty_color); | |
_cairo_color_set_alpha (&empty_color, .0); | |
intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, | |
CAIRO_FORMAT_A8, | |
gstate->clip.width, | |
gstate->clip.height, | |
&empty_color); | |
/* it is not completely clear what the "right" way to combine the | |
pattern and mask surface is. I will use the the clip as a source | |
and the pattern as a mask in building up my temporary, because | |
this is not *totally* bogus and accomodates the case where | |
pattern's source image is NULL reasonably well. feel free to | |
correct this if you see a reason. */ | |
status = _cairo_surface_composite (CAIRO_OPERATOR_SRC, | |
gstate->clip.surface, | |
pattern.source, | |
intermediate, | |
0, 0, | |
0, 0, | |
0, 0, | |
gstate->clip.width, | |
gstate->clip.height); | |
if (status) | |
goto BAIL; | |
status = _cairo_surface_composite (gstate->operator, | |
surface, | |
intermediate, | |
gstate->surface, | |
gstate->clip.x, gstate->clip.y, | |
0, 0, | |
gstate->clip.x, gstate->clip.y, | |
gstate->clip.width, | |
gstate->clip.height); | |
BAIL: | |
cairo_surface_destroy (intermediate); | |
} | |
else | |
{ | |
/* XXX: The rendered size is sometimes 1 or 2 pixels short from | |
what I expect. Need to fix this. */ | |
status = _cairo_surface_composite (gstate->operator, | |
surface, | |
pattern.source, | |
gstate->surface, | |
device_x, device_y, | |
0, 0, | |
device_x, device_y, | |
device_width, | |
device_height); | |
} | |
_cairo_pattern_fini (&pattern); | |
/* restore the matrix originally in the surface */ | |
cairo_surface_set_matrix (surface, &user_to_image); | |
if (status) | |
return status; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_select_font (cairo_gstate_t *gstate, | |
const char *family, | |
cairo_font_slant_t slant, | |
cairo_font_weight_t weight) | |
{ | |
if (gstate->font != NULL) | |
cairo_font_destroy (gstate->font); | |
gstate->font = _cairo_font_create (family, slant, weight); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_scale_font (cairo_gstate_t *gstate, | |
double scale) | |
{ | |
return _cairo_font_scale (gstate->font, scale); | |
} | |
cairo_status_t | |
_cairo_gstate_transform_font (cairo_gstate_t *gstate, | |
cairo_matrix_t *matrix) | |
{ | |
return _cairo_font_transform (gstate->font, matrix); | |
} | |
cairo_status_t | |
_cairo_gstate_current_font (cairo_gstate_t *gstate, | |
cairo_font_t **font) | |
{ | |
*font = gstate->font; | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_current_font_extents (cairo_gstate_t *gstate, | |
cairo_font_extents_t *extents) | |
{ | |
cairo_int_status_t status; | |
cairo_matrix_t saved_font_matrix; | |
cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); | |
cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); | |
status = _cairo_font_font_extents (gstate->font, extents); | |
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_set_font (cairo_gstate_t *gstate, | |
cairo_font_t *font) | |
{ | |
if (gstate->font != NULL) | |
cairo_font_destroy (gstate->font); | |
gstate->font = font; | |
cairo_font_reference (gstate->font); | |
return CAIRO_STATUS_SUCCESS; | |
} | |
cairo_status_t | |
_cairo_gstate_text_extents (cairo_gstate_t *gstate, | |
const unsigned char *utf8, | |
cairo_text_extents_t *extents) | |
{ | |
cairo_matrix_t saved_font_matrix; | |
cairo_status_t status; | |
double scale_x, scale_y; | |
cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); | |
_cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); | |
cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); | |
status = _cairo_font_text_extents (gstate->font, | |
utf8, extents); | |
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); | |
extents->x_bearing /= scale_x; | |
extents->y_bearing /= scale_y; | |
extents->width /= scale_x; | |
extents->height /= scale_y; | |
extents->x_advance /= scale_x; | |
extents->y_advance /= scale_y; | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_glyph_extents (cairo_gstate_t *gstate, | |
cairo_glyph_t *glyphs, | |
int num_glyphs, | |
cairo_text_extents_t *extents) | |
{ | |
cairo_status_t status; | |
cairo_matrix_t saved_font_matrix; | |
double scale_x, scale_y; | |
cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); | |
_cairo_matrix_compute_scale_factors (&gstate->ctm, &scale_x, &scale_y); | |
cairo_matrix_scale (&gstate->font->matrix, scale_x, scale_y); | |
status = _cairo_font_glyph_extents (gstate->font, | |
glyphs, num_glyphs, | |
extents); | |
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); | |
extents->x_bearing /= scale_x; | |
extents->y_bearing /= scale_y; | |
extents->width /= scale_x; | |
extents->height /= scale_y; | |
extents->x_advance /= scale_x; | |
extents->y_advance /= scale_y; | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_show_text (cairo_gstate_t *gstate, | |
const unsigned char *utf8) | |
{ | |
cairo_status_t status; | |
cairo_point_t point; | |
double x, y; | |
cairo_matrix_t saved_font_matrix; | |
cairo_pattern_t pattern; | |
cairo_box_t bbox; | |
status = _cairo_path_current_point (&gstate->path, &point); | |
if (status == CAIRO_STATUS_NO_CURRENT_POINT) { | |
x = 0; | |
y = 0; | |
cairo_matrix_transform_point (&gstate->ctm, &x, &y); | |
} else { | |
x = _cairo_fixed_to_double (point.x); | |
y = _cairo_fixed_to_double (point.y); | |
} | |
cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); | |
cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); | |
_cairo_pattern_init_copy (&pattern, gstate->pattern); | |
status = _cairo_font_text_bbox (gstate->font, gstate->surface, | |
x, y, utf8, &bbox); | |
if (status) | |
return status; | |
status = _cairo_gstate_create_pattern (gstate, &pattern, &bbox); | |
if (status) | |
return status; | |
if (gstate->clip.surface) | |
{ | |
cairo_surface_t *intermediate; | |
cairo_color_t empty_color; | |
_cairo_color_init (&empty_color); | |
_cairo_color_set_alpha (&empty_color, .0); | |
intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, | |
CAIRO_FORMAT_A8, | |
gstate->clip.width, | |
gstate->clip.height, | |
&empty_color); | |
status = _cairo_font_show_text (gstate->font, | |
CAIRO_OPERATOR_ADD, pattern.source, | |
intermediate, | |
gstate->clip.x - pattern.source_offset.x, | |
gstate->clip.y - pattern.source_offset.y, | |
x - gstate->clip.x, | |
y - gstate->clip.y, utf8); | |
if (status) | |
goto BAIL; | |
status = _cairo_surface_composite (CAIRO_OPERATOR_IN, | |
gstate->clip.surface, | |
NULL, | |
intermediate, | |
0, 0, | |
0, 0, | |
0, 0, | |
gstate->clip.width, | |
gstate->clip.height); | |
if (status) | |
goto BAIL; | |
status = _cairo_surface_composite (gstate->operator, | |
pattern.source, | |
intermediate, | |
gstate->surface, | |
0, 0, | |
0, 0, | |
gstate->clip.x, | |
gstate->clip.y, | |
gstate->clip.width, | |
gstate->clip.height); | |
BAIL: | |
cairo_surface_destroy (intermediate); | |
} | |
else | |
{ | |
status = _cairo_font_show_text (gstate->font, | |
gstate->operator, pattern.source, | |
gstate->surface, | |
-pattern.source_offset.x, | |
-pattern.source_offset.y, | |
x, y, utf8); | |
} | |
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); | |
_cairo_pattern_fini (&pattern); | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_show_glyphs (cairo_gstate_t *gstate, | |
cairo_glyph_t *glyphs, | |
int num_glyphs) | |
{ | |
cairo_status_t status; | |
cairo_matrix_t saved_font_matrix; | |
int i; | |
cairo_glyph_t *transformed_glyphs = NULL; | |
cairo_pattern_t pattern; | |
cairo_box_t bbox; | |
transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); | |
if (transformed_glyphs == NULL) | |
return CAIRO_STATUS_NO_MEMORY; | |
for (i = 0; i < num_glyphs; ++i) | |
{ | |
transformed_glyphs[i] = glyphs[i]; | |
cairo_matrix_transform_point (&gstate->ctm, | |
&(transformed_glyphs[i].x), | |
&(transformed_glyphs[i].y)); | |
} | |
cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); | |
cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); | |
_cairo_pattern_init_copy (&pattern, gstate->pattern); | |
status = _cairo_font_glyph_bbox (gstate->font, gstate->surface, | |
transformed_glyphs, num_glyphs, &bbox); | |
if (status) | |
return status; | |
status = _cairo_gstate_create_pattern (gstate, &pattern, &bbox); | |
if (status) | |
return status; | |
if (gstate->clip.surface) | |
{ | |
cairo_surface_t *intermediate; | |
cairo_color_t empty_color; | |
_cairo_color_init (&empty_color); | |
_cairo_color_set_alpha (&empty_color, .0); | |
intermediate = _cairo_surface_create_similar_solid (gstate->clip.surface, | |
CAIRO_FORMAT_A8, | |
gstate->clip.width, | |
gstate->clip.height, | |
&empty_color); | |
/* move the glyphs again, from dev space to clip space */ | |
for (i = 0; i < num_glyphs; ++i) | |
{ | |
transformed_glyphs[i].x -= gstate->clip.x; | |
transformed_glyphs[i].y -= gstate->clip.y; | |
} | |
status = _cairo_font_show_glyphs (gstate->font, | |
CAIRO_OPERATOR_ADD, | |
pattern.source, intermediate, | |
gstate->clip.x - pattern.source_offset.x, | |
gstate->clip.y - pattern.source_offset.y, | |
transformed_glyphs, num_glyphs); | |
if (status) | |
goto BAIL; | |
status = _cairo_surface_composite (CAIRO_OPERATOR_IN, | |
gstate->clip.surface, | |
NULL, | |
intermediate, | |
0, 0, | |
0, 0, | |
0, 0, | |
gstate->clip.width, | |
gstate->clip.height); | |
if (status) | |
goto BAIL; | |
status = _cairo_surface_composite (gstate->operator, | |
pattern.source, | |
intermediate, | |
gstate->surface, | |
0, 0, | |
0, 0, | |
gstate->clip.x, | |
gstate->clip.y, | |
gstate->clip.width, | |
gstate->clip.height); | |
BAIL: | |
cairo_surface_destroy (intermediate); | |
} | |
else | |
{ | |
status = _cairo_font_show_glyphs (gstate->font, | |
gstate->operator, pattern.source, | |
gstate->surface, | |
-pattern.source_offset.x, | |
-pattern.source_offset.y, | |
transformed_glyphs, num_glyphs); | |
} | |
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); | |
_cairo_pattern_fini (&pattern); | |
free (transformed_glyphs); | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_text_path (cairo_gstate_t *gstate, | |
const unsigned char *utf8) | |
{ | |
cairo_status_t status; | |
cairo_matrix_t saved_font_matrix; | |
cairo_point_t point; | |
double x, y; | |
status = _cairo_path_current_point (&gstate->path, &point); | |
if (status == CAIRO_STATUS_NO_CURRENT_POINT) { | |
x = 0; | |
y = 0; | |
cairo_matrix_transform_point (&gstate->ctm, &x, &y); | |
} else { | |
x = _cairo_fixed_to_double (point.x); | |
y = _cairo_fixed_to_double (point.y); | |
} | |
cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); | |
cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); | |
status = _cairo_font_text_path (gstate->font, | |
x, y, | |
utf8, | |
&gstate->path); | |
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); | |
return status; | |
} | |
cairo_status_t | |
_cairo_gstate_glyph_path (cairo_gstate_t *gstate, | |
cairo_glyph_t *glyphs, | |
int num_glyphs) | |
{ | |
cairo_status_t status; | |
int i; | |
cairo_glyph_t *transformed_glyphs = NULL; | |
cairo_matrix_t saved_font_matrix; | |
transformed_glyphs = malloc (num_glyphs * sizeof(cairo_glyph_t)); | |
if (transformed_glyphs == NULL) | |
return CAIRO_STATUS_NO_MEMORY; | |
for (i = 0; i < num_glyphs; ++i) | |
{ | |
transformed_glyphs[i] = glyphs[i]; | |
cairo_matrix_transform_point (&gstate->ctm, | |
&(transformed_glyphs[i].x), | |
&(transformed_glyphs[i].y)); | |
} | |
cairo_matrix_copy (&saved_font_matrix, &gstate->font->matrix); | |
cairo_matrix_multiply (&gstate->font->matrix, &gstate->ctm, &gstate->font->matrix); | |
status = _cairo_font_glyph_path (gstate->font, | |
transformed_glyphs, num_glyphs, | |
&gstate->path); | |
cairo_matrix_copy (&gstate->font->matrix, &saved_font_matrix); | |
free (transformed_glyphs); | |
return status; | |
} |