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; } }
cairo_status_t _cairo_bentley_ottmann_tessellate_rectangular_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { 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; cairo_status_t status; int i; assert (traps->is_rectangular); if (unlikely (traps->num_traps <= 1)) { if (traps->num_traps == 1) { cairo_trapezoid_t *trap = traps->traps; if (trap->left.p1.x > trap->right.p1.x) { cairo_line_t tmp = trap->left; trap->left = trap->right; trap->right = tmp; } } return CAIRO_STATUS_SUCCESS; } dump_traps (traps, "bo-rects-traps-in.txt"); rectangles = stack_rectangles; rectangles_ptrs = stack_rectangles_ptrs; if (traps->num_traps > ARRAY_LENGTH (stack_rectangles)) { rectangles = _cairo_malloc_ab_plus_c (traps->num_traps, sizeof (rectangle_t) + sizeof (rectangle_t *), 3*sizeof (rectangle_t *)); if (unlikely (rectangles == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); rectangles_ptrs = (rectangle_t **) (rectangles + traps->num_traps); } for (i = 0; i < traps->num_traps; i++) { if (traps->traps[i].left.p1.x < traps->traps[i].right.p1.x) { rectangles[i].left.x = traps->traps[i].left.p1.x; rectangles[i].left.dir = 1; rectangles[i].right.x = traps->traps[i].right.p1.x; rectangles[i].right.dir = -1; } else { rectangles[i].right.x = traps->traps[i].left.p1.x; rectangles[i].right.dir = 1; rectangles[i].left.x = traps->traps[i].right.p1.x; rectangles[i].left.dir = -1; } rectangles[i].left.right = NULL; rectangles[i].right.right = NULL; rectangles[i].top = traps->traps[i].top; rectangles[i].bottom = traps->traps[i].bottom; rectangles_ptrs[i+2] = &rectangles[i]; } /* XXX incremental sort */ _rectangle_sort (rectangles_ptrs+2, i); _cairo_traps_clear (traps); status = _cairo_bentley_ottmann_tessellate_rectangular (rectangles_ptrs+2, i, fill_rule, TRUE, traps); traps->is_rectilinear = TRUE; traps->is_rectangular = TRUE; if (rectangles != stack_rectangles) free (rectangles); dump_traps (traps, "bo-rects-traps-out.txt"); 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; }
cairo_status_t _cairo_bentley_ottmann_tessellate_rectilinear_traps (cairo_traps_t *traps, cairo_fill_rule_t fill_rule) { cairo_bo_event_t stack_events[CAIRO_STACK_ARRAY_LENGTH (cairo_bo_event_t)]; cairo_bo_event_t *events; cairo_bo_event_t *stack_event_ptrs[ARRAY_LENGTH (stack_events) + 1]; cairo_bo_event_t **event_ptrs; cairo_bo_edge_t stack_edges[ARRAY_LENGTH (stack_events)]; cairo_bo_edge_t *edges; cairo_status_t status; int i, j, k; if (unlikely (traps->num_traps == 0)) return CAIRO_STATUS_SUCCESS; assert (traps->is_rectilinear); i = 4 * traps->num_traps; events = stack_events; event_ptrs = stack_event_ptrs; edges = stack_edges; if (i > ARRAY_LENGTH (stack_events)) { events = _cairo_malloc_ab_plus_c (i, sizeof (cairo_bo_event_t) + sizeof (cairo_bo_edge_t) + sizeof (cairo_bo_event_t *), sizeof (cairo_bo_event_t *)); if (unlikely (events == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); event_ptrs = (cairo_bo_event_t **) (events + i); edges = (cairo_bo_edge_t *) (event_ptrs + i + 1); } for (i = j = k = 0; i < traps->num_traps; i++) { edges[k].edge.top = traps->traps[i].top; edges[k].edge.bottom = traps->traps[i].bottom; edges[k].edge.line = traps->traps[i].left; edges[k].edge.dir = 1; edges[k].deferred_trap.right = NULL; edges[k].prev = NULL; edges[k].next = NULL; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = traps->traps[i].top; events[j].point.x = traps->traps[i].left.p1.x; events[j].edge = &edges[k]; j++; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_STOP; events[j].point.y = traps->traps[i].bottom; events[j].point.x = traps->traps[i].left.p1.x; events[j].edge = &edges[k]; j++; k++; edges[k].edge.top = traps->traps[i].top; edges[k].edge.bottom = traps->traps[i].bottom; edges[k].edge.line = traps->traps[i].right; edges[k].edge.dir = -1; edges[k].deferred_trap.right = NULL; edges[k].prev = NULL; edges[k].next = NULL; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_START; events[j].point.y = traps->traps[i].top; events[j].point.x = traps->traps[i].right.p1.x; events[j].edge = &edges[k]; j++; event_ptrs[j] = &events[j]; events[j].type = CAIRO_BO_EVENT_TYPE_STOP; events[j].point.y = traps->traps[i].bottom; events[j].point.x = traps->traps[i].right.p1.x; events[j].edge = &edges[k]; j++; k++; } _cairo_traps_clear (traps); status = _cairo_bentley_ottmann_tessellate_rectilinear (event_ptrs, j, fill_rule, TRUE, traps); traps->is_rectilinear = TRUE; if (events != stack_events) free (events); return status; }
static cairo_int_status_t _cairo_clip_path_to_region_geometric (cairo_clip_path_t *clip_path) { cairo_traps_t traps; cairo_box_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_t)]; cairo_box_t *boxes = stack_boxes; cairo_status_t status; int n; /* If we have nothing to intersect with this path, then it cannot * magically be reduced into a region. */ if (clip_path->prev == NULL) goto UNSUPPORTED; /* Start simple... Intersect some boxes with an arbitrary path. */ if (! clip_path->path.is_rectilinear) goto UNSUPPORTED; if (clip_path->prev->prev != NULL) goto UNSUPPORTED; _cairo_traps_init (&traps); _cairo_box_from_rectangle (&boxes[0], &clip_path->extents); _cairo_traps_limit (&traps, boxes, 1); status = _cairo_path_fixed_fill_rectilinear_to_traps (&clip_path->path, clip_path->fill_rule, &traps); if (unlikely (_cairo_status_is_error (status))) return status; if (status == CAIRO_INT_STATUS_UNSUPPORTED) goto UNSUPPORTED; if (traps.num_traps > ARRAY_LENGTH (stack_boxes)) { boxes = _cairo_malloc_ab (traps.num_traps, sizeof (cairo_box_t)); if (unlikely (boxes == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } 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; } _cairo_traps_clear (&traps); _cairo_traps_limit (&traps, boxes, n); status = _cairo_path_fixed_fill_to_traps (&clip_path->prev->path, clip_path->prev->fill_rule, clip_path->prev->tolerance, &traps); if (boxes != stack_boxes) free (boxes); if (unlikely (status)) return status; status = _cairo_traps_extract_region (&traps, &clip_path->region); _cairo_traps_fini (&traps); if (status == CAIRO_INT_STATUS_UNSUPPORTED) goto UNSUPPORTED; if (unlikely (status)) return status; clip_path->flags |= CAIRO_CLIP_PATH_HAS_REGION; return CAIRO_STATUS_SUCCESS; UNSUPPORTED: clip_path->flags |= CAIRO_CLIP_PATH_REGION_IS_UNSUPPORTED; return CAIRO_INT_STATUS_UNSUPPORTED; }