static cairo_status_t _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, const cairo_point_t *p1, const cairo_point_t *p2, cairo_slope_t *dev_slope, double slope_dx, double slope_dy, cairo_stroke_face_t *start, cairo_stroke_face_t *end) { cairo_point_t rectangle[4]; _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start); /* XXX: This could be optimized slightly by not calling _compute_face again but rather translating the relevant fields from start. */ _compute_face (p2, dev_slope, slope_dx, slope_dy, stroker, end); if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; rectangle[0] = start->cw; rectangle[1] = start->ccw; rectangle[2] = end->ccw; rectangle[3] = end->cw; return _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle); }
static cairo_status_t _cairo_stroker_add_caps (cairo_stroker_t *stroker) { cairo_status_t status; /* check for a degenerative sub_path */ if (stroker->has_initial_sub_path && ! stroker->has_first_face && ! stroker->has_current_face && stroker->style.line_cap == CAIRO_LINE_CAP_ROUND) { /* pick an arbitrary slope to use */ double dx = 1.0, dy = 0.0; cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 }; cairo_stroke_face_t face; _compute_normalized_device_slope (&dx, &dy, stroker->ctm_inverse, NULL); /* arbitrarily choose first_point * first_point and current_point should be the same */ _compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face); status = _cairo_stroker_add_leading_cap (stroker, &face); if (unlikely (status)) return status; status = _cairo_stroker_add_trailing_cap (stroker, &face); if (unlikely (status)) return status; } if (stroker->has_first_face) { status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face); if (unlikely (status)) return status; } if (stroker->has_current_face) { status = _cairo_stroker_add_trailing_cap (stroker, &stroker->current_face); if (unlikely (status)) return status; } return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, const cairo_point_t *p1, const cairo_point_t *p2, cairo_slope_t *dev_slope, double slope_dx, double slope_dy, cairo_stroke_face_t *start, cairo_stroke_face_t *end) { _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start); *end = *start; if (p1->x == p2->x && p1->y == p2->y) return CAIRO_STATUS_SUCCESS; end->point = *p2; end->ccw.x += p2->x - p1->x; end->ccw.y += p2->y - p1->y; end->cw.x += p2->x - p1->x; end->cw.y += p2->y - p1->y; if (stroker->add_external_edge != NULL) { cairo_status_t status; status = stroker->add_external_edge (stroker->closure, &end->cw, &start->cw); if (unlikely (status)) return status; status = stroker->add_external_edge (stroker->closure, &start->ccw, &end->ccw); if (unlikely (status)) return status; return CAIRO_STATUS_SUCCESS; } else { cairo_point_t quad[4]; quad[0] = start->cw; quad[1] = end->cw; quad[2] = end->ccw; quad[3] = start->ccw; return stroker->add_convex_quad (stroker->closure, quad); } }
static cairo_status_t _cairo_stroker_spline_to (void *closure, const cairo_point_t *point, const cairo_slope_t *tangent) { cairo_stroker_t *stroker = closure; cairo_stroke_face_t new_face; double slope_dx, slope_dy; cairo_point_t points[3]; stroker->has_initial_sub_path = TRUE; if (stroker->current_point.x == point->x && stroker->current_point.y == point->y) return CAIRO_STATUS_SUCCESS; slope_dx = _cairo_fixed_to_double (tangent->dx); slope_dy = _cairo_fixed_to_double (tangent->dy); if (! _compute_normalized_device_slope (&slope_dx, &slope_dy, stroker->ctm_inverse, NULL)) return CAIRO_STATUS_SUCCESS; _compute_face (point, tangent, slope_dx, slope_dy, stroker, &new_face); assert(stroker->has_current_face); points[0] = stroker->current_face.cw; points[1] = stroker->current_face.ccw; points[2] = new_face.cw; stroker->add_triangle (stroker->closure, points); points[0] = stroker->current_face.ccw; points[1] = new_face.cw; points[2] = new_face.ccw; stroker->add_triangle (stroker->closure, points); stroker->current_face = new_face; 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; }