BNG IMAGE FORMAT SPECIFICATION Container identifier BTPNGF, version 1. File extension .bng. Revision of 2026-09-23. This document describes the format only. 0. CONVENTIONS All integers stored in a file are unsigned and little-endian. "Byte" means eight bits. Bit positions within a byte are numbered from 0 for the least significant bit to 7 for the most significant bit. x & y, x | y are bitwise operations. x >> n and x << n are shifts. A shift written as "arithmetic" propagates the sign bit. "s as int8" means the byte s reinterpreted as a two's complement signed 8-bit value: s if s < 128, otherwise s - 256. Arithmetic on samples, predictions and residual reconstruction is performed modulo 256 unless stated otherwise. The format defines no checksum. A decoder can detect a truncated or structurally invalid file, but cannot in general detect a corrupted one. 1. PIXELS An image is a rectangular array of width x height pixels, width >= 1 and height >= 1. A pixel has four 8-bit channels, named B, G, R and A. On the pixel side of an encoder or decoder one pixel occupies four consecutive bytes in this order: +0 B blue +1 G green +2 R red +3 A alpha R, G and B are sRGB premultiplied by A: stored = (component x A + 127) / 255 A is linear coverage. A stored component may exceed A, and A = 0 does not imply that R, G and B are 0. All four channels are coded losslessly and none of them is interpreted by the format, so a fourth byte that carries padding rather than coverage round-trips unchanged. No gamma or ICC information is carried by the format. Rows on the pixel side are addressed by a pitch, the distance in bytes between the first byte of one row and the first byte of the next, which must be at least 4 x width. The pitch is a property of the caller's memory, not of the file, and is never stored. 2. FILE STRUCTURE A file is a 20-byte header followed by a body. offset size field 0 1 0xFF 1 6 signature, the six bytes 0E 38 30 2A 1A 18 7 1 version, the byte 0x01 8 4 width in pixels, u32 12 4 height in pixels, u32 16 4 stream_byte_count, u32 20 ... body Bytes 0..7 are constant for every version-1 file and may be compared directly as the eight-byte sequence FF 0E 38 30 2A 1A 18 01 Bytes 1..6 spell "BTPNGF" in the Serenum CLEAR character encoding, which is not ASCII, and byte 7 is "1" in that same encoding. A decoder does not need to know that encoding; the bytes are fixed. stream_byte_count is interpreted as bit 31 RAW flag bits 0..30 body_length, the length of the body in bytes The header is not included in body_length. The total file length is exactly 20 + body_length so a reader can find the end of an image without decoding it, and images can be stored back to back in one stream. If the RAW flag is set the body is stored as described in section 3. If it is clear the body is a coded bitstream, sections 4 to 11. 3. RAW BODY With the RAW flag set the body is the pixel data itself: height rows of width pixels, each pixel four bytes in the order B, G, R, A, rows packed one after another with no padding between them. body_length is then exactly 4 x width x height; any other value makes the file invalid. A raw body carries the same image a coded body would. An encoder may choose either; a decoder must accept both. Because a raw body is never longer than the pixels it stores, no file of a width w, height h image exceeds 20 + 4 x w x h bytes. 4. CODED BODY: OVERVIEW The coded body is produced by, in order: (1) the colour transform of section 5, applied per pixel, giving four planes of 8-bit samples; (2) the prediction of section 6, applied per plane, giving one residual per sample; (3) the tokenisation of sections 7 to 9; (4) the Huffman code of section 10, packed as in section 11. A decoder reverses these steps. Nothing about the code or the model is stored in the file: sections 9 and 10 are fixed for all images. 5. COLOUR TRANSFORM The transform is YCoCg-R, which is reversible on 8-bit channels. It produces three samples Y, Co, Cg from R, G, B. The fourth plane carries A unchanged. Forward, from R, G, B to Y, Co, Cg: Co = (R - B) & 255 t = (B + ((Co as int8) >> 1)) & 255 arithmetic shift Cg = (G - t) & 255 Y = (t + ((Cg as int8) >> 1)) & 255 arithmetic shift Inverse, from Y, Co, Cg to R, G, B: t = (Y - ((Cg as int8) >> 1)) & 255 arithmetic shift G = (Cg + t) & 255 B = (t - ((Co as int8) >> 1)) & 255 arithmetic shift R = (B + Co) & 255 The four planes are numbered plane 0 Y plane 1 Co plane 2 Cg plane 3 A 6. PREDICTION Each plane is predicted independently. Within a plane, the sample at column x of row y is predicted from three causal neighbours of the same plane: L the sample at (x - 1, y) T the sample at (x, y - 1) TL the sample at (x - 1, y - 1) The predictor is MED, the median edge detector: mn = min(L, T) mx = max(L, T) g = L + T - TL computed as integers, not modulo 256 pred = g < mn ? mn : (g > mx ? mx : g) A neighbour that lies outside the image reads as 0, which needs no special case: the prediction is 0 for the first sample of a plane, the left neighbour throughout row 0, and the upper neighbour in column 0. The residual of a sample s is e = (s - pred) & 255 interpreted as a signed value, e as int8, in the range -128..127. The reconstruction is s = (pred + e) & 255 Predictions use reconstructed samples, which in a lossless format are the original samples. 7. RESIDUAL CODING Each residual is coded as a token, optionally followed by a mantissa of a width the token fixes. A run of consecutive zero residuals within one row of one plane may be coded as a single token. The alphabet is section 9. 8. SYMBOL ORDER The body codes rows in increasing y, and within a row the four planes in the order 0, 1, 2, 3: row 0 plane 0, row 0 plane 1, row 0 plane 2, row 0 plane 3, row 1 plane 0, ... Each (row, plane) pair is a self-contained sequence of tokens covering exactly width residuals, in increasing x. A run token never crosses a row or a plane boundary. A decoder therefore holds rows, not planes, and can emit an output row as soon as its four planes are decoded. 9. TOKEN ALPHABET There are 144 tokens, numbered 0..143. Each carries a base value, a mantissa width in bits, and a kind. The mantissa, when present, is written immediately after the token, most significant bit first, and its value is added to the base. token kind value mantissa bits 0 residual 0 0 1..126 residual +/- (tk + 1) / 2 0 positive if tk is odd, negative if tk is even 127..130 residual +/- (2^ex + mantissa), ex ex = 6 + (tk - 127) / 2, positive if tk is even, negative if tk is odd 131 end of row the rest of the row is zero 0 132..143 zero run 2^j + mantissa zeros, j j = tk - 131 Consequences of that table, stated explicitly: - Tokens 1..126 cover residual magnitudes 1..63 directly. Token 1 is +1, token 2 is -1, token 3 is +2, token 4 is -2, and so on, to token 125 = +63 and token 126 = -63. - Tokens 127..130 cover residual magnitudes 64..128 by exponent: token 127 is +(64 + m) with a 6-bit mantissa m, token 128 is -(64 + m) with a 6-bit mantissa, token 129 is +(128 + m) with a 7-bit mantissa, token 130 is -(128 + m) with a 7-bit mantissa. Residuals lie in -128..127, so of the values above magnitude 127 only -128 can occur, as token 130 with a zero mantissa. - Tokens 132..143 cover zero runs of length 2 to 8191: token 132 is 2 + m with a 1-bit mantissa, covering runs of 2..3; token 133 is 4 + m with a 2-bit mantissa, covering 4..7; and so on to token 143, 4096 + m with a 12-bit mantissa, covering 4096..8191. - A single zero residual is token 0, not a run. - Token 131 ends the row: every remaining residual of the current row of the current plane is zero, however many there are. No token in 0..143 is unused. 10. THE HUFFMAN CODE One canonical Huffman code is used for every token, every plane and every image. It is part of the format; an encoder does not choose it. The maximum codeword length is 12 bits. The Kraft sum of the lengths is exactly 1, so no codeword space is wasted and every 12-bit pattern completes a codeword. The code length in bits of each of the 144 tokens, in token order: tk 0..15 3 3 3 4 4 4 4 5 5 6 6 6 6 7 7 7 tk 16..31 7 7 7 8 8 8 8 8 8 8 8 9 9 9 9 9 tk 32..47 9 9 9 9 9 10 10 10 10 10 10 10 10 10 10 10 tk 48..63 10 10 10 10 10 11 11 11 11 11 11 11 11 11 11 11 tk 64..79 11 11 11 11 11 11 11 11 11 12 12 12 12 12 12 12 tk 80..95 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 tk 96..111 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 12 tk 112..127 12 12 12 12 12 12 12 12 12 12 12 12 12 11 12 7 tk 128..143 7 12 11 9 4 6 7 8 9 10 10 11 12 12 12 12 Codewords are assigned canonically: shorter lengths first, and within one length in increasing token order. code = 0 for L = 1 to 12: for tk = 0 to 143 in increasing order: if length[tk] == L: assign codeword code, of length L, to token tk code = code + 1 code = code << 1 For decoding, let first[L] be the value of code at the start of length L and count[L] the number of tokens of that length. A decoder shifts bits into an accumulator v one at a time, starting at L = 1 and increasing L by one per bit. When v - first[L] < count[L] the codeword is complete, and the token is the (v - first[L])-th token, counting from zero, among those of length L in increasing token order. 11. BIT PACKING The body is a sequence of bits packed into bytes most significant bit first: the first bit of the body is bit 7 of the body's first byte, the second is bit 6, and so on. A codeword is written most significant bit first, and its mantissa, if it has one, follows immediately, also most significant bit first. After the last token of the last row the body is padded with zero bits to a byte boundary. body_length counts that final byte. Because the final byte is zero-padded, a decoder reading the last codeword of a file may consume bits that were never written. A decoder must therefore supply zero bits past the end of the body rather than failing immediately. The image ends when width x height pixels have been decoded; truncation is detected by body_length, not by running out of bits. 12. ENCODING PROCEDURE For each pixel apply section 5 to obtain the four plane samples. For each plane of each row, in the order of section 8, compute residuals per section 6 and emit tokens for that row of that plane as follows, with x starting at 0 and the row ending at width: if the residual at x is not zero: let a be its magnitude if a <= 63: emit token 2a - 1 for a positive residual, token 2a for a negative residual otherwise: let ex = floor(log2(a)) emit token 127 + 2 x (ex - 6) for a positive residual, token 128 + 2 x (ex - 6) for a negative residual emit ex mantissa bits holding a - 2^ex advance x by 1 otherwise let run be the number of consecutive zero residuals starting at x: if x + run == width: emit token 131, and the row is finished if run == 1: emit token 0 and advance x by 1 otherwise: if run > 8191, set run = 8191 let j = floor(log2(run)) emit token 131 + j emit j mantissa bits holding run - 2^j advance x by run A trailing run that reaches the end of a row is always token 131, even when it is a single zero, so token 0 never appears as the last token of a row. After the last row, flush the bit writer with zero padding and write the header, with body_length set to the number of body bytes produced and the RAW flag clear. An encoder whose coded body would reach 4 x width x height bytes should write a raw body instead, per section 3. Either choice is valid. 13. DECODING PROCEDURE Validate the header as in section 14. If the RAW flag is set, copy the pixels out of the body as in section 3, and stop. Otherwise decode row by row. For each row, for each plane 0..3, fill width residuals: read a token read its mantissa, if it has one, and add it to the token's base if the token is a residual token: store the signed value at x and advance x by 1 if the token is a zero run or the end of row token: let n be its value; if n exceeds the number of residuals remaining in the row, reduce n to that number store n zeros and advance x by n if n is zero, the file is invalid Reconstruct that plane's samples with section 6. When all four planes of the row are reconstructed, invert the colour transform with section 5 and write the row's pixels at the caller's pitch. A decoder that meets a bit pattern completing no codeword within 12 bits must reject the file. 14. CONFORMANCE A decoder must reject a file if any of the following holds: - the file is shorter than 20 bytes; - bytes 0..6 are not FF 0E 38 30 2A 1A 18; - byte 7 is not 0x01; - width is 0, or height is 0; - width x height exceeds the decoder's own pixel budget, which it must apply before allocating anything; - body_length exceeds the number of bytes available after the header; - the RAW flag is set and body_length is not exactly 4 x width x height; - a codeword is not completed within 12 bits; - a zero run token advances the position by zero. A decoder must not read past 20 + body_length, and must accept a stream that continues with further data after that point. A file is valid if it decodes under these rules. Because the format carries no checksum, a file whose body bytes have been altered may still decode, to different pixels. Integrity protection, where it is needed, belongs to the layer around the file. 15. LIMITS maximum width 2^32 - 1, subject to the pixel budget maximum height 2^32 - 1, subject to the pixel budget recommended pixel budget 2^28 pixels maximum body length 2^31 - 1 bytes maximum file length 20 + 4 x width x height bytes channels 4, of 8 bits each codeword length 1..12 bits alphabet 144 tokens maximum zero run 8191 samples, or to the end of a row