npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@nktkas/bmp

v1.3.2

Published

Fast and lightweight BMP image encoder/decoder.

Readme

BMP Encoder/Decoder

npm JSR bundlejs

Fast and lightweight BMP image encoder/decoder.

Works with:

Usage

Decode

Supported BMP formats:

  • Any header types: BITMAPINFOHEADER, BITMAPV4HEADER, BITMAPV5HEADER, etc.
  • Any compression methods: BI_RGB, BI_RLE8, BI_RLE4, BI_BITFIELDS, etc.
  • Any bits per pixel: 1, 4, 8, 16, 24, 32
  • Top-down and bottom-up images

Full list of supported BMP formats here

Basic usage

import { decode } from "@nktkas/bmp";

// A minimal 1x1 pixel 24-bit BMP file
const file = new Uint8Array([
  // BMP File Header (14 bytes)
  0x42, 0x4D,
  0x3A, 0x00, 0x00, 0x00,
  0x00, 0x00,
  0x00, 0x00,
  0x36, 0x00, 0x00, 0x00,
  // DIB Header (BITMAPINFOHEADER, 40 bytes)
  0x28, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00,
  0x01, 0x00,
  0x18, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x04, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  // Pixel data (BGR + padding) - 1x1 black pixel
  0x00, 0x00, 0x00,
  0x00
]);

const raw = decode(bmp);
// { width: 1, height: 1, channels: 3, data: Uint8Array(3) [0, 0, 0] }
//                                 ^^^
//                                 may be 1 (grayscale), 3 (RGB), or 4 (RGBA)

BI_JPEG / BI_PNG compressed images

These formats use external compression (JPEG/PNG) for the pixel data, which is not directly supported by the BMP format. To work with these images, you need to extract the compressed data and decode it with a suitable library.

import { extractCompressedData } from "@nktkas/bmp";

// A minimal 1x1 pixel BMP file with embedded PNG data
const bmp = new Uint8Array([
  // BMP File Header (14 bytes)
  0x42, 0x4D,
  0x7B, 0x00, 0x00, 0x00,
  0x00, 0x00,
  0x00, 0x00,
  0x36, 0x00, 0x00, 0x00,
  // DIB Header (BITMAPINFOHEADER, 40 bytes)
  0x28, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00,
  0x01, 0x00,
  0x00, 0x00,
  0x05, 0x00, 0x00, 0x00,
  0x45, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00,
  // Embedded PNG data (69 bytes) - 1x1 black pixel
  // PNG signature
  0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
  // IHDR chunk
  0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01,
  0x08, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x7E, 0x9B, 0x55,
  // IDAT chunk
  0x00, 0x00, 0x00, 0x0C, 0x49, 0x44, 0x41, 0x54,
  0x08, 0x1D, 0x01, 0x02, 0x00, 0xFD, 0xFF, 0x00,
  0x00, 0xE5, 0xE3, 0x00, 0x09, 0x74, 0xC6, 0xD6, 0xC2,
  // IEND chunk
  0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44,
  0xAE, 0x42, 0x60, 0x82
]);

const extracted = extractCompressedData(bmp);
// { width: 1, height: 1, compression: 5, data: Uint8Array(69) [...] }
//                                    ^^^
//                                    may be 4 (BI_JPEG) or 5 (BI_PNG) or others compressions

// Then you can decode it with any JPEG/PNG decoder
import sharp from "sharp";
const raw = await sharp(extracted.data).raw().toBuffer();

Encode

Supported encoding formats:

  • Bits per pixel: 1, 4, 8, 16, 24, 32
  • Compression: BI_RGB, BI_RLE8, BI_RLE4, BI_BITFIELDS, BI_ALPHABITFIELDS
  • Header types: BITMAPINFOHEADER, BITMAPV4HEADER, BITMAPV5HEADER
  • Orientation: Top-down and bottom-up

Basic usage

import { encode } from "@nktkas/bmp";

// A minimal raw image
const raw = {
  width: 2,
  height: 2,
  channels: 3, // 1 (grayscale), 3 (RGB), or 4 (RGBA)
  data: new Uint8Array([ // 2x2 black and white pixels
    0, 0, 0,  255, 255, 255,
    0, 0, 0,  255, 255, 255,
  ]),
} as const;

// Encode to 24-bit BMP (automatic detection of best settings based on raw data)
const bmp = encode(raw);
// Returns Uint8Array with complete BMP file

Advanced options

interface EncodeOptions {
  /**
   * Bits per pixel (1, 4, 8, 16, 24, or 32).
   *
   * Default: Auto-detected from input channels
   * - channels=1 (grayscale) → 8-bit
   * - channels=3 (RGB) → 24-bit
   * - channels=4 (RGBA) → 32-bit
   */
  bitsPerPixel?: 1 | 4 | 8 | 16 | 24 | 32;

  /**
   * BMP compression method identifiers.
   * - 0 (BI_RGB) - No compression. Raw pixel data.
   * - 1 (BI_RLE8) - 8-bit run-length encoding. 256-color indexed only.
   * - 2 (BI_RLE4) - 4-bit run-length encoding. 16-color indexed only.
   * - 3 (BI_BITFIELDS) - Uncompressed with custom RGB bit masks.
   * - 6 (BI_ALPHABITFIELDS) - Uncompressed with custom RGBA bit masks.
   *
   * Default: 0 (BI_RGB)
   */
  compression?: CompressionType;

  /**
   * BMP header format type. Determines header size and features.
   * - `BITMAPINFOHEADER`: 40 bytes. Basic format.
   * - `BITMAPV4HEADER`: 108 bytes. Embedded masks, color space, gamma support.
   * - `BITMAPV5HEADER`: 124 bytes. Adds ICC profiles and rendering intent.
   *
   * Default: `BITMAPINFOHEADER`
   */
  headerType?: HeaderType;

  /**
   * BMP image orientation.
   * - `false` - bottom-up (standard BMP)
   * - `true` - top-down
   *
   * Default: false
   */
  topDown?: boolean;

  /**
   * Color palette for indexed formats (1, 4, 8-bit).
   *
   * If not provided, palette will be generated automatically.
   */
  palette?: RGBQUAD[];

  /**
   * Bitfield masks for BI_BITFIELDS/BI_ALPHABITFIELDS compression.
   *
   * If not provided, default masks will be used:
   * - 16-bit - RGB565
   * - 32-bit - BGRA
   */
  bitfields?: BitfieldMasks;
}

Benchmarks

Decode

Run command: deno run -A tests/decode/bench.ts

# BI_RGB (127x64)
1bit x 64,590 ops/sec @ 15.5µs/op
4bit x 56,930 ops/sec @ 17.6µs/op
8bit x 69,980 ops/sec @ 14.3µs/op
16bit x 56,710 ops/sec @ 17.6µs/op
24bit x 68,570 ops/sec @ 14.6µs/op
32bit x 59,350 ops/sec @ 16.8µs/op

# BI_RLE (127x64)
4bit x 65,920 ops/sec @ 15.2µs/op
8bit x 54,440 ops/sec @ 18.4µs/op

# BI_BITFIELDS (127x64)
16bit x 56,330 ops/sec @ 17.8µs/op
32bit x 50,920 ops/sec @ 19.6µs/op

Encode

Run command: deno run -A tests/encode/bench.ts

# BI_RGB (127x64)
1bit x 9,488 ops/sec @ 105.4µs/op
4bit x 3,677 ops/sec @ 271.9µs/op
8bit x 5,847 ops/sec @ 171.0µs/op
16bit x 27,340 ops/sec @ 36.6µs/op
24bit x 31,200 ops/sec @ 32.1µs/op
32bit x 27,690 ops/sec @ 36.1µs/op

# BI_RLE (127x64)
4bit x 3,591 ops/sec @ 278.5µs/op
8bit x 5,971 ops/sec @ 167.5µs/op

# BI_BITFIELDS (127x64)
16bit x 32,090 ops/sec @ 31.2µs/op
32bit x 29,110 ops/sec @ 34.4µs/op