cairo_int_status_t _cairo_path_fixed_stroke_to_tristrip (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_tristrip_t *strip) { struct stroker stroker; cairo_int_status_t status; int i; if (style->num_dashes) return CAIRO_INT_STATUS_UNSUPPORTED; stroker.style = *style; stroker.ctm = ctm; stroker.ctm_inverse = ctm_inverse; stroker.tolerance = tolerance; stroker.ctm_det_positive = _cairo_matrix_compute_determinant (ctm) >= 0.0; status = _cairo_pen_init (&stroker.pen, style->line_width / 2.0, tolerance, ctm); if (unlikely (status)) return status; if (stroker.pen.num_vertices <= 1) return CAIRO_INT_STATUS_NOTHING_TO_DO; stroker.has_current_face = FALSE; stroker.has_first_face = FALSE; stroker.has_sub_path = FALSE; stroker.has_limits = strip->num_limits > 0; stroker.limit = strip->limits[0]; for (i = 1; i < strip->num_limits; i++) _cairo_box_add_box (&stroker.limit, &strip->limits[i]); stroker.strip = strip; 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_INT_STATUS_SUCCESS)) add_caps (&stroker); _cairo_pen_fini (&stroker.pen); return status; }
cairo_status_t _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate) { int i; int reflect; double det; if (pen->num_vertices) { /* XXX: It would be nice to notice that the pen is already properly constructed. However, this test would also have to account for possible changes in the transformation matrix. if (pen->radius == radius && pen->tolerance == tolerance) return CAIRO_STATUS_SUCCESS; */ _cairo_pen_fini (pen); } pen->radius = radius; pen->tolerance = gstate->tolerance; _cairo_matrix_compute_determinant (&gstate->ctm, &det); if (det >= 0) { reflect = 0; } else { reflect = 1; } pen->num_vertices = _cairo_pen_vertices_needed (gstate->tolerance, radius, &gstate->ctm); pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t)); if (pen->vertices == NULL) { return CAIRO_STATUS_NO_MEMORY; } /* * Compute pen coordinates. To generate the right ellipse, compute points around * a circle in user space and transform them to device space. To get a consistent * orientation in device space, flip the pen if the transformation matrix * is reflecting */ for (i=0; i < pen->num_vertices; i++) { double theta = 2 * M_PI * i / (double) pen->num_vertices; double dx = radius * cos (reflect ? -theta : theta); double dy = radius * sin (reflect ? -theta : theta); cairo_pen_vertex_t *v = &pen->vertices[i]; cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy); v->point.x = _cairo_fixed_from_double (dx); v->point.y = _cairo_fixed_from_double (dy); } _cairo_pen_compute_slopes (pen); return CAIRO_STATUS_SUCCESS; }
static void _cairo_stroker_fini (cairo_stroker_t *stroker) { _cairo_pen_fini (&stroker->pen); }
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; }
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.style = *style; stroker.ctm = ctm; stroker.ctm_inverse = ctm_inverse; stroker.tolerance = tolerance; 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, style->line_width / 2.0, 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; }
static cairo_status_t _cairo_stroker_curve_to (void *closure, cairo_point_t *b, cairo_point_t *c, cairo_point_t *d) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_stroker_t *stroker = closure; cairo_spline_t spline; cairo_pen_t 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; status = _cairo_spline_init (&spline, a, b, c, d); if (status == CAIRO_INT_STATUS_DEGENERATE) return _cairo_stroker_line_to (closure, d); status = _cairo_pen_init_copy (&pen, &stroker->pen); if (status) goto CLEANUP_SPLINE; initial_slope_dx = _cairo_fixed_to_double (spline.initial_slope.dx); initial_slope_dy = _cairo_fixed_to_double (spline.initial_slope.dy); final_slope_dx = _cairo_fixed_to_double (spline.final_slope.dx); final_slope_dy = _cairo_fixed_to_double (spline.final_slope.dy); if (_compute_normalized_device_slope (&initial_slope_dx, &initial_slope_dy, stroker->ctm_inverse, NULL)) _compute_face (a, &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.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 (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 (&pen, extra_points, 4); if (status) goto CLEANUP_PEN; status = _cairo_pen_stroke_spline (&pen, &spline, stroker->tolerance, stroker->traps); if (status) goto CLEANUP_PEN; CLEANUP_PEN: _cairo_pen_fini (&pen); CLEANUP_SPLINE: _cairo_spline_fini (&spline); stroker->current_point = *d; return status; }