cairo_bool_t
_cairo_matrix_is_integer_translation (const cairo_matrix_t *matrix,
				      int *itx, int *ity)
{
    if (_cairo_matrix_is_translation (matrix))
    {
        cairo_fixed_t x0_fixed = _cairo_fixed_from_double (matrix->x0);
        cairo_fixed_t y0_fixed = _cairo_fixed_from_double (matrix->y0);

        if (_cairo_fixed_is_integer (x0_fixed) &&
            _cairo_fixed_is_integer (y0_fixed))
        {
            if (itx)
                *itx = _cairo_fixed_integer_part (x0_fixed);
            if (ity)
                *ity = _cairo_fixed_integer_part (y0_fixed);

            return TRUE;
        }
    }

    return FALSE;
}
Beispiel #2
0
/* Adjusts the fill extents (above) by the device-space pen.  */
void
_cairo_path_fixed_approximate_stroke_extents (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 {
	extents->x = extents->y = 0;
	extents->width = extents->height = 0;
    }

    _cairo_path_bounder_fini (&bounder);
}
/* 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;
    }
}
Beispiel #4
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;
    }
}
Beispiel #5
0
static void
_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t	*stroker,
				 cairo_stroke_style_t		*stroke_style,
				 const cairo_matrix_t		*ctm,
				 cairo_traps_t			*traps)
{
    stroker->stroke_style = stroke_style;
    stroker->ctm = ctm;

    stroker->half_line_width =
	_cairo_fixed_from_double (stroke_style->line_width / 2.0);
    stroker->traps = traps;
    stroker->open_sub_path = FALSE;
    stroker->segments = stroker->segments_embedded;
    stroker->segments_size = ARRAY_LENGTH (stroker->segments_embedded);
    stroker->num_segments = 0;

    _cairo_stroker_dash_init (&stroker->dash, stroke_style);

    stroker->has_bounds = FALSE;
}
static cairo_status_t
_cairo_xcb_surface_set_matrix (cairo_xcb_surface_t *surface,
			       cairo_matrix_t	   *matrix)
{
    XCBRenderTRANSFORM xtransform;

    if (!surface->picture.xid)
	return CAIRO_STATUS_SUCCESS;

    xtransform.matrix11 = _cairo_fixed_from_double (matrix->xx);
    xtransform.matrix12 = _cairo_fixed_from_double (matrix->xy);
    xtransform.matrix13 = _cairo_fixed_from_double (matrix->x0);

    xtransform.matrix21 = _cairo_fixed_from_double (matrix->yx);
    xtransform.matrix22 = _cairo_fixed_from_double (matrix->yy);
    xtransform.matrix23 = _cairo_fixed_from_double (matrix->y0);

    xtransform.matrix31 = 0;
    xtransform.matrix32 = 0;
    xtransform.matrix33 = _cairo_fixed_from_double (1);

    if (!CAIRO_SURFACE_RENDER_HAS_PICTURE_TRANSFORM (surface))
    {
	static const XCBRenderTRANSFORM identity = {
	    1 << 16, 0x00000, 0x00000,
	    0x00000, 1 << 16, 0x00000,
	    0x00000, 0x00000, 1 << 16
	};

	if (memcmp (&xtransform, &identity, sizeof (XCBRenderTRANSFORM)) == 0)
	    return CAIRO_STATUS_SUCCESS;

	return CAIRO_INT_STATUS_UNSUPPORTED;
    }

    XCBRenderSetPictureTransform (surface->dpy, surface->picture, xtransform);

    return CAIRO_STATUS_SUCCESS;
}
Beispiel #7
0
static OSStatus MyATSCubicCurveToCallback(const Float32Point * pt1,
                                          const Float32Point * pt2,
                                          const Float32Point * pt3,
                                          void *callBackDataPtr)
{
    cairo_ATSUI_glyph_path_callback_info_t *info = callBackDataPtr;
    double scaledPt[2];
    cairo_fixed_t x0, y0;
    cairo_fixed_t x1, y1;
    cairo_fixed_t x2, y2;


    scaledPt[0] = pt1->x;
    scaledPt[1] = pt1->y;

    cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]);

    x0 = _cairo_fixed_from_double(scaledPt[0]);
    y0 = _cairo_fixed_from_double(scaledPt[1]);


    scaledPt[0] = pt2->x;
    scaledPt[1] = pt2->y;

    cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]);

    x1 = _cairo_fixed_from_double(scaledPt[0]);
    y1 = _cairo_fixed_from_double(scaledPt[1]);


    scaledPt[0] = pt3->x;
    scaledPt[1] = pt3->y;

    cairo_matrix_transform_point(&info->scale, &scaledPt[0], &scaledPt[1]);

    x2 = _cairo_fixed_from_double(scaledPt[0]);
    y2 = _cairo_fixed_from_double(scaledPt[1]);


    _cairo_path_fixed_curve_to(info->path, x0, y0, x1, y1, x2, y2);


    return noErr;
}
Beispiel #8
0
static void _cairo_matrix_to_render_matrix (const cairo_matrix_t *matrix, s_render_matrix_t *render_matrix)
{
	static const s_render_matrix_t render_identity_matrix = {{
		{1 << 16,        0,       0},
		{       0, 1 << 16,       0},
		{       0,       0, 1 << 16}
	}};
	if (_cairo_matrix_is_identity(matrix)) {
		*render_matrix = render_identity_matrix;
	} else {
		render_matrix->matrix[0][0] = _cairo_fixed_from_double(matrix->xx);
		render_matrix->matrix[0][1] = _cairo_fixed_from_double(matrix->xy);
		render_matrix->matrix[0][2] = _cairo_fixed_from_double(matrix->x0);
		render_matrix->matrix[1][0] = _cairo_fixed_from_double(matrix->yx);
		render_matrix->matrix[1][1] = _cairo_fixed_from_double(matrix->yy);
		render_matrix->matrix[1][2] = _cairo_fixed_from_double(matrix->y0);
		render_matrix->matrix[2][0] = 0;
		render_matrix->matrix[2][1] = 0;
		render_matrix->matrix[2][2] = 1 << 16;
	}
}
Beispiel #9
0
static cairo_status_t
_cairo_stroker_add_cap (cairo_stroker_t *stroker,
			const cairo_stroke_face_t *f)
{
    switch (stroker->style.line_cap) {
    case CAIRO_LINE_CAP_ROUND: {
	cairo_slope_t slope;

	slope.dx = -f->dev_vector.dx;
	slope.dy = -f->dev_vector.dy;

	return _tessellate_fan (stroker,
				&f->dev_vector,
				&slope,
				&f->point, &f->cw, &f->ccw,
				FALSE);

    }

    case CAIRO_LINE_CAP_SQUARE: {
	double dx, dy;
	cairo_slope_t	fvector;
	cairo_point_t	quad[4];

	dx = f->usr_vector.x;
	dy = f->usr_vector.y;
	dx *= stroker->style.line_width / 2.0;
	dy *= stroker->style.line_width / 2.0;
	cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
	fvector.dx = _cairo_fixed_from_double (dx);
	fvector.dy = _cairo_fixed_from_double (dy);

	quad[0] = f->ccw;
	quad[1].x = f->ccw.x + fvector.dx;
	quad[1].y = f->ccw.y + fvector.dy;
	quad[2].x = f->cw.x + fvector.dx;
	quad[2].y = f->cw.y + fvector.dy;
	quad[3] = f->cw;

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

	    status = stroker->add_external_edge (stroker->closure,
						 &quad[0], &quad[1]);
	    if (unlikely (status))
		return status;

	    status = stroker->add_external_edge (stroker->closure,
						 &quad[1], &quad[2]);
	    if (unlikely (status))
		return status;

	    status = stroker->add_external_edge (stroker->closure,
						 &quad[2], &quad[3]);
	    if (unlikely (status))
		return status;

	    return CAIRO_STATUS_SUCCESS;
	} else {
	    return stroker->add_convex_quad (stroker->closure, quad);
	}
    }

    case CAIRO_LINE_CAP_BUTT:
    default:
	if (stroker->add_external_edge != NULL) {
	    return stroker->add_external_edge (stroker->closure,
					       &f->ccw, &f->cw);
	} else {
	    return CAIRO_STATUS_SUCCESS;
	}
    }
}
Beispiel #10
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;
}
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;
}
Beispiel #12
0
/**
 * _cairo_path_append_to_context:
 * @path: the path data to be appended
 * @cr: a cairo context
 *
 * Append @path to the current path within @cr.
 *
 * Return value: %CAIRO_STATUS_INVALID_PATH_DATA if the data in @path
 * is invalid, and %CAIRO_STATUS_SUCCESS otherwise.
 **/
cairo_status_t
_cairo_path_append_to_context (const cairo_path_t	*path,
			       cairo_t			*cr)
{
    const cairo_path_data_t *p, *end;
    cairo_fixed_t x1_fixed, y1_fixed;
    cairo_fixed_t x2_fixed, y2_fixed;
    cairo_fixed_t x3_fixed, y3_fixed;
    cairo_matrix_t user_to_backend;
    cairo_status_t status;
    double x, y;

    user_to_backend = cr->gstate->ctm;
    cairo_matrix_multiply (&user_to_backend,
			   &user_to_backend,
	                   &cr->gstate->target->device_transform);

    end = &path->data[path->num_data];
    for (p = &path->data[0]; p < end; p += p->header.length) {
	switch (p->header.type) {
	case CAIRO_PATH_MOVE_TO:
	    if (unlikely (p->header.length < 2))
		return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);

	    x = p[1].point.x, y = p[1].point.y;
	    cairo_matrix_transform_point (&user_to_backend, &x, &y);
	    x1_fixed = _cairo_fixed_from_double (x);
	    y1_fixed = _cairo_fixed_from_double (y);

	    status = _cairo_path_fixed_move_to (cr->path, x1_fixed, y1_fixed);
	    break;

	case CAIRO_PATH_LINE_TO:
	    if (unlikely (p->header.length < 2))
		return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);

	    x = p[1].point.x, y = p[1].point.y;
	    cairo_matrix_transform_point (&user_to_backend, &x, &y);
	    x1_fixed = _cairo_fixed_from_double (x);
	    y1_fixed = _cairo_fixed_from_double (y);

	    status = _cairo_path_fixed_line_to (cr->path, x1_fixed, y1_fixed);
	    break;

	case CAIRO_PATH_CURVE_TO:
	    if (unlikely (p->header.length < 4))
		return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);

	    x = p[1].point.x, y = p[1].point.y;
	    cairo_matrix_transform_point (&user_to_backend, &x, &y);
	    x1_fixed = _cairo_fixed_from_double (x);
	    y1_fixed = _cairo_fixed_from_double (y);

	    x = p[2].point.x, y = p[2].point.y;
	    cairo_matrix_transform_point (&user_to_backend, &x, &y);
	    x2_fixed = _cairo_fixed_from_double (x);
	    y2_fixed = _cairo_fixed_from_double (y);

	    x = p[3].point.x, y = p[3].point.y;
	    cairo_matrix_transform_point (&user_to_backend, &x, &y);
	    x3_fixed = _cairo_fixed_from_double (x);
	    y3_fixed = _cairo_fixed_from_double (y);

	    status = _cairo_path_fixed_curve_to (cr->path,
		                                 x1_fixed, y1_fixed,
						 x2_fixed, y2_fixed,
						 x3_fixed, y3_fixed);
	    break;

	case CAIRO_PATH_CLOSE_PATH:
	    if (unlikely (p->header.length < 1))
		return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);

	    status = _cairo_path_fixed_close_path (cr->path);
	    break;

	default:
	    return _cairo_error (CAIRO_STATUS_INVALID_PATH_DATA);
	}

	if (unlikely (status))
	    return status;
    }

    return CAIRO_STATUS_SUCCESS;
}
static void
add_cap (struct stroker *stroker,
	 const cairo_stroke_face_t *f,
	 struct stroke_contour *c)
{

    //TODO DONE add LINE_CAP_TRIANGULAR

    switch (stroker->style.line_cap) {
    case CAIRO_LINE_CAP_ROUND: {
	cairo_slope_t slope;

	slope.dx = -f->dev_vector.dx;
	slope.dy = -f->dev_vector.dy;

	add_fan (stroker, &f->dev_vector, &slope, &f->point, FALSE, c);
	break;
    }

    case CAIRO_LINE_CAP_SQUARE: {
	cairo_slope_t fvector;
	cairo_point_t p;
	double dx, dy;

	dx = f->usr_vector.x;
	dy = f->usr_vector.y;
	dx *= stroker->half_line_width;
	dy *= stroker->half_line_width;
	cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
	fvector.dx = _cairo_fixed_from_double (dx);
	fvector.dy = _cairo_fixed_from_double (dy);

	p.x = f->ccw.x + fvector.dx;
	p.y = f->ccw.y + fvector.dy;
	contour_add_point (stroker, c, &p);

	p.x = f->cw.x + fvector.dx;
	p.y = f->cw.y + fvector.dy;
	contour_add_point (stroker, c, &p);
	break;
    }

    case CAIRO_LINE_CAP_TRIANGULAR: {
	cairo_slope_t fvector;
	cairo_point_t p;
	double dx, dy;

	printf("cairo-path-stroke-polygon.c clear\n");

	dx = f->usr_vector.x;
	dy = f->usr_vector.y;
	dx *= stroker->half_line_width;
	dy *= stroker->half_line_width;
	cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
	fvector.dx = _cairo_fixed_from_double (dx);
	fvector.dy = _cairo_fixed_from_double (dy);

	p.x = (f->ccw.x + f->cw.x) / 2 + fvector.dx;
	p.y = (f->ccw.y + f->cw.y) / 2 + fvector.dy;
	contour_add_point (stroker, c, &p);
	break;
    }

    case CAIRO_LINE_CAP_BUTT:
    default:
	break;
    }
    contour_add_point (stroker, c, &f->cw);
}
static void
add_cap (struct stroker *stroker,
         const cairo_stroke_face_t *f)
{
    switch (stroker->style.line_cap) {
    case CAIRO_LINE_CAP_ROUND: {
        cairo_slope_t slope;

        slope.dx = -f->dev_vector.dx;
        slope.dy = -f->dev_vector.dy;

        add_fan (stroker, &f->dev_vector, &slope,
                 &f->point, &f->ccw, &f->cw,
                 FALSE);
        break;
    }

    case CAIRO_LINE_CAP_SQUARE: {
        double dx, dy;
        cairo_slope_t	fvector;
        cairo_point_t	quad[4];

        dx = f->usr_vector.x;
        dy = f->usr_vector.y;
        dx *= stroker->style.line_width / 2.0;
        dy *= stroker->style.line_width / 2.0;
        cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
        fvector.dx = _cairo_fixed_from_double (dx);
        fvector.dy = _cairo_fixed_from_double (dy);

        quad[0] = f->ccw;
        quad[1].x = f->ccw.x + fvector.dx;
        quad[1].y = f->ccw.y + fvector.dy;
        quad[2].x = f->cw.x + fvector.dx;
        quad[2].y = f->cw.y + fvector.dy;
        quad[3] = f->cw;

        //contour_add_point (stroker, c, &quad[1]);
        //contour_add_point (stroker, c, &quad[2]);
        break;
    }

    case CAIRO_LINE_CAP_TRIANGULAR: {
        double dx, dy;
        cairo_slope_t	fvector;
        cairo_point_t	quad[4];

        dx = f->usr_vector.x;
        dy = f->usr_vector.y;
        dx *= stroker->style.line_width / 2.0;
        dy *= stroker->style.line_width / 2.0;
        cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
        fvector.dx = _cairo_fixed_from_double (dx);
        fvector.dy = _cairo_fixed_from_double (dy);

        /* can we remove point quad[2]? */
        quad[0] = f->ccw;
        quad[1].x = (f->ccw.x + f->cw.x)/2 + fvector.dx;
        quad[1].y = (f->ccw.y + f->cw.y)/2 + fvector.dy;
        quad[2].x = quad[1].x;
        quad[2].y = quad[1].y;
        quad[3] = f->cw;

        //contour_add_point (stroker, c, &quad[1]);
        //contour_add_point (stroker, c, &quad[2]);
        break;
    }

    case CAIRO_LINE_CAP_BUTT:
    default:
        break;
    }
    //contour_add_point (stroker, c, &f->cw);
}
Beispiel #15
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;
}
Beispiel #16
0
    cairo_fixed_t GetFixedY(const D2D1_POINT_2F &point)
    {
	unsigned int control_word;
	_controlfp_s(&control_word, _CW_DEFAULT, MCW_PC);
	return _cairo_fixed_from_double(point.y);
    }
static void
_cairo_quartz_path_apply_func (void *info, const CGPathElement *el)
{
    cairo_path_fixed_t *path = (cairo_path_fixed_t *) info;
    cairo_status_t status;

    switch (el->type) {
	case kCGPathElementMoveToPoint:
	    status = _cairo_path_fixed_move_to (path,
						_cairo_fixed_from_double(el->points[0].x),
						_cairo_fixed_from_double(el->points[0].y));
	    assert(!status);
	    break;
	case kCGPathElementAddLineToPoint:
	    status = _cairo_path_fixed_line_to (path,
						_cairo_fixed_from_double(el->points[0].x),
						_cairo_fixed_from_double(el->points[0].y));
	    assert(!status);
	    break;
	case kCGPathElementAddQuadCurveToPoint: {
	    cairo_fixed_t fx, fy;
	    double x, y;
	    if (!_cairo_path_fixed_get_current_point (path, &fx, &fy))
		fx = fy = 0;
	    x = _cairo_fixed_to_double (fx);
	    y = _cairo_fixed_to_double (fy);

	    status = _cairo_path_fixed_curve_to (path,
						 _cairo_fixed_from_double((x + el->points[0].x * 2.0) / 3.0),
						 _cairo_fixed_from_double((y + el->points[0].y * 2.0) / 3.0),
						 _cairo_fixed_from_double((el->points[0].x * 2.0 + el->points[1].x) / 3.0),
						 _cairo_fixed_from_double((el->points[0].y * 2.0 + el->points[1].y) / 3.0),
						 _cairo_fixed_from_double(el->points[1].x),
						 _cairo_fixed_from_double(el->points[1].y));
	}
	    assert(!status);
	    break;
	case kCGPathElementAddCurveToPoint:
	    status = _cairo_path_fixed_curve_to (path,
						 _cairo_fixed_from_double(el->points[0].x),
						 _cairo_fixed_from_double(el->points[0].y),
						 _cairo_fixed_from_double(el->points[1].x),
						 _cairo_fixed_from_double(el->points[1].y),
						 _cairo_fixed_from_double(el->points[2].x),
						 _cairo_fixed_from_double(el->points[2].y));
	    assert(!status);	    
	    break;
	case kCGPathElementCloseSubpath:
	    status = _cairo_path_fixed_close_path (path);
	    assert(!status);
	    break;
    }
}
Beispiel #18
0
static int
_line_segs_intersect_ceil (cairo_line_t *l1, cairo_line_t *l2, cairo_fixed_t *y_ret)
{
    /*
     * x = m1y + b1
     * x = m2y + b2
     * m1y + b1 = m2y + b2
     * y * (m1 - m2) = b2 - b1
     * y = (b2 - b1) / (m1 - m2)
     */
    cairo_fixed_16_16_t y_intersect;
    double  m1 = _compute_inverse_slope (l1);
    double  b1 = _compute_x_intercept (l1, m1);
    double  m2 = _compute_inverse_slope (l2);
    double  b2 = _compute_x_intercept (l2, m2);

    if (m1 == m2)
	return 0;

    y_intersect = _cairo_fixed_from_double ((b2 - b1) / (m1 - m2));

    if (m1 < m2) {
	cairo_line_t *t;
	t = l1;
	l1 = l2;
	l2 = t;
    }

    /* Assuming 56 bits of floating point precision, the intersection
       is accurate within one sub-pixel coordinate. We must ensure
       that we return a value that is at or after the intersection. At
       most, we must increment once. */
    if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect))
	y_intersect++;
    /* XXX: Hmm... Keith's error calculations said we'd at most be off
       by one sub-pixel. But, I found that the paint-fill-BE-01.svg
       test from the W3C SVG conformance suite definitely requires two
       increments.

       It could be that we need one to overcome the error, and another
       to round up.

       It would be nice to be sure this code is correct, (but we can't
       do the while loop as it will work for way to long on
       exceedingly distant intersections with large errors that we
       really don't care about anyway as they will be ignored by the
       calling function.
    */
    if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect))
	y_intersect++;
    /* XXX: hmm... now I found "intersection_killer" inside xrspline.c
       that requires 3 increments. Clearly, we haven't characterized
       this completely yet. */
    if (_compute_x (l2, y_intersect) > _compute_x (l1, y_intersect))
	y_intersect++;
    /* I think I've found the answer to our problems. The insight is
       that everytime we round we are changing the slopes of the
       relevant lines, so we may be introducing new intersections that
       we miss, so everything breaks apart. John Hobby wrote a paper
       on how to fix this:

       [Hobby93c] John D. Hobby, Practical Segment Intersection with
       Finite Precision Output, Computation Geometry Theory and
       Applications, 13(4), 1999.

       Available online (2003-08017):

       http://cm.bell-labs.com/cm/cs/doc/93/2-27.ps.gz

       Now we just need to go off and implement that.
    */

    *y_ret = y_intersect;

    return 1;
}
Beispiel #19
0
/**
 * cairo_scaled_glyph_set_metrics:
 * @scaled_glyph: a #cairo_scaled_glyph_t
 * @scaled_font: a #cairo_scaled_font_t
 * @fs_metrics: a #cairo_text_extents_t in font space
 * 
 * _cairo_scaled_glyph_set_metrics() stores user space metrics
 * for the specified glyph given font space metrics. It is 
 * called by the font backend when initializing a glyph with
 * CAIRO_SCALED_GLYPH_INFO_METRICS.
 **/
void
_cairo_scaled_glyph_set_metrics (cairo_scaled_glyph_t *scaled_glyph,
				 cairo_scaled_font_t *scaled_font,
				 cairo_text_extents_t *fs_metrics)
{
    cairo_bool_t first = TRUE;
    double hm, wm;
    double min_user_x = 0.0, max_user_x = 0.0, min_user_y = 0.0, max_user_y = 0.0;
    double min_device_x = 0.0, max_device_x = 0.0, min_device_y = 0.0, max_device_y = 0.0;
    
    for (hm = 0.0; hm <= 1.0; hm += 1.0)
	for (wm = 0.0; wm <= 1.0; wm += 1.0) {
	    double x, y;

	    /* Transform this corner to user space */
	    x = fs_metrics->x_bearing + fs_metrics->width * wm;
	    y = fs_metrics->y_bearing + fs_metrics->height * hm;
	    cairo_matrix_transform_point (&scaled_font->font_matrix,
					  &x, &y);
	    if (first) {
		min_user_x = max_user_x = x;
		min_user_y = max_user_y = y;
	    } else {
		if (x < min_user_x) min_user_x = x;
		if (x > max_user_x) max_user_x = x;
		if (y < min_user_y) min_user_y = y;
		if (y > max_user_y) max_user_y = y;
	    }

	    /* Transform this corner to device space from glyph origin */
	    x = fs_metrics->x_bearing + fs_metrics->width * wm;
	    y = fs_metrics->y_bearing + fs_metrics->height * hm;
	    cairo_matrix_transform_distance (&scaled_font->scale,
					     &x, &y);
	    
	    if (first) {
		min_device_x = max_device_x = x;
		min_device_y = max_device_y = y;
	    } else {
		if (x < min_device_x) min_device_x = x;
		if (x > max_device_x) max_device_x = x;
		if (y < min_device_y) min_device_y = y;
		if (y > max_device_y) max_device_y = y;
	    }
	    first = FALSE;
	}
    scaled_glyph->metrics.x_bearing = min_user_x;
    scaled_glyph->metrics.y_bearing = min_user_y;
    scaled_glyph->metrics.width = max_user_x - min_user_x;
    scaled_glyph->metrics.height = max_user_y - min_user_y;

    scaled_glyph->metrics.x_advance = fs_metrics->x_advance;
    scaled_glyph->metrics.y_advance = fs_metrics->y_advance;
    cairo_matrix_transform_point (&scaled_font->font_matrix,
				  &scaled_glyph->metrics.x_advance,
				  &scaled_glyph->metrics.y_advance);

    scaled_glyph->bbox.p1.x = _cairo_fixed_from_double (min_device_x);
    scaled_glyph->bbox.p1.y = _cairo_fixed_from_double (min_device_y);
    scaled_glyph->bbox.p2.x = _cairo_fixed_from_double (max_device_x);
    scaled_glyph->bbox.p2.y = _cairo_fixed_from_double (max_device_y);
}
static void
outer_join (struct stroker *stroker,
            const cairo_stroke_face_t *in,
            const cairo_stroke_face_t *out,
            int clockwise)
{
    const cairo_point_t	*inpt, *outpt;

    if (in->cw.x == out->cw.x && in->cw.y == out->cw.y &&
            in->ccw.x == out->ccw.x && in->ccw.y == out->ccw.y)
    {
        return;
    }
    if (clockwise) {
        inpt = &in->cw;
        outpt = &out->cw;
    } else {
        inpt = &in->ccw;
        outpt = &out->ccw;
    }

    switch (stroker->style.line_join) {
    case CAIRO_LINE_JOIN_ROUND:
        /* construct a fan around the common midpoint */
        add_fan (stroker,
                 &in->dev_vector,
                 &out->dev_vector,
                 &in->point, inpt, outpt,
                 clockwise);
        break;

    case CAIRO_LINE_JOIN_MITER:
    default: {
        /* dot product of incoming slope vector with outgoing slope vector */
        double	in_dot_out = -in->usr_vector.x * out->usr_vector.x +
                             -in->usr_vector.y * out->usr_vector.y;
        double	ml = stroker->style.miter_limit;

        /* Check the miter limit -- lines meeting at an acute angle
         * can generate long miters, the limit converts them to bevel
         *
         * Consider the miter join formed when two line segments
         * meet at an angle psi:
         *
         *	   /.\
         *	  /. .\
         *	 /./ \.\
         *	/./psi\.\
         *
         * We can zoom in on the right half of that to see:
         *
         *	    |\
         *	    | \ psi/2
         *	    |  \
         *	    |   \
         *	    |    \
         *	    |     \
         *	  miter    \
         *	 length     \
         *	    |        \
         *	    |        .\
         *	    |    .     \
         *	    |.   line   \
         *	     \    width  \
         *	      \           \
         *
         *
         * The right triangle in that figure, (the line-width side is
         * shown faintly with three '.' characters), gives us the
         * following expression relating miter length, angle and line
         * width:
         *
         *	1 /sin (psi/2) = miter_length / line_width
         *
         * The right-hand side of this relationship is the same ratio
         * in which the miter limit (ml) is expressed. We want to know
         * when the miter length is within the miter limit. That is
         * when the following condition holds:
         *
         *	1/sin(psi/2) <= ml
         *	1 <= ml sin(psi/2)
         *	1 <= ml² sin²(psi/2)
         *	2 <= ml² 2 sin²(psi/2)
         *				2·sin²(psi/2) = 1-cos(psi)
         *	2 <= ml² (1-cos(psi))
         *
         *				in · out = |in| |out| cos (psi)
         *
         * in and out are both unit vectors, so:
         *
         *				in · out = cos (psi)
         *
         *	2 <= ml² (1 - in · out)
         *
         */
        if (2 <= ml * ml * (1 - in_dot_out)) {
            double		x1, y1, x2, y2;
            double		mx, my;
            double		dx1, dx2, dy1, dy2;
            double		ix, iy;
            double		fdx1, fdy1, fdx2, fdy2;
            double		mdx, mdy;

            /*
             * we've got the points already transformed to device
             * space, but need to do some computation with them and
             * also need to transform the slope from user space to
             * device space
             */
            /* outer point of incoming line face */
            x1 = _cairo_fixed_to_double (inpt->x);
            y1 = _cairo_fixed_to_double (inpt->y);
            dx1 = in->usr_vector.x;
            dy1 = in->usr_vector.y;
            cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);

            /* outer point of outgoing line face */
            x2 = _cairo_fixed_to_double (outpt->x);
            y2 = _cairo_fixed_to_double (outpt->y);
            dx2 = out->usr_vector.x;
            dy2 = out->usr_vector.y;
            cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);

            /*
             * Compute the location of the outer corner of the miter.
             * That's pretty easy -- just the intersection of the two
             * outer edges.  We've got slopes and points on each
             * of those edges.  Compute my directly, then compute
             * mx by using the edge with the larger dy; that avoids
             * dividing by values close to zero.
             */
            my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
                  (dx1 * dy2 - dx2 * dy1));
            if (fabs (dy1) >= fabs (dy2))
                mx = (my - y1) * dx1 / dy1 + x1;
            else
                mx = (my - y2) * dx2 / dy2 + x2;

            /*
             * When the two outer edges are nearly parallel, slight
             * perturbations in the position of the outer points of the lines
             * caused by representing them in fixed point form can cause the
             * intersection point of the miter to move a large amount. If
             * that moves the miter intersection from between the two faces,
             * then draw a bevel instead.
             */

            ix = _cairo_fixed_to_double (in->point.x);
            iy = _cairo_fixed_to_double (in->point.y);

            /* slope of one face */
            fdx1 = x1 - ix;
            fdy1 = y1 - iy;

            /* slope of the other face */
            fdx2 = x2 - ix;
            fdy2 = y2 - iy;

            /* slope from the intersection to the miter point */
            mdx = mx - ix;
            mdy = my - iy;

            /*
             * Make sure the miter point line lies between the two
             * faces by comparing the slopes
             */
            if (slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
                    slope_compare_sgn (fdx2, fdy2, mdx, mdy))
            {
                cairo_point_t p;

                p.x = _cairo_fixed_from_double (mx);
                p.y = _cairo_fixed_from_double (my);

                //*_cairo_contour_last_point (&outer->contour) = p;
                return;
            }
        }
        break;
    }

    case CAIRO_LINE_JOIN_BEVEL:
        break;
    }
    //contour_add_point (stroker,outer, outpt);
}
Beispiel #21
0
static cairo_status_t
_cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_stroke_face_t *out)
{
    int			clockwise = _cairo_stroker_face_clockwise (out, in);
    cairo_point_t	*inpt, *outpt;
    cairo_status_t status;

    if (in->cw.x == out->cw.x
	&& in->cw.y == out->cw.y
	&& in->ccw.x == out->ccw.x
	&& in->ccw.y == out->ccw.y)
    {
	return CAIRO_STATUS_SUCCESS;
    }

    if (clockwise) {
	inpt = &in->ccw;
	outpt = &out->ccw;
    } else {
	inpt = &in->cw;
	outpt = &out->cw;
    }

    switch (stroker->style->line_join) {
    case CAIRO_LINE_JOIN_ROUND: {
	int i;
	int start, step, stop;
	cairo_point_t tri[3];
	cairo_pen_t *pen = &stroker->pen;

	tri[0] = in->point;
	if (clockwise) {
	    start =
		_cairo_pen_find_active_ccw_vertex_index (pen, &in->dev_vector);
	    stop =
		_cairo_pen_find_active_ccw_vertex_index (pen, &out->dev_vector);
	    step = -1;
	} else {
	    start =
		_cairo_pen_find_active_cw_vertex_index (pen, &in->dev_vector);
	    stop =
		_cairo_pen_find_active_cw_vertex_index (pen, &out->dev_vector);
	    step = +1;
	}

	i = start;
	tri[1] = *inpt;
	while (i != stop) {
	    tri[2] = in->point;
	    _translate_point (&tri[2], &pen->vertices[i].point);
	    status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
	    if (unlikely (status))
		return status;
	    tri[1] = tri[2];
	    i += step;
	    if (i < 0)
		i = pen->num_vertices - 1;
	    if (i >= pen->num_vertices)
		i = 0;
	}

	tri[2] = *outpt;

	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
    }
    case CAIRO_LINE_JOIN_MITER:
    default: {
	/* dot product of incoming slope vector with outgoing slope vector */
	double	in_dot_out = ((-in->usr_vector.x * out->usr_vector.x)+
			      (-in->usr_vector.y * out->usr_vector.y));
	double	ml = stroker->style->miter_limit;

	/* Check the miter limit -- lines meeting at an acute angle
	 * can generate long miters, the limit converts them to bevel
	 *
	 * Consider the miter join formed when two line segments
	 * meet at an angle psi:
	 *
	 *	   /.\
	 *	  /. .\
	 *	 /./ \.\
	 *	/./psi\.\
	 *
	 * We can zoom in on the right half of that to see:
	 *
	 *	    |\
	 *	    | \ psi/2
	 *	    |  \
	 *	    |   \
	 *	    |    \
	 *	    |     \
	 *	  miter    \
	 *	 length     \
	 *	    |        \
	 *	    |        .\
	 *	    |    .     \
	 *	    |.   line   \
	 *	     \    width  \
	 *	      \           \
	 *
	 *
	 * The right triangle in that figure, (the line-width side is
	 * shown faintly with three '.' characters), gives us the
	 * following expression relating miter length, angle and line
	 * width:
	 *
	 *	1 /sin (psi/2) = miter_length / line_width
	 *
	 * The right-hand side of this relationship is the same ratio
	 * in which the miter limit (ml) is expressed. We want to know
	 * when the miter length is within the miter limit. That is
	 * when the following condition holds:
	 *
	 *	1/sin(psi/2) <= ml
	 *	1 <= ml sin(psi/2)
	 *	1 <= ml² sin²(psi/2)
	 *	2 <= ml² 2 sin²(psi/2)
	 *				2·sin²(psi/2) = 1-cos(psi)
	 *	2 <= ml² (1-cos(psi))
	 *
	 *				in · out = |in| |out| cos (psi)
	 *
	 * in and out are both unit vectors, so:
	 *
	 *				in · out = cos (psi)
	 *
	 *	2 <= ml² (1 - in · out)
	 *
	 */
	if (2 <= ml * ml * (1 - in_dot_out))
	{
	    double		x1, y1, x2, y2;
	    double		mx, my;
	    double		dx1, dx2, dy1, dy2;
	    cairo_point_t	outer;
	    cairo_point_t	quad[4];
	    double		ix, iy;
	    double		fdx1, fdy1, fdx2, fdy2;
	    double		mdx, mdy;

	    /*
	     * we've got the points already transformed to device
	     * space, but need to do some computation with them and
	     * also need to transform the slope from user space to
	     * device space
	     */
	    /* outer point of incoming line face */
	    x1 = _cairo_fixed_to_double (inpt->x);
	    y1 = _cairo_fixed_to_double (inpt->y);
	    dx1 = in->usr_vector.x;
	    dy1 = in->usr_vector.y;
	    cairo_matrix_transform_distance (stroker->ctm, &dx1, &dy1);

	    /* outer point of outgoing line face */
	    x2 = _cairo_fixed_to_double (outpt->x);
	    y2 = _cairo_fixed_to_double (outpt->y);
	    dx2 = out->usr_vector.x;
	    dy2 = out->usr_vector.y;
	    cairo_matrix_transform_distance (stroker->ctm, &dx2, &dy2);

	    /*
	     * Compute the location of the outer corner of the miter.
	     * That's pretty easy -- just the intersection of the two
	     * outer edges.  We've got slopes and points on each
	     * of those edges.  Compute my directly, then compute
	     * mx by using the edge with the larger dy; that avoids
	     * dividing by values close to zero.
	     */
	    my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) /
		  (dx1 * dy2 - dx2 * dy1));
	    if (fabs (dy1) >= fabs (dy2))
		mx = (my - y1) * dx1 / dy1 + x1;
	    else
		mx = (my - y2) * dx2 / dy2 + x2;

	    /*
	     * When the two outer edges are nearly parallel, slight
	     * perturbations in the position of the outer points of the lines
	     * caused by representing them in fixed point form can cause the
	     * intersection point of the miter to move a large amount. If
	     * that moves the miter intersection from between the two faces,
	     * then draw a bevel instead.
	     */

	    ix = _cairo_fixed_to_double (in->point.x);
	    iy = _cairo_fixed_to_double (in->point.y);

	    /* slope of one face */
	    fdx1 = x1 - ix; fdy1 = y1 - iy;

	    /* slope of the other face */
	    fdx2 = x2 - ix; fdy2 = y2 - iy;

	    /* slope from the intersection to the miter point */
	    mdx = mx - ix; mdy = my - iy;

	    /*
	     * Make sure the miter point line lies between the two
	     * faces by comparing the slopes
	     */
	    if (_cairo_slope_compare_sgn (fdx1, fdy1, mdx, mdy) !=
		_cairo_slope_compare_sgn (fdx2, fdy2, mdx, mdy))
	    {
		/*
		 * Draw the quadrilateral
		 */
		outer.x = _cairo_fixed_from_double (mx);
		outer.y = _cairo_fixed_from_double (my);

		quad[0] = in->point;
		quad[1] = *inpt;
		quad[2] = outer;
		quad[3] = *outpt;

		return _cairo_traps_tessellate_convex_quad (stroker->traps, quad);
	    }
	}
	/* fall through ... */
    }
    case CAIRO_LINE_JOIN_BEVEL: {
	cairo_point_t tri[3];
	tri[0] = in->point;
	tri[1] = *inpt;
	tri[2] = *outpt;

	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
    }
    }
}
static void
compute_face (const cairo_point_t *point,
              const cairo_slope_t *dev_slope,
              struct stroker *stroker,
              cairo_stroke_face_t *face)
{
    double face_dx, face_dy;
    cairo_point_t offset_ccw, offset_cw;
    double slope_dx, slope_dy;

    slope_dx = _cairo_fixed_to_double (dev_slope->dx);
    slope_dy = _cairo_fixed_to_double (dev_slope->dy);
    face->length = normalize_slope (&slope_dx, &slope_dy);
    face->dev_slope.x = slope_dx;
    face->dev_slope.y = slope_dy;

    /*
     * rotate to get a line_width/2 vector along the face, note that
     * the vector must be rotated the right direction in device space,
     * but by 90° in user space. So, the rotation depends on
     * whether the ctm reflects or not, and that can be determined
     * by looking at the determinant of the matrix.
     */
    if (! _cairo_matrix_is_identity (stroker->ctm_inverse)) {
        /* Normalize the matrix! */
        cairo_matrix_transform_distance (stroker->ctm_inverse,
                                         &slope_dx, &slope_dy);
        normalize_slope (&slope_dx, &slope_dy);

        if (stroker->ctm_det_positive) {
            face_dx = - slope_dy * (stroker->style.line_width / 2.0);
            face_dy = slope_dx * (stroker->style.line_width / 2.0);
        } else {
            face_dx = slope_dy * (stroker->style.line_width / 2.0);
            face_dy = - slope_dx * (stroker->style.line_width / 2.0);
        }

        /* back to device space */
        cairo_matrix_transform_distance (stroker->ctm, &face_dx, &face_dy);
    } else {
        face_dx = - slope_dy * (stroker->style.line_width / 2.0);
        face_dy = slope_dx * (stroker->style.line_width / 2.0);
    }

    offset_ccw.x = _cairo_fixed_from_double (face_dx);
    offset_ccw.y = _cairo_fixed_from_double (face_dy);
    offset_cw.x = -offset_ccw.x;
    offset_cw.y = -offset_ccw.y;

    face->ccw = *point;
    translate_point (&face->ccw, &offset_ccw);

    face->point = *point;

    face->cw = *point;
    translate_point (&face->cw, &offset_cw);

    face->usr_vector.x = slope_dx;
    face->usr_vector.y = slope_dy;

    face->dev_vector = *dev_slope;
}
Beispiel #23
0
static cairo_status_t
_cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f)
{
    cairo_status_t	    status;

    if (stroker->style->line_cap == CAIRO_LINE_CAP_BUTT)
	return CAIRO_STATUS_SUCCESS;

    switch (stroker->style->line_cap) {
    case CAIRO_LINE_CAP_ROUND: {
	int i;
	int start, stop;
	cairo_slope_t slope;
	cairo_point_t tri[3];
	cairo_pen_t *pen = &stroker->pen;

	slope = f->dev_vector;
	start = _cairo_pen_find_active_cw_vertex_index (pen, &slope);
	slope.dx = -slope.dx;
	slope.dy = -slope.dy;
	stop = _cairo_pen_find_active_cw_vertex_index (pen, &slope);

	tri[0] = f->point;
	tri[1] = f->cw;
	for (i=start; i != stop; i = (i+1) % pen->num_vertices) {
	    tri[2] = f->point;
	    _translate_point (&tri[2], &pen->vertices[i].point);
	    status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
	    if (unlikely (status))
		return status;
	    tri[1] = tri[2];
	}
	tri[2] = f->ccw;

	return _cairo_traps_tessellate_triangle (stroker->traps, tri);
    }
    case CAIRO_LINE_CAP_SQUARE: {
	double dx, dy;
	cairo_slope_t	fvector;
	cairo_point_t	occw, ocw;
	cairo_polygon_t	polygon;

	dx = f->usr_vector.x;
	dy = f->usr_vector.y;
	dx *= stroker->style->line_width / 2.0;
	dy *= stroker->style->line_width / 2.0;
	cairo_matrix_transform_distance (stroker->ctm, &dx, &dy);
	fvector.dx = _cairo_fixed_from_double (dx);
	fvector.dy = _cairo_fixed_from_double (dy);
	occw.x = f->ccw.x + fvector.dx;
	occw.y = f->ccw.y + fvector.dy;
	ocw.x = f->cw.x + fvector.dx;
	ocw.y = f->cw.y + fvector.dy;

	_cairo_polygon_init (&polygon);
	_cairo_polygon_move_to (&polygon, &f->cw);
	_cairo_polygon_line_to (&polygon, &ocw);
	_cairo_polygon_line_to (&polygon, &occw);
	_cairo_polygon_line_to (&polygon, &f->ccw);
	_cairo_polygon_close (&polygon);
	status = _cairo_polygon_status (&polygon);

	if (status == CAIRO_STATUS_SUCCESS) {
	    status = _cairo_bentley_ottmann_tessellate_polygon (stroker->traps,
								&polygon,
								CAIRO_FILL_RULE_WINDING);
	}

	_cairo_polygon_fini (&polygon);

	return status;
    }
    case CAIRO_LINE_CAP_BUTT:
    default:
	return CAIRO_STATUS_SUCCESS;
    }
}