[stroker] Rectilinear dashing.

Extend the rectilinear stroker to handle dashes, so that for pixel-aligned
dashed strokes we completely avoid tessellation overhead.
This commit is contained in:
Chris Wilson 2008-11-30 13:49:24 +00:00
parent 7f95288c03
commit dd4276c661
10 changed files with 438 additions and 103 deletions

View file

@ -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;

View file

@ -38,11 +38,23 @@
#include "cairoint.h"
#include "cairo-path-fixed-private.h"
typedef struct cairo_stroker {
cairo_stroke_style_t *style;
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;
cairo_matrix_t *ctm;
cairo_matrix_t *ctm_inverse;
double dash_offset;
const double *dashes;
unsigned int num_dashes;
} cairo_stroker_dash_t;
typedef struct cairo_stroker {
cairo_stroke_style_t *style;
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;
@ -1358,6 +1428,126 @@ _cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
return CAIRO_STATUS_SUCCESS;
}
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)
@ -1365,10 +1555,16 @@ _cairo_rectilinear_stroker_move_to (void *closure,
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;
@ -1399,6 +1597,110 @@ _cairo_rectilinear_stroker_line_to (void *closure,
return status;
}
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)
{
@ -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);

View file

@ -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

View file

@ -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));
}

View file

@ -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,

View file

@ -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 \

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

View file

Before

Width:  |  Height:  |  Size: 444 B

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

After

Width:  |  Height:  |  Size: 347 B