/* 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 even for 64 bit types. */
int64_t data;
};
/* The int64_t data member of _pool_chunk just exists to enforce alignment,
* it shouldn't be included in the allocated size for the struct. */ #define SIZEOF_POOL_CHUNK (sizeof(struct _pool_chunk) - sizeof(int64_t))
/* 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 *thesentinelchunkallocatesfrom.Thisisexpressedasachar *arraysothatthe'int64_tdata'memberof_pool_chunkisn't *included.Thiswayembeddingstructpoolinotherstructsworks
* without wasting space. */ char sentinel[SIZEOF_POOL_CHUNK];
};
/* A polygon edge. */ struct edge { /* Next in y-bucket or active list. */ struct edge *next, *prev;
/* The clipped y of the top of the edge. */
grid_scaled_y_t ytop;
/* 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 cell;
/* 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;
/* Advance of the current x when moving down a full pixel *row.Onlyinitialisedwhentheheightoftheedgeislarge *enoughthatthere'sachancetheedgecouldbesteppedbya
* full row's worth of subsample rows at a time. */ struct quorem dxdy_full;
/* y2-y1 after orienting the edge downwards. */
int64_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 != (void *) 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 != (void *) pool->sentinel) { while (chunk->prev_chunk != (void *) 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 = (void *) pool->sentinel;
pool->current->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;
}
inlinestaticvoid
cell_list_maybe_rewind (struct cell_list *cells, int x)
{ if (x < cells->cursor->x) {
cells->cursor = cells->rewind; if (x < cells->cursor->x)
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;
/* Edge is entirely within a column? */ if (ix1 == ix2) { /* We always know that ix1 is >= the cell list cursor in this
* case due to the no-intersections precondition. */ struct cell *cell = cell_list_find(cells, ix1);
cell->covered_height += sign*GRID_Y;
cell->uncovered_area += sign*(fx1 + fx2)*GRID_Y; return;
}
/* Orient the edge left-to-right. */ if (ix2 < ix1) { struct quorem tx; int t;
t = ix1;
ix1 = ix2;
ix2 = t;
t = fx1;
fx1 = fx2;
fx2 = t;
tx = x1;
x1 = x2;
x2 = tx;
}
/* Add coverage for all pixels [ix1,ix2] on this row crossed
* by the edge. */
{ struct cell_pair pair; struct quorem y;
int64_t tmp, dx; int y_last;
/* When rendering a previous edge on the active list we may *advancethecelllistcursorpasttheleftmostpixelofthe *currentedgeeventhoughthetwoedgesdon'tintersect. *e.g.considertwoedgesgoingdownandrightwards: * *--\_+---\_+-----+-----+---- *\_\_|| *|\_|\_|| *|\_|\_|| *|\_\_| *----+-----+-\---+-\---+---- * *Theleftedgetouchescellspastthestartingcellofthe *rightedge.Fortunatelysuchcasesarerare.
*/
/* 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; int prev_x = INT_MIN;
/* 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->dy == 0;
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);
}
inlinestaticint
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; int max_suby = 0;
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)
{
polygon_add_edge (converter->polygon, edge);
}
/* 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_fill_buckets (active,
polygon->y_buckets[i],
(i+ymin_i)*GRID_Y,
buckets) == 0) { if (buckets[0]) {
active_list_merge_edges_from_bucket (active, buckets[0]);
buckets[0] = NULL;
}
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;
/* Subsample this row. */ for (sub = 0; sub < GRID_Y; sub++) { if (buckets[sub]) {
active_list_merge_edges_from_bucket (active, buckets[sub]);
buckets[sub] = NULL;
}
sub_row (active, coverages, winding_mask);
}
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.64Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-30)
¤
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.
Diese beiden folgenden Angebotsgruppen bietet das Unternehmen
Angebot
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.