Initial commit
This commit is contained in:
+296
@@ -0,0 +1,296 @@
|
||||
#include "CgeScanline.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#define AET_INIT_SIZE 256
|
||||
#define SPAN_INIT_SIZE 256
|
||||
|
||||
#define SWAP(x, y) \
|
||||
do { uint32_t t = (x); (x) = (y); (y) = t; } while (0)
|
||||
|
||||
#define SETC(x, value) \
|
||||
do { if (x) *(x) = (value); } while(0)
|
||||
|
||||
typedef size_t (*SpanGeneratorCb)(CgeScanlineEdge *, CgeScanlineEdgeTable *,
|
||||
size_t, CgeScanlineSpan *, size_t);
|
||||
|
||||
static void aetStep(CgeScanlineEdge *edge, CgeScanlineEdgeTable *aet,
|
||||
size_t size) {
|
||||
CgeScanlineEdgeTable *current, *end;
|
||||
|
||||
for (current = aet, end = aet + size; current != end; ++current) {
|
||||
CgeScanlineEdge *lookupEdge = edge + current->index;
|
||||
uint32_t overflow;
|
||||
|
||||
current->x += lookupEdge->step;
|
||||
current->error += lookupEdge->error;
|
||||
|
||||
overflow = (current->error >= lookupEdge->threshold) ? 0xFFFFFFFFul : 0;
|
||||
current->x += lookupEdge->dir & overflow;
|
||||
current->error -= lookupEdge->threshold & overflow;
|
||||
}
|
||||
}
|
||||
|
||||
static void aetSort(CgeScanlineEdgeTable *aet, size_t aetSize) {
|
||||
CgeScanlineEdgeTable tmp;
|
||||
size_t i, j;
|
||||
|
||||
for (i = 1; i < aetSize; ++i) {
|
||||
tmp = aet[i];
|
||||
for (j = i; j > 0 && aet[j - 1].x > tmp.x; --j)
|
||||
aet[j] = aet[j - 1];
|
||||
aet[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t aetRemove(CgeScanlineEdge *edge, CgeScanlineEdgeTable *aet,
|
||||
size_t aetSize, uint32_t y) {
|
||||
size_t writeIdx, i;
|
||||
|
||||
for (writeIdx = 0, i = 0; i < aetSize; ++i) {
|
||||
if (edge[aet[i].index].endY != y)
|
||||
aet[writeIdx++] = aet[i];
|
||||
}
|
||||
|
||||
return writeIdx;
|
||||
}
|
||||
|
||||
static int aetAdd(CgeScanlineEdge *edge, size_t edgeSize,
|
||||
CgeScanlineEdgeTable *aet, size_t *aetSize,
|
||||
size_t aetCapacity, size_t *edgeCursor, uint32_t y) {
|
||||
for (; *edgeCursor < edgeSize && edge[*edgeCursor].startY == y; ++*edgeCursor) {
|
||||
if (*aetSize >= aetCapacity)
|
||||
return 0;
|
||||
aet[*aetSize].index = *edgeCursor;
|
||||
aet[*aetSize].x = edge[*edgeCursor].startX;
|
||||
aet[*aetSize].error = 0;
|
||||
++*aetSize;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int edgesCmp(const void *lhs, const void *rhs) {
|
||||
CgeScanlineEdge *left = (CgeScanlineEdge *)lhs;
|
||||
CgeScanlineEdge *right = (CgeScanlineEdge *)rhs;
|
||||
|
||||
return left->startY < right->startY ? -1 : (left->startY == right->startY ? 0 : 1);
|
||||
}
|
||||
|
||||
int CgeScanlineEdgeAdd(CgeScanlineEdge *edge, uint32_t aX, uint32_t aY,
|
||||
uint32_t bX, uint32_t bY) {
|
||||
uint32_t dX;
|
||||
|
||||
if (aY == bY)
|
||||
return 0;
|
||||
|
||||
edge->winding = 1;
|
||||
if (bY < aY) {
|
||||
SWAP(aX, bX);
|
||||
SWAP(aY, bY);
|
||||
edge->winding = -1;
|
||||
}
|
||||
|
||||
edge->threshold = bY - aY;
|
||||
edge->startY = aY;
|
||||
edge->endY = bY;
|
||||
edge->startX = aX;
|
||||
dX = (aX < bX) ? bX - aX : aX - bX;
|
||||
edge->step = dX / edge->threshold;
|
||||
edge->error = dX % edge->threshold;
|
||||
edge->dir = 1;
|
||||
|
||||
if (bX < aX) {
|
||||
edge->dir = -1;
|
||||
edge->step *= -1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static size_t spansOddEven(CgeScanlineEdge *edge, CgeScanlineEdgeTable *aet,
|
||||
size_t aetSize, CgeScanlineSpan *span,
|
||||
size_t spanSize) {
|
||||
size_t i, size;
|
||||
(void)edge;
|
||||
|
||||
for (i = 0, size = 0; i + 1 < aetSize; i += 2) {
|
||||
if (aet[i].x < aet[i + 1].x) {
|
||||
if (size >= spanSize)
|
||||
return SIZE_MAX;
|
||||
span[size].start = aet[i].x;
|
||||
span[size].end = aet[i + 1].x;
|
||||
++size;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static size_t spansNonZero(CgeScanlineEdge *edge, CgeScanlineEdgeTable *aet,
|
||||
size_t aetSize, CgeScanlineSpan *span,
|
||||
size_t spanSize) {
|
||||
int winding, inSpan;
|
||||
uint32_t spanStart;
|
||||
size_t i, size;
|
||||
|
||||
winding = 0;
|
||||
inSpan = 0;
|
||||
spanStart = 0;
|
||||
size = 0;
|
||||
|
||||
for (i = 0; i < aetSize; ++i) {
|
||||
winding += edge[aet[i].index].winding;
|
||||
|
||||
if (!inSpan && winding != 0) {
|
||||
spanStart = aet[i].x;
|
||||
inSpan = 1;
|
||||
} else if (inSpan && winding == 0) {
|
||||
if (spanStart < aet[i].x) {
|
||||
if (size >= spanSize)
|
||||
return SIZE_MAX;
|
||||
span[size].start = spanStart;
|
||||
span[size].end = aet[i].x;
|
||||
++size;
|
||||
}
|
||||
inSpan = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
int CgeScanlineStateInit(CgeScanlineState *state, CgeScanlineEdge *edge,
|
||||
size_t edgeSize, CgeScanlineEdgeTable *aet,
|
||||
size_t aetCapacity, CgeScanlineSpan *span,
|
||||
size_t spanCapacity, CgeScanlineRowCallback callback,
|
||||
void *userData, int flags) {
|
||||
uint32_t yMin, yMax;
|
||||
size_t i;
|
||||
|
||||
if (!edgeSize || !aetCapacity || !spanCapacity || !callback)
|
||||
return 0;
|
||||
|
||||
state->edge = edge;
|
||||
state->edgeSize = edgeSize;
|
||||
state->aet = aet;
|
||||
state->aetCapacity = aetCapacity;
|
||||
state->span = span;
|
||||
state->spanCapacity = spanCapacity;
|
||||
state->callback = callback;
|
||||
state->userData = userData;
|
||||
state->flags = flags;
|
||||
|
||||
yMin = state->edge[0].startY;
|
||||
yMax = state->edge[0].endY;
|
||||
for (i = 1; i < edgeSize; ++i) {
|
||||
if (state->edge[i].startY < yMin)
|
||||
yMin = state->edge[i].startY;
|
||||
if (state->edge[i].endY > yMax)
|
||||
yMax = state->edge[i].endY;
|
||||
}
|
||||
|
||||
qsort(state->edge, state->edgeSize, sizeof(*state->edge), edgesCmp);
|
||||
state->yCurrent = yMin;
|
||||
state->yMax = yMax;
|
||||
state->edgeCursor = 0;
|
||||
state->aetSize = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CgeScanlineStateNext(CgeScanlineState *state, int *result) {
|
||||
SpanGeneratorCb generate;
|
||||
size_t rowSpans;
|
||||
|
||||
generate = (state->flags & CGE_SCANLINE_NON_ZERO) ? spansNonZero
|
||||
: spansOddEven;
|
||||
|
||||
if (state->yCurrent >= state->yMax) {
|
||||
SETC(result, CGE_SCANLINE_EDONE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
state->aetSize = aetRemove(state->edge, state->aet, state->aetSize,
|
||||
state->yCurrent);
|
||||
if (!aetAdd(state->edge, state->edgeSize, state->aet, &state->aetSize,
|
||||
state->aetCapacity, &state->edgeCursor, state->yCurrent)) {
|
||||
SETC(result, CGE_SCANLINE_EAET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rowSpans = 0;
|
||||
if (state->aetSize != 0) {
|
||||
aetSort(state->aet, state->aetSize);
|
||||
|
||||
rowSpans = generate(state->edge, state->aet, state->aetSize,
|
||||
state->span, state->spanCapacity);
|
||||
if (rowSpans == SIZE_MAX) {
|
||||
SETC(result, CGE_SCANLINE_ESPANS);
|
||||
return 0;
|
||||
}
|
||||
aetStep(state->edge, state->aet, state->aetSize);
|
||||
}
|
||||
|
||||
state->callback(state->yCurrent, state->span, rowSpans, state->userData);
|
||||
state->yCurrent++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CgeScanlineSpans(CgeScanlineEdge *edge, size_t edgeSize,
|
||||
CgeScanlineRowCallback callback, void *userData,
|
||||
int flags) {
|
||||
CgeScanlineEdgeTable *aet;
|
||||
CgeScanlineSpan *spans;
|
||||
CgeScanlineState state;
|
||||
int result;
|
||||
|
||||
aet = malloc(sizeof(*aet) * AET_INIT_SIZE);
|
||||
spans = malloc(sizeof(*spans) * SPAN_INIT_SIZE);
|
||||
if (!aet || !spans) {
|
||||
free(aet);
|
||||
free(spans);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!CgeScanlineStateInit(&state, edge, edgeSize, aet, AET_INIT_SIZE, spans,
|
||||
SPAN_INIT_SIZE, callback, userData, flags)) {
|
||||
free(aet);
|
||||
free(spans);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
if (CgeScanlineStateNext(&state, &result))
|
||||
continue;
|
||||
|
||||
switch (result) {
|
||||
case CGE_SCANLINE_EDONE:
|
||||
free(state.aet);
|
||||
free(state.span);
|
||||
return 1;
|
||||
|
||||
case CGE_SCANLINE_EAET:
|
||||
aet = realloc(state.aet, sizeof(*aet) * state.aetCapacity * 2);
|
||||
if (aet) {
|
||||
state.aetCapacity *= 2;
|
||||
state.aet = aet;
|
||||
} else {
|
||||
free(state.aet);
|
||||
free(state.span);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case CGE_SCANLINE_ESPANS:
|
||||
spans = realloc(state.span, sizeof(*spans) * state.spanCapacity * 2);
|
||||
if (spans) {
|
||||
state.spanCapacity *= 2;
|
||||
state.span = spans;
|
||||
} else {
|
||||
free(state.aet);
|
||||
free(state.span);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user