cairo_status_t _cairo_scaled_font_glyph_path (cairo_scaled_font_t *scaled_font, cairo_glyph_t *glyphs, int num_glyphs, cairo_path_fixed_t *path) { cairo_status_t status; int i; cairo_scaled_glyph_path_closure_t closure; if (scaled_font->status) return scaled_font->status; closure.path = path; for (i = 0; i < num_glyphs; i++) { cairo_scaled_glyph_t *scaled_glyph; status = _cairo_scaled_glyph_lookup (scaled_font, glyphs[i].index, CAIRO_SCALED_GLYPH_INFO_PATH, &scaled_glyph); if (status) return status; closure.offset.x = _cairo_fixed_from_double (glyphs[i].x); closure.offset.y = _cairo_fixed_from_double (glyphs[i].y); status = _cairo_path_fixed_interpret (scaled_glyph->path, CAIRO_DIRECTION_FORWARD, _scaled_glyph_path_move_to, _scaled_glyph_path_line_to, _scaled_glyph_path_curve_to, _scaled_glyph_path_close_path, &closure); } return CAIRO_STATUS_SUCCESS; }
/* The line cap value is needed to workaround the fact that PostScript * and PDF semantics for stroking degenerate sub-paths do not match * cairo semantics. (PostScript draws something for any line cap * value, while cairo draws something only for round caps). * * When using this function to emit a path to be filled, rather than * stroked, simply pass %CAIRO_LINE_CAP_ROUND which will guarantee that * the stroke workaround will not modify the path being emitted. */ static cairo_status_t _cairo_pdf_operators_emit_path (cairo_pdf_operators_t *pdf_operators, const cairo_path_fixed_t*path, cairo_matrix_t *path_transform, cairo_line_cap_t line_cap) { cairo_output_stream_t *word_wrap; cairo_status_t status, status2; pdf_path_info_t info; cairo_box_t box; word_wrap = _word_wrap_stream_create (pdf_operators->stream, pdf_operators->ps_output, 72); status = _cairo_output_stream_get_status (word_wrap); if (unlikely (status)) return _cairo_output_stream_destroy (word_wrap); info.output = word_wrap; info.path_transform = path_transform; info.line_cap = line_cap; if (_cairo_path_fixed_is_rectangle (path, &box) && ((path_transform->xx == 0 && path_transform->yy == 0) || (path_transform->xy == 0 && path_transform->yx == 0))) { status = _cairo_pdf_path_rectangle (&info, &box); } else { status = _cairo_path_fixed_interpret (path, _cairo_pdf_path_move_to, _cairo_pdf_path_line_to, _cairo_pdf_path_curve_to, _cairo_pdf_path_close_path, &info); } status2 = _cairo_output_stream_destroy (word_wrap); if (status == CAIRO_STATUS_SUCCESS) status = status2; return status; }
cairo_status_t _cairo_path_fixed_interpret_flat (const cairo_path_fixed_t *path, cairo_direction_t dir, cairo_path_fixed_move_to_func_t *move_to, cairo_path_fixed_line_to_func_t *line_to, cairo_path_fixed_close_path_func_t *close_path, void *closure, double tolerance) { cpf_t flattener; flattener.tolerance = tolerance; flattener.move_to = move_to; flattener.line_to = line_to; flattener.close_path = close_path; flattener.closure = closure; return _cairo_path_fixed_interpret (path, dir, _cpf_move_to, _cpf_line_to, _cpf_curve_to, _cpf_close_path, &flattener); }
/* 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); }
static int _cairo_path_count (cairo_path_t *path, cairo_path_fixed_t *path_fixed, double tolerance, cairo_bool_t flatten) { cairo_status_t status; cpc_t cpc; cpc.count = 0; cpc.current_point.x = 0; cpc.current_point.y = 0; if (flatten) { status = _cairo_path_fixed_interpret_flat (path_fixed, CAIRO_DIRECTION_FORWARD, _cpc_move_to, _cpc_line_to, _cpc_close_path, &cpc, tolerance); } else { status = _cairo_path_fixed_interpret (path_fixed, CAIRO_DIRECTION_FORWARD, _cpc_move_to, _cpc_line_to, _cpc_curve_to, _cpc_close_path, &cpc); } if (unlikely (status)) return -1; return cpc.count; }
static cairo_status_t _cairo_path_populate (cairo_path_t *path, cairo_path_fixed_t *path_fixed, cairo_t *cr, cairo_bool_t flatten) { cairo_status_t status; cpp_t cpp; cpp.data = path->data; cpp.cr = cr; if (flatten) { status = _cairo_path_fixed_interpret_flat (path_fixed, _cpp_move_to, _cpp_line_to, _cpp_close_path, &cpp, cairo_get_tolerance (cr)); } else { status = _cairo_path_fixed_interpret (path_fixed, _cpp_move_to, _cpp_line_to, _cpp_curve_to, _cpp_close_path, &cpp); } if (unlikely (status)) return status; /* Sanity check the count */ XASSERT (cpp.data - path->data == path->num_data); return CAIRO_STATUS_SUCCESS; }
/* A slightly better approximation than above - we actually decompose the * Bezier, but we continue to ignore winding. */ void _cairo_path_fixed_approximate_fill_extents (const cairo_path_fixed_t *path, 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) { _cairo_box_round_to_rectangle (&bounder.extents, extents); } else { extents->x = extents->y = 0; extents->width = extents->height = 0; } }
cairo_status_t _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path, cairo_fill_rule_t fill_rule, double tolerance, cairo_traps_t *traps) { cairo_status_t status = CAIRO_STATUS_SUCCESS; cairo_filler_t filler; _cairo_filler_init (&filler, tolerance, traps); status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, _cairo_filler_move_to, _cairo_filler_line_to, _cairo_filler_curve_to, _cairo_filler_close_path, &filler); if (status) goto BAIL; status = _cairo_polygon_close (&filler.polygon); if (status) goto BAIL; status = _cairo_traps_tessellate_polygon (filler.traps, &filler.polygon, fill_rule); if (status) goto BAIL; BAIL: _cairo_filler_fini (&filler); return status; }
cairo_int_status_t _cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path, const cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, cairo_antialias_t antialias, cairo_boxes_t *boxes) { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; cairo_box_t box; assert (_cairo_path_fixed_stroke_is_rectilinear (path)); if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, ctm, antialias, boxes)) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! rectilinear_stroker.dash.dashed && _cairo_path_fixed_is_stroke_box (path, &box) && /* if the segments overlap we need to feed them into the tessellator */ box.p2.x - box.p1.x > 2* rectilinear_stroker.half_line_x && box.p2.y - box.p1.y > 2* rectilinear_stroker.half_line_y) { cairo_box_t b; /* top */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y - rectilinear_stroker.half_line_y; b.p2.y = box.p1.y + rectilinear_stroker.half_line_y; status = (cairo_int_status_t)_cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* left (excluding top/bottom) */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p1.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; status = (cairo_int_status_t)_cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* right (excluding top/bottom) */ b.p1.x = box.p2.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p1.y + rectilinear_stroker.half_line_y; b.p2.y = box.p2.y - rectilinear_stroker.half_line_y; status = (cairo_int_status_t)_cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); /* bottom */ b.p1.x = box.p1.x - rectilinear_stroker.half_line_x; b.p2.x = box.p2.x + rectilinear_stroker.half_line_x; b.p1.y = box.p2.y - rectilinear_stroker.half_line_y; b.p2.y = box.p2.y + rectilinear_stroker.half_line_y; status = (cairo_int_status_t)_cairo_boxes_add (boxes, antialias, &b); assert (status == CAIRO_INT_STATUS_SUCCESS); goto done; } if (boxes->num_limits) { _cairo_rectilinear_stroker_limit (&rectilinear_stroker, boxes->limits, boxes->num_limits); } status = (cairo_int_status_t)_cairo_path_fixed_interpret (path, _cairo_rectilinear_stroker_move_to, rectilinear_stroker.dash.dashed ? _cairo_rectilinear_stroker_line_to_dashed : _cairo_rectilinear_stroker_line_to, NULL, _cairo_rectilinear_stroker_close_path, &rectilinear_stroker); if (unlikely (status)) goto BAIL; if (rectilinear_stroker.dash.dashed) status = (cairo_int_status_t)_cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); else status = (cairo_int_status_t)_cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); if (unlikely (status)) goto BAIL; /* As we incrementally tessellate, we do not eliminate self-intersections */ status = (cairo_int_status_t)_cairo_bentley_ottmann_tessellate_boxes (boxes, CAIRO_FILL_RULE_WINDING, boxes); if (unlikely (status)) goto BAIL; done: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); return (cairo_int_status_t)CAIRO_STATUS_SUCCESS; BAIL: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); _cairo_boxes_clear (boxes); 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.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_int_status_t _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path, cairo_stroke_style_t *stroke_style, const cairo_matrix_t *ctm, cairo_traps_t *traps) { cairo_rectilinear_stroker_t rectilinear_stroker; cairo_int_status_t status; /* This special-case rectilinear stroker only supports * miter-joined lines (not curves) and a translation-only matrix * (though it could probably be extended to support a matrix with * uniform, integer scaling). * * It also only supports horizontal and vertical line_to * elements. But we don't catch that here, but instead return * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any * non-rectilinear line_to is encountered. */ if (path->has_curve_to) return CAIRO_INT_STATUS_UNSUPPORTED; if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER) return CAIRO_INT_STATUS_UNSUPPORTED; /* If the miter limit turns right angles into bevels, then we * can't use this optimization. Remember, the ratio is * 1/sin(ɸ/2). So the cutoff is 1/sin(π/4.0) or ⎷2, * which we round for safety. */ if (stroke_style->miter_limit < M_SQRT2) return CAIRO_INT_STATUS_UNSUPPORTED; if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT || stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE)) { return CAIRO_INT_STATUS_UNSUPPORTED; } if (! (_cairo_matrix_is_identity (ctm) || _cairo_matrix_is_translation (ctm))) { return CAIRO_INT_STATUS_UNSUPPORTED; } _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, ctm, traps); if (traps->has_limits) { _cairo_rectilinear_stroker_limit (&rectilinear_stroker, &traps->limits); } status = _cairo_path_fixed_interpret (path, CAIRO_DIRECTION_FORWARD, _cairo_rectilinear_stroker_move_to, rectilinear_stroker.dash.dashed ? _cairo_rectilinear_stroker_line_to_dashed : _cairo_rectilinear_stroker_line_to, NULL, _cairo_rectilinear_stroker_close_path, &rectilinear_stroker); if (unlikely (status)) goto BAIL; if (rectilinear_stroker.dash.dashed) status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker); else status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker); BAIL: _cairo_rectilinear_stroker_fini (&rectilinear_stroker); if (unlikely (status)) _cairo_traps_clear (traps); return status; }