this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: tessera-viz PNG encoder + tessera-viz-jsoo design

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+71
+71
docs/plans/2026-03-06-tessera-viz-png-jsoo-design.md
··· 1 + # tessera-viz PNG Encoder + tessera-viz-jsoo Design 2 + 3 + ## Goal 4 + 5 + Add PNG encoding to the portable tessera-viz library and create a thin tessera-viz-jsoo package for displaying images in the OCaml notebook. 6 + 7 + ## Architecture 8 + 9 + Two changes: 10 + 11 + 1. **tessera-viz**: Add `png_of_rgba` — a minimal PNG encoder using uncompressed deflate (stored blocks). No external dependencies. Works in native, bytecode, and js_of_ocaml contexts. 12 + 13 + 2. **tessera-viz-jsoo**: A thin package that base64-encodes PNG bytes and formats them as a `data:image/png;base64,...` URL for display in notebook cells. 14 + 15 + ## Why uncompressed deflate 16 + 17 + PNG requires zlib/deflate compression for IDAT chunks. Rather than adding a dependency on `decompress` or similar, we use deflate stored blocks (no compression). This produces valid but larger PNGs. For our tile sizes (~200x200 pixels = ~160KB uncompressed RGBA), the overhead is acceptable. A proper compressor can be swapped in later if needed. 18 + 19 + ## API 20 + 21 + ### tessera-viz (addition to existing library) 22 + 23 + ```ocaml 24 + val png_of_rgba : rgba_image -> string 25 + (** Encode an RGBA image as PNG bytes. 26 + Uses uncompressed deflate for portability. *) 27 + ``` 28 + 29 + ### tessera-viz-jsoo (new package) 30 + 31 + ```ocaml 32 + val to_data_url : Viz.rgba_image -> string 33 + (** Convert an RGBA image to a data:image/png;base64,... URL 34 + suitable for use in an <img> tag. *) 35 + ``` 36 + 37 + ## PNG format (minimal subset) 38 + 39 + ``` 40 + PNG signature (8 bytes) 41 + IHDR chunk: width, height, bit_depth=8, color_type=6 (RGBA) 42 + IDAT chunk: zlib header (2 bytes) + stored deflate blocks + adler32 43 + IEND chunk 44 + ``` 45 + 46 + Each chunk: length (4B) + type (4B) + data + CRC32 (4B). 47 + 48 + CRC32 is computed over type+data using the standard polynomial (0xEDB88320). 49 + 50 + Scanlines: each row is prefixed with filter byte 0 (None), then raw RGBA pixels. 51 + 52 + ## Testing 53 + 54 + - **PNG encoder (Alcotest)**: Encode a small known image, verify PNG magic bytes and valid structure. Decode with Python/ImageMagick to verify correctness. 55 + - **tessera-viz-jsoo (Playwright)**: Encode an image in a web worker, post data URL to page, verify `<img>` displays. 56 + 57 + ## Package structure 58 + 59 + ``` 60 + tessera-viz-jsoo/ 61 + dune-project 62 + tessera-viz-jsoo.opam 63 + lib/ 64 + dune 65 + viz_jsoo.ml 66 + viz_jsoo.mli 67 + test/ 68 + dune 69 + test_browser.ml 70 + test_browser.html 71 + ```