mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-02 10:48:06 +02:00
[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:
parent
7f95288c03
commit
dd4276c661
10 changed files with 438 additions and 103 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
BIN
test/leaky-dashed-rectangle.pdf.ref.png
Normal file
BIN
test/leaky-dashed-rectangle.pdf.ref.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 347 B |
|
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 |
Loading…
Add table
Reference in a new issue