static cairo_status_t line_to (void *closure, const cairo_point_t *point) { struct stroker *stroker = closure; cairo_stroke_face_t start; cairo_point_t *p1 = &stroker->current_face.point; cairo_slope_t dev_slope; stroker->has_initial_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; #if DEBUG _cairo_contour_add_point (&stroker->path, point); #endif _cairo_slope_init (&dev_slope, p1, point); compute_face (p1, &dev_slope, stroker, &start); if (stroker->has_current_face) { int clockwise = _cairo_slope_compare (&stroker->current_face.dev_vector, &start.dev_vector); if (clockwise) { clockwise = clockwise < 0; /* Join with final face from previous segment */ if (! within_tolerance (&stroker->current_face.ccw, &start.ccw, stroker->contour_tolerance) || ! within_tolerance (&stroker->current_face.cw, &start.cw, stroker->contour_tolerance)) { outer_join (stroker, &stroker->current_face, &start, clockwise); inner_join (stroker, &stroker->current_face, &start, clockwise); } } } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; contour_add_point (stroker, &stroker->cw, &start.cw); contour_add_point (stroker, &stroker->ccw, &start.ccw); } stroker->current_face = start; stroker->current_face.point = *point; stroker->current_face.ccw.x += dev_slope.dx; stroker->current_face.ccw.y += dev_slope.dy; stroker->current_face.cw.x += dev_slope.dx; stroker->current_face.cw.y += dev_slope.dy; contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); return CAIRO_STATUS_SUCCESS; }
static void add_cap (struct stroker *stroker, const cairo_stroke_face_t *f, struct stroke_contour *c) { switch (stroker->style.line_cap) { case CAIRO_LINE_CAP_ROUND: { cairo_slope_t slope; slope.dx = -f->dev_vector.dx; slope.dy = -f->dev_vector.dy; add_fan (stroker, &f->dev_vector, &slope, &f->point, &f->ccw, &f->cw, FALSE, c); break; } case CAIRO_LINE_CAP_SQUARE: { double dx, dy; cairo_slope_t fvector; cairo_point_t quad[4]; dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= stroker->style.line_width / 2.0; dy *= stroker->style.line_width / 2.0; cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); quad[0] = f->ccw; quad[1].x = f->ccw.x + fvector.dx; quad[1].y = f->ccw.y + fvector.dy; quad[2].x = f->cw.x + fvector.dx; quad[2].y = f->cw.y + fvector.dy; quad[3] = f->cw; contour_add_point (stroker, c, &quad[1]); contour_add_point (stroker, c, &quad[2]); } case CAIRO_LINE_CAP_BUTT: default: break; } contour_add_point (stroker, c, &f->cw); }
/* * Construct a fan around the midpoint using the vertices from pen between * inpt and outpt. */ static void add_fan (struct stroker *stroker, const cairo_slope_t *in_vector, const cairo_slope_t *out_vector, const cairo_point_t *midpt, cairo_bool_t clockwise, struct stroke_contour *c) { cairo_pen_t *pen = &stroker->pen; int start, stop; if (stroker->has_bounds && ! _cairo_box_contains_point (&stroker->bounds, midpt)) return; assert (stroker->pen.num_vertices); if (clockwise) { _cairo_pen_find_active_cw_vertices (pen, in_vector, out_vector, &start, &stop); while (start != stop) { cairo_point_t p = *midpt; translate_point (&p, &pen->vertices[start].point); contour_add_point (stroker, c, &p); if (++start == pen->num_vertices) start = 0; } } else { _cairo_pen_find_active_ccw_vertices (pen, in_vector, out_vector, &start, &stop); while (start != stop) { cairo_point_t p = *midpt; translate_point (&p, &pen->vertices[start].point); contour_add_point (stroker, c, &p); if (start-- == 0) start += pen->num_vertices; } } }
static cairo_status_t curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { struct stroker *stroker = closure; cairo_spline_t spline; cairo_stroke_face_t face; if (stroker->has_bounds && ! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, &stroker->bounds)) return line_to (closure, d); if (! _cairo_spline_init (&spline, spline_to, stroker, &stroker->current_face.point, b, c, d)) return line_to (closure, d); compute_face (&stroker->current_face.point, &spline.initial_slope, stroker, &face); if (stroker->has_current_face) { int clockwise = join_is_clockwise (&stroker->current_face, &face); /* Join with final face from previous segment */ outer_join (stroker, &stroker->current_face, &face, clockwise); inner_join (stroker, &stroker->current_face, &face, clockwise); } else { if (! stroker->has_first_face) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = face; stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; contour_add_point (stroker, &stroker->cw, &face.cw); contour_add_point (stroker, &stroker->ccw, &face.ccw); } stroker->current_face = face; return _cairo_spline_decompose (&spline, stroker->tolerance); }
static void outer_join (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out, int clockwise) { const cairo_point_t *inpt, *outpt; struct stroke_contour *outer; if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y) { return; } if (clockwise) { inpt = &in->cw; outpt = &out->cw; outer = &stroker->cw; } else { inpt = &in->ccw; outpt = &out->ccw; outer = &stroker->ccw; } switch (stroker->style.line_join) { case CAIRO_LINE_JOIN_ROUND: /* construct a fan around the common midpoint */ add_fan (stroker, &in->dev_vector, &out->dev_vector, &in->point, clockwise, outer); break; case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = in->dev_slope.x * out->dev_slope.x + in->dev_slope.y * out->dev_slope.y; double ml = stroker->style.miter_limit; /* Check the miter limit -- lines meeting at an acute angle * can generate long miters, the limit converts them to bevel * * Consider the miter join formed when two line segments * meet at an angle psi: * * /.\ * /. .\ * /./ \.\ * /./psi\.\ * * We can zoom in on the right half of that to see: * * |\ * | \ psi/2 * | \ * | \ * | \ * | \ * miter \ * length \ * | \ * | .\ * | . \ * |. line \ * \ width \ * \ \ * * * The right triangle in that figure, (the line-width side is * shown faintly with three '.' characters), gives us the * following expression relating miter length, angle and line * width: * * 1 /sin (psi/2) = miter_length / line_width * * The right-hand side of this relationship is the same ratio * in which the miter limit (ml) is expressed. We want to know * when the miter length is within the miter limit. That is * when the following condition holds: * * 1/sin(psi/2) <= ml * 1 <= ml sin(psi/2) * 1 <= ml² sin²(psi/2) * 2 <= ml² 2 sin²(psi/2) * 2·sin²(psi/2) = 1-cos(psi) * 2 <= ml² (1-cos(psi)) * * in · out = |in| |out| cos (psi) * * in and out are both unit vectors, so: * * in · out = cos (psi) * * 2 <= ml² (1 - in · out) * */ if (2 <= ml * ml * (1 + in_dot_out)) { double x1, y1, x2, y2; double mx, my; double dx1, dx2, dy1, dy2; double ix, iy; double fdx1, fdy1, fdx2, fdy2; double mdx, mdy; /* * we've got the points already transformed to device * space, but need to do some computation with them and * also need to transform the slope from user space to * device space */ /* outer point of incoming line face */ x1 = _cairo_fixed_to_double (inpt->x); y1 = _cairo_fixed_to_double (inpt->y); dx1 = in->dev_slope.x; dy1 = in->dev_slope.y; /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->dev_slope.x; dy2 = out->dev_slope.y; /* * Compute the location of the outer corner of the miter. * That's pretty easy -- just the intersection of the two * outer edges. We've got slopes and points on each * of those edges. Compute my directly, then compute * mx by using the edge with the larger dy; that avoids * dividing by values close to zero. */ my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / (dx1 * dy2 - dx2 * dy1)); if (fabs (dy1) >= fabs (dy2)) mx = (my - y1) * dx1 / dy1 + x1; else mx = (my - y2) * dx2 / dy2 + x2; /* * When the two outer edges are nearly parallel, slight * perturbations in the position of the outer points of the lines * caused by representing them in fixed point form can cause the * intersection point of the miter to move a large amount. If * that moves the miter intersection from between the two faces, * then draw a bevel instead. */ ix = _cairo_fixed_to_double (in->point.x); iy = _cairo_fixed_to_double (in->point.y); /* slope of one face */ fdx1 = x1 - ix; fdy1 = y1 - iy; /* slope of the other face */ fdx2 = x2 - ix; fdy2 = y2 - iy; /* slope from the intersection to the miter point */ mdx = mx - ix; mdy = my - iy; /* * Make sure the miter point line lies between the two * faces by comparing the slopes */ if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) != slope_compare_sgn (fdx2, fdy2, mdx, mdy)) { cairo_point_t p; p.x = _cairo_fixed_from_double (mx); p.y = _cairo_fixed_from_double (my); *_cairo_contour_last_point (&outer->contour) = p; return; } } break; } case CAIRO_LINE_JOIN_BEVEL: break; } contour_add_point (stroker,outer, outpt); }
static void inner_close (struct stroker *stroker, const cairo_stroke_face_t *in, cairo_stroke_face_t *out) { #if 0 cairo_point_t last; const cairo_point_t *p, *outpt, *inpt; struct stroke_contour *inner; struct _cairo_contour_chain *chain; /* XXX line segments shorter than line-width */ if (join_is_clockwise (in, out)) { inner = &stroker->ccw; outpt = &in->ccw; inpt = &out->ccw; } else { inner = &stroker->cw; outpt = &in->cw; inpt = &out->cw; } if (inner->contour.chain.num_points == 0) { contour_add_point (stroker, inner, &in->point); contour_add_point (stroker, inner, inpt); *_cairo_contour_first_point (&inner->contour) = *_cairo_contour_last_point (&inner->contour); return; } line_width = stroker->style.line_width/2; line_width *= CAIRO_FIXED_ONE; d_last = sign * distance_from_face (out, outpt); last = *outpt; for (chain = &inner->contour.chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { p = &chain->points[i]; if ((d_p = sign * distance_from_face (in, p)) >= line_width && distance_from_edge (stroker, inpt, &last, p) < line_width) { goto out; } if (p->x != last.x || p->y != last.y) { last = *p; d_last = d_p; } } } out: if (d_p != d_last) { double dot = (line_width - d_last) / (d_p - d_last); last.x += dot * (p->x - last.x); last.y += dot * (p->y - last.y); } *_cairo_contour_last_point (&inner->contour) = last; for (chain = &inner->contour.chain; chain; chain = chain->next) { for (i = 0; i < chain->num_points; i++) { cairo_point_t *pp = &chain->points[i]; if (pp == p) return; *pp = last; } } #else const cairo_point_t *inpt; struct stroke_contour *inner; if (join_is_clockwise (in, out)) { inner = &stroker->ccw; inpt = &out->ccw; } else { inner = &stroker->cw; inpt = &out->cw; } contour_add_point (stroker, inner, &in->point); contour_add_point (stroker, inner, inpt); *_cairo_contour_first_point (&inner->contour) = *_cairo_contour_last_point (&inner->contour); #endif }
static void inner_join (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out, int clockwise) { #if 0 cairo_point_t last; const cairo_point_t *p, *outpt; struct stroke_contour *inner; cairo_int64_t d_p, d_last; cairo_int64_t half_line_width; cairo_bool_t negate; /* XXX line segments shorter than line-width */ if (clockwise) { inner = &stroker->ccw; outpt = &out->ccw; negate = 1; } else { inner = &stroker->cw; outpt = &out->cw; negate = 0; } half_line_width = CAIRO_FIXED_ONE*CAIRO_FIXED_ONE/2 * stroker->style.line_width * out->length + .5; /* On the inside, the previous end-point is always * closer to the new face by definition. */ last = *_cairo_contour_last_point (&inner->contour); d_last = distance_from_face (out, &last, negate); _cairo_contour_remove_last_point (&inner->contour); prev: if (inner->contour.chain.num_points == 0) { contour_add_point (stroker, inner, outpt); return; } p = _cairo_contour_last_point (&inner->contour); d_p = distance_from_face (out, p, negate); if (_cairo_int64_lt (d_p, half_line_width) && !_cairo_int64_negative (distance_along_face (out, p))) { last = *p; d_last = d_p; _cairo_contour_remove_last_point (&inner->contour); goto prev; } compute_inner_joint (&last, d_last, p, d_p, half_line_width); contour_add_point (stroker, inner, &last); #else const cairo_point_t *outpt; struct stroke_contour *inner; if (clockwise) { inner = &stroker->ccw; outpt = &out->ccw; } else { inner = &stroker->cw; outpt = &out->cw; } contour_add_point (stroker, inner, &in->point); contour_add_point (stroker, inner, outpt); #endif }
static cairo_status_t spline_to (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent) { struct stroker *stroker = closure; cairo_stroke_face_t face; #if DEBUG _cairo_contour_add_point (&stroker->path, point); #endif if ((tangent->dx | tangent->dy) == 0) { const cairo_point_t *inpt, *outpt; struct stroke_contour *outer; cairo_point_t t; int clockwise; face = stroker->current_face; face.usr_vector.x = -face.usr_vector.x; face.usr_vector.y = -face.usr_vector.y; face.dev_vector.dx = -face.dev_vector.dx; face.dev_vector.dy = -face.dev_vector.dy; t = face.cw; face.cw = face.ccw; face.ccw = t; clockwise = join_is_clockwise (&stroker->current_face, &face); if (clockwise) { inpt = &stroker->current_face.cw; outpt = &face.cw; outer = &stroker->cw; } else { inpt = &stroker->current_face.ccw; outpt = &face.ccw; outer = &stroker->ccw; } add_fan (stroker, &stroker->current_face.dev_vector, &face.dev_vector, &stroker->current_face.point, clockwise, outer); } else { compute_face (point, tangent, stroker, &face); if ((face.dev_slope.x * stroker->current_face.dev_slope.x + face.dev_slope.y * stroker->current_face.dev_slope.y) < stroker->spline_cusp_tolerance) { const cairo_point_t *inpt, *outpt; struct stroke_contour *outer; int clockwise = join_is_clockwise (&stroker->current_face, &face); stroker->current_face.cw.x += face.point.x - stroker->current_face.point.x; stroker->current_face.cw.y += face.point.y - stroker->current_face.point.y; contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); stroker->current_face.ccw.x += face.point.x - stroker->current_face.point.x; stroker->current_face.ccw.y += face.point.y - stroker->current_face.point.y; contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); if (clockwise) { inpt = &stroker->current_face.cw; outpt = &face.cw; outer = &stroker->cw; } else { inpt = &stroker->current_face.ccw; outpt = &face.ccw; outer = &stroker->ccw; } add_fan (stroker, &stroker->current_face.dev_vector, &face.dev_vector, &stroker->current_face.point, clockwise, outer); } contour_add_point (stroker, &stroker->cw, &face.cw); contour_add_point (stroker, &stroker->ccw, &face.ccw); } stroker->current_face = face; return CAIRO_STATUS_SUCCESS; }
/* * Construct a fan around the midpoint using the vertices from pen between * inpt and outpt. */ static void add_fan (struct stroker *stroker, const cairo_slope_t *in_vector, const cairo_slope_t *out_vector, const cairo_point_t *midpt, const cairo_point_t *inpt, const cairo_point_t *outpt, cairo_bool_t clockwise, struct stroke_contour *c) { int start, stop, step, i, npoints; assert (stroker->pen.num_vertices); if (clockwise) { step = 1; start = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, in_vector); if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_cw, in_vector) < 0) start = range_step (start, 1, stroker->pen.num_vertices); stop = _cairo_pen_find_active_cw_vertex_index (&stroker->pen, out_vector); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, out_vector) > 0) { stop = range_step (stop, -1, stroker->pen.num_vertices); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, in_vector) < 0) return; } npoints = stop - start; } else { step = -1; start = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, in_vector); if (_cairo_slope_compare (&stroker->pen.vertices[start].slope_ccw, in_vector) < 0) start = range_step (start, -1, stroker->pen.num_vertices); stop = _cairo_pen_find_active_ccw_vertex_index (&stroker->pen, out_vector); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_cw, out_vector) > 0) { stop = range_step (stop, 1, stroker->pen.num_vertices); if (_cairo_slope_compare (&stroker->pen.vertices[stop].slope_ccw, in_vector) < 0) return; } npoints = start - stop; } stop = range_step (stop, step, stroker->pen.num_vertices); if (npoints < 0) npoints += stroker->pen.num_vertices; if (npoints <= 1) return; for (i = start; i != stop; i = range_step (i, step, stroker->pen.num_vertices)) { cairo_point_t p = *midpt; translate_point (&p, &stroker->pen.vertices[i].point); contour_add_point (stroker, c, &p); } }
static void add_cap (struct stroker *stroker, const cairo_stroke_face_t *f, struct stroke_contour *c) { //TODO DONE add LINE_CAP_TRIANGULAR switch (stroker->style.line_cap) { case CAIRO_LINE_CAP_ROUND: { cairo_slope_t slope; slope.dx = -f->dev_vector.dx; slope.dy = -f->dev_vector.dy; add_fan (stroker, &f->dev_vector, &slope, &f->point, FALSE, c); break; } case CAIRO_LINE_CAP_SQUARE: { cairo_slope_t fvector; cairo_point_t p; double dx, dy; dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= stroker->half_line_width; dy *= stroker->half_line_width; cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); p.x = f->ccw.x + fvector.dx; p.y = f->ccw.y + fvector.dy; contour_add_point (stroker, c, &p); p.x = f->cw.x + fvector.dx; p.y = f->cw.y + fvector.dy; contour_add_point (stroker, c, &p); break; } case CAIRO_LINE_CAP_TRIANGULAR: { cairo_slope_t fvector; cairo_point_t p; double dx, dy; printf("cairo-path-stroke-polygon.c clear\n"); dx = f->usr_vector.x; dy = f->usr_vector.y; dx *= stroker->half_line_width; dy *= stroker->half_line_width; cairo_matrix_transform_distance (stroker->ctm, &dx, &dy); fvector.dx = _cairo_fixed_from_double (dx); fvector.dy = _cairo_fixed_from_double (dy); p.x = (f->ccw.x + f->cw.x) / 2 + fvector.dx; p.y = (f->ccw.y + f->cw.y) / 2 + fvector.dy; contour_add_point (stroker, c, &p); break; } case CAIRO_LINE_CAP_BUTT: default: break; } contour_add_point (stroker, c, &f->cw); }