perf: new-tessellator: Deferred trapezoid generation (first try)

This commit is contained in:
Joonas Pihlaja 2006-11-22 08:30:28 +02:00 committed by Carl Worth
parent 6bd72ce74a
commit fac3684e68
4 changed files with 285 additions and 23 deletions

View file

@ -180,6 +180,8 @@ libcairo_la_SOURCES = \
cairo-fixed.c \
cairo-font.c \
cairo-font-options.c \
cairo-freelist.c \
cairo-freelist-private.h \
cairo-gstate.c \
cairo-gstate-private.h \
cairo-hash.c \

View file

@ -37,6 +37,7 @@
#include "cairoint.h"
#include "cairo-skiplist-private.h"
#include "cairo-freelist-private.h"
#define CAIRO_BO_GUARD_BITS 2
@ -59,6 +60,19 @@ typedef struct _cairo_bo_intersect_point {
typedef struct _cairo_bo_edge cairo_bo_edge_t;
typedef struct _sweep_line_elt sweep_line_elt_t;
typedef struct _cairo_bo_trap cairo_bo_trap_t;
typedef struct _cairo_bo_traps cairo_bo_traps_t;
/* A deferred trapezoid of an edge. */
struct _cairo_bo_trap {
cairo_bo_edge_t *right;
int32_t top;
};
struct _cairo_bo_traps {
cairo_traps_t *traps;
cairo_freelist_t freelist;
};
struct _cairo_bo_edge {
cairo_bo_point32_t top;
@ -67,6 +81,7 @@ struct _cairo_bo_edge {
cairo_bool_t reversed;
cairo_bo_edge_t *prev;
cairo_bo_edge_t *next;
cairo_bo_trap_t *deferred_trap;
sweep_line_elt_t *sweep_line_elt;
};
@ -1021,6 +1036,101 @@ print_state (const char *msg,
}
#endif
/* Adds the trapezoid, if any, of the left edge to the cairo_traps_t
* of bo_traps. */
static cairo_status_t
_cairo_bo_edge_end_trap (cairo_bo_edge_t *left,
int32_t bot,
cairo_bo_traps_t *bo_traps)
{
cairo_fixed_t fixed_top, fixed_bot;
cairo_status_t status = CAIRO_STATUS_SUCCESS;
cairo_bo_trap_t *trap = left->deferred_trap;
if (!trap)
return CAIRO_STATUS_SUCCESS;
fixed_top = trap->top >> CAIRO_BO_GUARD_BITS;
fixed_bot = bot >> CAIRO_BO_GUARD_BITS;
if (fixed_top < fixed_bot) {
cairo_bo_edge_t *right = trap->right;
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;
status = _cairo_traps_add_trap_from_points (bo_traps->traps,
fixed_top,
fixed_bot,
left_top, left_bot,
right_top, right_bot);
#if DEBUG_PRINT_STATE
printf ("Deferred trap: left=(%08x, %08x)-(%08x,%08x) "
"right=(%08x,%08x)-(%08x,%08x) top=%08x, bot=%08x\n",
left->top.x, left->top.y, left->bottom.x, left->bottom.y,
right->top.x, right->top.y, right->bottom.x, right->bottom.y,
trap->top, bot);
#endif
}
_cairo_freelist_free (&bo_traps->freelist, trap);
left->deferred_trap = NULL;
return status;
}
/* Start a new trapezoid at the given top y coordinate, whose edges
* are `edge' and `edge->next'. If `edge' already has a trapezoid,
* then either add it to the traps in `bo_traps', if the trapezoid's
* right edge differs from `edge->next', or do nothing if the new
* trapezoid would be a continuation of the existing one. */
static cairo_status_t
_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *edge,
int32_t top,
cairo_bo_traps_t *bo_traps)
{
cairo_status_t status;
cairo_bo_trap_t *trap = edge->deferred_trap;
if (trap) {
if (trap->right == edge->next) return CAIRO_STATUS_SUCCESS;
status = _cairo_bo_edge_end_trap (edge, top, bo_traps);
if (status)
return status;
}
if (edge->next) {
trap = edge->deferred_trap = _cairo_freelist_alloc (&bo_traps->freelist);
if (!edge->deferred_trap)
return CAIRO_STATUS_NO_MEMORY;
trap->right = edge->next;
trap->top = top;
}
return CAIRO_STATUS_SUCCESS;
}
static void
_cairo_bo_traps_init (cairo_bo_traps_t *bo_traps,
cairo_traps_t *traps)
{
bo_traps->traps = traps;
_cairo_freelist_init (&bo_traps->freelist, sizeof(cairo_bo_trap_t));
}
static void
_cairo_bo_traps_fini (cairo_bo_traps_t *bo_traps)
{
_cairo_freelist_fini (&bo_traps->freelist);
}
static void
_cairo_bo_sweep_line_validate (cairo_bo_sweep_line_t *sweep_line)
{
@ -1047,17 +1157,17 @@ _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_traps_t *traps)
cairo_bo_traps_t *bo_traps)
{
cairo_status_t status;
int in_out = 0;
cairo_bo_edge_t *edge;
cairo_bo_point32_t left_top, left_bottom, right_top, right_bottom;
for (edge = head; edge && edge->next; edge = edge->next) {
if (fill_rule == CAIRO_FILL_RULE_WINDING) {
@ -1065,31 +1175,23 @@ _active_edges_to_traps (cairo_bo_edge_t *head,
in_out++;
else
in_out--;
if (in_out == 0)
if (in_out == 0) {
status = _cairo_bo_edge_end_trap (edge, top, bo_traps);
if (status)
return status;
continue;
}
} else {
in_out++;
if ((in_out & 1) == 0)
if ((in_out & 1) == 0) {
status = _cairo_bo_edge_end_trap (edge, top, bo_traps);
if (status)
return status;
continue;
}
}
#if DEBUG_PRINT_STATE
printf ("Adding trap 0x%x 0x%x: ", top, bottom);
_cairo_bo_edge_print (edge);
_cairo_bo_edge_print (edge->next);
#endif
left_top.x = edge->middle.x >> CAIRO_BO_GUARD_BITS;
left_top.y = edge->middle.y >> CAIRO_BO_GUARD_BITS;
left_bottom.x = edge->bottom.x >> CAIRO_BO_GUARD_BITS;
left_bottom.y = edge->bottom.y >> CAIRO_BO_GUARD_BITS;
right_top.x = edge->next->middle.x >> CAIRO_BO_GUARD_BITS;
right_top.y = edge->next->middle.y >> CAIRO_BO_GUARD_BITS;
right_bottom.x = edge->next->bottom.x >> CAIRO_BO_GUARD_BITS;
right_bottom.y = edge->next->bottom.y >> CAIRO_BO_GUARD_BITS;
status = _cairo_traps_add_trap_from_points (traps,
top >> CAIRO_BO_GUARD_BITS,
bottom >> CAIRO_BO_GUARD_BITS,
left_top, left_bottom,
right_top, right_bottom);
status = _cairo_bo_edge_start_or_continue_trap (edge, top, bo_traps);
if (status)
return status;
}
@ -1111,6 +1213,7 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges,
int intersection_count = 0;
cairo_bo_event_queue_t event_queue;
cairo_bo_sweep_line_t sweep_line;
cairo_bo_traps_t bo_traps;
cairo_bo_event_t *event, event_saved;
cairo_bo_edge_t *edge;
cairo_bo_edge_t *left, *right;
@ -1129,6 +1232,7 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges,
_cairo_bo_event_queue_init (&event_queue, edges, num_edges);
_cairo_bo_sweep_line_init (&sweep_line);
_cairo_bo_traps_init (&bo_traps, traps);
#if DEBUG_PRINT_STATE
print_state ("After initializing", &event_queue, &sweep_line);
@ -1143,7 +1247,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,
fill_rule, traps);
fill_rule, &bo_traps);
if (status)
goto unwind;
@ -1184,6 +1288,10 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges,
_cairo_bo_sweep_line_delete (&sweep_line, edge);
status = _cairo_bo_edge_end_trap (edge, edge->bottom.y, &bo_traps);
if (status)
goto unwind;
_cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right);
#if DEBUG_PRINT_STATE
@ -1229,6 +1337,14 @@ _cairo_bentley_ottmann_tessellate_bo_edges (cairo_bo_edge_t *edges,
*num_intersections = intersection_count;
unwind:
for (edge = sweep_line.head; edge; edge = edge->next) {
cairo_status_t status2 = _cairo_bo_edge_end_trap (edge,
sweep_line.current_y,
&bo_traps);
if (!status)
status = status2;
}
_cairo_bo_traps_fini (&bo_traps);
_cairo_bo_sweep_line_fini (&sweep_line);
_cairo_bo_event_queue_fini (&event_queue);
return status;
@ -1256,6 +1372,7 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps,
* totally bogus. It's really a (negated!) description of
* whether the edge is reversed. */
edges[i].reversed = (! polygon->edges[i].clockWise);
edges[i].deferred_trap = NULL;
edges[i].prev = NULL;
edges[i].next = NULL;
edges[i].sweep_line_elt = NULL;

View file

@ -0,0 +1,71 @@
/*
* Copyright © 2006 Joonas Pihlaja
*
* 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 copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS 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.
*/
#ifndef CAIRO_FREELIST_H
#define CAIRO_FREELIST_H
#include <stddef.h>
/* Opaque implementation types. */
struct _cairo_freelist;
struct _cairo_freelist_node;
typedef struct _cairo_freelist cairo_freelist_t;
typedef struct _cairo_freelist_node cairo_freelist_node_t;
struct _cairo_freelist_node {
cairo_freelist_node_t *next;
};
struct _cairo_freelist {
cairo_freelist_node_t *first_free_node;
unsigned nodesize;
};
/* Initialise a freelist that will be responsible for allocating
* nodes of size nodesize. */
void
_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize);
/* Deallocate any nodes in the freelist. */
void
_cairo_freelist_fini (cairo_freelist_t *freelist);
/* Allocate a new node from the freelist. If the freelist contains no
* nodes, a new one will be allocated using malloc(). The caller is
* responsible for calling _cairo_freelist_free() or free() on the
* returned node. Returns NULL on memory allocation error. */
void *
_cairo_freelist_alloc (cairo_freelist_t *freelist);
/* Allocate a new node from the freelist. If the freelist contains no
* nodes, a new one will be allocated using calloc(). The caller is
* responsible for calling _cairo_freelist_free() or free() on the
* returned node. Returns NULL on memory allocation error. */
void *
_cairo_freelist_calloc (cairo_freelist_t *freelist);
/* Return a node to the freelist. This does not deallocate the memory,
* but makes it available for later reuse by
* _cairo_freelist_alloc(). */
void
_cairo_freelist_free (cairo_freelist_t *freelist, void *node);
#endif /* CAIRO_FREELIST_H */

72
src/cairo-freelist.c Normal file
View file

@ -0,0 +1,72 @@
/*
* Copyright © 2006 Joonas Pihlaja
*
* 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 copyright holders not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. The copyright holders make no representations
* about the suitability of this software for any purpose. It is provided "as
* is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
* EVENT SHALL THE COPYRIGHT HOLDERS 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.
*/
#include <stdlib.h>
#include <string.h>
#include "cairo-freelist-private.h"
void
_cairo_freelist_init (cairo_freelist_t *freelist, unsigned nodesize)
{
memset (freelist, 0, sizeof(cairo_freelist_t));
freelist->nodesize = nodesize;
}
void
_cairo_freelist_fini (cairo_freelist_t *freelist)
{
cairo_freelist_node_t *node = freelist->first_free_node;
while (node) {
cairo_freelist_node_t *next = node->next;
free (node);
node = next;
}
}
void *
_cairo_freelist_alloc (cairo_freelist_t *freelist)
{
if (freelist->first_free_node) {
cairo_freelist_node_t *node = freelist->first_free_node;
freelist->first_free_node = node->next;
return (void*)node;
}
return malloc (freelist->nodesize);
}
void *
_cairo_freelist_calloc (cairo_freelist_t *freelist)
{
void *node = _cairo_freelist_alloc (freelist);
if (node)
memset (node, 0, freelist->nodesize);
return node;
}
void
_cairo_freelist_free (cairo_freelist_t *freelist, void *voidnode)
{
cairo_freelist_node_t *node = voidnode;
if (node) {
node->next = freelist->first_free_node;
freelist->first_free_node = node;
}
}