diff options
-rw-r--r-- | src/cairo-fixed-private.h | 34 | ||||
-rw-r--r-- | src/cairo-path-stroke.c | 90 |
2 files changed, 116 insertions, 8 deletions
diff --git a/src/cairo-fixed-private.h b/src/cairo-fixed-private.h index b6cc6be7..352ca39d 100644 --- a/src/cairo-fixed-private.h +++ b/src/cairo-fixed-private.h @@ -352,6 +352,40 @@ _cairo_edge_compute_intersection_x_for_y (const cairo_point_t *p1, return x; } +/* Intersect two segments based on the algorithm described at + * http://paulbourke.net/geometry/pointlineplane/. This implementation + * uses floating point math. */ +static inline cairo_bool_t +_slow_segment_intersection (const cairo_point_t *seg1_p1, + const cairo_point_t *seg1_p2, + const cairo_point_t *seg2_p1, + const cairo_point_t *seg2_p2, + cairo_point_t *intersection) +{ + double denominator, u_a, u_b; + double seg1_dx, seg1_dy, seg2_dx, seg2_dy, seg_start_dx, seg_start_dy; + + seg1_dx = _cairo_fixed_to_double (seg1_p2->x - seg1_p1->x); + seg1_dy = _cairo_fixed_to_double (seg1_p2->y - seg1_p1->y); + seg2_dx = _cairo_fixed_to_double (seg2_p2->x - seg2_p1->x); + seg2_dy = _cairo_fixed_to_double (seg2_p2->y - seg2_p1->y); + denominator = (seg2_dy * seg1_dx) - (seg2_dx * seg1_dy); + if (denominator == 0) + return FALSE; + + seg_start_dx = _cairo_fixed_to_double (seg1_p1->x - seg2_p1->x); + seg_start_dy = _cairo_fixed_to_double (seg1_p1->y - seg2_p1->y); + u_a = ((seg2_dx * seg_start_dy) - (seg2_dy * seg_start_dx)) / denominator; + u_b = ((seg1_dx * seg_start_dy) - (seg1_dy * seg_start_dx)) / denominator; + + if (u_a <= 0 || u_a >= 1 || u_b <= 0 || u_b >= 1) + return FALSE; + + intersection->x = seg1_p1->x + _cairo_fixed_from_double ((u_a * seg1_dx)); + intersection->y = seg1_p1->y + _cairo_fixed_from_double ((u_a * seg1_dy)); + return TRUE; +} + #else # error Please define multiplication and other operands for your fixed-point type size #endif diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c index cd6b3a27..6fae093d 100644 --- a/src/cairo-path-stroke.c +++ b/src/cairo-path-stroke.c @@ -761,9 +761,12 @@ _compute_normalized_device_slope (double *dx, double *dy, } static void -_compute_face (const cairo_point_t *point, cairo_slope_t *dev_slope, - double slope_dx, double slope_dy, - cairo_stroker_t *stroker, cairo_stroke_face_t *face) +_compute_face (const cairo_point_t *point, + const cairo_slope_t *dev_slope, + double slope_dx, + double slope_dy, + cairo_stroker_t *stroker, + cairo_stroke_face_t *face) { double face_dx, face_dy; cairo_point_t offset_ccw, offset_cw; @@ -979,6 +982,68 @@ _cairo_stroker_line_to (void *closure, return CAIRO_STATUS_SUCCESS; } +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]; + cairo_point_t intersect_point; + + 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); + + if (_slow_segment_intersection (&stroker->current_face.cw, + &stroker->current_face.ccw, + &new_face.cw, + &new_face.ccw, + &intersect_point)) { + points[0] = stroker->current_face.ccw; + points[1] = new_face.ccw; + points[2] = intersect_point; + stroker->add_triangle (stroker->closure, points); + + points[0] = stroker->current_face.cw; + points[1] = new_face.cw; + stroker->add_triangle (stroker->closure, points); + } else { + points[0] = stroker->current_face.ccw; + points[1] = stroker->current_face.cw; + 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; +} + /* * Dashed lines. Cap each dash end, join around turns when on */ @@ -1135,18 +1200,27 @@ _cairo_stroker_curve_to (void *closure, 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_spline_add_point_func_t line_to; + cairo_spline_add_point_func_t spline_to; cairo_status_t status = CAIRO_STATUS_SUCCESS; line_to = stroker->dash.dashed ? - _cairo_stroker_line_to_dashed : - _cairo_stroker_line_to; + (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed : + (cairo_spline_add_point_func_t) _cairo_stroker_line_to; + + /* spline_to is only capable of rendering non-degenerate splines. */ + spline_to = stroker->dash.dashed ? + (cairo_spline_add_point_func_t) _cairo_stroker_line_to_dashed : + (cairo_spline_add_point_func_t) _cairo_stroker_spline_to; if (! _cairo_spline_init (&spline, - (cairo_spline_add_point_func_t)line_to, stroker, + spline_to, + stroker, &stroker->current_point, b, c, d)) { - return line_to (closure, d); + cairo_slope_t fallback_slope; + _cairo_slope_init (&fallback_slope, &stroker->current_point, d); + return line_to (closure, d, &fallback_slope); } /* If the line width is so small that the pen is reduced to a |