Originally: 2005-04-19 Carl Worth <cworth@cworth.org>

Add cairo_stroke_preserve, cairo_fill_preserve, and cairo_clip_preserve.
Rip the path out of cairo_gstate_t.
Add path to cairo_t.
Bring in most of the path code that used to live in cairo-gstate.c
Move arc generation code into its own file.
Accept path+ctm_inverse+tolerance instead of gstate. Absorb flattening and device space->user space conversion that used to be in _cairo_gstate_intepret_path.
Prefer cairo_fixed_t parameters over ciaro_point_t for cross-file interfaces.
Track changes in _cairo_path_fixed interfaces.
Port to use cairo_fill_preserve rather than cairo_save/cairo_restore which no longer work for saving the path.
Remove get and set of current point since it is no longer affected by cairo_save and cairo_restore. Add get and set testing for cairo_matrix_t.
This commit is contained in:
Carl Worth 2005-04-26 12:38:06 +00:00
parent 618792c8c0
commit 1baa4d1329
17 changed files with 1110 additions and 914 deletions

View file

@ -1,3 +1,43 @@
2005-04-26 Carl Worth <cworth@cworth.org>
Originally: 2005-04-19 Carl Worth <cworth@cworth.org>
* src/cairo.h: Add cairo_stroke_preserve, cairo_fill_preserve,
and cairo_clip_preserve.
* src/cairoint.h:
* src/cairo-gstate-private.h:
* src/cairo-gstate.c: Rip the path out of cairo_gstate_t.
* src/cairo-private.h: Add path to cairo_t.
* src/cairo.c: Bring in most of the path code that used to live in
cairo-gstate.c
* src/Makefile.am:
* src/cairo-arc-private.h:
* src/cairo-arc.c: Move arc generation code into its own file.
* src/cairo-path-data-private.h:
* src/cairo-path-data.c: Accept path+ctm_inverse+tolerance instead
of gstate. Absorb flattening and device space->user space
conversion that used to be in _cairo_gstate_intepret_path.
* src/cairo-path.c: Prefer cairo_fixed_t parameters over
ciaro_point_t for cross-file interfaces.
* src/cairo-ft-font.c: Track changes in _cairo_path_fixed
interfaces.
* test/fill-and-stroke.c: (draw): Port to use cairo_fill_preserve
rather than cairo_save/cairo_restore which no longer work for
saving the path.
* test/get-and-set.c: (settings_set), (settings_get),
(settings_equal): Remove get and set of current point since it is
no longer affected by cairo_save and cairo_restore. Add get and
set testing for cairo_matrix_t.
2005-04-26 Carl Worth <cworth@cworth.org>
* test/.cvsignore:

2
TODO
View file

@ -23,7 +23,7 @@ PDRTC Making set_source consistent
PDRTC cairo_current_matrix
cairo_mask
cairo_create and eliminating cairo_set_target_surface
PD T cairo_fill_preserve, cairo_stroke_preserve, cairo_clip_preserve
PDRTC cairo_fill_preserve, cairo_stroke_preserve, cairo_clip_preserve
cairo_<device>_surface_mark_dirty
PDR C A hidden offset for the xlib backend
Simplifying the operator set

View file

@ -78,6 +78,7 @@ lib_LTLIBRARIES = libcairo.la
libcairo_la_SOURCES = \
cairo.c \
cairo.h \
cairo-arc.c \
cairo-array.c \
cairo-cache.c \
cairo-color.c \

57
src/cairo-arc-private.h Normal file
View file

@ -0,0 +1,57 @@
/* cairo - a vector graphics library with display and print output
*
* Copyright © 2005 Red Hat, Inc.
*
* 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 Red Hat, Inc.
*
* Contributor(s):
* Carl D. Worth <cworth@redhat.com>
*/
#ifndef CAIRO_ARC_PRIVATE_H
#define CAIRO_ARC_PRIVATE_H
#include "cairoint.h"
void
_cairo_arc_path (cairo_t *cr,
double xc,
double yc,
double radius,
double angle1,
double angle2);
void
_cairo_arc_path_negative (cairo_t *cr,
double xc,
double yc,
double radius,
double angle1,
double angle2);
#endif /* CAIRO_ARC_PRIVATE_H */

296
src/cairo-arc.c Normal file
View file

@ -0,0 +1,296 @@
/* 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@cworth.org>
*/
#include <math.h>
#include "cairo-arc-private.h"
/* 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
_arc_segments_needed (double angle,
double radius,
cairo_matrix_t *ctm,
double tolerance)
{
double l1, l2, lmax;
double max_angle;
_cairo_matrix_compute_eigen_values (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 (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 void
_cairo_arc_segment (cairo_t *cr,
double xc,
double yc,
double radius,
double angle_A,
double angle_B)
{
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);
cairo_curve_to (cr,
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);
}
static void
_cairo_arc_in_direction (cairo_t *cr,
double xc,
double yc,
double radius,
double angle_min,
double angle_max,
cairo_direction_t dir)
{
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) {
_cairo_arc_in_direction (cr, xc, yc, radius,
angle_min, angle_mid,
dir);
_cairo_arc_in_direction (cr, xc, yc, radius,
angle_mid, angle_max,
dir);
} else {
_cairo_arc_in_direction (cr, xc, yc, radius,
angle_mid, angle_max,
dir);
_cairo_arc_in_direction (cr, xc, yc, radius,
angle_min, angle_mid,
dir);
}
} else {
cairo_matrix_t ctm;
int i, segments;
double angle, angle_step;
cairo_get_matrix (cr, &ctm);
segments = _arc_segments_needed (angle_max - angle_min,
radius, &ctm,
cairo_get_tolerance (cr));
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_arc_segment (cr, xc, yc,
radius,
angle,
angle + angle_step);
}
}
}
/**
* _cairo_arc_path_negative:
* @cr: a cairo context
* @xc: X position of the center of the arc
* @yc: Y position of the center of the arc
* @radius: the radius of the arc
* @angle1: the start angle, in radians
* @angle2: the end angle, in radians
*
* Compute a path for the given arc and append it onto the current
* path within @cr. The arc will be accurate within the current
* tolerance and given the current transformation.
**/
void
_cairo_arc_path (cairo_t *cr,
double xc,
double yc,
double radius,
double angle1,
double angle2)
{
_cairo_arc_in_direction (cr, xc, yc,
radius,
angle1, angle2,
CAIRO_DIRECTION_FORWARD);
}
/**
* _cairo_arc_path_negative:
* @xc: X position of the center of the arc
* @yc: Y position of the center of the arc
* @radius: the radius of the arc
* @angle1: the start angle, in radians
* @angle2: the end angle, in radians
* @ctm: the current transformation matrix
* @tolerance: the current tolerance value
* @path: the path onto which th earc will be appended
*
* Compute a path for the given arc (defined in the negative
* direction) and append it onto the current path within @cr. The arc
* will be accurate within the current tolerance and given the current
* transformation.
**/
void
_cairo_arc_path_negative (cairo_t *cr,
double xc,
double yc,
double radius,
double angle1,
double angle2)
{
return _cairo_arc_in_direction (cr, xc, yc,
radius,
angle2, angle1,
CAIRO_DIRECTION_REVERSE);
}

View file

@ -1181,13 +1181,13 @@ static int
_move_to (FT_Vector *to, void *closure)
{
cairo_path_fixed_t *path = closure;
cairo_point_t point;
cairo_fixed_t x, y;
point.x = _cairo_fixed_from_26_6 (to->x);
point.y = _cairo_fixed_from_26_6 (to->y);
x = _cairo_fixed_from_26_6 (to->x);
y = _cairo_fixed_from_26_6 (to->y);
_cairo_path_fixed_close_path (path);
_cairo_path_fixed_move_to (path, &point);
_cairo_path_fixed_move_to (path, x, y);
return 0;
}
@ -1196,12 +1196,12 @@ static int
_line_to (FT_Vector *to, void *closure)
{
cairo_path_fixed_t *path = closure;
cairo_point_t point;
cairo_fixed_t x, y;
point.x = _cairo_fixed_from_26_6 (to->x);
point.y = _cairo_fixed_from_26_6 (to->y);
x = _cairo_fixed_from_26_6 (to->x);
y = _cairo_fixed_from_26_6 (to->y);
_cairo_path_fixed_line_to (path, &point);
_cairo_path_fixed_line_to (path, x, y);
return 0;
}
@ -1211,25 +1211,30 @@ _conic_to (FT_Vector *control, FT_Vector *to, void *closure)
{
cairo_path_fixed_t *path = closure;
cairo_point_t p0, p1, p2, p3;
cairo_fixed_t x0, y0;
cairo_fixed_t x1, y1;
cairo_fixed_t x2, y2;
cairo_fixed_t x3, y3;
cairo_point_t conic;
_cairo_path_fixed_get_current_point (path, &p0);
_cairo_path_fixed_get_current_point (path, &x0, &y0);
conic.x = _cairo_fixed_from_26_6 (control->x);
conic.y = _cairo_fixed_from_26_6 (control->y);
p3.x = _cairo_fixed_from_26_6 (to->x);
p3.y = _cairo_fixed_from_26_6 (to->y);
x3 = _cairo_fixed_from_26_6 (to->x);
y3 = _cairo_fixed_from_26_6 (to->y);
p1.x = p0.x + 2.0/3.0 * (conic.x - p0.x);
p1.y = p0.y + 2.0/3.0 * (conic.y - p0.y);
x1 = x0 + 2.0/3.0 * (conic.x - x0);
y1 = y0 + 2.0/3.0 * (conic.y - y0);
p2.x = p3.x + 2.0/3.0 * (conic.x - p3.x);
p2.y = p3.y + 2.0/3.0 * (conic.y - p3.y);
x2 = x3 + 2.0/3.0 * (conic.x - x3);
y2 = y3 + 2.0/3.0 * (conic.y - y3);
_cairo_path_fixed_curve_to (path,
&p1, &p2, &p3);
x1, y1,
x2, y2,
x3, y3);
return 0;
}
@ -1238,18 +1243,23 @@ static int
_cubic_to (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *closure)
{
cairo_path_fixed_t *path = closure;
cairo_point_t p0, p1, p2;
cairo_fixed_t x0, y0;
cairo_fixed_t x1, y1;
cairo_fixed_t x2, y2;
p0.x = _cairo_fixed_from_26_6 (control1->x);
p0.y = _cairo_fixed_from_26_6 (control1->y);
x0 = _cairo_fixed_from_26_6 (control1->x);
y0 = _cairo_fixed_from_26_6 (control1->y);
p1.x = _cairo_fixed_from_26_6 (control2->x);
p1.y = _cairo_fixed_from_26_6 (control2->y);
x1 = _cairo_fixed_from_26_6 (control2->x);
y1 = _cairo_fixed_from_26_6 (control2->y);
p2.x = _cairo_fixed_from_26_6 (to->x);
p2.y = _cairo_fixed_from_26_6 (to->y);
x2 = _cairo_fixed_from_26_6 (to->x);
y2 = _cairo_fixed_from_26_6 (to->y);
_cairo_path_fixed_curve_to (path, &p0, &p1, &p2);
_cairo_path_fixed_curve_to (path,
x0, y0,
x1, y1,
x2, y2);
return 0;
}

View file

@ -36,8 +36,6 @@
#ifndef CAIRO_GSTATE_PRIVATE_H
#define CAIRO_GSTATE_PRIVATE_H
#include "cairo-path-fixed-private.h"
struct _cairo_gstate {
cairo_operator_t operator;
@ -73,8 +71,6 @@ struct _cairo_gstate {
cairo_matrix_t ctm;
cairo_matrix_t ctm_inverse;
cairo_path_fixed_t path;
cairo_pen_t pen_regular;
struct _cairo_gstate *next;

View file

@ -113,8 +113,6 @@ _cairo_gstate_init (cairo_gstate_t *gstate)
_cairo_gstate_identity_matrix (gstate);
_cairo_path_fixed_init (&gstate->path);
_cairo_pen_init_empty (&gstate->pen_regular);
gstate->next = NULL;
@ -158,19 +156,12 @@ _cairo_gstate_init_copy (cairo_gstate_t *gstate, cairo_gstate_t *other)
cairo_pattern_reference (gstate->source);
status = _cairo_path_fixed_init_copy (&gstate->path, &other->path);
status = _cairo_pen_init_copy (&gstate->pen_regular, &other->pen_regular);
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_fixed_fini (&gstate->path);
CLEANUP_FONT:
cairo_scaled_font_destroy (gstate->scaled_font);
gstate->scaled_font = NULL;
@ -204,8 +195,6 @@ _cairo_gstate_fini (cairo_gstate_t *gstate)
cairo_pattern_destroy (gstate->source);
_cairo_path_fixed_fini (&gstate->path);
_cairo_pen_fini (&gstate->pen_regular);
if (gstate->dash) {
@ -680,7 +669,7 @@ _cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate,
return CAIRO_STATUS_SUCCESS;
}
static void
void
_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y)
{
cairo_matrix_transform_point (&gstate->ctm, x, y);
@ -690,7 +679,7 @@ _cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y)
}
}
static void
void
_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y)
{
if (gstate->surface) {
@ -700,393 +689,9 @@ _cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y)
cairo_matrix_transform_point (&gstate->ctm_inverse, x, y);
}
cairo_status_t
_cairo_gstate_new_path (cairo_gstate_t *gstate)
{
_cairo_path_fixed_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_gstate_user_to_backend (gstate, &x, &y);
point.x = _cairo_fixed_from_double (x);
point.y = _cairo_fixed_from_double (y);
return _cairo_path_fixed_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_gstate_user_to_backend (gstate, &x, &y);
point.x = _cairo_fixed_from_double (x);
point.y = _cairo_fixed_from_double (y);
return _cairo_path_fixed_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_gstate_user_to_backend (gstate, &x0, &y0);
_cairo_gstate_user_to_backend (gstate, &x1, &y1);
_cairo_gstate_user_to_backend (gstate, &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_fixed_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_fixed_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_fixed_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_fixed_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_gstate_stroke_to_path (cairo_gstate_t *gstate)
{
cairo_status_t status;
@ -1095,194 +700,6 @@ _cairo_gstate_stroke_path (cairo_gstate_t *gstate)
}
*/
cairo_status_t
_cairo_gstate_close_path (cairo_gstate_t *gstate)
{
return _cairo_path_fixed_close_path (&gstate->path);
}
cairo_status_t
_cairo_gstate_get_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_fixed_get_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_gstate_backend_to_user (gstate, &x, &y);
}
if (x_ret)
*x_ret = x;
if (y_ret)
*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_fixed_t path;
gpi_t gpi;
/* Anything we want from gstate must be copied. We must not retain
pointers into gstate. */
_cairo_path_fixed_init_copy (&path, &gstate->path);
gpi.ctm_inverse = gstate->ctm_inverse;
if (gstate->surface)
cairo_matrix_translate (&gpi.ctm_inverse,
- gstate->surface->device_x_offset,
- gstate->surface->device_y_offset);
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_fixed_interpret (&path,
CAIRO_DIRECTION_FORWARD,
_gpi_move_to,
_gpi_line_to,
_gpi_curve_to,
_gpi_close_path,
&gpi);
}
static void
_cairo_gstate_pattern_transform (cairo_gstate_t *gstate,
cairo_pattern_t *pattern)
@ -1305,7 +722,7 @@ _cairo_gstate_get_clip_extents (cairo_gstate_t *gstate,
}
cairo_status_t
_cairo_gstate_stroke (cairo_gstate_t *gstate)
_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
{
cairo_status_t status;
cairo_traps_t traps;
@ -1317,7 +734,7 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate)
_cairo_traps_init (&traps);
status = _cairo_path_fixed_stroke_to_traps (&gstate->path, gstate, &traps);
status = _cairo_path_fixed_stroke_to_traps (path, gstate, &traps);
if (status) {
_cairo_traps_fini (&traps);
return status;
@ -1331,16 +748,15 @@ _cairo_gstate_stroke (cairo_gstate_t *gstate)
_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,
cairo_bool_t *inside_ret)
_cairo_gstate_in_stroke (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
double x,
double y,
cairo_bool_t *inside_ret)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_traps_t traps;
@ -1351,7 +767,7 @@ _cairo_gstate_in_stroke (cairo_gstate_t *gstate,
_cairo_traps_init (&traps);
status = _cairo_path_fixed_stroke_to_traps (&gstate->path, gstate, &traps);
status = _cairo_path_fixed_stroke_to_traps (path, gstate, &traps);
if (status)
goto BAIL;
@ -1835,14 +1251,14 @@ _cairo_gstate_clip_and_composite_trapezoids (cairo_gstate_t *gstate,
}
cairo_status_t
_cairo_gstate_fill (cairo_gstate_t *gstate)
_cairo_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
{
cairo_status_t status;
cairo_traps_t traps;
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_to_traps (&gstate->path, gstate, &traps);
status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps);
if (status) {
_cairo_traps_fini (&traps);
return status;
@ -1856,16 +1272,15 @@ _cairo_gstate_fill (cairo_gstate_t *gstate)
_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,
cairo_bool_t *inside_ret)
_cairo_gstate_in_fill (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
double x,
double y,
cairo_bool_t *inside_ret)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_traps_t traps;
@ -1874,7 +1289,7 @@ _cairo_gstate_in_fill (cairo_gstate_t *gstate,
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_to_traps (&gstate->path, gstate, &traps);
status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps);
if (status)
goto BAIL;
@ -1905,7 +1320,8 @@ _cairo_gstate_show_page (cairo_gstate_t *gstate)
}
cairo_status_t
_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
double *x1, double *y1,
double *x2, double *y2)
{
@ -1917,7 +1333,7 @@ _cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
_cairo_traps_init (&traps);
status = _cairo_path_fixed_stroke_to_traps (&gstate->path, gstate, &traps);
status = _cairo_path_fixed_stroke_to_traps (path, gstate, &traps);
if (status)
goto BAIL;
@ -1938,7 +1354,8 @@ BAIL:
}
cairo_status_t
_cairo_gstate_fill_extents (cairo_gstate_t *gstate,
_cairo_gstate_fill_extents (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
double *x1, double *y1,
double *x2, double *y2)
{
@ -1948,7 +1365,7 @@ _cairo_gstate_fill_extents (cairo_gstate_t *gstate,
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_to_traps (&gstate->path, gstate, &traps);
status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps);
if (status)
goto BAIL;
@ -2008,7 +1425,7 @@ _cairo_gstate_restore_external_state (cairo_gstate_t *gstate)
}
cairo_status_t
_cairo_gstate_clip (cairo_gstate_t *gstate)
_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path)
{
cairo_status_t status;
cairo_pattern_union_t pattern;
@ -2019,7 +1436,7 @@ _cairo_gstate_clip (cairo_gstate_t *gstate)
/* Fill the clip region as traps. */
_cairo_traps_init (&traps);
status = _cairo_path_fixed_fill_to_traps (&gstate->path, gstate, &traps);
status = _cairo_path_fixed_fill_to_traps (path, gstate, &traps);
if (!CAIRO_OK (status)) {
_cairo_traps_fini (&traps);
return status;
@ -2104,8 +1521,10 @@ _cairo_gstate_clip (cairo_gstate_t *gstate)
cairo_status_t
_cairo_gstate_show_surface (cairo_gstate_t *gstate,
cairo_surface_t *surface,
int width,
int height)
double x,
double y,
double width,
double height)
{
/* We are dealing with 6 coordinate spaces in this function. this makes
@ -2199,7 +1618,8 @@ _cairo_gstate_show_surface (cairo_gstate_t *gstate,
image_to_backend = image_to_device;
}
_cairo_gstate_get_current_point (gstate, &backend_x, &backend_y);
backend_x = x;
backend_y = y;
backend_width = width;
backend_height = height;
_cairo_matrix_transform_bounding_box (&image_to_backend,
@ -2480,30 +1900,18 @@ _cairo_gstate_get_font_extents (cairo_gstate_t *gstate,
cairo_status_t
_cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
const char *utf8,
double x,
double y,
cairo_glyph_t **glyphs,
int *num_glyphs)
{
cairo_status_t status;
cairo_point_t point;
double origin_x, origin_y;
int i;
status = _cairo_gstate_ensure_font (gstate);
if (status)
return status;
status = _cairo_path_fixed_get_current_point (&gstate->path, &point);
if (status == CAIRO_STATUS_NO_CURRENT_POINT) {
origin_x = 0.0;
origin_y = 0.0;
} else {
origin_x = _cairo_fixed_to_double (point.x);
origin_y = _cairo_fixed_to_double (point.y);
_cairo_gstate_backend_to_user (gstate,
&origin_x, &origin_y);
}
status = _cairo_scaled_font_text_to_glyphs (gstate->scaled_font,
utf8, glyphs, num_glyphs);
@ -2518,8 +1926,8 @@ _cairo_gstate_text_to_glyphs (cairo_gstate_t *gstate,
cairo_matrix_transform_point (&gstate->font_matrix,
&((*glyphs)[i].x),
&((*glyphs)[i].y));
(*glyphs)[i].x += origin_x;
(*glyphs)[i].y += origin_y;
(*glyphs)[i].x += x;
(*glyphs)[i].y += y;
}
return CAIRO_STATUS_SUCCESS;
@ -2703,9 +2111,10 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate,
}
cairo_status_t
_cairo_gstate_glyph_path (cairo_gstate_t *gstate,
cairo_glyph_t *glyphs,
int num_glyphs)
_cairo_gstate_glyph_path (cairo_gstate_t *gstate,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_fixed_t *path)
{
cairo_status_t status;
int i;
@ -2725,7 +2134,7 @@ _cairo_gstate_glyph_path (cairo_gstate_t *gstate,
status = _cairo_scaled_font_glyph_path (gstate->scaled_font,
transformed_glyphs, num_glyphs,
&gstate->path);
path);
free (transformed_glyphs);
return status;

View file

@ -41,10 +41,14 @@
extern cairo_path_t _cairo_path_nil;
cairo_path_t *
_cairo_path_data_create (cairo_gstate_t *gstate);
_cairo_path_data_create (cairo_path_fixed_t *path,
cairo_matrix_t *ctm_inverse,
double tolerance);
cairo_path_t *
_cairo_path_data_create_flat (cairo_gstate_t *gstate);
_cairo_path_data_create_flat (cairo_path_fixed_t *path,
cairo_matrix_t *ctm_inverse,
double tolerance);
cairo_status_t
_cairo_path_data_append_to_context (cairo_path_t *path,

View file

@ -34,6 +34,8 @@
*/
#include "cairo-path-data-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-gstate-private.h"
cairo_path_t
_cairo_path_nil = { NULL, 0 };
@ -41,56 +43,113 @@ _cairo_path_nil = { NULL, 0 };
/* Closure for path interpretation. */
typedef struct cairo_path_data_count {
int count;
double tolerance;
cairo_point_t current_point;
} cpdc_t;
static void
_cpdc_move_to (void *closure, double x, double y)
static cairo_status_t
_cpdc_move_to (void *closure, cairo_point_t *point)
{
cpdc_t *cpdc = closure;
cpdc->count += 2;
cpdc->current_point = *point;
return CAIRO_STATUS_SUCCESS;
}
static void
_cpdc_line_to (void *closure, double x, double y)
static cairo_status_t
_cpdc_line_to (void *closure, cairo_point_t *point)
{
cpdc_t *cpdc = closure;
cpdc->count += 2;
cpdc->current_point = *point;
return CAIRO_STATUS_SUCCESS;
}
static void
_cpdc_curve_to (void *closure,
double x1, double y1,
double x2, double y2,
double x3, double y3)
static cairo_status_t
_cpdc_curve_to (void *closure,
cairo_point_t *p1,
cairo_point_t *p2,
cairo_point_t *p3)
{
cpdc_t *cpdc = closure;
cpdc->count += 4;
cpdc->current_point = *p3;
return CAIRO_STATUS_SUCCESS;
}
static void
static cairo_status_t
_cpdc_curve_to_flatten (void *closure,
cairo_point_t *p1,
cairo_point_t *p2,
cairo_point_t *p3)
{
cpdc_t *cpdc = closure;
cairo_status_t status;
cairo_spline_t spline;
int i;
cairo_point_t *p0 = &cpdc->current_point;
status = _cairo_spline_init (&spline, p0, p1, p2, p3);
if (status == CAIRO_INT_STATUS_DEGENERATE)
return CAIRO_STATUS_SUCCESS;
status = _cairo_spline_decompose (&spline, cpdc->tolerance);
if (status)
return status;
for (i=1; i < spline.num_points; i++)
_cpdc_line_to (cpdc, &spline.points[i]);
cpdc->current_point = *p3;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cpdc_close_path (void *closure)
{
cpdc_t *cpdc = closure;
cpdc->count += 1;
cpdc->current_point.x = 0;
cpdc->current_point.y = 0;
return CAIRO_STATUS_SUCCESS;
}
static int
_cairo_path_data_count (cairo_gstate_t *gstate, cairo_bool_t flatten)
_cairo_path_data_count (cairo_path_t *path,
cairo_path_fixed_t *path_fixed,
double tolerance,
cairo_bool_t flatten)
{
cpdc_t cpdc;
cpdc.count = 0;
cpdc.tolerance = tolerance;
cpdc.current_point.x = 0;
cpdc.current_point.y = 0;
_cairo_gstate_interpret_path (gstate,
_cpdc_move_to,
_cpdc_line_to,
flatten ? NULL : _cpdc_curve_to,
_cpdc_close_path,
&cpdc);
_cairo_path_fixed_interpret (path_fixed,
CAIRO_DIRECTION_FORWARD,
_cpdc_move_to,
_cpdc_line_to,
flatten ?
_cpdc_curve_to_flatten :
_cpdc_curve_to,
_cpdc_close_path,
&cpdc);
return cpdc.count;
}
@ -98,13 +157,22 @@ _cairo_path_data_count (cairo_gstate_t *gstate, cairo_bool_t flatten)
/* Closure for path interpretation. */
typedef struct cairo_path_data_populate {
cairo_path_data_t *data;
cairo_matrix_t *ctm_inverse;
double tolerance;
cairo_point_t current_point;
} cpdp_t;
static void
_cpdp_move_to (void *closure, double x, double y)
static cairo_status_t
_cpdp_move_to (void *closure, cairo_point_t *point)
{
cpdp_t *cpdp = closure;
cairo_path_data_t *data = cpdp->data;
double x, y;
x = _cairo_fixed_to_double (point->x);
y = _cairo_fixed_to_double (point->y);
cairo_matrix_transform_point (cpdp->ctm_inverse, &x, &y);
data->header.type = CAIRO_PATH_MOVE_TO;
data->header.length = 2;
@ -114,13 +182,23 @@ _cpdp_move_to (void *closure, double x, double y)
data[1].point.y = y;
cpdp->data += data->header.length;
cpdp->current_point = *point;
return CAIRO_STATUS_SUCCESS;
}
static void
_cpdp_line_to (void *closure, double x, double y)
static cairo_status_t
_cpdp_line_to (void *closure, cairo_point_t *point)
{
cpdp_t *cpdp = closure;
cairo_path_data_t *data = cpdp->data;
double x, y;
x = _cairo_fixed_to_double (point->x);
y = _cairo_fixed_to_double (point->y);
cairo_matrix_transform_point (cpdp->ctm_inverse, &x, &y);
data->header.type = CAIRO_PATH_LINE_TO;
data->header.length = 2;
@ -130,16 +208,35 @@ _cpdp_line_to (void *closure, double x, double y)
data[1].point.y = y;
cpdp->data += data->header.length;
cpdp->current_point = *point;
return CAIRO_STATUS_SUCCESS;
}
static void
_cpdp_curve_to (void *closure,
double x1, double y1,
double x2, double y2,
double x3, double y3)
static cairo_status_t
_cpdp_curve_to (void *closure,
cairo_point_t *p1,
cairo_point_t *p2,
cairo_point_t *p3)
{
cpdp_t *cpdp = closure;
cairo_path_data_t *data = cpdp->data;
double x1, y1;
double x2, y2;
double x3, y3;
x1 = _cairo_fixed_to_double (p1->x);
y1 = _cairo_fixed_to_double (p1->y);
cairo_matrix_transform_point (cpdp->ctm_inverse, &x1, &y1);
x2 = _cairo_fixed_to_double (p2->x);
y2 = _cairo_fixed_to_double (p2->y);
cairo_matrix_transform_point (cpdp->ctm_inverse, &x2, &y2);
x3 = _cairo_fixed_to_double (p3->x);
y3 = _cairo_fixed_to_double (p3->y);
cairo_matrix_transform_point (cpdp->ctm_inverse, &x3, &y3);
data->header.type = CAIRO_PATH_CURVE_TO;
data->header.length = 4;
@ -155,9 +252,42 @@ _cpdp_curve_to (void *closure,
data[3].point.y = y3;
cpdp->data += data->header.length;
cpdp->current_point = *p3;
return CAIRO_STATUS_SUCCESS;
}
static void
static cairo_status_t
_cpdp_curve_to_flatten (void *closure,
cairo_point_t *p1,
cairo_point_t *p2,
cairo_point_t *p3)
{
cpdp_t *cpdp = closure;
cairo_status_t status;
cairo_spline_t spline;
int i;
cairo_point_t *p0 = &cpdp->current_point;
status = _cairo_spline_init (&spline, p0, p1, p2, p3);
if (status == CAIRO_INT_STATUS_DEGENERATE)
return CAIRO_STATUS_SUCCESS;
status = _cairo_spline_decompose (&spline, cpdp->tolerance);
if (status)
return status;
for (i=1; i < spline.num_points; i++)
_cpdp_line_to (cpdp, &spline.points[i]);
cpdp->current_point = *p3;
return CAIRO_STATUS_SUCCESS;
}
static cairo_status_t
_cpdp_close_path (void *closure)
{
cpdp_t *cpdp = closure;
@ -167,30 +297,47 @@ _cpdp_close_path (void *closure)
data->header.length = 1;
cpdp->data += data->header.length;
cpdp->current_point.x = 0;
cpdp->current_point.y = 0;
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_path_data_populate (cairo_path_t *path,
cairo_gstate_t *gstate,
cairo_path_fixed_t *path_fixed,
cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_bool_t flatten)
{
cpdp_t cpdp;
cpdp.data = path->data;
cpdp.ctm_inverse = ctm_inverse;
cpdp.tolerance = tolerance;
cpdp.current_point.x = 0;
cpdp.current_point.y = 0;
_cairo_gstate_interpret_path (gstate,
_cpdp_move_to,
_cpdp_line_to,
flatten ? NULL : _cpdp_curve_to,
_cpdp_close_path,
&cpdp);
_cairo_path_fixed_interpret (path_fixed,
CAIRO_DIRECTION_FORWARD,
_cpdp_move_to,
_cpdp_line_to,
flatten ?
_cpdp_curve_to_flatten :
_cpdp_curve_to,
_cpdp_close_path,
&cpdp);
/* Sanity check the count */
assert (cpdp.data - path->data == path->num_data);
}
static cairo_path_t *
_cairo_path_data_create_real (cairo_gstate_t *gstate, cairo_bool_t flatten)
_cairo_path_data_create_real (cairo_path_fixed_t *path_fixed,
cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_bool_t flatten)
{
cairo_path_t *path;
@ -198,7 +345,8 @@ _cairo_path_data_create_real (cairo_gstate_t *gstate, cairo_bool_t flatten)
if (path == NULL)
return &_cairo_path_nil;
path->num_data = _cairo_path_data_count (gstate, flatten);
path->num_data = _cairo_path_data_count (path, path_fixed,
tolerance, flatten);
path->data = malloc (path->num_data * sizeof (cairo_path_data_t));
if (path->data == NULL) {
@ -206,7 +354,8 @@ _cairo_path_data_create_real (cairo_gstate_t *gstate, cairo_bool_t flatten)
return &_cairo_path_nil;
}
_cairo_path_data_populate (path, gstate, flatten);
_cairo_path_data_populate (path, path_fixed,
ctm_inverse, tolerance, flatten);
return path;
}
@ -220,15 +369,19 @@ cairo_path_destroy (cairo_path_t *path)
}
cairo_path_t *
_cairo_path_data_create (cairo_gstate_t *gstate)
_cairo_path_data_create (cairo_path_fixed_t *path,
cairo_matrix_t *ctm_inverse,
double tolerance)
{
return _cairo_path_data_create_real (gstate, FALSE);
return _cairo_path_data_create_real (path, ctm_inverse, tolerance, FALSE);
}
cairo_path_t *
_cairo_path_data_create_flat (cairo_gstate_t *gstate)
_cairo_path_data_create_flat (cairo_path_fixed_t *path,
cairo_matrix_t *ctm_inverse,
double tolerance)
{
return _cairo_path_data_create_real (gstate, TRUE);
return _cairo_path_data_create_real (path, ctm_inverse, tolerance, TRUE);
}
cairo_status_t

View file

@ -156,16 +156,21 @@ _cairo_path_fixed_fini (cairo_path_fixed_t *path)
}
cairo_status_t
_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
cairo_point_t *point)
_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
cairo_fixed_t x,
cairo_fixed_t y)
{
cairo_status_t status;
cairo_point_t point;
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, point, 1);
point.x = x;
point.y = y;
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, &point, 1);
if (status)
return status;
path->current_point = *point;
path->current_point = point;
path->has_current_point = 1;
path->last_move_point = path->current_point;
@ -174,27 +179,33 @@ _cairo_path_fixed_move_to (cairo_path_fixed_t *path,
cairo_status_t
_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
cairo_distance_t *distance)
cairo_fixed_t dx,
cairo_fixed_t dy)
{
cairo_point_t point;
cairo_fixed_t x, y;
point.x = path->current_point.x + distance->dx;
point.y = path->current_point.y + distance->dy;
x = path->current_point.x + dx;
y = path->current_point.y + dy;
return _cairo_path_fixed_move_to (path, &point);
return _cairo_path_fixed_move_to (path, x, y);
}
cairo_status_t
_cairo_path_fixed_line_to (cairo_path_fixed_t *path,
cairo_point_t *point)
cairo_fixed_t x,
cairo_fixed_t y)
{
cairo_status_t status;
cairo_point_t point;
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, point, 1);
point.x = x;
point.y = y;
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, &point, 1);
if (status)
return status;
path->current_point = *point;
path->current_point = point;
path->has_current_point = 1;
return CAIRO_STATUS_SUCCESS;
@ -202,34 +213,35 @@ _cairo_path_fixed_line_to (cairo_path_fixed_t *path,
cairo_status_t
_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
cairo_distance_t *distance)
cairo_fixed_t dx,
cairo_fixed_t dy)
{
cairo_point_t point;
cairo_fixed_t x, y;
point.x = path->current_point.x + distance->dx;
point.y = path->current_point.y + distance->dy;
x = path->current_point.x + dx;
y = path->current_point.y + dy;
return _cairo_path_fixed_line_to (path, &point);
return _cairo_path_fixed_line_to (path, x, y);
}
cairo_status_t
_cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
cairo_point_t *p0,
cairo_point_t *p1,
cairo_point_t *p2)
cairo_fixed_t x0, cairo_fixed_t y0,
cairo_fixed_t x1, cairo_fixed_t y1,
cairo_fixed_t x2, cairo_fixed_t y2)
{
cairo_status_t status;
cairo_point_t point[3];
point[0] = *p0;
point[1] = *p1;
point[2] = *p2;
point[0].x = x0; point[0].y = y0;
point[1].x = x1; point[1].y = y1;
point[2].x = x2; point[2].y = y2;
status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_CURVE_TO, point, 3);
if (status)
return status;
path->current_point = *p2;
path->current_point = point[2];
path->has_current_point = 1;
return CAIRO_STATUS_SUCCESS;
@ -237,22 +249,27 @@ _cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
cairo_status_t
_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
cairo_distance_t *d0,
cairo_distance_t *d1,
cairo_distance_t *d2)
cairo_fixed_t dx0, cairo_fixed_t dy0,
cairo_fixed_t dx1, cairo_fixed_t dy1,
cairo_fixed_t dx2, cairo_fixed_t dy2)
{
cairo_point_t p0, p1, p2;
cairo_fixed_t x0, y0;
cairo_fixed_t x1, y1;
cairo_fixed_t x2, y2;
p0.x = path->current_point.x + d0->dx;
p0.y = path->current_point.y + d0->dy;
x0 = path->current_point.x + dx0;
y0 = path->current_point.y + dy0;
p1.x = path->current_point.x + d1->dx;
p1.y = path->current_point.y + d1->dy;
x1 = path->current_point.x + dx0;
y1 = path->current_point.y + dy0;
p2.x = path->current_point.x + d2->dx;
p2.y = path->current_point.y + d2->dy;
x2 = path->current_point.x + dx0;
y2 = path->current_point.y + dy0;
return _cairo_path_fixed_curve_to (path, &p0, &p1, &p2);
return _cairo_path_fixed_curve_to (path,
x0, y0,
x1, y1,
x2, y2);
}
cairo_status_t
@ -273,12 +290,14 @@ _cairo_path_fixed_close_path (cairo_path_fixed_t *path)
cairo_status_t
_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
cairo_point_t *point)
cairo_fixed_t *x,
cairo_fixed_t *y)
{
if (! path->has_current_point)
return CAIRO_STATUS_NO_CURRENT_POINT;
*point = path->current_point;
*x = path->current_point.x;
*y = path->current_point.y;
return CAIRO_STATUS_SUCCESS;
}

View file

@ -37,10 +37,14 @@
#define CAIRO_PRIVATE_H
#include "cairo-gstate-private.h"
#include "cairo-path-fixed-private.h"
struct _cairo {
unsigned int ref_count;
cairo_gstate_t *gstate;
cairo_path_fixed_t path;
cairo_status_t status;
};

View file

@ -36,8 +36,9 @@
*/
#include "cairoint.h"
#include "cairo-private.h"
#include "cairo-arc-private.h"
#include "cairo-path-data-private.h"
#define CAIRO_TOLERANCE_MINIMUM 0.0002 /* We're limited by 16 bits of sub-pixel precision */
@ -101,6 +102,8 @@ cairo_create (void)
if (cr->gstate == NULL)
cr->status = CAIRO_STATUS_NO_MEMORY;
_cairo_path_fixed_init (&cr->path);
CAIRO_CHECK_SANITY (cr);
return cr;
}
@ -147,6 +150,8 @@ cairo_destroy (cairo_t *cr)
_cairo_gstate_destroy (tmp);
}
_cairo_path_fixed_fini (&cr->path);
free (cr);
}
@ -1161,18 +1166,27 @@ cairo_new_path (cairo_t *cr)
if (cr->status)
return;
cr->status = _cairo_gstate_new_path (cr->gstate);
_cairo_path_fixed_fini (&cr->path);
CAIRO_CHECK_SANITY (cr);
}
slim_hidden_def(cairo_new_path);
void
cairo_move_to (cairo_t *cr, double x, double y)
{
cairo_fixed_t x_fixed, y_fixed;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_move_to (cr->gstate, x, y);
_cairo_gstate_user_to_backend (cr->gstate, &x, &y);
x_fixed = _cairo_fixed_from_double (x);
y_fixed = _cairo_fixed_from_double (y);
cr->status = _cairo_path_fixed_move_to (&cr->path, x_fixed, y_fixed);
CAIRO_CHECK_SANITY (cr);
}
slim_hidden_def(cairo_move_to);
@ -1180,11 +1194,18 @@ slim_hidden_def(cairo_move_to);
void
cairo_line_to (cairo_t *cr, double x, double y)
{
cairo_fixed_t x_fixed, y_fixed;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_line_to (cr->gstate, x, y);
_cairo_gstate_user_to_backend (cr->gstate, &x, &y);
x_fixed = _cairo_fixed_from_double (x);
y_fixed = _cairo_fixed_from_double (y);
cr->status = _cairo_path_fixed_line_to (&cr->path, x_fixed, y_fixed);
CAIRO_CHECK_SANITY (cr);
}
@ -1194,14 +1215,32 @@ cairo_curve_to (cairo_t *cr,
double x2, double y2,
double x3, double y3)
{
cairo_fixed_t x1_fixed, y1_fixed;
cairo_fixed_t x2_fixed, y2_fixed;
cairo_fixed_t x3_fixed, y3_fixed;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_curve_to (cr->gstate,
x1, y1,
x2, y2,
x3, y3);
_cairo_gstate_user_to_backend (cr->gstate, &x1, &y1);
_cairo_gstate_user_to_backend (cr->gstate, &x2, &y2);
_cairo_gstate_user_to_backend (cr->gstate, &x3, &y3);
x1_fixed = _cairo_fixed_from_double (x1);
y1_fixed = _cairo_fixed_from_double (y1);
x2_fixed = _cairo_fixed_from_double (x2);
y2_fixed = _cairo_fixed_from_double (y2);
x3_fixed = _cairo_fixed_from_double (x3);
y3_fixed = _cairo_fixed_from_double (y3);
cr->status = _cairo_path_fixed_curve_to (&cr->path,
x1_fixed, y1_fixed,
x2_fixed, y2_fixed,
x3_fixed, y3_fixed);
CAIRO_CHECK_SANITY (cr);
}
@ -1248,10 +1287,20 @@ cairo_arc (cairo_t *cr,
if (cr->status)
return;
cr->status = _cairo_gstate_arc (cr->gstate,
xc, yc,
radius,
angle1, angle2);
/* Do nothing, successfully, if radius is <= 0 */
if (radius <= 0.0)
return;
while (angle2 < angle1)
angle2 += 2 * M_PI;
cairo_line_to (cr,
xc + radius * cos (angle1),
yc + radius * sin (angle1));
_cairo_arc_path (cr, xc, yc, radius,
angle1, angle2);
CAIRO_CHECK_SANITY (cr);
}
@ -1279,10 +1328,20 @@ cairo_arc_negative (cairo_t *cr,
if (cr->status)
return;
cr->status = _cairo_gstate_arc_negative (cr->gstate,
xc, yc,
radius,
angle1, angle2);
/* Do nothing, successfully, if radius is <= 0 */
if (radius <= 0.0)
return;
while (angle2 > angle1)
angle2 -= 2 * M_PI;
cairo_line_to (cr,
xc + radius * cos (angle1),
yc + radius * sin (angle1));
_cairo_arc_path_negative (cr, xc, yc, radius,
angle1, angle2);
CAIRO_CHECK_SANITY (cr);
}
@ -1306,22 +1365,36 @@ cairo_arc_to (cairo_t *cr,
void
cairo_rel_move_to (cairo_t *cr, double dx, double dy)
{
cairo_fixed_t dx_fixed, dy_fixed;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_rel_move_to (cr->gstate, dx, dy);
_cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy);
dx_fixed = _cairo_fixed_from_double (dx);
dy_fixed = _cairo_fixed_from_double (dy);
cr->status = _cairo_path_fixed_rel_move_to (&cr->path, dx_fixed, dy_fixed);
CAIRO_CHECK_SANITY (cr);
}
void
cairo_rel_line_to (cairo_t *cr, double dx, double dy)
{
cairo_fixed_t dx_fixed, dy_fixed;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_rel_line_to (cr->gstate, dx, dy);
_cairo_gstate_user_to_device_distance (cr->gstate, &dx, &dy);
dx_fixed = _cairo_fixed_from_double (dx);
dy_fixed = _cairo_fixed_from_double (dy);
cr->status = _cairo_path_fixed_rel_line_to (&cr->path, dx_fixed, dy_fixed);
CAIRO_CHECK_SANITY (cr);
}
slim_hidden_def(cairo_rel_line_to);
@ -1332,14 +1405,32 @@ cairo_rel_curve_to (cairo_t *cr,
double dx2, double dy2,
double dx3, double dy3)
{
cairo_fixed_t dx1_fixed, dy1_fixed;
cairo_fixed_t dx2_fixed, dy2_fixed;
cairo_fixed_t dx3_fixed, dy3_fixed;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_rel_curve_to (cr->gstate,
dx1, dy1,
dx2, dy2,
dx3, dy3);
_cairo_gstate_user_to_device_distance (cr->gstate, &dx1, &dy1);
_cairo_gstate_user_to_device_distance (cr->gstate, &dx2, &dy2);
_cairo_gstate_user_to_device_distance (cr->gstate, &dx3, &dy3);
dx1_fixed = _cairo_fixed_from_double (dx1);
dy1_fixed = _cairo_fixed_from_double (dy1);
dx2_fixed = _cairo_fixed_from_double (dx2);
dy2_fixed = _cairo_fixed_from_double (dy2);
dx3_fixed = _cairo_fixed_from_double (dx3);
dy3_fixed = _cairo_fixed_from_double (dy3);
cr->status = _cairo_path_fixed_rel_curve_to (&cr->path,
dx1_fixed, dy1_fixed,
dx2_fixed, dy2_fixed,
dx3_fixed, dy3_fixed);
CAIRO_CHECK_SANITY (cr);
}
@ -1378,7 +1469,8 @@ cairo_close_path (cairo_t *cr)
if (cr->status)
return;
cr->status = _cairo_gstate_close_path (cr->gstate);
cr->status = _cairo_path_fixed_close_path (&cr->path);
CAIRO_CHECK_SANITY (cr);
}
slim_hidden_def(cairo_close_path);
@ -1412,27 +1504,90 @@ cairo_paint (cairo_t *cr)
CAIRO_CHECK_SANITY (cr);
}
/**
* cairo_stroke:
* @cr: a cairo context
*
* A drawing operator that strokes the current path according to the
* current line width, line join, line cap, and dash settings. After
* cairo_stroke, the current path will be cleared from the cairo
* context. See cairo_set_line_width(), cairo_set_line_join(),
* cairo_set_line_cap(), cairo_set_dash(), and
* cairo_stroke_preserve().
**/
void
cairo_stroke (cairo_t *cr)
{
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cairo_stroke_preserve (cr);
cr->status = _cairo_gstate_stroke (cr->gstate);
CAIRO_CHECK_SANITY (cr);
cairo_new_path (cr);
}
/**
* cairo_stroke_preserve:
* @cr: a cairo context
*
* A drawing operator that strokes the current path according to the
* current line width, line join, line cap, and dash settings. Unlike
* cairo_stroke(), cairo_stroke_preserve preserves the path within the
* cairo context.
*
* See cairo_set_line_width(), cairo_set_line_join(),
* cairo_set_line_cap(), cairo_set_dash(), and
* cairo_stroke_preserve().
**/
void
cairo_fill (cairo_t *cr)
cairo_stroke_preserve (cairo_t *cr)
{
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_fill (cr->gstate);
cr->status = _cairo_gstate_stroke (cr->gstate, &cr->path);
CAIRO_CHECK_SANITY (cr);
}
slim_hidden_def(cairo_stroke_preserve);
/**
* cairo_fill:
* @cr: a cairo context
*
* A drawing operator that fills the current path according to the
* current fill rule. After cairo_fill, the current path will be
* cleared from the cairo context. See cairo_set_fill_rule() and
* cairo_fill_preserve().
**/
void
cairo_fill (cairo_t *cr)
{
cairo_fill_preserve (cr);
cairo_new_path (cr);
}
/**
* cairo_fill_preserve:
* @cr: a cairo context
*
* A drawing operator that fills the current path according to the
* current fill rule. Unlike cairo_fill(), cairo_fill_preserve
* preserves the path within the cairo context.
*
* See cairo_set_fill_rule() and cairo_fill().
**/
void
cairo_fill_preserve (cairo_t *cr)
{
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_fill (cr->gstate, &cr->path);
CAIRO_CHECK_SANITY (cr);
}
slim_hidden_def(cairo_fill_preserve);
void
cairo_copy_page (cairo_t *cr)
@ -1465,7 +1620,9 @@ cairo_in_stroke (cairo_t *cr, double x, double y)
if (cr->status)
return 0;
cr->status = _cairo_gstate_in_stroke (cr->gstate, x, y, &inside);
cr->status = _cairo_gstate_in_stroke (cr->gstate,
&cr->path,
x, y, &inside);
CAIRO_CHECK_SANITY (cr);
@ -1484,7 +1641,9 @@ cairo_in_fill (cairo_t *cr, double x, double y)
if (cr->status)
return 0;
cr->status = _cairo_gstate_in_fill (cr->gstate, x, y, &inside);
cr->status = _cairo_gstate_in_fill (cr->gstate,
&cr->path,
x, y, &inside);
CAIRO_CHECK_SANITY (cr);
@ -1502,7 +1661,9 @@ cairo_stroke_extents (cairo_t *cr,
if (cr->status)
return;
cr->status = _cairo_gstate_stroke_extents (cr->gstate, x1, y1, x2, y2);
cr->status = _cairo_gstate_stroke_extents (cr->gstate,
&cr->path,
x1, y1, x2, y2);
CAIRO_CHECK_SANITY (cr);
}
@ -1514,7 +1675,9 @@ cairo_fill_extents (cairo_t *cr,
if (cr->status)
return;
cr->status = _cairo_gstate_fill_extents (cr->gstate, x1, y1, x2, y2);
cr->status = _cairo_gstate_fill_extents (cr->gstate,
&cr->path,
x1, y1, x2, y2);
CAIRO_CHECK_SANITY (cr);
}
@ -1526,6 +1689,9 @@ cairo_fill_extents (cairo_t *cr,
* region with the current path as it would be filled by cairo_fill()
* and according to the current fill rule (see cairo_set_fill_rule()).
*
* After cairo_clip, the current path will be cleared from the cairo
* context.
*
* The current clip region affects all drawing operations by
* effectively masking out any changes to the surface that are outside
* the current clip region.
@ -1539,14 +1705,45 @@ cairo_fill_extents (cairo_t *cr,
**/
void
cairo_clip (cairo_t *cr)
{
cairo_clip_preserve (cr);
cairo_new_path (cr);
}
/**
* cairo_clip_preserve:
* @cr: a cairo context
*
* Establishes a new clip region by intersecting the current clip
* region with the current path as it would be filled by cairo_fill()
* and according to the current fill rule (see cairo_set_fill_rule()).
*
* Unlike cairo_clip(), cairo_clip_preserve preserves the path within
* the cairo context.
*
* The current clip region affects all drawing operations by
* effectively masking out any changes to the surface that are outside
* the current clip region.
*
* Calling cairo_clip() can only make the clip region smaller, never
* larger. But the current clip is part of the graphics state, so a
* tempoarary restriction of the clip region can be achieved by
* calling cairo_clip() within a cairo_save()/cairo_restore()
* pair. The only other means of increasing the size of the clip
* region is cairo_reset_clip().
**/
void
cairo_clip_preserve (cairo_t *cr)
{
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cr->status = _cairo_gstate_clip (cr->gstate);
cr->status = _cairo_gstate_clip (cr->gstate, &cr->path);
CAIRO_CHECK_SANITY (cr);
}
slim_hidden_def(cairo_clip_preserve);
/**
* cairo_reset_clip:
@ -1763,6 +1960,7 @@ cairo_text_extents (cairo_t *cr,
{
cairo_glyph_t *glyphs = NULL;
int num_glyphs;
double x, y;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
@ -1778,7 +1976,11 @@ cairo_text_extents (cairo_t *cr,
return;
}
cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8, &glyphs, &num_glyphs);
cairo_get_current_point (cr, &x, &y);
cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8,
x, y,
&glyphs, &num_glyphs);
CAIRO_CHECK_SANITY (cr);
if (cr->status) {
@ -1832,6 +2034,7 @@ cairo_show_text (cairo_t *cr, const char *utf8)
{
cairo_glyph_t *glyphs = NULL;
int num_glyphs;
double x, y;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
@ -1840,7 +2043,10 @@ cairo_show_text (cairo_t *cr, const char *utf8)
if (utf8 == NULL)
return;
cairo_get_current_point (cr, &x, &y);
cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8,
x, y,
&glyphs, &num_glyphs);
CAIRO_CHECK_SANITY (cr);
@ -1873,12 +2079,16 @@ cairo_text_path (cairo_t *cr, const char *utf8)
{
cairo_glyph_t *glyphs = NULL;
int num_glyphs;
double x, y;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cairo_get_current_point (cr, &x, &y);
cr->status = _cairo_gstate_text_to_glyphs (cr->gstate, utf8,
x, y,
&glyphs, &num_glyphs);
CAIRO_CHECK_SANITY (cr);
@ -1888,7 +2098,9 @@ cairo_text_path (cairo_t *cr, const char *utf8)
return;
}
cr->status = _cairo_gstate_glyph_path (cr->gstate, glyphs, num_glyphs);
cr->status = _cairo_gstate_glyph_path (cr->gstate,
glyphs, num_glyphs,
&cr->path);
CAIRO_CHECK_SANITY (cr);
if (glyphs)
@ -1902,7 +2114,10 @@ cairo_glyph_path (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs)
if (cr->status)
return;
cr->status = _cairo_gstate_glyph_path (cr->gstate, glyphs, num_glyphs);
cr->status = _cairo_gstate_glyph_path (cr->gstate,
glyphs, num_glyphs,
&cr->path);
CAIRO_CHECK_SANITY (cr);
}
@ -1912,12 +2127,18 @@ cairo_show_surface (cairo_t *cr,
int width,
int height)
{
double x, y;
CAIRO_CHECK_SANITY (cr);
if (cr->status)
return;
cairo_get_current_point (cr, &x, &y);
cr->status = _cairo_gstate_show_surface (cr->gstate,
surface, width, height);
surface,
x, y,
width, height);
CAIRO_CHECK_SANITY (cr);
}
@ -2001,12 +2222,32 @@ DEPRECATE (cairo_current_tolerance, cairo_get_tolerance);
* cairo_text_path(), cairo_stroke_to_path()
**/
void
cairo_get_current_point (cairo_t *cr, double *x, double *y)
cairo_get_current_point (cairo_t *cr, double *x_ret, double *y_ret)
{
cairo_status_t status;
cairo_fixed_t x_fixed, y_fixed;
double x, y;
CAIRO_CHECK_SANITY (cr);
_cairo_gstate_get_current_point (cr->gstate, x, y);
status = _cairo_path_fixed_get_current_point (&cr->path, &x_fixed, &y_fixed);
if (status == CAIRO_STATUS_NO_CURRENT_POINT) {
x = 0.0;
y = 0.0;
} else {
x = _cairo_fixed_to_double (x_fixed);
y = _cairo_fixed_to_double (y_fixed);
_cairo_gstate_backend_to_user (cr->gstate, &x, &y);
}
if (x_ret)
*x_ret = x;
if (y_ret)
*y_ret = y;
CAIRO_CHECK_SANITY (cr);
}
slim_hidden_def(cairo_get_current_point);
DEPRECATE (cairo_current_point, cairo_get_current_point);
/**
@ -2228,7 +2469,9 @@ cairo_copy_path (cairo_t *cr)
if (cr->status)
return &_cairo_path_nil;
return _cairo_path_data_create (cr->gstate);
return _cairo_path_data_create (&cr->path,
&cr->gstate->ctm_inverse,
cr->gstate->tolerance);
}
/**
@ -2257,7 +2500,9 @@ cairo_copy_path_flat (cairo_t *cr)
if (cr->status)
return &_cairo_path_nil;
return _cairo_path_data_create_flat (cr->gstate);
return _cairo_path_data_create_flat (&cr->path,
&cr->gstate->ctm_inverse,
cr->gstate->tolerance);
}
/**

View file

@ -465,9 +465,15 @@ cairo_paint (cairo_t *cr);
void
cairo_stroke (cairo_t *cr);
void
cairo_stroke_preserve (cairo_t *cr);
void
cairo_fill (cairo_t *cr);
void
cairo_fill_preserve (cairo_t *cr);
void
cairo_copy_page (cairo_t *cr);
@ -496,10 +502,12 @@ cairo_fill_extents (cairo_t *cr,
void
cairo_reset_clip (cairo_t *cr);
/* Note: cairo_clip does not consume the current path */
void
cairo_clip (cairo_t *cr);
void
cairo_clip_preserve (cairo_t *cr);
/* Font/Text functions */
/**

View file

@ -1042,74 +1042,22 @@ _cairo_gstate_device_to_user (cairo_gstate_t *gstate, double *x, double *y);
cairo_private cairo_status_t
_cairo_gstate_device_to_user_distance (cairo_gstate_t *gstate, double *dx, double *dy);
cairo_private cairo_status_t
_cairo_gstate_new_path (cairo_gstate_t *gstate);
cairo_private void
_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y);
cairo_private void
_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y);
cairo_private cairo_status_t
_cairo_gstate_move_to (cairo_gstate_t *gstate, double x, double y);
_cairo_gstate_stroke (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
cairo_private cairo_status_t
_cairo_gstate_line_to (cairo_gstate_t *gstate, double x, double y);
cairo_private cairo_status_t
_cairo_gstate_curve_to (cairo_gstate_t *gstate,
double x1, double y1,
double x2, double y2,
double x3, double y3);
cairo_private cairo_status_t
_cairo_gstate_arc (cairo_gstate_t *gstate,
double xc, double yc,
double radius,
double angle1, double angle2);
cairo_private cairo_status_t
_cairo_gstate_arc_negative (cairo_gstate_t *gstate,
double xc, double yc,
double radius,
double angle1, double angle2);
cairo_private cairo_status_t
_cairo_gstate_rel_move_to (cairo_gstate_t *gstate, double dx, double dy);
cairo_private cairo_status_t
_cairo_gstate_rel_line_to (cairo_gstate_t *gstate, double dx, double dy);
cairo_private cairo_status_t
_cairo_gstate_rel_curve_to (cairo_gstate_t *gstate,
double dx1, double dy1,
double dx2, double dy2,
double dx3, double dy3);
/* XXX: NYI
cairo_private cairo_status_t
_cairo_gstate_stroke_path (cairo_gstate_t *gstate);
*/
cairo_private cairo_status_t
_cairo_gstate_close_path (cairo_gstate_t *gstate);
cairo_private cairo_status_t
_cairo_gstate_get_current_point (cairo_gstate_t *gstate, double *x, double *y);
cairo_private 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_gstate_fill (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
cairo_private cairo_status_t
_cairo_gstate_get_clip_extents (cairo_gstate_t *gstate,
cairo_rectangle_t *rectangle);
cairo_private cairo_status_t
_cairo_gstate_stroke (cairo_gstate_t *gstate);
cairo_private cairo_status_t
_cairo_gstate_fill (cairo_gstate_t *gstate);
cairo_private cairo_status_t
_cairo_gstate_copy_page (cairo_gstate_t *gstate);
@ -1117,29 +1065,33 @@ cairo_private cairo_status_t
_cairo_gstate_show_page (cairo_gstate_t *gstate);
cairo_private cairo_status_t
_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
_cairo_gstate_stroke_extents (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
double *x1, double *y1,
double *x2, double *y2);
cairo_private cairo_status_t
_cairo_gstate_fill_extents (cairo_gstate_t *gstate,
_cairo_gstate_fill_extents (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
double *x1, double *y1,
double *x2, double *y2);
cairo_private cairo_status_t
_cairo_gstate_in_stroke (cairo_gstate_t *gstate,
double x,
double y,
cairo_bool_t *inside_ret);
_cairo_gstate_in_stroke (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
double x,
double y,
cairo_bool_t *inside_ret);
cairo_private cairo_status_t
_cairo_gstate_in_fill (cairo_gstate_t *gstate,
double x,
double y,
cairo_bool_t *inside_ret);
_cairo_gstate_in_fill (cairo_gstate_t *gstate,
cairo_path_fixed_t *path,
double x,
double y,
cairo_bool_t *inside_ret);
cairo_private cairo_status_t
_cairo_gstate_clip (cairo_gstate_t *gstate);
_cairo_gstate_clip (cairo_gstate_t *gstate, cairo_path_fixed_t *path);
cairo_private cairo_status_t
_cairo_gstate_reset_clip (cairo_gstate_t *gstate);
@ -1150,8 +1102,10 @@ _cairo_gstate_restore_external_state (cairo_gstate_t *gstate);
cairo_private cairo_status_t
_cairo_gstate_show_surface (cairo_gstate_t *gstate,
cairo_surface_t *surface,
int width,
int height);
double x,
double y,
double width,
double height);
cairo_private cairo_status_t
_cairo_gstate_select_font_face (cairo_gstate_t *gstate,
@ -1185,6 +1139,8 @@ _cairo_gstate_set_font_face (cairo_gstate_t *gstate,
cairo_private cairo_status_t
_cairo_gstate_text_to_glyphs (cairo_gstate_t *font,
const char *utf8,
double x,
double y,
cairo_glyph_t **glyphs,
int *num_glyphs);
@ -1200,9 +1156,10 @@ _cairo_gstate_show_glyphs (cairo_gstate_t *gstate,
int num_glyphs);
cairo_private cairo_status_t
_cairo_gstate_glyph_path (cairo_gstate_t *gstate,
cairo_glyph_t *glyphs,
int num_glyphs);
_cairo_gstate_glyph_path (cairo_gstate_t *gstate,
cairo_glyph_t *glyphs,
int num_glyphs,
cairo_path_fixed_t *path);
/* cairo_color.c */
@ -1331,40 +1288,45 @@ _cairo_path_fixed_init_copy (cairo_path_fixed_t *path,
cairo_private void
_cairo_path_fixed_fini (cairo_path_fixed_t *path);
cairo_private cairo_status_t
_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
cairo_point_t *point);
cairo_status_t
_cairo_path_fixed_move_to (cairo_path_fixed_t *path,
cairo_fixed_t x,
cairo_fixed_t y);
cairo_private cairo_status_t
cairo_status_t
_cairo_path_fixed_rel_move_to (cairo_path_fixed_t *path,
cairo_slope_t *slope);
cairo_fixed_t dx,
cairo_fixed_t dy);
cairo_private cairo_status_t
_cairo_path_fixed_line_to (cairo_path_fixed_t *path,
cairo_point_t *point);
cairo_status_t
_cairo_path_fixed_line_to (cairo_path_fixed_t *path,
cairo_fixed_t x,
cairo_fixed_t y);
cairo_private cairo_status_t
cairo_status_t
_cairo_path_fixed_rel_line_to (cairo_path_fixed_t *path,
cairo_slope_t *slope);
cairo_fixed_t dx,
cairo_fixed_t dy);
cairo_private cairo_status_t
_cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
cairo_point_t *p0,
cairo_point_t *p1,
cairo_point_t *p2);
cairo_status_t
_cairo_path_fixed_curve_to (cairo_path_fixed_t *path,
cairo_fixed_t x0, cairo_fixed_t y0,
cairo_fixed_t x1, cairo_fixed_t y1,
cairo_fixed_t x2, cairo_fixed_t y2);
cairo_private cairo_status_t
cairo_status_t
_cairo_path_fixed_rel_curve_to (cairo_path_fixed_t *path,
cairo_slope_t *s0,
cairo_slope_t *s1,
cairo_slope_t *s2);
cairo_fixed_t dx0, cairo_fixed_t dy0,
cairo_fixed_t dx1, cairo_fixed_t dy1,
cairo_fixed_t dx2, cairo_fixed_t dy2);
cairo_private cairo_status_t
_cairo_path_fixed_close_path (cairo_path_fixed_t *path);
cairo_private cairo_status_t
_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
cairo_point_t *point);
cairo_status_t
_cairo_path_fixed_get_current_point (cairo_path_fixed_t *path,
cairo_fixed_t *x,
cairo_fixed_t *y);
typedef cairo_status_t
(cairo_path_fixed_move_to_func_t) (void *closure,
@ -1805,6 +1767,9 @@ _cairo_output_stream_create_for_file (FILE *fp);
/* Avoid unnecessary PLT entries. */
slim_hidden_proto(cairo_get_current_point)
slim_hidden_proto(cairo_fill_preserve)
slim_hidden_proto(cairo_clip_preserve)
slim_hidden_proto(cairo_close_path)
slim_hidden_proto(cairo_matrix_copy)
slim_hidden_proto(cairo_matrix_invert)
@ -1818,10 +1783,12 @@ slim_hidden_proto(cairo_matrix_init_rotate)
slim_hidden_proto(cairo_matrix_transform_distance)
slim_hidden_proto(cairo_matrix_transform_point)
slim_hidden_proto(cairo_move_to)
slim_hidden_proto(cairo_new_path)
slim_hidden_proto(cairo_rel_line_to)
slim_hidden_proto(cairo_restore)
slim_hidden_proto(cairo_save)
slim_hidden_proto(cairo_set_target_surface)
slim_hidden_proto(cairo_stroke_preserve)
slim_hidden_proto(cairo_surface_destroy)
slim_hidden_proto(cairo_surface_get_matrix)
slim_hidden_proto(cairo_surface_set_matrix)

View file

@ -38,10 +38,8 @@ static cairo_test_status_t
draw (cairo_t *cr, int width, int height)
{
cairo_rectangle (cr, PAD, PAD, SIZE, SIZE);
cairo_save (cr);
cairo_set_source_rgb (cr, 0, 0, 1);
cairo_fill (cr);
cairo_restore (cr);
cairo_fill_preserve (cr);
cairo_set_source_rgb (cr, 1, 0, 0);
cairo_stroke (cr);
@ -51,9 +49,7 @@ draw (cairo_t *cr, int width, int height)
PAD + SIZE / 2, PAD + SIZE / 2,
SIZE / 2,
0, 2 * M_PI);
cairo_save (cr);
cairo_fill (cr);
cairo_restore (cr);
cairo_fill_preserve (cr);
cairo_set_source_rgb (cr, 0, 0, 1);
cairo_stroke (cr);

View file

@ -34,14 +34,12 @@ cairo_test_t test = {
typedef struct {
cairo_operator_t operator;
double tolerance;
double point_x;
double point_y;
cairo_fill_rule_t fill_rule;
double line_width;
cairo_line_cap_t line_cap;
cairo_line_join_t line_join;
double miter_limit;
/* XXX: Add cairo_matrix_t here when it is exposed */
cairo_matrix_t matrix;
} settings_t;
/* Two sets of settings, no defaults */
@ -49,24 +47,22 @@ settings_t settings[] = {
{
CAIRO_OPERATOR_IN,
2.0,
12.3,
4.56,
CAIRO_FILL_RULE_EVEN_ODD,
7.7,
CAIRO_LINE_CAP_SQUARE,
CAIRO_LINE_JOIN_ROUND,
3.14
3.14,
{1.0, 2.0, 3.0, 4.0, 5.0, 6.0}
},
{
CAIRO_OPERATOR_ATOP,
5.25,
99.99,
0.001,
CAIRO_FILL_RULE_WINDING,
2.17,
CAIRO_LINE_CAP_ROUND,
CAIRO_LINE_JOIN_BEVEL,
1000.0
1000.0,
{.1, .01, .001, .0001, .00001, .000001}
}
};
@ -75,12 +71,12 @@ settings_set (cairo_t *cr, settings_t *settings)
{
cairo_set_operator (cr, settings->operator);
cairo_set_tolerance (cr, settings->tolerance);
cairo_move_to (cr, settings->point_x, settings->point_y);
cairo_set_fill_rule (cr, settings->fill_rule);
cairo_set_line_width (cr, settings->line_width);
cairo_set_line_cap (cr, settings->line_cap);
cairo_set_line_join (cr, settings->line_join);
cairo_set_miter_limit (cr, settings->miter_limit);
cairo_set_matrix (cr, &settings->matrix);
}
static void
@ -88,21 +84,12 @@ settings_get (cairo_t *cr, settings_t *settings)
{
settings->operator = cairo_get_operator (cr);
settings->tolerance = cairo_get_tolerance (cr);
cairo_get_current_point (cr, &settings->point_x, &settings->point_y);
settings->fill_rule = cairo_get_fill_rule (cr);
settings->line_width = cairo_get_line_width (cr);
settings->line_cap = cairo_get_line_cap (cr);
settings->line_join = cairo_get_line_join (cr);
settings->miter_limit = cairo_get_miter_limit (cr);
}
/* Maximum error is one part of our fixed-point grid */
#define EPSILON (1.0 / 65536.0)
static int
DOUBLES_WITHIN_EPSILON(double a, double b) {
double delta = fabs(a - b);
return delta < EPSILON;
cairo_get_matrix (cr, &settings->matrix);
}
static int
@ -110,13 +97,17 @@ settings_equal (settings_t *a, settings_t *b)
{
return (a->operator == b->operator &&
a->tolerance == b->tolerance &&
DOUBLES_WITHIN_EPSILON (a->point_x, b->point_x) &&
DOUBLES_WITHIN_EPSILON (a->point_y, b->point_y) &&
a->fill_rule == b->fill_rule &&
a->line_width == b->line_width &&
a->line_cap == b->line_cap &&
a->line_join == b->line_join &&
a->miter_limit == b->miter_limit);
a->miter_limit == b->miter_limit &&
a->matrix.xx == b->matrix.xx &&
a->matrix.xy == b->matrix.xy &&
a->matrix.x0 == b->matrix.x0 &&
a->matrix.yx == b->matrix.yx &&
a->matrix.yy == b->matrix.yy &&
a->matrix.y0 == b->matrix.y0);
}
static cairo_test_status_t