static void edge_end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot) { cairo_status_t status = CAIRO_STATUS_SUCCESS; /* Only emit (trivial) non-degenerate trapezoids with positive height. */ if (likely (left->top < bot)) { if (sweep_line->do_traps) { cairo_line_t _left = { { left->x, left->top }, { left->x, bot }, }, _right = { { left->right->x, left->top }, { left->right->x, bot }, }; _cairo_traps_add_trap (sweep_line->container, left->top, bot, &_left, &_right); status = _cairo_traps_status ((cairo_traps_t *) sweep_line->container); } else { cairo_box_t box; box.p1.x = left->x; box.p1.y = left->top; box.p2.x = left->right->x; box.p2.y = bot; status = _cairo_boxes_add (sweep_line->container, CAIRO_ANTIALIAS_DEFAULT, &box); } } if (unlikely (status)) longjmp (sweep_line->unwind, status); left->right = NULL; }
static cairo_status_t _cairo_bo_edge_end_trap (cairo_bo_edge_t *left, int32_t bot, cairo_bool_t do_traps, void *container) { cairo_bo_trap_t *trap = &left->deferred_trap; cairo_status_t status = CAIRO_STATUS_SUCCESS; /* Only emit (trivial) non-degenerate trapezoids with positive height. */ if (likely (trap->top < bot)) { if (do_traps) { _cairo_traps_add_trap (container, trap->top, bot, &left->edge.line, &trap->right->edge.line); status = _cairo_traps_status ((cairo_traps_t *) container); } else { cairo_box_t box; box.p1.x = left->edge.line.p1.x; box.p1.y = trap->top; box.p2.x = trap->right->edge.line.p1.x; box.p2.y = bot; status = _cairo_boxes_add (container, CAIRO_ANTIALIAS_DEFAULT, &box); } } trap->right = NULL; return status; }
cairo_status_t _cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, cairo_antialias_t antialias, cairo_boxes_t *boxes) { cairo_path_fixed_iter_t iter; cairo_status_t status; cairo_box_t box; if (_cairo_path_fixed_is_box (path, &box)) return _cairo_boxes_add (boxes, antialias, &box); _cairo_path_fixed_iter_init (&iter, path); while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) { if (box.p1.y == box.p2.y || box.p1.x == box.p2.x) continue; 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_boxes_add (boxes, antialias, &box); if (unlikely (status)) return status; } if (_cairo_path_fixed_iter_at_end (&iter)) return _cairo_bentley_ottmann_tessellate_boxes (boxes, fill_rule, boxes); /* path is not rectangular, try extracting clipped rectilinear edges */ _cairo_boxes_clear (boxes); return _cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (path, fill_rule, antialias, boxes); }
cairo_int_status_t _cairo_composite_rectangles_add_to_damage (cairo_composite_rectangles_t *composite, cairo_boxes_t *damage) { cairo_int_status_t status; int n; for (n = 0; n < composite->clip->num_boxes; n++) { status = _cairo_boxes_add (damage, CAIRO_ANTIALIAS_NONE, &composite->clip->boxes[n]); if (unlikely (status)) return status; } return CAIRO_INT_STATUS_SUCCESS; }
static void end_box (sweep_line_t *sweep_line, edge_t *left, int32_t bot, cairo_boxes_t *out) { if (likely (left->top < bot)) { cairo_status_t status; cairo_box_t box; box.p1.x = left->x; box.p1.y = left->top; box.p2.x = left->right->x; box.p2.y = bot; status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box); if (unlikely (status)) longjmp (sweep_line->unwind, status); } left->right = NULL; }
static cairo_status_t span_to_boxes (void *abstract_renderer, int y, int h, const cairo_half_open_span_t *spans, unsigned num_spans) { struct cairo_box_renderer *r = abstract_renderer; cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_box_t box; if (num_spans == 0) return CAIRO_STATUS_SUCCESS; box.p1.y = _cairo_fixed_from_int (y); box.p2.y = _cairo_fixed_from_int (y + h); do { if (spans[0].coverage) { box.p1.x = _cairo_fixed_from_int(spans[0].x); box.p2.x = _cairo_fixed_from_int(spans[1].x); status = _cairo_boxes_add (r->boxes, CAIRO_ANTIALIAS_DEFAULT, &box); } spans++; } while (--num_spans > 1 && status == CAIRO_STATUS_SUCCESS); return status; }
cairo_status_t _cairo_bentley_ottmann_tessellate_boxes (const cairo_boxes_t *in, cairo_fill_rule_t fill_rule, cairo_boxes_t *out) { rectangle_t stack_rectangles[CAIRO_STACK_ARRAY_LENGTH (rectangle_t)]; rectangle_t *stack_rectangles_ptrs[ARRAY_LENGTH (stack_rectangles) + 3]; rectangle_t *rectangles, **rectangles_ptrs; rectangle_t *stack_rectangles_chain[CAIRO_STACK_ARRAY_LENGTH (rectangle_t *) ]; rectangle_t **rectangles_chain = NULL; const struct _cairo_boxes_chunk *chunk; cairo_status_t status; int i, j, y_min, y_max; if (unlikely (in->num_boxes == 0)) { _cairo_boxes_clear (out); return CAIRO_STATUS_SUCCESS; } if (in->num_boxes == 1) { if (in == out) { cairo_box_t *box = &in->chunks.base[0]; if (box->p1.x > box->p2.x) { cairo_fixed_t tmp = box->p1.x; box->p1.x = box->p2.x; box->p2.x = tmp; } } else { cairo_box_t box = in->chunks.base[0]; if (box.p1.x > box.p2.x) { cairo_fixed_t tmp = box.p1.x; box.p1.x = box.p2.x; box.p2.x = tmp; } _cairo_boxes_clear (out); status = _cairo_boxes_add (out, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_STATUS_SUCCESS); } return CAIRO_STATUS_SUCCESS; } y_min = INT_MAX; y_max = INT_MIN; for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { if (box[i].p1.y < y_min) y_min = box[i].p1.y; if (box[i].p1.y > y_max) y_max = box[i].p1.y; } } y_min = _cairo_fixed_integer_floor (y_min); y_max = _cairo_fixed_integer_floor (y_max) + 1; y_max -= y_min; if (y_max < in->num_boxes) { rectangles_chain = stack_rectangles_chain; if (y_max > ARRAY_LENGTH (stack_rectangles_chain)) { rectangles_chain = _cairo_malloc_ab (y_max, sizeof (rectangle_t *)); if (unlikely (rectangles_chain == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } memset (rectangles_chain, 0, y_max * sizeof (rectangle_t*)); } rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; if (in->num_boxes > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (in->num_boxes, sizeof (rectangle_t) + sizeof (rectangle_t *), 3*sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) { if (rectangles_chain != stack_rectangles_chain) free (rectangles_chain); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } rectangles_ptrs = (rectangle_t **) (rectangles + in->num_boxes); } j = 0; for (chunk = &in->chunks; chunk != NULL; chunk = chunk->next) { const cairo_box_t *box = chunk->base; for (i = 0; i < chunk->count; i++) { int h; if (box[i].p1.x < box[i].p2.x) { rectangles[j].left.x = box[i].p1.x; rectangles[j].left.dir = 1; rectangles[j].right.x = box[i].p2.x; rectangles[j].right.dir = -1; } else { rectangles[j].right.x = box[i].p1.x; rectangles[j].right.dir = 1; rectangles[j].left.x = box[i].p2.x; rectangles[j].left.dir = -1; } rectangles[j].left.right = NULL; rectangles[j].right.right = NULL; rectangles[j].top = box[i].p1.y; rectangles[j].bottom = box[i].p2.y; if (rectangles_chain) { h = _cairo_fixed_integer_floor (box[i].p1.y) - y_min; rectangles[j].left.next = (edge_t *)rectangles_chain[h]; rectangles_chain[h] = &rectangles[j]; } else { rectangles_ptrs[j+2] = &rectangles[j]; } j++; } } if (rectangles_chain) { j = 2; for (y_min = 0; y_min < y_max; y_min++) { rectangle_t *r; int start = j; for (r = rectangles_chain[y_min]; r; r = (rectangle_t *)r->left.next) rectangles_ptrs[j++] = r; if (j > start + 1) _rectangle_sort (rectangles_ptrs + start, j - start); } if (rectangles_chain != stack_rectangles_chain) free (rectangles_chain); j -= 2; } else { _rectangle_sort (rectangles_ptrs + 2, j); } _cairo_boxes_clear (out); status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, j, fill_rule, FALSE, out); if (rectangles != stack_rectangles) free (rectangles); return status; }
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_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_x = stroker->half_line_x; cairo_fixed_t half_line_y = stroker->half_line_y; int i; for (i = 0; i < stroker->num_segments; i++) { cairo_point_t *a, *b; cairo_bool_t is_horizontal; cairo_box_t box; a = &stroker->segments[i].p1; b = &stroker->segments[i].p2; is_horizontal = stroker->segments[i].flags & HORIZONTAL; /* Handle the joins for a potentially degenerate segment. */ if (line_cap == CAIRO_LINE_CAP_BUTT && stroker->segments[i].flags & JOIN && (i != stroker->num_segments - 1 || (! stroker->open_sub_path && stroker->dash.dash_starts_on))) { cairo_slope_t out_slope; int j = (i + 1) % stroker->num_segments; cairo_bool_t forwards = !!(stroker->segments[i].flags & FORWARDS); _cairo_slope_init (&out_slope, &stroker->segments[j].p1, &stroker->segments[j].p2); box.p2 = box.p1 = stroker->segments[i].p2; if (is_horizontal) { if (forwards) box.p2.x += half_line_x; else box.p1.x -= half_line_x; if (out_slope.dy > 0) box.p1.y -= half_line_y; else box.p2.y += half_line_y; } else { if (forwards) box.p2.y += half_line_y; else box.p1.y -= half_line_y; if (out_slope.dx > 0) box.p1.x -= half_line_x; else box.p2.x += half_line_x; } status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); 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_x; b->x += half_line_x; } else { a->x += half_line_x; b->x -= half_line_x; } } a->y += half_line_y; b->y -= half_line_y; } else { if (line_cap == CAIRO_LINE_CAP_SQUARE) { if (a->y <= b->y) { a->y -= half_line_y; b->y += half_line_y; } else { a->y += half_line_y; b->y -= half_line_y; } } a->x += half_line_x; b->x -= half_line_x; } if (a->x == b->x && a->y == b->y) continue; if (a->x < b->x) { box.p1.x = a->x; box.p2.x = b->x; } else { box.p1.x = b->x; box.p2.x = a->x; } if (a->y < b->y) { box.p1.y = a->y; box.p2.y = b->y; } else { box.p1.y = b->y; box.p2.y = a->y; } status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); 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_line_cap_t line_cap = stroker->stroke_style->line_cap; cairo_fixed_t half_line_x = stroker->half_line_x; cairo_fixed_t half_line_y = stroker->half_line_y; cairo_status_t status; int i, j; /* For each segment we generate a single rectangle. * 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. */ for (i = 0; i < stroker->num_segments; i++) { cairo_bool_t lengthen_initial, lengthen_final; cairo_point_t *a, *b; cairo_box_t box; a = &stroker->segments[i].p1; b = &stroker->segments[i].p2; /* 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). However, we must be * careful not to emit a miter join across a degenerate segment * which has been elided. * * Overlapping segments will be eliminated by the tessellation. * Ideally, we would not emit these self-intersections at all, * but that is tricky with segments shorter than half_line_width. */ j = i == 0 ? stroker->num_segments - 1 : i-1; lengthen_initial = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL; j = i == stroker->num_segments - 1 ? 0 : i+1; lengthen_final = (stroker->segments[i].flags ^ stroker->segments[j].flags) & HORIZONTAL; if (stroker->open_sub_path) { if (i == 0) lengthen_initial = line_cap != CAIRO_LINE_CAP_BUTT; if (i == stroker->num_segments - 1) lengthen_final = line_cap != CAIRO_LINE_CAP_BUTT; } /* Perform the adjustments of the endpoints. */ if (lengthen_initial | lengthen_final) { if (a->y == b->y) { if (a->x < b->x) { if (lengthen_initial) a->x -= half_line_x; if (lengthen_final) b->x += half_line_x; } else { if (lengthen_initial) a->x += half_line_x; if (lengthen_final) b->x -= half_line_x; } } else { if (a->y < b->y) { if (lengthen_initial) a->y -= half_line_y; if (lengthen_final) b->y += half_line_y; } else { if (lengthen_initial) a->y += half_line_y; if (lengthen_final) b->y -= half_line_y; } } } /* Form the rectangle by expanding by half the line width in * either perpendicular direction. */ if (a->y == b->y) { a->y -= half_line_y; b->y += half_line_y; } else { a->x -= half_line_x; b->x += half_line_x; } if (a->x < b->x) { box.p1.x = a->x; box.p2.x = b->x; } else { box.p1.x = b->x; box.p2.x = a->x; } if (a->y < b->y) { box.p1.y = a->y; box.p2.y = b->y; } else { box.p1.y = b->y; box.p2.y = a->y; } status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box); if (unlikely (status)) return status; } stroker->num_segments = 0; return CAIRO_STATUS_SUCCESS; }
static cairo_int_status_t fixup_unbounded_boxes (const cairo_spans_compositor_t *compositor, const cairo_composite_rectangles_t *extents, cairo_boxes_t *boxes) { cairo_boxes_t tmp, clear; cairo_box_t box; cairo_int_status_t status; assert (boxes->is_pixel_aligned); TRACE ((stderr, "%s\n", __FUNCTION__)); if (extents->bounded.width == extents->unbounded.width && extents->bounded.height == extents->unbounded.height) { return CAIRO_STATUS_SUCCESS; } /* subtract the drawn boxes from the unbounded area */ _cairo_boxes_init (&clear); box.p1.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); box.p1.y = _cairo_fixed_from_int (extents->unbounded.y); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.y = _cairo_fixed_from_int (extents->unbounded.y + extents->unbounded.height); if (boxes->num_boxes) { _cairo_boxes_init (&tmp); status = _cairo_boxes_add (&tmp, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_INT_STATUS_SUCCESS); tmp.chunks.next = &boxes->chunks; tmp.num_boxes += boxes->num_boxes; status = _cairo_bentley_ottmann_tessellate_boxes (&tmp, CAIRO_FILL_RULE_WINDING, &clear); tmp.chunks.next = NULL; if (unlikely (status)) goto error; } else { box.p1.x = _cairo_fixed_from_int (extents->unbounded.x); box.p2.x = _cairo_fixed_from_int (extents->unbounded.x + extents->unbounded.width); status = _cairo_boxes_add (&clear, CAIRO_ANTIALIAS_DEFAULT, &box); assert (status == CAIRO_INT_STATUS_SUCCESS); } /* If we have a clip polygon, we need to intersect with that as well */ if (extents->clip->path) { status = fixup_unbounded_polygon (compositor, extents, &clear); if (status == CAIRO_INT_STATUS_UNSUPPORTED) status = fixup_unbounded_mask (compositor, extents, &clear); } else { /* Otherwise just intersect with the clip boxes */ if (extents->clip->num_boxes) { _cairo_boxes_init_for_array (&tmp, extents->clip->boxes, extents->clip->num_boxes); status = _cairo_boxes_intersect (&clear, &tmp, &clear); if (unlikely (status)) goto error; } if (clear.is_pixel_aligned) { status = compositor->fill_boxes (extents->surface, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, &clear); } else { cairo_composite_rectangles_t composite; status = _cairo_composite_rectangles_init_for_boxes (&composite, extents->surface, CAIRO_OPERATOR_CLEAR, &_cairo_pattern_clear.base, &clear, NULL); if (likely (status == CAIRO_INT_STATUS_SUCCESS)) { status = composite_boxes (compositor, &composite, &clear); _cairo_composite_rectangles_fini (&composite); } } } error: _cairo_boxes_fini (&clear); return status; }