Initial commit
CI / build-and-analyze (push) Successful in 1m6s

This commit is contained in:
2026-09-18 18:25:05 +03:00
commit 27fb732418
12 changed files with 885 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
name: CI
on:
push:
branches: [trunk]
pull_request:
branches: [trunk]
jobs:
build-and-analyze:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install tools
run: |
sudo apt-get update -qq
sudo apt-get install -y build-essential clang clang-tools clang-tidy cppcheck gcc-mingw-w64-x86-64 cmake
- name: Build with GCC
run: |
make -f Makefile.posix clean
make -f Makefile.posix CC=gcc CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Build with Clang
run: |
make -f Makefile.posix clean
make -f Makefile.posix CC=clang CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Build with MinGW
run: |
make -f Makefile.posix clean
make -f Makefile.mingw CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-ar CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -Werror -O2"
- name: Build with CMake
run: |
cmake -S . -B build
cmake --build build
- name: Linting checks
run: |
echo "Running all linting checks..."
FAILED=0
# cppcheck
echo "=== Check 1/4: cppcheck ==="
if make -f Makefile.lint cppcheck; then
echo "cppcheck: PASSED"
else
echo "cppcheck: FAILED"
FAILED=1
fi
# clang-tidy
echo "=== Check 2/4: clang-tidy ==="
if make -f Makefile.lint clang-tidy; then
echo "clang-tidy: PASSED"
else
echo "clang-tidy: FAILED"
FAILED=1
fi
# scan-build
echo "=== Check 3/4: scan-build ==="
if make -f Makefile.lint scan-build; then
echo "scan-build: PASSED"
else
echo "scan-build: FAILED"
FAILED=1
fi
# security-check
echo "=== Check 4/4: security-check ==="
if make -f Makefile.lint security-check; then
echo "security-check: PASSED"
else
echo "security-check: FAILED"
FAILED=1
fi
# Final result
if [ $FAILED -ne 0 ]; then
echo "One or more linting checks failed."
exit 1
else
echo "All linting checks passed."
fi
+68
View File
@@ -0,0 +1,68 @@
# ---> C
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# ---> CMake
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
CMakeUserPresets.json
+167
View File
@@ -0,0 +1,167 @@
#include "CgeScanline.h"
#define BEZIER_MAX_DEPTH 64
typedef struct {
CgeScanlineEdge *edge;
size_t edgeSize;
size_t count;
} EdgeAddContext;
static void edgeAddLineTo(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1,
void *userData) {
EdgeAddContext *ctx = (EdgeAddContext *)userData;
if (ctx->count >= ctx->edgeSize)
return;
ctx->count += CgeScanlineEdgeAdd(ctx->edge + ctx->count, x0, y0, x1, y1);
}
static void addVec(uint32_t a[2], uint32_t b[2], uint32_t c[2]) {
c[0] = a[0] + b[0];
c[1] = a[1] + b[1];
}
static void shiftVec(uint32_t a[2], uint32_t b[2], int val) {
b[0] = a[0] >> val;
b[1] = a[1] >> val;
}
static uint64_t distSq(uint32_t x, uint32_t y) {
if (x & 0x80000000)
x = -x;
if (y & 0x80000000)
y = -y;
return ((uint64_t)x * x + (uint64_t)y * y);
}
static uint64_t flatnessCubic(uint32_t p[4][2]) {
uint64_t f1, f2;
f1 = distSq(p[0][0] - (p[1][0] << 1) + p[2][0],
p[0][1] - (p[1][1] << 1) + p[2][1]);
f2 = distSq(p[1][0] - (p[2][0] << 1) + p[3][0],
p[1][1] - (p[2][1] << 1) + p[3][1]);
return f1 > f2 ? f1 : f2;
}
static uint64_t flatnessQuad(uint32_t p[3][2]) {
return distSq(p[0][0] - (p[1][0] << 1) + p[2][0],
p[0][1] - (p[1][1] << 1) + p[2][1]);
}
static void bezierCubicFlattenRec(uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t x3, uint32_t y3, uint64_t flatnessSq,
int depth, CgeScanlineLineToCallback lineTo,
void *userData) {
uint32_t a[4][2] = {{x0, y0}, {x1, y1}, {x2, y2}, {x3, y3}};
uint32_t b[3][2];
uint32_t c[2][2];
uint32_t d[2];
if (depth < BEZIER_MAX_DEPTH && flatnessCubic(a) > flatnessSq) {
addVec(a[0], a[1], b[0]);
addVec(a[1], a[2], b[1]);
addVec(a[2], a[3], b[2]);
addVec(b[0], b[1], c[0]);
addVec(b[1], b[2], c[1]);
addVec(c[0], c[1], d);
shiftVec(b[0], b[0], 1);
shiftVec(b[1], b[1], 1);
shiftVec(b[2], b[2], 1);
shiftVec(c[0], c[0], 2);
shiftVec(c[1], c[1], 2);
shiftVec(d, d, 3);
bezierCubicFlattenRec(a[0][0], a[0][1], b[0][0], b[0][1], c[0][0],
c[0][1], d[0], d[1], flatnessSq, depth + 1,
lineTo, userData);
bezierCubicFlattenRec(d[0], d[1], c[1][0], c[1][1], b[2][0], b[2][1],
a[3][0], a[3][1], flatnessSq, depth + 1,
lineTo, userData);
} else {
lineTo(x0, y0, x3, y3, userData);
}
}
static void bezierQuadFlattenRec(uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint64_t flatnessSq, int depth,
CgeScanlineLineToCallback lineTo,
void *userData) {
uint32_t a[3][2] = {{x0, y0}, {x1, y1}, {x2, y2}};
uint32_t b[2][2];
uint32_t c[2];
if (depth < BEZIER_MAX_DEPTH && flatnessQuad(a) > flatnessSq) {
addVec(a[0], a[1], b[0]);
addVec(a[1], a[2], b[1]);
addVec(b[0], b[1], c);
shiftVec(b[0], b[0], 1);
shiftVec(b[1], b[1], 1);
shiftVec(c, c, 2);
bezierQuadFlattenRec(a[0][0], a[0][1], b[0][0], b[0][1], c[0], c[1],
flatnessSq, depth + 1, lineTo, userData);
bezierQuadFlattenRec(c[0], c[1], b[1][0], b[1][1], a[2][0], a[2][1],
flatnessSq, depth + 1, lineTo, userData);
} else {
lineTo(x0, y0, x2, y2, userData);
}
}
void CgeScanlineBezierCubicFlatten(uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t x3, uint32_t y3, uint32_t flatness,
CgeScanlineLineToCallback lineTo,
void *userData) {
uint64_t flatnessSq = (uint64_t)flatness * (uint64_t)flatness;
if (flatnessSq < 1)
flatnessSq = 1;
bezierCubicFlattenRec(x0, y0, x1, y1, x2, y2, x3, y3,
flatnessSq, 0, lineTo, userData);
}
void CgeScanlineBezierQuadFlatten(uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t flatness,
CgeScanlineLineToCallback lineTo,
void *userData) {
uint64_t flatnessSq = (uint64_t)flatness * (uint64_t)flatness;
if (flatnessSq < 1)
flatnessSq = 1;
bezierQuadFlattenRec(x0, y0, x1, y1, x2, y2,
flatnessSq, 0, lineTo, userData);
}
size_t CgeScanlineBezierCubicEdges(CgeScanlineEdge *edge, size_t edgeSize,
uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t x3, uint32_t y3,
uint32_t flatness) {
EdgeAddContext ctx;
ctx.edge = edge;
ctx.edgeSize = edgeSize;
ctx.count = 0;
CgeScanlineBezierCubicFlatten(x0, y0, x1, y1, x2, y2, x3, y3, flatness,
edgeAddLineTo, &ctx);
return ctx.count;
}
size_t CgeScanlineBezierQuadEdges(CgeScanlineEdge *edge, size_t edgeSize,
uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t flatness) {
EdgeAddContext ctx;
ctx.edge = edge;
ctx.edgeSize = edgeSize;
ctx.count = 0;
CgeScanlineBezierQuadFlatten(x0, y0, x1, y1, x2, y2, flatness,
edgeAddLineTo, &ctx);
return ctx.count;
}
+26
View File
@@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 3.10)
project(CgeScanline LANGUAGES C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(SOURCES
Scanline.c
Bezier.c
)
set(HEADERS
CgeScanline.h
)
add_library(CgeScanline STATIC ${SOURCES} ${HEADERS})
target_include_directories(CgeScanline PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
install(TARGETS CgeScanline
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
install(FILES ${HEADERS} DESTINATION include)
+111
View File
@@ -0,0 +1,111 @@
#ifndef CGE_SCANLINE_H
#define CGE_SCANLINE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
enum CgeScanlineFillRule {
CGE_SCANLINE_ODD_EVEN = 0,
CGE_SCANLINE_NON_ZERO = (1 << 0),
};
enum CgeScanlineCode {
CGE_SCANLINE_EOK,
CGE_SCANLINE_EDONE,
CGE_SCANLINE_EAET,
CGE_SCANLINE_ESPANS,
};
typedef struct CgeScanlineEdge {
uint32_t startX;
uint32_t startY;
uint32_t endY;
uint32_t step;
uint32_t error;
uint32_t threshold;
uint32_t dir;
int winding;
} CgeScanlineEdge;
typedef struct CgeScanlineEdgeTable {
size_t index;
uint32_t x;
uint32_t error;
} CgeScanlineEdgeTable;
typedef struct CgeScanlineSpan {
uint32_t start;
uint32_t end;
} CgeScanlineSpan;
typedef void (*CgeScanlineRowCallback)(uint32_t y, CgeScanlineSpan *spans,
size_t size, void *userData);
typedef void (*CgeScanlineLineToCallback)(uint32_t x0, uint32_t y0,
uint32_t x1, uint32_t y1,
void *userData);
typedef struct CgeScanlineState {
CgeScanlineEdge *edge;
size_t edgeSize;
CgeScanlineEdgeTable *aet;
size_t aetCapacity;
CgeScanlineSpan *span;
size_t spanCapacity;
CgeScanlineRowCallback callback;
void *userData;
int flags;
/* Internal state - do not edit */
uint32_t yCurrent;
uint32_t yMax;
size_t aetSize;
size_t edgeCursor;
} CgeScanlineState;
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);
int CgeScanlineStateNext(CgeScanlineState *state, int *result);
int CgeScanlineEdgeAdd(CgeScanlineEdge *edge, uint32_t aX, uint32_t aY,
uint32_t bX, uint32_t bY);
int CgeScanlineSpans(CgeScanlineEdge *edge, size_t edgeSize,
CgeScanlineRowCallback callback, void *userData,
int flags);
void CgeScanlineBezierCubicFlatten(uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t x3, uint32_t y3, uint32_t flatness,
CgeScanlineLineToCallback lineTo,
void *userData);
void CgeScanlineBezierQuadFlatten(uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t flatness,
CgeScanlineLineToCallback lineTo,
void *userData);
size_t CgeScanlineBezierCubicEdges(CgeScanlineEdge *edge, size_t edgeSize,
uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t x3, uint32_t y3, uint32_t flatness);
size_t CgeScanlineBezierQuadEdges(CgeScanlineEdge *edge, size_t edgeSize,
uint32_t x0, uint32_t y0, uint32_t x1,
uint32_t y1, uint32_t x2, uint32_t y2,
uint32_t flatness);
#ifdef __cplusplus
}
#endif
#endif /* CGE_SCANLINE_H */
+12
View File
@@ -0,0 +1,12 @@
Copyright (C) 2026 by blankhex me@blankhex.com
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
+29
View File
@@ -0,0 +1,29 @@
# Makefile.lint - Individual linting targets
.PHONY: all
all: cppcheck clang-tidy scan-build security-check
@echo "All linting scheduled"
.PHONY: cppcheck
cppcheck:
@echo "Running cppcheck..."
@cppcheck --enable=warning,performance,portability --std=c99 --quiet -Iinclude -I. .
.PHONY: clang-tidy
clang-tidy:
@echo "Running clang-tidy..."
@for file in $$(find . -name "*.c" -type f); do \
clang-tidy "$$file" -- -I. -Iinclude -std=c99; \
done
.PHONY: scan-build
scan-build:
@echo "Running scan-build..."
@scan-build --status-bugs make -f Makefile.posix clean all CC=clang
.PHONY: security-check
security-check:
@echo "Scanning for unsafe C functions..."
@grep -rnE '\b(strcpy|strcat|sprintf|gets|scanf|sscanf|realpath|mktemp|tempnam|tmpnam|getwd|getlogin)\b' \
. --include="*.c" --include="*.h" | grep -v "^Binary file" && \
exit 1 || exit 0
+23
View File
@@ -0,0 +1,23 @@
# MinGW Makefile for CgeScanline
CC = gcc
AR = ar
CFLAGS = -std=c99 -O2 -Wall -Wextra
ARFLAGS = rcs
TARGET = libCgeScanline.a
SOURCES = Scanline.c Bezier.c
OBJECTS = $(SOURCES:.c=.o)
.PHONY: all clean
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c CgeScanline.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
del $(OBJECTS) $(TARGET) 2>nul || exit 0
+33
View File
@@ -0,0 +1,33 @@
# POSIX Makefile for CgeScanline
CC = gcc
AR = ar
CFLAGS = -std=c99 -O2 -Wall -Wextra -fPIC
ARFLAGS = rcs
TARGET = libCgeScanline.a
SOURCES = Scanline.c Bezier.c
OBJECTS = $(SOURCES:.c=.o)
.PHONY: all clean install
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(AR) $(ARFLAGS) $@ $^
%.o: %.c CgeScanline.h
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f $(OBJECTS) $(TARGET)
install: $(TARGET)
cp $(TARGET) /usr/local/lib/
cp CgeScanline.h /usr/local/include/
ldconfig || echo "Run ldconfig manually if needed"
uninstall:
rm -f /usr/local/lib/libCgeScanline.a
rm -f /usr/local/include/CgeScanline.h
ldconfig || true
+23
View File
@@ -0,0 +1,23 @@
# Makefile.win32 for MSVC (NMake)
# Usage: Open "x86 Native Tools Command Prompt", then:
# nmake -f Makefile.win32
CC = cl
LIB = lib
CFLAGS = /c /nologo /W3 /O2
LIBFLAGS = /nologo
TARGET = CgeScanline.lib
SOURCES = Scanline.c Bezier.c
OBJECTS = $(SOURCES:.c=.obj)
$(TARGET): $(OBJECTS)
$(LIB) $(LIBFLAGS) /OUT:$(TARGET) $(OBJECTS)
{.}.c{}.obj:
$(CC) $(CFLAGS) /Fo$@ $<
clean:
del $(OBJECTS) $(TARGET) 2>nul
.PHONY: clean
+8
View File
@@ -0,0 +1,8 @@
# CgeScanline
TODO
## License
0BSD - a permissive license with no attribution required.
+296
View File
@@ -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;
}
}
}