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_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&dev_slope, p1, point); compute_face (p1, &dev_slope, stroker, &start); if (stroker->has_current_face) { int clockwise = join_is_clockwise (&stroker->current_face, &start); /* Join with final face from previous segment */ 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; _cairo_tristrip_move_to (stroker->strip, &start.cw); stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; _cairo_tristrip_add_point (stroker->strip, &start.cw); _cairo_tristrip_add_point (stroker->strip, &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; _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.cw); _cairo_tristrip_add_point (stroker->strip, &stroker->current_face.ccw); return CAIRO_STATUS_SUCCESS; }
static void inner_close (struct stroker *stroker, const cairo_stroke_face_t *in, cairo_stroke_face_t *out) { const cairo_point_t *inpt; if (join_is_clockwise (in, out)) { inpt = &out->ccw; } else { 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); }
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_limits) { if (! _cairo_spline_intersects (&stroker->current_face.point, b, c, d, &stroker->limit)) 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; _cairo_tristrip_move_to (stroker->strip, &face.cw); stroker->has_first_face = TRUE; } stroker->has_current_face = TRUE; _cairo_tristrip_add_point (stroker->strip, &face.cw); _cairo_tristrip_add_point (stroker->strip, &face.ccw); } stroker->current_face = face; return _cairo_spline_decompose (&spline, stroker->tolerance); }
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 (tangent->dx == 0 && tangent->dy == 0) { const cairo_point_t *inpt, *outpt; 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; } else { inpt = &stroker->current_face.ccw; outpt = &face.ccw; } add_fan (stroker, &stroker->current_face.dev_vector, &face.dev_vector, &stroker->current_face.point, inpt, outpt, clockwise); } 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 < 0) { const cairo_point_t *inpt, *outpt; 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; } else { inpt = &stroker->current_face.ccw; outpt = &face.ccw; } add_fan (stroker, &stroker->current_face.dev_vector, &face.dev_vector, &stroker->current_face.point, inpt, outpt, clockwise); } _cairo_tristrip_add_point (stroker->strip, &face.cw); _cairo_tristrip_add_point (stroker->strip, &face.ccw); } stroker->current_face = face; return CAIRO_STATUS_SUCCESS; }
static void outer_close (struct stroker *stroker, const cairo_stroke_face_t *in, const cairo_stroke_face_t *out) { const cairo_point_t *inpt, *outpt; int clockwise; 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; } clockwise = join_is_clockwise (in, out); if (clockwise) { inpt = &in->cw; outpt = &out->cw; } else { inpt = &in->ccw; outpt = &out->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, inpt, outpt, clockwise); break; case CAIRO_LINE_JOIN_MITER: default: { /* dot product of incoming slope vector with outgoing slope vector */ double in_dot_out = -in->usr_vector.x * out->usr_vector.x + -in->usr_vector.y * out->usr_vector.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->usr_vector.x; dy1 = in->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1); /* outer point of outgoing line face */ x2 = _cairo_fixed_to_double (outpt->x); y2 = _cairo_fixed_to_double (outpt->y); dx2 = out->usr_vector.x; dy2 = out->usr_vector.y; cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); /* * 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; //*_cairo_contour_first_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 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; }