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.
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.
All parsing code for C++98/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.
Using Kaitai Struct in C++/STL usually consists of 3 steps.
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);
#include "kaitai/kaitaistream.h"
kaitai::kstream ks(&is);
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
#ifndef PIF_H_
#define PIF_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
class pif_t;
#include "kaitai/kaitaistruct.h"
#include <stdint.h>
#include <set>
#include <vector>
#if KAITAI_STRUCT_VERSION < 11000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.11 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 color_table_data_t;
class information_header_t;
class pif_header_t;
enum compression_type_t {
COMPRESSION_TYPE_NONE = 0,
COMPRESSION_TYPE_RLE = 32222
};
static bool _is_defined_compression_type_t(compression_type_t v);
private:
static const std::set<compression_type_t> _values_compression_type_t;
static std::set<compression_type_t> _build_values_compression_type_t();
public:
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
};
static bool _is_defined_image_type_t(image_type_t v);
private:
static const std::set<image_type_t> _values_image_type_t;
static std::set<image_type_t> _build_values_image_type_t();
public:
pif_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, pif_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~pif_t();
class color_table_data_t : public kaitai::kstruct {
public:
color_table_data_t(kaitai::kstream* p__io, pif_t* p__parent = 0, pif_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~color_table_data_t();
private:
std::vector<int32_t>* m_entries;
pif_t* m__root;
pif_t* m__parent;
public:
std::vector<int32_t>* entries() const { return m_entries; }
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 = 0, pif_t* p__root = 0);
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 pif_header_t : public kaitai::kstruct {
public:
pif_header_t(kaitai::kstream* p__io, pif_t* p__parent = 0, pif_t* p__root = 0);
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; }
};
private:
bool f_image_data;
std::string m_image_data;
public:
std::string image_data();
private:
pif_header_t* m_file_header;
information_header_t* m_info_header;
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:
kaitai::kstream* m__io__raw_color_table;
public:
pif_header_t* file_header() const { return m_file_header; }
information_header_t* info_header() const { return m_info_header; }
color_table_data_t* color_table() const { return m_color_table; }
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; }
};
#endif // PIF_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "pif.h"
#include "kaitai/exceptions.h"
std::set<pif_t::compression_type_t> pif_t::_build_values_compression_type_t() {
std::set<pif_t::compression_type_t> _t;
_t.insert(pif_t::COMPRESSION_TYPE_NONE);
_t.insert(pif_t::COMPRESSION_TYPE_RLE);
return _t;
}
const std::set<pif_t::compression_type_t> pif_t::_values_compression_type_t = pif_t::_build_values_compression_type_t();
bool pif_t::_is_defined_compression_type_t(pif_t::compression_type_t v) {
return pif_t::_values_compression_type_t.find(v) != pif_t::_values_compression_type_t.end();
}
std::set<pif_t::image_type_t> pif_t::_build_values_image_type_t() {
std::set<pif_t::image_type_t> _t;
_t.insert(pif_t::IMAGE_TYPE_RGB332);
_t.insert(pif_t::IMAGE_TYPE_RGB888);
_t.insert(pif_t::IMAGE_TYPE_INDEXED_RGB332);
_t.insert(pif_t::IMAGE_TYPE_INDEXED_RGB565);
_t.insert(pif_t::IMAGE_TYPE_INDEXED_RGB888);
_t.insert(pif_t::IMAGE_TYPE_BLACK_WHITE);
_t.insert(pif_t::IMAGE_TYPE_RGB16C);
_t.insert(pif_t::IMAGE_TYPE_RGB565);
return _t;
}
const std::set<pif_t::image_type_t> pif_t::_values_image_type_t = pif_t::_build_values_image_type_t();
bool pif_t::_is_defined_image_type_t(pif_t::image_type_t v) {
return pif_t::_values_image_type_t.find(v) != pif_t::_values_image_type_t.end();
}
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 = p__root ? p__root : this;
m_file_header = 0;
m_info_header = 0;
m_color_table = 0;
m__io__raw_color_table = 0;
f_image_data = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void pif_t::_read() {
m_file_header = new pif_header_t(m__io, this, m__root);
m_info_header = 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 = new kaitai::kstream(m__raw_color_table);
m_color_table = new color_table_data_t(m__io__raw_color_table, this, m__root);
}
}
pif_t::~pif_t() {
_clean_up();
}
void pif_t::_clean_up() {
if (m_file_header) {
delete m_file_header; m_file_header = 0;
}
if (m_info_header) {
delete m_info_header; m_info_header = 0;
}
if (!n_color_table) {
if (m__io__raw_color_table) {
delete m__io__raw_color_table; m__io__raw_color_table = 0;
}
if (m_color_table) {
delete m_color_table; m_color_table = 0;
}
}
if (f_image_data) {
}
}
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 = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void pif_t::color_table_data_t::_read() {
m_entries = 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_RGB332: {
m_entries->push_back(m__io->read_bits_int_le(8));
break;
}
case pif_t::IMAGE_TYPE_INDEXED_RGB565: {
m_entries->push_back(m__io->read_bits_int_le(16));
break;
}
case pif_t::IMAGE_TYPE_INDEXED_RGB888: {
m_entries->push_back(m__io->read_bits_int_le(24));
break;
}
}
i++;
}
}
}
pif_t::color_table_data_t::~color_table_data_t() {
_clean_up();
}
void pif_t::color_table_data_t::_clean_up() {
if (m_entries) {
delete m_entries; m_entries = 0;
}
}
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;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void pif_t::information_header_t::_read() {
m_image_type = static_cast<pif_t::image_type_t>(m__io->read_u2le());
if (!( ((m_image_type == pif_t::IMAGE_TYPE_RGB888) || (m_image_type == pif_t::IMAGE_TYPE_RGB565) || (m_image_type == pif_t::IMAGE_TYPE_RGB332) || (m_image_type == pif_t::IMAGE_TYPE_RGB16C) || (m_image_type == pif_t::IMAGE_TYPE_BLACK_WHITE) || (m_image_type == pif_t::IMAGE_TYPE_INDEXED_RGB888) || (m_image_type == pif_t::IMAGE_TYPE_INDEXED_RGB565) || (m_image_type == pif_t::IMAGE_TYPE_INDEXED_RGB332)) )) {
throw kaitai::validation_not_any_of_error<pif_t::image_type_t>(m_image_type, m__io, std::string("/types/information_header/seq/0"));
}
m_bits_per_pixel = m__io->read_u2le();
{
uint16_t _ = m_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>(m_bits_per_pixel, m__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 (!(m_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(), m_len_image_data, m__io, std::string("/types/information_header/seq/4"));
}
m_len_color_table = m__io->read_u2le();
if (!(m_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)), m_len_color_table, m__io, std::string("/types/information_header/seq/5"));
}
if (!(m_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)), m_len_color_table, m__io, std::string("/types/information_header/seq/5"));
}
m_compression = static_cast<pif_t::compression_type_t>(m__io->read_u2le());
if (!( ((m_compression == pif_t::COMPRESSION_TYPE_NONE) || (m_compression == pif_t::COMPRESSION_TYPE_RLE)) )) {
throw kaitai::validation_not_any_of_error<pif_t::compression_type_t>(m_compression, m__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;
f_len_color_table_entry = true;
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))))));
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;
f_len_color_table_full = true;
m_len_color_table_full = len_color_table_entry() * (1 << bits_per_pixel());
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;
f_len_color_table_max = true;
m_len_color_table_max = _root()->file_header()->ofs_image_data() - _root()->file_header()->ofs_image_data_min();
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;
f_uses_indexed_mode = true;
m_uses_indexed_mode = len_color_table_entry() != 0;
return m_uses_indexed_mode;
}
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;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void pif_t::pif_header_t::_read() {
m_magic = m__io->read_bytes(4);
if (!(m_magic == std::string("\x50\x49\x46\x00", 4))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x50\x49\x46\x00", 4), m_magic, m__io, std::string("/types/pif_header/seq/0"));
}
m_len_file = m__io->read_u4le();
if (!(m_len_file >= ofs_image_data_min())) {
throw kaitai::validation_less_than_error<uint32_t>(ofs_image_data_min(), m_len_file, m__io, std::string("/types/pif_header/seq/1"));
}
m_ofs_image_data = m__io->read_u4le();
if (!(m_ofs_image_data >= ofs_image_data_min())) {
throw kaitai::validation_less_than_error<uint32_t>(ofs_image_data_min(), m_ofs_image_data, m__io, std::string("/types/pif_header/seq/2"));
}
if (!(m_ofs_image_data <= len_file())) {
throw kaitai::validation_greater_than_error<uint32_t>(len_file(), m_ofs_image_data, m__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;
f_ofs_image_data_min = true;
m_ofs_image_data_min = 12 + 16;
return m_ofs_image_data_min;
}
std::string pif_t::image_data() {
if (f_image_data)
return m_image_data;
f_image_data = true;
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);
return m_image_data;
}