Initial commit
This commit is contained in:
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* demo_bezier.c
|
||||
*
|
||||
* Demonstrates the Bezier curve flattening helpers in CgeScanline.
|
||||
* Uses 8x5 sub-pixel coverage anti-aliasing (8 sub-pixels in X,
|
||||
* 5 sub-scanlines in Y per pixel).
|
||||
*
|
||||
* Coordinate transform:
|
||||
* x' = x * 8 + 4
|
||||
* y' = y * 5 + 2
|
||||
* (centered in the pixel for correct coverage)
|
||||
*
|
||||
* Coverage per scanline is accumulated via a LUT that maps 0..8
|
||||
* sub-pixel X-coverage to 0..51. With 5 scanlines, a fully covered
|
||||
* pixel reaches 5 * 51 = 255.
|
||||
*
|
||||
* Renders "teardrop" shape to a PPM image:
|
||||
*
|
||||
* Compile with:
|
||||
* gcc -std=c99 -Wall -Wextra -o demo_bezier.exe demo_bezier.c Scanline.c
|
||||
*
|
||||
* Run:
|
||||
* demo_bezier.exe
|
||||
* View output_bezier.ppm (open in any image viewer / convert to PNG).
|
||||
*/
|
||||
|
||||
#include "CgeScanline.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define WIDTH 200
|
||||
#define HEIGHT 200
|
||||
|
||||
/* 8x5 subsampling factors */
|
||||
#define SUB_X 8
|
||||
#define SUB_Y 5
|
||||
#define SUB_X_SHIFT 3
|
||||
#define SUB_Y_SHIFT 0
|
||||
#define SUB_X_OFFSET (SUB_X / 2)
|
||||
#define SUB_Y_OFFSET (SUB_Y / 2)
|
||||
#define SUB_X_MASK 7
|
||||
|
||||
static const uint8_t covLut[9] = {
|
||||
0, 6, 13, 19, 26, 32, 38, 45, 51,
|
||||
};
|
||||
|
||||
static uint8_t covBuf[HEIGHT][WIDTH];
|
||||
|
||||
static void toSubPixel(int *sx, int *sy, int ix, int iy) {
|
||||
*sx = ix * SUB_X + SUB_X_OFFSET;
|
||||
*sy = iy * SUB_Y + SUB_Y_OFFSET;
|
||||
}
|
||||
|
||||
static void fillRow(uint32_t y, CgeScanlineSpan *spans, size_t size,
|
||||
void *userData) {
|
||||
(void)userData;
|
||||
|
||||
uint32_t row = y / SUB_Y;
|
||||
if (row >= HEIGHT)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
uint32_t s = spans[i].start;
|
||||
uint32_t e = spans[i].end;
|
||||
|
||||
if (s >= e) continue;
|
||||
|
||||
uint32_t colStart = s >> SUB_X_SHIFT;
|
||||
uint32_t colEnd = (e - 1) >> SUB_X_SHIFT;
|
||||
|
||||
if (colStart >= WIDTH)
|
||||
continue;
|
||||
if (colEnd >= WIDTH)
|
||||
colEnd = WIDTH - 1;
|
||||
if (colStart > colEnd)
|
||||
continue;
|
||||
|
||||
/* Span covers only one pixel (partially or entirely) */
|
||||
if (colStart == colEnd) {
|
||||
uint32_t add = covLut[e - s];
|
||||
uint32_t cur = (uint32_t)covBuf[row][colStart] + add;
|
||||
covBuf[row][colStart] = cur > 255 ? 255 : (uint8_t)cur;
|
||||
continue;
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t n = SUB_X - (s & SUB_X_MASK);
|
||||
uint32_t cur = (uint32_t)covBuf[row][colStart] + covLut[n];
|
||||
covBuf[row][colStart] = cur > 255 ? 255 : (uint8_t)cur;
|
||||
}
|
||||
|
||||
for (uint32_t col = colStart + 1; col < colEnd; ++col) {
|
||||
uint32_t cur = (uint32_t)covBuf[row][col] + covLut[SUB_X];
|
||||
covBuf[row][col] = cur > 255 ? 255 : (uint8_t)cur;
|
||||
}
|
||||
|
||||
{
|
||||
uint32_t n = e - (colEnd << SUB_X_SHIFT);
|
||||
uint32_t cur = (uint32_t)covBuf[row][colEnd] + covLut[n];
|
||||
covBuf[row][colEnd] = cur > 255 ? 255 : (uint8_t)cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void writePPM(const char *filename) {
|
||||
FILE *f = fopen(filename, "wb");
|
||||
int y, x;
|
||||
|
||||
if (!f) {
|
||||
perror(filename);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "P6\n%d %d\n255\n", WIDTH, HEIGHT);
|
||||
for (y = 0; y < HEIGHT; ++y) {
|
||||
for (x = 0; x < WIDTH; ++x) {
|
||||
unsigned char pixel = (unsigned char)(255 - covBuf[y][x]);
|
||||
fwrite(&pixel, 1, 1, f);
|
||||
fwrite(&pixel, 1, 1, f);
|
||||
fwrite(&pixel, 1, 1, f);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
printf("Wrote %s (%dx%d)\n", filename, WIDTH, HEIGHT);
|
||||
}
|
||||
|
||||
static size_t buildBezierCubicEdges(CgeScanlineEdge *edge, size_t edgeSize,
|
||||
int iX0, int iY0,
|
||||
int iX1, int iY1,
|
||||
int iX2, int iY2,
|
||||
int iX3, int iY3,
|
||||
uint32_t flatness) {
|
||||
int sx0, sy0, sx1, sy1, sx2, sy2, sx3, sy3;
|
||||
|
||||
toSubPixel(&sx0, &sy0, iX0, iY0);
|
||||
toSubPixel(&sx1, &sy1, iX1, iY1);
|
||||
toSubPixel(&sx2, &sy2, iX2, iY2);
|
||||
toSubPixel(&sx3, &sy3, iX3, iY3);
|
||||
return CgeScanlineBezierCubicEdges(edge, edgeSize,
|
||||
(uint32_t)sx0, (uint32_t)sy0,
|
||||
(uint32_t)sx1, (uint32_t)sy1,
|
||||
(uint32_t)sx2, (uint32_t)sy2,
|
||||
(uint32_t)sx3, (uint32_t)sy3,
|
||||
flatness);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
CgeScanlineEdge edgeBuf[16384];
|
||||
size_t edgeCount;
|
||||
|
||||
memset(covBuf, 0, sizeof(covBuf));
|
||||
printf("=== Demo: Bezier Flattening (8x5 subsampled) ===\n\n");
|
||||
printf("Rounded shape (two cubic Beziers, flatness=1)\n");
|
||||
|
||||
edgeCount = 0;
|
||||
|
||||
edgeCount = buildBezierCubicEdges(
|
||||
edgeBuf, 16384,
|
||||
100, 20, /* P0: top centre */
|
||||
90, 20, /* P1: left top */
|
||||
10, 180, /* P2: left bottom */
|
||||
100, 180, /* P3: bottom centre */
|
||||
2);
|
||||
printf(" Left cubic: %zu edges\n", edgeCount);
|
||||
|
||||
edgeCount += buildBezierCubicEdges(
|
||||
edgeBuf + edgeCount, 16384 - edgeCount,
|
||||
100, 180, /* P0: bottom centre */
|
||||
190, 180, /* P1: right bottom */
|
||||
110, 20, /* P2: right top */
|
||||
100, 20, /* P3: top centre */
|
||||
2);
|
||||
printf(" Right cubic: %zu edges (cumulative)\n", edgeCount);
|
||||
printf(" Total: %zu edges\n", edgeCount);
|
||||
|
||||
{
|
||||
int ok = CgeScanlineSpans(edgeBuf, edgeCount, fillRow, NULL,
|
||||
CGE_SCANLINE_ODD_EVEN);
|
||||
printf(" Raster: %s\n\n", ok ? "OK" : "FAILED");
|
||||
}
|
||||
|
||||
writePPM("output_bezier.ppm");
|
||||
printf("Done.\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user