/* Opaque type for scan converting. */ typedefstruct glitter_scan_converter glitter_scan_converter_t;
/* Reset a scan converter to accept polygon edges and set the clip box *inpixels.AllocatesO(ymax-ymin)bytesofmemory.Theclipbox *issettointegerpixelcoordinatesxmin<=x<xmax,ymin<=y<
* ymax. */
I glitter_status_t
glitter_scan_converter_reset(
glitter_scan_converter_t *converter, int xmin, int ymin, int xmax, int ymax);
/* Render the polygon in the scan converter to the given A8 format *imageraster.Onlythepixelsaccessibleaspixels[y*stride+x]for *x,yinsidetheclipboxarewrittento,wherexmin<=x<xmax, *ymin<=y<ymax.Theimageisassumedtobeclearoninput. * *Ifnonzero_fillistruethentheinteriorofthepolygonis *computedwiththenon-zerofillrule.Otherwisetheeven-oddfill *ruleisused. *
* The scan converter must be reset or destroyed after this call. */
/* Use GRID_X/Y_BITS to define GRID_X/Y if they're available. */ #ifdef GRID_X_BITS # define GRID_X (1 << GRID_X_BITS) #endif #ifdef GRID_Y_BITS # define GRID_Y (1 << GRID_Y_BITS) #endif
/* The GRID_X_TO_INT_FRAC macro splits a grid scaled coordinate into
* integer and fractional parts. The integer part is floored. */ #ifdefined(GRID_X_TO_INT_FRAC) /* do nothing */ #elifdefined(GRID_X_BITS) # define GRID_X_TO_INT_FRAC(x, i, f) \
_GRID_TO_INT_FRAC_shift(x, i, f, GRID_X_BITS) #else # define GRID_X_TO_INT_FRAC(x, i, f) \
_GRID_TO_INT_FRAC_general(x, i, f, GRID_X) #endif
#define _GRID_TO_INT_FRAC_general(t, i, f, m) do { \
(i) = (t) / (m); \
(f) = (t) % (m); \ if ((f) < 0) { \
--(i); \
(f) += (m); \
} \
} while (0)
#define _GRID_TO_INT_FRAC_shift(t, i, f, b) do { \
(f) = (t) & ((1 << (b)) - 1); \
(i) = (t) >> (b); \
} while (0)
/* A grid area is a real in [0,1] scaled by 2*GRID_X*GRID_Y. We want *tobeabletorepresentexactlyareasofsubpixeltrapezoidswhose *verticesaregiveningridscaledcoordinates.Thescalefactor *comesfromneedingtoaccuratelyrepresentthearea0.5*dx*dyofa
* triangle with base dx and height dy in grid scaled numbers. */ #define GRID_XY (2*GRID_X*GRID_Y) /* Unit area on the grid. */
/* Header for a chunk of memory in a memory pool. */ struct _pool_chunk { /* # bytes used in this chunk. */
size_t size;
/* # bytes total in this chunk */
size_t capacity;
/* Pointer to the previous chunk or %NULL if this is the sentinel
* chunk in the pool header. */ struct _pool_chunk *prev_chunk;
/* Actual data starts here. Well aligned for pointers. */
};
/* A memory pool. This is supposed to be embedded on the stack or *withinsomeotherstructure.Itmayoptionallybefollowedbyan *embeddedarrayfromwhichrequestsarefulfilleduntil
* malloc needs to be called to allocate a first real chunk. */ struct pool { /* Chunk we're allocating from. */ struct _pool_chunk *current;
jmp_buf *jmp;
/* Free list of previously allocated chunks. All have >= default
* capacity. */ struct _pool_chunk *first_free;
/* The default capacity of a chunk. */
size_t default_capacity;
/* Header for the sentinel chunk. Directly following the pool *structshouldbesomespaceforembeddedelementsfromwhich
* the sentinel chunk allocates from. */ struct _pool_chunk sentinel[1];
};
/* A polygon edge. */ struct edge { /* Next in y-bucket or active list. */ struct edge *next, *prev;
/* Number of subsample rows remaining to scan convert of this
* edge. */
grid_scaled_y_t height_left;
/* Original sign of the edge: +1 for downwards, -1 for upwards
* edges. */ int dir; int vertical;
/* Current x coordinate while the edge is on the active *list.Initialisedtothexcoordinateofthetopofthe *edge.Thequotientisingrid_scaled_x_tunitsandthe
* remainder is mod dy in grid_scaled_y_t units.*/ struct quorem x;
/* Advance of the current x when moving down a subsample line. */ struct quorem dxdy;
/* The clipped y of the top of the edge. */
grid_scaled_y_t ytop;
/* y2-y1 after orienting the edge downwards. */
grid_scaled_y_t dy;
};
/* A collection of sorted and vertically clipped edges of the polygon. *Edgesaremovedfromthepolygontoanactivelistwhilescan
* converting. */ struct polygon { /* The vertical clip extents. */
grid_scaled_y_t ymin, ymax;
/* Array of edges all starting in the same bucket. An edge is put *intobucketEDGE_BUCKET_INDEX(edge->ytop,polygon->ymin)when
* it is added to the polygon. */ struct edge **y_buckets; struct edge *y_buckets_embedded[64];
/* A cell list represents the scan line sparsely as cells ordered by *ascendingx.Itisgearedtowardsscanningthecellsinorder
* using an internal cursor. */ struct cell_list { /* Sentinel nodes */ struct cell head, tail;
/* Cursor state for iterating through the cell list. */ struct cell *cursor, *rewind;
/* Cells in the cell list are owned by the cell list and are
* allocated from this pool. */ struct { struct pool base[1]; struct cell embedded[32];
} cell_pool;
};
/* The active list contains edges in the current scan line ordered by
* the x-coordinate of the intercept of the edge and the scan line. */ struct active_list { /* Leftmost edge on the current scan line. */ struct edge head, tail;
/* A lower bound on the height of the active edges is used to *estimatehowsoonsomeactiveedgeends.Wecan'tadvancethe *scanconversionbyafullpixelrowifanedgeendssomewhere
* within it. */
grid_scaled_y_t min_height; int is_vertical;
};
staticvoid
pool_fini(struct pool *pool)
{ struct _pool_chunk *p = pool->current; do { while (NULL != p) { struct _pool_chunk *prev = p->prev_chunk; if (p != pool->sentinel)
free(p);
p = prev;
}
p = pool->first_free;
pool->first_free = NULL;
} while (NULL != p);
}
/* Satisfy an allocation by first allocating a new large enough chunk *andaddingittotheheadofthepool'schunklist.Thisfunction *iscalledasafallbackifpool_alloc()couldn'tdoaquick
* allocation from the current chunk in the pool. */ staticvoid *
_pool_alloc_from_new_chunk( struct pool *pool,
size_t size)
{ struct _pool_chunk *chunk; void *obj;
size_t capacity;
/* If the allocation is smaller than the default chunk size then *trygettingachunkoffthefreelist.Forceallocofanew
* chunk for large requests. */
capacity = size;
chunk = NULL; if (size < pool->default_capacity) {
capacity = pool->default_capacity;
chunk = pool->first_free; if (chunk) {
pool->first_free = chunk->prev_chunk;
_pool_chunk_init(chunk, pool->current, chunk->capacity);
}
}
/* Relinquish all pool_alloced memory back to the pool. */ staticvoid
pool_reset (struct pool *pool)
{ /* Transfer all used chunks to the chunk free list. */ struct _pool_chunk *chunk = pool->current; if (chunk != pool->sentinel) { while (chunk->prev_chunk != pool->sentinel) {
chunk = chunk->prev_chunk;
}
chunk->prev_chunk = pool->first_free;
pool->first_free = pool->current;
} /* Reset the sentinel as the current chunk. */
pool->current = pool->sentinel;
pool->sentinel->size = 0;
}
/* Rewinds the cell list's cursor to the beginning. After rewinding
* we're good to cell_list_find() the cell any x coordinate. */ inlinestaticvoid
cell_list_rewind (struct cell_list *cells)
{
cells->cursor = &cells->head;
}
/* Empty the cell list. This is called at the start of every pixel
* row. */ inlinestaticvoid
cell_list_reset (struct cell_list *cells)
{
cell_list_rewind (cells);
cells->head.next = &cells->tail;
pool_reset (cells->cell_pool.base);
}
/* Find a cell at the given x-coordinate. Returns %NULL if a new cell *neededtobeallocatedbutcouldn'tbe.Cellsmustbefoundwith *non-decreasingx-coordinateuntilthecelllistisrewoundusing *cell_list_rewind().Ownershipofthereturnedcellisretainedby
* the cell list. */ inlinestaticstruct cell *
cell_list_find (struct cell_list *cells, int x)
{ struct cell *tail = cells->cursor;
if (tail->x == x) return tail;
while (1) {
UNROLL3({ if (tail->next->x > x) break;
tail = tail->next;
});
}
/* Find two cells at x1 and x2. This is exactly equivalent *to * *pair.cell1=cell_list_find(cells,x1); *pair.cell2=cell_list_find(cells,x2); *
* except with less function call overhead. */ inlinestaticstruct cell_pair
cell_list_find_pair(struct cell_list *cells, int x1, int x2)
{ struct cell_pair pair;
pair.cell1 = cells->cursor; while (1) {
UNROLL3({ if (pair.cell1->next->x > x1) break;
pair.cell1 = pair.cell1->next;
});
} if (pair.cell1->x != x1)
pair.cell1 = cell_list_alloc (cells, pair.cell1, x1);
pair.cell2 = pair.cell1; while (1) {
UNROLL3({ if (pair.cell2->next->x > x2) break;
pair.cell2 = pair.cell2->next;
});
} if (pair.cell2->x != x2)
pair.cell2 = cell_list_alloc (cells, pair.cell2, x2);
cells->cursor = pair.cell2; return pair;
}
/* Add a subpixel span covering [x1, x2) to the coverage cells. */ inlinestaticvoid
cell_list_add_subspan(struct cell_list *cells,
grid_scaled_x_t x1,
grid_scaled_x_t x2)
{ int ix1, fx1; int ix2, fx2;
/* Adds the analytical coverage of an edge crossing the current pixel *rowtothecoveragecellsandadvancestheedge'sxpositiontothe *followingrow. * *Thisfunctionisonlycalledwhenweknowthatduringthispixelrow: * *1)Therelativeorderofalledgesontheactivelistdoesn't *change.Inparticular,noedgesintersectwithinthisrowtopixel *precision. * *2)Nonewedgesstartinthisrow. * *3)Noexistingedgesendmid-row. * *Thisfunctiondependsonbeingcalledwithalledgesfromthe *activelistintheordertheyappearonthelist(i.e.with
* non-decreasing x-coordinate.) */ staticvoid
cell_list_render_edge(struct cell_list *cells, struct edge *edge, int sign)
{
grid_scaled_x_t fx; struct cell *cell; int ix;
GRID_X_TO_INT_FRAC(edge->x.quo, ix, fx);
/* We always know that ix1 is >= the cell list cursor in this
* case due to the no-intersections precondition. */
cell = cell_list_find(cells, ix);
cell->covered_height += sign*GRID_Y;
cell->uncovered_area += sign*2*fx*GRID_Y;
}
/* Test if the edges on the active list can be safely advanced by a
* full row without intersections or any edges ending. */ inlinestaticint
can_do_full_row (struct active_list *active)
{ conststruct edge *e;
/* Recomputes the minimum height of all edges on the active
* list if we have been dropping edges. */ if (active->min_height <= 0) { int min_height = INT_MAX; int is_vertical = 1;
e = active->head.next; while (NULL != e) { if (e->height_left < min_height)
min_height = e->height_left;
is_vertical &= e->vertical;
e = e->next;
}
/* Merges edges on the given subpixel row from the polygon to the
* active_list. */ inlinestaticvoid
active_list_merge_edges_from_bucket(struct active_list *active, struct edge *edges)
{
active->head.next = merge_unsorted_edges (active->head.next, edges);
}
inlinestaticvoid
polygon_fill_buckets (struct active_list *active, struct edge *edge, int y, struct edge **buckets)
{
grid_scaled_y_t min_height = active->min_height; int is_vertical = active->is_vertical;
static grid_scaled_t
int_to_grid_scaled(int i, int scale)
{ /* Clamp to max/min representable scaled number. */ if (i >= 0) { if (i >= INT_MAX/scale)
i = INT_MAX/scale;
} else { if (i <= INT_MIN/scale)
i = INT_MIN/scale;
} return i*scale;
}
I glitter_status_t
glitter_scan_converter_reset(
glitter_scan_converter_t *converter, int xmin, int ymin, int xmax, int ymax)
{
glitter_status_t status; int max_num_spans;
/* Add a new polygon edge from pixel (x1,y1) to (x2,y2) to the scan *converter.Thecoordinatesrepresentpixelpositionsscaledby *2**GLITTER_PIXEL_BITS.Ifthisfunctionfailsthenthescan *convertershouldberesetordestroyed.Dirmustbe+1or-1,
* with the latter reversing the orientation of the edge. */
I void
glitter_scan_converter_add_edge (glitter_scan_converter_t *converter, const cairo_edge_t *edge)
{
cairo_edge_t e;
/* Render each pixel row. */ for (i = 0; i < h; i = j) { int do_full_row = 0;
j = i + 1;
/* Determine if we can ignore this row or use the full pixel
* stepper. */ if (! polygon->y_buckets[i]) { if (active->head.next == &active->tail) {
active->min_height = INT_MAX;
active->is_vertical = 1; for (; j < h && ! polygon->y_buckets[j]; j++)
; continue;
}
do_full_row = can_do_full_row (active);
}
if (do_full_row) { /* Step by a full pixel row's worth. */
full_row (active, coverages, winding_mask);
if (active->is_vertical) { while (j < h &&
polygon->y_buckets[j] == NULL &&
active->min_height >= 2*GRID_Y)
{
active->min_height -= GRID_Y;
j++;
} if (j != i + 1)
step_edges (active, j - (i + 1));
}
} else { int sub;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.