Generate convex hull of pen before stroking.

This commit is contained in:
Carl Worth 2003-10-04 14:34:42 +00:00
parent a249bd717c
commit 61726a88f2
11 changed files with 534 additions and 179 deletions

View file

@ -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

View file

@ -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 \

190
src/cairo-hull.c Normal file
View file

@ -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;
}

View file

@ -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];
}

View file

@ -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 {

View file

@ -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

190
src/cairo_hull.c Normal file
View file

@ -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;
}

View file

@ -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];
}

View file

@ -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 {

View file

@ -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

View file

@ -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);