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