diff options
author | Carl Worth <cworth@cworth.org> | 2003-10-04 14:34:42 +0000 |
---|---|---|
committer | Carl Worth <cworth@cworth.org> | 2003-10-04 14:34:42 +0000 |
commit | 61726a88f24efcbabdff980abdfe1ff8301315b2 (patch) | |
tree | b14a5724e721f01fd4767611e9ef89950f7b7843 | |
parent | a249bd717c194d03d480d7803351ee6f21daf0c2 (diff) |
Generate convex hull of pen before stroking.
-rw-r--r-- | ChangeLog | 8 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/cairo-hull.c | 190 | ||||
-rw-r--r-- | src/cairo-path-stroke.c | 4 | ||||
-rw-r--r-- | src/cairo-pen.c | 115 | ||||
-rw-r--r-- | src/cairo-slope.c | 40 | ||||
-rw-r--r-- | src/cairo_hull.c | 190 | ||||
-rw-r--r-- | src/cairo_path_stroke.c | 4 | ||||
-rw-r--r-- | src/cairo_pen.c | 115 | ||||
-rw-r--r-- | src/cairo_slope.c | 40 | ||||
-rw-r--r-- | src/cairoint.h | 6 |
11 files changed, 534 insertions, 179 deletions
@@ -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); |