/* * Add an approximation of an arc to the current path. * The current point of the path is the initial point of the arc; * parameters are the final point of the arc * and the point at which the extended tangents meet. * We require that the arc be less than a semicircle. * The arc may go either clockwise or counterclockwise. * The approximation is a very simple one: a single curve * whose other two control points are a fraction F of the way * to the intersection of the tangents, where * F = (4/3)(1 / (1 + sqrt(1+(d/r)^2))) * where r is the radius and d is the distance from either tangent * point to the intersection of the tangents. This produces * a curve whose center point, as well as its ends, lies on * the desired arc. * * Because F has to be computed in user space, we let the client * compute it and pass it in as an argument. */ int gx_path_add_partial_arc_notes(gx_path * ppath, fixed x3, fixed y3, fixed xt, fixed yt, floatp fraction, segment_notes notes) { fixed x0 = ppath->position.x, y0 = ppath->position.y; vd_curveto(x0 + (fixed) ((xt - x0) * fraction), y0 + (fixed) ((yt - y0) * fraction), x3 + (fixed) ((xt - x3) * fraction), y3 + (fixed) ((yt - y3) * fraction), x3, y3); return gx_path_add_curve_notes(ppath, x0 + (fixed) ((xt - x0) * fraction), y0 + (fixed) ((yt - y0) * fraction), x3 + (fixed) ((xt - x3) * fraction), y3 + (fixed) ((yt - y3) * fraction), x3, y3, notes | sn_from_arc); }
static void gx_ttfExport__CurveTo(ttfExport *self, FloatPoint *p0, FloatPoint *p1, FloatPoint *p2) { gx_ttfExport *e = (gx_ttfExport *)self; if (!e->error) { if (e->monotonize) { curve_segment s; s.notes = sn_none; s.p1.x = float2fixed(p0->x), s.p1.y = float2fixed(p0->y), s.p2.x = float2fixed(p1->x), s.p2.y = float2fixed(p1->y), s.pt.x = float2fixed(p2->x), s.pt.y = float2fixed(p2->y); e->error = gx_curve_monotonize(e->path, &s); } else e->error = gx_path_add_curve_notes(e->path, float2fixed(p0->x), float2fixed(p0->y), float2fixed(p1->x), float2fixed(p1->y), float2fixed(p2->x), float2fixed(p2->y), sn_none); } }
/* * Reverse a path. We know ppath != ppath_old. * NOTE: in releases 5.01 and earlier, the implicit line added by closepath * became the first segment of the reversed path. Starting in release * 5.02, the code follows the Adobe implementation (and LanguageLevel 3 * specification), in which this line becomes the *last* segment of the * reversed path. This can produce some quite unintuitive results. * * The order of the subpaths is unspecified in the PLRM, but the CPSI * reverses the subpaths, and the CET (11-05 p6, test 3) tests for it. */ int gx_path_copy_reversed(const gx_path * ppath_old, gx_path * ppath) { const subpath *psub = ppath_old->current_subpath; #ifdef DEBUG if (gs_debug_c('P')) gx_dump_path(ppath_old, "before reversepath"); #endif nsp: if (psub) { const segment *prev = psub->last; const segment *pseg; segment_notes notes = (prev == (const segment *)psub ? sn_none : psub->next->notes); segment_notes prev_notes; int code; if (!psub->is_closed) { code = gx_path_add_point(ppath, prev->pt.x, prev->pt.y); if (code < 0) return code; } /* * The do ... while structure of this loop is artificial, * designed solely to keep compilers from complaining about * 'statement not reached' or 'end-of-loop code not reached'. * The normal exit from this loop is the goto statement in * the s_start arm of the switch. */ do { pseg = prev; prev_notes = notes; prev = pseg->prev; notes = pseg->notes; prev_notes = (prev_notes & sn_not_first) | (notes & ~sn_not_first); switch (pseg->type) { case s_start: /* Finished subpath */ if (psub->is_closed) { code = gx_path_close_subpath_notes(ppath, prev_notes); if (code < 0) return code; } do { psub = (const subpath *)psub->prev; } while (psub && psub->type != s_start); goto nsp; case s_curve: { const curve_segment *pc = (const curve_segment *)pseg; code = gx_path_add_curve_notes(ppath, pc->p2.x, pc->p2.y, pc->p1.x, pc->p1.y, prev->pt.x, prev->pt.y, prev_notes); break; } case s_line: code = gx_path_add_line_notes(ppath, prev->pt.x, prev->pt.y, prev_notes); break; case s_line_close: /* Skip the closing line. */ code = gx_path_add_point(ppath, prev->pt.x, prev->pt.y); break; default: /* not possible */ return_error(gs_error_Fatal); } } while (code >= 0); return code; /* only reached if code < 0 */ } #undef sn_not_end /* * In the Adobe implementations, reversepath discards a trailing * moveto unless the path consists only of a moveto. We reproduce * this behavior here, even though we consider it a bug. */ if (ppath_old->first_subpath == 0 && path_last_is_moveto(ppath_old) ) { int code = gx_path_add_point(ppath, ppath_old->position.x, ppath_old->position.y); if (code < 0) return code; } #ifdef DEBUG if (gs_debug_c('P')) gx_dump_path(ppath, "after reversepath"); #endif return 0; }
/* Internal routine for adding an arc to the path. */ static int arc_add(const arc_curve_params_t * arc, bool is_quadrant) { gx_path *path = arc->ppath; gs_imager_state *pis = arc->pis; double x0 = arc->p0.x, y0 = arc->p0.y; double xt = arc->pt.x, yt = arc->pt.y; floatp fraction; gs_fixed_point p0, p2, p3, pt; int code; if ((arc->action != arc_nothing && #if !PRECISE_CURRENTPOINT (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 #else (code = gs_point_transform2fixed_rounding(&pis->ctm, x0, y0, &p0)) < 0) || (code = gs_point_transform2fixed_rounding(&pis->ctm, xt, yt, &pt)) < 0 || (code = gs_point_transform2fixed_rounding(&pis->ctm, arc->p3.x, arc->p3.y, &p3)) < 0 #endif ) return code; #if PRECISE_CURRENTPOINT if (!path_position_valid(path)) gs_point_transform(arc->p0.x, arc->p0.y, &ctm_only(arc->pis), &pis->subpath_start); #endif code = (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, or lineto with no current point */ gx_path_add_point(path, p0.x, p0.y)); if (code < 0) return code; /* 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); }