Task 065: .BMP File Format
Task 065: .BMP File Format
1. BMP File Format Specifications
The BMP (Bitmap) file format is a raster graphics format primarily used for storing digital images on Microsoft Windows and OS/2 systems. It supports monochrome and color images with various depths (1 to 32 bits per pixel), optional compression (such as run-length encoding), alpha channels in later versions, and color profiles. The format is device-independent and consists of a file header, a device-independent bitmap (DIB) header, an optional color table, pixel data, and additional optional sections for alignment or profiles. Standard versions include Windows 2.0/3.x (BITMAPCOREHEADER), Windows 3.1/NT (BITMAPINFOHEADER), Windows 95/NT4 (BITMAPV4HEADER), and Windows 98/2000 (BITMAPV5HEADER). Data is stored in little-endian byte order, with pixel arrays typically bottom-up (starting from the lower-left corner).
2. List of Properties Intrinsic to the BMP File Format
The properties intrinsic to the BMP file format are derived from its internal structure, specifically the bitmap file header and DIB header fields. These are stored directly within the file and define its characteristics, such as dimensions, color information, and layout. Below is a comprehensive list based on standard Windows BMP versions (focusing on versions 3, 4, and 5). Properties are grouped by header section, with offsets relative to the file start.
Bitmap File Header (Offsets 0–13, 14 bytes):
- Signature: 2 bytes (offset 0), ASCII "BM" (0x42 0x4D), identifies the file as BMP.
- File Size: 4 bytes (offset 2), total size of the file in bytes.
- Reserved 1: 2 bytes (offset 6), application-specific (often 0).
- Reserved 2: 2 bytes (offset 8), application-specific (often 0).
- Pixel Array Offset: 4 bytes (offset 10), byte offset to the start of pixel data.
DIB Header (Starts at offset 14, size varies by version):
Common Fields (Present in All Versions):
- DIB Header Size: 4 bytes (offset 14), size of the DIB header (e.g., 40 for version 3, 108 for version 4, 124 for version 5).
- Width: 4 bytes (offset 18), image width in pixels (signed integer).
- Height: 4 bytes (offset 22), image height in pixels (signed integer; positive for bottom-up, negative for top-down).
- Color Planes: 2 bytes (offset 26), number of color planes (must be 1).
- Bits Per Pixel: 2 bytes (offset 28), color depth (e.g., 1, 4, 8, 16, 24, 32).
- Compression Method: 4 bytes (offset 30), compression type (e.g., 0=BI_RGB (none), 1=BI_RLE8, 2=BI_RLE4, 3=BI_BITFIELDS).
- Raw Image Size: 4 bytes (offset 34), size of uncompressed pixel data (0 if uncompressed).
- Horizontal Resolution: 4 bytes (offset 38), pixels per meter (signed).
- Vertical Resolution: 4 bytes (offset 42), pixels per meter (signed).
- Palette Colors: 4 bytes (offset 46), number of colors in the color table (0 defaults to maximum for bit depth).
- Important Colors: 4 bytes (offset 50), number of important colors (0 means all are important).
Version 4 Additional Fields (BITMAPV4HEADER, offsets 54–107):
- Red Mask: 4 bytes (offset 54), bit mask for red channel.
- Green Mask: 4 bytes (offset 58), bit mask for green channel.
- Blue Mask: 4 bytes (offset 62), bit mask for blue channel.
- Alpha Mask: 4 bytes (offset 66), bit mask for alpha channel.
- Color Space Type: 4 bytes (offset 70), color space (e.g., LCS_CALIBRATED_RGB).
- CIEXYZTRIPLE Endpoints: 36 bytes (offset 74), color space coordinates (red, green, blue X/Y/Z).
- Gamma Red: 4 bytes (offset 110), red gamma curve.
- Gamma Green: 4 bytes (offset 114), green gamma curve.
- Gamma Blue: 4 bytes (offset 118), blue gamma curve.
Version 5 Additional Fields (BITMAPV5HEADER, offsets 122–125):
- Intent: 4 bytes (offset 122), rendering intent.
- Profile Data Offset: 4 bytes (offset 126), offset to ICC profile data.
- Profile Size: 4 bytes (offset 130), size of ICC profile data.
- Reserved: 4 bytes (offset 134), reserved (0).
Additional intrinsic elements include an optional color table (after DIB header, 4 bytes per entry for RGBQUAD), pixel array (padded to 4-byte multiples per row), and optional ICC profile. These properties define the file's layout and content without reliance on external file system metadata.
3. HTML/JavaScript for Ghost Blog (Drag-and-Drop BMP Property Dumper)
The following is embeddable HTML with JavaScript suitable for a Ghost blog post. It creates a drop zone where users can drag and drop a .BMP file. Upon drop, it reads the file as an ArrayBuffer, parses the properties using DataView, and displays them in a preformatted text area on the screen. It assumes a standard BMP with BITMAPINFOHEADER (40 bytes) for core properties; extended fields are noted if the header is larger.
4. Python Class for BMP Handling
The following Python class can open a .BMP file, decode and read its properties, print them to the console, and write a copy of the file (preserving original data).
import struct
import os
class BMPHandler:
def __init__(self, filepath):
with open(filepath, 'rb') as f:
self.data = f.read()
self.properties = self._decode_properties()
def _decode_properties(self):
props = {}
# Bitmap File Header
props['Signature'] = struct.unpack('<2s', self.data[0:2])[0].decode('ascii')
props['File Size'] = struct.unpack('<I', self.data[2:6])[0]
props['Reserved 1'] = struct.unpack('<H', self.data[6:8])[0]
props['Reserved 2'] = struct.unpack('<H', self.data[8:10])[0]
props['Pixel Array Offset'] = struct.unpack('<I', self.data[10:14])[0]
# DIB Header
dib_size = struct.unpack('<I', self.data[14:18])[0]
props['DIB Header Size'] = dib_size
props['Width'] = struct.unpack('<i', self.data[18:22])[0]
props['Height'] = struct.unpack('<i', self.data[22:26])[0]
props['Color Planes'] = struct.unpack('<H', self.data[26:28])[0]
props['Bits Per Pixel'] = struct.unpack('<H', self.data[28:30])[0]
props['Compression Method'] = struct.unpack('<I', self.data[30:34])[0]
props['Raw Image Size'] = struct.unpack('<I', self.data[34:38])[0]
props['Horizontal Resolution'] = struct.unpack('<i', self.data[38:42])[0]
props['Vertical Resolution'] = struct.unpack('<i', self.data[42:46])[0]
props['Palette Colors'] = struct.unpack('<I', self.data[46:50])[0]
props['Important Colors'] = struct.unpack('<I', self.data[50:54])[0]
if dib_size >= 108: # Version 4+
props['Red Mask'] = struct.unpack('<I', self.data[54:58])[0]
props['Green Mask'] = struct.unpack('<I', self.data[58:62])[0]
props['Blue Mask'] = struct.unpack('<I', self.data[62:66])[0]
props['Alpha Mask'] = struct.unpack('<I', self.data[66:70])[0]
props['Color Space Type'] = struct.unpack('<I', self.data[70:74])[0]
# Additional fields like CIEXYZTRIPLE and gamma can be added if needed
if dib_size >= 124: # Version 5+
props['Intent'] = struct.unpack('<I', self.data[122:126])[0]
props['Profile Data Offset'] = struct.unpack('<I', self.data[126:130])[0]
props['Profile Size'] = struct.unpack('<I', self.data[130:134])[0]
return props
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, output_path):
with open(output_path, 'wb') as f:
f.write(self.data)
5. Java Class for BMP Handling
The following Java class can open a .BMP file, decode and read its properties, print them to the console, and write a copy of the file.
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class BMPHandler {
private byte[] data;
private Map<String, Object> properties;
public BMPHandler(String filepath) throws IOException {
this.data = Files.readAllBytes(Paths.get(filepath));
this.properties = decodeProperties();
}
private Map<String, Object> decodeProperties() {
Map<String, Object> props = new HashMap<>();
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
// Bitmap File Header
props.put("Signature", new String(new byte[]{bb.get(0), bb.get(1)}));
props.put("File Size", bb.getInt(2));
props.put("Reserved 1", bb.getShort(6));
props.put("Reserved 2", bb.getShort(8));
props.put("Pixel Array Offset", bb.getInt(10));
// DIB Header
int dibSize = bb.getInt(14);
props.put("DIB Header Size", dibSize);
props.put("Width", bb.getInt(18));
props.put("Height", bb.getInt(22));
props.put("Color Planes", bb.getShort(26));
props.put("Bits Per Pixel", bb.getShort(28));
props.put("Compression Method", bb.getInt(30));
props.put("Raw Image Size", bb.getInt(34));
props.put("Horizontal Resolution", bb.getInt(38));
props.put("Vertical Resolution", bb.getInt(42));
props.put("Palette Colors", bb.getInt(46));
props.put("Important Colors", bb.getInt(50));
if (dibSize >= 108) { // Version 4+
props.put("Red Mask", bb.getInt(54));
props.put("Green Mask", bb.getInt(58));
props.put("Blue Mask", bb.getInt(62));
props.put("Alpha Mask", bb.getInt(66));
props.put("Color Space Type", bb.getInt(70));
// Additional fields can be added
}
if (dibSize >= 124) { // Version 5+
props.put("Intent", bb.getInt(122));
props.put("Profile Data Offset", bb.getInt(126));
props.put("Profile Size", bb.getInt(130));
}
return props;
}
public void printProperties() {
for (Map.Entry<String, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
public void write(String outputPath) throws IOException {
Files.write(Paths.get(outputPath), data);
}
}
6. JavaScript Class for BMP Handling
The following JavaScript class can handle a .BMP file provided as an ArrayBuffer (e.g., from FileReader), decode and read its properties, print them to the console, and write a copy via Blob download (since browser JS cannot directly write to disk).
class BMPHandler {
constructor(arrayBuffer) {
this.data = arrayBuffer;
this.dv = new DataView(this.data);
this.properties = this.decodeProperties();
}
decodeProperties() {
const props = {};
// Bitmap File Header
props['Signature'] = String.fromCharCode(this.dv.getUint8(0), this.dv.getUint8(1));
props['File Size'] = this.dv.getUint32(2, true);
props['Reserved 1'] = this.dv.getUint16(6, true);
props['Reserved 2'] = this.dv.getUint16(8, true);
props['Pixel Array Offset'] = this.dv.getUint32(10, true);
// DIB Header
const dibSize = this.dv.getUint32(14, true);
props['DIB Header Size'] = dibSize;
props['Width'] = this.dv.getInt32(18, true);
props['Height'] = this.dv.getInt32(22, true);
props['Color Planes'] = this.dv.getUint16(26, true);
props['Bits Per Pixel'] = this.dv.getUint16(28, true);
props['Compression Method'] = this.dv.getUint32(30, true);
props['Raw Image Size'] = this.dv.getUint32(34, true);
props['Horizontal Resolution'] = this.dv.getInt32(38, true);
props['Vertical Resolution'] = this.dv.getInt32(42, true);
props['Palette Colors'] = this.dv.getUint32(46, true);
props['Important Colors'] = this.dv.getUint32(50, true);
if (dibSize >= 108) { // Version 4+
props['Red Mask'] = this.dv.getUint32(54, true);
props['Green Mask'] = this.dv.getUint32(58, true);
props['Blue Mask'] = this.dv.getUint32(62, true);
props['Alpha Mask'] = this.dv.getUint32(66, true);
props['Color Space Type'] = this.dv.getUint32(70, true);
// Additional fields can be added
}
if (dibSize >= 124) { // Version 5+
props['Intent'] = this.dv.getUint32(122, true);
props['Profile Data Offset'] = this.dv.getUint32(126, true);
props['Profile Size'] = this.dv.getUint32(130, true);
}
return props;
}
printProperties() {
for (const [key, value] of Object.entries(this.properties)) {
console.log(`${key}: ${value}`);
}
}
write(filename) {
const blob = new Blob([this.data], { type: 'image/bmp' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
}
// Example usage: new BMPHandler(arrayBuffer).printProperties();
7. C Class for BMP Handling
Since C does not natively support classes, the following is implemented in C++ (a superset of C) using a class for object-oriented structure. It can open a .BMP file, decode and read its properties, print them to the console, and write a copy of the file.
#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <cstdint>
#include <cstring>
class BMPHandler {
private:
std::vector<uint8_t> data;
std::map<std::string, int64_t> properties; // Using int64_t for generality
void decodeProperties() {
// Helper to read little-endian values
auto readUint16 = [&](size_t offset) { return static_cast<uint16_t>(data[offset] | (data[offset + 1] << 8)); };
auto readInt32 = [&](size_t offset) { return static_cast<int32_t>(data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) | (data[offset + 3] << 24)); };
auto readUint32 = [&](size_t offset) { return static_cast<uint32_t>(readInt32(offset)); };
// Bitmap File Header
char sig[3] = {static_cast<char>(data[0]), static_cast<char>(data[1]), '\0'};
properties["Signature"] = *reinterpret_cast<int64_t*>(sig); // For display, but treat as string later
properties["File Size"] = readUint32(2);
properties["Reserved 1"] = readUint16(6);
properties["Reserved 2"] = readUint16(8);
properties["Pixel Array Offset"] = readUint32(10);
// DIB Header
uint32_t dibSize = readUint32(14);
properties["DIB Header Size"] = dibSize;
properties["Width"] = readInt32(18);
properties["Height"] = readInt32(22);
properties["Color Planes"] = readUint16(26);
properties["Bits Per Pixel"] = readUint16(28);
properties["Compression Method"] = readUint32(30);
properties["Raw Image Size"] = readUint32(34);
properties["Horizontal Resolution"] = readInt32(38);
properties["Vertical Resolution"] = readInt32(42);
properties["Palette Colors"] = readUint32(46);
properties["Important Colors"] = readUint32(50);
if (dibSize >= 108) { // Version 4+
properties["Red Mask"] = readUint32(54);
properties["Green Mask"] = readUint32(58);
properties["Blue Mask"] = readUint32(62);
properties["Alpha Mask"] = readUint32(66);
properties["Color Space Type"] = readUint32(70);
// Additional fields can be added
}
if (dibSize >= 124) { // Version 5+
properties["Intent"] = readUint32(122);
properties["Profile Data Offset"] = readUint32(126);
properties["Profile Size"] = readUint32(130);
}
}
public:
BMPHandler(const std::string& filepath) {
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file) {
throw std::runtime_error("Failed to open file");
}
size_t size = file.tellg();
file.seekg(0);
data.resize(size);
file.read(reinterpret_cast<char*>(data.data()), size);
decodeProperties();
}
void printProperties() {
for (const auto& pair : properties) {
if (pair.first == "Signature") {
char sig[3];
memcpy(sig, &pair.second, 2);
sig[2] = '\0';
std::cout << pair.first << ": " << sig << std::endl;
} else {
std::cout << pair.first << ": " << pair.second << std::endl;
}
}
}
void write(const std::string& outputPath) {
std::ofstream file(outputPath, std::ios::binary);
if (!file) {
throw std::runtime_error("Failed to write file");
}
file.write(reinterpret_cast<const char*>(data.data()), data.size());
}
};