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;
}
Exemple #3
0
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;
}