diff options
-rw-r--r-- | ChangeLog | 40 | ||||
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/cairo-arc-private.h | 57 | ||||
-rw-r--r-- | src/cairo-arc.c | 296 | ||||
-rw-r--r-- | src/cairo-ft-font.c | 60 | ||||
-rw-r--r-- | src/cairo-gstate-private.h | 4 | ||||
-rw-r--r-- | src/cairo-gstate.c | 677 | ||||
-rw-r--r-- | src/cairo-path-data-private.h | 8 | ||||
-rw-r--r-- | src/cairo-path-data.c | 239 | ||||
-rw-r--r-- | src/cairo-path.c | 93 | ||||
-rw-r--r-- | src/cairo-private.h | 4 | ||||
-rw-r--r-- | src/cairo.c | 321 | ||||
-rw-r--r-- | src/cairo.h | 10 | ||||
-rw-r--r-- | src/cairoint.h | 159 | ||||
-rw-r--r-- | test/fill-and-stroke.c | 8 | ||||
-rw-r--r-- | test/get-and-set.c | 37 |
17 files changed, 1106 insertions, 910 deletions
@@ -1,5 +1,45 @@ 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: * test/Makefile.am: * test/cairo-test.h: @@ -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 diff --git a/src/Makefile.am b/src/Makefile.am index c31b94ac..a16cd096 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/cairo-arc-private.h b/src/cairo-arc-private.h new file mode 100644 index 00000000..1cd41cc1 --- /dev/null +++ b/src/cairo-arc-private.h @@ -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 */ diff --git a/src/cairo-arc.c b/src/cairo-arc.c new file mode 100644 index 00000000..2e6c6895 --- /dev/null +++ b/src/cairo-arc.c @@ -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); +} diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c index 0ae98902..5c463e44 100644 --- a/src/cairo-ft-font.c +++ b/src/cairo-ft-font.c @@ -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; } diff --git a/src/cairo-gstate-private.h b/src/cairo-gstate-private.h index f266bbf4..fcf8a23f 100644 --- a/src/cairo-gstate-private.h +++ b/src/cairo-gstate-private.h @@ -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; diff --git a/src/cairo-gstate.c b/src/cairo-gstate.c index 4290077f..c0cb7dbd 100644 --- a/src/cairo-gstate.c +++ b/src/cairo-gstate.c @@ -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); - if (status) - goto CLEANUP_FONT; - status = _cairo_pen_init_copy (&gstate->pen_regular, &other->pen_regular); if (status) - goto CLEANUP_PATH; + goto CLEANUP_FONT; 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; diff --git a/src/cairo-path-data-private.h b/src/cairo-path-data-private.h index 2833e2ea..946efca6 100644 --- a/src/cairo-path-data-private.h +++ b/src/cairo-path-data-private.h @@ -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, diff --git a/src/cairo-path-data.c b/src/cairo-path-data.c index 84d26554..3bb5fff7 100644 --- a/src/cairo-path-data.c +++ b/src/cairo-path-data.c @@ -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; - - _cairo_gstate_interpret_path (gstate, - _cpdc_move_to, - _cpdc_line_to, - flatten ? NULL : _cpdc_curve_to, - _cpdc_close_path, - &cpdc); + cpdc.tolerance = tolerance; + cpdc.current_point.x = 0; + cpdc.current_point.y = 0; + + _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; - - _cairo_gstate_interpret_path (gstate, - _cpdp_move_to, - _cpdp_line_to, - flatten ? NULL : _cpdp_curve_to, - _cpdp_close_path, - &cpdp); + cpdp.ctm_inverse = ctm_inverse; + cpdp.tolerance = tolerance; + cpdp.current_point.x = 0; + cpdp.current_point.y = 0; + + _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 diff --git a/src/cairo-path.c b/src/cairo-path.c index 21125e0f..53c2375c 100644 --- a/src/cairo-path.c +++ b/src/cairo-path.c @@ -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; + + point.x = x; + point.y = y; - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_MOVE_TO, point, 1); + 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; + + point.x = x; + point.y = y; - status = _cairo_path_fixed_add (path, CAIRO_PATH_OP_LINE_TO, point, 1); + 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; } diff --git a/src/cairo-private.h b/src/cairo-private.h index e125a4b1..a43c8ef7 100644 --- a/src/cairo-private.h +++ b/src/cairo-private.h @@ -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; }; diff --git a/src/cairo.c b/src/cairo.c index 0faddb35..ce25f3f1 100644 --- a/src/cairo.c +++ b/src/cairo.c @@ -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_stroke_preserve (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_stroke_preserve (cairo_t *cr) +{ CAIRO_CHECK_SANITY (cr); if (cr->status) return; - cr->status = _cairo_gstate_stroke (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->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. @@ -1540,13 +1706,44 @@ 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); } /** diff --git a/src/cairo.h b/src/cairo.h index cf70a27b..e6cd720c 100644 --- a/src/cairo.h +++ b/src/cairo.h @@ -466,9 +466,15 @@ 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); void @@ -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 */ /** diff --git a/src/cairoint.h b/src/cairoint.h index d046d45a..324c2f32 100644 --- a/src/cairoint.h +++ b/src/cairoint.h @@ -1042,104 +1042,56 @@ _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 cairo_status_t -_cairo_gstate_move_to (cairo_gstate_t *gstate, double x, double y); - -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 void +_cairo_gstate_user_to_backend (cairo_gstate_t *gstate, double *x, double *y); -cairo_private cairo_status_t -_cairo_gstate_close_path (cairo_gstate_t *gstate); +cairo_private void +_cairo_gstate_backend_to_user (cairo_gstate_t *gstate, double *x, double *y); cairo_private cairo_status_t -_cairo_gstate_get_current_point (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_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); 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) diff --git a/test/fill-and-stroke.c b/test/fill-and-stroke.c index 5e0e4942..98931711 100644 --- a/test/fill-and-stroke.c +++ b/test/fill-and-stroke.c @@ -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); diff --git a/test/get-and-set.c b/test/get-and-set.c index c7d54a67..72d66104 100644 --- a/test/get-and-set.c +++ b/test/get-and-set.c @@ -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 |