static cairo_status_t close_path (void *closure) { struct stroker *stroker = closure; cairo_status_t status; status = line_to (stroker, &stroker->first_point); if (unlikely (status)) return status; if (stroker->has_first_face && stroker->has_current_face) { /* Join first and final faces of sub path */ outer_close (stroker, &stroker->current_face, &stroker->first_face); inner_close (stroker, &stroker->current_face, &stroker->first_face); #if 0 *_cairo_contour_first_point (&stroker->ccw.contour) = *_cairo_contour_last_point (&stroker->ccw.contour); *_cairo_contour_first_point (&stroker->cw.contour) = *_cairo_contour_last_point (&stroker->cw.contour); #endif _cairo_polygon_add_contour (stroker->polygon, &stroker->cw.contour); _cairo_polygon_add_contour (stroker->polygon, &stroker->ccw.contour); #if DEBUG { FILE *file = fopen ("contours.txt", "a"); _cairo_debug_print_contour (file, &stroker->path); _cairo_debug_print_contour (file, &stroker->cw.contour); _cairo_debug_print_contour (file, &stroker->ccw.contour); fclose (file); _cairo_contour_reset (&stroker->path); } #endif _cairo_contour_reset (&stroker->cw.contour); _cairo_contour_reset (&stroker->ccw.contour); } else { /* Cap the start and end of the sub path as needed */ add_caps (stroker); } stroker->has_initial_sub_path = FALSE; stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; return CAIRO_STATUS_SUCCESS; }
static void contour_add_point (struct stroker *stroker, struct stroke_contour *c, const cairo_point_t *point) { if (! within_tolerance (point, _cairo_contour_last_point (&c->contour), stroker->contour_tolerance)) _cairo_contour_add_point (&c->contour, point); //*_cairo_contour_last_point (&c->contour) = *point; }
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 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; int move_last = 0; 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 == 0) { move_last = 1; } else { 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; if (move_last) { *_cairo_contour_last_point (&stroker->cw.contour) = stroker->current_face.cw; *_cairo_contour_last_point (&stroker->ccw.contour) = stroker->current_face.ccw; } else { contour_add_point (stroker, &stroker->cw, &stroker->current_face.cw); contour_add_point (stroker, &stroker->ccw, &stroker->current_face.ccw); } return CAIRO_STATUS_SUCCESS; }