.bmp file format: C++/STL parsing library

This page hosts a formal specification of .bmp file format using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Usage

Using Kaitai Struct in C++/STL usually consists of 3 steps.

  1. We need to create an STL input stream (std::istream). One can open local file for that, or use existing std::string or char* buffer.
    #include <fstream>
    
    std::ifstream is("path/to/local/file.bmp", std::ifstream::binary);
    #include <sstream>
    
    std::istringstream is(str);
    #include <sstream>
    
    const char buf[] = { ... };
    std::string str(buf, sizeof buf);
    std::istringstream is(str);
  2. We need to wrap our input stream into Kaitai stream:
    #include <kaitai/kaitaistream.h>
    
    kaitai::kstream ks(&is);
  3. And finally, we can invoke the parsing:
    bmp_t data(&ks);

After that, one can get various attributes from the structure by invoking getter methods like:

data.file_hdr() // => get file hdr

C++/STL source code to parse .bmp file format

bmp.h

#ifndef BMP_H_
#define BMP_H_

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "kaitai/kaitaistruct.h"

#include <stdint.h>

#if KAITAI_STRUCT_VERSION < 7000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.7 or later is required"
#endif

class bmp_t : public kaitai::kstruct {

public:
    class file_header_t;
    class bitmap_core_header_t;
    class bitmap_info_header_t;

    enum compressions_t {
        COMPRESSIONS_RGB = 0,
        COMPRESSIONS_RLE8 = 1,
        COMPRESSIONS_RLE4 = 2,
        COMPRESSIONS_BITFIELDS = 3,
        COMPRESSIONS_JPEG = 4,
        COMPRESSIONS_PNG = 5,
        COMPRESSIONS_CMYK = 11,
        COMPRESSIONS_CMYK_RLE8 = 12,
        COMPRESSIONS_CMYK_RLE4 = 13
    };

    bmp_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, bmp_t* p__root = 0);

private:
    void _read();

public:
    ~bmp_t();

    /**
     * \sa Source
     */

    class file_header_t : public kaitai::kstruct {

    public:

        file_header_t(kaitai::kstream* p__io, bmp_t* p__parent = 0, bmp_t* p__root = 0);

    private:
        void _read();

    public:
        ~file_header_t();

    private:
        std::string m_magic;
        uint32_t m_len_file;
        uint16_t m_reserved1;
        uint16_t m_reserved2;
        int32_t m_ofs_bitmap;
        bmp_t* m__root;
        bmp_t* m__parent;

    public:
        std::string magic() const { return m_magic; }
        uint32_t len_file() const { return m_len_file; }
        uint16_t reserved1() const { return m_reserved1; }
        uint16_t reserved2() const { return m_reserved2; }

        /**
         * Offset to actual raw pixel data of the image
         */
        int32_t ofs_bitmap() const { return m_ofs_bitmap; }
        bmp_t* _root() const { return m__root; }
        bmp_t* _parent() const { return m__parent; }
    };

    /**
     * \sa Source
     */

    class bitmap_core_header_t : public kaitai::kstruct {

    public:

        bitmap_core_header_t(kaitai::kstream* p__io, bmp_t* p__parent = 0, bmp_t* p__root = 0);

    private:
        void _read();

    public:
        ~bitmap_core_header_t();

    private:
        uint16_t m_image_width;
        uint16_t m_image_height;
        uint16_t m_num_planes;
        uint16_t m_bits_per_pixel;
        bmp_t* m__root;
        bmp_t* m__parent;

    public:

        /**
         * Image width, px
         */
        uint16_t image_width() const { return m_image_width; }

        /**
         * Image height, px
         */
        uint16_t image_height() const { return m_image_height; }

        /**
         * Number of planes for target device, must be 1
         */
        uint16_t num_planes() const { return m_num_planes; }

        /**
         * Number of bits per pixel that image buffer uses (1, 4, 8, or 24)
         */
        uint16_t bits_per_pixel() const { return m_bits_per_pixel; }
        bmp_t* _root() const { return m__root; }
        bmp_t* _parent() const { return m__parent; }
    };

    /**
     * \sa Source
     */

    class bitmap_info_header_t : public kaitai::kstruct {

    public:

        bitmap_info_header_t(kaitai::kstream* p__io, bmp_t* p__parent = 0, bmp_t* p__root = 0);

    private:
        void _read();

    public:
        ~bitmap_info_header_t();

    private:
        uint32_t m_image_width;
        uint32_t m_image_height;
        uint16_t m_num_planes;
        uint16_t m_bits_per_pixel;
        compressions_t m_compression;
        uint32_t m_len_image;
        uint32_t m_x_px_per_m;
        uint32_t m_y_px_per_m;
        uint32_t m_num_colors_used;
        uint32_t m_num_colors_important;
        bmp_t* m__root;
        bmp_t* m__parent;

    public:
        uint32_t image_width() const { return m_image_width; }
        uint32_t image_height() const { return m_image_height; }
        uint16_t num_planes() const { return m_num_planes; }
        uint16_t bits_per_pixel() const { return m_bits_per_pixel; }
        compressions_t compression() const { return m_compression; }
        uint32_t len_image() const { return m_len_image; }
        uint32_t x_px_per_m() const { return m_x_px_per_m; }
        uint32_t y_px_per_m() const { return m_y_px_per_m; }
        uint32_t num_colors_used() const { return m_num_colors_used; }
        uint32_t num_colors_important() const { return m_num_colors_important; }
        bmp_t* _root() const { return m__root; }
        bmp_t* _parent() const { return m__parent; }
    };

private:
    bool f_image;
    std::string m_image;

public:
    std::string image();

private:
    file_header_t* m_file_hdr;
    int32_t m_len_dib_header;
    kaitai::kstruct* m_dib_header;
    bool n_dib_header;

public:
    bool _is_null_dib_header() { dib_header(); return n_dib_header; };

private:
    bmp_t* m__root;
    kaitai::kstruct* m__parent;
    std::string m__raw_dib_header;
    kaitai::kstream* m__io__raw_dib_header;

public:
    file_header_t* file_hdr() const { return m_file_hdr; }
    int32_t len_dib_header() const { return m_len_dib_header; }
    kaitai::kstruct* dib_header() const { return m_dib_header; }
    bmp_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
    std::string _raw_dib_header() const { return m__raw_dib_header; }
    kaitai::kstream* _io__raw_dib_header() const { return m__io__raw_dib_header; }
};

#endif  // BMP_H_

bmp.cpp

// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild

#include "bmp.h"



bmp_t::bmp_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    f_image = false;
    _read();
}

void bmp_t::_read() {
    m_file_hdr = new file_header_t(m__io, this, m__root);
    m_len_dib_header = m__io->read_s4le();
    n_dib_header = true;
    switch (len_dib_header()) {
    case 104: {
        n_dib_header = false;
        m__raw_dib_header = m__io->read_bytes((len_dib_header() - 4));
        m__io__raw_dib_header = new kaitai::kstream(m__raw_dib_header);
        m_dib_header = new bitmap_core_header_t(m__io__raw_dib_header, this, m__root);
        break;
    }
    case 12: {
        n_dib_header = false;
        m__raw_dib_header = m__io->read_bytes((len_dib_header() - 4));
        m__io__raw_dib_header = new kaitai::kstream(m__raw_dib_header);
        m_dib_header = new bitmap_core_header_t(m__io__raw_dib_header, this, m__root);
        break;
    }
    case 40: {
        n_dib_header = false;
        m__raw_dib_header = m__io->read_bytes((len_dib_header() - 4));
        m__io__raw_dib_header = new kaitai::kstream(m__raw_dib_header);
        m_dib_header = new bitmap_info_header_t(m__io__raw_dib_header, this, m__root);
        break;
    }
    case 124: {
        n_dib_header = false;
        m__raw_dib_header = m__io->read_bytes((len_dib_header() - 4));
        m__io__raw_dib_header = new kaitai::kstream(m__raw_dib_header);
        m_dib_header = new bitmap_core_header_t(m__io__raw_dib_header, this, m__root);
        break;
    }
    default: {
        m__raw_dib_header = m__io->read_bytes((len_dib_header() - 4));
        break;
    }
    }
}

bmp_t::~bmp_t() {
    delete m_file_hdr;
    if (!n_dib_header) {
        delete m__io__raw_dib_header;
        delete m_dib_header;
    }
    if (f_image) {
    }
}

bmp_t::file_header_t::file_header_t(kaitai::kstream* p__io, bmp_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void bmp_t::file_header_t::_read() {
    m_magic = m__io->ensure_fixed_contents(std::string("\x42\x4D", 2));
    m_len_file = m__io->read_u4le();
    m_reserved1 = m__io->read_u2le();
    m_reserved2 = m__io->read_u2le();
    m_ofs_bitmap = m__io->read_s4le();
}

bmp_t::file_header_t::~file_header_t() {
}

bmp_t::bitmap_core_header_t::bitmap_core_header_t(kaitai::kstream* p__io, bmp_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void bmp_t::bitmap_core_header_t::_read() {
    m_image_width = m__io->read_u2le();
    m_image_height = m__io->read_u2le();
    m_num_planes = m__io->read_u2le();
    m_bits_per_pixel = m__io->read_u2le();
}

bmp_t::bitmap_core_header_t::~bitmap_core_header_t() {
}

bmp_t::bitmap_info_header_t::bitmap_info_header_t(kaitai::kstream* p__io, bmp_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    _read();
}

void bmp_t::bitmap_info_header_t::_read() {
    m_image_width = m__io->read_u4le();
    m_image_height = m__io->read_u4le();
    m_num_planes = m__io->read_u2le();
    m_bits_per_pixel = m__io->read_u2le();
    m_compression = static_cast<bmp_t::compressions_t>(m__io->read_u4le());
    m_len_image = m__io->read_u4le();
    m_x_px_per_m = m__io->read_u4le();
    m_y_px_per_m = m__io->read_u4le();
    m_num_colors_used = m__io->read_u4le();
    m_num_colors_important = m__io->read_u4le();
}

bmp_t::bitmap_info_header_t::~bitmap_info_header_t() {
}

std::string bmp_t::image() {
    if (f_image)
        return m_image;
    std::streampos _pos = m__io->pos();
    m__io->seek(file_hdr()->ofs_bitmap());
    m_image = m__io->read_bytes_full();
    m__io->seek(_pos);
    f_image = true;
    return m_image;
}