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