168 lines
5.9 KiB
C
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;
|
||
|
|
}
|