summaryrefslogtreecommitdiff
path: root/gs/src/gspath1.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/src/gspath1.c')
-rw-r--r--gs/src/gspath1.c232
1 files changed, 170 insertions, 62 deletions
diff --git a/gs/src/gspath1.c b/gs/src/gspath1.c
index a23929d83..1d7fb3baf 100644
--- a/gs/src/gspath1.c
+++ b/gs/src/gspath1.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1989, 1995, 1996, 1997, 1998 Aladdin Enterprises. All rights reserved.
+/* Copyright (C) 1989, 1995, 1996, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
This file is part of Aladdin Ghostscript.
@@ -53,10 +53,15 @@ typedef struct arc_curve_params_s {
gs_point p0, p3, pt;
gs_sincos_t sincos; /* (not used by arc_add) */
fixed angle; /* (not used by arc_add) */
+ int fast_quadrant; /* 0 = not calculated, -1 = not fast, */
+ /* 1 = fast (only used for quadrants) */
+ /* The following are set once iff fast_quadrant > 0. */
+ fixed scaled_radius; /* radius * CTM scale */
+ fixed quadrant_delta; /* scaled_radius * quarter_arc_fraction */
} arc_curve_params_t;
/* Forward declarations */
-private int arc_add(P1(const arc_curve_params_t *));
+private int arc_add(P2(const arc_curve_params_t *arc, bool is_quadrant));
int
gs_arc(gs_state * pgs,
@@ -84,34 +89,83 @@ gs_arc_add(gs_state * pgs, bool clockwise, floatp axc, floatp ayc,
private int
next_arc_curve(arc_curve_params_t * arc, fixed anext)
{
- bool ortho = arc->sincos.orthogonal;
- double sin0 = arc->sincos.sin, cos0 = arc->sincos.cos;
double x0 = arc->p0.x = arc->p3.x;
double y0 = arc->p0.y = arc->p3.y;
- double x3, y3;
-
- gs_sincos_degrees(fixed2float(anext), &arc->sincos);
- arc->p3.x = x3 =
- arc->center.x + arc->radius * arc->sincos.cos;
- arc->p3.y = y3 =
- arc->center.y + arc->radius * arc->sincos.sin;
- if (ortho && arc->sincos.orthogonal) {
- /* The common tangent point is easy to compute. */
- if (x0 == arc->center.x)
- arc->pt.x = x3, arc->pt.y = y0;
- else
- arc->pt.x = x0, arc->pt.y = y3;
- } else {
- /* Do it the hard way. */
- double trad = arc->radius *
+ double trad = arc->radius *
tan(fixed2float(anext - arc->angle) *
(degrees_to_radians / 2));
- arc->pt.x = x0 - trad * sin0;
- arc->pt.y = y0 + trad * cos0;
+ arc->pt.x = x0 - trad * arc->sincos.sin;
+ arc->pt.y = y0 + trad * arc->sincos.cos;
+ gs_sincos_degrees(fixed2float(anext), &arc->sincos);
+ arc->p3.x = arc->center.x + arc->radius * arc->sincos.cos;
+ arc->p3.y = arc->center.y + arc->radius * arc->sincos.sin;
+ arc->angle = anext;
+ return arc_add(arc, false);
+}
+/*
+ * Use this when both arc.angle and anext are multiples of 90 degrees,
+ * and anext = arc.angle +/- 90.
+ */
+private int
+next_arc_quadrant(arc_curve_params_t * arc, fixed anext)
+{
+ double x0 = arc->p0.x = arc->p3.x;
+ double y0 = arc->p0.y = arc->p3.y;
+
+ if (!arc->fast_quadrant) {
+ /*
+ * If the CTM is well-behaved, we can pre-calculate the delta
+ * from the arc points to the control points.
+ */
+ const gs_imager_state *pis = arc->pis;
+ double scale;
+
+ if (is_fzero2(pis->ctm.xy, pis->ctm.yx) ?
+ (scale = fabs(pis->ctm.xx)) == fabs(pis->ctm.yy) :
+ is_fzero2(pis->ctm.xx, pis->ctm.yy) ?
+ (scale = fabs(pis->ctm.xy)) == fabs(pis->ctm.yx) :
+ 0
+ ) {
+ double scaled_radius = arc->radius * scale;
+
+ arc->scaled_radius = float2fixed(scaled_radius);
+ arc->quadrant_delta =
+ float2fixed(scaled_radius * quarter_arc_fraction);
+ arc->fast_quadrant = 1;
+ } else {
+ arc->fast_quadrant = -1;
+ }
}
+ /*
+ * We know that anext is a multiple of 90 (as a fixed); we want
+ * (anext / 90) & 3. The following is much faster than a division.
+ */
+ switch ((fixed2int(anext) >> 1) & 3) {
+ case 0:
+ arc->sincos.sin = 0, arc->sincos.cos = 1;
+ arc->p3.x = x0 = arc->center.x + arc->radius;
+ arc->p3.y = arc->center.y;
+ break;
+ case 1:
+ arc->sincos.sin = 1, arc->sincos.cos = 0;
+ arc->p3.x = arc->center.x;
+ arc->p3.y = y0 = arc->center.y + arc->radius;
+ break;
+ case 2:
+ arc->sincos.sin = 0, arc->sincos.cos = -1;
+ arc->p3.x = x0 = arc->center.x - arc->radius;
+ arc->p3.y = arc->center.y;
+ break;
+ case 3:
+ arc->sincos.sin = -1, arc->sincos.cos = 0;
+ arc->p3.x = arc->center.x;
+ arc->p3.y = y0 = arc->center.y - arc->radius;
+ break;
+ }
+ arc->pt.x = x0, arc->pt.y = y0;
arc->angle = anext;
- return arc_add(arc);
+ return arc_add(arc, true);
}
int
@@ -140,6 +194,7 @@ gs_imager_arc_add(gx_path * ppath, gs_imager_state * pis, bool clockwise,
arc.radius = ar;
arc.action = (add_line ? arc_lineto : arc_moveto);
arc.notes = sn_none;
+ arc.fast_quadrant = 0;
ang1r = fixed2float(ang1 % fixed_360);
gs_sincos_degrees(ang1r, &arc.sincos);
arc.p3.x = axc + ar * arc.sincos.cos;
@@ -148,46 +203,69 @@ gs_imager_arc_add(gx_path * ppath, gs_imager_state * pis, bool clockwise,
while (ang1 < ang2)
ang2 -= fixed_360;
if (ang2 < 0) {
- fixed adjust = round_up(-ang2, fixed_360);
+ fixed adjust = ROUND_UP(-ang2, fixed_360);
ang1 += adjust, ang2 += adjust;
}
arc.angle = ang1;
- /*
- * Cut at multiples of 90 degrees. Invariant: ang1 >= ang2 >= 0.
- */
- while ((anext = round_down(arc.angle - fixed_epsilon, fixed_90)) > ang2) {
+ if (ang1 == ang2)
+ goto last;
+ /* Do the first part, up to a multiple of 90 degrees. */
+ if (!arc.sincos.orthogonal) {
+ anext = ROUND_DOWN(arc.angle - fixed_epsilon, fixed_90);
+ if (anext < ang2)
+ goto last;
code = next_arc_curve(&arc, anext);
if (code < 0)
return code;
arc.action = arc_nothing;
arc.notes = sn_not_first;
+ }
+ /* Do multiples of 90 degrees. Invariant: ang1 >= ang2 >= 0. */
+ while ((anext = arc.angle - fixed_90) >= ang2) {
+ code = next_arc_quadrant(&arc, anext);
+ if (code < 0)
+ return code;
+ arc.action = arc_nothing;
+ arc.notes = sn_not_first;
}
} else {
while (ang2 < ang1)
ang2 += fixed_360;
if (ang1 < 0) {
- fixed adjust = round_up(-ang1, fixed_360);
+ fixed adjust = ROUND_UP(-ang1, fixed_360);
ang1 += adjust, ang2 += adjust;
}
arc.angle = ang1;
- /*
- * Cut at multiples of 90 degrees. Invariant: 0 <= ang1 <= ang2.
- * We can't use round_up because of the inchoate definition of
- * % and / for negative numbers.
- */
- while ((anext = round_up(arc.angle + fixed_epsilon, fixed_90)) < ang2) {
+ if (ang1 == ang2)
+ return next_arc_curve(&arc, ang2);
+ /* Do the first part, up to a multiple of 90 degrees. */
+ if (!arc.sincos.orthogonal) {
+ anext = ROUND_UP(arc.angle + fixed_epsilon, fixed_90);
+ if (anext > ang2)
+ goto last;
code = next_arc_curve(&arc, anext);
if (code < 0)
return code;
arc.action = arc_nothing;
arc.notes = sn_not_first;
+ }
+ /* Do multiples of 90 degrees. Invariant: 0 <= ang1 <= ang2. */
+ while ((anext = arc.angle + fixed_90) <= ang2) {
+ code = next_arc_quadrant(&arc, anext);
+ if (code < 0)
+ return code;
+ arc.action = arc_nothing;
+ arc.notes = sn_not_first;
}
}
/*
- * Do the last curve of the arc.
+ * Do the last curve of the arc, if any.
*/
+ if (arc.angle == ang2)
+ return 0;
+last:
return next_arc_curve(&arc, ang2);
}
@@ -244,7 +322,7 @@ floatp ax1, floatp ay1, floatp ax2, floatp ay2, floatp arad, float retxy[4])
arc.p3.y = yt2 = ay1 + dy2 * l2;
arc.pt.x = ax1;
arc.pt.y = ay1;
- code = arc_add(&arc);
+ code = arc_add(&arc, false);
}
}
if (retxy != 0) {
@@ -258,45 +336,75 @@ floatp ax1, floatp ay1, floatp ax2, floatp ay2, floatp arad, float retxy[4])
/* Internal routine for adding an arc to the path. */
private int
-arc_add(const arc_curve_params_t * arc)
+arc_add(const arc_curve_params_t * arc, bool is_quadrant)
{
gx_path *path = arc->ppath;
gs_imager_state *pis = arc->pis;
- double r = arc->radius;
double x0 = arc->p0.x, y0 = arc->p0.y;
- double x3 = arc->p3.x, y3 = arc->p3.y;
double xt = arc->pt.x, yt = arc->pt.y;
- floatp dx = xt - x0, dy = yt - y0;
- double dist = dx * dx + dy * dy;
- double r2 = r * r;
floatp fraction;
- gs_fixed_point p0, p3, pt, cpt;
+ gs_fixed_point p0, p2, p3, pt;
int code;
- /* Compute the fraction coefficient for the curve. */
- /* See gx_path_add_partial_arc for details. */
- if (dist >= r2 * 1.0e8) /* almost zero radius; */
- /* the >= catches dist == r == 0 */
- fraction = 0.0;
- else
- fraction = (4.0 / 3.0) / (1 + sqrt(1 + dist / r2));
- if_debug8('r',
- "[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) action=%d\n",
- fraction, x0, y0, xt, yt, x3, y3, (int)arc->action);
- if ((code = gs_point_transform2fixed(&pis->ctm, x0, y0, &p0)) < 0 ||
- (code = gs_point_transform2fixed(&pis->ctm, x3, y3, &p3)) < 0 ||
+ if ((arc->action != arc_nothing &&
+ (code = gs_point_transform2fixed(&pis->ctm, x0, y0, &p0)) < 0) ||
(code = gs_point_transform2fixed(&pis->ctm, xt, yt, &pt)) < 0 ||
+ (code = gs_point_transform2fixed(&pis->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 ||
(code =
- (arc->action == arc_nothing ? 0 :
- arc->action == arc_lineto &&
- gx_path_current_point(path, &cpt) >= 0 ?
+ (arc->action == arc_nothing ?
+ (p0.x = path->position.x, p0.y = path->position.y, 0) :
+ arc->action == arc_lineto && path_position_valid(path) ?
gx_path_add_line(path, p0.x, p0.y) :
- /* action == arc_moveto */
+ /* action == arc_moveto, or lineto with no current point */
gx_path_add_point(path, p0.x, p0.y))) < 0
)
return code;
- return gx_path_add_partial_arc_notes(path, p3.x, p3.y, pt.x, pt.y,
- fraction, arc->notes);
+ /* Compute the fraction coefficient for the curve. */
+ /* See gx_path_add_partial_arc for details. */
+ if (is_quadrant) {
+ /* one of |dx| and |dy| is r, the other is zero */
+ fraction = quarter_arc_fraction;
+ if (arc->fast_quadrant > 0) {
+ /*
+ * The CTM is well-behaved, and we have pre-calculated the delta
+ * from the circumference points to the control points.
+ */
+ fixed delta = arc->quadrant_delta;
+
+ if (pt.x != p0.x)
+ p0.x = (pt.x > p0.x ? p0.x + delta : p0.x - delta);
+ if (pt.y != p0.y)
+ p0.y = (pt.y > p0.y ? p0.y + delta : p0.y - delta);
+ p2.x = (pt.x == p3.x ? p3.x :
+ pt.x > p3.x ? p3.x + delta : p3.x - delta);
+ p2.y = (pt.y == p3.y ? p3.y :
+ pt.y > p3.y ? p3.y + delta : p3.y - delta);
+ goto add;
+ }
+ } else {
+ double r = arc->radius;
+ floatp dx = xt - x0, dy = yt - y0;
+ double dist = dx * dx + dy * dy;
+ double r2 = r * r;
+
+ if (dist >= r2 * 1.0e8) /* almost zero radius; */
+ /* the >= catches dist == r == 0 */
+ fraction = 0.0;
+ else
+ fraction = (4.0 / 3.0) / (1 + sqrt(1 + dist / r2));
+ }
+ p0.x += (fixed)((pt.x - p0.x) * fraction);
+ p0.y += (fixed)((pt.y - p0.y) * fraction);
+ p2.x = p3.x + (fixed)((pt.x - p3.x) * fraction);
+ p2.y = p3.y + (fixed)((pt.y - p3.y) * fraction);
+add:
+ if_debug8('r',
+ "[r]Arc f=%f p0=(%f,%f) pt=(%f,%f) p3=(%f,%f) action=%d\n",
+ fraction, x0, y0, xt, yt, arc->p3.x, arc->p3.y,
+ (int)arc->action);
+ /* Open-code gx_path_add_partial_arc_notes */
+ return gx_path_add_curve_notes(path, p0.x, p0.y, p2.x, p2.y, p3.x, p3.y,
+ arc->notes | sn_from_arc);
}
/* ------ Path transformers ------ */