Пример #1
0
/* assumes a has been previously added */
void
_cairo_box_add_curve_to (cairo_box_t *extents,
			 const cairo_point_t *a,
			 const cairo_point_t *b,
			 const cairo_point_t *c,
			 const cairo_point_t *d)
{
    _cairo_box_add_point (extents, d);
    if (!_cairo_box_contains_point (extents, b) ||
	!_cairo_box_contains_point (extents, c))
    {
	cairo_status_t status;

	status = _cairo_spline_bound (_cairo_box_add_spline_point,
				      extents, a, b, c, d);
	assert (status == CAIRO_STATUS_SUCCESS);
    }
}
Пример #2
0
cairo_bool_t
_cairo_spline_intersects (const cairo_point_t *a,
			  const cairo_point_t *b,
			  const cairo_point_t *c,
			  const cairo_point_t *d,
			  const cairo_box_t *box)
{
    cairo_box_t bounds;

    if (_cairo_box_contains_point (box, a) ||
	_cairo_box_contains_point (box, b) ||
	_cairo_box_contains_point (box, c) ||
	_cairo_box_contains_point (box, d))
    {
	return TRUE;
    }

    bounds.p2 = bounds.p1 = *a;
    _cairo_box_add_point (&bounds, b);
    _cairo_box_add_point (&bounds, c);
    _cairo_box_add_point (&bounds, d);

    if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
	bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
    {
	return FALSE;
    }

#if 0 /* worth refining? */
    bounds.p2 = bounds.p1 = *a;
    _cairo_box_add_curve_to (&bounds, b, c, d);
    if (bounds.p2.x <= box->p1.x || bounds.p1.x >= box->p2.x ||
	bounds.p2.y <= box->p1.y || bounds.p1.y >= box->p2.y)
    {
	return FALSE;
    }
#endif

    return TRUE;
}
Пример #3
0
/*
 * Construct a fan around the midpoint using the vertices from pen between
 * inpt and outpt.
 */
static void
add_fan (struct stroker *stroker,
	 const cairo_slope_t *in_vector,
	 const cairo_slope_t *out_vector,
	 const cairo_point_t *midpt,
	 cairo_bool_t clockwise,
	 struct stroke_contour *c)
{
    cairo_pen_t *pen = &stroker->pen;
    int start, stop;

    if (stroker->has_bounds &&
	! _cairo_box_contains_point (&stroker->bounds, midpt))
	return;

    assert (stroker->pen.num_vertices);

    if (clockwise) {
	_cairo_pen_find_active_cw_vertices (pen,
					    in_vector, out_vector,
					    &start, &stop);
	while (start != stop) {
	    cairo_point_t p = *midpt;
	    translate_point (&p, &pen->vertices[start].point);
	    contour_add_point (stroker, c, &p);

	    if (++start == pen->num_vertices)
		start = 0;
	}
    } else {
	_cairo_pen_find_active_ccw_vertices (pen,
					     in_vector, out_vector,
					     &start, &stop);
	while (start != stop) {
	    cairo_point_t p = *midpt;
	    translate_point (&p, &pen->vertices[start].point);
	    contour_add_point (stroker, c, &p);

	    if (start-- == 0)
		start += pen->num_vertices;
	}
    }
}
Пример #4
0
cairo_bool_t
_cairo_box_intersects_line_segment (cairo_box_t *box, cairo_line_t *line)
{
    cairo_fixed_t t1=0, t2=0, t3=0, t4=0;
    cairo_int64_t t1y, t2y, t3x, t4x;

    cairo_fixed_t xlen, ylen;

    if (_cairo_box_contains_point (box, &line->p1) ||
	_cairo_box_contains_point (box, &line->p2))
	return TRUE;

    xlen = P2x - P1x;
    ylen = P2y - P1y;

    if (xlen) {
	if (xlen > 0) {
	    t1 = B1x - P1x;
	    t2 = B2x - P1x;
	} else {
	    t1 = P1x - B2x;
	    t2 = P1x - B1x;
	    xlen = - xlen;
	}

	if ((t1 < 0 || t1 > xlen) &&
	    (t2 < 0 || t2 > xlen))
	    return FALSE;
    } else {
	/* Fully vertical line -- check that X is in bounds */
	if (P1x < B1x || P1x > B2x)
	    return FALSE;
    }

    if (ylen) {
	if (ylen > 0) {
	    t3 = B1y - P1y;
	    t4 = B2y - P1y;
	} else {
	    t3 = P1y - B2y;
	    t4 = P1y - B1y;
	    ylen = - ylen;
	}

	if ((t3 < 0 || t3 > ylen) &&
	    (t4 < 0 || t4 > ylen))
	    return FALSE;
    } else {
	/* Fully horizontal line -- check Y */
	if (P1y < B1y || P1y > B2y)
	    return FALSE;
    }

    /* If we had a horizontal or vertical line, then it's already been checked */
    if (P1x == P2x || P1y == P2y)
	return TRUE;

    /* Check overlap.  Note that t1 < t2 and t3 < t4 here. */
    t1y = _cairo_int32x32_64_mul (t1, ylen);
    t2y = _cairo_int32x32_64_mul (t2, ylen);
    t3x = _cairo_int32x32_64_mul (t3, xlen);
    t4x = _cairo_int32x32_64_mul (t4, xlen);

    if (_cairo_int64_lt(t1y, t4x) &&
	_cairo_int64_lt(t3x, t2y))
	return TRUE;

    return FALSE;
}
Пример #5
0
/*
 * Dashed lines.  Cap each dash end, join around turns when on
 */
static cairo_status_t
_cairo_stroker_line_to_dashed (void *closure,
			       const cairo_point_t *p2)
{
    cairo_stroker_t *stroker = closure;
    double mag, remain, step_length = 0;
    double slope_dx, slope_dy;
    double dx2, dy2;
    cairo_stroke_face_t sub_start, sub_end;
    cairo_point_t *p1 = &stroker->current_point;
    cairo_slope_t dev_slope;
    cairo_line_t segment;
    cairo_bool_t fully_in_bounds;
    cairo_status_t status;

    stroker->has_initial_sub_path = stroker->dash.dash_starts_on;

    if (p1->x == p2->x && p1->y == p2->y)
	return CAIRO_STATUS_SUCCESS;

    fully_in_bounds = TRUE;
    if (stroker->has_bounds &&
	(! _cairo_box_contains_point (&stroker->bounds, p1) ||
	 ! _cairo_box_contains_point (&stroker->bounds, p2)))
    {
	fully_in_bounds = FALSE;
    }

    _cairo_slope_init (&dev_slope, p1, p2);

    slope_dx = _cairo_fixed_to_double (p2->x - p1->x);
    slope_dy = _cairo_fixed_to_double (p2->y - p1->y);

    if (! _compute_normalized_device_slope (&slope_dx, &slope_dy,
					    stroker->ctm_inverse, &mag))
    {
	return CAIRO_STATUS_SUCCESS;
    }

    remain = mag;
    segment.p1 = *p1;
    while (remain) {
	step_length = MIN (stroker->dash.dash_remain, remain);
	remain -= step_length;
	dx2 = slope_dx * (mag - remain);
	dy2 = slope_dy * (mag - remain);
	cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);
	segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
	segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;

	if (stroker->dash.dash_on &&
	    (fully_in_bounds ||
	     (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
	     _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
	{
	    status = _cairo_stroker_add_sub_edge (stroker,
						  &segment.p1, &segment.p2,
						  &dev_slope,
						  slope_dx, slope_dy,
						  &sub_start, &sub_end);
	    if (unlikely (status))
		return status;

	    if (stroker->has_current_face)
	    {
		/* Join with final face from previous segment */
		status = _cairo_stroker_join (stroker,
					      &stroker->current_face,
					      &sub_start);
		if (unlikely (status))
		    return status;

		stroker->has_current_face = FALSE;
	    }
	    else if (! stroker->has_first_face &&
		       stroker->dash.dash_starts_on)
	    {
		/* Save sub path's first face in case needed for closing join */
		stroker->first_face = sub_start;
		stroker->has_first_face = TRUE;
	    }
	    else
	    {
		/* Cap dash start if not connecting to a previous segment */
		status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
		if (unlikely (status))
		    return status;
	    }

	    if (remain) {
		/* Cap dash end if not at end of segment */
		status = _cairo_stroker_add_trailing_cap (stroker, &sub_end);
		if (unlikely (status))
		    return status;
	    } else {
		stroker->current_face = sub_end;
		stroker->has_current_face = TRUE;
	    }
	} else {
	    if (stroker->has_current_face) {
		/* Cap final face from previous segment */
		status = _cairo_stroker_add_trailing_cap (stroker,
							  &stroker->current_face);
		if (unlikely (status))
		    return status;

		stroker->has_current_face = FALSE;
	    }
	}

	_cairo_stroker_dash_step (&stroker->dash, step_length);
	segment.p1 = segment.p2;
    }

    if (stroker->dash.dash_on && ! stroker->has_current_face) {
	/* This segment ends on a transition to dash_on, compute a new face
	 * and add cap for the beginning of the next dash_on step.
	 *
	 * Note: this will create a degenerate cap if this is not the last line
	 * in the path. Whether this behaviour is desirable or not is debatable.
	 * On one side these degenerate caps can not be reproduced with regular
	 * path stroking.
	 * On the other hand, Acroread 7 also produces the degenerate caps.
	 */
	_compute_face (p2, &dev_slope,
		       slope_dx, slope_dy,
		       stroker,
		       &stroker->current_face);

	status = _cairo_stroker_add_leading_cap (stroker,
						 &stroker->current_face);
	if (unlikely (status))
	    return status;

	stroker->has_current_face = TRUE;
    }

    stroker->current_point = *p2;

    return CAIRO_STATUS_SUCCESS;
}
Пример #6
0
/*
 * Construct a fan around the midpoint using the vertices from pen between
 * inpt and outpt.
 */
static cairo_status_t
_tessellate_fan (cairo_stroker_t *stroker,
		 const cairo_slope_t *in_vector,
		 const cairo_slope_t *out_vector,
		 const cairo_point_t *midpt,
		 const cairo_point_t *inpt,
		 const cairo_point_t *outpt,
		 cairo_bool_t clockwise)
{
    cairo_point_t stack_points[64], *points = stack_points;
    cairo_pen_t *pen = &stroker->pen;
    int start, stop, num_points = 0;
    cairo_status_t status;

    if (stroker->has_bounds &&
	! _cairo_box_contains_point (&stroker->bounds, midpt))
	goto BEVEL;

    assert (stroker->pen.num_vertices);

    if (clockwise) {
	_cairo_pen_find_active_ccw_vertices (pen,
					     in_vector, out_vector,
					     &start, &stop);
	if (stroker->add_external_edge) {
	    cairo_point_t last;
	    last = *inpt;
	    while (start != stop) {
		cairo_point_t p = *midpt;
		_translate_point (&p, &pen->vertices[start].point);

		status = stroker->add_external_edge (stroker->closure,
						     &last, &p);
		if (unlikely (status))
		    return status;
		last = p;

		if (start-- == 0)
		    start += pen->num_vertices;
	    }
	    status = stroker->add_external_edge (stroker->closure,
						 &last, outpt);
	} else {
	    if (start == stop)
		goto BEVEL;

	    num_points = stop - start;
	    if (num_points < 0)
		num_points += pen->num_vertices;
	    num_points += 2;
	    if (num_points > ARRAY_LENGTH(stack_points)) {
		points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t));
		if (unlikely (points == NULL))
		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    }

	    points[0] = *inpt;
	    num_points = 1;
	    while (start != stop) {
		points[num_points] = *midpt;
		_translate_point (&points[num_points], &pen->vertices[start].point);
		num_points++;

		if (start-- == 0)
		    start += pen->num_vertices;
	    }
	    points[num_points++] = *outpt;
	}
    } else {
	_cairo_pen_find_active_cw_vertices (pen,
					    in_vector, out_vector,
					    &start, &stop);
	if (stroker->add_external_edge) {
	    cairo_point_t last;
	    last = *inpt;
	    while (start != stop) {
		cairo_point_t p = *midpt;
		_translate_point (&p, &pen->vertices[start].point);

		status = stroker->add_external_edge (stroker->closure,
						     &p, &last);
		if (unlikely (status))
		    return status;
		last = p;

		if (++start == pen->num_vertices)
		    start = 0;
	    }
	    status = stroker->add_external_edge (stroker->closure,
						 outpt, &last);
	} else {
	    if (start == stop)
		goto BEVEL;

	    num_points = stop - start;
	    if (num_points < 0)
		num_points += pen->num_vertices;
	    num_points += 2;
	    if (num_points > ARRAY_LENGTH(stack_points)) {
		points = _cairo_malloc_ab (num_points, sizeof (cairo_point_t));
		if (unlikely (points == NULL))
		    return _cairo_error (CAIRO_STATUS_NO_MEMORY);
	    }

	    points[0] = *inpt;
	    num_points = 1;
	    while (start != stop) {
		points[num_points] = *midpt;
		_translate_point (&points[num_points], &pen->vertices[start].point);
		num_points++;

		if (++start == pen->num_vertices)
		    start = 0;
	    }
	    points[num_points++] = *outpt;
	}
    }

    if (num_points) {
	status = stroker->add_triangle_fan (stroker->closure,
					    midpt, points, num_points);
    }

    if (points != stack_points)
	free (points);

    return status;

BEVEL:
    /* Ensure a leak free connection... */
    if (stroker->add_external_edge != NULL) {
	if (clockwise)
	    return stroker->add_external_edge (stroker->closure, inpt, outpt);
	else
	    return stroker->add_external_edge (stroker->closure, outpt, inpt);
    } else {
	stack_points[0] = *midpt;
	stack_points[1] = *inpt;
	stack_points[2] = *outpt;
	return stroker->add_triangle (stroker->closure, stack_points);
    }
}
Пример #7
0
static cairo_status_t
_cairo_rectilinear_stroker_line_to_dashed (void		*closure,
					   const cairo_point_t	*point)
{
    cairo_rectilinear_stroker_t *stroker = closure;
    const cairo_point_t *a = &stroker->current_point;
    const cairo_point_t *b = point;
    cairo_bool_t fully_in_bounds;
    double sf, sign, remain;
    cairo_fixed_t mag;
    cairo_status_t status;
    cairo_line_t segment;
    cairo_bool_t dash_on = FALSE;
    unsigned is_horizontal;

    /* We don't draw anything for degenerate paths. */
    if (a->x == b->x && a->y == b->y)
	return CAIRO_STATUS_SUCCESS;

    /* We only support horizontal or vertical elements. */
    assert (a->x == b->x || a->y == b->y);

    fully_in_bounds = TRUE;
    if (stroker->has_bounds &&
	(! _cairo_box_contains_point (&stroker->bounds, a) ||
	 ! _cairo_box_contains_point (&stroker->bounds, b)))
    {
	fully_in_bounds = FALSE;
    }

    is_horizontal = a->y == b->y;
    if (is_horizontal) {
	mag = b->x - a->x;
	sf = fabs (stroker->ctm->xx);
    } else {
	mag = b->y - a->y;
	sf = fabs (stroker->ctm->yy);
    }
    if (mag < 0) {
	remain = _cairo_fixed_to_double (-mag);
	sign = 1.;
    } else {
	remain = _cairo_fixed_to_double (mag);
	is_horizontal |= FORWARDS;
	sign = -1.;
    }

    segment.p2 = segment.p1 = *a;
    while (remain > 0.) {
	double step_length;

	step_length = MIN (sf * stroker->dash.dash_remain, remain);
	remain -= step_length;

	mag = _cairo_fixed_from_double (sign*remain);
	if (is_horizontal & 0x1)
	    segment.p2.x = b->x + mag;
	else
	    segment.p2.y = b->y + mag;

	if (stroker->dash.dash_on &&
	    (fully_in_bounds ||
	     _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
	{
	    status = _cairo_rectilinear_stroker_add_segment (stroker,
							     &segment.p1,
							     &segment.p2,
							     is_horizontal | (remain <= 0.) << 2);
	    if (unlikely (status))
		return status;

	    dash_on = TRUE;
	}
	else
	{
	    dash_on = FALSE;
	}

	_cairo_stroker_dash_step (&stroker->dash, step_length / sf);
	segment.p1 = segment.p2;
    }

    if (stroker->dash.dash_on && ! dash_on &&
	(fully_in_bounds ||
	 _cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
    {

	/* This segment ends on a transition to dash_on, compute a new face
	 * and add cap for the beginning of the next dash_on step.
	 */

	status = _cairo_rectilinear_stroker_add_segment (stroker,
							 &segment.p1,
							 &segment.p1,
							 is_horizontal | JOIN);
	if (unlikely (status))
	    return status;
    }

    stroker->current_point = *point;
    stroker->open_sub_path = TRUE;

    return CAIRO_STATUS_SUCCESS;
}