Graphics
A renderer built out of an array of bytes, with no graphics library underneath. Start with a framebuffer, draw lines and circles, rasterize triangles with barycentric coordinates, composite with alpha, and kill the staircase with supersampling. Then read the buffer back: brightness and contrast as lookup tables, blur and Sobel edges as convolution kernels, grayscale and sepia as colour matrices, auto-levels from a histogram, median filters for noise, and the half-pixel bug that shifts a resized image. Everything before the last chapter runs in your browser.
- A framebuffer is an array of bytes. Every chapter here is arithmetic on that array, with no library underneath.
- Rasterizing decides which pixels a shape covers. Antialiasing decides how much of each, which is why it costs more.
- Blur, sharpen and edge detection are one operation with different numbers in the kernel.
- Resampling shifts the image half a pixel unless you sample pixel centres. It looks like a rounding bug and it is not.
14 chapters.
- A Framebuffer Is an ArrayRow-major layout, top-left origin, packed pixels, and a clipping putPixel.
- Writing an Image FilePPM, the image format you can write without a library, and the one rule that will bite you.
- LinesBresenham's algorithm: one add and one compare per pixel, integers only.
- CirclesCompare squared distances, loop over the bounding box, and let putPixel clip.
- Alpha BlendingSource-over compositing in integers, why the /255 matters, and why order still does.
- Rasterizing TrianglesEdge functions decide inside from outside, and the same three numbers interpolate across the face.
- AntialiasingCoverage is alpha. Sample the inside test sixteen times per pixel and the staircase goes away.
- Per-Pixel TransformsBrightness, contrast, threshold and invert are all one table lookup, and the add that does brightness is where Zig has an opinion.
- ConvolutionBlur, sharpen and Sobel are one loop with different numbers in it. The two bugs are always the border and the buffer.
- Colour MatricesGrayscale, sepia and saturation are one 3x3 matrix, and a chain of them multiplies into a single matrix before the image is touched.
- Histograms and LevelsCount what the image contains, and the lookup table that fixes it falls out of the counts. Auto-levels, equalization, and why you apply them to luma.
- Median and Rank FiltersA convolution cannot ignore a value. Sorting the neighbourhood gives one function that is a denoiser, an eroder and a dilator depending on the index.
- Scaling and ResamplingNearest, bilinear and box filtering, the half-pixel bug that shifts your whole image, and why downscaling without a filter turns detail into a solid bar.
- Onto the ScreenThe same buffer, handed to X11 through translate-c, and what changes on Wayland.