mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-05-08 10:18:02 +02:00
Merge branch 'degenerate-path' into cairo
This commit is contained in:
commit
988a28e259
6 changed files with 155 additions and 43 deletions
|
|
@ -50,6 +50,8 @@ typedef struct cairo_stroker {
|
|||
cairo_point_t current_point;
|
||||
cairo_point_t first_point;
|
||||
|
||||
cairo_bool_t has_sub_path;
|
||||
|
||||
cairo_bool_t has_current_face;
|
||||
cairo_stroke_face_t current_face;
|
||||
|
||||
|
|
@ -164,6 +166,7 @@ _cairo_stroker_init (cairo_stroker_t *stroker,
|
|||
|
||||
stroker->has_current_face = FALSE;
|
||||
stroker->has_first_face = FALSE;
|
||||
stroker->has_sub_path = FALSE;
|
||||
|
||||
if (stroker->style->dash)
|
||||
_cairo_stroker_start_dash (stroker);
|
||||
|
|
@ -205,7 +208,8 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
|
|||
if (in->cw.x == out->cw.x
|
||||
&& in->cw.y == out->cw.y
|
||||
&& in->ccw.x == out->ccw.x
|
||||
&& in->ccw.y == out->ccw.y) {
|
||||
&& in->ccw.y == out->ccw.y)
|
||||
{
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
|
|
@ -459,10 +463,26 @@ _cairo_stroker_add_trailing_cap (cairo_stroker_t *stroker,
|
|||
return _cairo_stroker_add_cap (stroker, face);
|
||||
}
|
||||
|
||||
static void
|
||||
_compute_face (cairo_point_t *point, cairo_slope_t *slope, cairo_stroker_t *stroker, cairo_stroke_face_t *face);
|
||||
|
||||
static cairo_status_t
|
||||
_cairo_stroker_add_caps (cairo_stroker_t *stroker)
|
||||
{
|
||||
cairo_status_t status;
|
||||
/* check for a degenerative sub_path */
|
||||
if (stroker->has_sub_path
|
||||
&& !stroker->has_first_face
|
||||
&& !stroker->has_current_face
|
||||
&& stroker->style->line_cap == CAIRO_LINE_JOIN_ROUND)
|
||||
{
|
||||
/* pick an arbitrary slope to use */
|
||||
cairo_slope_t slope = {1, 0};
|
||||
_compute_face (&stroker->first_point, &slope, stroker, &stroker->first_face);
|
||||
|
||||
stroker->has_first_face = stroker->has_current_face = TRUE;
|
||||
stroker->current_face = stroker->first_face;
|
||||
}
|
||||
|
||||
if (stroker->has_first_face) {
|
||||
status = _cairo_stroker_add_leading_cap (stroker, &stroker->first_face);
|
||||
|
|
@ -563,12 +583,8 @@ _cairo_stroker_add_sub_edge (cairo_stroker_t *stroker, cairo_point_t *p1, cairo_
|
|||
fields from start. */
|
||||
_compute_face (p2, slope, stroker, end);
|
||||
|
||||
if (p1->x == p2->x && p1->y == p2->y) {
|
||||
/* XXX: Need to rethink how this case should be handled, (both
|
||||
here and in _compute_face). The key behavior is that
|
||||
degenerate paths should draw as much as possible. */
|
||||
if (p1->x == p2->x && p1->y == p2->y)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* XXX: I should really check the return value of the
|
||||
move_to/line_to functions here to catch out of memory
|
||||
|
|
@ -609,8 +625,9 @@ _cairo_stroker_move_to (void *closure, cairo_point_t *point)
|
|||
stroker->first_point = *point;
|
||||
stroker->current_point = *point;
|
||||
|
||||
stroker->has_first_face = 0;
|
||||
stroker->has_current_face = 0;
|
||||
stroker->has_first_face = FALSE;
|
||||
stroker->has_current_face = FALSE;
|
||||
stroker->has_sub_path = FALSE;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
@ -635,13 +652,10 @@ _cairo_stroker_line_to (void *closure, cairo_point_t *point)
|
|||
cairo_point_t *p2 = point;
|
||||
cairo_slope_t slope;
|
||||
|
||||
if (p1->x == p2->x && p1->y == p2->y) {
|
||||
/* XXX: Need to rethink how this case should be handled, (both
|
||||
here and in cairo_stroker_add_sub_edge and in _compute_face). The
|
||||
key behavior is that degenerate paths should draw as much
|
||||
as possible. */
|
||||
stroker->has_sub_path = TRUE;
|
||||
|
||||
if (p1->x == p2->x && p1->y == p2->y)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_cairo_slope_init (&slope, p1, p2);
|
||||
|
||||
|
|
@ -656,11 +670,11 @@ _cairo_stroker_line_to (void *closure, cairo_point_t *point)
|
|||
} else {
|
||||
if (!stroker->has_first_face) {
|
||||
stroker->first_face = start;
|
||||
stroker->has_first_face = 1;
|
||||
stroker->has_first_face = TRUE;
|
||||
}
|
||||
}
|
||||
stroker->current_face = end;
|
||||
stroker->has_current_face = 1;
|
||||
stroker->has_current_face = TRUE;
|
||||
|
||||
stroker->current_point = *point;
|
||||
|
||||
|
|
@ -679,19 +693,14 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point)
|
|||
double dx, dy;
|
||||
double dx2, dy2;
|
||||
cairo_point_t fd1, fd2;
|
||||
int first = 1;
|
||||
cairo_bool_t first = TRUE;
|
||||
cairo_stroke_face_t sub_start, sub_end;
|
||||
cairo_point_t *p1 = &stroker->current_point;
|
||||
cairo_point_t *p2 = point;
|
||||
cairo_slope_t slope;
|
||||
|
||||
if (p1->x == p2->x && p1->y == p2->y) {
|
||||
/* XXX: Need to rethink how this case should be handled, (both
|
||||
here and in cairo_stroker_add_sub_edge and in _compute_face). The
|
||||
key behavior is that degenerate paths should draw as much
|
||||
as possible. */
|
||||
if (p1->x == p2->x && p1->y == p2->y)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
_cairo_slope_init (&slope, p1, p2);
|
||||
|
||||
|
|
@ -742,7 +751,7 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point)
|
|||
} else {
|
||||
if (!stroker->has_first_face) {
|
||||
stroker->first_face = sub_start;
|
||||
stroker->has_first_face = 1;
|
||||
stroker->has_first_face = TRUE;
|
||||
} else {
|
||||
status = _cairo_stroker_add_leading_cap (stroker, &sub_start);
|
||||
if (status)
|
||||
|
|
@ -763,7 +772,7 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point)
|
|||
* through
|
||||
*/
|
||||
stroker->current_face = sub_end;
|
||||
stroker->has_current_face = 1;
|
||||
stroker->has_current_face = TRUE;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
|
|
@ -778,11 +787,11 @@ _cairo_stroker_line_to_dashed (void *closure, cairo_point_t *point)
|
|||
}
|
||||
}
|
||||
if (!remain)
|
||||
stroker->has_current_face = 0;
|
||||
stroker->has_current_face = FALSE;
|
||||
}
|
||||
_cairo_stroker_step_dash (stroker, tmp);
|
||||
fd1 = fd2;
|
||||
first = 0;
|
||||
first = FALSE;
|
||||
}
|
||||
|
||||
stroker->current_point = *point;
|
||||
|
|
@ -822,11 +831,11 @@ _cairo_stroker_curve_to (void *closure,
|
|||
} else {
|
||||
if (!stroker->has_first_face) {
|
||||
stroker->first_face = start;
|
||||
stroker->has_first_face = 1;
|
||||
stroker->has_first_face = TRUE;
|
||||
}
|
||||
}
|
||||
stroker->current_face = end;
|
||||
stroker->has_current_face = 1;
|
||||
stroker->has_current_face = TRUE;
|
||||
|
||||
extra_points[0] = start.cw;
|
||||
extra_points[0].x -= start.point.x;
|
||||
|
|
@ -943,10 +952,15 @@ _cairo_stroker_close_path (void *closure)
|
|||
status = _cairo_stroker_join (stroker, &stroker->current_face, &stroker->first_face);
|
||||
if (status)
|
||||
return status;
|
||||
} else {
|
||||
status = _cairo_stroker_add_caps (stroker);
|
||||
if (status)
|
||||
return status;
|
||||
}
|
||||
|
||||
stroker->has_first_face = 0;
|
||||
stroker->has_current_face = 0;
|
||||
stroker->has_sub_path = FALSE;
|
||||
stroker->has_first_face = FALSE;
|
||||
stroker->has_current_face = FALSE;
|
||||
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
|||
50
src/cairo.c
50
src/cairo.c
|
|
@ -900,6 +900,11 @@ cairo_set_line_join (cairo_t *cr, cairo_line_join_t line_join)
|
|||
* stroke. The @offset specifies an offset into the pattern at which
|
||||
* the stroke begins.
|
||||
*
|
||||
* Each "on" segment will have caps applied as if the segment were a
|
||||
* separate sub-path. In particular, it is valid to use an "on" length
|
||||
* of 0.0 with CAIRO_LINE_CAP_ROUND or CAIRO_LINE_CAP_SQUARE in order
|
||||
* to distributed dots or squares along a path.
|
||||
*
|
||||
* Note: The length values are in user-space units as evaluated at the
|
||||
* time of stroking. This is not necessarily the same as the user
|
||||
* space at the time of cairo_set_dash().
|
||||
|
|
@ -1178,7 +1183,7 @@ slim_hidden_def(cairo_new_path);
|
|||
* @x: the X coordinate of the new position
|
||||
* @y: the Y coordinate of the new position
|
||||
*
|
||||
* Begin a new subpath. After this call the current point will be (@x,
|
||||
* Begin a new sub-path. After this call the current point will be (@x,
|
||||
* @y).
|
||||
**/
|
||||
void
|
||||
|
|
@ -1203,14 +1208,14 @@ slim_hidden_def(cairo_move_to);
|
|||
* cairo_new_sub_path:
|
||||
* @cr: a cairo context
|
||||
*
|
||||
* Begin a new subpath. Note that the existing path is not
|
||||
* Begin a new sub-path. Note that the existing path is not
|
||||
* affected. After this call there will be no current point.
|
||||
*
|
||||
* In many cases, this call is not needed since new subpaths are
|
||||
* In many cases, this call is not needed since new sub-paths are
|
||||
* frequently started with cairo_move_to().
|
||||
*
|
||||
* A call to cairo_new_sub_path() is particularly useful when
|
||||
* beginning a new subpath with one of the cairo_arc() calls. This
|
||||
* beginning a new sub-path with one of the cairo_arc() calls. This
|
||||
* makes things easier as it is no longer necessary to manually
|
||||
* compute the arc's initial coordinates for a call to
|
||||
* cairo_move_to().
|
||||
|
|
@ -1442,7 +1447,7 @@ cairo_arc_to (cairo_t *cr,
|
|||
* @dx: the X offset
|
||||
* @dy: the Y offset
|
||||
*
|
||||
* Begin a new subpath. After this call the current point will offset
|
||||
* Begin a new sub-path. After this call the current point will offset
|
||||
* by (@x, @y).
|
||||
*
|
||||
* Given a current point of (x, y), cairo_rel_move_to(@cr, @dx, @dy)
|
||||
|
|
@ -1573,7 +1578,7 @@ cairo_rel_curve_to (cairo_t *cr,
|
|||
* @width: the width of the rectangle
|
||||
* @height: the height of the rectangle
|
||||
*
|
||||
* Adds a closed-subpath rectangle of the given size to the current
|
||||
* Adds a closed sub-path rectangle of the given size to the current
|
||||
* path at position (@x, @y) in user-space coordinates.
|
||||
*
|
||||
* This function is logically equivalent to:
|
||||
|
|
@ -1618,15 +1623,15 @@ cairo_stroke_to_path (cairo_t *cr)
|
|||
* @cr: a cairo context
|
||||
*
|
||||
* Adds a line segment to the path from the current point to the
|
||||
* beginning of the current subpath, (the most recent point passed to
|
||||
* cairo_move_to()), and closes this subpath. After this call the
|
||||
* current point will be at the joined endpoint of the subpath.
|
||||
* beginning of the current sub-path, (the most recent point passed to
|
||||
* cairo_move_to()), and closes this sub-path. After this call the
|
||||
* current point will be at the joined endpoint of the sub-path.
|
||||
*
|
||||
* The behavior of cairo_close_path() is distinct from simply calling
|
||||
* cairo_line_to() with the equivalent coordinate in the case of
|
||||
* stroking. When a closed subpath is stroked, there are no caps on
|
||||
* the ends of the subpath. Instead, there is a line join connecting
|
||||
* the final and initial segments of the subpath.
|
||||
* stroking. When a closed sub-path is stroked, there are no caps on
|
||||
* the ends of the sub-path. Instead, there is a line join connecting
|
||||
* the final and initial segments of the sub-path.
|
||||
*
|
||||
* If there is no current point before the call to cairo_close_path,
|
||||
* this function will have no effect.
|
||||
|
|
@ -1776,6 +1781,27 @@ cairo_mask_surface (cairo_t *cr,
|
|||
* context. See cairo_set_line_width(), cairo_set_line_join(),
|
||||
* cairo_set_line_cap(), cairo_set_dash(), and
|
||||
* cairo_stroke_preserve().
|
||||
*
|
||||
* Note: Degenerate segments and sub-paths are treated specially and
|
||||
* provide a useful result. These can result in two different
|
||||
* situations:
|
||||
*
|
||||
* 1. Zero-length "on" segments set in cairo_set_dash(). If the cap
|
||||
* style is CAIRO_LINE_CAP_ROUND or CAIRO_LINE_CAP_SQUARE then these
|
||||
* segments will be drawn as circular dots or squares respectively. In
|
||||
* the case of CAIRO_LINE_CAP_SQUARE, the orientation of the squares
|
||||
* is determined by the direction of the underlying path.
|
||||
*
|
||||
* 2. A sub-path created by cairo_move_to() followed by either a
|
||||
* cairo_close_path() or one or more calls to cairo_line_to() to the
|
||||
* same coordinate as the cairo_move_to(). If the cap style is
|
||||
* CAIRO_LINE_CAP_ROUND then these sub-paths will be drawn as circular
|
||||
* dots. Note that in the case of CAIRO_LINE_CAP_SQUARE a degenerate
|
||||
* sub-path will not be drawn at all, (since the correct orientation
|
||||
* is indeterminate).
|
||||
*
|
||||
* In no case will a cap style of CAIRO_LINE_CAP_BUTT cause anything
|
||||
* to be drawn in the case of either degenerate segments or sub-paths.
|
||||
**/
|
||||
void
|
||||
cairo_stroke (cairo_t *cr)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ dash-caps-joins \
|
|||
dash-scale \
|
||||
dash-offset-negative \
|
||||
dash-zero-length \
|
||||
degenerate-path \
|
||||
device-offset \
|
||||
device-offset-positive \
|
||||
extend-reflect \
|
||||
|
|
@ -178,6 +179,9 @@ dash-offset-negative-ps-argb32-ref.png \
|
|||
dash-zero-length-ref.png \
|
||||
dash-zero-length-rgb24-ref.png \
|
||||
dash-zero-length-ps-argb32-ref.png \
|
||||
degenerate-path-ref.png \
|
||||
degenerate-path-rgb24-ref.png \
|
||||
degenerate-path-ps-argb32-ref.png \
|
||||
device-offset-ref.png \
|
||||
device-offset-positive-ref.png \
|
||||
fill-and-stroke-ref.png \
|
||||
|
|
|
|||
BIN
test/degenerate-path-ref.png
Normal file
BIN
test/degenerate-path-ref.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 193 B |
BIN
test/degenerate-path-rgb24-ref.png
Normal file
BIN
test/degenerate-path-rgb24-ref.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 174 B |
68
test/degenerate-path.c
Normal file
68
test/degenerate-path.c
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright © 2006 Jeff Muizelaar
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software
|
||||
* and its documentation for any purpose is hereby granted without
|
||||
* fee, provided that the above copyright notice appear in all copies
|
||||
* and that both that copyright notice and this permission notice
|
||||
* appear in supporting documentation, and that the name of
|
||||
* Jeff Muizelaar not be used in advertising or publicity pertaining to
|
||||
* distribution of the software without specific, written prior
|
||||
* permission. Jeff Muizelaar makes no representations about the
|
||||
* suitability of this software for any purpose. It is provided "as
|
||||
* is" without express or implied warranty.
|
||||
*
|
||||
* JEFF MUIZELAAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
|
||||
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS, IN NO EVENT SHALL JEFF MUIZELAAR BE LIABLE FOR ANY SPECIAL,
|
||||
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
* IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Author: Jeff Muizelaar <jeff@infidigm.net>
|
||||
*/
|
||||
|
||||
#include "cairo-test.h"
|
||||
|
||||
#define IMAGE_WIDTH 40
|
||||
#define IMAGE_HEIGHT 22
|
||||
|
||||
cairo_test_t test = {
|
||||
"degenerate-path",
|
||||
"Tests the behaviour of degenerate paths with different cap types",
|
||||
IMAGE_WIDTH, IMAGE_HEIGHT
|
||||
};
|
||||
|
||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||
|
||||
static cairo_test_status_t
|
||||
draw (cairo_t *cr, int width, int height)
|
||||
{
|
||||
const cairo_line_cap_t cap[] = { CAIRO_LINE_CAP_ROUND, CAIRO_LINE_CAP_SQUARE, CAIRO_LINE_CAP_BUTT };
|
||||
int i;
|
||||
|
||||
cairo_set_source_rgb (cr, 1, 0, 0);
|
||||
|
||||
for (i=0; i<ARRAY_SIZE(cap); i++) {
|
||||
cairo_set_line_cap (cr, cap[i]);
|
||||
|
||||
cairo_set_line_width (cr, 6);
|
||||
cairo_move_to (cr, 6, 6);
|
||||
cairo_line_to (cr, 6, 6);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_move_to (cr, 6, 15);
|
||||
cairo_close_path (cr);
|
||||
cairo_stroke (cr);
|
||||
|
||||
cairo_translate (cr, 12, 0);
|
||||
}
|
||||
return CAIRO_TEST_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return cairo_test (&test, draw);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue