Skip to content

Commit

Permalink
tessellator: offset working coordinates to be nonnegative
Browse files Browse the repository at this point in the history
This patch improves the translation invariance of the tessellator
by offsetting all input coordinates to be nonnegative and paves
the way for future optimisations using the coordinate range.

Also changes the assertions to make sure that it is safe to add
the guard bits.  This needs to be changed to do something sensible
about input coordinates that are too large instead of croaking.
The plan is to steal the guard bits from the least significant
instead of the most significant user bits, and having all coordinates
nonnegative will make the rounding involved there easier.
  • Loading branch information
M Joonas Pihlaja committed Dec 6, 2006
1 parent 633c51b commit f8ba749
Showing 1 changed file with 96 additions and 30 deletions.
126 changes: 96 additions & 30 deletions src/cairo-bentley-ottmann.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
#include "cairo-freelist-private.h"

#define CAIRO_BO_GUARD_BITS 2
#define CAIRO_BO_GUARD_UNITY (1 << CAIRO_BO_GUARD_BITS)
#define CAIRO_BO_GUARD_HALF (CAIRO_BO_GUARD_UNITY / 2)
#define _cairo_bo_add_guard_bits(x) ((x) * CAIRO_BO_GUARD_UNITY)
#define _cairo_bo_strip_guard_bits(x) ((x) / CAIRO_BO_GUARD_UNITY)

typedef cairo_point_t cairo_bo_point32_t;

Expand Down Expand Up @@ -72,6 +76,13 @@ struct _cairo_bo_trap {
struct _cairo_bo_traps {
cairo_traps_t *traps;
cairo_freelist_t freelist;

/* These form the extent of the original input points without any
* guard bits. */
cairo_fixed_t xmin;
cairo_fixed_t ymin;
cairo_fixed_t xmax;
cairo_fixed_t ymax;
};

struct _cairo_bo_edge {
Expand Down Expand Up @@ -765,12 +776,6 @@ _cairo_bo_event_queue_init (cairo_bo_event_queue_t *event_queue,
sorted_event_ptrs[2*i] = &events[2*i];
sorted_event_ptrs[2*i+1] = &events[2*i+1];

/* We must not be given horizontal edges. */
assert (edges[i].top.y != edges[i].bottom.y);

/* We also must not be given any upside-down edges. */
assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0);

/* Initialize "middle" to top */
edges[i].middle = edges[i].top;

Expand Down Expand Up @@ -1075,21 +1080,25 @@ _cairo_bo_edge_end_trap (cairo_bo_edge_t *left,
if (right->bottom.y < bot)
bot = right->bottom.y;

fixed_top = trap->top >> CAIRO_BO_GUARD_BITS;
fixed_bot = bot >> CAIRO_BO_GUARD_BITS;
fixed_top = _cairo_bo_strip_guard_bits (trap->top);
fixed_bot = _cairo_bo_strip_guard_bits (bot);

/* Only emit trapezoids with positive height. */
if (fixed_top < fixed_bot) {
cairo_point_t left_top, left_bot, right_top, right_bot;

left_top.x = left->top.x >> CAIRO_BO_GUARD_BITS;
left_top.y = left->top.y >> CAIRO_BO_GUARD_BITS;
right_top.x = right->top.x >> CAIRO_BO_GUARD_BITS;
right_top.y = right->top.y >> CAIRO_BO_GUARD_BITS;
left_bot.x = left->bottom.x >> CAIRO_BO_GUARD_BITS;
left_bot.y = left->bottom.y >> CAIRO_BO_GUARD_BITS;
right_bot.x = right->bottom.x >> CAIRO_BO_GUARD_BITS;
right_bot.y = right->bottom.y >> CAIRO_BO_GUARD_BITS;
cairo_fixed_t xmin = bo_traps->xmin;
cairo_fixed_t ymin = bo_traps->ymin;
fixed_top += ymin;
fixed_bot += ymin;

left_top.x = _cairo_bo_strip_guard_bits (left->top.x) + xmin;
left_top.y = _cairo_bo_strip_guard_bits (left->top.y) + ymin;
right_top.x = _cairo_bo_strip_guard_bits (right->top.x) + xmin;
right_top.y = _cairo_bo_strip_guard_bits (right->top.y) + ymin;
left_bot.x = _cairo_bo_strip_guard_bits (left->bottom.x) + xmin;
left_bot.y = _cairo_bo_strip_guard_bits (left->bottom.y) + ymin;
right_bot.x = _cairo_bo_strip_guard_bits (right->bottom.x) + xmin;
right_bot.y = _cairo_bo_strip_guard_bits (right->bottom.y) + ymin;

/* Avoid emitting the trapezoid if it is obviously degenerate.
* TODO: need a real collinearity test here for the cases
Expand Down Expand Up @@ -1154,10 +1163,18 @@ _cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *edge,

static void
_cairo_bo_traps_init (cairo_bo_traps_t *bo_traps,
cairo_traps_t *traps)
cairo_traps_t *traps,
cairo_fixed_t xmin,
cairo_fixed_t ymin,
cairo_fixed_t xmax,
cairo_fixed_t ymax)
{
bo_traps->traps = traps;
_cairo_freelist_init (&bo_traps->freelist, sizeof(cairo_bo_trap_t));
bo_traps->xmin = xmin;
bo_traps->ymin = ymin;
bo_traps->xmax = xmax;
bo_traps->ymax = ymax;
}

static void
Expand Down Expand Up @@ -1196,7 +1213,6 @@ _cairo_bo_sweep_line_validate (cairo_bo_sweep_line_t *sweep_line)
static cairo_status_t
_active_edges_to_traps (cairo_bo_edge_t *head,
int32_t top,
int32_t bottom,
cairo_fill_rule_t fill_rule,
cairo_bo_traps_t *bo_traps)
{
Expand Down Expand Up @@ -1242,6 +1258,10 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges,
int num_edges,
cairo_fill_rule_t fill_rule,
cairo_traps_t *traps,
cairo_fixed_t xmin,
cairo_fixed_t ymin,
cairo_fixed_t xmax,
cairo_fixed_t ymax,
int *num_intersections)
{
cairo_status_t status = CAIRO_STATUS_SUCCESS;
Expand All @@ -1258,16 +1278,15 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges,

for (i = 0; i < num_edges; i++) {
edge = &edges[i];
edge->top.x <<= CAIRO_BO_GUARD_BITS;
edge->top.y <<= CAIRO_BO_GUARD_BITS;
edge->middle = edge->top;
edge->bottom.x <<= CAIRO_BO_GUARD_BITS;
edge->bottom.y <<= CAIRO_BO_GUARD_BITS;
edge->top.x = _cairo_bo_add_guard_bits (edge->top.x);
edge->top.y = _cairo_bo_add_guard_bits (edge->top.y);
edge->bottom.x = _cairo_bo_add_guard_bits (edge->bottom.x);
edge->bottom.y = _cairo_bo_add_guard_bits (edge->bottom.y);
}

_cairo_bo_event_queue_init (&event_queue, edges, num_edges);
_cairo_bo_sweep_line_init (&sweep_line);
_cairo_bo_traps_init (&bo_traps, traps);
_cairo_bo_traps_init (&bo_traps, traps, xmin, ymin, xmax, ymax);

#if DEBUG_PRINT_STATE
print_state ("After initializing", &event_queue, &sweep_line);
Expand All @@ -1281,7 +1300,7 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges,

if (event->point.y != sweep_line.current_y) {
status = _active_edges_to_traps (sweep_line.head,
sweep_line.current_y, event->point.y,
sweep_line.current_y,
fill_rule, &bo_traps);
if (status)
goto unwind;
Expand Down Expand Up @@ -1385,6 +1404,17 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges,
return status;
}

static void
update_minmax(cairo_fixed_t *inout_min,
cairo_fixed_t *inout_max,
cairo_fixed_t v)
{
if (v < *inout_min)
*inout_min = v;
if (v > *inout_max)
*inout_max = v;
}

cairo_status_t
_cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps,
cairo_polygon_t *polygon,
Expand All @@ -1393,16 +1423,35 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps,
int intersections;
cairo_status_t status;
cairo_bo_edge_t *edges;
cairo_fixed_t xmin = 0x7FFFFFFF;
cairo_fixed_t ymin = 0x7FFFFFFF;
cairo_fixed_t xmax = -0x80000000;
cairo_fixed_t ymax = -0x80000000;
int i;

if (0 == polygon->num_edges)
return CAIRO_STATUS_SUCCESS;

edges = malloc (polygon->num_edges * sizeof (cairo_bo_edge_t));
if (edges == NULL)
return CAIRO_STATUS_NO_MEMORY;

/* Figure out the bounding box of input coordinates and validate
* that we're not given invalid polygon edges. */
for (i = 0; i < polygon->num_edges; i++) {
edges[i].top = polygon->edges[i].edge.p1;
edges[i].middle = edges[i].top;
edges[i].bottom = polygon->edges[i].edge.p2;
update_minmax(&xmin, &xmax, polygon->edges[i].edge.p1.x);
update_minmax(&ymin, &ymax, polygon->edges[i].edge.p1.y);
update_minmax(&xmin, &xmax, polygon->edges[i].edge.p2.x);
update_minmax(&ymin, &ymax, polygon->edges[i].edge.p2.y);
assert (polygon->edges[i].edge.p1.y <= polygon->edges[i].edge.p2.y &&
"BUG: tessellator given upside down or horizontal edges");
}

for (i = 0; i < polygon->num_edges; i++) {
edges[i].top.x = polygon->edges[i].edge.p1.x - xmin;
edges[i].top.y = polygon->edges[i].edge.p1.y - ymin;
edges[i].bottom.x = polygon->edges[i].edge.p2.x - xmin;
edges[i].bottom.y = polygon->edges[i].edge.p2.y - ymin;
/* XXX: The 'clockWise' name that cairo_polygon_t uses is
* totally bogus. It's really a (negated!) description of
* whether the edge is reversed. */
Expand All @@ -1411,14 +1460,31 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps,
edges[i].prev = NULL;
edges[i].next = NULL;
edges[i].sweep_line_elt = NULL;

/* Make sure the input coordinates aren't too large that they
* overflow after we later shift them to make room for the
* guard bits. Note that the coordinates should now all be
* non-negative, so as a side effect we will know that
* absolute values of coordinate deltas all fit in 31 bits.
* If the original polygon input coordinates span a >= 2^31
* range, we will catch that here, as then the offsetting by
* (xoffset,yoffset) won't have brought them into range. XXX:
* relies on unsigned comparison. */
assert ((uint32_t)edges[i].top.x < (1UL << (31-CAIRO_BO_GUARD_BITS)) &&
(uint32_t)edges[i].top.y < (1UL << (31-CAIRO_BO_GUARD_BITS)) &&
(uint32_t)edges[i].bottom.x < (1UL << (31-CAIRO_BO_GUARD_BITS)) &&
(uint32_t)edges[i].bottom.y < (1UL << (31-CAIRO_BO_GUARD_BITS)) &&
"FIXME: input coordinates to tessellator too magnificent");
}

/* XXX: This would be the convenient place to throw in multiple
* passes of the Bentley-Ottmann algorithm. It would merely
* require storing the results of each pass into a temporary
* cairo_traps_t. */
status = _cairo_bentley_ottmann_tessellate_bo_edges (edges, polygon->num_edges,
fill_rule, traps, &intersections);
fill_rule, traps,
xmin, ymin, xmax, ymax,
&intersections);

free (edges);

Expand Down

0 comments on commit f8ba749

Please sign in to comment.