From ea8d23b1054265f728563d0148768e971d4fe798 Mon Sep 17 00:00:00 2001 From: Slendi Date: Mon, 8 Sep 2025 17:27:42 +0300 Subject: [PATCH] Add specification Signed-off-by: Slendi --- docs/specification.pdf | Bin 0 -> 37742 bytes docs/specification.typ | 126 +++++++++++++++++++++++++++++++++++++++++ flake.nix | 5 +- 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 docs/specification.pdf create mode 100644 docs/specification.typ diff --git a/docs/specification.pdf b/docs/specification.pdf new file mode 100644 index 0000000000000000000000000000000000000000..eb51ba6f9265306d9ae45f802019fde928d62fca GIT binary patch literal 37742 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 + ] + + #line(length: 100%, stroke: 0.5pt + gray) + #v(8pt) +] + +#set heading(numbering: "1.1.") + +#columns(2)[ + += Overview + +BFL is a compact, monochrome bitmap image format with optional 1-bit +transparency. Each image has two bitplanes that are encoded: + +1. *Image bitplane* (required): 1 = white pixel, 0 = black pixel +2. *Alpha bitplane* (optional): 1 = transparent pixel, 0 = opaque pixel + +Each bitplane is independently encoded using a run-length encoding or stored raw +bit-packed. Each resulting byte stream can then be optionally LZSS-compressed. +All multi-byte integers are little-endian. + += File Structure + +``` +struct Header { + char magic[3]; // Magic bytes "BFL" + u16 width; // Width of the image in pixels + u16 height; // Height of the image in pixels + u8 flags; // Various flags + u32 img_len; // Image stream length + u32 al_len; // Alpha stream length, 0 if none + u8 img[]; // Image stream bytes + u8 al[]; // Alpha stream bytes +} +``` + +== Dimensions + +- Width and height are 16-bit unsigned integers. +- Pixel order is row-major, top-to-bottom, left-to-right. +- Total pixel count `N = Width * Height`. All bitplane encoders/decoders operate + on exactly `N` bits. + +== Flags + +- `0x01` *FLAG_HAS_ALPHA* - Alpha bitplane is present. +- `0x02` *FLAG_IMG_RAW* - Image stream is raw bit-packed. +- `0x04` *FLAG_TRA_RAW* - Alpha stream is raw bit-packed. +- `0x08` *FLAG_IMG_NOLZ* - Image stream is *not* LZSS-compressed. +- `0x10` *FLAG_TRA_NOLZ* - Alpha stream is *not* LZSS-compressed. + +It is up to the encoder to determine which combination of those flags results in +a smaller file. + +All remaining bits are reserved and should be set to 0. + += Bit-packing RAW streams + +When a stream is marked `RAW` in flags, bits are packed LSB-first within each +byte. + +- Bit for pixel index `i` is stored at byte `i / 8`, bit position `i % 8`. +- Unused high bits of the final byte (if `N` is not a multiple of 8) *must* be + zero when encoding and *must* be ignored when decoding. + +#colbreak() + += Coinflip RLE + +Unless a stream is marked `RAW`, each bitplane is encoded using "coinflip" +run-length coding: +``` +Byte 0: Initial state (0 = start with 0-runs, 1 = start with 1-runs) +Byte 1..k: Repeated groups of: + Count (u8, number of pixels to emit of the current state) + [Optional 0] (u8, do not toggle state if present) +``` + += LZSS + +After coinflip RLE or RAW bit-packing, each resulting byte stream may be +compressed independently using LZSS with the following parameters: +- *Window size*: 4096 +- *Lookahead*: 18 +- *Minimum match*: 3 + +== Block format + +Streams are encoded as a sequence of 8‑item groups preceded by a flag byte: + +- For each bit b (0..7) in the flag (LSB first): + - If `(flag >> b) & 1 == 1`: Literal — copy next byte to output. + - Else: Match — read two bytes b0, b1 and emit: + - `length = (b0 >> 4) + 3` (range 3..18) + - `offset = ((b0 & 0x0F) << 8) | b1` (range 1..4095) + - Copy length bytes from `out_size - offset`. + +] diff --git a/flake.nix b/flake.nix index f8ea713..7fed347 100644 --- a/flake.nix +++ b/flake.nix @@ -33,6 +33,7 @@ doxygen gtest cppcheck + typst ] ++ buildInputs ++ nativeBuildInputs @@ -42,10 +43,6 @@ valgrind-light ] ); - - env = { }; - - shellHook = ''''; }; } );