module

PNGGIF::Raster

Generic raster utilities over PNGGIF::Bitmap/row-major grids: palette quantization with dithering, run-length scanning, RGBA flattening and alpha compositing. Nothing here is terminal- or toolkit-specific; these are the building blocks any consumer rendering bitmaps to a constrained target (palette device, byte stream, cell grid) needs.

Constants

BAYER_MATRIX = [[0, 8, 2, 10], [12, 4, 14, 6], [3, 11, 1, 9], [15, 7, 13, 5]]

4×4 Bayer ordered-dither matrix (values 0..15).

Class methods

clamp8(v : Int32) : Int32

Clamps v into the 0..255 byte range.

Source
composite(canvas : PNGGIF::Bitmap, bmp : PNGGIF::Bitmap, ox : Int32, oy : Int32) : Nil

Alpha-blends bmp onto canvas with its top-left at pixel (ox,oy), clipping to the canvas bounds. The result is fully opaque where written.

Source
dims(bmp : Array(Array(T))) : Tuple(Int32, Int32) forall T

Dimensions {w, h} of a row-major 2D grid bmp (bmp.size rows, each bmp[0].size wide; {0, 0} if empty). Generic, so it covers both a PNGGIF::Bitmap and other row-major grids.

Source
dither_rgb(bmp : PNGGIF::Bitmap, pw : Int32, ph : Int32, dither : Dither, animated : Bool, transparent : V, into : Array(Array(V)) | Nil = nil, & : Int32, Int32, Int32, Float64 -> Tuple(V, Int32, Int32, Int32)) : Array(Array(V)) forall V

Quantizes an RGBA bmp (pw×ph) to one value per pixel, applying the requested dither.

The block is invoked once per opaque pixel with the channels to quantize and an ordered-dither threshold t (Bayer offset in [-0.5, 0.5) for Ordered, else 0.0); it must return {value, qr, qg, qb} — the stored value (palette index or packed 0xRRGGBB) plus the RGB it resolves to, so Diffusion can spread the residual onto not-yet-visited neighbours. Fully transparent pixels (a == 0 or missing) are assigned transparent and never reach the block. animated collapses Dither::Auto.

into, when non-nil and already sized ph rows × pw wide, is reused as the output grid, avoiding a per-frame allocation; every cell is assigned on every pass, so no stale value can survive. Only a caller that re-encodes per frame may pass a persistent scratch here — a caller that caches the returned grid must leave it nil.

Source
each_run(row : Indexable(T), width : Int32, & : T, Int32, Int32 -> ) forall T

Scans a row of width values into maximal runs of equal adjacent values, yielding each run's value, start column, and length.

Source
grid_fits?(grid : Array(Array(T)), w : Int32, h : Int32) : Bool forall T

Whether grid already measures exactly w×h. The canonical spelling of the "can I reuse this scratch buffer?" guard for per-frame encode fast paths — a hand-written copy with the wrong polarity either silently disables reuse or fills an undersized grid in place.

Source
luminance(px : PNGGIF::Pixel) : Float64

Rec.709 relative luminance of px (0.2126·R + 0.7152·G + 0.0722·B), in the channels' own 0..255 scale. Callers fold alpha/scale in themselves.

Source
nearest_index(palette : Array(Int32), r : Int32, g : Int32, b : Int32) : Int32

Index of the nearest entry in palette (packed 0xRRGGBB values) to r,g,b by squared RGB distance; ties go to the lower index.

Source
rgb24(v : Int32) : Tuple(Int32, Int32, Int32)

Unpacks a packed 0xRRGGBB color into its {r, g, b} byte channels. Inlined because dither callers use it per pixel: it lets LLVM scalarize the intermediate tuple away.

Source
rgba(bmp : PNGGIF::Bitmap) : Bytes

Flattens bmp to raw interleaved RGBA bytes (w*h*4) — the format ffmpeg ingests as -f rawvideo -pixel_format rgba, and a common interchange payload generally.

Source

Nested types