Files
CgeScanline/demo_bezier.c
T
2026-09-15 14:37:22 +03:00

188 lines
5.5 KiB
C

/*
* 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;
}