/* Adjusts the fill extents (above) by the device-space pen.  */
void
_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
					      cairo_stroke_style_t *style,
					      const cairo_matrix_t *ctm,
					      cairo_rectangle_int_t *extents)
{
    cairo_path_bounder_t bounder;
    cairo_status_t status;

    _cairo_path_bounder_init (&bounder);

    status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD,
					  _cairo_path_bounder_move_to,
					  _cairo_path_bounder_line_to,
					  _cairo_path_bounder_curve_to,
					  _cairo_path_bounder_close_path,
					  &bounder);
    assert (status == CAIRO_STATUS_SUCCESS);

    if (bounder.has_point) {
	double dx, dy;

	_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);

	bounder.extents.p1.x -= _cairo_fixed_from_double (dx);
	bounder.extents.p2.x += _cairo_fixed_from_double (dx);
	bounder.extents.p1.y -= _cairo_fixed_from_double (dy);
	bounder.extents.p2.y += _cairo_fixed_from_double (dy);

	_cairo_box_round_to_rectangle (&bounder.extents, extents);
    } else if (bounder.has_initial_point) {
	double dx, dy;

	/* accommodate capping of degenerate paths */

	_cairo_stroke_style_max_distance_from_path (style, ctm, &dx, &dy);

	bounder.extents.p1.x = bounder.current_point.x - _cairo_fixed_from_double (dx);
	bounder.extents.p2.x = bounder.current_point.x + _cairo_fixed_from_double (dx);
	bounder.extents.p1.y = bounder.current_point.y - _cairo_fixed_from_double (dy);
	bounder.extents.p2.y = bounder.current_point.y + _cairo_fixed_from_double (dy);

	_cairo_box_round_to_rectangle (&bounder.extents, extents);
    } else {
	extents->x = extents->y = 0;
	extents->width = extents->height = 0;
    }
}
static void
_cairo_stroker_limit (cairo_stroker_t *stroker,
		      const cairo_path_fixed_t *path,
		      const cairo_box_t *boxes,
		      int num_boxes)
{
    double dx, dy;
    cairo_fixed_t fdx, fdy;

    stroker->has_bounds = TRUE;
    _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);

    /* Extend the bounds in each direction to account for the maximum area
     * we might generate trapezoids, to capture line segments that are outside
     * of the bounds but which might generate rendering that's within bounds.
     */

    _cairo_stroke_style_max_distance_from_path (&stroker->style, path,
						stroker->ctm, &dx, &dy);

    fdx = _cairo_fixed_from_double (dx);
    fdy = _cairo_fixed_from_double (dy);

    stroker->bounds.p1.x -= fdx;
    stroker->bounds.p2.x += fdx;

    stroker->bounds.p1.y -= fdy;
    stroker->bounds.p2.y += fdy;
}
static cairo_status_t
_cairo_stroker_init (cairo_stroker_t		*stroker,
		     cairo_stroke_style_t	*stroke_style,
		     cairo_matrix_t		*ctm,
		     cairo_matrix_t		*ctm_inverse,
		     double			 tolerance,
		     cairo_traps_t		*traps)
{
    cairo_status_t status;
    stroker->style = stroke_style;
    stroker->ctm = ctm;
    stroker->ctm_inverse = ctm_inverse;
    stroker->tolerance = tolerance;
    stroker->traps = traps;

    stroker->ctm_determinant = _cairo_matrix_compute_determinant (stroker->ctm);
    stroker->ctm_det_positive = stroker->ctm_determinant >= 0.0;

    status = _cairo_pen_init (&stroker->pen,
		              stroke_style->line_width / 2.0,
			      tolerance, ctm);
    if (status)
	return status;

    stroker->has_current_face = FALSE;
    stroker->has_first_face = FALSE;
    stroker->has_initial_sub_path = FALSE;

    if (stroker->style->dash)
	_cairo_stroker_start_dash (stroker);
    else
	stroker->dashed = FALSE;

    stroker->has_bounds = _cairo_traps_get_limit (traps, &stroker->bounds);
    if (stroker->has_bounds) {
	/* Extend the bounds in each direction to account for the maximum area
	 * we might generate trapezoids, to capture line segments that are outside
	 * of the bounds but which might generate rendering that's within bounds.
	 */
	double dx, dy;
	cairo_fixed_t fdx, fdy;

	_cairo_stroke_style_max_distance_from_path (stroker->style, stroker->ctm, &dx, &dy);

	fdx = _cairo_fixed_from_double (dx);
	fdy = _cairo_fixed_from_double (dy);

	stroker->bounds.p1.x -= fdx;
	stroker->bounds.p2.x += fdx;

	stroker->bounds.p1.y -= fdy;
	stroker->bounds.p2.y += fdy;
    }

    return CAIRO_STATUS_SUCCESS;
}
/* Adjusts the fill extents (above) by the device-space pen.  */
void
_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
        const cairo_stroke_style_t *style,
        const cairo_matrix_t *ctm,
        cairo_rectangle_int_t *extents)
{
    if (path->has_extents) {
        cairo_box_t box_extents;
        double dx, dy;

        _cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);

        box_extents = path->extents;
        box_extents.p1.x -= _cairo_fixed_from_double (dx);
        box_extents.p1.y -= _cairo_fixed_from_double (dy);
        box_extents.p2.x += _cairo_fixed_from_double (dx);
        box_extents.p2.y += _cairo_fixed_from_double (dy);

        _cairo_box_round_to_rectangle (&box_extents, extents);
    } else {
        extents->x = extents->y = 0;
        extents->width = extents->height = 0;
    }
}
Exemple #5
0
/* Adjusts the fill extents (above) by the device-space pen.  */
void
_cairo_path_fixed_approximate_stroke_extents (const cairo_path_fixed_t *path,
					      const cairo_stroke_style_t *style,
					      const cairo_matrix_t *ctm,
					      cairo_bool_t is_vector,
					      cairo_rectangle_int_t *extents)
{
    if (path->has_extents) {
	cairo_box_t box_extents;
	double dx, dy;

	_cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
	if (is_vector)
	{
	    /* When calculating extents for vector surfaces, ensure lines thinner
	     * than the fixed point resolution are not optimized away. */
	    double min = _cairo_fixed_to_double (CAIRO_FIXED_EPSILON*2);
	    if (dx < min)
		dx = min;

	    if (dy < min)
		dy = min;
	}

	box_extents = path->extents;
	box_extents.p1.x -= _cairo_fixed_from_double (dx);
	box_extents.p1.y -= _cairo_fixed_from_double (dy);
	box_extents.p2.x += _cairo_fixed_from_double (dx);
	box_extents.p2.y += _cairo_fixed_from_double (dy);

	_cairo_box_round_to_rectangle (&box_extents, extents);
    } else {
	extents->x = extents->y = 0;
	extents->width = extents->height = 0;
    }
}
cairo_status_t
_cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t	*path,
				     const cairo_stroke_style_t	*style,
				     const cairo_matrix_t	*ctm,
				     const cairo_matrix_t	*ctm_inverse,
				     double		 tolerance,
				     cairo_polygon_t *polygon)
{
    struct stroker stroker;
    cairo_status_t status;

    if (style->num_dashes) {
	return _cairo_path_fixed_stroke_dashed_to_polygon (path,
							   style,
							   ctm,
							   ctm_inverse,
							   tolerance,
							   polygon);
    }

    stroker.has_bounds = polygon->num_limits;
    if (stroker.has_bounds) {
	/* Extend the bounds in each direction to account for the maximum area
	 * we might generate trapezoids, to capture line segments that are
	 * outside of the bounds but which might generate rendering that's
	 * within bounds.
	 */
	double dx, dy;
	cairo_fixed_t fdx, fdy;
	int i;

	stroker.bounds = polygon->limits[0];
	for (i = 1; i < polygon->num_limits; i++)
	     _cairo_box_add_box (&stroker.bounds, &polygon->limits[i]);

	_cairo_stroke_style_max_distance_from_path (style, path, ctm, &dx, &dy);
	fdx = _cairo_fixed_from_double (dx);
	fdy = _cairo_fixed_from_double (dy);

	stroker.bounds.p1.x -= fdx;
	stroker.bounds.p2.x += fdx;
	stroker.bounds.p1.y -= fdy;
	stroker.bounds.p2.y += fdy;
    }

    stroker.style = *style;
    stroker.ctm = ctm;
    stroker.ctm_inverse = ctm_inverse;
    stroker.tolerance = tolerance;
    stroker.half_line_width = style->line_width / 2.;
    /* To test whether we need to join two segments of a spline using
     * a round-join or a bevel-join, we can inspect the angle between the
     * two segments. If the difference between the chord distance
     * (half-line-width times the cosine of the bisection angle) and the
     * half-line-width itself is greater than tolerance then we need to
     * inject a point.
     */
    stroker.spline_cusp_tolerance = 1 - tolerance / stroker.half_line_width;
    stroker.spline_cusp_tolerance *= stroker.spline_cusp_tolerance;
    stroker.spline_cusp_tolerance *= 2;
    stroker.spline_cusp_tolerance -= 1;
    stroker.ctm_det_positive =
	_cairo_matrix_compute_determinant (ctm) >= 0.0;

    stroker.pen.num_vertices = 0;
    if (path->has_curve_to ||
	style->line_join == CAIRO_LINE_JOIN_ROUND ||
	style->line_cap == CAIRO_LINE_CAP_ROUND) {
	status = _cairo_pen_init (&stroker.pen,
				  stroker.half_line_width,
				  tolerance, ctm);
	if (unlikely (status))
	    return status;

	/* 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;
    }

    stroker.has_current_face = FALSE;
    stroker.has_first_face = FALSE;
    stroker.has_initial_sub_path = FALSE;

#if DEBUG
    remove ("contours.txt");
    remove ("polygons.txt");
    _cairo_contour_init (&stroker.path, 0);
#endif
    _cairo_contour_init (&stroker.cw.contour, 1);
    _cairo_contour_init (&stroker.ccw.contour, -1);
    tolerance *= CAIRO_FIXED_ONE;
    tolerance *= tolerance;
    stroker.contour_tolerance = tolerance;
    stroker.polygon = polygon;

    status = _cairo_path_fixed_interpret (path,
					  move_to,
					  line_to,
					  curve_to,
					  close_path,
					  &stroker);
    /* Cap the start and end of the final sub path as needed */
    if (likely (status == CAIRO_STATUS_SUCCESS))
	add_caps (&stroker);

    _cairo_contour_fini (&stroker.cw.contour);
    _cairo_contour_fini (&stroker.ccw.contour);
    if (stroker.pen.num_vertices)
	_cairo_pen_fini (&stroker.pen);

#if DEBUG
    {
	FILE *file = fopen ("polygons.txt", "a");
	_cairo_debug_print_polygon (file, polygon);
	fclose (file);
    }
#endif

    return status;
}