/* This special-case filler supports only a path that describes a * device-axis aligned rectangle. It exists to avoid the overhead of * the general tessellator when drawing very common rectangles. * * If the path described anything but a device-axis aligned rectangle, * this function will return %CAIRO_INT_STATUS_UNSUPPORTED. */ static cairo_int_status_t _cairo_path_fixed_fill_rectangle (cairo_path_fixed_t *path, cairo_traps_t *traps) { if (_cairo_path_fixed_is_box (path, NULL)) { cairo_point_t *p = path->buf_head.base.points; cairo_point_t *top_left, *bot_right; top_left = &p[0]; bot_right = &p[2]; if (top_left->x > bot_right->x || top_left->y > bot_right->y) { int n; /* not a simple cairo_rectangle() */ for (n = 0; n < 4; n++) { if (p[n].x <= top_left->x && p[n].y <= top_left->y) top_left = &p[n]; if (p[n].x >= bot_right->x && p[n].y >= bot_right->y) bot_right = &p[n]; } } return _cairo_traps_tessellate_rectangle (traps, top_left, bot_right); } return CAIRO_INT_STATUS_UNSUPPORTED; }
cairo_int_status_t _cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_traps_t *traps) { cairo_box_t box; cairo_status_t status; traps->is_rectilinear = TRUE; traps->is_rectangular = TRUE; if (_cairo_path_fixed_is_box (path, &box)) { return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); } else { cairo_path_fixed_iter_t iter; _cairo_path_fixed_iter_init (&iter, path); while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { if (box.p1.y > box.p2.y) { cairo_fixed_t t; t = box.p1.y; box.p1.y = box.p2.y; box.p2.y = t; t = box.p1.x; box.p1.x = box.p2.x; box.p2.x = t; } status = _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2); if (unlikely (status)) { _cairo_traps_clear (traps); return status; } } if (_cairo_path_fixed_iter_at_end (&iter)) return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule); _cairo_traps_clear (traps); return CAIRO_INT_STATUS_UNSUPPORTED; } }
static cairo_status_t _cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker) { cairo_status_t status; cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; cairo_fixed_t half_line_width = stroker->half_line_width; int i; for (i = 0; i < stroker->num_segments; i++) { cairo_point_t *a, *b; cairo_bool_t is_horizontal; a = &stroker->segments[i].p1; b = &stroker->segments[i].p2; is_horizontal = stroker->segments[i].is_horizontal; /* Handle the joins for a potentially degenerate segment. */ if (line_cap == CAIRO_LINE_CAP_BUTT && stroker->segments[i].has_join && (i != stroker->num_segments - 1 || (! stroker->open_sub_path && stroker->dash.dash_starts_on))) { cairo_point_t p1 = stroker->segments[i].p1; cairo_point_t p2 = stroker->segments[i].p2; cairo_slope_t out_slope; int j = (i + 1) % stroker->num_segments; _cairo_slope_init (&out_slope, &stroker->segments[j].p1, &stroker->segments[j].p2); if (is_horizontal) { if (p1.x <= p2.x) { p1.x = p2.x; p2.x += half_line_width; } else { p1.x = p2.x - half_line_width; } if (out_slope.dy >= 0) p1.y -= half_line_width; if (out_slope.dy <= 0) p2.y += half_line_width; } else { if (p1.y <= p2.y) { p1.y = p2.y; p2.y += half_line_width; } else { p1.y = p2.y - half_line_width; } if (out_slope.dx >= 0) p1.x -= half_line_width; if (out_slope.dx <= 0) p2.x += half_line_width; } status = _cairo_traps_tessellate_rectangle (stroker->traps, &p1, &p2); if (unlikely (status)) return status; } /* Perform the adjustments of the endpoints. */ if (is_horizontal) { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->x <= b->x) { a->x -= half_line_width; b->x += half_line_width; } else { a->x += half_line_width; b->x -= half_line_width; } } if (a->x > b->x) { cairo_point_t *t; t = a; a = b; b = t; } a->y -= half_line_width; b->y += half_line_width; } else { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->y <= b->y) { a->y -= half_line_width; b->y += half_line_width; } else { a->y += half_line_width; b->y -= half_line_width; } } if (a->y > b->y) { cairo_point_t *t; t = a; a = b; b = t; } a->x -= half_line_width; b->x += half_line_width; } if (a->x == b->x && a->y == b->y) continue; status = _cairo_traps_tessellate_rectangle (stroker->traps, a, b); if (unlikely (status)) return status; } stroker->num_segments = 0; return CAIRO_STATUS_SUCCESS; }
static cairo_status_t _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker) { cairo_status_t status; cairo_line_cap_t line_cap = stroker->stroke_style->line_cap; cairo_fixed_t half_line_width = stroker->half_line_width; int i; for (i = 0; i < stroker->num_segments; i++) { cairo_point_t *a, *b; cairo_bool_t lengthen_initial, shorten_final, lengthen_final; a = &stroker->segments[i].p1; b = &stroker->segments[i].p2; /* For each segment we generate a single rectangular * trapezoid. This rectangle is based on a perpendicular * extension (by half the line width) of the segment endpoints * after some adjustments of the endpoints to account for caps * and joins. */ /* We adjust the initial point of the segment to extend the * rectangle to include the previous cap or join, (this * adjustment applies to all segments except for the first * segment of open, butt-capped paths). */ lengthen_initial = TRUE; if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT) lengthen_initial = FALSE; /* The adjustment of the final point is trickier. For all but * the last segment we shorten the segment at the final * endpoint to not overlap with the subsequent join. For the * last segment we do the same shortening if the path is * closed. If the path is open and butt-capped we do no * adjustment, while if it's open and square-capped we do a * lengthening adjustment instead to include the cap. */ shorten_final = TRUE; lengthen_final = FALSE; if (i == stroker->num_segments - 1 && stroker->open_sub_path) { shorten_final = FALSE; if (line_cap == CAIRO_LINE_CAP_SQUARE) lengthen_final = TRUE; } /* Perform the adjustments of the endpoints. */ if (a->y == b->y) { if (a->x < b->x) { if (lengthen_initial) a->x -= half_line_width; if (shorten_final) b->x -= half_line_width; else if (lengthen_final) b->x += half_line_width; } else { if (lengthen_initial) a->x += half_line_width; if (shorten_final) b->x += half_line_width; else if (lengthen_final) b->x -= half_line_width; } if (a->x > b->x) { cairo_point_t *t; t = a; a = b; b = t; } } else { if (a->y < b->y) { if (lengthen_initial) a->y -= half_line_width; if (shorten_final) b->y -= half_line_width; else if (lengthen_final) b->y += half_line_width; } else { if (lengthen_initial) a->y += half_line_width; if (shorten_final) b->y += half_line_width; else if (lengthen_final) b->y -= half_line_width; } if (a->y > b->y) { cairo_point_t *t; t = a; a = b; b = t; } } /* Form the rectangle by expanding by half the line width in * either perpendicular direction. */ if (a->y == b->y) { a->y -= half_line_width; b->y += half_line_width; } else { a->x -= half_line_width; b->x += half_line_width; } status = _cairo_traps_tessellate_rectangle (stroker->traps, a, b); if (unlikely (status)) return status; } stroker->num_segments = 0; return CAIRO_STATUS_SUCCESS; }
/* XXX there is likely a faster method! ;-) */ static cairo_status_t _region_clip_to_boxes (const cairo_region_t *region, cairo_box_t **boxes, int *num_boxes, int *size_boxes) { cairo_traps_t traps; cairo_status_t status; int n, num_rects; _cairo_traps_init (&traps); _cairo_traps_limit (&traps, *boxes, *num_boxes); traps.is_rectilinear = TRUE; traps.is_rectangular = TRUE; num_rects = cairo_region_num_rectangles (region); for (n = 0; n < num_rects; n++) { cairo_rectangle_int_t rect; cairo_point_t p1, p2; cairo_region_get_rectangle (region, n, &rect); p1.x = _cairo_fixed_from_int (rect.x); p1.y = _cairo_fixed_from_int (rect.y); p2.x = _cairo_fixed_from_int (rect.x + rect.width); p2.y = _cairo_fixed_from_int (rect.y + rect.height); status = _cairo_traps_tessellate_rectangle (&traps, &p1, &p2); if (unlikely (status)) goto CLEANUP; } status = _cairo_bentley_ottmann_tessellate_rectangular_traps (&traps, CAIRO_FILL_RULE_WINDING); if (unlikely (status)) goto CLEANUP; n = *size_boxes; if (n < 0) n = -n; if (traps.num_traps > n) { cairo_box_t *new_boxes; new_boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); if (unlikely (new_boxes == NULL)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto CLEANUP; } if (*size_boxes > 0) free (*boxes); *boxes = new_boxes; *size_boxes = traps.num_traps; } for (n = 0; n < traps.num_traps; n++) { (*boxes)[n].p1.x = traps.traps[n].left.p1.x; (*boxes)[n].p1.y = traps.traps[n].top; (*boxes)[n].p2.x = traps.traps[n].right.p1.x; (*boxes)[n].p2.y = traps.traps[n].bottom; } *num_boxes = n; CLEANUP: _cairo_traps_fini (&traps); return status; }