image.bend

gralha-image@0.1.0.1 · 65 lines · 11 definitions

1# Premultiplied colors and quadtree pixel buffers shared by the decoders and the renderer.
2import Base
3import gralha-text-scan@0.1.0.1/scan.bend as U
4
5type Color is Data:
6  Color{r: F32, g: F32, b: F32, a: F32}
7
8def clear() -> Color:
9  Color{0.0, 0.0, 0.0, 0.0}
10
11def 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.
16type Pixels is Data:
17  Flat{color: Color}
18  Quad{tl: Pixels, tr: Pixels, bl: Pixels, br: Pixels}
19
20def 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
25def 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.
32type Picture is Data:
33  Picture{width: U32, height: U32, depth: Nat, pixels: Pixels}
34
35def picture.blank() -> Picture:
36  Picture{0, 0, 0n, Flat{clear()}}
37
38@unsafe
39def 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
47def 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
51def 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))))