[cairo-commit] 4 commits - src/cairo.c src/cairo-path-fixed.c
src/cairo-path-fixed-private.h src/cairo-path-stroke.c
test/ft-text-vertical-layout-type1.c
test/ft-text-vertical-layout-type3-ref.png
test/ft-text-vertical-layout-type3-svg-ref.png
test/.gitignore test/Makefile.am test/rectilinear-stroke.c
test/rectilinear-stroke-ref.png
Carl Worth
cworth at kemper.freedesktop.org
Fri Dec 22 18:00:50 PST 2006
src/cairo-path-fixed-private.h | 3
src/cairo-path-fixed.c | 4
src/cairo-path-stroke.c | 318 ++++++++++++++++++++++++-
src/cairo.c | 11
test/.gitignore | 1
test/Makefile.am | 3
test/ft-text-vertical-layout-type1.c | 3
test/ft-text-vertical-layout-type3-ref.png |binary
test/ft-text-vertical-layout-type3-svg-ref.png |binary
test/rectilinear-stroke-ref.png |binary
test/rectilinear-stroke.c | 138 ++++++++++
11 files changed, 473 insertions(+), 8 deletions(-)
New commits:
diff-tree ba531642f79d492ecbad8f086f1e44b56e157e36 (from b1189118532a1fe93e126843af739809d38a62bd)
Author: Carl Worth <cworth at cworth.org>
Date: Tue Dec 19 21:34:16 2006 -0800
Add optimization for rectilinear stroke
This custom stroking code allows backends to use optimized region-based
drawing operations for rectilinear strokes. This results in a 5-25x
performance improvement when drawing rectilinear shapes:
image-rgb box-outline-stroke-100 0.18 -> 0.01: 25.58x speedup
âââââââââââââââââââââââââ
image-rgba box-outline-stroke-100 0.18 -> 0.01: 25.57x speedup
âââââââââââââââââââââââââ
xlib-rgb box-outline-stroke-100 0.49 -> 0.06: 8.67x speedup
ââââââââ
xlib-rgba box-outline-stroke-100 0.22 -> 0.04: 5.39x speedup
âââââ
In other words, using cairo_stroke instead of cairo_fill to draw the
same shape was 5-15x slower before, but is 1.2-2x faster now.
diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index 8e0d530..12ca618 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -68,7 +68,8 @@ struct _cairo_path_fixed {
cairo_point_t last_move_point;
cairo_point_t current_point;
- unsigned int has_current_point : 1;
+ unsigned int has_current_point : 1;
+ unsigned int has_curve_to : 1;
};
#endif /* CAIRO_PATH_FIXED_PRIVATE_H */
diff --git a/src/cairo-path-fixed.c b/src/cairo-path-fixed.c
index 9549f8d..71d646b 100644
--- a/src/cairo-path-fixed.c
+++ b/src/cairo-path-fixed.c
@@ -88,6 +88,7 @@ _cairo_path_fixed_init (cairo_path_fixed
path->current_point.x = 0;
path->current_point.y = 0;
path->has_current_point = FALSE;
+ path->has_curve_to = FALSE;
path->last_move_point = path->current_point;
}
@@ -101,6 +102,7 @@ _cairo_path_fixed_init_copy (cairo_path_
_cairo_path_fixed_init (path);
path->current_point = other->current_point;
path->has_current_point = other->has_current_point;
+ path->has_curve_to = other->has_curve_to;
path->last_move_point = other->last_move_point;
for (other_op_buf = other->op_buf_head;
@@ -164,6 +166,7 @@ _cairo_path_fixed_fini (cairo_path_fixed
path->arg_buf_tail = NULL;
path->has_current_point = FALSE;
+ path->has_curve_to = FALSE;
}
void
@@ -289,6 +292,7 @@ _cairo_path_fixed_curve_to (cairo_path_f
path->current_point = point[2];
path->has_current_point = TRUE;
+ path->has_curve_to = TRUE;
return CAIRO_STATUS_SUCCESS;
}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index ced31c1..63f0f4d 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -35,6 +35,7 @@
*/
#include "cairoint.h"
+#include "cairo-path-fixed-private.h"
typedef struct cairo_stroker {
cairo_stroke_style_t *style;
@@ -943,6 +944,12 @@ _cairo_stroker_close_path (void *closure
return CAIRO_STATUS_SUCCESS;
}
+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,
+ cairo_traps_t *traps);
+
cairo_status_t
_cairo_path_fixed_stroke_to_traps (cairo_path_fixed_t *path,
cairo_stroke_style_t *stroke_style,
@@ -951,9 +958,21 @@ _cairo_path_fixed_stroke_to_traps (cairo
double tolerance,
cairo_traps_t *traps)
{
- cairo_status_t status = CAIRO_STATUS_SUCCESS;
+ cairo_status_t status;
cairo_stroker_t stroker;
+ /* 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.) */
+ status = _cairo_path_fixed_stroke_rectilinear (path,
+ stroke_style,
+ ctm,
+ traps);
+ if (status != CAIRO_INT_STATUS_UNSUPPORTED)
+ return status;
+
_cairo_stroker_init (&stroker, stroke_style,
ctm, ctm_inverse, tolerance,
traps);
@@ -984,3 +1003,300 @@ BAIL:
return status;
}
+
+typedef struct _cairo_rectilinear_stroker
+{
+ cairo_stroke_style_t *stroke_style;
+ 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_line_t *segments;
+ int segments_size;
+ int num_segments;
+} cairo_rectilinear_stroker_t;
+
+static void
+_cairo_rectilinear_stroker_init (cairo_rectilinear_stroker_t *stroker,
+ cairo_stroke_style_t *stroke_style,
+ cairo_traps_t *traps)
+{
+ stroker->stroke_style = stroke_style;
+ stroker->half_line_width =
+ _cairo_fixed_from_double (stroke_style->line_width / 2.0);
+ stroker->traps = traps;
+ stroker->open_sub_path = FALSE;
+ stroker->segments = NULL;
+ stroker->segments_size = 0;
+ stroker->num_segments = 0;
+}
+
+static void
+_cairo_rectilinear_stroker_fini (cairo_rectilinear_stroker_t *stroker)
+{
+ free (stroker->segments);
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker,
+ cairo_point_t *p1,
+ cairo_point_t *p2)
+{
+ int new_size;
+ cairo_line_t *new_segments;
+
+ if (stroker->num_segments == stroker->segments_size) {
+ new_size = stroker->segments_size * 2;
+ /* Common case is one rectangle of exactly 4 segments. */
+ if (new_size == 0)
+ new_size = 4;
+ new_segments = realloc (stroker->segments, new_size * sizeof (cairo_line_t));
+ if (new_segments == NULL)
+ return 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->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;
+ cairo_bool_t lengthen_initial, shorten_final, lengthen_final;
+ cairo_point_t *a, *b;
+ cairo_point_t r[4];
+ int i;
+
+ for (i = 0; i < stroker->num_segments; i++) {
+ 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;
+ }
+ } 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;
+ }
+ }
+
+ /* Form the rectangle by expanding by half the line width in
+ * either perdendicular direction. */
+ r[0] = *a;
+ r[1] = *b;
+ r[2] = *b;
+ r[3] = *a;
+ if (a->y == b->y) {
+ r[0].y -= half_line_width;
+ r[1].y -= half_line_width;
+ r[2].y += half_line_width;
+ r[3].y += half_line_width;
+ } else {
+ r[0].x -= half_line_width;
+ r[1].x -= half_line_width;
+ r[2].x += half_line_width;
+ r[3].x += half_line_width;
+ }
+
+ status = _cairo_traps_tessellate_convex_quad (stroker->traps, r);
+ if (status)
+ return status;
+ }
+
+ stroker->num_segments = 0;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_move_to (void *closure,
+ cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_status_t status;
+
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (status)
+ return status;
+
+ stroker->current_point = *point;
+ stroker->first_point = *point;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_rectilinear_stroker_line_to (void *closure,
+ cairo_point_t *point)
+{
+ cairo_rectilinear_stroker_t *stroker = closure;
+ cairo_point_t *a = &stroker->current_point;
+ cairo_point_t *b = point;
+ cairo_status_t status;
+
+ /* We only support horizontal or vertical elements. */
+ if (! (a->x == b->x || a->y == b->y))
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+
+ /* 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);
+
+ stroker->current_point = *point;
+ stroker->open_sub_path = TRUE;
+
+ return status;
+}
+
+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;
+
+ status = _cairo_rectilinear_stroker_line_to (stroker,
+ &stroker->first_point);
+ if (status)
+ return status;
+
+ stroker->open_sub_path = FALSE;
+
+ status = _cairo_rectilinear_stroker_emit_segments (stroker);
+ if (status)
+ return status;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+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,
+ 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 sacling).
+ *
+ * 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 (path->has_curve_to)
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ if (stroke_style->line_join != CAIRO_LINE_JOIN_MITER)
+ 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))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+ if (! (_cairo_matrix_is_identity (ctm) ||
+ _cairo_matrix_is_translation (ctm)))
+ {
+ return CAIRO_INT_STATUS_UNSUPPORTED;
+ }
+
+ _cairo_rectilinear_stroker_init (&rectilinear_stroker, stroke_style, traps);
+
+ status = _cairo_path_fixed_interpret (path,
+ CAIRO_DIRECTION_FORWARD,
+ _cairo_rectilinear_stroker_move_to,
+ _cairo_rectilinear_stroker_line_to,
+ NULL,
+ _cairo_rectilinear_stroker_close_path,
+ &rectilinear_stroker);
+ if (status) {
+ _cairo_traps_fini (traps);
+ return status;
+ }
+
+ status = _cairo_rectilinear_stroker_emit_segments (&rectilinear_stroker);
+ if (status)
+ return status;
+
+ _cairo_rectilinear_stroker_fini (&rectilinear_stroker);
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo.c b/src/cairo.c
index 5d77ce3..d842d75 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -48,11 +48,12 @@ static const cairo_t cairo_nil = {
CAIRO_REF_COUNT_INVALID, /* ref_count */
CAIRO_STATUS_NO_MEMORY, /* status */
{ /* path */
- NULL, NULL, /* op_buf_head, op_buf_tail */
- NULL, NULL, /* arg_buf_head, arg_buf_tail */
- { 0, 0 }, /* last_move_point */
- { 0, 0 }, /* current point */
- FALSE, /* has_current_point */
+ NULL, NULL, /* op_buf_head, op_buf_tail */
+ NULL, NULL, /* arg_buf_head, arg_buf_tail */
+ { 0, 0 }, /* last_move_point */
+ { 0, 0 }, /* current point */
+ FALSE, /* has_current_point */
+ FALSE /* has_curve_to */
},
NULL /* gstate */
};
diff --git a/test/ft-text-vertical-layout-type3-ref.png b/test/ft-text-vertical-layout-type3-ref.png
index eaee4f0..8ec2ebe 100644
Binary files a/test/ft-text-vertical-layout-type3-ref.png and b/test/ft-text-vertical-layout-type3-ref.png differ
diff --git a/test/ft-text-vertical-layout-type3-svg-ref.png b/test/ft-text-vertical-layout-type3-svg-ref.png
index a01c9d3..7aa322d 100644
Binary files a/test/ft-text-vertical-layout-type3-svg-ref.png and b/test/ft-text-vertical-layout-type3-svg-ref.png differ
diff-tree b1189118532a1fe93e126843af739809d38a62bd (from 7b1509f4f37118d14bd5d70365d1608ead5e2819)
Author: Carl Worth <cworth at cworth.org>
Date: Fri Dec 22 17:11:08 2006 -0800
Put ft-text-vertical-layout-type1 back on the XFAIL list
I must not have the right font available, (test result is coming out
looking like the result of ft-text-vertical-layout-type3, Vera?).
We should switch this test to load a bundled font, (should do that for
all font-using tests, too).
diff --git a/test/Makefile.am b/test/Makefile.am
index 4ed21d7..16237dc 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -347,6 +347,7 @@ a8-mask \
big-trap \
extend-reflect \
filter-nearest-offset \
+ft-text-vertical-layout-type1 \
leaky-dash \
long-lines \
self-intersecting \
diff --git a/test/ft-text-vertical-layout-type1.c b/test/ft-text-vertical-layout-type1.c
index 82e31f1..c52c28d 100644
--- a/test/ft-text-vertical-layout-type1.c
+++ b/test/ft-text-vertical-layout-type1.c
@@ -37,7 +37,8 @@ static cairo_test_draw_function_t draw;
cairo_test_t test = {
"ft-text-vertical-layout-type1",
- "Tests text rendering for vertical layout with Type1 fonts",
+ "Tests text rendering for vertical layout with Type1 fonts"
+ "\nCan fail if an incorrect font is loaded---need to bundle the desired font",
WIDTH, HEIGHT,
draw
};
diff-tree 7b1509f4f37118d14bd5d70365d1608ead5e2819 (from 9d2d3b95e359cd2829c8d02a378dbfec2065e832)
Author: Carl Worth <cworth at cworth.org>
Date: Tue Dec 19 13:13:11 2006 -0800
Reimplement path.has_current point as a 1-bit bitfield
diff --git a/src/cairo-path-fixed-private.h b/src/cairo-path-fixed-private.h
index e8e0df1..8e0d530 100644
--- a/src/cairo-path-fixed-private.h
+++ b/src/cairo-path-fixed-private.h
@@ -68,7 +68,7 @@ struct _cairo_path_fixed {
cairo_point_t last_move_point;
cairo_point_t current_point;
- int has_current_point;
+ unsigned int has_current_point : 1;
};
#endif /* CAIRO_PATH_FIXED_PRIVATE_H */
diff-tree 9d2d3b95e359cd2829c8d02a378dbfec2065e832 (from 39ce31ade64e1c3b9e5880134ab77ca96f48f1b9)
Author: Carl Worth <cworth at cworth.org>
Date: Thu Dec 21 08:20:20 2006 -0800
Add new rectilinear-stroke test
This is in preparation for an optimized implementation of cairo_stroke
for rectilinear paths.
diff --git a/test/.gitignore b/test/.gitignore
index 489bce6..a9f71be 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -104,6 +104,7 @@ svg-surface.svg
pixman-rotate
pthread-show-text
rectangle-rounding-error
+rectilinear-stroke
rel-path
scale-source-surface-paint
select-font-face
diff --git a/test/Makefile.am b/test/Makefile.am
index 6dd82cb..4ed21d7 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -71,6 +71,7 @@ pattern-getters \
pixman-rotate \
random-intersections \
rectangle-rounding-error \
+rectilinear-stroke \
scale-source-surface-paint \
select-font-face \
select-font-no-show-text \
@@ -286,6 +287,7 @@ random-intersections-ref.png \
random-intersections-rgb24-ref.png \
random-intersections-ps-argb32-ref.png \
rectangle-rounding-error-ref.png \
+rectilinear-stroke-ref.png \
rel-path-ref.png \
rel-path-rgb24-ref.png \
romedalen.png \
diff --git a/test/rectilinear-stroke-ref.png b/test/rectilinear-stroke-ref.png
new file mode 100644
index 0000000..0a40b0d
Binary files /dev/null and b/test/rectilinear-stroke-ref.png differ
diff --git a/test/rectilinear-stroke.c b/test/rectilinear-stroke.c
new file mode 100644
index 0000000..6ba3f1a
--- /dev/null
+++ b/test/rectilinear-stroke.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright © 2006 Red Hat, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Author: Carl D. Worth <cworth at cworth.org>
+ */
+
+#include "cairo-test.h"
+
+#define SIZE 25
+
+static cairo_test_draw_function_t draw;
+
+cairo_test_t test = {
+ "rectilinear-stroke",
+ "Test rectilinear stroke operations (covering only whole pixels)",
+ SIZE, SIZE,
+ draw
+};
+
+static cairo_test_status_t
+draw (cairo_t *cr, int width, int height)
+{
+ /* Paint background white, then draw in black. */
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); /* white */
+ cairo_paint (cr);
+ cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); /* black */
+
+ cairo_set_line_width (cr, 1.0);
+ cairo_translate (cr, 1, 1);
+
+ /* Draw everything first with square caps. */
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
+
+ /* Draw horizontal and vertical segments, each in both
+ * directions. */
+ cairo_move_to (cr, 4.5, 0.5);
+ cairo_rel_line_to (cr, 2.0, 0.0);
+
+ cairo_move_to (cr, 10.5, 4.5);
+ cairo_rel_line_to (cr, 0.0, 2.0);
+
+ cairo_move_to (cr, 6.5, 10.5);
+ cairo_rel_line_to (cr, -2.0, 0.0);
+
+ cairo_move_to (cr, 0.5, 6.5);
+ cairo_rel_line_to (cr, 0.0, -2.0);
+
+ /* Draw right angle turns in four directions. */
+ cairo_move_to (cr, 0.5, 2.5);
+ cairo_rel_line_to (cr, 0.0, -2.0);
+ cairo_rel_line_to (cr, 2.0, 0.0);
+
+ cairo_move_to (cr, 8.5, 0.5);
+ cairo_rel_line_to (cr, 2.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 2.0);
+
+ cairo_move_to (cr, 10.5, 8.5);
+ cairo_rel_line_to (cr, 0.0, 2.0);
+ cairo_rel_line_to (cr, -2.0, 0.0);
+
+ cairo_move_to (cr, 2.5, 10.5);
+ cairo_rel_line_to (cr, -2.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, -2.0);
+
+ /* Draw a closed-path rectangle */
+ cairo_rectangle (cr, 0.5, 12.5, 10.0, 10.0);
+
+ cairo_stroke (cr);
+
+ cairo_translate (cr, 12, 0);
+
+ /* Now draw the same results, but with butt caps. */
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+
+ /* Draw horizontal and vertical segments, each in both
+ * directions. */
+ cairo_move_to (cr, 4.0, 0.5);
+ cairo_rel_line_to (cr, 3.0, 0.0);
+
+ cairo_move_to (cr, 10.5, 4.0);
+ cairo_rel_line_to (cr, 0.0, 3.0);
+
+ cairo_move_to (cr, 7.0, 10.5);
+ cairo_rel_line_to (cr, -3.0, 0.0);
+
+ cairo_move_to (cr, 0.5, 7.0);
+ cairo_rel_line_to (cr, 0.0, -3.0);
+
+ /* Draw right angle turns in four directions. */
+ cairo_move_to (cr, 0.5, 3.0);
+ cairo_rel_line_to (cr, 0.0, -2.5);
+ cairo_rel_line_to (cr, 2.5, 0.0);
+
+ cairo_move_to (cr, 8.0, 0.5);
+ cairo_rel_line_to (cr, 2.5, 0.0);
+ cairo_rel_line_to (cr, 0.0, 2.5);
+
+ cairo_move_to (cr, 10.5, 8.0);
+ cairo_rel_line_to (cr, 0.0, 2.5);
+ cairo_rel_line_to (cr, -2.5, 0.0);
+
+ cairo_move_to (cr, 3.0, 10.5);
+ cairo_rel_line_to (cr, -2.5, 0.0);
+ cairo_rel_line_to (cr, 0.0, -2.5);
+
+ /* Draw a closed-path rectangle */
+ cairo_rectangle (cr, 0.5, 12.5, 10.0, 10.0);
+
+ cairo_stroke (cr);
+
+ return CAIRO_TEST_SUCCESS;
+}
+
+int
+main (void)
+{
+ return cairo_test (&test);
+}
More information about the cairo-commit
mailing list