예제 #1
0
static cairo_status_t
_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
			     const cairo_point_t *p1,
			     const cairo_point_t *p2,
			     cairo_slope_t *dev_slope,
			     double slope_dx, double slope_dy,
			     cairo_stroke_face_t *start,
			     cairo_stroke_face_t *end)
{
    cairo_point_t rectangle[4];

    _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);

    /* XXX: This could be optimized slightly by not calling
       _compute_face again but rather  translating the relevant
       fields from start. */
    _compute_face (p2, dev_slope, slope_dx, slope_dy, stroker, end);

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

    rectangle[0] = start->cw;
    rectangle[1] = start->ccw;
    rectangle[2] = end->ccw;
    rectangle[3] = end->cw;

    return _cairo_traps_tessellate_convex_quad (stroker->traps, rectangle);
}
예제 #2
0
static cairo_status_t
_cairo_stroker_add_caps (cairo_stroker_t *stroker)
{
    cairo_status_t status;

    /* check for a degenerative sub_path */
    if (stroker->has_initial_sub_path
	&& ! stroker->has_first_face
	&& ! stroker->has_current_face
	&& stroker->style.line_cap == CAIRO_LINE_CAP_ROUND)
    {
	/* pick an arbitrary slope to use */
	double dx = 1.0, dy = 0.0;
	cairo_slope_t slope = { CAIRO_FIXED_ONE, 0 };
	cairo_stroke_face_t face;

	_compute_normalized_device_slope (&dx, &dy,
					  stroker->ctm_inverse, NULL);

	/* arbitrarily choose first_point
	 * first_point and current_point should be the same */
	_compute_face (&stroker->first_point, &slope, dx, dy, stroker, &face);

	status = _cairo_stroker_add_leading_cap (stroker, &face);
	if (unlikely (status))
	    return status;

	status = _cairo_stroker_add_trailing_cap (stroker, &face);
	if (unlikely (status))
	    return status;
    }

    if (stroker->has_first_face) {
	status = _cairo_stroker_add_leading_cap (stroker,
						 &stroker->first_face);
	if (unlikely (status))
	    return status;
    }

    if (stroker->has_current_face) {
	status = _cairo_stroker_add_trailing_cap (stroker,
						  &stroker->current_face);
	if (unlikely (status))
	    return status;
    }

    return CAIRO_STATUS_SUCCESS;
}
예제 #3
0
static cairo_status_t
_cairo_stroker_add_sub_edge (cairo_stroker_t *stroker,
			     const cairo_point_t *p1,
			     const cairo_point_t *p2,
			     cairo_slope_t *dev_slope,
			     double slope_dx, double slope_dy,
			     cairo_stroke_face_t *start,
			     cairo_stroke_face_t *end)
{
    _compute_face (p1, dev_slope, slope_dx, slope_dy, stroker, start);
    *end = *start;

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

    end->point = *p2;
    end->ccw.x += p2->x - p1->x;
    end->ccw.y += p2->y - p1->y;
    end->cw.x += p2->x - p1->x;
    end->cw.y += p2->y - p1->y;

    if (stroker->add_external_edge != NULL) {
	cairo_status_t status;

	status = stroker->add_external_edge (stroker->closure,
					     &end->cw, &start->cw);
	if (unlikely (status))
	    return status;

	status = stroker->add_external_edge (stroker->closure,
					     &start->ccw, &end->ccw);
	if (unlikely (status))
	    return status;

	return CAIRO_STATUS_SUCCESS;
    } else {
	cairo_point_t quad[4];

	quad[0] = start->cw;
	quad[1] = end->cw;
	quad[2] = end->ccw;
	quad[3] = start->ccw;

	return stroker->add_convex_quad (stroker->closure, quad);
    }
}
예제 #4
0
static cairo_status_t
_cairo_stroker_spline_to (void *closure,
			  const cairo_point_t *point,
			  const cairo_slope_t *tangent)
{
    cairo_stroker_t *stroker = closure;
    cairo_stroke_face_t new_face;
    double slope_dx, slope_dy;
    cairo_point_t points[3];

    stroker->has_initial_sub_path = TRUE;

    if (stroker->current_point.x == point->x &&
	stroker->current_point.y == point->y)
	return CAIRO_STATUS_SUCCESS;

    slope_dx = _cairo_fixed_to_double (tangent->dx);
    slope_dy = _cairo_fixed_to_double (tangent->dy);

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

    _compute_face (point, tangent,
		   slope_dx, slope_dy,
		   stroker, &new_face);

    assert(stroker->has_current_face);
    points[0] = stroker->current_face.cw;
    points[1] = stroker->current_face.ccw;
    points[2] = new_face.cw;
    stroker->add_triangle (stroker->closure, points);

    points[0] = stroker->current_face.ccw;
    points[1] = new_face.cw;
    points[2] = new_face.ccw;
    stroker->add_triangle (stroker->closure, points);

    stroker->current_face = new_face;
    stroker->has_current_face = TRUE;
    stroker->current_point = *point;

    return CAIRO_STATUS_SUCCESS;
}
예제 #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
static cairo_status_t
_cairo_stroker_curve_to (void *closure,
			 const cairo_point_t *b,
			 const cairo_point_t *c,
			 const cairo_point_t *d)
{
    cairo_stroker_t *stroker = closure;
    cairo_spline_t spline;
    cairo_line_join_t line_join_save;
    cairo_stroke_face_t face;
    double slope_dx, slope_dy;
    cairo_path_fixed_line_to_func_t *line_to;
    cairo_status_t status = CAIRO_STATUS_SUCCESS;

    line_to = stroker->dash.dashed ?
	_cairo_stroker_line_to_dashed :
	_cairo_stroker_line_to;

    if (! _cairo_spline_init (&spline,
			      (cairo_spline_add_point_func_t)line_to, stroker,
			      &stroker->current_point, b, c, d))
    {
	return line_to (closure, d);
    }

    /* If the line width is so small that the pen is reduced to a
       single point, then we have nothing to do. */
    if (stroker->pen.num_vertices <= 1)
	return CAIRO_STATUS_SUCCESS;

    /* Compute the initial face */
    if (! stroker->dash.dashed || stroker->dash.dash_on) {
	slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx);
	slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy);
	if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
					      stroker->ctm_inverse, NULL))
	{
	    _compute_face (&stroker->current_point,
			   &spline.initial_slope,
			   slope_dx, slope_dy,
			   stroker, &face);
	}
	if (stroker->has_current_face) {
	    status = _cairo_stroker_join (stroker,
					  &stroker->current_face, &face);
	    if (unlikely (status))
		return status;
	} else if (! stroker->has_first_face) {
	    stroker->first_face = face;
	    stroker->has_first_face = TRUE;
	}

	stroker->current_face = face;
	stroker->has_current_face = TRUE;
    }

    /* Temporarily modify the stroker to use round joins to guarantee
     * smooth stroked curves. */
    line_join_save = stroker->style.line_join;
    stroker->style.line_join = CAIRO_LINE_JOIN_ROUND;

    status = _cairo_spline_decompose (&spline, stroker->tolerance);
    if (unlikely (status))
	return status;

    /* And join the final face */
    if (! stroker->dash.dashed || stroker->dash.dash_on) {
	slope_dx = _cairo_fixed_to_double (spline.final_slope.dx);
	slope_dy = _cairo_fixed_to_double (spline.final_slope.dy);
	if (_compute_normalized_device_slope (&slope_dx, &slope_dy,
					      stroker->ctm_inverse, NULL))
	{
	    _compute_face (&stroker->current_point,
			   &spline.final_slope,
			   slope_dx, slope_dy,
			   stroker, &face);
	}

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

	stroker->current_face = face;
    }

    stroker->style.line_join = line_join_save;

    return CAIRO_STATUS_SUCCESS;
}
예제 #7
0
static cairo_status_t
_cairo_stroker_curve_to (void *closure,
			 const cairo_point_t *b,
			 const cairo_point_t *c,
			 const cairo_point_t *d)
{
    cairo_stroker_t *stroker = closure;
    cairo_pen_stroke_spline_t spline_pen;
    cairo_stroke_face_t start, end;
    cairo_point_t extra_points[4];
    cairo_point_t *a = &stroker->current_point;
    double initial_slope_dx, initial_slope_dy;
    double final_slope_dx, final_slope_dy;
    cairo_status_t status;

    status = _cairo_pen_stroke_spline_init (&spline_pen,
					    &stroker->pen,
					    a, b, c, d);
    if (status == CAIRO_INT_STATUS_DEGENERATE)
	return _cairo_stroker_line_to (closure, d);
    else if (unlikely (status))
	return status;

    initial_slope_dx = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dx);
    initial_slope_dy = _cairo_fixed_to_double (spline_pen.spline.initial_slope.dy);
    final_slope_dx = _cairo_fixed_to_double (spline_pen.spline.final_slope.dx);
    final_slope_dy = _cairo_fixed_to_double (spline_pen.spline.final_slope.dy);

    if (_compute_normalized_device_slope (&initial_slope_dx, &initial_slope_dy,
					  stroker->ctm_inverse, NULL))
    {
	_compute_face (a,
		       &spline_pen.spline.initial_slope,
		       initial_slope_dx, initial_slope_dy,
		       stroker, &start);
    }

    if (_compute_normalized_device_slope (&final_slope_dx, &final_slope_dy,
					  stroker->ctm_inverse, NULL))
    {
	_compute_face (d,
		       &spline_pen.spline.final_slope,
		       final_slope_dx, final_slope_dy,
		       stroker, &end);
    }

    if (stroker->has_current_face) {
	status = _cairo_stroker_join (stroker, &stroker->current_face, &start);
	if (unlikely (status))
	    goto CLEANUP_PEN;
    } else if (! stroker->has_first_face) {
	stroker->first_face = start;
	stroker->has_first_face = TRUE;
    }
    stroker->current_face = end;
    stroker->has_current_face = TRUE;

    extra_points[0] = start.cw;
    extra_points[0].x -= start.point.x;
    extra_points[0].y -= start.point.y;
    extra_points[1] = start.ccw;
    extra_points[1].x -= start.point.x;
    extra_points[1].y -= start.point.y;
    extra_points[2] = end.cw;
    extra_points[2].x -= end.point.x;
    extra_points[2].y -= end.point.y;
    extra_points[3] = end.ccw;
    extra_points[3].x -= end.point.x;
    extra_points[3].y -= end.point.y;

    status = _cairo_pen_add_points (&spline_pen.pen, extra_points, 4);
    if (unlikely (status))
	goto CLEANUP_PEN;

    status = _cairo_pen_stroke_spline (&spline_pen,
				       stroker->tolerance,
				       stroker->traps);

  CLEANUP_PEN:
    _cairo_pen_stroke_spline_fini (&spline_pen);

    stroker->current_point = *d;

    return status;
}