[cairo-commit] src/cairoint.h src/cairo-matrix.c src/cairo-path-stroke.c src/cairo-pen.c src/cairo-stroke-style.c test/leaky-dashed-rectangle.pdf.ref.png test/leaky-dashed-rectangle.ps2.ref.png test/leaky-dashed-rectangle.ps3.ref.png test/leaky-dashed-rectangle.ps.ref.png test/leaky-dashed-rectangle.ref.png test/Makefile.am
Chris Wilson
ickle at kemper.freedesktop.org
Thu Jan 29 07:00:37 PST 2009
dev/null |binary
src/cairo-matrix.c | 3
src/cairo-path-stroke.c | 492 ++++++++++++++++++++++++++------
src/cairo-pen.c | 11
src/cairo-stroke-style.c | 18 -
src/cairoint.h | 11
test/Makefile.am | 4
test/leaky-dashed-rectangle.pdf.ref.png |binary
test/leaky-dashed-rectangle.ps.ref.png |binary
test/leaky-dashed-rectangle.ref.png |binary
10 files changed, 437 insertions(+), 102 deletions(-)
New commits:
commit dd4276c6618aa250637e4499bc7cb0a35b24448c
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sun Nov 30 13:49:24 2008 +0000
[stroker] Rectilinear dashing.
Extend the rectilinear stroker to handle dashes, so that for pixel-aligned
dashed strokes we completely avoid tessellation overhead.
diff --git a/src/cairo-matrix.c b/src/cairo-matrix.c
index b617472..b9e7290 100644
--- a/src/cairo-matrix.c
+++ b/src/cairo-matrix.c
@@ -839,7 +839,8 @@ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix)
/* determine the length of the major axis of a circle of the given radius
after applying the transformation matrix. */
double
-_cairo_matrix_transformed_circle_major_axis (cairo_matrix_t *matrix, double radius)
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+ double radius)
{
double a, b, c, d, f, g, h, i, j;
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 2fba174..d6e5790 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -38,11 +38,23 @@
#include "cairoint.h"
#include "cairo-path-fixed-private.h"
+typedef struct _cairo_stroker_dash {
+ cairo_bool_t dashed;
+ unsigned int dash_index;
+ cairo_bool_t dash_on;
+ cairo_bool_t dash_starts_on;
+ double dash_remain;
+
+ double dash_offset;
+ const double *dashes;
+ unsigned int num_dashes;
+} cairo_stroker_dash_t;
+
typedef struct cairo_stroker {
- cairo_stroke_style_t *style;
+ cairo_stroke_style_t *style;
- cairo_matrix_t *ctm;
- cairo_matrix_t *ctm_inverse;
+ const cairo_matrix_t *ctm;
+ const cairo_matrix_t *ctm_inverse;
double tolerance;
double ctm_determinant;
cairo_bool_t ctm_det_positive;
@@ -62,62 +74,77 @@ typedef struct cairo_stroker {
cairo_bool_t has_first_face;
cairo_stroke_face_t first_face;
- cairo_bool_t dashed;
- unsigned int dash_index;
- cairo_bool_t dash_on;
- cairo_bool_t dash_starts_on;
- double dash_remain;
+ cairo_stroker_dash_t dash;
cairo_bool_t has_bounds;
cairo_box_t bounds;
} cairo_stroker_t;
static void
-_cairo_stroker_start_dash (cairo_stroker_t *stroker)
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
{
double offset;
cairo_bool_t on = TRUE;
unsigned int i = 0;
- offset = stroker->style->dash_offset;
+ if (! dash->dashed)
+ return;
+
+ offset = dash->dash_offset;
/* We stop searching for a starting point as soon as the
offset reaches zero. Otherwise when an initial dash
segment shrinks to zero it will be skipped over. */
- while (offset > 0.0 && offset >= stroker->style->dash[i]) {
- offset -= stroker->style->dash[i];
+ while (offset > 0.0 && offset >= dash->dashes[i]) {
+ offset -= dash->dashes[i];
on = !on;
- if (++i == stroker->style->num_dashes)
+ if (++i == dash->num_dashes)
i = 0;
}
- stroker->dashed = TRUE;
- stroker->dash_index = i;
- stroker->dash_on = stroker->dash_starts_on = on;
- stroker->dash_remain = stroker->style->dash[i] - offset;
+
+ dash->dash_index = i;
+ dash->dash_on = dash->dash_starts_on = on;
+ dash->dash_remain = dash->dashes[i] - offset;
}
static void
-_cairo_stroker_step_dash (cairo_stroker_t *stroker, double step)
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
{
- stroker->dash_remain -= step;
- if (stroker->dash_remain <= 0) {
- stroker->dash_index++;
- if (stroker->dash_index == stroker->style->num_dashes)
- stroker->dash_index = 0;
- stroker->dash_on = !stroker->dash_on;
- stroker->dash_remain = stroker->style->dash[stroker->dash_index];
+ dash->dash_remain -= step;
+ if (dash->dash_remain <= 0.) {
+ if (++dash->dash_index == dash->num_dashes)
+ dash->dash_index = 0;
+
+ dash->dash_on = ! dash->dash_on;
+ dash->dash_remain = dash->dashes[dash->dash_index];
}
}
+static void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+ const cairo_stroke_style_t *style)
+{
+ dash->dashed = style->dash != NULL;
+ if (! dash->dashed)
+ return;
+
+ dash->dashes = style->dash;
+ dash->num_dashes = style->num_dashes;
+ dash->dash_offset = style->dash_offset;
+
+ _cairo_stroker_dash_start (dash);
+}
+
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,
+ const cairo_matrix_t *ctm,
+ const 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;
@@ -137,10 +164,7 @@ _cairo_stroker_init (cairo_stroker_t *stroker,
stroker->has_first_face = FALSE;
stroker->has_initial_sub_path = FALSE;
- if (stroker->style->dash)
- _cairo_stroker_start_dash (stroker);
- else
- stroker->dashed = FALSE;
+ _cairo_stroker_dash_init (&stroker->dash, stroke_style);
stroker->has_bounds = _cairo_traps_get_limit (traps, &stroker->bounds);
if (stroker->has_bounds) {
@@ -151,14 +175,15 @@ _cairo_stroker_init (cairo_stroker_t *stroker,
double dx, dy;
cairo_fixed_t fdx, fdy;
- _cairo_stroke_style_max_distance_from_path (stroker->style, stroker->ctm, &dx, &dy);
+ _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;
+ fdy = _cairo_fixed_from_double (dy);
stroker->bounds.p1.y -= fdy;
stroker->bounds.p2.y += fdy;
}
@@ -222,11 +247,11 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
}
if (clockwise) {
- inpt = &in->ccw;
- outpt = &out->ccw;
+ inpt = &in->ccw;
+ outpt = &out->ccw;
} else {
- inpt = &in->cw;
- outpt = &out->cw;
+ inpt = &in->cw;
+ outpt = &out->cw;
}
switch (stroker->style->line_join) {
@@ -257,7 +282,7 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
tri[2] = in->point;
_translate_point (&tri[2], &pen->vertices[i].point);
status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
- if (status)
+ if (unlikely (status))
return status;
tri[1] = tri[2];
i += step;
@@ -297,7 +322,7 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
* | \
* | \
* | \
- * miter \
+ * miter \
* length \
* | \
* | .\
@@ -464,7 +489,7 @@ _cairo_stroker_add_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f)
tri[2] = f->point;
_translate_point (&tri[2], &pen->vertices[i].point);
status = _cairo_traps_tessellate_triangle (stroker->traps, tri);
- if (status)
+ if (unlikely (status))
return status;
tri[1] = tri[2];
}
@@ -543,7 +568,9 @@ _cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker,
}
static inline cairo_bool_t
-_compute_normalized_device_slope (double *dx, double *dy, cairo_matrix_t *ctm_inverse, double *mag_out)
+_compute_normalized_device_slope (double *dx, double *dy,
+ const cairo_matrix_t *ctm_inverse,
+ double *mag_out)
{
double dx0 = *dx, dy0 = *dy;
double mag;
@@ -575,7 +602,7 @@ _compute_normalized_device_slope (double *dx, double *dy, cairo_matrix_t *ctm_in
*dx = -1.0;
}
} else {
- mag = sqrt (dx0 * dx0 + dy0 * dy0);
+ mag = hypot (dx0, dy0);
*dx = dx0 / mag;
*dy = dy0 / mag;
}
@@ -716,8 +743,8 @@ static cairo_status_t
_cairo_stroker_move_to (void *closure,
const cairo_point_t *point)
{
- cairo_status_t status;
cairo_stroker_t *stroker = closure;
+ cairo_status_t status;
/* Cap the start and end of the previous sub path as needed */
status = _cairo_stroker_add_caps (stroker);
@@ -738,9 +765,10 @@ static cairo_status_t
_cairo_stroker_move_to_dashed (void *closure,
const cairo_point_t *point)
{
- /* reset the dash pattern for new sub paths */
cairo_stroker_t *stroker = closure;
- _cairo_stroker_start_dash (stroker);
+
+ /* reset the dash pattern for new sub paths */
+ _cairo_stroker_dash_start (&stroker->dash);
return _cairo_stroker_move_to (closure, point);
}
@@ -810,7 +838,7 @@ _cairo_stroker_line_to_dashed (void *closure,
cairo_bool_t fully_in_bounds;
cairo_status_t status;
- stroker->has_initial_sub_path = stroker->dash_starts_on;
+ stroker->has_initial_sub_path = stroker->dash.dash_starts_on;
if (p1->x == p2->x && p1->y == p2->y)
return CAIRO_STATUS_SUCCESS;
@@ -837,7 +865,7 @@ _cairo_stroker_line_to_dashed (void *closure,
remain = mag;
segment.p1 = *p1;
while (remain) {
- step_length = MIN (stroker->dash_remain, remain);
+ step_length = MIN (stroker->dash.dash_remain, remain);
remain -= step_length;
dx2 = slope_dx * (mag - remain);
dy2 = slope_dy * (mag - remain);
@@ -845,9 +873,9 @@ _cairo_stroker_line_to_dashed (void *closure,
segment.p2.x = _cairo_fixed_from_double (dx2) + p1->x;
segment.p2.y = _cairo_fixed_from_double (dy2) + p1->y;
- if (stroker->dash_on &&
+ if (stroker->dash.dash_on &&
(fully_in_bounds ||
- (! stroker->has_first_face && stroker->dash_starts_on) ||
+ (! stroker->has_first_face && stroker->dash.dash_starts_on) ||
_cairo_box_intersects_line_segment (&stroker->bounds, &segment)))
{
status = _cairo_stroker_add_sub_edge (stroker,
@@ -858,7 +886,8 @@ _cairo_stroker_line_to_dashed (void *closure,
if (unlikely (status))
return status;
- if (stroker->has_current_face) {
+ if (stroker->has_current_face)
+ {
/* Join with final face from previous segment */
status = _cairo_stroker_join (stroker,
&stroker->current_face,
@@ -867,11 +896,16 @@ _cairo_stroker_line_to_dashed (void *closure,
return status;
stroker->has_current_face = FALSE;
- } else if (! stroker->has_first_face && stroker->dash_starts_on) {
+ }
+ 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 {
+ }
+ else
+ {
/* Cap dash start if not connecting to a previous segment */
status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
if (unlikely (status))
@@ -899,11 +933,11 @@ _cairo_stroker_line_to_dashed (void *closure,
}
}
- _cairo_stroker_step_dash (stroker, step_length);
+ _cairo_stroker_dash_step (&stroker->dash, step_length);
segment.p1 = segment.p2;
}
- if (stroker->dash_on && ! stroker->has_current_face) {
+ 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.
*
@@ -1048,9 +1082,7 @@ _cairo_stroker_curve_to_dashed (void *closure,
cairo_status_t status;
if (! _cairo_spline_init (&spline,
- stroker->dashed ?
- _cairo_stroker_line_to_dashed :
- _cairo_stroker_line_to,
+ _cairo_stroker_line_to_dashed,
stroker,
a, b, c, d))
{
@@ -1080,7 +1112,7 @@ _cairo_stroker_close_path (void *closure)
cairo_status_t status;
cairo_stroker_t *stroker = closure;
- if (stroker->dashed)
+ if (stroker->dash.dashed)
status = _cairo_stroker_line_to_dashed (stroker, &stroker->first_point);
else
status = _cairo_stroker_line_to (stroker, &stroker->first_point);
@@ -1109,14 +1141,14 @@ _cairo_stroker_close_path (void *closure)
static cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
- cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm,
cairo_traps_t *traps);
cairo_status_t
_cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
- cairo_matrix_t *ctm,
- cairo_matrix_t *ctm_inverse,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_traps_t *traps)
{
@@ -1169,26 +1201,56 @@ BAIL:
return status;
}
-typedef struct _cairo_rectilinear_stroker
-{
+typedef struct _segment_t {
+ cairo_point_t p1, p2;
+ cairo_bool_t is_horizontal;
+ cairo_bool_t has_join;
+} segment_t;
+
+typedef struct _cairo_rectilinear_stroker {
cairo_stroke_style_t *stroke_style;
+ const cairo_matrix_t *ctm;
+
cairo_fixed_t half_line_width;
cairo_traps_t *traps;
cairo_point_t current_point;
cairo_point_t first_point;
cairo_bool_t open_sub_path;
+
+ cairo_stroker_dash_t dash;
+
+ cairo_bool_t has_bounds;
+ cairo_box_t bounds;
+
int num_segments;
int segments_size;
- cairo_line_t *segments;
- cairo_line_t segments_embedded[8]; /* common case is a single rectangle */
+ segment_t *segments;
+ segment_t segments_embedded[8]; /* common case is a single rectangle */
} cairo_rectilinear_stroker_t;
static void
+_cairo_rectilinear_stroker_limit (cairo_rectilinear_stroker_t *stroker,
+ const cairo_box_t *box)
+{
+ stroker->has_bounds = TRUE;
+ stroker->bounds = *box;
+
+ stroker->bounds.p1.x -= stroker->half_line_width;
+ stroker->bounds.p2.x += stroker->half_line_width;
+
+ stroker->bounds.p1.y -= stroker->half_line_width;
+ stroker->bounds.p2.y += stroker->half_line_width;
+}
+
+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;
@@ -1196,6 +1258,10 @@ _cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
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 void
@@ -1206,25 +1272,27 @@ _cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
}
static cairo_status_t
-_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
- const cairo_point_t *p1,
- const cairo_point_t *p2)
+_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
+ const cairo_point_t *p1,
+ const cairo_point_t *p2,
+ cairo_bool_t is_horizontal,
+ cairo_bool_t has_join)
{
if (stroker->num_segments == stroker->segments_size) {
int new_size = stroker->segments_size * 2;
- cairo_line_t *new_segments;
+ segment_t *new_segments;
if (stroker->segments == stroker->segments_embedded) {
- new_segments = _cairo_malloc_ab (new_size, sizeof (cairo_line_t));
+ new_segments = _cairo_malloc_ab (new_size, sizeof (segment_t));
if (unlikely (new_segments == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
memcpy (new_segments, stroker->segments,
- stroker->num_segments * sizeof (cairo_line_t));
+ stroker->num_segments * sizeof (segment_t));
} else {
new_segments = _cairo_realloc_ab (stroker->segments,
- new_size, sizeof (cairo_line_t));
+ new_size, sizeof (segment_t));
if (unlikely (new_segments == NULL))
return _cairo_error (CAIRO_STATUS_NO_MEMORY);
}
@@ -1235,6 +1303,8 @@ _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
stroker->segments[stroker->num_segments].p1 = *p1;
stroker->segments[stroker->num_segments].p2 = *p2;
+ stroker->segments[stroker->num_segments].has_join = has_join;
+ stroker->segments[stroker->num_segments].is_horizontal = is_horizontal;
stroker->num_segments++;
return CAIRO_STATUS_SUCCESS;
@@ -1359,16 +1429,142 @@ _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
}
static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments_dashed (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_status_t status;
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_width = stroker->half_line_width;
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_point_t *a, *b;
+ cairo_bool_t is_horizontal;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ is_horizontal = stroker->segments[i].is_horizontal;
+
+ /* Handle the joins for a potentially degenerate segment. */
+ if (line_cap == CAIRO_LINE_CAP_BUTT &&
+ stroker->segments[i].has_join &&
+ (i != stroker->num_segments - 1 ||
+ (! stroker->open_sub_path && stroker->dash.dash_starts_on)))
+ {
+ cairo_point_t p1 = stroker->segments[i].p1;
+ cairo_point_t p2 = stroker->segments[i].p2;
+ cairo_slope_t out_slope;
+ int j = (i + 1) % stroker->num_segments;
+
+ _cairo_slope_init (&out_slope,
+ &stroker->segments[j].p1,
+ &stroker->segments[j].p2);
+
+ if (is_horizontal) {
+ if (p1.x <= p2.x) {
+ p1.x = p2.x;
+ p2.x += half_line_width;
+ } else {
+ p1.x = p2.x - half_line_width;
+ }
+ if (out_slope.dy >= 0)
+ p1.y -= half_line_width;
+ if (out_slope.dy <= 0)
+ p2.y += half_line_width;
+ } else {
+ if (p1.y <= p2.y) {
+ p1.y = p2.y;
+ p2.y += half_line_width;
+ } else {
+ p1.y = p2.y - half_line_width;
+ }
+ if (out_slope.dx >= 0)
+ p1.x -= half_line_width;
+ if (out_slope.dx <= 0)
+ p2.x += half_line_width;
+ }
+
+ status = _cairo_traps_tessellate_rectangle (stroker->traps,
+ &p1, &p2);
+ if (unlikely (status))
+ return status;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (is_horizontal) {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->x <= b->x) {
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ } else {
+ a->x += half_line_width;
+ b->x -= half_line_width;
+ }
+ }
+
+ if (a->x > b->x) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ if (line_cap == CAIRO_LINE_CAP_SQUARE) {
+ if (a->y <= b->y) {
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ a->y += half_line_width;
+ b->y -= half_line_width;
+ }
+ }
+
+ if (a->y > b->y) {
+ cairo_point_t *t;
+
+ t = a;
+ a = b;
+ b = t;
+ }
+
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ }
+
+ if (a->x == b->x && a->y == b->y)
+ continue;
+
+ status = _cairo_traps_tessellate_rectangle (stroker->traps, a, b);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
_cairo_rectilinear_stroker_move_to (void *closure,
const cairo_point_t *point)
{
cairo_rectilinear_stroker_t *stroker = closure;
cairo_status_t status;
- status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
if (unlikely (status))
return status;
+ /* reset the dash pattern for new sub paths */
+ _cairo_stroker_dash_start (&stroker->dash);
+
stroker->current_point = *point;
stroker->first_point = *point;
@@ -1391,7 +1587,9 @@ _cairo_rectilinear_stroker_line_to (void *closure,
if (a->x == b->x && a->y == b->y)
return CAIRO_STATUS_SUCCESS;
- status = _cairo_rectilinear_stroker_add_segment (stroker, a, b);
+ status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+ a->y == b->y,
+ TRUE);
stroker->current_point = *b;
stroker->open_sub_path = TRUE;
@@ -1400,6 +1598,110 @@ _cairo_rectilinear_stroker_line_to (void *closure,
}
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 sign, remain;
+ cairo_fixed_t mag;
+ cairo_status_t status;
+ cairo_line_t segment;
+ cairo_bool_t dash_on = FALSE;
+ cairo_bool_t 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. */
+ if (! (a->x == b->x || a->y == b->y))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ 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;
+ else
+ mag = b->y - a->y;
+ if (mag < 0) {
+ remain = _cairo_fixed_to_double (-mag);
+ sign = 1.;
+ } else {
+ remain = _cairo_fixed_to_double (mag);
+ sign = -1.;
+ }
+
+ segment.p2 = segment.p1 = *a;
+ while (remain > 0.) {
+ double step_length;
+
+ step_length = MIN (stroker->dash.dash_remain, remain);
+ remain -= step_length;
+
+ mag = _cairo_fixed_from_double (sign*remain);
+ if (is_horizontal)
+ 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.);
+ if (unlikely (status))
+ return status;
+
+ dash_on = TRUE;
+ }
+ else
+ {
+ dash_on = FALSE;
+ }
+
+ _cairo_stroker_dash_step (&stroker->dash, step_length);
+ 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,
+ TRUE);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->current_point = *point;
+ stroker->open_sub_path = TRUE;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
_cairo_rectilinear_stroker_close_path (void *closure)
{
cairo_rectilinear_stroker_t *stroker = closure;
@@ -1409,14 +1711,22 @@ _cairo_rectilinear_stroker_close_path (void *closure)
if (! stroker->open_sub_path)
return CAIRO_STATUS_SUCCESS;
- status = _cairo_rectilinear_stroker_line_to (stroker,
- &stroker->first_point);
+ if (stroker->dash.dashed) {
+ status = _cairo_rectilinear_stroker_line_to_dashed (stroker,
+ &stroker->first_point);
+ } else {
+ status = _cairo_rectilinear_stroker_line_to (stroker,
+ &stroker->first_point);
+ }
if (unlikely (status))
return status;
stroker->open_sub_path = FALSE;
- status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (stroker->dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
if (unlikely (status))
return status;
@@ -1426,16 +1736,16 @@ _cairo_rectilinear_stroker_close_path (void *closure)
static cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
- cairo_matrix_t *ctm,
+ 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 no dashing and a
- * translation-only matrix (though it could probably be extended
- * to support a matrix with uniform, integer scaling).
+ * 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
@@ -1452,8 +1762,6 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path,
* which we round for safety. */
if (stroke_style->miter_limit < M_SQRT2)
return CAIRO_INT_STATUS_UNSUPPORTED;
- if (stroke_style->dash)
- return CAIRO_INT_STATUS_UNSUPPORTED;
if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
{
@@ -1465,11 +1773,20 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path,
return CAIRO_INT_STATUS_UNSUPPORTED;
}
- _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, traps);
+ _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,
@@ -1477,7 +1794,10 @@ _cairo_path_fixed_stroke_rectilinear (cairo_path_fixed_t *path,
if (unlikely (status))
goto BAIL;
- status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+ 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);
diff --git a/src/cairo-pen.c b/src/cairo-pen.c
index d543765..9d5e895 100644
--- a/src/cairo-pen.c
+++ b/src/cairo-pen.c
@@ -39,7 +39,9 @@
#include "cairoint.h"
static int
-_cairo_pen_vertices_needed (double tolerance, double radius, cairo_matrix_t *matrix);
+_cairo_pen_vertices_needed (double tolerance,
+ double radius,
+ const cairo_matrix_t *matrix);
static void
_cairo_pen_compute_slopes (cairo_pen_t *pen);
@@ -48,7 +50,7 @@ cairo_status_t
_cairo_pen_init (cairo_pen_t *pen,
double radius,
double tolerance,
- cairo_matrix_t *ctm)
+ const cairo_matrix_t *ctm)
{
int i;
int reflect;
@@ -258,7 +260,7 @@ doesn't matter where on the circle the error is computed.
static int
_cairo_pen_vertices_needed (double tolerance,
double radius,
- cairo_matrix_t *matrix)
+ const cairo_matrix_t *matrix)
{
/*
* the pen is a circle that gets transformed to an ellipse by matrix.
@@ -266,7 +268,8 @@ _cairo_pen_vertices_needed (double tolerance,
* we don't need the minor axis length.
*/
- double major_axis = _cairo_matrix_transformed_circle_major_axis(matrix, radius);
+ double major_axis = _cairo_matrix_transformed_circle_major_axis (matrix,
+ radius);
/*
* compute number of vertices needed
diff --git a/src/cairo-stroke-style.c b/src/cairo-stroke-style.c
index edc4f88..d808ad8 100644
--- a/src/cairo-stroke-style.c
+++ b/src/cairo-stroke-style.c
@@ -95,9 +95,19 @@ _cairo_stroke_style_max_distance_from_path (const cairo_stroke_style_t *style,
const cairo_matrix_t *ctm,
double *dx, double *dy)
{
- double style_expansion = MAX(style->line_cap == CAIRO_LINE_CAP_SQUARE ? M_SQRT1_2 : 0.5,
- style->line_join == CAIRO_LINE_JOIN_MITER ? style->miter_limit : 0.5);
+ double style_expansion = 0.5;
- *dx = style->line_width * style_expansion * (fabs(ctm->xx) + fabs(ctm->xy));
- *dy = style->line_width * style_expansion * (fabs(ctm->yy) + fabs(ctm->yx));
+ if (style->line_cap == CAIRO_LINE_CAP_SQUARE)
+ style_expansion = M_SQRT1_2;
+
+ if (style->line_join == CAIRO_LINE_JOIN_MITER &&
+ style_expansion < style->miter_limit)
+ {
+ style_expansion = style->miter_limit;
+ }
+
+ style_expansion *= style->line_width;
+
+ *dx = style_expansion * (fabs (ctm->xx) + fabs (ctm->xy));
+ *dy = style_expansion * (fabs (ctm->yy) + fabs (ctm->yx));
}
diff --git a/src/cairoint.h b/src/cairoint.h
index fe9ea5f..8115c81 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1599,8 +1599,8 @@ _cairo_path_fixed_fill_to_traps (cairo_path_fixed_t *path,
cairo_private cairo_status_t
_cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
- cairo_matrix_t *ctm,
- cairo_matrix_t *ctm_inverse,
+ const cairo_matrix_t *ctm,
+ const cairo_matrix_t *ctm_inverse,
double tolerance,
cairo_traps_t *traps);
@@ -2173,7 +2173,7 @@ cairo_private cairo_status_t
_cairo_pen_init (cairo_pen_t *pen,
double radius,
double tolerance,
- cairo_matrix_t *ctm);
+ const cairo_matrix_t *ctm);
cairo_private void
_cairo_pen_init_empty (cairo_pen_t *pen);
@@ -2298,7 +2298,7 @@ _cairo_matrix_compute_determinant (const cairo_matrix_t *matrix);
cairo_private cairo_status_t
_cairo_matrix_compute_basis_scale_factors (const cairo_matrix_t *matrix,
- double *sx, double *sy, int x_major);
+ double *sx, double *sy, int x_major);
cairo_private cairo_bool_t
_cairo_matrix_is_identity (const cairo_matrix_t *matrix);
@@ -2314,7 +2314,8 @@ cairo_private cairo_bool_t
_cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix);
cairo_private double
-_cairo_matrix_transformed_circle_major_axis(cairo_matrix_t *matrix, double radius);
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+ double radius);
cairo_private void
_cairo_matrix_to_pixman_matrix (const cairo_matrix_t *matrix,
diff --git a/test/Makefile.am b/test/Makefile.am
index 8c849ba..0b53116 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -634,8 +634,8 @@ REFERENCE_IMAGES = \
leaky-dash.quartz.ref.png \
leaky-dash.ref.png \
leaky-dashed-rectangle.ref.png \
- leaky-dashed-rectangle.ps2.ref.png \
- leaky-dashed-rectangle.ps3.ref.png \
+ leaky-dashed-rectangle.pdf.ref.png \
+ leaky-dashed-rectangle.ps.ref.png \
leaky-dashed-stroke.ref.png \
leaky-dashed-stroke.ps2.ref.png \
leaky-dashed-stroke.ps3.ref.png \
diff --git a/test/leaky-dashed-rectangle.pdf.ref.png b/test/leaky-dashed-rectangle.pdf.ref.png
new file mode 100644
index 0000000..690cb36
Binary files /dev/null and b/test/leaky-dashed-rectangle.pdf.ref.png differ
diff --git a/test/leaky-dashed-rectangle.ps.ref.png b/test/leaky-dashed-rectangle.ps.ref.png
new file mode 100644
index 0000000..e432de2
Binary files /dev/null and b/test/leaky-dashed-rectangle.ps.ref.png differ
diff --git a/test/leaky-dashed-rectangle.ps2.ref.png b/test/leaky-dashed-rectangle.ps2.ref.png
deleted file mode 100644
index e432de2..0000000
Binary files a/test/leaky-dashed-rectangle.ps2.ref.png and /dev/null differ
diff --git a/test/leaky-dashed-rectangle.ps3.ref.png b/test/leaky-dashed-rectangle.ps3.ref.png
deleted file mode 100644
index e432de2..0000000
Binary files a/test/leaky-dashed-rectangle.ps3.ref.png and /dev/null differ
diff --git a/test/leaky-dashed-rectangle.ref.png b/test/leaky-dashed-rectangle.ref.png
index 332d2fd..c0ba7b2 100644
Binary files a/test/leaky-dashed-rectangle.ref.png and b/test/leaky-dashed-rectangle.ref.png differ
More information about the cairo-commit
mailing list