BMP codec support
August 10, 2026 ยท View on GitHub
This document is the capability contract for PureJsImage's first-party BMP codec. A checked item is implemented in the current code. An unchecked item is planned and must not be presented as supported until its output is independently validated. The correctness section records the current fixture and benchmark coverage.
Decode
File and DIB headers
- Windows bitmap files with the
BMsignature - OS/2 1.x / Windows 2
BITMAPCOREHEADER(12 bytes) - Windows
BITMAPINFOHEADER(40 bytes) - 52-byte and 56-byte bitfield headers
- OS/2 2.x 64-byte DIB headers
- Windows
BITMAPV4HEADER(108 bytes) - Windows
BITMAPV5HEADER(124 bytes) - Header-only width, height, bit depth, and explicit-alpha inspection
- Bottom-up and top-down row order
- Four-byte row alignment and odd-width row padding
- Bitmap-array, color-icon, color-pointer, and other non-
BMOS/2 file signatures - Standalone DIB pixel data without a 14-byte bitmap file header
- Embedded or linked V5 color-profile validation and exposure
Pixel formats
- 1-bit indexed pixels
- 4-bit indexed pixels
- 8-bit indexed pixels
- OS/2 three-byte BGR palette entries
- Windows four-byte BGR palette entries
- Palette sizes smaller than the bit-depth maximum through
biClrUsed - Uncompressed 16-bit RGB555
- Uncompressed 24-bit BGR
- Uncompressed 32-bit BGRX treated as opaque
- 16-bit and 32-bit
BI_BITFIELDS - 16-bit and 32-bit
BI_ALPHABITFIELDS - RGB555, RGB565, reordered channels, and other non-overlapping contiguous channel masks with full-range 8-bit scaling
- Embedded RGB masks from V2/V3/V4/V5 headers and explicit alpha masks from V3/V4/V5 headers or appended mask blocks
- 2-bit indexed OS/2 pixels
- Non-contiguous channel masks if compatible real-world files require them
- Producer-specific alpha in 32-bit
BI_RGBfiles without an explicit alpha mask - High-dynamic-range or scRGB BMP variants
Compression
- Uncompressed
BI_RGB -
BI_RLE4encoded runs -
BI_RLE4absolute runs and word padding -
BI_RLE8encoded runs -
BI_RLE8absolute runs and word padding - RLE end-of-line, end-of-bitmap, and delta commands
-
BI_BITFIELDS -
BI_ALPHABITFIELDS - Embedded
BI_JPEG - Embedded
BI_PNG -
BI_CMYK,BI_CMYKRLE4, andBI_CMYKRLE8
Color and metadata
- Structural indexed or RGB pixel conversion to
rgb8 - Explicit masked-alpha conversion to
rgba8 - Calibrated RGB endpoints and gamma from V4/V5 headers
- Embedded ICC profile conversion to sRGB
- Rendering intent, resolution, palette importance, and application metadata exposure
- Metadata preservation or explicit stripping controls when re-encoding
Memory and execution
- Direct region reads for uncompressed and bitfield images
- Bounded 32-row decode blocks without a source-sized RGB or RGBA bitmap for uncompressed and bitfield images
- Horizontal crop conversion without materializing pixels outside the requested output region
- Direct seeking to requested top-down or bottom-up source rows
- Public crop, resize, BMP-to-BMP, and BMP-to-other-codec pipelines
- Explicit RLE fallback that retains one byte of palette index data per source pixel to reorder the bottom-up command stream
- Bounded RLE row reconstruction or a disk-backed fallback that avoids a source-sized index plane
- Decoder-driven scaled output for large downscales
Encode
Implemented target
- First-party Windows BMP encoding
- Top-down output for streaming row order
- Uncompressed 24-bit
BI_RGBwith a 40-byteBITMAPINFOHEADER - Uncompressed 32-bit
BI_BITFIELDSwith a 108-byteBITMAPV4HEADER - Explicit RGBA masks and sRGB color-space declaration for alpha output
- Correct four-byte row padding
-
gray8,rgb8, andrgba8pipeline input - Automatic alpha output for
rgba8input unlessalpha: falseis requested - Opaque alpha insertion when 32-bit output is requested from gray or RGB input
- Streaming, top-to-bottom output with a single encoded-row buffer
- Public
image.bmp()andimage.encode('bmp')APIs
Planned for common output
- Indexed 1-, 4-, and 8-bit output with deterministic palette generation
- RLE4 and RLE8 output
- 16-bit RGB555 and RGB565 output
- Configurable 24-bit versus 32-bit output independent of source pixel type
- Configurable bottom-up output for consumers that reject negative heights
- Explicit alpha-discard versus background-compositing behavior when writing opaque BMP from RGBA input
- Configurable resolution instead of the current fixed 2835 pixels per metre
- V5 ICC profile, rendering intent, and color metadata writing or preservation
- OS/2 BMP and standalone DIB output if required by real consumers
- Validate output-size arithmetic and reject files that exceed BMP's 32-bit offsets or file-size fields
Correctness and safety contract
- Validate the declared file size does not exceed the input or precede the pixels, plus pixel offset, DIB size, dimensions, plane count, bit-depth/compression combinations, row extents, and palette bounds
- Validate channel masks are non-empty, contiguous, and non-overlapping
- Validate RLE commands, runs, deltas, row boundaries, padding, and the final end marker
- Apply configurable input-size, dimension, pixel-count, and decoded-byte limits before decoding
- Reject truncated pixels, invalid planes, invalid masks, and RLE overruns explicitly
- Verify 14 public-domain BMP Suite fixtures with pinned reference pixels
- Cover OS/2, Windows V3/V5, 1/4/8-bit palettes, RLE4/RLE8, top-down rows, odd-width padding, RGB555/RGB565, reordered 32-bit masks, and explicit alpha
- Round-trip encoded 24-bit and 32-bit output through the first-party decoder and verify exact pixels
- Require the benchmark's independent BMP decoder to recognize encoded output and its dimensions
- Benchmark metadata, decode, crop, resize, conversion, and encode workflows in isolated processes, including a deterministic 4000x3000 image
- Add focused fixtures for every accepted DIB header size and every RLE command boundary rather than relying on shared parser paths
- Verify encoded opaque and alpha pixels with an independent BMP decoder
- Complete the 125-file Imazen BMP corpus decode-to-PNG baseline with all supported valid files decoded and every invalid input rejected safely
- Add malformed-header, palette, mask, offset, stride, RLE, and allocation fuzzing with strict limits