cairo_int_status_t _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, cairo_antialias_t antialias, cairo_boxes_t *boxes) { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; cairo_box_t box; assert (_cairo_path_fixed_stroke_is_rectilinear (path)); if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, ctm, antialias, boxes)) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! rectilinear_stroker.dash.dashed && _cairo_path_fixed_is_stroke_box (path, &box) && /* if the segments overlap we need to feed them into the tessellator */ box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x && box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y) { cairo_box_t b; /* top */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y - rectilinear_stroker.half_line_y; b.p2.y = box.p1.y + rectilinear_stroker.half_line_y; status = (cairo_int_status_t)_cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* left (excluding top/bottom) */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p1.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; status = (cairo_int_status_t)_cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* right (excluding top/bottom) */ b.p1.x = box.p2.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; status = (cairo_int_status_t)_cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* bottom */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p2.y - rectilinear_stroker.half_line_y; b.p2.y = box.p2.y + rectilinear_stroker.half_line_y; status = (cairo_int_status_t)_cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); goto done; } if (boxes->num_limits) { _cairo_rectilinear_stroker_limit (&rectilinear_stroker, boxes->limits, boxes->num_limits); } status = (cairo_int_status_t)_cairo_path_fixed_interpret (path, _cairo_rectilinear_stroker_move_to, rectilinear_stroker.dash.dashed ? _cairo_rectilinear_stroker_line_to_dashed : _cairo_rectilinear_stroker_line_to, NULL, _cairo_rectilinear_stroker_close_path, &rectilinear_stroker); if (unlikely (status)) goto BAIL; if (rectilinear_stroker.dash.dashed) status = (cairo_int_status_t)_cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); else status = (cairo_int_status_t)_cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); if (unlikely (status)) goto BAIL; /* As we incrementally tessellate, we do not eliminate self-intersections */ status = (cairo_int_status_t)_cairo_bentley_ottmann_tessellate_boxes (boxes, CAIRO_FILL_RULE_WINDING, boxes); if (unlikely (status)) goto BAIL; done: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; BAIL: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); _cairo_boxes_clear (boxes); return status; }
static cairo_int_status_t _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, cairo_traps_t *traps) { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; /* This special-case rectilinear stroker only supports * miter-joined lines (not curves) and a translation-only matrix * (though it could probably be extended to support a matrix with * uniform, integer scaling). * * It also only supports horizontal and vertical line_to * elements. But we don't catch that here, but instead return * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any * non-rectilinear line_to is encountered. */ if (path->has_curve_to) return CAIRO_INT_STATUS_UNSUPPORTED; if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) return CAIRO_INT_STATUS_UNSUPPORTED; /* If the miter limit turns right angles into bevels, then we * can't use this optimization. Remember, the ratio is * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, * which we round for safety. */ if (stroke_style->miter_limit < M_SQRT2) return CAIRO_INT_STATUS_UNSUPPORTED; if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! (_cairo_matrix_is_identity (ctm) || _cairo_matrix_is_translation (ctm))) { return CAIRO_INT_STATUS_UNSUPPORTED; } _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, ctm, traps); if (traps->has_limits) { _cairo_rectilinear_stroker_limit (&rectilinear_stroker, &traps->limits); } status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, _cairo_rectilinear_stroker_move_to, rectilinear_stroker.dash.dashed ? _cairo_rectilinear_stroker_line_to_dashed : _cairo_rectilinear_stroker_line_to, NULL, _cairo_rectilinear_stroker_close_path, &rectilinear_stroker); if (unlikely (status)) goto BAIL; if (rectilinear_stroker.dash.dashed) status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); else status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); BAIL: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); if (unlikely (status)) _cairo_traps_clear (traps); return status; }