static cairo_status_t _cairo_stroker_close_path (void *closure) { cairo_stroker_t *stroker = closure; cairo_status_t status; if (stroker->dash.dashed) status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point); else status = _cairo_stroker_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 */ status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face); if (unlikely (status)) return status; } else { /* Cap the start and end of the sub path as needed */ status = _cairo_stroker_add_caps (stroker); if (unlikely (status)) return status; } stroker->has_initial_sub_path = FALSE; stroker->has_first_face = FALSE; stroker->has_current_face = FALSE; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_stroker_line_to (void *closure, const cairo_point_t *point) { cairo_stroker_t *stroker = closure; cairo_stroke_face_t start, end; cairo_point_t *p1 = &stroker->current_point; cairo_slope_t dev_slope; double slope_dx, slope_dy; cairo_status_t status; stroker->has_initial_sub_path = TRUE; if (p1->x == point->x && p1->y == point->y) return CAIRO_STATUS_SUCCESS; _cairo_slope_init (&dev_slope, p1, point); slope_dx = _cairo_fixed_to_double (point->x - p1->x); slope_dy = _cairo_fixed_to_double (point->y - p1->y); _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL); status = _cairo_stroker_add_sub_edge (stroker, p1, point, &dev_slope, slope_dx, slope_dy, &start, &end); if (unlikely (status)) return status; if (stroker->has_current_face) { /* Join with final face from previous segment */ status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (unlikely (status)) return status; } 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->current_face = end; stroker->has_current_face = TRUE; stroker->current_point = *point; return CAIRO_STATUS_SUCCESS; }
/* * Dashed lines. Cap each dash end, join around turns when on */ static cairo_status_t _cairo_stroker_line_to_dashed (void *closure, const cairo_point_t *p2) { cairo_stroker_t *stroker = closure; double mag, remain, step_length = 0; double slope_dx, slope_dy; double dx2, dy2; cairo_stroke_face_t sub_start, sub_end; cairo_point_t *p1 = &stroker->current_point; cairo_slope_t dev_slope; cairo_line_t segment; cairo_bool_t fully_in_bounds; cairo_status_t status; stroker->has_initial_sub_path = stroker->dash.dash_starts_on; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; fully_in_bounds = TRUE; if (stroker->has_bounds && (! _cairo_box_contains_point (&stroker->bounds, p1) || ! _cairo_box_contains_point (&stroker->bounds, p2))) { fully_in_bounds = FALSE; } _cairo_slope_init (&dev_slope, p1, p2); slope_dx = _cairo_fixed_to_double (p2->x - p1->x); slope_dy = _cairo_fixed_to_double (p2->y - p1->y); if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, &mag)) { return CAIRO_STATUS_SUCCESS; } remain = mag; segment.p1 = *p1; while (remain) { step_length = MIN (stroker->dash.dash_remain, remain); remain -= step_length; dx2 = slope_dx * (mag - remain); dy2 = slope_dy * (mag - remain); cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2); segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x; segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y; if (stroker->dash.dash_on && (fully_in_bounds || (! stroker->has_first_face && stroker->dash.dash_starts_on) || _cairo_box_intersects_line_segment (&stroker->bounds, &segment))) { status = _cairo_stroker_add_sub_edge (stroker, &segment.p1, &segment.p2, &dev_slope, slope_dx, slope_dy, &sub_start, &sub_end); if (unlikely (status)) return status; if (stroker->has_current_face) { /* Join with final face from previous segment */ status = _cairo_stroker_join (stroker, &stroker->current_face, &sub_start); if (unlikely (status)) return status; stroker->has_current_face = FALSE; } else if (! stroker->has_first_face && stroker->dash.dash_starts_on) { /* Save sub path's first face in case needed for closing join */ stroker->first_face = sub_start; stroker->has_first_face = TRUE; } else { /* Cap dash start if not connecting to a previous segment */ status = _cairo_stroker_add_leading_cap (stroker, &sub_start); if (unlikely (status)) return status; } if (remain) { /* Cap dash end if not at end of segment */ status = _cairo_stroker_add_trailing_cap (stroker, &sub_end); if (unlikely (status)) return status; } else { stroker->current_face = sub_end; stroker->has_current_face = TRUE; } } else { if (stroker->has_current_face) { /* Cap final face from previous segment */ status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face); if (unlikely (status)) return status; stroker->has_current_face = FALSE; } } _cairo_stroker_dash_step (&stroker->dash, step_length); segment.p1 = segment.p2; } if (stroker->dash.dash_on && ! stroker->has_current_face) { /* This segment ends on a transition to dash_on, compute a new face * and add cap for the beginning of the next dash_on step. * * Note: this will create a degenerate cap if this is not the last line * in the path. Whether this behaviour is desirable or not is debatable. * On one side these degenerate caps can not be reproduced with regular * path stroking. * On the other hand, Acroread 7 also produces the degenerate caps. */ _compute_face (p2, &dev_slope, slope_dx, slope_dy, stroker, &stroker->current_face); status = _cairo_stroker_add_leading_cap (stroker, &stroker->current_face); if (unlikely (status)) return status; stroker->has_current_face = TRUE; } stroker->current_point = *p2; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_stroker_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { cairo_stroker_t *stroker = closure; cairo_spline_t spline; cairo_line_join_t line_join_save; cairo_stroke_face_t face; double slope_dx, slope_dy; cairo_path_fixed_line_to_func_t *line_to; cairo_status_t status = CAIRO_STATUS_SUCCESS; line_to = stroker->dash.dashed ? _cairo_stroker_line_to_dashed : _cairo_stroker_line_to; if (! _cairo_spline_init (&spline, (cairo_spline_add_point_func_t)line_to, stroker, &stroker->current_point, b, c, d)) { return line_to (closure, d); } /* If the line width is so small that the pen is reduced to a single point, then we have nothing to do. */ if (stroker->pen.num_vertices <= 1) return CAIRO_STATUS_SUCCESS; /* Compute the initial face */ if (! stroker->dash.dashed || stroker->dash.dash_on) { slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); if (_compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (&stroker->current_point, &spline.initial_slope, slope_dx, slope_dy, stroker, &face); } if (stroker->has_current_face) { status = _cairo_stroker_join (stroker, &stroker->current_face, &face); if (unlikely (status)) return status; } else if (! stroker->has_first_face) { stroker->first_face = face; stroker->has_first_face = TRUE; } stroker->current_face = face; stroker->has_current_face = TRUE; } /* Temporarily modify the stroker to use round joins to guarantee * smooth stroked curves. */ line_join_save = stroker->style.line_join; stroker->style.line_join = CAIRO_LINE_JOIN_ROUND; status = _cairo_spline_decompose (&spline, stroker->tolerance); if (unlikely (status)) return status; /* And join the final face */ if (! stroker->dash.dashed || stroker->dash.dash_on) { slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); if (_compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (&stroker->current_point, &spline.final_slope, slope_dx, slope_dy, stroker, &face); } status = _cairo_stroker_join (stroker, &stroker->current_face, &face); if (unlikely (status)) return status; stroker->current_face = face; } stroker->style.line_join = line_join_save; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_stroker_curve_to (void *closure, const cairo_point_t *b, const cairo_point_t *c, const cairo_point_t *d) { cairo_stroker_t *stroker = closure; cairo_pen_stroke_spline_t spline_pen; cairo_stroke_face_t start, end; cairo_point_t extra_points[4]; cairo_point_t *a = &stroker->current_point; double initial_slope_dx, initial_slope_dy; double final_slope_dx, final_slope_dy; cairo_status_t status; status = _cairo_pen_stroke_spline_init (&spline_pen, &stroker->pen, a, b, c, d); if (status == CAIRO_INT_STATUS_DEGENERATE) return _cairo_stroker_line_to (closure, d); else if (unlikely (status)) return status; initial_slope_dx = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dx); initial_slope_dy = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dy); final_slope_dx = _cairo_fixed_to_double (spline_pen.spline.final_slope.dx); final_slope_dy = _cairo_fixed_to_double (spline_pen.spline.final_slope.dy); if (_compute_normalized_device_slope (&initial_slope_dx, &initial_slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (a, &spline_pen.spline.initial_slope, initial_slope_dx, initial_slope_dy, stroker, &start); } if (_compute_normalized_device_slope (&final_slope_dx, &final_slope_dy, stroker->ctm_inverse, NULL)) { _compute_face (d, &spline_pen.spline.final_slope, final_slope_dx, final_slope_dy, stroker, &end); } if (stroker->has_current_face) { status = _cairo_stroker_join (stroker, &stroker->current_face, &start); if (unlikely (status)) goto CLEANUP_PEN; } else if (! stroker->has_first_face) { stroker->first_face = start; stroker->has_first_face = TRUE; } stroker->current_face = end; stroker->has_current_face = TRUE; extra_points[0] = start.cw; extra_points[0].x -= start.point.x; extra_points[0].y -= start.point.y; extra_points[1] = start.ccw; extra_points[1].x -= start.point.x; extra_points[1].y -= start.point.y; extra_points[2] = end.cw; extra_points[2].x -= end.point.x; extra_points[2].y -= end.point.y; extra_points[3] = end.ccw; extra_points[3].x -= end.point.x; extra_points[3].y -= end.point.y; status = _cairo_pen_add_points (&spline_pen.pen, extra_points, 4); if (unlikely (status)) goto CLEANUP_PEN; status = _cairo_pen_stroke_spline (&spline_pen, stroker->tolerance, stroker->traps); CLEANUP_PEN: _cairo_pen_stroke_spline_fini (&spline_pen); stroker->current_point = *d; return status; }