diff --git a/docs/specification.pdf b/docs/specification.pdf new file mode 100644 index 0000000..eb51ba6 Binary files /dev/null and b/docs/specification.pdf differ diff --git a/docs/specification.typ b/docs/specification.typ new file mode 100644 index 0000000..c788dda --- /dev/null +++ b/docs/specification.typ @@ -0,0 +1,126 @@ +#import "@preview/codly:1.3.0": * +#import "@preview/codly-languages:0.1.1": * + +#show: codly-init.with() + +#codly( + languages: codly-languages, + number-format: none, + zebra-fill: none, + display-name: false, + display-icon: false +) + +#set page( + paper: "a4", + flipped: true, + margin: 1cm, +) +#set text(size: 8pt) + +#align(center)[ + #text(20pt)[*BitFLip (BFL) Image Format*] \ + + #text(12pt)[ + Specification *v1.0*, + #datetime.today().display("[year]-[month]-[day]"), + Slendi <#link("mailto:slendi@socopon.com")> + ] + + #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 = ''''; }; } );