[cairo-commit] 3 commits - src/cairoint.h src/cairo-path-fill.c src/cairo-path-stroke-boxes.c src/cairo-path-stroke.c src/cairo-stroke-dash.c src/cairo-stroke-dash-private.h src/cairo-surface-fallback.c src/Makefile.sources test/line-width.c test/line-width-overlap.c test/line-width-overlap-flipped.ref.png test/line-width-overlap-flopped.ref.png test/line-width-overlap-offset.ref.png test/line-width-overlap-rotated.ref.png test/Makefile.refs
Chris Wilson
ickle at kemper.freedesktop.org
Sat Aug 13 08:44:42 PDT 2011
src/Makefile.sources | 3
src/cairo-path-fill.c | 245 ---------
src/cairo-path-stroke-boxes.c | 658 +++++++++++++++++++++++++
src/cairo-path-stroke.c | 812 --------------------------------
src/cairo-stroke-dash-private.h | 70 ++
src/cairo-stroke-dash.c | 96 +++
src/cairo-surface-fallback.c | 116 ++--
src/cairoint.h | 13
test/Makefile.refs | 4
test/line-width-overlap-flipped.ref.png |binary
test/line-width-overlap-flopped.ref.png |binary
test/line-width-overlap-offset.ref.png |binary
test/line-width-overlap-rotated.ref.png |binary
test/line-width-overlap.c | 81 +++
test/line-width.c | 27 -
15 files changed, 970 insertions(+), 1155 deletions(-)
New commits:
commit ba406866be320c3a344b4e4a8d4bd19f48fa158d
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sat Aug 13 15:20:03 2011 +0100
stroke: Rely on the tessellator to remove self-intersections
As handling joins/caps between line segments shorter than
half_line_width is tricky.
Rather than also fixing the bug in traps, remove that code. The plan is
to avoiding hitting the traps code, short-circuiting several steps along
the fast rectangular paths.
Fixes line-width-overlap.
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/src/Makefile.sources b/src/Makefile.sources
index 5b116c6..c007d18 100644
--- a/src/Makefile.sources
+++ b/src/Makefile.sources
@@ -96,6 +96,7 @@ cairo_private = \
cairo-scaled-font-private.h \
cairo-slope-private.h \
cairo-spans-private.h \
+ cairo-stroke-dash-private.h \
cairo-surface-fallback-private.h \
cairo-surface-private.h \
cairo-surface-clipper-private.h \
@@ -160,6 +161,7 @@ cairo_sources = \
cairo-path-fixed.c \
cairo-path-in-fill.c \
cairo-path-stroke.c \
+ cairo-path-stroke-boxes.c \
cairo-pattern.c \
cairo-pen.c \
cairo-polygon.c \
@@ -174,6 +176,7 @@ cairo_sources = \
cairo-slope.c \
cairo-spans.c \
cairo-spline.c \
+ cairo-stroke-dash.c \
cairo-stroke-style.c \
cairo-surface.c \
cairo-surface-fallback.c \
diff --git a/src/cairo-path-fill.c b/src/cairo-path-fill.c
index b15b1a4..ce4c86a 100644
--- a/src/cairo-path-fill.c
+++ b/src/cairo-path-fill.c
@@ -235,251 +235,6 @@ _cairo_path_fixed_fill_to_traps (const cairo_path_fixed_t *path,
return status;
}
-static cairo_region_t *
-_cairo_path_fixed_fill_rectilinear_tessellate_to_region (const cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- const cairo_rectangle_int_t *extents)
-{
- cairo_box_t box;
- cairo_polygon_t polygon;
- cairo_traps_t traps;
- cairo_status_t status;
- cairo_region_t *region;
-
- /* first try to bypass fill-to-polygon */
- _cairo_traps_init (&traps);
- status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
- fill_rule,
- CAIRO_ANTIALIAS_NONE,
- &traps);
- if (_cairo_status_is_error (status))
- goto CLEANUP_TRAPS;
-
- if (status == CAIRO_STATUS_SUCCESS) {
- status = _cairo_traps_extract_region (&traps,
- CAIRO_ANTIALIAS_NONE,
- ®ion);
- goto CLEANUP_TRAPS;
- }
-
- /* path is not rectangular, try extracting clipped rectilinear edges */
- if (extents != NULL) {
- _cairo_box_from_rectangle (&box, extents);
- _cairo_polygon_init (&polygon, &box, 1);
- } else {
- _cairo_polygon_init (&polygon, NULL, 0);
- }
-
- /* tolerance will be ignored as the path is rectilinear */
- status = _cairo_path_fixed_fill_to_polygon (path, 0., &polygon);
- if (unlikely (status))
- goto CLEANUP_POLYGON;
-
- if (polygon.num_edges == 0) {
- region = cairo_region_create ();
- } else {
- status =
- _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
- &polygon,
- fill_rule);
- if (likely (status == CAIRO_STATUS_SUCCESS))
- status = _cairo_traps_extract_region (&traps,
- CAIRO_ANTIALIAS_NONE,
- ®ion);
- }
-
- CLEANUP_POLYGON:
- _cairo_polygon_fini (&polygon);
-
- CLEANUP_TRAPS:
- _cairo_traps_fini (&traps);
-
- if (unlikely (status))
- region = _cairo_region_create_in_error (status);
-
- return region;
-}
-
-/* This special-case filler supports only a path that describes a
- * device-axis aligned rectangle. It exists to avoid the overhead of
- * the general tessellator when drawing very common rectangles.
- *
- * If the path described anything but a device-axis aligned rectangle,
- * this function will abort.
- */
-cairo_region_t *
-_cairo_path_fixed_fill_rectilinear_to_region (const cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- const cairo_rectangle_int_t *extents)
-{
- cairo_rectangle_int_t rectangle_stack[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)];
- cairo_box_t box;
- cairo_region_t *region = NULL;
-
- assert (_cairo_path_fixed_fill_maybe_region (path));
- assert (! _cairo_path_fixed_fill_is_empty (path));
-
- if (_cairo_path_fixed_is_box (path, &box)) {
- rectangle_stack[0].x = _cairo_fixed_integer_part (box.p1.x);
- rectangle_stack[0].y = _cairo_fixed_integer_part (box.p1.y);
- rectangle_stack[0].width = _cairo_fixed_integer_part (box.p2.x) -
- rectangle_stack[0].x;
- rectangle_stack[0].height = _cairo_fixed_integer_part (box.p2.y) -
- rectangle_stack[0].y;
- if (! _cairo_rectangle_intersect (&rectangle_stack[0], extents))
- region = cairo_region_create ();
- else
- region = cairo_region_create_rectangle (&rectangle_stack[0]);
- } else if (fill_rule == CAIRO_FILL_RULE_WINDING) {
- cairo_rectangle_int_t *rects = rectangle_stack;
- cairo_path_fixed_iter_t iter;
- int last_cw = -1;
- int size = ARRAY_LENGTH (rectangle_stack);
- int count = 0;
-
- /* Support a series of rectangles as can be expected to describe a
- * GdkRegion clip region during exposes.
- */
- _cairo_path_fixed_iter_init (&iter, path);
- while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
- int cw = 0;
-
- if (box.p1.x > box.p2.x) {
- cairo_fixed_t t;
-
- t = box.p1.x;
- box.p1.x = box.p2.x;
- box.p2.x = t;
-
- cw = ! cw;
- }
-
- if (box.p1.y > box.p2.y) {
- cairo_fixed_t t;
-
- t = box.p1.y;
- box.p1.y = box.p2.y;
- box.p2.y = t;
-
- cw = ! cw;
- }
-
- if (last_cw < 0)
- last_cw = cw;
- else if (last_cw != cw)
- goto TESSELLATE;
-
- if (count == size) {
- cairo_rectangle_int_t *new_rects;
-
- size *= 4;
- if (rects == rectangle_stack) {
- new_rects = _cairo_malloc_ab (size,
- sizeof (cairo_rectangle_int_t));
- if (unlikely (new_rects == NULL)) {
- /* XXX _cairo_region_nil */
- break;
- }
- memcpy (new_rects, rects, sizeof (rectangle_stack));
- } else {
- new_rects = _cairo_realloc_ab (rects, size,
- sizeof (cairo_rectangle_int_t));
- if (unlikely (new_rects == NULL)) {
- /* XXX _cairo_region_nil */
- break;
- }
- }
- rects = new_rects;
- }
-
- rects[count].x = _cairo_fixed_integer_part (box.p1.x);
- rects[count].y = _cairo_fixed_integer_part (box.p1.y);
- rects[count].width = _cairo_fixed_integer_part (box.p2.x) - rects[count].x;
- rects[count].height = _cairo_fixed_integer_part (box.p2.y) - rects[count].y;
- if (_cairo_rectangle_intersect (&rects[count], extents))
- count++;
- }
-
- if (_cairo_path_fixed_iter_at_end (&iter))
- region = cairo_region_create_rectangles (rects, count);
-
-TESSELLATE:
- if (rects != rectangle_stack)
- free (rects);
- }
-
- if (region == NULL) {
- /* Hmm, complex polygon */
- region = _cairo_path_fixed_fill_rectilinear_tessellate_to_region (path,
- fill_rule,
- extents);
-
-
- }
-
- return region;
-}
-
-cairo_int_status_t
-_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- cairo_antialias_t antialias,
- cairo_traps_t *traps)
-{
- cairo_box_t box;
- cairo_status_t status;
-
- traps->is_rectilinear = TRUE;
- traps->is_rectangular = TRUE;
-
- if (_cairo_path_fixed_is_box (path, &box)) {
- if (antialias == CAIRO_ANTIALIAS_NONE) {
- box.p1.x = _cairo_fixed_round_down (box.p1.x);
- box.p1.y = _cairo_fixed_round_down (box.p1.y);
- box.p2.x = _cairo_fixed_round_down (box.p2.x);
- box.p2.y = _cairo_fixed_round_down (box.p2.y);
- }
- return _cairo_traps_tessellate_rectangle (traps, &box.p1, &box.p2);
- } else {
- cairo_path_fixed_iter_t iter;
-
- _cairo_path_fixed_iter_init (&iter, path);
- while (_cairo_path_fixed_iter_is_fill_box (&iter, &box)) {
- if (box.p1.y > box.p2.y) {
- cairo_fixed_t t;
-
- t = box.p1.y;
- box.p1.y = box.p2.y;
- box.p2.y = t;
-
- t = box.p1.x;
- box.p1.x = box.p2.x;
- box.p2.x = t;
- }
-
- if (antialias == CAIRO_ANTIALIAS_NONE) {
- box.p1.x = _cairo_fixed_round_down (box.p1.x);
- box.p1.y = _cairo_fixed_round_down (box.p1.y);
- box.p2.x = _cairo_fixed_round_down (box.p2.x);
- box.p2.y = _cairo_fixed_round_down (box.p2.y);
- }
-
- status = _cairo_traps_tessellate_rectangle (traps,
- &box.p1, &box.p2);
- if (unlikely (status)) {
- _cairo_traps_clear (traps);
- return status;
- }
- }
-
- if (_cairo_path_fixed_iter_at_end (&iter))
- return _cairo_bentley_ottmann_tessellate_rectangular_traps (traps, fill_rule);
-
- _cairo_traps_clear (traps);
- return CAIRO_INT_STATUS_UNSUPPORTED;
- }
-}
-
static cairo_status_t
_cairo_path_fixed_fill_rectilinear_tessellate_to_boxes (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
diff --git a/src/cairo-path-stroke-boxes.c b/src/cairo-path-stroke-boxes.c
new file mode 100644
index 0000000..de3a628
--- /dev/null
+++ b/src/cairo-path-stroke-boxes.c
@@ -0,0 +1,658 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#define _BSD_SOURCE /* for hypot() */
+#include "cairoint.h"
+
+#include "cairo-box-private.h"
+#include "cairo-boxes-private.h"
+#include "cairo-error-private.h"
+#include "cairo-path-fixed-private.h"
+#include "cairo-slope-private.h"
+#include "cairo-stroke-dash-private.h"
+
+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 {
+ const cairo_stroke_style_t *stroke_style;
+ const cairo_matrix_t *ctm;
+ cairo_antialias_t antialias;
+
+ cairo_fixed_t half_line_width;
+ cairo_boxes_t *boxes;
+ 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;
+ 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 *boxes,
+ int num_boxes)
+{
+ stroker->has_bounds = TRUE;
+ _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
+
+ 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 cairo_bool_t
+_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ /* This special-case rectilinear stroker only supports
+ * miter-joined lines (not curves) and a translation-only matrix
+ * (though it could probably be extended to support a matrix with
+ * uniform, integer scaling).
+ *
+ * It also only supports horizontal and vertical line_to
+ * elements. But we don't catch that here, but instead return
+ * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
+ * non-rectilinear line_to is encountered.
+ */
+ if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
+ return FALSE;
+
+ /* If the miter limit turns right angles into bevels, then we
+ * can't use this optimization. Remember, the ratio is
+ * 1/sin(ɸ/2). So the cutoff is 1/sin(Ï/4.0) or â·2,
+ * which we round for safety. */
+ if (stroke_style->miter_limit < M_SQRT2)
+ return FALSE;
+
+ if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
+ stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
+ {
+ return FALSE;
+ }
+
+ if (! _cairo_matrix_has_unity_scale (ctm))
+ return FALSE;
+
+ stroker->stroke_style = stroke_style;
+ stroker->ctm = ctm;
+ stroker->antialias = antialias;
+
+ stroker->half_line_width =
+ _cairo_fixed_from_double (stroke_style->line_width / 2.0);
+ stroker->open_sub_path = FALSE;
+ 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;
+
+ stroker->boxes = boxes;
+
+ return TRUE;
+}
+
+static void
+_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
+{
+ if (stroker->segments != stroker->segments_embedded)
+ free (stroker->segments);
+}
+
+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_bool_t is_horizontal,
+ cairo_bool_t has_join)
+{
+ if (CAIRO_INJECT_FAULT ())
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+
+ if (stroker->num_segments == stroker->segments_size) {
+ int new_size = stroker->segments_size * 2;
+ segment_t *new_segments;
+
+ if (stroker->segments == stroker->segments_embedded) {
+ 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 (segment_t));
+ } else {
+ new_segments = _cairo_realloc_ab (stroker->segments,
+ new_size, sizeof (segment_t));
+ if (unlikely (new_segments == NULL))
+ return _cairo_error (CAIRO_STATUS_NO_MEMORY);
+ }
+
+ stroker->segments_size = new_size;
+ stroker->segments = new_segments;
+ }
+
+ 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;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_emit_segments (cairo_rectilinear_stroker_t *stroker)
+{
+ cairo_line_cap_t line_cap = stroker->stroke_style->line_cap;
+ cairo_fixed_t half_line_width = stroker->half_line_width;
+ cairo_status_t status;
+ int i;
+
+ /* For each segment we generate a single rectangle.
+ * This rectangle is based on a perpendicular extension (by half the
+ * line width) of the segment endpoints * after some adjustments of the
+ * endpoints to account for caps and joins.
+ */
+ for (i = 0; i < stroker->num_segments; i++) {
+ cairo_bool_t lengthen_initial, lengthen_final;
+ cairo_point_t *a, *b;
+ cairo_box_t box;
+
+ a = &stroker->segments[i].p1;
+ b = &stroker->segments[i].p2;
+
+ /* We adjust the initial point of the segment to extend the
+ * rectangle to include the previous cap or join, (this
+ * adjustment applies to all segments except for the first
+ * segment of open, butt-capped paths).
+ *
+ * Overlapping segments will be eliminated by the tessellation.
+ * Ideally, we would not emit these self-intersections at all,
+ * but that is tricky with segments shorter than half_line_width.
+ */
+ lengthen_initial = TRUE;
+ lengthen_final = TRUE;
+ if (stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT) {
+ if (i == 0)
+ lengthen_initial = FALSE;
+
+ if (i == stroker->num_segments - 1)
+ lengthen_final = FALSE;
+ }
+
+ /* Perform the adjustments of the endpoints. */
+ if (a->y == b->y) {
+ if (a->x < b->x) {
+ if (lengthen_initial)
+ a->x -= half_line_width;
+ if (lengthen_final)
+ b->x += half_line_width;
+ } else {
+ if (lengthen_initial)
+ a->x += half_line_width;
+ if (lengthen_final)
+ b->x -= half_line_width;
+ }
+ } else {
+ if (a->y < b->y) {
+ if (lengthen_initial)
+ a->y -= half_line_width;
+ if (lengthen_final)
+ b->y += half_line_width;
+ } else {
+ if (lengthen_initial)
+ a->y += half_line_width;
+ if (lengthen_final)
+ b->y -= half_line_width;
+ }
+ }
+
+ /* Form the rectangle by expanding by half the line width in
+ * either perpendicular direction. */
+ if (a->y == b->y) {
+ a->y -= half_line_width;
+ b->y += half_line_width;
+ } else {
+ a->x -= half_line_width;
+ b->x += half_line_width;
+ }
+
+ if (a->x < b->x) {
+ box.p1.x = a->x;
+ box.p2.x = b->x;
+ } else {
+ box.p1.x = b->x;
+ box.p2.x = a->x;
+ }
+ if (a->y < b->y) {
+ box.p1.y = a->y;
+ box.p2.y = b->y;
+ } else {
+ box.p1.y = b->y;
+ box.p2.y = a->y;
+ }
+
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ if (unlikely (status))
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ 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;
+ cairo_box_t box;
+
+ 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_slope_t out_slope;
+ int j = (i + 1) % stroker->num_segments;
+
+ box.p1 = stroker->segments[i].p1;
+ box.p2 = stroker->segments[i].p2;
+ _cairo_slope_init (&out_slope,
+ &stroker->segments[j].p1,
+ &stroker->segments[j].p2);
+
+ if (is_horizontal) {
+ if (box.p1.x <= box.p2.x) {
+ box.p1.x = box.p2.x;
+ box.p2.x += half_line_width;
+ } else {
+ box.p1.x = box.p2.x - half_line_width;
+ }
+ if (out_slope.dy >= 0)
+ box.p1.y -= half_line_width;
+ if (out_slope.dy <= 0)
+ box.p2.y += half_line_width;
+ } else {
+ if (box.p1.y <= box.p2.y) {
+ box.p1.y = box.p2.y;
+ box.p2.y += half_line_width;
+ } else {
+ box.p1.y = box.p2.y - half_line_width;
+ }
+ if (out_slope.dx >= 0)
+ box.p1.x -= half_line_width;
+ if (out_slope.dx <= 0)
+ box.p2.x += half_line_width;
+ }
+
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ 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;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ a->x += half_line_width;
+ b->x -= half_line_width;
+ }
+
+ if (a->x == b->x && a->y == b->y)
+ continue;
+
+ if (a->x < b->x) {
+ box.p1.x = a->x;
+ box.p2.x = b->x;
+ } else {
+ box.p1.x = b->x;
+ box.p2.x = a->x;
+ }
+ if (a->y < b->y) {
+ box.p1.y = a->y;
+ box.p2.y = b->y;
+ } else {
+ box.p1.y = b->y;
+ box.p2.y = a->y;
+ }
+
+ status = _cairo_boxes_add (stroker->boxes, stroker->antialias, &box);
+ 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;
+
+ 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;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to (void *closure,
+ const cairo_point_t *b)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_point_t *a = &stroker->current_point;
+ cairo_status_t status;
+
+ /* We only support horizontal or vertical elements. */
+ assert (a->x == b->x || a->y == b->y);
+
+ /* We don't draw anything for degenerate paths. */
+ if (a->x == b->x && a->y == b->y)
+ return CAIRO_STATUS_SUCCESS;
+
+ status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
+ a->y == b->y,
+ TRUE);
+
+ stroker->current_point = *b;
+ stroker->open_sub_path = TRUE;
+
+ 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. */
+ assert (a->x == b->x || a->y == b->y);
+
+ 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;
+ cairo_status_t status;
+
+ /* We don't draw anything for degenerate paths. */
+ if (! stroker->open_sub_path)
+ return CAIRO_STATUS_SUCCESS;
+
+ 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;
+
+ 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;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+cairo_int_status_t
+_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
+ const cairo_stroke_style_t *stroke_style,
+ const cairo_matrix_t *ctm,
+ cairo_antialias_t antialias,
+ cairo_boxes_t *boxes)
+{
+ cairo_rectilinear_stroker_t rectilinear_stroker;
+ cairo_int_status_t status;
+
+ assert (_cairo_path_fixed_stroke_is_rectilinear (path));
+
+ if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
+ stroke_style, ctm, antialias,
+ boxes))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ if (boxes->num_limits) {
+ _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
+ boxes->limits,
+ boxes->num_limits);
+ }
+
+ status = _cairo_path_fixed_interpret (path,
+ _cairo_rectilinear_stroker_move_to,
+ rectilinear_stroker.dash.dashed ?
+ _cairo_rectilinear_stroker_line_to_dashed :
+ _cairo_rectilinear_stroker_line_to,
+ NULL,
+ _cairo_rectilinear_stroker_close_path,
+ &rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ if (rectilinear_stroker.dash.dashed)
+ status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
+ else
+ status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+ if (unlikely (status))
+ goto BAIL;
+
+ /* As we incrementally tessellate, we do not eliminate self-intersections */
+ status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
+ CAIRO_FILL_RULE_WINDING,
+ boxes);
+ if (unlikely (status))
+ goto BAIL;
+
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+
+ return CAIRO_STATUS_SUCCESS;
+
+BAIL:
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+ _cairo_boxes_clear (boxes);
+ return status;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 31ce1ac..02f6ba2 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -44,18 +44,7 @@
#include "cairo-error-private.h"
#include "cairo-path-fixed-private.h"
#include "cairo-slope-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;
+#include "cairo-stroke-dash-private.h"
typedef struct cairo_stroker {
cairo_stroke_style_t style;
@@ -98,61 +87,6 @@ typedef struct cairo_stroker {
cairo_box_t bounds;
} cairo_stroker_t;
-static void
-_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
-{
- double offset;
- cairo_bool_t on = TRUE;
- unsigned int i = 0;
-
- 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 >= dash->dashes[i]) {
- offset -= dash->dashes[i];
- on = !on;
- if (++i == dash->num_dashes)
- i = 0;
- }
-
- dash->dash_index = i;
- dash->dash_on = dash->dash_starts_on = on;
- dash->dash_remain = dash->dashes[i] - offset;
-}
-
-static void
-_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
-{
- 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,
const cairo_stroke_style_t *stroke_style,
@@ -1393,21 +1327,6 @@ _cairo_path_fixed_stroke_to_traps (const cairo_path_fixed_t *path,
cairo_int_status_t status;
cairo_polygon_t polygon;
- /* Before we do anything else, we attempt the rectilinear
- * stroker. It's careful to generate trapezoids that align to
- * device-pixel boundaries when possible. Many backends can render
- * those much faster than non-aligned trapezoids, (by using clip
- * regions, etc.) */
- if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
- status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
- stroke_style,
- ctm,
- CAIRO_ANTIALIAS_DEFAULT,
- traps);
- if (status != CAIRO_INT_STATUS_UNSUPPORTED)
- return status;
- }
-
_cairo_polygon_init (&polygon, traps->limits, traps->num_limits);
status = _cairo_path_fixed_stroke_to_polygon (path,
@@ -1431,732 +1350,3 @@ BAIL:
return status;
}
-
-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 {
- const cairo_stroke_style_t *stroke_style;
- const cairo_matrix_t *ctm;
- cairo_antialias_t antialias;
-
- cairo_fixed_t half_line_width;
- cairo_bool_t do_traps;
- void *container;
- 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;
- 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 *boxes,
- int num_boxes)
-{
- stroker->has_bounds = TRUE;
- _cairo_boxes_get_extents (boxes, num_boxes, &stroker->bounds);
-
- 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 cairo_bool_t
-_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- cairo_antialias_t antialias,
- cairo_bool_t do_traps,
- void *container)
-{
- /* This special-case rectilinear stroker only supports
- * miter-joined lines (not curves) and a translation-only matrix
- * (though it could probably be extended to support a matrix with
- * uniform, integer scaling).
- *
- * It also only supports horizontal and vertical line_to
- * elements. But we don't catch that here, but instead return
- * UNSUPPORTED from _cairo_rectilinear_stroker_line_to if any
- * non-rectilinear line_to is encountered.
- */
- if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
- return FALSE;
-
- /* If the miter limit turns right angles into bevels, then we
- * can't use this optimization. Remember, the ratio is
- * 1/sin(ɸ/2). So the cutoff is 1/sin(Ï/4.0) or â·2,
- * which we round for safety. */
- if (stroke_style->miter_limit < M_SQRT2)
- return FALSE;
-
- if (! (stroke_style->line_cap == CAIRO_LINE_CAP_BUTT ||
- stroke_style->line_cap == CAIRO_LINE_CAP_SQUARE))
- {
- return FALSE;
- }
-
- if (! _cairo_matrix_has_unity_scale (ctm))
- return FALSE;
-
- stroker->stroke_style = stroke_style;
- stroker->ctm = ctm;
- stroker->antialias = antialias;
-
- stroker->half_line_width =
- _cairo_fixed_from_double (stroke_style->line_width / 2.0);
- stroker->open_sub_path = FALSE;
- 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;
-
- stroker->do_traps = do_traps;
- stroker->container = container;
-
- return TRUE;
-}
-
-static void
-_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
-{
- if (stroker->segments != stroker->segments_embedded)
- free (stroker->segments);
-}
-
-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_bool_t is_horizontal,
- cairo_bool_t has_join)
-{
- if (CAIRO_INJECT_FAULT ())
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
-
- if (stroker->num_segments == stroker->segments_size) {
- int new_size = stroker->segments_size * 2;
- segment_t *new_segments;
-
- if (stroker->segments == stroker->segments_embedded) {
- 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 (segment_t));
- } else {
- new_segments = _cairo_realloc_ab (stroker->segments,
- new_size, sizeof (segment_t));
- if (unlikely (new_segments == NULL))
- return _cairo_error (CAIRO_STATUS_NO_MEMORY);
- }
-
- stroker->segments_size = new_size;
- stroker->segments = new_segments;
- }
-
- 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;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_emit_segments (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 lengthen_initial, shorten_final, lengthen_final;
-
- a = &stroker->segments[i].p1;
- b = &stroker->segments[i].p2;
-
- /* For each segment we generate a single rectangular
- * trapezoid. This rectangle is based on a perpendicular
- * extension (by half the line width) of the segment endpoints
- * after some adjustments of the endpoints to account for caps
- * and joins.
- */
-
- /* We adjust the initial point of the segment to extend the
- * rectangle to include the previous cap or join, (this
- * adjustment applies to all segments except for the first
- * segment of open, butt-capped paths).
- */
- lengthen_initial = TRUE;
- if (i == 0 && stroker->open_sub_path && line_cap == CAIRO_LINE_CAP_BUTT)
- lengthen_initial = FALSE;
-
- /* The adjustment of the final point is trickier. For all but
- * the last segment we shorten the segment at the final
- * endpoint to not overlap with the subsequent join. For the
- * last segment we do the same shortening if the path is
- * closed. If the path is open and butt-capped we do no
- * adjustment, while if it's open and square-capped we do a
- * lengthening adjustment instead to include the cap.
- */
- shorten_final = TRUE;
- lengthen_final = FALSE;
- if (i == stroker->num_segments - 1 && stroker->open_sub_path) {
- shorten_final = FALSE;
- if (line_cap == CAIRO_LINE_CAP_SQUARE)
- lengthen_final = TRUE;
- }
-
- /* Perform the adjustments of the endpoints. */
- if (a->y == b->y) {
- if (a->x < b->x) {
- if (lengthen_initial)
- a->x -= half_line_width;
- if (shorten_final)
- b->x -= half_line_width;
- else if (lengthen_final)
- b->x += half_line_width;
- } else {
- if (lengthen_initial)
- a->x += half_line_width;
- if (shorten_final)
- b->x += half_line_width;
- else if (lengthen_final)
- b->x -= half_line_width;
- }
-
- if (a->x > b->x) {
- cairo_point_t *t;
-
- t = a;
- a = b;
- b = t;
- }
- } else {
- if (a->y < b->y) {
- if (lengthen_initial)
- a->y -= half_line_width;
- if (shorten_final)
- b->y -= half_line_width;
- else if (lengthen_final)
- b->y += half_line_width;
- } else {
- if (lengthen_initial)
- a->y += half_line_width;
- if (shorten_final)
- b->y += half_line_width;
- else if (lengthen_final)
- b->y -= half_line_width;
- }
-
- if (a->y > b->y) {
- cairo_point_t *t;
-
- t = a;
- a = b;
- b = t;
- }
- }
-
- /* Form the rectangle by expanding by half the line width in
- * either perpendicular direction. */
- if (a->y == b->y) {
- a->y -= half_line_width;
- b->y += half_line_width;
- } else {
- a->x -= half_line_width;
- b->x += half_line_width;
- }
-
- if (stroker->do_traps) {
- if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
- a->x = _cairo_fixed_round_down (a->x);
- a->y = _cairo_fixed_round_down (a->y);
- b->x = _cairo_fixed_round_down (b->x);
- b->y = _cairo_fixed_round_down (b->y);
- }
- status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
- } else {
- cairo_box_t box;
-
- box.p1 = *a;
- box.p2 = *b;
-
- status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
- }
- if (unlikely (status))
- return status;
- }
-
- stroker->num_segments = 0;
-
- 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;
- }
-
- if (stroker->do_traps) {
- if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
- p1.x = _cairo_fixed_round_down (p1.x);
- p1.y = _cairo_fixed_round_down (p1.y);
- p2.x = _cairo_fixed_round_down (p2.x);
- p2.y = _cairo_fixed_round_down (p2.y);
- }
- status = _cairo_traps_tessellate_rectangle (stroker->container, &p1, &p2);
- } else {
- cairo_box_t box;
-
- box.p1 = p1;
- box.p2 = p2;
-
- status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
- }
- 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;
-
- if (stroker->do_traps) {
- if (stroker->antialias == CAIRO_ANTIALIAS_NONE) {
- a->x = _cairo_fixed_round_down (a->x);
- a->y = _cairo_fixed_round_down (a->y);
- b->x = _cairo_fixed_round_down (b->x);
- b->y = _cairo_fixed_round_down (b->y);
- }
- status = _cairo_traps_tessellate_rectangle (stroker->container, a, b);
- } else {
- cairo_box_t box;
-
- box.p1 = *a;
- box.p2 = *b;
-
- status = _cairo_boxes_add (stroker->container, stroker->antialias, &box);
- }
- 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;
-
- 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;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-static cairo_status_t
-_cairo_rectilinear_stroker_line_to (void *closure,
- const cairo_point_t *b)
-{
- cairo_rectilinear_stroker_t *stroker = closure;
- cairo_point_t *a = &stroker->current_point;
- cairo_status_t status;
-
- /* We only support horizontal or vertical elements. */
- assert (a->x == b->x || a->y == b->y);
-
- /* We don't draw anything for degenerate paths. */
- if (a->x == b->x && a->y == b->y)
- return CAIRO_STATUS_SUCCESS;
-
- status = _cairo_rectilinear_stroker_add_segment (stroker, a, b,
- a->y == b->y,
- TRUE);
-
- stroker->current_point = *b;
- stroker->open_sub_path = TRUE;
-
- 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. */
- assert (a->x == b->x || a->y == b->y);
-
- 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;
- cairo_status_t status;
-
- /* We don't draw anything for degenerate paths. */
- if (! stroker->open_sub_path)
- return CAIRO_STATUS_SUCCESS;
-
- 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;
-
- 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;
-
- return CAIRO_STATUS_SUCCESS;
-}
-
-cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- cairo_antialias_t antialias,
- cairo_traps_t *traps)
-{
- cairo_rectilinear_stroker_t rectilinear_stroker;
- cairo_int_status_t status;
-
- assert (_cairo_path_fixed_stroke_is_rectilinear (path));
-
- if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
- stroke_style, ctm, antialias,
- TRUE, traps))
- {
- return CAIRO_INT_STATUS_UNSUPPORTED;
- }
-
- if (traps->num_limits) {
- _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
- traps->limits,
- traps->num_limits);
- }
-
- status = _cairo_path_fixed_interpret (path,
- _cairo_rectilinear_stroker_move_to,
- rectilinear_stroker.dash.dashed ?
- _cairo_rectilinear_stroker_line_to_dashed :
- _cairo_rectilinear_stroker_line_to,
- NULL,
- _cairo_rectilinear_stroker_close_path,
- &rectilinear_stroker);
- if (unlikely (status))
- goto BAIL;
-
- if (rectilinear_stroker.dash.dashed)
- status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
- else
- status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
-
- traps->is_rectilinear = 1;
- traps->is_rectangular = 1;
- /* As we incrementally tessellate, we do not eliminate self-intersections */
- traps->has_intersections = traps->num_traps > 1;
-BAIL:
- _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
-
- if (unlikely (status))
- _cairo_traps_clear (traps);
-
- return status;
-}
-
-cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- cairo_antialias_t antialias,
- cairo_boxes_t *boxes)
-{
- cairo_rectilinear_stroker_t rectilinear_stroker;
- cairo_int_status_t status;
-
- assert (_cairo_path_fixed_stroke_is_rectilinear (path));
-
- if (! _cairo_rectilinear_stroker_init (&rectilinear_stroker,
- stroke_style, ctm, antialias,
- FALSE, boxes))
- {
- return CAIRO_INT_STATUS_UNSUPPORTED;
- }
-
- if (boxes->num_limits) {
- _cairo_rectilinear_stroker_limit (&rectilinear_stroker,
- boxes->limits,
- boxes->num_limits);
- }
-
- status = _cairo_path_fixed_interpret (path,
- _cairo_rectilinear_stroker_move_to,
- rectilinear_stroker.dash.dashed ?
- _cairo_rectilinear_stroker_line_to_dashed :
- _cairo_rectilinear_stroker_line_to,
- NULL,
- _cairo_rectilinear_stroker_close_path,
- &rectilinear_stroker);
- if (unlikely (status))
- goto BAIL;
-
- if (rectilinear_stroker.dash.dashed)
- status = _cairo_rectilinear_stroker_emit_segments_dashed (&rectilinear_stroker);
- else
- status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
- if (unlikely (status))
- goto BAIL;
-
- /* As we incrementally tessellate, we do not eliminate self-intersections */
- status = _cairo_bentley_ottmann_tessellate_boxes (boxes,
- CAIRO_FILL_RULE_WINDING,
- boxes);
- if (unlikely (status))
- goto BAIL;
-
- _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
-
- return CAIRO_STATUS_SUCCESS;
-
-BAIL:
- _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
- _cairo_boxes_clear (boxes);
- return status;
-}
diff --git a/src/cairo-stroke-dash-private.h b/src/cairo-stroke-dash-private.h
new file mode 100644
index 0000000..75c000c
--- /dev/null
+++ b/src/cairo-stroke-dash-private.h
@@ -0,0 +1,70 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_STROKE_DASH_PRIVATE_H
+#define CAIRO_STROKE_DASH_PRIVATE_H
+
+#include "cairoint.h"
+
+CAIRO_BEGIN_DECLS
+
+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;
+
+cairo_private void
+_cairo_stroker_dash_init (cairo_stroker_dash_t *dash,
+ const cairo_stroke_style_t *style);
+
+cairo_private void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash);
+
+cairo_private void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step);
+
+CAIRO_END_DECLS
+
+#endif /* CAIRO_STROKE_DASH_PRIVATE_H */
diff --git a/src/cairo-stroke-dash.c b/src/cairo-stroke-dash.c
new file mode 100644
index 0000000..d581bdc
--- /dev/null
+++ b/src/cairo-stroke-dash.c
@@ -0,0 +1,96 @@
+/* -*- Mode: c; tab-width: 8; c-basic-offset: 4; indent-tabs-mode: t; -*- */
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2002 University of Southern California
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ * Carl D. Worth <cworth at cworth.org>
+ * Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#include "cairoint.h"
+
+#include "cairo-stroke-dash-private.h"
+
+void
+_cairo_stroker_dash_start (cairo_stroker_dash_t *dash)
+{
+ double offset;
+ cairo_bool_t on = TRUE;
+ unsigned int i = 0;
+
+ 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 >= dash->dashes[i]) {
+ offset -= dash->dashes[i];
+ on = !on;
+ if (++i == dash->num_dashes)
+ i = 0;
+ }
+
+ dash->dash_index = i;
+ dash->dash_on = dash->dash_starts_on = on;
+ dash->dash_remain = dash->dashes[i] - offset;
+}
+
+void
+_cairo_stroker_dash_step (cairo_stroker_dash_t *dash, double step)
+{
+ 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];
+ }
+}
+
+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);
+}
diff --git a/src/cairo-surface-fallback.c b/src/cairo-surface-fallback.c
index b65b2bf..ec6946b 100644
--- a/src/cairo-surface-fallback.c
+++ b/src/cairo-surface-fallback.c
@@ -1038,7 +1038,7 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface,
cairo_traps_t traps;
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t rect;
- cairo_status_t status;
+ cairo_int_status_t status;
if (!_cairo_surface_get_extents (surface, &rect))
ASSERT_NOT_REACHED;
@@ -1053,44 +1053,47 @@ _cairo_surface_fallback_stroke (cairo_surface_t *surface,
_cairo_polygon_init_with_clip (&polygon, extents.clip);
_cairo_traps_init_with_clip (&traps, extents.clip);
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
if (_cairo_path_fixed_stroke_is_rectilinear (path)) {
- status = _cairo_path_fixed_stroke_rectilinear_to_traps (path,
+ cairo_boxes_t boxes;
+
+ /* Did I mention, that I need to rewrite surface-fallback? */
+ _cairo_boxes_init_with_clip (&boxes, extents.clip);
+ status = _cairo_path_fixed_stroke_rectilinear_to_boxes (path,
stroke_style,
ctm,
antialias,
- &traps);
- if (likely (status == CAIRO_STATUS_SUCCESS))
- goto DO_TRAPS;
+ &boxes);
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = _cairo_traps_init_boxes (&traps, &boxes);
+ _cairo_boxes_fini (&boxes);
- if (_cairo_status_is_error (status))
+ if (unlikely (_cairo_int_status_is_error (status)))
goto CLEANUP;
}
- status = _cairo_path_fixed_stroke_to_polygon (path,
- stroke_style,
- ctm, ctm_inverse,
- tolerance,
- &polygon);
- if (unlikely (status))
- goto CLEANUP;
+ /* Fall back to trapezoid fills. */
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_path_fixed_stroke_to_polygon (path,
+ stroke_style,
+ ctm, ctm_inverse,
+ tolerance,
+ &polygon);
+ if (unlikely (status))
+ goto CLEANUP;
- if (polygon.num_edges == 0)
- goto DO_TRAPS;
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+ &polygon.extents);
+ if (unlikely (status))
+ goto CLEANUP;
- if (_cairo_operator_bounded_by_mask (op)) {
- _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
- if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ &polygon,
+ CAIRO_FILL_RULE_WINDING);
+ if (unlikely (status))
goto CLEANUP;
}
- /* Fall back to trapezoid fills. */
- status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
- &polygon,
- CAIRO_FILL_RULE_WINDING);
- if (unlikely (status))
- goto CLEANUP;
-
- DO_TRAPS:
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, antialias,
extents.clip,
@@ -1116,10 +1119,9 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface,
{
cairo_polygon_t polygon;
cairo_traps_t traps;
- cairo_bool_t is_rectilinear;
cairo_composite_rectangles_t extents;
cairo_rectangle_int_t rect;
- cairo_status_t status;
+ cairo_int_status_t status;
if (!_cairo_surface_get_extents (surface, &rect))
ASSERT_NOT_REACHED;
@@ -1136,50 +1138,44 @@ _cairo_surface_fallback_fill (cairo_surface_t *surface,
if (_cairo_path_fixed_fill_is_empty (path))
goto DO_TRAPS;
- is_rectilinear = _cairo_path_fixed_fill_is_rectilinear (path);
- if (is_rectilinear) {
- status = _cairo_path_fixed_fill_rectilinear_to_traps (path,
+ status = CAIRO_INT_STATUS_UNSUPPORTED;
+ if (_cairo_path_fixed_fill_is_rectilinear (path)) {
+ cairo_boxes_t boxes;
+
+ /* meh, surface-fallback is dying anyway... */
+ _cairo_boxes_init_with_clip (&boxes, extents.clip);
+ status = _cairo_path_fixed_fill_rectilinear_to_boxes (path,
fill_rule,
antialias,
- &traps);
- if (likely (status == CAIRO_STATUS_SUCCESS))
- goto DO_TRAPS;
+ &boxes);
+ if (unlikely (status))
+ goto CLEANUP;
- if (_cairo_status_is_error (status))
+ if (status == CAIRO_INT_STATUS_SUCCESS)
+ status = _cairo_traps_init_boxes (&traps, &boxes);
+ _cairo_boxes_fini (&boxes);
+ if (unlikely (_cairo_int_status_is_error (status)))
goto CLEANUP;
}
- status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
- if (unlikely (status))
- goto CLEANUP;
-
- if (polygon.num_edges == 0)
- goto DO_TRAPS;
-
- if (_cairo_operator_bounded_by_mask (op)) {
- _cairo_box_round_to_rectangle (&polygon.extents, &extents.mask);
- if (! _cairo_rectangle_intersect (&extents.bounded, &extents.mask))
+ /* Fall back to trapezoid fills. */
+ if (status == CAIRO_INT_STATUS_UNSUPPORTED) {
+ status = _cairo_path_fixed_fill_to_polygon (path, tolerance, &polygon);
+ if (unlikely (status))
goto CLEANUP;
- }
- if (is_rectilinear) {
- status = _cairo_bentley_ottmann_tessellate_rectilinear_polygon (&traps,
- &polygon,
- fill_rule);
- if (likely (status == CAIRO_STATUS_SUCCESS))
- goto DO_TRAPS;
+ status = _cairo_composite_rectangles_intersect_mask_extents (&extents,
+ &polygon.extents);
+ if (unlikely (status))
+ goto CLEANUP;
- if (unlikely (_cairo_status_is_error (status)))
+ status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
+ &polygon,
+ fill_rule);
+ if (unlikely (status))
goto CLEANUP;
}
- /* Fall back to trapezoid fills. */
- status = _cairo_bentley_ottmann_tessellate_polygon (&traps,
- &polygon,
- fill_rule);
- if (unlikely (status))
- goto CLEANUP;
-
DO_TRAPS:
status = _clip_and_composite_trapezoids (source, op, surface,
&traps, antialias,
diff --git a/src/cairoint.h b/src/cairoint.h
index 8d87aa5..9a76557 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -1371,12 +1371,6 @@ _cairo_path_fixed_fill_rectilinear_to_polygon (const cairo_path_fixed_t *path,
cairo_antialias_t antialias,
cairo_polygon_t *polygon);
-cairo_private cairo_int_status_t
-_cairo_path_fixed_fill_rectilinear_to_traps (const cairo_path_fixed_t *path,
- cairo_fill_rule_t fill_rule,
- cairo_antialias_t antialias,
- cairo_traps_t *traps);
-
cairo_private cairo_status_t
_cairo_path_fixed_fill_rectilinear_to_boxes (const cairo_path_fixed_t *path,
cairo_fill_rule_t fill_rule,
@@ -1404,13 +1398,6 @@ _cairo_path_fixed_stroke_to_polygon (const cairo_path_fixed_t *path,
cairo_polygon_t *polygon);
cairo_private cairo_int_status_t
-_cairo_path_fixed_stroke_rectilinear_to_traps (const cairo_path_fixed_t *path,
- const cairo_stroke_style_t *stroke_style,
- const cairo_matrix_t *ctm,
- cairo_antialias_t antialias,
- cairo_traps_t *traps);
-
-cairo_private cairo_int_status_t
_cairo_path_fixed_stroke_rectilinear_to_boxes (const cairo_path_fixed_t *path,
const cairo_stroke_style_t *stroke_style,
const cairo_matrix_t *ctm,
commit 54c8e8ccfc242fd17144c64202f628c87edbb6f4
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sat Aug 13 13:24:52 2011 +0100
test: Add a couple of variants to line-width-overlap
The bug may be in only the fast-path, but future bugs may lie elsewhere.
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/test/Makefile.refs b/test/Makefile.refs
index 5a73386..c435bf6 100644
--- a/test/Makefile.refs
+++ b/test/Makefile.refs
@@ -690,6 +690,10 @@ REFERENCE_IMAGES = \
leaky-polygon.image16.ref.png \
leaky-polygon.ps.ref.png \
leaky-polygon.ref.png \
+ line-width-overlap-flipped.ref.png \
+ line-width-overlap-flopped.ref.png \
+ line-width-overlap-offset.ref.png \
+ line-width-overlap-rotated.ref.png \
line-width-overlap.ref.png \
line-width-scale.image16.ref.png \
line-width-scale.ps2.ref.png \
diff --git a/test/line-width-overlap-flipped.ref.png b/test/line-width-overlap-flipped.ref.png
new file mode 100644
index 0000000..09911bc
Binary files /dev/null and b/test/line-width-overlap-flipped.ref.png differ
diff --git a/test/line-width-overlap-flopped.ref.png b/test/line-width-overlap-flopped.ref.png
new file mode 100644
index 0000000..09911bc
Binary files /dev/null and b/test/line-width-overlap-flopped.ref.png differ
diff --git a/test/line-width-overlap-offset.ref.png b/test/line-width-overlap-offset.ref.png
new file mode 100644
index 0000000..eafa50b
Binary files /dev/null and b/test/line-width-overlap-offset.ref.png differ
diff --git a/test/line-width-overlap-rotated.ref.png b/test/line-width-overlap-rotated.ref.png
new file mode 100644
index 0000000..cd89a85
Binary files /dev/null and b/test/line-width-overlap-rotated.ref.png differ
diff --git a/test/line-width-overlap.c b/test/line-width-overlap.c
index cd23d32..ac8c234 100644
--- a/test/line-width-overlap.c
+++ b/test/line-width-overlap.c
@@ -54,9 +54,9 @@ draw (cairo_t *cr, int width, int height)
/* rectangle that is smaller than the line width in center of image */
cairo_rectangle (cr,
- (SIZE - RECT_SIZE) / 2,
(SIZE - RECT_SIZE) / 2,
- RECT_SIZE,
+ (SIZE - RECT_SIZE) / 2,
+ RECT_SIZE,
RECT_SIZE);
cairo_stroke (cr);
@@ -64,9 +64,86 @@ draw (cairo_t *cr, int width, int height)
return CAIRO_TEST_SUCCESS;
}
+/* and again slightly offset to trigger another path */
+static cairo_test_status_t
+draw_offset (cairo_t *cr, int width, int height)
+{
+ cairo_translate (cr, .5, .5);
+ return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_rotated (cairo_t *cr, int width, int height)
+{
+ cairo_translate (cr, SIZE/2, SIZE/2);
+ cairo_rotate (cr, M_PI/4);
+ cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+ return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_flipped (cairo_t *cr, int width, int height)
+{
+ cairo_translate (cr, SIZE/2, SIZE/2);
+ cairo_scale (cr, -1, 1);
+ cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+ return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_flopped (cairo_t *cr, int width, int height)
+{
+ cairo_translate (cr, SIZE/2, SIZE/2);
+ cairo_scale (cr, 1, -1);
+ cairo_translate (cr, -SIZE/2, -SIZE/2);
+
+ return draw (cr, width, height);
+}
+
+static cairo_test_status_t
+draw_dashed (cairo_t *cr, int width, int height)
+{
+ const double dashes[] = { 4 };
+ cairo_set_dash (cr, dashes, 1, 0);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+ return draw (cr, width, height);
+}
+
CAIRO_TEST (line_width_overlap,
"Test overlapping lines due to large line width",
"stroke", /* keywords */
NULL, /* requirements */
SIZE, SIZE,
NULL, draw)
+CAIRO_TEST (line_width_overlap_offset,
+ "Test overlapping lines due to large line width",
+ "stroke", /* keywords */
+ NULL, /* requirements */
+ SIZE, SIZE,
+ NULL, draw_offset)
+CAIRO_TEST (line_width_overlap_rotated,
+ "Test overlapping lines due to large line width",
+ "stroke", /* keywords */
+ NULL, /* requirements */
+ SIZE, SIZE,
+ NULL, draw_rotated)
+CAIRO_TEST (line_width_overlap_flipped,
+ "Test overlapping lines due to large line width",
+ "stroke", /* keywords */
+ NULL, /* requirements */
+ SIZE, SIZE,
+ NULL, draw_flipped)
+CAIRO_TEST (line_width_overlap_flopped,
+ "Test overlapping lines due to large line width",
+ "stroke", /* keywords */
+ NULL, /* requirements */
+ SIZE, SIZE,
+ NULL, draw_flopped)
+CAIRO_TEST (line_width_overlap_dashed,
+ "Test overlapping lines due to large line width",
+ "stroke", /* keywords */
+ NULL, /* requirements */
+ SIZE, SIZE,
+ NULL, draw_dashed)
commit 829eabfc9531a3e4490760b6bbd33286cd280e95
Author: Chris Wilson <chris at chris-wilson.co.uk>
Date: Sat Aug 13 16:13:41 2011 +0100
test/line-width: Refactor and tidy
Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
diff --git a/test/line-width.c b/test/line-width.c
index 66924f1..283e41f 100644
--- a/test/line-width.c
+++ b/test/line-width.c
@@ -31,7 +31,7 @@
#define IMAGE_HEIGHT ((LINES+4)*LINES)/2 + 2
static cairo_test_status_t
-draw_a8 (cairo_t *cr, int width, int height)
+draw (cairo_t *cr, int width, int height)
{
int i;
@@ -59,29 +59,8 @@ draw_a8 (cairo_t *cr, int width, int height)
static cairo_test_status_t
draw_a1 (cairo_t *cr, int width, int height)
{
- int i;
-
- /* We draw in black, so paint white first. */
- cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
- cairo_paint (cr);
-
- cairo_set_source_rgb (cr, 0, 0, 0);
- cairo_translate (cr, 2, 2);
-
cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
-
- for (i=0; i < LINES; i++) {
- cairo_set_line_width (cr, i+1);
- cairo_move_to (cr, 0, 0);
- cairo_rel_line_to (cr, LINE_LENGTH, 0);
- cairo_stroke (cr);
- cairo_move_to (cr, LINE_LENGTH + 2, 0.5);
- cairo_rel_line_to (cr, LINE_LENGTH, 0);
- cairo_stroke (cr);
- cairo_translate (cr, 0, i+3);
- }
-
- return CAIRO_TEST_SUCCESS;
+ return draw (cr, width, height);
}
CAIRO_TEST (line_width,
@@ -89,7 +68,7 @@ CAIRO_TEST (line_width,
"stroke", /* keywords */
NULL, /* requirements */
IMAGE_WIDTH, IMAGE_HEIGHT,
- NULL, draw_a8)
+ NULL, draw)
CAIRO_TEST (a1_line_width,
"Tests cairo_set_line_width",
"stroke", /* keywords */
More information about the cairo-commit
mailing list