Layer images are the size of the printer’s screen
(ResolutionX × ResolutionY from the header) and are
stored row by row from the top left. CTB, CBDDLP and the previews each use a
different run-length code. As with the rest of the format, these are known from
reverse engineering; this page follows UVtools (CtbRleCodec.cs,
BitPlaneRle.cs, FileFormat.cs).
CTB layers: 7-bit greyscale runs
Each entry starts with a byte whose low 7 bits are a grey level (0 to 127).
- High bit clear: one pixel of that grey.
- High bit set: a run of that grey, whose length follows in 1 to 4 bytes, big-endian, with the length of the field given by its first bits:
| First length byte | Bytes | Run length |
|---|---|---|
0xxxxxxx | 1 | 7 bits: up to 127 |
10xxxxxx | 2 | 14 bits: up to 16 383 |
110xxxxx | 3 | 21 bits: up to 2 097 151 |
1110xxxx | 4 | 28 bits: up to 268 435 455 |
A grey level g other than 0 becomes the 8-bit pixel
(g << 1) | 1, so 127 is 255 (white); 0 stays black. Longer runs
than 268 435 455 pixels are split. Black runs at the end
may be left out, since readers start from a black image.
Worked example
| Bytes | Meaning |
|---|---|
80 64 | Run (high bit) of grey 0 (black), length 0x64 = 100 |
FF 81 2C | Run of grey 127 (white, 255); length bytes 10-prefixed: ((0x81 & 0x3F) << 8) + 0x2C = 300 |
40 | One pixel of grey 64, written as (64 << 1) | 1 = 129 |
CTB layer encryption
When the header’s EncryptionKey is not 0, each layer’s encoded bytes
are XORed with a keystream made from that seed and the layer number
n (all arithmetic on unsigned 32-bit numbers, wrapping):
init = seed × 0x2D83CDAC + 0xD8A83423
key = (n × 0x1E1530CD + 0xEC3D47CD) × init
for each whole 4-byte word w (little-endian):
w = w XOR key
key = key + init
remaining 1–3 bytes: XOR with the low, then next, bytes of key
With seed 1, init is 0x062C01CF and layer 0’s first key is
0x6810DBC3, so the 3-byte layer FF 81 2C is stored as
3C 5A 3C (XOR with C3 DB 10). Chitubox picks a random
seed; CBDDLP files are never encrypted.
CBDDLP layers: 1-bit planes
Each byte is one run: the high bit is the pixel state (1 lit, 0 dark) and the low 7 bits the run length, 1 to 125 in files written by Chitubox. Runs fill the image row by row; a plane ends when all pixels are covered.
For antialiasing a layer is stored as N planes
(AntiAliasLevel, 1, 2, 4 or 8), each with its own entry in the layer
table. Plane b is lit where the original grey is at or above a
threshold, so a pixel is lit in more planes the brighter it is. To rebuild the
grey, count the planes in which a pixel is lit, c, and take
c × (256 / N) − 1 (0 when c is 0). With one plane the
threshold is 127.
Example: FD FD 8A 05 is 125 lit, 125 lit, 10 lit, then 5 dark
pixels.
Previews: 16-bit colour runs
Each pixel is a 16-bit little-endian word in RGB 5-6-5 form whose lowest green bit (bit 5, mask 0x0020) is reused as a repeat flag:
| Bits | Meaning |
|---|---|
| 15–11 | Red, 5 bits |
| 10–6 | Green, top 5 bits |
| 5 | Repeat flag |
| 4–0 | Blue, 5 bits |
Without the flag the word is one pixel. With it, a second 16-bit word follows
whose low 12 bits are the number of extra copies (so up to 4096 pixels in all);
Chitubox sets its bits 12 and 13 (| 0x3000). To get 8-bit channels,
shift each 5-bit value left by 3.
Example: FF FF 63 30 is white (0xFFFF, flag set) repeated
0x063 + 1 = 100 times; 00 00 is one black pixel.