1 # Premultiplied colors and quadtree pixel buffers shared by the decoders and the renderer. 2 import Base 3 import gralha-text-scan@0.1.0.1/scan.bend as U 4 5 type Color is Data: 6 Color{r: F32, g: F32, b: F32, a: F32} 7 8 def clear() -> Color: 9 Color{0.0, 0.0, 0.0, 0.0} 10 11 def rgba(r: F32, g: F32, b: F32, +a: F32) -> Color: 12 Color{(r * a : F32), (g * a : F32), (b * a : F32), a} 13 14 # Filter and embedded-image buffers retain premultiplied floating-point channels. 15 # Uniform tiles share a single color instead of allocating one value per pixel. 16 type Pixels is Data: 17 Flat{color: Color} 18 Quad{tl: Pixels, tr: Pixels, bl: Pixels, br: Pixels} 19 20 def color.same(a: Color, b: Color) -> Bool: 21 Color{ar, ag, ab, aa} = a 22 Color{br, bg, bb, ba} = b 23 F32.is_eq(ar, br) && F32.is_eq(ag, bg) && F32.is_eq(ab, bb) && F32.is_eq(aa, ba) 24 25 def pixels.join(+a: Pixels, +b: Pixels, +c: Pixels, +d: Pixels) -> Pixels: 26 match a b c d: 27 case Flat{aa} Flat{bb} Flat{cc} Flat{dd}: 28 U.choose(Pixels, color.same(aa, bb) && color.same(aa, cc) && color.same(aa, dd), Flat{aa}, Quad{a, b, c, d}) 29 case _ _ _ _: Quad{a, b, c, d} 30 31 # A decoded image: a square quadtree of premultiplied colors covering width by height. 32 type Picture is Data: 33 Picture{width: U32, height: U32, depth: Nat, pixels: Pixels} 34 35 def picture.blank() -> Picture: 36 Picture{0, 0, 0n, Flat{clear()}} 37 38 @unsafe 39 def pixels.get(xs: Pixels, +size: U32, +x: U32, +y: U32) -> Color: 40 match xs: 41 case Flat{c}: c 42 case Quad{a, b, c, d}: 43 +half = (size / 2 : U32) 44 pixels.get(U.choose(Pixels, U32.is_lt(y, half), U.choose(Pixels, U32.is_lt(x, half), a, b), U.choose(Pixels, U32.is_lt(x, half), c, d)), half, U32.mod(x, half), U32.mod(y, half)) 45 46 @unsafe 47 def picture.depth(+size: U32, +side: U32, +depth: Nat) -> Nat: 48 U.branch(Nat, U32.is_ge(side, size), _ => depth, _ => picture.depth(size, (side * 2 : U32), 1n+depth)) 49 50 @unsafe 51 def pixels.set(+pixels: Pixels, +depth: Nat, +x: U32, +y: U32, +col: Color) -> Pixels: 52 match pixels: 53 case Flat{c}: 54 match depth: 55 case 0n: Flat{col} 56 case 1n+d: pixels.set(Quad{Flat{c}, Flat{c}, Flat{c}, Flat{c}}, 1n+d, x, y, col) 57 case Quad{a, b, c, e}: 58 match depth: 59 case 0n: Flat{col} 60 case 1n+d: 61 +dd = d 62 +half = U32.shln(1, dd) 63 U.branch(Pixels, U32.is_lt(y, half), 64 _ => U.branch(Pixels, U32.is_lt(x, half), _ => pixels.join(pixels.set(a, dd, x, y, col), b, c, e), _ => pixels.join(a, pixels.set(b, dd, (x - half : U32), y, col), c, e)), 65 _ => U.branch(Pixels, U32.is_lt(x, half), _ => pixels.join(a, b, pixels.set(c, dd, x, (y - half : U32), col), e), _ => pixels.join(a, b, c, pixels.set(e, dd, (x - half : U32), (y - half : U32), col))))