From f8ba74917296be226f7a957ad1a26685bb6d846c Mon Sep 17 00:00:00 2001 From: M Joonas Pihlaja Date: Tue, 5 Dec 2006 22:56:22 +0200 Subject: [PATCH] tessellator: offset working coordinates to be nonnegative 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. --- src/cairo-bentley-ottmann.c | 126 +++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 30 deletions(-) diff --git a/src/cairo-bentley-ottmann.c b/src/cairo-bentley-ottmann.c index 3767fdf14..a0d650210 100644 --- a/src/cairo-bentley-ottmann.c +++ b/src/cairo-bentley-ottmann.c @@ -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; @@ -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 { @@ -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; @@ -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 @@ -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 @@ -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) { @@ -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; @@ -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); @@ -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; @@ -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, @@ -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. */ @@ -1411,6 +1460,21 @@ _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 @@ -1418,7 +1482,9 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, * 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);