summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Worth <cworth@cworth.org>2003-10-04 14:34:42 +0000
committerCarl Worth <cworth@cworth.org>2003-10-04 14:34:42 +0000
commit61726a88f24efcbabdff980abdfe1ff8301315b2 (patch)
treeb14a5724e721f01fd4767611e9ef89950f7b7843
parenta249bd717c194d03d480d7803351ee6f21daf0c2 (diff)
Generate convex hull of pen before stroking.
-rw-r--r--ChangeLog8
-rw-r--r--src/Makefile.am1
-rw-r--r--src/cairo-hull.c190
-rw-r--r--src/cairo-path-stroke.c4
-rw-r--r--src/cairo-pen.c115
-rw-r--r--src/cairo-slope.c40
-rw-r--r--src/cairo_hull.c190
-rw-r--r--src/cairo_path_stroke.c4
-rw-r--r--src/cairo_pen.c115
-rw-r--r--src/cairo_slope.c40
-rw-r--r--src/cairoint.h6
11 files changed, 534 insertions, 179 deletions
diff --git a/ChangeLog b/ChangeLog
index 5e1dee38..2ec251b7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
2003-10-04 Carl Worth <cworth@isi.edu>
+ * src/cairo_hull.c (_cairo_hull_compute): Add cairo_hull.c to
+ compute a convex hull, (using Graham scan algorithm).
+
+ * src/cairo_pen.c (_cairo_pen_add_points): Generate convex hull of
+ pen after adding new points.
+
+ * src/cairoint.h: Rename pen->vertex to pen->vertices
+
* Replaced "pt" with "point" in about a zillion places.
* src/cairo.c (cairo_destroy): Fix to continue with destroy even
diff --git a/src/Makefile.am b/src/Makefile.am
index 5d292a34..87ca3952 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -8,6 +8,7 @@ libcairo_la_SOURCES = \
cairo_fixed.c \
cairo_font.c \
cairo_gstate.c \
+ cairo_hull.c \
cairo_matrix.c \
cairo_path.c \
cairo_path_bounds.c \
diff --git a/src/cairo-hull.c b/src/cairo-hull.c
new file mode 100644
index 00000000..fc659853
--- /dev/null
+++ b/src/cairo-hull.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright © 2003 USC, Information Sciences Institute
+ *
+ * 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 the
+ * University of Southern California not be used in advertising or
+ * publicity pertaining to distribution of the software without
+ * specific, written prior permission. The University of Southern
+ * California makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
+ * SOUTHERN CALIFORNIA 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: Carl D. Worth <cworth@isi.edu>
+ */
+
+#include "cairoint.h"
+
+typedef struct cairo_hull
+{
+ cairo_point_t point;
+ cairo_slope_t slope;
+ int discard;
+} cairo_hull_t;
+
+static cairo_hull_t *
+_cairo_hull_create (cairo_pen_vertex_t *vertices, int num_vertices)
+{
+ int i;
+ cairo_hull_t *hull;
+ cairo_point_t *p, *extremum, tmp;
+
+ extremum = &vertices[0].point;
+ for (i = 1; i < num_vertices; i++) {
+ p = &vertices[i].point;
+ if (p->y < extremum->y || (p->y == extremum->y && p->x < extremum->x))
+ extremum = p;
+ }
+ /* Put the extremal point at the beginning of the array */
+ tmp = *extremum;
+ *extremum = vertices[0].point;
+ vertices[0].point = tmp;
+
+ hull = malloc (num_vertices * sizeof (cairo_hull_t));
+ if (hull == NULL)
+ return NULL;
+
+ for (i = 0; i < num_vertices; i++) {
+ hull[i].point = vertices[i].point;
+ _cairo_slope_init (&hull[i].slope, &hull[0].point, &hull[i].point);
+
+ /* Discard all points coincident with the extremal point */
+ if (i != 0 && hull[i].slope.dx == 0 && hull[i].slope.dy == 0)
+ hull[i].discard = 1;
+ else
+ hull[i].discard = 0;
+ }
+
+ return hull;
+}
+
+static int
+_cairo_hull_vertex_compare (const void *av, const void *bv)
+{
+ cairo_hull_t *a = (cairo_hull_t *) av;
+ cairo_hull_t *b = (cairo_hull_t *) bv;
+ int ret;
+
+ ret = _cairo_slope_compare (&a->slope, &b->slope);
+
+ /* In the case of two vertices with identical slope from the
+ extremal point discard the nearer point. */
+
+ if (ret == 0) {
+ cairo_fixed_48_16_t a_dist, b_dist;
+ a_dist = ((cairo_fixed_48_16_t) a->slope.dx * a->slope.dx +
+ (cairo_fixed_48_16_t) a->slope.dy * a->slope.dy);
+ b_dist = ((cairo_fixed_48_16_t) b->slope.dx * b->slope.dx +
+ (cairo_fixed_48_16_t) b->slope.dy * b->slope.dy);
+ if (a_dist < b_dist)
+ a->discard = 1;
+ else
+ b->discard = 1;
+ }
+
+ return ret;
+}
+
+static int
+_cairo_hull_prev_valid (cairo_hull_t *hull, int num_hull, int index)
+{
+ do {
+ /* hull[0] is always valid, so don't test and wraparound */
+ index--;
+ } while (hull[index].discard);
+
+ return index;
+}
+
+static int
+_cairo_hull_next_valid (cairo_hull_t *hull, int num_hull, int index)
+{
+ do {
+ index = (index + 1) % num_hull;
+ } while (hull[index].discard);
+
+ return index;
+}
+
+static cairo_status_t
+_cairo_hull_eliminate_concave (cairo_hull_t *hull, int num_hull)
+{
+ int i, j, k;
+ cairo_slope_t slope_ij, slope_jk;
+
+ i = 0;
+ j = _cairo_hull_next_valid (hull, num_hull, i);
+ k = _cairo_hull_next_valid (hull, num_hull, j);
+
+ do {
+ _cairo_slope_init (&slope_ij, &hull[i].point, &hull[j].point);
+ _cairo_slope_init (&slope_jk, &hull[j].point, &hull[k].point);
+
+ /* Is the angle formed by ij and jk concave? */
+ if (_cairo_slope_compare (&slope_ij, &slope_jk) >= 0) {
+ if (i == k)
+ return CAIRO_STATUS_SUCCESS;
+ hull[j].discard = 1;
+ j = i;
+ i = _cairo_hull_prev_valid (hull, num_hull, j);
+ } else {
+ i = j;
+ j = k;
+ k = _cairo_hull_next_valid (hull, num_hull, j);
+ }
+ } while (j != 0);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_hull_to_pen (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int *num_vertices)
+{
+ int i, j = 0;
+
+ for (i = 0; i < *num_vertices; i++) {
+ if (hull[i].discard)
+ continue;
+ vertices[j++].point = hull[i].point;
+ }
+
+ *num_vertices = j;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Given a set of vertices, compute the convex hull using the Graham
+ scan algorithm. */
+cairo_status_t
+_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices)
+{
+ cairo_hull_t *hull;
+ int num_hull = *num_vertices;
+
+ hull = _cairo_hull_create (vertices, num_hull);
+ if (hull == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ qsort (hull + 1, num_hull - 1,
+ sizeof (cairo_hull_t), _cairo_hull_vertex_compare);
+
+ _cairo_hull_eliminate_concave (hull, num_hull);
+
+ _cairo_hull_to_pen (hull, vertices, num_vertices);
+
+ free (hull);
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo-path-stroke.c b/src/cairo-path-stroke.c
index 535d7683..a9bf266b 100644
--- a/src/cairo-path-stroke.c
+++ b/src/cairo-path-stroke.c
@@ -189,7 +189,7 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
tri[1] = *inpt;
while (i != stop) {
tri[2] = in->point;
- _translate_point (&tri[2], &pen->vertex[i].point);
+ _translate_point (&tri[2], &pen->vertices[i].point);
_cairo_traps_tessellate_triangle (stroker->traps, tri);
tri[1] = tri[2];
i += step;
@@ -337,7 +337,7 @@ _cairo_stroker_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f)
tri[1] = f->cw;
for (i=start; i != stop; i = (i+1) % pen->num_vertices) {
tri[2] = f->point;
- _translate_point (&tri[2], &pen->vertex[i].point);
+ _translate_point (&tri[2], &pen->vertices[i].point);
_cairo_traps_tessellate_triangle (stroker->traps, tri);
tri[1] = tri[2];
}
diff --git a/src/cairo-pen.c b/src/cairo-pen.c
index 032d505a..5e76a1b3 100644
--- a/src/cairo-pen.c
+++ b/src/cairo-pen.c
@@ -33,9 +33,6 @@ _cairo_pen_vertices_needed (double radius, double tolerance, double expansion);
static void
_cairo_pen_compute_slopes (cairo_pen_t *pen);
-static int
-_pen_vertex_compare (const void *av, const void *bv);
-
static cairo_status_t
_cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon);
@@ -44,7 +41,7 @@ _cairo_pen_init_empty (cairo_pen_t *pen)
{
pen->radius = 0;
pen->tolerance = 0;
- pen->vertex = NULL;
+ pen->vertices = NULL;
pen->num_vertices = 0;
return CAIRO_STATUS_SUCCESS;
@@ -88,8 +85,8 @@ _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate)
if (pen->num_vertices % 2)
pen->num_vertices++;
- pen->vertex = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
- if (pen->vertex == NULL) {
+ pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
+ if (pen->vertices == NULL) {
return CAIRO_STATUS_NO_MEMORY;
}
@@ -103,7 +100,7 @@ _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate)
double theta = 2 * M_PI * i / (double) pen->num_vertices;
double dx = radius * cos (reflect ? -theta : theta);
double dy = radius * sin (reflect ? -theta : theta);
- cairo_pen_vertex_t *v = &pen->vertex[i];
+ cairo_pen_vertex_t *v = &pen->vertices[i];
cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy);
v->point.x = _cairo_fixed_from_double (dx);
v->point.y = _cairo_fixed_from_double (dy);
@@ -117,7 +114,7 @@ _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate)
void
_cairo_pen_fini (cairo_pen_t *pen)
{
- free (pen->vertex);
+ free (pen->vertices);
_cairo_pen_init_empty (pen);
}
@@ -127,11 +124,11 @@ _cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other)
*pen = *other;
if (pen->num_vertices) {
- pen->vertex = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
- if (pen->vertex == NULL) {
+ pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
+ if (pen->vertices == NULL) {
return CAIRO_STATUS_NO_MEMORY;
}
- memcpy (pen->vertex, other->vertex, pen->num_vertices * sizeof (cairo_pen_vertex_t));
+ memcpy (pen->vertices, other->vertices, pen->num_vertices * sizeof (cairo_pen_vertex_t));
}
return CAIRO_STATUS_SUCCESS;
@@ -140,37 +137,23 @@ _cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other)
cairo_status_t
_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points)
{
+ cairo_pen_vertex_t *vertices;
+ int num_vertices;
int i;
- cairo_pen_vertex_t *v, *v_next, *new_vertex;
- pen->num_vertices += num_points;
- new_vertex = realloc (pen->vertex, pen->num_vertices * sizeof (cairo_pen_vertex_t));
- if (new_vertex == NULL) {
- pen->num_vertices -= num_points;
+ num_vertices = pen->num_vertices + num_points;
+ vertices = realloc (pen->vertices, num_vertices * sizeof (cairo_pen_vertex_t));
+ if (vertices == NULL)
return CAIRO_STATUS_NO_MEMORY;
- }
- pen->vertex = new_vertex;
+
+ pen->vertices = vertices;
+ pen->num_vertices = num_vertices;
/* initialize new vertices */
- for (i=0; i < num_points; i++) {
- v = &pen->vertex[pen->num_vertices-(i+1)];
- v->point = point[i];
- }
+ for (i=0; i < num_points; i++)
+ pen->vertices[pen->num_vertices-num_points+i].point = point[i];
- qsort (pen->vertex, pen->num_vertices, sizeof (cairo_pen_vertex_t), _pen_vertex_compare);
-
- /* eliminate any duplicate vertices */
- for (i=0; i < pen->num_vertices; i++ ) {
- v = &pen->vertex[i];
- v_next = (i < pen->num_vertices - 1) ? &pen->vertex[i+1] : &pen->vertex[0];
- if (_pen_vertex_compare (v, v_next) == 0) {
- pen->num_vertices--;
- memmove (&pen->vertex[i], &pen->vertex[i+1],
- (pen->num_vertices - i) * sizeof (cairo_pen_vertex_t));
- /* There may be more of the same duplicate, check again */
- i--;
- }
- }
+ _cairo_hull_compute (pen->vertices, &pen->num_vertices);
_cairo_pen_compute_slopes (pen);
@@ -199,53 +182,15 @@ _cairo_pen_compute_slopes (cairo_pen_t *pen)
for (i=0, i_prev = pen->num_vertices - 1;
i < pen->num_vertices;
i_prev = i++) {
- prev = &pen->vertex[i_prev];
- v = &pen->vertex[i];
- next = &pen->vertex[(i + 1) % pen->num_vertices];
+ prev = &pen->vertices[i_prev];
+ v = &pen->vertices[i];
+ next = &pen->vertices[(i + 1) % pen->num_vertices];
_cairo_slope_init (&v->slope_cw, &prev->point, &v->point);
_cairo_slope_init (&v->slope_ccw, &v->point, &next->point);
}
}
-/* Is a further clockwise from (1,0) than b?
- *
- * There are a two special cases to consider:
- * 1) a and b are not in the same half plane.
- * 2) both a and b are on the X axis
- * After that, the computation is a simple slope comparison.
- */
-static int
-_pen_vertex_compare (const void *av, const void *bv)
-{
- const cairo_pen_vertex_t *a = av;
- const cairo_pen_vertex_t *b = bv;
- cairo_fixed_48_16_t diff;
-
- int a_above = a->point.y >= 0;
- int b_above = b->point.y >= 0;
-
- if (a_above != b_above)
- return b_above - a_above;
-
- if (a->point.y == 0 && b->point.y == 0) {
- int a_right = a->point.x >= 0;
- int b_right = b->point.x >= 0;
-
- if (a_right != b_right)
- return b_right - a_right;
- }
-
- diff = ((cairo_fixed_48_16_t) a->point.y * (cairo_fixed_48_16_t) b->point.x
- - (cairo_fixed_48_16_t) b->point.y * (cairo_fixed_48_16_t) a->point.x);
-
- if (diff > 0)
- return 1;
- if (diff < 0)
- return -1;
- return 0;
-}
-
/* Find active pen vertex for clockwise edge of stroke at the given slope.
*
* NOTE: The behavior of this function is sensitive to the sense of
@@ -264,8 +209,8 @@ _cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen,
int i;
for (i=0; i < pen->num_vertices; i++) {
- if (_cairo_slope_clockwise (slope, &pen->vertex[i].slope_ccw)
- && _cairo_slope_counter_clockwise (slope, &pen->vertex[i].slope_cw))
+ if (_cairo_slope_clockwise (slope, &pen->vertices[i].slope_ccw)
+ && _cairo_slope_counter_clockwise (slope, &pen->vertices[i].slope_cw))
break;
}
@@ -292,8 +237,8 @@ _cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen,
slope_reverse.dy = -slope_reverse.dy;
for (i=pen->num_vertices-1; i >= 0; i--) {
- if (_cairo_slope_counter_clockwise (&pen->vertex[i].slope_ccw, &slope_reverse)
- && _cairo_slope_clockwise (&pen->vertex[i].slope_cw, &slope_reverse))
+ if (_cairo_slope_counter_clockwise (&pen->vertices[i].slope_ccw, &slope_reverse)
+ && _cairo_slope_clockwise (&pen->vertices[i].slope_cw, &slope_reverse))
break;
}
@@ -339,8 +284,8 @@ _cairo_pen_stroke_spline_half (cairo_pen_t *pen,
i = start;
while (i != stop) {
- hull_point.x = point[i].x + pen->vertex[active].point.x;
- hull_point.y = point[i].y + pen->vertex[active].point.y;
+ hull_point.x = point[i].x + pen->vertices[active].point.x;
+ hull_point.y = point[i].y + pen->vertices[active].point.y;
status = _cairo_polygon_add_point (polygon, &hull_point);
if (status)
return status;
@@ -349,10 +294,10 @@ _cairo_pen_stroke_spline_half (cairo_pen_t *pen,
slope = final_slope;
else
_cairo_slope_init (&slope, &point[i], &point[i+step]);
- if (_cairo_slope_counter_clockwise (&slope, &pen->vertex[active].slope_ccw)) {
+ if (_cairo_slope_counter_clockwise (&slope, &pen->vertices[active].slope_ccw)) {
if (++active == pen->num_vertices)
active = 0;
- } else if (_cairo_slope_clockwise (&slope, &pen->vertex[active].slope_cw)) {
+ } else if (_cairo_slope_clockwise (&slope, &pen->vertices[active].slope_cw)) {
if (--active == -1)
active = pen->num_vertices - 1;
} else {
diff --git a/src/cairo-slope.c b/src/cairo-slope.c
index 619ced1d..6d7d682f 100644
--- a/src/cairo-slope.c
+++ b/src/cairo-slope.c
@@ -34,6 +34,43 @@ _cairo_slope_init (cairo_slope_t *slope, cairo_point_t *a, cairo_point_t *b)
slope->dy = b->y - a->y;
}
+/* Compare two slopes. Slope angles begin at 0 in the direction of the
+ positive X axis and increase in the direction of the positive Y
+ axis.
+
+ WARNING: This function only gives correct results if the angular
+ difference between a and b is less than PI.
+
+ < 0 => a less positive than b
+ == 0 => a equal to be
+ > 0 => a more positive than b
+*/
+int
+_cairo_slope_compare (cairo_slope_t *a, cairo_slope_t *b)
+{
+ cairo_fixed_48_16_t diff;
+
+ diff = ((cairo_fixed_48_16_t) a->dy * (cairo_fixed_48_16_t) b->dx
+ - (cairo_fixed_48_16_t) b->dy * (cairo_fixed_48_16_t) a->dx);
+
+ if (diff > 0)
+ return 1;
+ if (diff < 0)
+ return -1;
+
+ if (a->dx == 0 && a->dy == 0)
+ return 1;
+ if (b->dx == 0 && b->dy ==0)
+ return -1;
+
+ return 0;
+}
+
+/* XXX: It might be cleaner to move away from usage of
+ _cairo_slope_clockwise/_cairo_slope_counter_clockwise in favor of
+ directly using _cairo_slope_compare.
+*/
+
/* Is a clockwise of b?
*
* NOTE: The strict equality here is not significant in and of itself,
@@ -43,8 +80,7 @@ _cairo_slope_init (cairo_slope_t *slope, cairo_point_t *a, cairo_point_t *b)
int
_cairo_slope_clockwise (cairo_slope_t *a, cairo_slope_t *b)
{
- return ((cairo_fixed_48_16_t) b->dy * (cairo_fixed_48_16_t) a->dx
- > (cairo_fixed_48_16_t) a->dy * (cairo_fixed_48_16_t) b->dx);
+ return _cairo_slope_compare (a, b) < 0;
}
int
diff --git a/src/cairo_hull.c b/src/cairo_hull.c
new file mode 100644
index 00000000..fc659853
--- /dev/null
+++ b/src/cairo_hull.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright © 2003 USC, Information Sciences Institute
+ *
+ * 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 the
+ * University of Southern California not be used in advertising or
+ * publicity pertaining to distribution of the software without
+ * specific, written prior permission. The University of Southern
+ * California makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
+ * SOUTHERN CALIFORNIA 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: Carl D. Worth <cworth@isi.edu>
+ */
+
+#include "cairoint.h"
+
+typedef struct cairo_hull
+{
+ cairo_point_t point;
+ cairo_slope_t slope;
+ int discard;
+} cairo_hull_t;
+
+static cairo_hull_t *
+_cairo_hull_create (cairo_pen_vertex_t *vertices, int num_vertices)
+{
+ int i;
+ cairo_hull_t *hull;
+ cairo_point_t *p, *extremum, tmp;
+
+ extremum = &vertices[0].point;
+ for (i = 1; i < num_vertices; i++) {
+ p = &vertices[i].point;
+ if (p->y < extremum->y || (p->y == extremum->y && p->x < extremum->x))
+ extremum = p;
+ }
+ /* Put the extremal point at the beginning of the array */
+ tmp = *extremum;
+ *extremum = vertices[0].point;
+ vertices[0].point = tmp;
+
+ hull = malloc (num_vertices * sizeof (cairo_hull_t));
+ if (hull == NULL)
+ return NULL;
+
+ for (i = 0; i < num_vertices; i++) {
+ hull[i].point = vertices[i].point;
+ _cairo_slope_init (&hull[i].slope, &hull[0].point, &hull[i].point);
+
+ /* Discard all points coincident with the extremal point */
+ if (i != 0 && hull[i].slope.dx == 0 && hull[i].slope.dy == 0)
+ hull[i].discard = 1;
+ else
+ hull[i].discard = 0;
+ }
+
+ return hull;
+}
+
+static int
+_cairo_hull_vertex_compare (const void *av, const void *bv)
+{
+ cairo_hull_t *a = (cairo_hull_t *) av;
+ cairo_hull_t *b = (cairo_hull_t *) bv;
+ int ret;
+
+ ret = _cairo_slope_compare (&a->slope, &b->slope);
+
+ /* In the case of two vertices with identical slope from the
+ extremal point discard the nearer point. */
+
+ if (ret == 0) {
+ cairo_fixed_48_16_t a_dist, b_dist;
+ a_dist = ((cairo_fixed_48_16_t) a->slope.dx * a->slope.dx +
+ (cairo_fixed_48_16_t) a->slope.dy * a->slope.dy);
+ b_dist = ((cairo_fixed_48_16_t) b->slope.dx * b->slope.dx +
+ (cairo_fixed_48_16_t) b->slope.dy * b->slope.dy);
+ if (a_dist < b_dist)
+ a->discard = 1;
+ else
+ b->discard = 1;
+ }
+
+ return ret;
+}
+
+static int
+_cairo_hull_prev_valid (cairo_hull_t *hull, int num_hull, int index)
+{
+ do {
+ /* hull[0] is always valid, so don't test and wraparound */
+ index--;
+ } while (hull[index].discard);
+
+ return index;
+}
+
+static int
+_cairo_hull_next_valid (cairo_hull_t *hull, int num_hull, int index)
+{
+ do {
+ index = (index + 1) % num_hull;
+ } while (hull[index].discard);
+
+ return index;
+}
+
+static cairo_status_t
+_cairo_hull_eliminate_concave (cairo_hull_t *hull, int num_hull)
+{
+ int i, j, k;
+ cairo_slope_t slope_ij, slope_jk;
+
+ i = 0;
+ j = _cairo_hull_next_valid (hull, num_hull, i);
+ k = _cairo_hull_next_valid (hull, num_hull, j);
+
+ do {
+ _cairo_slope_init (&slope_ij, &hull[i].point, &hull[j].point);
+ _cairo_slope_init (&slope_jk, &hull[j].point, &hull[k].point);
+
+ /* Is the angle formed by ij and jk concave? */
+ if (_cairo_slope_compare (&slope_ij, &slope_jk) >= 0) {
+ if (i == k)
+ return CAIRO_STATUS_SUCCESS;
+ hull[j].discard = 1;
+ j = i;
+ i = _cairo_hull_prev_valid (hull, num_hull, j);
+ } else {
+ i = j;
+ j = k;
+ k = _cairo_hull_next_valid (hull, num_hull, j);
+ }
+ } while (j != 0);
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+static cairo_status_t
+_cairo_hull_to_pen (cairo_hull_t *hull, cairo_pen_vertex_t *vertices, int *num_vertices)
+{
+ int i, j = 0;
+
+ for (i = 0; i < *num_vertices; i++) {
+ if (hull[i].discard)
+ continue;
+ vertices[j++].point = hull[i].point;
+ }
+
+ *num_vertices = j;
+
+ return CAIRO_STATUS_SUCCESS;
+}
+
+/* Given a set of vertices, compute the convex hull using the Graham
+ scan algorithm. */
+cairo_status_t
+_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices)
+{
+ cairo_hull_t *hull;
+ int num_hull = *num_vertices;
+
+ hull = _cairo_hull_create (vertices, num_hull);
+ if (hull == NULL)
+ return CAIRO_STATUS_NO_MEMORY;
+
+ qsort (hull + 1, num_hull - 1,
+ sizeof (cairo_hull_t), _cairo_hull_vertex_compare);
+
+ _cairo_hull_eliminate_concave (hull, num_hull);
+
+ _cairo_hull_to_pen (hull, vertices, num_vertices);
+
+ free (hull);
+
+ return CAIRO_STATUS_SUCCESS;
+}
diff --git a/src/cairo_path_stroke.c b/src/cairo_path_stroke.c
index 535d7683..a9bf266b 100644
--- a/src/cairo_path_stroke.c
+++ b/src/cairo_path_stroke.c
@@ -189,7 +189,7 @@ _cairo_stroker_join (cairo_stroker_t *stroker, cairo_stroke_face_t *in, cairo_st
tri[1] = *inpt;
while (i != stop) {
tri[2] = in->point;
- _translate_point (&tri[2], &pen->vertex[i].point);
+ _translate_point (&tri[2], &pen->vertices[i].point);
_cairo_traps_tessellate_triangle (stroker->traps, tri);
tri[1] = tri[2];
i += step;
@@ -337,7 +337,7 @@ _cairo_stroker_cap (cairo_stroker_t *stroker, cairo_stroke_face_t *f)
tri[1] = f->cw;
for (i=start; i != stop; i = (i+1) % pen->num_vertices) {
tri[2] = f->point;
- _translate_point (&tri[2], &pen->vertex[i].point);
+ _translate_point (&tri[2], &pen->vertices[i].point);
_cairo_traps_tessellate_triangle (stroker->traps, tri);
tri[1] = tri[2];
}
diff --git a/src/cairo_pen.c b/src/cairo_pen.c
index 032d505a..5e76a1b3 100644
--- a/src/cairo_pen.c
+++ b/src/cairo_pen.c
@@ -33,9 +33,6 @@ _cairo_pen_vertices_needed (double radius, double tolerance, double expansion);
static void
_cairo_pen_compute_slopes (cairo_pen_t *pen);
-static int
-_pen_vertex_compare (const void *av, const void *bv);
-
static cairo_status_t
_cairo_pen_stroke_spline_half (cairo_pen_t *pen, cairo_spline_t *spline, cairo_direction_t dir, cairo_polygon_t *polygon);
@@ -44,7 +41,7 @@ _cairo_pen_init_empty (cairo_pen_t *pen)
{
pen->radius = 0;
pen->tolerance = 0;
- pen->vertex = NULL;
+ pen->vertices = NULL;
pen->num_vertices = 0;
return CAIRO_STATUS_SUCCESS;
@@ -88,8 +85,8 @@ _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate)
if (pen->num_vertices % 2)
pen->num_vertices++;
- pen->vertex = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
- if (pen->vertex == NULL) {
+ pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
+ if (pen->vertices == NULL) {
return CAIRO_STATUS_NO_MEMORY;
}
@@ -103,7 +100,7 @@ _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate)
double theta = 2 * M_PI * i / (double) pen->num_vertices;
double dx = radius * cos (reflect ? -theta : theta);
double dy = radius * sin (reflect ? -theta : theta);
- cairo_pen_vertex_t *v = &pen->vertex[i];
+ cairo_pen_vertex_t *v = &pen->vertices[i];
cairo_matrix_transform_distance (&gstate->ctm, &dx, &dy);
v->point.x = _cairo_fixed_from_double (dx);
v->point.y = _cairo_fixed_from_double (dy);
@@ -117,7 +114,7 @@ _cairo_pen_init (cairo_pen_t *pen, double radius, cairo_gstate_t *gstate)
void
_cairo_pen_fini (cairo_pen_t *pen)
{
- free (pen->vertex);
+ free (pen->vertices);
_cairo_pen_init_empty (pen);
}
@@ -127,11 +124,11 @@ _cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other)
*pen = *other;
if (pen->num_vertices) {
- pen->vertex = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
- if (pen->vertex == NULL) {
+ pen->vertices = malloc (pen->num_vertices * sizeof (cairo_pen_vertex_t));
+ if (pen->vertices == NULL) {
return CAIRO_STATUS_NO_MEMORY;
}
- memcpy (pen->vertex, other->vertex, pen->num_vertices * sizeof (cairo_pen_vertex_t));
+ memcpy (pen->vertices, other->vertices, pen->num_vertices * sizeof (cairo_pen_vertex_t));
}
return CAIRO_STATUS_SUCCESS;
@@ -140,37 +137,23 @@ _cairo_pen_init_copy (cairo_pen_t *pen, cairo_pen_t *other)
cairo_status_t
_cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points)
{
+ cairo_pen_vertex_t *vertices;
+ int num_vertices;
int i;
- cairo_pen_vertex_t *v, *v_next, *new_vertex;
- pen->num_vertices += num_points;
- new_vertex = realloc (pen->vertex, pen->num_vertices * sizeof (cairo_pen_vertex_t));
- if (new_vertex == NULL) {
- pen->num_vertices -= num_points;
+ num_vertices = pen->num_vertices + num_points;
+ vertices = realloc (pen->vertices, num_vertices * sizeof (cairo_pen_vertex_t));
+ if (vertices == NULL)
return CAIRO_STATUS_NO_MEMORY;
- }
- pen->vertex = new_vertex;
+
+ pen->vertices = vertices;
+ pen->num_vertices = num_vertices;
/* initialize new vertices */
- for (i=0; i < num_points; i++) {
- v = &pen->vertex[pen->num_vertices-(i+1)];
- v->point = point[i];
- }
+ for (i=0; i < num_points; i++)
+ pen->vertices[pen->num_vertices-num_points+i].point = point[i];
- qsort (pen->vertex, pen->num_vertices, sizeof (cairo_pen_vertex_t), _pen_vertex_compare);
-
- /* eliminate any duplicate vertices */
- for (i=0; i < pen->num_vertices; i++ ) {
- v = &pen->vertex[i];
- v_next = (i < pen->num_vertices - 1) ? &pen->vertex[i+1] : &pen->vertex[0];
- if (_pen_vertex_compare (v, v_next) == 0) {
- pen->num_vertices--;
- memmove (&pen->vertex[i], &pen->vertex[i+1],
- (pen->num_vertices - i) * sizeof (cairo_pen_vertex_t));
- /* There may be more of the same duplicate, check again */
- i--;
- }
- }
+ _cairo_hull_compute (pen->vertices, &pen->num_vertices);
_cairo_pen_compute_slopes (pen);
@@ -199,53 +182,15 @@ _cairo_pen_compute_slopes (cairo_pen_t *pen)
for (i=0, i_prev = pen->num_vertices - 1;
i < pen->num_vertices;
i_prev = i++) {
- prev = &pen->vertex[i_prev];
- v = &pen->vertex[i];
- next = &pen->vertex[(i + 1) % pen->num_vertices];
+ prev = &pen->vertices[i_prev];
+ v = &pen->vertices[i];
+ next = &pen->vertices[(i + 1) % pen->num_vertices];
_cairo_slope_init (&v->slope_cw, &prev->point, &v->point);
_cairo_slope_init (&v->slope_ccw, &v->point, &next->point);
}
}
-/* Is a further clockwise from (1,0) than b?
- *
- * There are a two special cases to consider:
- * 1) a and b are not in the same half plane.
- * 2) both a and b are on the X axis
- * After that, the computation is a simple slope comparison.
- */
-static int
-_pen_vertex_compare (const void *av, const void *bv)
-{
- const cairo_pen_vertex_t *a = av;
- const cairo_pen_vertex_t *b = bv;
- cairo_fixed_48_16_t diff;
-
- int a_above = a->point.y >= 0;
- int b_above = b->point.y >= 0;
-
- if (a_above != b_above)
- return b_above - a_above;
-
- if (a->point.y == 0 && b->point.y == 0) {
- int a_right = a->point.x >= 0;
- int b_right = b->point.x >= 0;
-
- if (a_right != b_right)
- return b_right - a_right;
- }
-
- diff = ((cairo_fixed_48_16_t) a->point.y * (cairo_fixed_48_16_t) b->point.x
- - (cairo_fixed_48_16_t) b->point.y * (cairo_fixed_48_16_t) a->point.x);
-
- if (diff > 0)
- return 1;
- if (diff < 0)
- return -1;
- return 0;
-}
-
/* Find active pen vertex for clockwise edge of stroke at the given slope.
*
* NOTE: The behavior of this function is sensitive to the sense of
@@ -264,8 +209,8 @@ _cairo_pen_find_active_cw_vertex_index (cairo_pen_t *pen,
int i;
for (i=0; i < pen->num_vertices; i++) {
- if (_cairo_slope_clockwise (slope, &pen->vertex[i].slope_ccw)
- && _cairo_slope_counter_clockwise (slope, &pen->vertex[i].slope_cw))
+ if (_cairo_slope_clockwise (slope, &pen->vertices[i].slope_ccw)
+ && _cairo_slope_counter_clockwise (slope, &pen->vertices[i].slope_cw))
break;
}
@@ -292,8 +237,8 @@ _cairo_pen_find_active_ccw_vertex_index (cairo_pen_t *pen,
slope_reverse.dy = -slope_reverse.dy;
for (i=pen->num_vertices-1; i >= 0; i--) {
- if (_cairo_slope_counter_clockwise (&pen->vertex[i].slope_ccw, &slope_reverse)
- && _cairo_slope_clockwise (&pen->vertex[i].slope_cw, &slope_reverse))
+ if (_cairo_slope_counter_clockwise (&pen->vertices[i].slope_ccw, &slope_reverse)
+ && _cairo_slope_clockwise (&pen->vertices[i].slope_cw, &slope_reverse))
break;
}
@@ -339,8 +284,8 @@ _cairo_pen_stroke_spline_half (cairo_pen_t *pen,
i = start;
while (i != stop) {
- hull_point.x = point[i].x + pen->vertex[active].point.x;
- hull_point.y = point[i].y + pen->vertex[active].point.y;
+ hull_point.x = point[i].x + pen->vertices[active].point.x;
+ hull_point.y = point[i].y + pen->vertices[active].point.y;
status = _cairo_polygon_add_point (polygon, &hull_point);
if (status)
return status;
@@ -349,10 +294,10 @@ _cairo_pen_stroke_spline_half (cairo_pen_t *pen,
slope = final_slope;
else
_cairo_slope_init (&slope, &point[i], &point[i+step]);
- if (_cairo_slope_counter_clockwise (&slope, &pen->vertex[active].slope_ccw)) {
+ if (_cairo_slope_counter_clockwise (&slope, &pen->vertices[active].slope_ccw)) {
if (++active == pen->num_vertices)
active = 0;
- } else if (_cairo_slope_clockwise (&slope, &pen->vertex[active].slope_cw)) {
+ } else if (_cairo_slope_clockwise (&slope, &pen->vertices[active].slope_cw)) {
if (--active == -1)
active = pen->num_vertices - 1;
} else {
diff --git a/src/cairo_slope.c b/src/cairo_slope.c
index 619ced1d..6d7d682f 100644
--- a/src/cairo_slope.c
+++ b/src/cairo_slope.c
@@ -34,6 +34,43 @@ _cairo_slope_init (cairo_slope_t *slope, cairo_point_t *a, cairo_point_t *b)
slope->dy = b->y - a->y;
}
+/* Compare two slopes. Slope angles begin at 0 in the direction of the
+ positive X axis and increase in the direction of the positive Y
+ axis.
+
+ WARNING: This function only gives correct results if the angular
+ difference between a and b is less than PI.
+
+ < 0 => a less positive than b
+ == 0 => a equal to be
+ > 0 => a more positive than b
+*/
+int
+_cairo_slope_compare (cairo_slope_t *a, cairo_slope_t *b)
+{
+ cairo_fixed_48_16_t diff;
+
+ diff = ((cairo_fixed_48_16_t) a->dy * (cairo_fixed_48_16_t) b->dx
+ - (cairo_fixed_48_16_t) b->dy * (cairo_fixed_48_16_t) a->dx);
+
+ if (diff > 0)
+ return 1;
+ if (diff < 0)
+ return -1;
+
+ if (a->dx == 0 && a->dy == 0)
+ return 1;
+ if (b->dx == 0 && b->dy ==0)
+ return -1;
+
+ return 0;
+}
+
+/* XXX: It might be cleaner to move away from usage of
+ _cairo_slope_clockwise/_cairo_slope_counter_clockwise in favor of
+ directly using _cairo_slope_compare.
+*/
+
/* Is a clockwise of b?
*
* NOTE: The strict equality here is not significant in and of itself,
@@ -43,8 +80,7 @@ _cairo_slope_init (cairo_slope_t *slope, cairo_point_t *a, cairo_point_t *b)
int
_cairo_slope_clockwise (cairo_slope_t *a, cairo_slope_t *b)
{
- return ((cairo_fixed_48_16_t) b->dy * (cairo_fixed_48_16_t) a->dx
- > (cairo_fixed_48_16_t) a->dy * (cairo_fixed_48_16_t) b->dx);
+ return _cairo_slope_compare (a, b) < 0;
}
int
diff --git a/src/cairoint.h b/src/cairoint.h
index e61c011d..8a38af0e 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -222,8 +222,8 @@ typedef struct cairo_pen {
double radius;
double tolerance;
+ cairo_pen_vertex_t *vertices;
int num_vertices;
- cairo_pen_vertex_t *vertex;
} cairo_pen_t;
typedef struct cairo_color cairo_color_t;
@@ -715,6 +715,10 @@ _cairo_font_show_text (cairo_font_t *font,
double y,
const unsigned char *utf8);
+/* cairo_hull.c */
+extern cairo_status_t
+_cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices);
+
/* cairo_path.c */
extern void __internal_linkage
_cairo_path_init (cairo_path_t *path);