Portable Image Format: C++11/STL parsing library

The Portable Image Format (PIF) is a basic, bitmap-like image format with the focus on ease of use (implementation) and small size for embedded applications.

See https://github.com/gfcwfzkm/PIF-Image-Format for more info.

File extension

pif

KS implementation details

License: LGPL-2.1
Minimal Kaitai Struct required: 0.9

References

This page hosts a formal specification of Portable Image 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.pif", 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:
    pif_t data(&ks);
    

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

data.file_header() // => get file header

C++11/STL source code to parse Portable Image Format

pif.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 Portable Image Format (PIF) is a basic, bitmap-like image format with the
 * focus on ease of use (implementation) and small size for embedded
 * applications.
 * 
 * See <https://github.com/gfcwfzkm/PIF-Image-Format> for more info.
 * \sa https://github.com/gfcwfzkm/PIF-Image-Format/blob/4ec261b/Specification/PIF%20Format%20Specification.pdf Source
 * \sa https://github.com/gfcwfzkm/PIF-Image-Format/blob/4ec261b/C%20Library/pifdec.c#L300 Source
 */

class pif_t : public kaitai::kstruct {

public:
    class pif_header_t;
    class information_header_t;
    class color_table_data_t;

    enum image_type_t {
        IMAGE_TYPE_RGB332 = 7763,
        IMAGE_TYPE_RGB888 = 17212,
        IMAGE_TYPE_INDEXED_RGB332 = 18754,
        IMAGE_TYPE_INDEXED_RGB565 = 18759,
        IMAGE_TYPE_INDEXED_RGB888 = 18770,
        IMAGE_TYPE_BLACK_WHITE = 32170,
        IMAGE_TYPE_RGB16C = 47253,
        IMAGE_TYPE_RGB565 = 58821
    };

    enum compression_type_t {
        COMPRESSION_TYPE_NONE = 0,
        COMPRESSION_TYPE_RLE = 32222
    };

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

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

public:
    ~pif_t();

    class pif_header_t : public kaitai::kstruct {

    public:

        pif_header_t(kaitai::kstream* p__io, pif_t* p__parent = nullptr, pif_t* p__root = nullptr);

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

    public:
        ~pif_header_t();

    private:
        bool f_ofs_image_data_min;
        int32_t m_ofs_image_data_min;

    public:
        int32_t ofs_image_data_min();

    private:
        std::string m_magic;
        uint32_t m_len_file;
        uint32_t m_ofs_image_data;
        pif_t* m__root;
        pif_t* m__parent;

    public:
        std::string magic() const { return m_magic; }
        uint32_t len_file() const { return m_len_file; }
        uint32_t ofs_image_data() const { return m_ofs_image_data; }
        pif_t* _root() const { return m__root; }
        pif_t* _parent() const { return m__parent; }
    };

    class information_header_t : public kaitai::kstruct {

    public:

        information_header_t(kaitai::kstream* p__io, pif_t* p__parent = nullptr, pif_t* p__root = nullptr);

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

    public:
        ~information_header_t();

    private:
        bool f_len_color_table_entry;
        int8_t m_len_color_table_entry;

    public:
        int8_t len_color_table_entry();

    private:
        bool f_len_color_table_full;
        int32_t m_len_color_table_full;

    public:
        int32_t len_color_table_full();

    private:
        bool f_len_color_table_max;
        int32_t m_len_color_table_max;

    public:
        int32_t len_color_table_max();

    private:
        bool f_uses_indexed_mode;
        bool m_uses_indexed_mode;

    public:
        bool uses_indexed_mode();

    private:
        image_type_t m_image_type;
        uint16_t m_bits_per_pixel;
        uint16_t m_width;
        uint16_t m_height;
        uint32_t m_len_image_data;
        uint16_t m_len_color_table;
        compression_type_t m_compression;
        pif_t* m__root;
        pif_t* m__parent;

    public:
        image_type_t image_type() const { return m_image_type; }

        /**
         * See <https://github.com/gfcwfzkm/PIF-Image-Format/blob/4ec261b/Specification/PIF%20Format%20Specification.pdf>:
         * 
         * > Bits per Pixel: Bit size that each Pixel occupies. Bit size for an
         * > Indexed Image cannot go beyond 8 bits.
         */
        uint16_t bits_per_pixel() const { return m_bits_per_pixel; }
        uint16_t width() const { return m_width; }
        uint16_t height() const { return m_height; }
        uint32_t len_image_data() const { return m_len_image_data; }

        /**
         * See <https://github.com/gfcwfzkm/PIF-Image-Format/blob/4ec261b/Specification/PIF%20Format%20Specification.pdf>:
         * 
         * > Color Table Size: (...), only used in Indexed mode, otherwise zero.
         * ---
         * > **Note**: The presence of the Color Table is mandatory when Bits per
         * > Pixel <= 8, unless Image Type states RGB332, RGB16C or B/W
         * ---
         * > **Color Table** (semi-optional)
         * >
         * > (...) The amount of Colors has to be same or less than [Bits per
         * > Pixel] allow, otherwise the image is invalid.
         */
        uint16_t len_color_table() const { return m_len_color_table; }
        compression_type_t compression() const { return m_compression; }
        pif_t* _root() const { return m__root; }
        pif_t* _parent() const { return m__parent; }
    };

    class color_table_data_t : public kaitai::kstruct {

    public:

        color_table_data_t(kaitai::kstream* p__io, pif_t* p__parent = nullptr, pif_t* p__root = nullptr);

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

    public:
        ~color_table_data_t();

    private:
        std::unique_ptr<std::vector<int32_t>> m_entries;
        pif_t* m__root;
        pif_t* m__parent;

    public:
        std::vector<int32_t>* entries() const { return m_entries.get(); }
        pif_t* _root() const { return m__root; }
        pif_t* _parent() const { return m__parent; }
    };

private:
    bool f_image_data;
    std::string m_image_data;

public:
    std::string image_data();

private:
    std::unique_ptr<pif_header_t> m_file_header;
    std::unique_ptr<information_header_t> m_info_header;
    std::unique_ptr<color_table_data_t> m_color_table;
    bool n_color_table;

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

private:
    pif_t* m__root;
    kaitai::kstruct* m__parent;
    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:
    pif_header_t* file_header() const { return m_file_header.get(); }
    information_header_t* info_header() const { return m_info_header.get(); }
    color_table_data_t* color_table() const { return m_color_table.get(); }
    pif_t* _root() const { return m__root; }
    kaitai::kstruct* _parent() const { return m__parent; }
    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(); }
};

pif.cpp

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

#include "pif.h"
#include "kaitai/exceptions.h"

pif_t::pif_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, pif_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = this;
    m_file_header = nullptr;
    m_info_header = nullptr;
    m_color_table = nullptr;
    m__io__raw_color_table = nullptr;
    f_image_data = false;
    _read();
}

void pif_t::_read() {
    m_file_header = std::unique_ptr<pif_header_t>(new pif_header_t(m__io, this, m__root));
    m_info_header = std::unique_ptr<information_header_t>(new information_header_t(m__io, this, m__root));
    n_color_table = true;
    if (info_header()->uses_indexed_mode()) {
        n_color_table = false;
        m__raw_color_table = m__io->read_bytes(info_header()->len_color_table());
        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_data_t>(new color_table_data_t(m__io__raw_color_table.get(), this, m__root));
    }
}

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

void pif_t::_clean_up() {
    if (!n_color_table) {
    }
    if (f_image_data) {
    }
}

pif_t::pif_header_t::pif_header_t(kaitai::kstream* p__io, pif_t* p__parent, pif_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_ofs_image_data_min = false;
    _read();
}

void pif_t::pif_header_t::_read() {
    m_magic = m__io->read_bytes(4);
    if (!(magic() == std::string("\x50\x49\x46\x00", 4))) {
        throw kaitai::validation_not_equal_error<std::string>(std::string("\x50\x49\x46\x00", 4), magic(), _io(), std::string("/types/pif_header/seq/0"));
    }
    m_len_file = m__io->read_u4le();
    if (!(len_file() >= ofs_image_data_min())) {
        throw kaitai::validation_less_than_error<uint32_t>(ofs_image_data_min(), len_file(), _io(), std::string("/types/pif_header/seq/1"));
    }
    m_ofs_image_data = m__io->read_u4le();
    if (!(ofs_image_data() >= ofs_image_data_min())) {
        throw kaitai::validation_less_than_error<uint32_t>(ofs_image_data_min(), ofs_image_data(), _io(), std::string("/types/pif_header/seq/2"));
    }
    if (!(ofs_image_data() <= len_file())) {
        throw kaitai::validation_greater_than_error<uint32_t>(len_file(), ofs_image_data(), _io(), std::string("/types/pif_header/seq/2"));
    }
}

pif_t::pif_header_t::~pif_header_t() {
    _clean_up();
}

void pif_t::pif_header_t::_clean_up() {
}

int32_t pif_t::pif_header_t::ofs_image_data_min() {
    if (f_ofs_image_data_min)
        return m_ofs_image_data_min;
    m_ofs_image_data_min = (12 + 16);
    f_ofs_image_data_min = true;
    return m_ofs_image_data_min;
}

pif_t::information_header_t::information_header_t(kaitai::kstream* p__io, pif_t* p__parent, pif_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    f_len_color_table_entry = false;
    f_len_color_table_full = false;
    f_len_color_table_max = false;
    f_uses_indexed_mode = false;
    _read();
}

void pif_t::information_header_t::_read() {
    m_image_type = static_cast<pif_t::image_type_t>(m__io->read_u2le());
    if (!( ((image_type() == pif_t::IMAGE_TYPE_RGB888) || (image_type() == pif_t::IMAGE_TYPE_RGB565) || (image_type() == pif_t::IMAGE_TYPE_RGB332) || (image_type() == pif_t::IMAGE_TYPE_RGB16C) || (image_type() == pif_t::IMAGE_TYPE_BLACK_WHITE) || (image_type() == pif_t::IMAGE_TYPE_INDEXED_RGB888) || (image_type() == pif_t::IMAGE_TYPE_INDEXED_RGB565) || (image_type() == pif_t::IMAGE_TYPE_INDEXED_RGB332)) )) {
        throw kaitai::validation_not_any_of_error<pif_t::image_type_t>(image_type(), _io(), std::string("/types/information_header/seq/0"));
    }
    m_bits_per_pixel = m__io->read_u2le();
    {
        uint16_t _ = bits_per_pixel();
        if (!(((image_type() == pif_t::IMAGE_TYPE_RGB888) ? (_ == 24) : (((image_type() == pif_t::IMAGE_TYPE_RGB565) ? (_ == 16) : (((image_type() == pif_t::IMAGE_TYPE_RGB332) ? (_ == 8) : (((image_type() == pif_t::IMAGE_TYPE_RGB16C) ? (_ == 4) : (((image_type() == pif_t::IMAGE_TYPE_BLACK_WHITE) ? (_ == 1) : (((uses_indexed_mode()) ? (_ <= 8) : (true)))))))))))))) {
            throw kaitai::validation_expr_error<uint16_t>(bits_per_pixel(), _io(), std::string("/types/information_header/seq/1"));
        }
    }
    m_width = m__io->read_u2le();
    m_height = m__io->read_u2le();
    m_len_image_data = m__io->read_u4le();
    if (!(len_image_data() <= (_root()->file_header()->len_file() - _root()->file_header()->ofs_image_data()))) {
        throw kaitai::validation_greater_than_error<uint32_t>((_root()->file_header()->len_file() - _root()->file_header()->ofs_image_data()), len_image_data(), _io(), std::string("/types/information_header/seq/4"));
    }
    m_len_color_table = m__io->read_u2le();
    if (!(len_color_table() >= ((uses_indexed_mode()) ? ((len_color_table_entry() * 1)) : (0)))) {
        throw kaitai::validation_less_than_error<uint16_t>(((uses_indexed_mode()) ? ((len_color_table_entry() * 1)) : (0)), len_color_table(), _io(), std::string("/types/information_header/seq/5"));
    }
    if (!(len_color_table() <= ((uses_indexed_mode()) ? (((len_color_table_max() < len_color_table_full()) ? (len_color_table_max()) : (len_color_table_full()))) : (0)))) {
        throw kaitai::validation_greater_than_error<uint16_t>(((uses_indexed_mode()) ? (((len_color_table_max() < len_color_table_full()) ? (len_color_table_max()) : (len_color_table_full()))) : (0)), len_color_table(), _io(), std::string("/types/information_header/seq/5"));
    }
    m_compression = static_cast<pif_t::compression_type_t>(m__io->read_u2le());
    if (!( ((compression() == pif_t::COMPRESSION_TYPE_NONE) || (compression() == pif_t::COMPRESSION_TYPE_RLE)) )) {
        throw kaitai::validation_not_any_of_error<pif_t::compression_type_t>(compression(), _io(), std::string("/types/information_header/seq/6"));
    }
}

pif_t::information_header_t::~information_header_t() {
    _clean_up();
}

void pif_t::information_header_t::_clean_up() {
}

int8_t pif_t::information_header_t::len_color_table_entry() {
    if (f_len_color_table_entry)
        return m_len_color_table_entry;
    m_len_color_table_entry = ((image_type() == pif_t::IMAGE_TYPE_INDEXED_RGB888) ? (3) : (((image_type() == pif_t::IMAGE_TYPE_INDEXED_RGB565) ? (2) : (((image_type() == pif_t::IMAGE_TYPE_INDEXED_RGB332) ? (1) : (0))))));
    f_len_color_table_entry = true;
    return m_len_color_table_entry;
}

int32_t pif_t::information_header_t::len_color_table_full() {
    if (f_len_color_table_full)
        return m_len_color_table_full;
    m_len_color_table_full = (len_color_table_entry() * (1 << bits_per_pixel()));
    f_len_color_table_full = true;
    return m_len_color_table_full;
}

int32_t pif_t::information_header_t::len_color_table_max() {
    if (f_len_color_table_max)
        return m_len_color_table_max;
    m_len_color_table_max = (_root()->file_header()->ofs_image_data() - _root()->file_header()->ofs_image_data_min());
    f_len_color_table_max = true;
    return m_len_color_table_max;
}

bool pif_t::information_header_t::uses_indexed_mode() {
    if (f_uses_indexed_mode)
        return m_uses_indexed_mode;
    m_uses_indexed_mode = len_color_table_entry() != 0;
    f_uses_indexed_mode = true;
    return m_uses_indexed_mode;
}

pif_t::color_table_data_t::color_table_data_t(kaitai::kstream* p__io, pif_t* p__parent, pif_t* p__root) : kaitai::kstruct(p__io) {
    m__parent = p__parent;
    m__root = p__root;
    m_entries = nullptr;
    _read();
}

void pif_t::color_table_data_t::_read() {
    m_entries = std::unique_ptr<std::vector<int32_t>>(new std::vector<int32_t>());
    {
        int i = 0;
        while (!m__io->is_eof()) {
            switch (_root()->info_header()->image_type()) {
            case pif_t::IMAGE_TYPE_INDEXED_RGB888: {
                m_entries->push_back(std::move(m__io->read_bits_int_le(24)));
                break;
            }
            case pif_t::IMAGE_TYPE_INDEXED_RGB565: {
                m_entries->push_back(std::move(m__io->read_bits_int_le(16)));
                break;
            }
            case pif_t::IMAGE_TYPE_INDEXED_RGB332: {
                m_entries->push_back(std::move(m__io->read_bits_int_le(8)));
                break;
            }
            }
            i++;
        }
    }
}

pif_t::color_table_data_t::~color_table_data_t() {
    _clean_up();
}

void pif_t::color_table_data_t::_clean_up() {
}

std::string pif_t::image_data() {
    if (f_image_data)
        return m_image_data;
    std::streampos _pos = m__io->pos();
    m__io->seek(file_header()->ofs_image_data());
    m_image_data = m__io->read_bytes(info_header()->len_image_data());
    m__io->seek(_pos);
    f_image_data = true;
    return m_image_data;
}