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

The BMP file format, also known as bitmap image file or device independent bitmap (DIB) file format or simply a bitmap, is a raster graphics image file format used to store bitmap digital images, independently of the display device (such as a graphics adapter), especially on Microsoft Windows and OS/2 operating systems.

Samples

Great collection of various BMP sample files: BMP Suite Image List (by Jason Summers)

If only there was such a comprehensive sample suite for every file format! It's like a dream for every developer of any binary file format parser. It contains a lot of different types and variations of BMP files, even the tricky ones, where it's not clear from the specification how to deal with them (marked there as "questionable").

If you make a program which will be able to read all the "good" and "questionable" BMP files and won't crash on the "bad" ones, it will definitely have one of the most extensive support of BMP files in the universe!

BITMAPV2INFOHEADER and BITMAPV3INFOHEADER

A beneficial discussion on Adobe forum (archived): Invalid BMP Format with Alpha channel

In 2010, someone noticed that Photoshop generated BMP with an odd type of header. There wasn't any documentation available for this header at the time (and still isn't). However, Chris Cox (former Adobe employee) claimed that they hadn't invented any type of proprietary header and everything they were writing was taken directly from the Microsoft documentation.

It showed up that the unknown header was called BITMAPV3INFOHEADER. Although Microsoft has apparently requested and verified the use of the header, the documentation on MSDN has probably got lost and they have probably forgotten about this type of header.

This is the only source I could find about these structures, so we could't rely on it so much, but I think supporting them as a read-only format won't harm anything. Due to the fact that it isn't documented anywhere else, most applications don't support it.

All Windows headers at once (including mentioned BITMAPV2INFOHEADER and BITMAPV3INFOHEADER):

Bitmap headers overview

Specs

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

Runtime library

All parsing code for C++11/STL generated by Kaitai Struct depends on the C++/STL runtime library. You have to install it before you can parse data.

For C++, the easiest way is to clone the runtime library sources and build them along with your project.

Code

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++11/STL source code to parse .bmp file format

bmp.h

#pragma once

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

#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <memory>
#include <vector>

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

/**
 * The **BMP file format**, also known as **bitmap image file** or **device independent
 * bitmap (DIB) file format** or simply a **bitmap**, is a raster graphics image file
 * format used to store bitmap digital images, independently of the display
 * device (such as a graphics adapter), especially on Microsoft Windows
 * and OS/2 operating systems.
 * 
 * ## Samples
 * 
 * Great collection of various BMP sample files:
 * [**BMP Suite Image List**](
 *   http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html
 * ) (by Jason Summers)
 * 
 * If only there was such a comprehensive sample suite for every file format! It's like
 * a dream for every developer of any binary file format parser. It contains a lot of
 * different types and variations of BMP files, even the tricky ones, where it's not clear
 * from the specification how to deal with them (marked there as "**q**uestionable").
 * 
 * If you make a program which will be able to read all the "**g**ood" and "**q**uestionable"
 * BMP files and won't crash on the "**b**ad" ones, it will definitely have one of the most
 * extensive support of BMP files in the universe!
 * 
 * ## BITMAPV2INFOHEADER and BITMAPV3INFOHEADER
 * 
 * A beneficial discussion on Adobe forum (archived):
 * [**Invalid BMP Format with Alpha channel**](
 *   https://web.archive.org/web/20150127132443/https://forums.adobe.com/message/3272950
 * )
 * 
 * In 2010, someone noticed that Photoshop generated BMP with an odd type of header. There wasn't
 * any documentation available for this header at the time (and still isn't).
 * However, Chris Cox (former Adobe employee) claimed that they hadn't invented any type
 * of proprietary header and everything they were writing was taken directly
 * from the Microsoft documentation.
 * 
 * It showed up that the unknown header was called BITMAPV3INFOHEADER.
 * Although Microsoft has apparently requested and verified the use of the header,
 * the documentation on MSDN has probably got lost and they have probably
 * forgotten about this type of header.
 * 
 * This is the only source I could find about these structures, so we could't rely
 * on it so much, but I think supporting them as a read-only format won't harm anything.
 * Due to the fact that it isn't documented anywhere else, most applications don't support it.
 * 
 * All Windows headers at once (including mentioned BITMAPV2INFOHEADER and BITMAPV3INFOHEADER):
 * 
 * ![Bitmap headers overview](
 *   https://web.archive.org/web/20190527043845/https://forums.adobe.com/servlet/JiveServlet/showImage/2-3273299-47801/BMP_Headers.png
 * )
 * 
 * ## Specs
 *  * [Bitmap Storage (Windows Dev Center)](
 *      https://learn.microsoft.com/en-us/windows/win32/gdi/bitmap-storage
 *    )
 *     * BITMAPFILEHEADER
 *     * BITMAPINFOHEADER
 *     * BITMAPV4HEADER
 *     * BITMAPV5HEADER
 *  * [OS/2 Bitmap File Format](
 *       https://www.fileformat.info/format/os2bmp/egff.htm
 *    )
 *     * BITMAPFILEHEADER (OS2BMPFILEHEADER)
 *     * BITMAPCOREHEADER (OS21XBITMAPHEADER)
 *     * OS22XBITMAPHEADER
 *  * [Microsoft Windows Bitmap](
 *       http://netghost.narod.ru/gff/graphics/summary/micbmp.htm
 *    )
 *     * BITMAPFILEHEADER (WINBMPFILEHEADER)
 *     * BITMAPCOREHEADER (WIN2XBITMAPHEADER)
 *     * BITMAPINFOHEADER (WINNTBITMAPHEADER)
 *     * BITMAPV4HEADER (WIN4XBITMAPHEADER)
 */

class bmp_t : public kaitai::kstruct {

public:
    class cie_xyz_t;
    class rgb_record_t;
    class bitmap_v5_extension_t;
    class color_mask_t;
    class bitmap_v4_extension_t;
    class bitmap_info_extension_t;
    class fixed_point_2_dot_30_t;
    class bitmap_t;
    class bitmap_header_t;
    class os2_2x_bitmap_extension_t;
    class fixed_point_16_dot_16_t;
    class color_table_t;
    class file_header_t;
    class bitmap_info_t;

    enum intent_t {
        INTENT_BUSINESS = 1,
        INTENT_GRAPHICS = 2,
        INTENT_IMAGES = 4,
        INTENT_ABS_COLORIMETRIC = 8
    };

    enum color_space_t {
        COLOR_SPACE_CALIBRATED_RGB = 0,
        COLOR_SPACE_PROFILE_LINKED = 1279872587,
        COLOR_SPACE_PROFILE_EMBEDDED = 1296188740,
        COLOR_SPACE_WINDOWS = 1466527264,
        COLOR_SPACE_S_RGB = 1934772034
    };

    enum os2_rendering_t {
        OS2_RENDERING_NO_HALFTONING = 0,
        OS2_RENDERING_ERROR_DIFFUSION = 1,
        OS2_RENDERING_PANDA = 2,
        OS2_RENDERING_SUPER_CIRCLE = 3
    };

    enum header_type_t {
        HEADER_TYPE_BITMAP_CORE_HEADER = 12,
        HEADER_TYPE_BITMAP_INFO_HEADER = 40,
        HEADER_TYPE_BITMAP_V2_INFO_HEADER = 52,
        HEADER_TYPE_BITMAP_V3_INFO_HEADER = 56,
        HEADER_TYPE_OS2_2X_BITMAP_HEADER = 64,
        HEADER_TYPE_BITMAP_V4_HEADER = 108,
        HEADER_TYPE_BITMAP_V5_HEADER = 124
    };

    enum compressions_t {
        COMPRESSIONS_RGB = 0,
        COMPRESSIONS_RLE8 = 1,
        COMPRESSIONS_RLE4 = 2,
        COMPRESSIONS_BITFIELDS = 3,
        COMPRESSIONS_JPEG = 4,
        COMPRESSIONS_PNG = 5,
        COMPRESSIONS_ALPHA_BITFIELDS = 6
    };

    enum os2_compressions_t {
        OS2_COMPRESSIONS_RGB = 0,
        OS2_COMPRESSIONS_RLE8 = 1,
        OS2_COMPRESSIONS_RLE4 = 2,
        OS2_COMPRESSIONS_HUFFMAN_1D = 3,
        OS2_COMPRESSIONS_RLE24 = 4
    };

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

private:
    void _read();
    void _clean_up();

public:
    ~bmp_t();

    /**
     * \sa https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-ciexyz Source
     */

    class cie_xyz_t : public kaitai::kstruct {

    public:

        cie_xyz_t(kaitai::kstream* p__io, bmp_t::bitmap_v4_extension_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~cie_xyz_t();

    private:
        std::unique_ptr<fixed_point_2_dot_30_t> m_x;
        std::unique_ptr<fixed_point_2_dot_30_t> m_y;
        std::unique_ptr<fixed_point_2_dot_30_t> m_z;
        bmp_t* m__root;
        bmp_t::bitmap_v4_extension_t* m__parent;

    public:
        fixed_point_2_dot_30_t* x() const { return m_x.get(); }
        fixed_point_2_dot_30_t* y() const { return m_y.get(); }
        fixed_point_2_dot_30_t* z() const { return m_z.get(); }
        bmp_t* _root() const { return m__root; }
        bmp_t::bitmap_v4_extension_t* _parent() const { return m__parent; }
    };

    class rgb_record_t : public kaitai::kstruct {

    public:

        rgb_record_t(bool p_has_reserved_field, kaitai::kstream* p__io, bmp_t::color_table_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~rgb_record_t();

    private:
        uint8_t m_blue;
        uint8_t m_green;
        uint8_t m_red;
        uint8_t m_reserved;
        bool n_reserved;

    public:
        bool _is_null_reserved() { reserved(); return n_reserved; };

    private:
        bool m_has_reserved_field;
        bmp_t* m__root;
        bmp_t::color_table_t* m__parent;

    public:
        uint8_t blue() const { return m_blue; }
        uint8_t green() const { return m_green; }
        uint8_t red() const { return m_red; }
        uint8_t reserved() const { return m_reserved; }
        bool has_reserved_field() const { return m_has_reserved_field; }
        bmp_t* _root() const { return m__root; }
        bmp_t::color_table_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header Source
     */

    class bitmap_v5_extension_t : public kaitai::kstruct {

    public:

        bitmap_v5_extension_t(kaitai::kstream* p__io, bmp_t::bitmap_header_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bitmap_v5_extension_t();

    private:
        bool f_has_profile;
        bool m_has_profile;

    public:
        bool has_profile();

    private:
        bool f_profile_data;
        std::string m_profile_data;
        bool n_profile_data;

    public:
        bool _is_null_profile_data() { profile_data(); return n_profile_data; };

    private:

    public:

        /**
         * \sa https://learn.microsoft.com/en-us/windows/win32/wcs/using-structures-in-wcs-1-0 "If the profile is embedded, profile data is the actual profile, and if it is linked, the profile data is the null-terminated file name of the profile. This cannot be a Unicode string. It must be composed exclusively of characters from the Windows character set (code page 1252)."
         */
        std::string profile_data();

    private:
        intent_t m_intent;
        uint32_t m_ofs_profile;
        uint32_t m_len_profile;
        uint32_t m_reserved;
        bmp_t* m__root;
        bmp_t::bitmap_header_t* m__parent;

    public:
        intent_t intent() const { return m_intent; }

        /**
         * The offset, in bytes, from the beginning of the BITMAPV5HEADER structure to the start of the profile data.
         */
        uint32_t ofs_profile() const { return m_ofs_profile; }
        uint32_t len_profile() const { return m_len_profile; }
        uint32_t reserved() const { return m_reserved; }
        bmp_t* _root() const { return m__root; }
        bmp_t::bitmap_header_t* _parent() const { return m__parent; }
    };

    class color_mask_t : public kaitai::kstruct {

    public:

        color_mask_t(bool p_has_alpha_mask, kaitai::kstream* p__io, kaitai::kstruct* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~color_mask_t();

    private:
        uint32_t m_red_mask;
        uint32_t m_green_mask;
        uint32_t m_blue_mask;
        uint32_t m_alpha_mask;
        bool n_alpha_mask;

    public:
        bool _is_null_alpha_mask() { alpha_mask(); return n_alpha_mask; };

    private:
        bool m_has_alpha_mask;
        bmp_t* m__root;
        kaitai::kstruct* m__parent;

    public:
        uint32_t red_mask() const { return m_red_mask; }
        uint32_t green_mask() const { return m_green_mask; }
        uint32_t blue_mask() const { return m_blue_mask; }
        uint32_t alpha_mask() const { return m_alpha_mask; }
        bool has_alpha_mask() const { return m_has_alpha_mask; }
        bmp_t* _root() const { return m__root; }
        kaitai::kstruct* _parent() const { return m__parent; }
    };

    /**
     * \sa https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv4header Source
     */

    class bitmap_v4_extension_t : public kaitai::kstruct {

    public:

        bitmap_v4_extension_t(kaitai::kstream* p__io, bmp_t::bitmap_header_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bitmap_v4_extension_t();

    private:
        color_space_t m_color_space_type;
        std::unique_ptr<cie_xyz_t> m_endpoint_red;
        std::unique_ptr<cie_xyz_t> m_endpoint_green;
        std::unique_ptr<cie_xyz_t> m_endpoint_blue;
        std::unique_ptr<fixed_point_16_dot_16_t> m_gamma_red;
        std::unique_ptr<fixed_point_16_dot_16_t> m_gamma_blue;
        std::unique_ptr<fixed_point_16_dot_16_t> m_gamma_green;
        bmp_t* m__root;
        bmp_t::bitmap_header_t* m__parent;

    public:
        color_space_t color_space_type() const { return m_color_space_type; }
        cie_xyz_t* endpoint_red() const { return m_endpoint_red.get(); }
        cie_xyz_t* endpoint_green() const { return m_endpoint_green.get(); }
        cie_xyz_t* endpoint_blue() const { return m_endpoint_blue.get(); }
        fixed_point_16_dot_16_t* gamma_red() const { return m_gamma_red.get(); }
        fixed_point_16_dot_16_t* gamma_blue() const { return m_gamma_blue.get(); }
        fixed_point_16_dot_16_t* gamma_green() const { return m_gamma_green.get(); }
        bmp_t* _root() const { return m__root; }
        bmp_t::bitmap_header_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://learn.microsoft.com/en-us/previous-versions/dd183376(v=vs.85) Source
     */

    class bitmap_info_extension_t : public kaitai::kstruct {

    public:

        bitmap_info_extension_t(kaitai::kstream* p__io, bmp_t::bitmap_header_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bitmap_info_extension_t();

    private:
        compressions_t m_compression;
        bool n_compression;

    public:
        bool _is_null_compression() { compression(); return n_compression; };

    private:
        os2_compressions_t m_os2_compression;
        bool n_os2_compression;

    public:
        bool _is_null_os2_compression() { os2_compression(); return n_os2_compression; };

    private:
        uint32_t m_len_image;
        uint32_t m_x_resolution;
        uint32_t m_y_resolution;
        uint32_t m_num_colors_used;
        uint32_t m_num_colors_important;
        bmp_t* m__root;
        bmp_t::bitmap_header_t* m__parent;

    public:
        compressions_t compression() const { return m_compression; }
        os2_compressions_t os2_compression() const { return m_os2_compression; }

        /**
         * If biCompression is BI_JPEG or BI_PNG, indicates the size of the JPEG or PNG image buffer.
         * This may be set to zero for BI_RGB bitmaps.
         */
        uint32_t len_image() const { return m_len_image; }
        uint32_t x_resolution() const { return m_x_resolution; }
        uint32_t y_resolution() const { return m_y_resolution; }
        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::bitmap_header_t* _parent() const { return m__parent; }
    };

    class fixed_point_2_dot_30_t : public kaitai::kstruct {

    public:

        fixed_point_2_dot_30_t(kaitai::kstream* p__io, bmp_t::cie_xyz_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~fixed_point_2_dot_30_t();

    private:
        bool f_value;
        double m_value;

    public:
        double value();

    private:
        uint32_t m_raw;
        bmp_t* m__root;
        bmp_t::cie_xyz_t* m__parent;

    public:
        uint32_t raw() const { return m_raw; }
        bmp_t* _root() const { return m__root; }
        bmp_t::cie_xyz_t* _parent() const { return m__parent; }
    };

    /**
     * Replace with an opaque type if you care about the pixels. You can look at
     * an example of a JavaScript implementation:
     * <https://github.com/generalmimon/bmptool/blob/master/src/Bitmap.js>
     * 
     * There is a proposal for adding bitmap data type to Kaitai Struct:
     * <https://github.com/kaitai-io/kaitai_struct/issues/188>
     */

    class bitmap_t : public kaitai::kstruct {

    public:

        bitmap_t(kaitai::kstream* p__io, bmp_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bitmap_t();

    private:
        bmp_t* m__root;
        bmp_t* m__parent;

    public:
        bmp_t* _root() const { return m__root; }
        bmp_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapcoreheader Source
     * \sa https://www.fileformat.info/format/os2bmp/egff.htm#OS2BMP-DMYID.3.1 Source
     */

    class bitmap_header_t : public kaitai::kstruct {

    public:

        bitmap_header_t(uint32_t p_len_header, kaitai::kstream* p__io, bmp_t::bitmap_info_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bitmap_header_t();

    private:
        bool f_extends_bitmap_v4;
        bool m_extends_bitmap_v4;

    public:
        bool extends_bitmap_v4();

    private:
        bool f_extends_os2_2x_bitmap;
        bool m_extends_os2_2x_bitmap;

    public:
        bool extends_os2_2x_bitmap();

    private:
        bool f_uses_fixed_palette;
        bool m_uses_fixed_palette;

    public:
        bool uses_fixed_palette();

    private:
        bool f_extends_bitmap_info;
        bool m_extends_bitmap_info;

    public:
        bool extends_bitmap_info();

    private:
        bool f_image_height;
        int32_t m_image_height;

    public:
        int32_t image_height();

    private:
        bool f_is_core_header;
        bool m_is_core_header;

    public:
        bool is_core_header();

    private:
        bool f_extends_bitmap_v5;
        bool m_extends_bitmap_v5;

    public:
        bool extends_bitmap_v5();

    private:
        bool f_is_color_mask_here;
        bool m_is_color_mask_here;

    public:
        bool is_color_mask_here();

    private:
        bool f_bottom_up;
        bool m_bottom_up;

    public:
        bool bottom_up();

    private:
        uint32_t m_image_width;
        bool n_image_width;

    public:
        bool _is_null_image_width() { image_width(); return n_image_width; };

    private:
        int32_t m_image_height_raw;
        bool n_image_height_raw;

    public:
        bool _is_null_image_height_raw() { image_height_raw(); return n_image_height_raw; };

    private:
        uint16_t m_num_planes;
        uint16_t m_bits_per_pixel;
        std::unique_ptr<bitmap_info_extension_t> m_bitmap_info_ext;
        bool n_bitmap_info_ext;

    public:
        bool _is_null_bitmap_info_ext() { bitmap_info_ext(); return n_bitmap_info_ext; };

    private:
        std::unique_ptr<color_mask_t> m_color_mask;
        bool n_color_mask;

    public:
        bool _is_null_color_mask() { color_mask(); return n_color_mask; };

    private:
        std::unique_ptr<os2_2x_bitmap_extension_t> m_os2_2x_bitmap_ext;
        bool n_os2_2x_bitmap_ext;

    public:
        bool _is_null_os2_2x_bitmap_ext() { os2_2x_bitmap_ext(); return n_os2_2x_bitmap_ext; };

    private:
        std::unique_ptr<bitmap_v4_extension_t> m_bitmap_v4_ext;
        bool n_bitmap_v4_ext;

    public:
        bool _is_null_bitmap_v4_ext() { bitmap_v4_ext(); return n_bitmap_v4_ext; };

    private:
        std::unique_ptr<bitmap_v5_extension_t> m_bitmap_v5_ext;
        bool n_bitmap_v5_ext;

    public:
        bool _is_null_bitmap_v5_ext() { bitmap_v5_ext(); return n_bitmap_v5_ext; };

    private:
        uint32_t m_len_header;
        bmp_t* m__root;
        bmp_t::bitmap_info_t* m__parent;

    public:

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

        /**
         * Image height, px (positive => bottom-up image, negative => top-down image)
         */
        int32_t image_height_raw() const { return m_image_height_raw; }

        /**
         * 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, 16, 24 or 32)
         */
        uint16_t bits_per_pixel() const { return m_bits_per_pixel; }
        bitmap_info_extension_t* bitmap_info_ext() const { return m_bitmap_info_ext.get(); }
        color_mask_t* color_mask() const { return m_color_mask.get(); }
        os2_2x_bitmap_extension_t* os2_2x_bitmap_ext() const { return m_os2_2x_bitmap_ext.get(); }
        bitmap_v4_extension_t* bitmap_v4_ext() const { return m_bitmap_v4_ext.get(); }
        bitmap_v5_extension_t* bitmap_v5_ext() const { return m_bitmap_v5_ext.get(); }
        uint32_t len_header() const { return m_len_header; }
        bmp_t* _root() const { return m__root; }
        bmp_t::bitmap_info_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://www.fileformat.info/format/os2bmp/egff.htm#OS2BMP-DMYID.3.2 Source
     */

    class os2_2x_bitmap_extension_t : public kaitai::kstruct {

    public:

        os2_2x_bitmap_extension_t(kaitai::kstream* p__io, bmp_t::bitmap_header_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~os2_2x_bitmap_extension_t();

    private:
        uint16_t m_units;
        uint16_t m_reserved;
        uint16_t m_recording;
        os2_rendering_t m_rendering;
        uint32_t m_size1;
        uint32_t m_size2;
        uint32_t m_color_encoding;
        uint32_t m_identifier;
        bmp_t* m__root;
        bmp_t::bitmap_header_t* m__parent;

    public:
        uint16_t units() const { return m_units; }
        uint16_t reserved() const { return m_reserved; }

        /**
         * Specifies how the bitmap scan lines are stored.
         * The only valid value for this field is 0, indicating that the bitmap is
         * stored from left to right and from the bottom up.
         */
        uint16_t recording() const { return m_recording; }

        /**
         * Specifies the halftoning algorithm used on the bitmap data.
         */
        os2_rendering_t rendering() const { return m_rendering; }

        /**
         * rendering == os2_rendering::error_diffusion
         *   => error damping as a percentage in the range 0 through 100
         * rendering == os2_rendering::panda or rendering == os2_rendering::super_circle
         *   => X dimension of the pattern used in pixels
         */
        uint32_t size1() const { return m_size1; }

        /**
         * rendering == os2_rendering::error_diffusion
         *   => not used
         * rendering == os2_rendering::panda or rendering == os2_rendering::super_circle
         *   => Y dimension of the pattern used in pixels
         */
        uint32_t size2() const { return m_size2; }

        /**
         * Specifies the color model used to describe the bitmap data.
         * The only valid value is 0, indicating the RGB encoding scheme.
         */
        uint32_t color_encoding() const { return m_color_encoding; }

        /**
         * Application-specific value
         */
        uint32_t identifier() const { return m_identifier; }
        bmp_t* _root() const { return m__root; }
        bmp_t::bitmap_header_t* _parent() const { return m__parent; }
    };

    class fixed_point_16_dot_16_t : public kaitai::kstruct {

    public:

        fixed_point_16_dot_16_t(kaitai::kstream* p__io, bmp_t::bitmap_v4_extension_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~fixed_point_16_dot_16_t();

    private:
        bool f_value;
        double m_value;

    public:
        double value();

    private:
        uint32_t m_raw;
        bmp_t* m__root;
        bmp_t::bitmap_v4_extension_t* m__parent;

    public:
        uint32_t raw() const { return m_raw; }
        bmp_t* _root() const { return m__root; }
        bmp_t::bitmap_v4_extension_t* _parent() const { return m__parent; }
    };

    class color_table_t : public kaitai::kstruct {

    public:

        color_table_t(bool p_has_reserved_field, uint32_t p_num_colors, kaitai::kstream* p__io, bmp_t::bitmap_info_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~color_table_t();

    private:
        bool f_num_colors_present;
        int32_t m_num_colors_present;

    public:
        int32_t num_colors_present();

    private:
        std::unique_ptr<std::vector<std::unique_ptr<rgb_record_t>>> m_colors;
        bool m_has_reserved_field;
        uint32_t m_num_colors;
        bmp_t* m__root;
        bmp_t::bitmap_info_t* m__parent;

    public:
        std::vector<std::unique_ptr<rgb_record_t>>* colors() const { return m_colors.get(); }
        bool has_reserved_field() const { return m_has_reserved_field; }

        /**
         * If equal to 0, the pallete should contain as many colors as can fit into the pixel value
         * according to the `bits_per_pixel` field (if `bits_per_pixel` = 8, then the pixel can
         * represent 2 ** 8 = 256 values, so exactly 256 colors should be present). For more flexibility,
         * it reads as many colors as it can until EOS is reached (and the image data begin).
         */
        uint32_t num_colors() const { return m_num_colors; }
        bmp_t* _root() const { return m__root; }
        bmp_t::bitmap_info_t* _parent() const { return m__parent; }
    };

    /**
     * \sa https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapfileheader Source
     */

    class file_header_t : public kaitai::kstruct {

    public:

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

    private:
        void _read();
        void _clean_up();

    public:
        ~file_header_t();

    private:
        std::string m_file_type;
        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 file_type() const { return m_file_type; }

        /**
         * not reliable, mostly ignored by BMP decoders
         */
        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 https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfo Source
     */

    class bitmap_info_t : public kaitai::kstruct {

    public:

        bitmap_info_t(kaitai::kstream* p__io, bmp_t* p__parent = nullptr, bmp_t* p__root = nullptr);

    private:
        void _read();
        void _clean_up();

    public:
        ~bitmap_info_t();

    private:
        bool f_is_color_mask_given;
        bool m_is_color_mask_given;

    public:
        bool is_color_mask_given();

    private:
        bool f_color_mask_given;
        color_mask_t* m_color_mask_given;
        bool n_color_mask_given;

    public:
        bool _is_null_color_mask_given() { color_mask_given(); return n_color_mask_given; };

    private:

    public:
        color_mask_t* color_mask_given();

    private:
        bool f_color_mask_blue;
        uint32_t m_color_mask_blue;

    public:
        uint32_t color_mask_blue();

    private:
        bool f_color_mask_alpha;
        uint32_t m_color_mask_alpha;

    public:
        uint32_t color_mask_alpha();

    private:
        bool f_color_mask_green;
        int32_t m_color_mask_green;

    public:
        int32_t color_mask_green();

    private:
        bool f_is_color_mask_here;
        bool m_is_color_mask_here;

    public:
        bool is_color_mask_here();

    private:
        bool f_color_mask_red;
        int32_t m_color_mask_red;

    public:
        int32_t color_mask_red();

    private:
        uint32_t m_len_header;
        std::unique_ptr<bitmap_header_t> m_header;
        std::unique_ptr<color_mask_t> m_color_mask;
        bool n_color_mask;

    public:
        bool _is_null_color_mask() { color_mask(); return n_color_mask; };

    private:
        std::unique_ptr<color_table_t> m_color_table;
        bool n_color_table;

    public:
        bool _is_null_color_table() { color_table(); return n_color_table; };

    private:
        bmp_t* m__root;
        bmp_t* m__parent;
        std::string m__raw_header;
        std::unique_ptr<kaitai::kstream> m__io__raw_header;
        std::string m__raw_color_table;
        bool n__raw_color_table;

    public:
        bool _is_null__raw_color_table() { _raw_color_table(); return n__raw_color_table; };

    private:
        std::unique_ptr<kaitai::kstream> m__io__raw_color_table;

    public:
        uint32_t len_header() const { return m_len_header; }
        bitmap_header_t* header() const { return m_header.get(); }

        /**
         * Valid only for BITMAPINFOHEADER, in all headers extending it the masks are contained in the header itself.
         */
        color_mask_t* color_mask() const { return m_color_mask.get(); }
        color_table_t* color_table() const { return m_color_table.get(); }
        bmp_t* _root() const { return m__root; }
        bmp_t* _parent() const { return m__parent; }
        std::string _raw_header() const { return m__raw_header; }
        kaitai::kstream* _io__raw_header() const { return m__io__raw_header.get(); }
        std::string _raw_color_table() const { return m__raw_color_table; }
        kaitai::kstream* _io__raw_color_table() const { return m__io__raw_color_table.get(); }
    };

private:
    std::unique_ptr<file_header_t> m_file_hdr;
    std::unique_ptr<bitmap_info_t> m_dib_info;
    std::unique_ptr<bitmap_t> m_bitmap;
    bmp_t* m__root;
    kaitai::kstruct* m__parent;
    std::string m__raw_dib_info;
    std::unique_ptr<kaitai::kstream> m__io__raw_dib_info;
    std::string m__raw_bitmap;
    std::unique_ptr<kaitai::kstream> m__io__raw_bitmap;

public:
    file_header_t* file_hdr() const { return m_file_hdr.get(); }
    bitmap_info_t* dib_info() const { return m_dib_info.get(); }
    bitmap_t* bitmap() const { return m_bitmap.get(); }
    bmp_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
    std::string _raw_dib_info() const { return m__raw_dib_info; }
    kaitai::kstream* _io__raw_dib_info() const { return m__io__raw_dib_info.get(); }
    std::string _raw_bitmap() const { return m__raw_bitmap; }
    kaitai::kstream* _io__raw_bitmap() const { return m__io__raw_bitmap.get(); }
};

bmp.cpp

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

#include "bmp.h"
#include "kaitai/exceptions.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;
    m_file_hdr = nullptr;
    m_dib_info = nullptr;
    m__io__raw_dib_info = nullptr;
    m_bitmap = nullptr;
    m__io__raw_bitmap = nullptr;
    _read();
}

void bmp_t::_read() {
    m_file_hdr = std::unique_ptr<file_header_t>(new file_header_t(m__io, this, m__root));
    m__raw_dib_info = m__io->read_bytes((file_hdr()->ofs_bitmap() - 14));
    m__io__raw_dib_info = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_dib_info));
    m_dib_info = std::unique_ptr<bitmap_info_t>(new bitmap_info_t(m__io__raw_dib_info.get(), this, m__root));
    m__raw_bitmap = m__io->read_bytes_full();
    m__io__raw_bitmap = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_bitmap));
    m_bitmap = std::unique_ptr<bitmap_t>(new bitmap_t(m__io__raw_bitmap.get(), this, m__root));
}

bmp_t::~bmp_t() {
    _clean_up();
}

void bmp_t::_clean_up() {
}

bmp_t::cie_xyz_t::cie_xyz_t(kaitai::kstream* p__io, bmp_t::bitmap_v4_extension_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_x = nullptr;
    m_y = nullptr;
    m_z = nullptr;
    _read();
}

void bmp_t::cie_xyz_t::_read() {
    m_x = std::unique_ptr<fixed_point_2_dot_30_t>(new fixed_point_2_dot_30_t(m__io, this, m__root));
    m_y = std::unique_ptr<fixed_point_2_dot_30_t>(new fixed_point_2_dot_30_t(m__io, this, m__root));
    m_z = std::unique_ptr<fixed_point_2_dot_30_t>(new fixed_point_2_dot_30_t(m__io, this, m__root));
}

bmp_t::cie_xyz_t::~cie_xyz_t() {
    _clean_up();
}

void bmp_t::cie_xyz_t::_clean_up() {
}

bmp_t::rgb_record_t::rgb_record_t(bool p_has_reserved_field, kaitai::kstream* p__io, bmp_t::color_table_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_has_reserved_field = p_has_reserved_field;
    _read();
}

void bmp_t::rgb_record_t::_read() {
    m_blue = m__io->read_u1();
    m_green = m__io->read_u1();
    m_red = m__io->read_u1();
    n_reserved = true;
    if (has_reserved_field()) {
        n_reserved = false;
        m_reserved = m__io->read_u1();
    }
}

bmp_t::rgb_record_t::~rgb_record_t() {
    _clean_up();
}

void bmp_t::rgb_record_t::_clean_up() {
    if (!n_reserved) {
    }
}

bmp_t::bitmap_v5_extension_t::bitmap_v5_extension_t(kaitai::kstream* p__io, bmp_t::bitmap_header_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_has_profile = false;
    f_profile_data = false;
    _read();
}

void bmp_t::bitmap_v5_extension_t::_read() {
    m_intent = static_cast<bmp_t::intent_t>(m__io->read_u4le());
    m_ofs_profile = m__io->read_u4le();
    m_len_profile = m__io->read_u4le();
    m_reserved = m__io->read_u4le();
}

bmp_t::bitmap_v5_extension_t::~bitmap_v5_extension_t() {
    _clean_up();
}

void bmp_t::bitmap_v5_extension_t::_clean_up() {
    if (f_profile_data && !n_profile_data) {
    }
}

bool bmp_t::bitmap_v5_extension_t::has_profile() {
    if (f_has_profile)
        return m_has_profile;
    m_has_profile =  ((_parent()->bitmap_v4_ext()->color_space_type() == bmp_t::COLOR_SPACE_PROFILE_LINKED) || (_parent()->bitmap_v4_ext()->color_space_type() == bmp_t::COLOR_SPACE_PROFILE_EMBEDDED)) ;
    f_has_profile = true;
    return m_has_profile;
}

std::string bmp_t::bitmap_v5_extension_t::profile_data() {
    if (f_profile_data)
        return m_profile_data;
    n_profile_data = true;
    if (has_profile()) {
        n_profile_data = false;
        kaitai::kstream *io = _root()->_io();
        std::streampos _pos = io->pos();
        io->seek((14 + ofs_profile()));
        n_profile_data = true;
        {
            bool on = _parent()->bitmap_v4_ext()->color_space_type() == bmp_t::COLOR_SPACE_PROFILE_LINKED;
            if (on == true) {
                n_profile_data = false;
                m_profile_data = kaitai::kstream::bytes_to_str(kaitai::kstream::bytes_terminate(io->read_bytes(len_profile()), 0, false), std::string("windows-1252"));
            }
            else {
                m__raw_profile_data = io->read_bytes(len_profile());
            }
        }
        io->seek(_pos);
        f_profile_data = true;
    }
    return m_profile_data;
}

bmp_t::color_mask_t::color_mask_t(bool p_has_alpha_mask, kaitai::kstream* p__io, kaitai::kstruct* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_has_alpha_mask = p_has_alpha_mask;
    _read();
}

void bmp_t::color_mask_t::_read() {
    m_red_mask = m__io->read_u4le();
    m_green_mask = m__io->read_u4le();
    m_blue_mask = m__io->read_u4le();
    n_alpha_mask = true;
    if (has_alpha_mask()) {
        n_alpha_mask = false;
        m_alpha_mask = m__io->read_u4le();
    }
}

bmp_t::color_mask_t::~color_mask_t() {
    _clean_up();
}

void bmp_t::color_mask_t::_clean_up() {
    if (!n_alpha_mask) {
    }
}

bmp_t::bitmap_v4_extension_t::bitmap_v4_extension_t(kaitai::kstream* p__io, bmp_t::bitmap_header_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_endpoint_red = nullptr;
    m_endpoint_green = nullptr;
    m_endpoint_blue = nullptr;
    m_gamma_red = nullptr;
    m_gamma_blue = nullptr;
    m_gamma_green = nullptr;
    _read();
}

void bmp_t::bitmap_v4_extension_t::_read() {
    m_color_space_type = static_cast<bmp_t::color_space_t>(m__io->read_u4le());
    m_endpoint_red = std::unique_ptr<cie_xyz_t>(new cie_xyz_t(m__io, this, m__root));
    m_endpoint_green = std::unique_ptr<cie_xyz_t>(new cie_xyz_t(m__io, this, m__root));
    m_endpoint_blue = std::unique_ptr<cie_xyz_t>(new cie_xyz_t(m__io, this, m__root));
    m_gamma_red = std::unique_ptr<fixed_point_16_dot_16_t>(new fixed_point_16_dot_16_t(m__io, this, m__root));
    m_gamma_blue = std::unique_ptr<fixed_point_16_dot_16_t>(new fixed_point_16_dot_16_t(m__io, this, m__root));
    m_gamma_green = std::unique_ptr<fixed_point_16_dot_16_t>(new fixed_point_16_dot_16_t(m__io, this, m__root));
}

bmp_t::bitmap_v4_extension_t::~bitmap_v4_extension_t() {
    _clean_up();
}

void bmp_t::bitmap_v4_extension_t::_clean_up() {
}

bmp_t::bitmap_info_extension_t::bitmap_info_extension_t(kaitai::kstream* p__io, bmp_t::bitmap_header_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_extension_t::_read() {
    n_compression = true;
    if (!(_parent()->extends_os2_2x_bitmap())) {
        n_compression = false;
        m_compression = static_cast<bmp_t::compressions_t>(m__io->read_u4le());
    }
    n_os2_compression = true;
    if (_parent()->extends_os2_2x_bitmap()) {
        n_os2_compression = false;
        m_os2_compression = static_cast<bmp_t::os2_compressions_t>(m__io->read_u4le());
    }
    m_len_image = m__io->read_u4le();
    m_x_resolution = m__io->read_u4le();
    m_y_resolution = m__io->read_u4le();
    m_num_colors_used = m__io->read_u4le();
    m_num_colors_important = m__io->read_u4le();
}

bmp_t::bitmap_info_extension_t::~bitmap_info_extension_t() {
    _clean_up();
}

void bmp_t::bitmap_info_extension_t::_clean_up() {
    if (!n_compression) {
    }
    if (!n_os2_compression) {
    }
}

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

void bmp_t::fixed_point_2_dot_30_t::_read() {
    m_raw = m__io->read_u4le();
}

bmp_t::fixed_point_2_dot_30_t::~fixed_point_2_dot_30_t() {
    _clean_up();
}

void bmp_t::fixed_point_2_dot_30_t::_clean_up() {
}

double bmp_t::fixed_point_2_dot_30_t::value() {
    if (f_value)
        return m_value;
    m_value = ((raw() + 0.0) / (1 << 30));
    f_value = true;
    return m_value;
}

bmp_t::bitmap_t::bitmap_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_t::_read() {
}

bmp_t::bitmap_t::~bitmap_t() {
    _clean_up();
}

void bmp_t::bitmap_t::_clean_up() {
}

bmp_t::bitmap_header_t::bitmap_header_t(uint32_t p_len_header, kaitai::kstream* p__io, bmp_t::bitmap_info_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_len_header = p_len_header;
    m_bitmap_info_ext = nullptr;
    m_color_mask = nullptr;
    m_os2_2x_bitmap_ext = nullptr;
    m_bitmap_v4_ext = nullptr;
    m_bitmap_v5_ext = nullptr;
    f_extends_bitmap_v4 = false;
    f_extends_os2_2x_bitmap = false;
    f_uses_fixed_palette = false;
    f_extends_bitmap_info = false;
    f_image_height = false;
    f_is_core_header = false;
    f_extends_bitmap_v5 = false;
    f_is_color_mask_here = false;
    f_bottom_up = false;
    _read();
}

void bmp_t::bitmap_header_t::_read() {
    n_image_width = true;
    {
        bool on = is_core_header();
        if (on == true) {
            n_image_width = false;
            m_image_width = m__io->read_u2le();
        }
        else if (on == false) {
            n_image_width = false;
            m_image_width = m__io->read_u4le();
        }
    }
    n_image_height_raw = true;
    {
        bool on = is_core_header();
        if (on == true) {
            n_image_height_raw = false;
            m_image_height_raw = m__io->read_s2le();
        }
        else if (on == false) {
            n_image_height_raw = false;
            m_image_height_raw = m__io->read_s4le();
        }
    }
    m_num_planes = m__io->read_u2le();
    m_bits_per_pixel = m__io->read_u2le();
    n_bitmap_info_ext = true;
    if (extends_bitmap_info()) {
        n_bitmap_info_ext = false;
        m_bitmap_info_ext = std::unique_ptr<bitmap_info_extension_t>(new bitmap_info_extension_t(m__io, this, m__root));
    }
    n_color_mask = true;
    if (is_color_mask_here()) {
        n_color_mask = false;
        m_color_mask = std::unique_ptr<color_mask_t>(new color_mask_t(len_header() != bmp_t::HEADER_TYPE_BITMAP_V2_INFO_HEADER, m__io, this, m__root));
    }
    n_os2_2x_bitmap_ext = true;
    if (extends_os2_2x_bitmap()) {
        n_os2_2x_bitmap_ext = false;
        m_os2_2x_bitmap_ext = std::unique_ptr<os2_2x_bitmap_extension_t>(new os2_2x_bitmap_extension_t(m__io, this, m__root));
    }
    n_bitmap_v4_ext = true;
    if (extends_bitmap_v4()) {
        n_bitmap_v4_ext = false;
        m_bitmap_v4_ext = std::unique_ptr<bitmap_v4_extension_t>(new bitmap_v4_extension_t(m__io, this, m__root));
    }
    n_bitmap_v5_ext = true;
    if (extends_bitmap_v5()) {
        n_bitmap_v5_ext = false;
        m_bitmap_v5_ext = std::unique_ptr<bitmap_v5_extension_t>(new bitmap_v5_extension_t(m__io, this, m__root));
    }
}

bmp_t::bitmap_header_t::~bitmap_header_t() {
    _clean_up();
}

void bmp_t::bitmap_header_t::_clean_up() {
    if (!n_image_width) {
    }
    if (!n_image_height_raw) {
    }
    if (!n_bitmap_info_ext) {
    }
    if (!n_color_mask) {
    }
    if (!n_os2_2x_bitmap_ext) {
    }
    if (!n_bitmap_v4_ext) {
    }
    if (!n_bitmap_v5_ext) {
    }
}

bool bmp_t::bitmap_header_t::extends_bitmap_v4() {
    if (f_extends_bitmap_v4)
        return m_extends_bitmap_v4;
    m_extends_bitmap_v4 = len_header() >= bmp_t::HEADER_TYPE_BITMAP_V4_HEADER;
    f_extends_bitmap_v4 = true;
    return m_extends_bitmap_v4;
}

bool bmp_t::bitmap_header_t::extends_os2_2x_bitmap() {
    if (f_extends_os2_2x_bitmap)
        return m_extends_os2_2x_bitmap;
    m_extends_os2_2x_bitmap = len_header() == bmp_t::HEADER_TYPE_OS2_2X_BITMAP_HEADER;
    f_extends_os2_2x_bitmap = true;
    return m_extends_os2_2x_bitmap;
}

bool bmp_t::bitmap_header_t::uses_fixed_palette() {
    if (f_uses_fixed_palette)
        return m_uses_fixed_palette;
    m_uses_fixed_palette =  ((!( ((bits_per_pixel() == 16) || (bits_per_pixel() == 24) || (bits_per_pixel() == 32)) )) && (!( ((extends_bitmap_info()) && (!(extends_os2_2x_bitmap())) && ( ((bitmap_info_ext()->compression() == bmp_t::COMPRESSIONS_JPEG) || (bitmap_info_ext()->compression() == bmp_t::COMPRESSIONS_PNG)) )) ))) ;
    f_uses_fixed_palette = true;
    return m_uses_fixed_palette;
}

bool bmp_t::bitmap_header_t::extends_bitmap_info() {
    if (f_extends_bitmap_info)
        return m_extends_bitmap_info;
    m_extends_bitmap_info = len_header() >= bmp_t::HEADER_TYPE_BITMAP_INFO_HEADER;
    f_extends_bitmap_info = true;
    return m_extends_bitmap_info;
}

int32_t bmp_t::bitmap_header_t::image_height() {
    if (f_image_height)
        return m_image_height;
    m_image_height = ((image_height_raw() < 0) ? (-(image_height_raw())) : (image_height_raw()));
    f_image_height = true;
    return m_image_height;
}

bool bmp_t::bitmap_header_t::is_core_header() {
    if (f_is_core_header)
        return m_is_core_header;
    m_is_core_header = len_header() == bmp_t::HEADER_TYPE_BITMAP_CORE_HEADER;
    f_is_core_header = true;
    return m_is_core_header;
}

bool bmp_t::bitmap_header_t::extends_bitmap_v5() {
    if (f_extends_bitmap_v5)
        return m_extends_bitmap_v5;
    m_extends_bitmap_v5 = len_header() >= bmp_t::HEADER_TYPE_BITMAP_V5_HEADER;
    f_extends_bitmap_v5 = true;
    return m_extends_bitmap_v5;
}

bool bmp_t::bitmap_header_t::is_color_mask_here() {
    if (f_is_color_mask_here)
        return m_is_color_mask_here;
    m_is_color_mask_here =  ((len_header() == bmp_t::HEADER_TYPE_BITMAP_V2_INFO_HEADER) || (len_header() == bmp_t::HEADER_TYPE_BITMAP_V3_INFO_HEADER) || (extends_bitmap_v4())) ;
    f_is_color_mask_here = true;
    return m_is_color_mask_here;
}

bool bmp_t::bitmap_header_t::bottom_up() {
    if (f_bottom_up)
        return m_bottom_up;
    m_bottom_up = image_height_raw() > 0;
    f_bottom_up = true;
    return m_bottom_up;
}

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

void bmp_t::os2_2x_bitmap_extension_t::_read() {
    m_units = m__io->read_u2le();
    m_reserved = m__io->read_u2le();
    m_recording = m__io->read_u2le();
    m_rendering = static_cast<bmp_t::os2_rendering_t>(m__io->read_u2le());
    m_size1 = m__io->read_u4le();
    m_size2 = m__io->read_u4le();
    m_color_encoding = m__io->read_u4le();
    m_identifier = m__io->read_u4le();
}

bmp_t::os2_2x_bitmap_extension_t::~os2_2x_bitmap_extension_t() {
    _clean_up();
}

void bmp_t::os2_2x_bitmap_extension_t::_clean_up() {
}

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

void bmp_t::fixed_point_16_dot_16_t::_read() {
    m_raw = m__io->read_u4le();
}

bmp_t::fixed_point_16_dot_16_t::~fixed_point_16_dot_16_t() {
    _clean_up();
}

void bmp_t::fixed_point_16_dot_16_t::_clean_up() {
}

double bmp_t::fixed_point_16_dot_16_t::value() {
    if (f_value)
        return m_value;
    m_value = ((raw() + 0.0) / (1 << 16));
    f_value = true;
    return m_value;
}

bmp_t::color_table_t::color_table_t(bool p_has_reserved_field, uint32_t p_num_colors, kaitai::kstream* p__io, bmp_t::bitmap_info_t* p__parent, bmp_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_has_reserved_field = p_has_reserved_field;
    m_num_colors = p_num_colors;
    m_colors = nullptr;
    f_num_colors_present = false;
    _read();
}

void bmp_t::color_table_t::_read() {
    m_colors = std::unique_ptr<std::vector<std::unique_ptr<rgb_record_t>>>(new std::vector<std::unique_ptr<rgb_record_t>>());
    const int l_colors = (( ((num_colors() > 0) && (num_colors() < num_colors_present())) ) ? (num_colors()) : (num_colors_present()));
    for (int i = 0; i < l_colors; i++) {
        m_colors->push_back(std::move(std::unique_ptr<rgb_record_t>(new rgb_record_t(has_reserved_field(), m__io, this, m__root))));
    }
}

bmp_t::color_table_t::~color_table_t() {
    _clean_up();
}

void bmp_t::color_table_t::_clean_up() {
}

int32_t bmp_t::color_table_t::num_colors_present() {
    if (f_num_colors_present)
        return m_num_colors_present;
    m_num_colors_present = (_io()->size() / ((has_reserved_field()) ? (4) : (3)));
    f_num_colors_present = true;
    return m_num_colors_present;
}

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_file_type = m__io->read_bytes(2);
    if (!(file_type() == std::string("\x42\x4D", 2))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x42\x4D", 2), file_type(), _io(), std::string("/types/file_header/seq/0"));
    }
    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() {
    _clean_up();
}

void bmp_t::file_header_t::_clean_up() {
}

bmp_t::bitmap_info_t::bitmap_info_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;
    m_header = nullptr;
    m__io__raw_header = nullptr;
    m_color_mask = nullptr;
    m_color_table = nullptr;
    m__io__raw_color_table = nullptr;
    f_is_color_mask_given = false;
    f_color_mask_given = false;
    f_color_mask_blue = false;
    f_color_mask_alpha = false;
    f_color_mask_green = false;
    f_is_color_mask_here = false;
    f_color_mask_red = false;
    _read();
}

void bmp_t::bitmap_info_t::_read() {
    m_len_header = m__io->read_u4le();
    m__raw_header = m__io->read_bytes((len_header() - 4));
    m__io__raw_header = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_header));
    m_header = std::unique_ptr<bitmap_header_t>(new bitmap_header_t(len_header(), m__io__raw_header.get(), this, m__root));
    n_color_mask = true;
    if (is_color_mask_here()) {
        n_color_mask = false;
        m_color_mask = std::unique_ptr<color_mask_t>(new color_mask_t(header()->bitmap_info_ext()->compression() == bmp_t::COMPRESSIONS_ALPHA_BITFIELDS, m__io, this, m__root));
    }
    n_color_table = true;
    if (!(_io()->is_eof())) {
        n_color_table = false;
        m__raw_color_table = m__io->read_bytes_full();
        m__io__raw_color_table = std::unique_ptr<kaitai::kstream>(new kaitai::kstream(m__raw_color_table));
        m_color_table = std::unique_ptr<color_table_t>(new color_table_t(!(header()->is_core_header()), ((header()->extends_bitmap_info()) ? (header()->bitmap_info_ext()->num_colors_used()) : (0)), m__io__raw_color_table.get(), this, m__root));
    }
}

bmp_t::bitmap_info_t::~bitmap_info_t() {
    _clean_up();
}

void bmp_t::bitmap_info_t::_clean_up() {
    if (!n_color_mask) {
    }
    if (!n_color_table) {
    }
}

bool bmp_t::bitmap_info_t::is_color_mask_given() {
    if (f_is_color_mask_given)
        return m_is_color_mask_given;
    m_is_color_mask_given =  ((header()->extends_bitmap_info()) && ( ((header()->bitmap_info_ext()->compression() == bmp_t::COMPRESSIONS_BITFIELDS) || (header()->bitmap_info_ext()->compression() == bmp_t::COMPRESSIONS_ALPHA_BITFIELDS)) ) && ( ((is_color_mask_here()) || (header()->is_color_mask_here())) )) ;
    f_is_color_mask_given = true;
    return m_is_color_mask_given;
}

bmp_t::color_mask_t* bmp_t::bitmap_info_t::color_mask_given() {
    if (f_color_mask_given)
        return m_color_mask_given;
    n_color_mask_given = true;
    if (is_color_mask_given()) {
        n_color_mask_given = false;
        m_color_mask_given = ((is_color_mask_here()) ? (color_mask()) : (header()->color_mask()));
    }
    f_color_mask_given = true;
    return m_color_mask_given;
}

uint32_t bmp_t::bitmap_info_t::color_mask_blue() {
    if (f_color_mask_blue)
        return m_color_mask_blue;
    m_color_mask_blue = ((is_color_mask_given()) ? (color_mask_given()->blue_mask()) : (((header()->bits_per_pixel() == 16) ? (31) : ((( ((header()->bits_per_pixel() == 24) || (header()->bits_per_pixel() == 32)) ) ? (255) : (0))))));
    f_color_mask_blue = true;
    return m_color_mask_blue;
}

uint32_t bmp_t::bitmap_info_t::color_mask_alpha() {
    if (f_color_mask_alpha)
        return m_color_mask_alpha;
    m_color_mask_alpha = (( ((is_color_mask_given()) && (color_mask_given()->has_alpha_mask())) ) ? (color_mask_given()->alpha_mask()) : (0));
    f_color_mask_alpha = true;
    return m_color_mask_alpha;
}

int32_t bmp_t::bitmap_info_t::color_mask_green() {
    if (f_color_mask_green)
        return m_color_mask_green;
    m_color_mask_green = ((is_color_mask_given()) ? (color_mask_given()->green_mask()) : (((header()->bits_per_pixel() == 16) ? (992) : ((( ((header()->bits_per_pixel() == 24) || (header()->bits_per_pixel() == 32)) ) ? (65280) : (0))))));
    f_color_mask_green = true;
    return m_color_mask_green;
}

bool bmp_t::bitmap_info_t::is_color_mask_here() {
    if (f_is_color_mask_here)
        return m_is_color_mask_here;
    m_is_color_mask_here =  ((!(_io()->is_eof())) && (header()->len_header() == bmp_t::HEADER_TYPE_BITMAP_INFO_HEADER) && ( ((header()->bitmap_info_ext()->compression() == bmp_t::COMPRESSIONS_BITFIELDS) || (header()->bitmap_info_ext()->compression() == bmp_t::COMPRESSIONS_ALPHA_BITFIELDS)) )) ;
    f_is_color_mask_here = true;
    return m_is_color_mask_here;
}

int32_t bmp_t::bitmap_info_t::color_mask_red() {
    if (f_color_mask_red)
        return m_color_mask_red;
    m_color_mask_red = ((is_color_mask_given()) ? (color_mask_given()->red_mask()) : (((header()->bits_per_pixel() == 16) ? (31744) : ((( ((header()->bits_per_pixel() == 24) || (header()->bits_per_pixel() == 32)) ) ? (16711680) : (0))))));
    f_color_mask_red = true;
    return m_color_mask_red;
}