#include "CgeScanline.h" #include #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; } } }