Files
CgeScanline/Bezier.c
T
blankhex 27fb732418
CI / build-and-analyze (push) Successful in 1m6s
Initial commit
2026-09-18 18:25:05 +03:00

168 lines
5.9 KiB
C

#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;
}