297 lines
8.3 KiB
C
297 lines
8.3 KiB
C
#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;
|
|
}
|
|
}
|
|
}
|