JPEG File Interchange Format, or JFIF, or, more colloquially known as just "JPEG" or "JPG", is a popular 2D bitmap image file format, offering lossy compression which works reasonably well with photographic images.
Format is organized as a container format, serving multiple "segments", each starting with a magic and a marker. JFIF standard dictates order and mandatory apperance of segments:
This page hosts a formal specification of JPEG (Joint Photographic Experts Group) File Interchange 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.jpg", 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);
jpeg_t data(&ks);
After that, one can get various attributes from the structure by invoking getter methods like:
data.segments() // => get segments
#ifndef JPEG_H_
#define JPEG_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>
#include "exif.h"
#include <vector>
#if KAITAI_STRUCT_VERSION < 9000L
#error "Incompatible Kaitai Struct C++/STL API: version 0.9 or later is required"
#endif
class exif_t;
/**
* JPEG File Interchange Format, or JFIF, or, more colloquially known
* as just "JPEG" or "JPG", is a popular 2D bitmap image file format,
* offering lossy compression which works reasonably well with
* photographic images.
*
* Format is organized as a container format, serving multiple
* "segments", each starting with a magic and a marker. JFIF standard
* dictates order and mandatory apperance of segments:
*
* * SOI
* * APP0 (with JFIF magic)
* * APP0 (with JFXX magic, optional)
* * everything else
* * SOS
* * JPEG-compressed stream
* * EOI
*/
class jpeg_t : public kaitai::kstruct {
public:
class segment_t;
class segment_sos_t;
class segment_app1_t;
class segment_sof0_t;
class exif_in_jpeg_t;
class segment_app0_t;
enum component_id_t {
COMPONENT_ID_Y = 1,
COMPONENT_ID_CB = 2,
COMPONENT_ID_CR = 3,
COMPONENT_ID_I = 4,
COMPONENT_ID_Q = 5
};
jpeg_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~jpeg_t();
class segment_t : public kaitai::kstruct {
public:
enum marker_enum_t {
MARKER_ENUM_TEM = 1,
MARKER_ENUM_SOF0 = 192,
MARKER_ENUM_SOF1 = 193,
MARKER_ENUM_SOF2 = 194,
MARKER_ENUM_SOF3 = 195,
MARKER_ENUM_DHT = 196,
MARKER_ENUM_SOF5 = 197,
MARKER_ENUM_SOF6 = 198,
MARKER_ENUM_SOF7 = 199,
MARKER_ENUM_SOI = 216,
MARKER_ENUM_EOI = 217,
MARKER_ENUM_SOS = 218,
MARKER_ENUM_DQT = 219,
MARKER_ENUM_DNL = 220,
MARKER_ENUM_DRI = 221,
MARKER_ENUM_DHP = 222,
MARKER_ENUM_APP0 = 224,
MARKER_ENUM_APP1 = 225,
MARKER_ENUM_APP2 = 226,
MARKER_ENUM_APP3 = 227,
MARKER_ENUM_APP4 = 228,
MARKER_ENUM_APP5 = 229,
MARKER_ENUM_APP6 = 230,
MARKER_ENUM_APP7 = 231,
MARKER_ENUM_APP8 = 232,
MARKER_ENUM_APP9 = 233,
MARKER_ENUM_APP10 = 234,
MARKER_ENUM_APP11 = 235,
MARKER_ENUM_APP12 = 236,
MARKER_ENUM_APP13 = 237,
MARKER_ENUM_APP14 = 238,
MARKER_ENUM_APP15 = 239,
MARKER_ENUM_COM = 254
};
segment_t(kaitai::kstream* p__io, jpeg_t* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~segment_t();
private:
std::string m_magic;
marker_enum_t m_marker;
uint16_t m_length;
bool n_length;
public:
bool _is_null_length() { length(); return n_length; };
private:
kaitai::kstruct* m_data;
bool n_data;
public:
bool _is_null_data() { data(); return n_data; };
private:
std::string m_image_data;
bool n_image_data;
public:
bool _is_null_image_data() { image_data(); return n_image_data; };
private:
jpeg_t* m__root;
jpeg_t* m__parent;
std::string m__raw_data;
bool n__raw_data;
public:
bool _is_null__raw_data() { _raw_data(); return n__raw_data; };
private:
kaitai::kstream* m__io__raw_data;
public:
std::string magic() const { return m_magic; }
marker_enum_t marker() const { return m_marker; }
uint16_t length() const { return m_length; }
kaitai::kstruct* data() const { return m_data; }
std::string image_data() const { return m_image_data; }
jpeg_t* _root() const { return m__root; }
jpeg_t* _parent() const { return m__parent; }
std::string _raw_data() const { return m__raw_data; }
kaitai::kstream* _io__raw_data() const { return m__io__raw_data; }
};
class segment_sos_t : public kaitai::kstruct {
public:
class component_t;
segment_sos_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~segment_sos_t();
class component_t : public kaitai::kstruct {
public:
component_t(kaitai::kstream* p__io, jpeg_t::segment_sos_t* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~component_t();
private:
component_id_t m_id;
uint8_t m_huffman_table;
jpeg_t* m__root;
jpeg_t::segment_sos_t* m__parent;
public:
/**
* Scan component selector
*/
component_id_t id() const { return m_id; }
uint8_t huffman_table() const { return m_huffman_table; }
jpeg_t* _root() const { return m__root; }
jpeg_t::segment_sos_t* _parent() const { return m__parent; }
};
private:
uint8_t m_num_components;
std::vector<component_t*>* m_components;
uint8_t m_start_spectral_selection;
uint8_t m_end_spectral;
uint8_t m_appr_bit_pos;
jpeg_t* m__root;
jpeg_t::segment_t* m__parent;
public:
/**
* Number of components in scan
*/
uint8_t num_components() const { return m_num_components; }
/**
* Scan components specification
*/
std::vector<component_t*>* components() const { return m_components; }
/**
* Start of spectral selection or predictor selection
*/
uint8_t start_spectral_selection() const { return m_start_spectral_selection; }
/**
* End of spectral selection
*/
uint8_t end_spectral() const { return m_end_spectral; }
/**
* Successive approximation bit position high + Successive approximation bit position low or point transform
*/
uint8_t appr_bit_pos() const { return m_appr_bit_pos; }
jpeg_t* _root() const { return m__root; }
jpeg_t::segment_t* _parent() const { return m__parent; }
};
class segment_app1_t : public kaitai::kstruct {
public:
segment_app1_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~segment_app1_t();
private:
std::string m_magic;
exif_in_jpeg_t* m_body;
bool n_body;
public:
bool _is_null_body() { body(); return n_body; };
private:
jpeg_t* m__root;
jpeg_t::segment_t* m__parent;
public:
std::string magic() const { return m_magic; }
exif_in_jpeg_t* body() const { return m_body; }
jpeg_t* _root() const { return m__root; }
jpeg_t::segment_t* _parent() const { return m__parent; }
};
class segment_sof0_t : public kaitai::kstruct {
public:
class component_t;
segment_sof0_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~segment_sof0_t();
class component_t : public kaitai::kstruct {
public:
component_t(kaitai::kstream* p__io, jpeg_t::segment_sof0_t* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~component_t();
private:
bool f_sampling_x;
int32_t m_sampling_x;
public:
int32_t sampling_x();
private:
bool f_sampling_y;
int32_t m_sampling_y;
public:
int32_t sampling_y();
private:
component_id_t m_id;
uint8_t m_sampling_factors;
uint8_t m_quantization_table_id;
jpeg_t* m__root;
jpeg_t::segment_sof0_t* m__parent;
public:
/**
* Component selector
*/
component_id_t id() const { return m_id; }
uint8_t sampling_factors() const { return m_sampling_factors; }
uint8_t quantization_table_id() const { return m_quantization_table_id; }
jpeg_t* _root() const { return m__root; }
jpeg_t::segment_sof0_t* _parent() const { return m__parent; }
};
private:
uint8_t m_bits_per_sample;
uint16_t m_image_height;
uint16_t m_image_width;
uint8_t m_num_components;
std::vector<component_t*>* m_components;
jpeg_t* m__root;
jpeg_t::segment_t* m__parent;
public:
uint8_t bits_per_sample() const { return m_bits_per_sample; }
uint16_t image_height() const { return m_image_height; }
uint16_t image_width() const { return m_image_width; }
uint8_t num_components() const { return m_num_components; }
std::vector<component_t*>* components() const { return m_components; }
jpeg_t* _root() const { return m__root; }
jpeg_t::segment_t* _parent() const { return m__parent; }
};
class exif_in_jpeg_t : public kaitai::kstruct {
public:
exif_in_jpeg_t(kaitai::kstream* p__io, jpeg_t::segment_app1_t* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~exif_in_jpeg_t();
private:
std::string m_extra_zero;
exif_t* m_data;
jpeg_t* m__root;
jpeg_t::segment_app1_t* m__parent;
std::string m__raw_data;
kaitai::kstream* m__io__raw_data;
public:
std::string extra_zero() const { return m_extra_zero; }
exif_t* data() const { return m_data; }
jpeg_t* _root() const { return m__root; }
jpeg_t::segment_app1_t* _parent() const { return m__parent; }
std::string _raw_data() const { return m__raw_data; }
kaitai::kstream* _io__raw_data() const { return m__io__raw_data; }
};
class segment_app0_t : public kaitai::kstruct {
public:
enum density_unit_t {
DENSITY_UNIT_NO_UNITS = 0,
DENSITY_UNIT_PIXELS_PER_INCH = 1,
DENSITY_UNIT_PIXELS_PER_CM = 2
};
segment_app0_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent = 0, jpeg_t* p__root = 0);
private:
void _read();
void _clean_up();
public:
~segment_app0_t();
private:
std::string m_magic;
uint8_t m_version_major;
uint8_t m_version_minor;
density_unit_t m_density_units;
uint16_t m_density_x;
uint16_t m_density_y;
uint8_t m_thumbnail_x;
uint8_t m_thumbnail_y;
std::string m_thumbnail;
jpeg_t* m__root;
jpeg_t::segment_t* m__parent;
public:
std::string magic() const { return m_magic; }
uint8_t version_major() const { return m_version_major; }
uint8_t version_minor() const { return m_version_minor; }
density_unit_t density_units() const { return m_density_units; }
/**
* Horizontal pixel density. Must not be zero.
*/
uint16_t density_x() const { return m_density_x; }
/**
* Vertical pixel density. Must not be zero.
*/
uint16_t density_y() const { return m_density_y; }
/**
* Horizontal pixel count of the following embedded RGB thumbnail. May be zero.
*/
uint8_t thumbnail_x() const { return m_thumbnail_x; }
/**
* Vertical pixel count of the following embedded RGB thumbnail. May be zero.
*/
uint8_t thumbnail_y() const { return m_thumbnail_y; }
/**
* Uncompressed 24 bit RGB (8 bits per color channel) raster thumbnail data in the order R0, G0, B0, ... Rn, Gn, Bn
*/
std::string thumbnail() const { return m_thumbnail; }
jpeg_t* _root() const { return m__root; }
jpeg_t::segment_t* _parent() const { return m__parent; }
};
private:
std::vector<segment_t*>* m_segments;
jpeg_t* m__root;
kaitai::kstruct* m__parent;
public:
std::vector<segment_t*>* segments() const { return m_segments; }
jpeg_t* _root() const { return m__root; }
kaitai::kstruct* _parent() const { return m__parent; }
};
#endif // JPEG_H_
// This is a generated file! Please edit source .ksy file and use kaitai-struct-compiler to rebuild
#include "jpeg.h"
#include "kaitai/exceptions.h"
jpeg_t::jpeg_t(kaitai::kstream* p__io, kaitai::kstruct* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = this;
m_segments = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::_read() {
m_segments = new std::vector<segment_t*>();
{
int i = 0;
while (!m__io->is_eof()) {
m_segments->push_back(new segment_t(m__io, this, m__root));
i++;
}
}
}
jpeg_t::~jpeg_t() {
_clean_up();
}
void jpeg_t::_clean_up() {
if (m_segments) {
for (std::vector<segment_t*>::iterator it = m_segments->begin(); it != m_segments->end(); ++it) {
delete *it;
}
delete m_segments; m_segments = 0;
}
}
jpeg_t::segment_t::segment_t(kaitai::kstream* p__io, jpeg_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m__io__raw_data = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::segment_t::_read() {
m_magic = m__io->read_bytes(1);
if (!(magic() == std::string("\xFF", 1))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\xFF", 1), magic(), _io(), std::string("/types/segment/seq/0"));
}
m_marker = static_cast<jpeg_t::segment_t::marker_enum_t>(m__io->read_u1());
n_length = true;
if ( ((marker() != jpeg_t::segment_t::MARKER_ENUM_SOI) && (marker() != jpeg_t::segment_t::MARKER_ENUM_EOI)) ) {
n_length = false;
m_length = m__io->read_u2be();
}
n_data = true;
if ( ((marker() != jpeg_t::segment_t::MARKER_ENUM_SOI) && (marker() != jpeg_t::segment_t::MARKER_ENUM_EOI)) ) {
n_data = false;
n_data = true;
switch (marker()) {
case jpeg_t::segment_t::MARKER_ENUM_APP1: {
n_data = false;
m__raw_data = m__io->read_bytes((length() - 2));
m__io__raw_data = new kaitai::kstream(m__raw_data);
m_data = new segment_app1_t(m__io__raw_data, this, m__root);
break;
}
case jpeg_t::segment_t::MARKER_ENUM_APP0: {
n_data = false;
m__raw_data = m__io->read_bytes((length() - 2));
m__io__raw_data = new kaitai::kstream(m__raw_data);
m_data = new segment_app0_t(m__io__raw_data, this, m__root);
break;
}
case jpeg_t::segment_t::MARKER_ENUM_SOF0: {
n_data = false;
m__raw_data = m__io->read_bytes((length() - 2));
m__io__raw_data = new kaitai::kstream(m__raw_data);
m_data = new segment_sof0_t(m__io__raw_data, this, m__root);
break;
}
case jpeg_t::segment_t::MARKER_ENUM_SOS: {
n_data = false;
m__raw_data = m__io->read_bytes((length() - 2));
m__io__raw_data = new kaitai::kstream(m__raw_data);
m_data = new segment_sos_t(m__io__raw_data, this, m__root);
break;
}
default: {
m__raw_data = m__io->read_bytes((length() - 2));
break;
}
}
}
n_image_data = true;
if (marker() == jpeg_t::segment_t::MARKER_ENUM_SOS) {
n_image_data = false;
m_image_data = m__io->read_bytes_full();
}
}
jpeg_t::segment_t::~segment_t() {
_clean_up();
}
void jpeg_t::segment_t::_clean_up() {
if (!n_length) {
}
if (!n_data) {
if (m__io__raw_data) {
delete m__io__raw_data; m__io__raw_data = 0;
}
if (m_data) {
delete m_data; m_data = 0;
}
}
if (!n_image_data) {
}
}
jpeg_t::segment_sos_t::segment_sos_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_components = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::segment_sos_t::_read() {
m_num_components = m__io->read_u1();
m_components = new std::vector<component_t*>();
const int l_components = num_components();
for (int i = 0; i < l_components; i++) {
m_components->push_back(new component_t(m__io, this, m__root));
}
m_start_spectral_selection = m__io->read_u1();
m_end_spectral = m__io->read_u1();
m_appr_bit_pos = m__io->read_u1();
}
jpeg_t::segment_sos_t::~segment_sos_t() {
_clean_up();
}
void jpeg_t::segment_sos_t::_clean_up() {
if (m_components) {
for (std::vector<component_t*>::iterator it = m_components->begin(); it != m_components->end(); ++it) {
delete *it;
}
delete m_components; m_components = 0;
}
}
jpeg_t::segment_sos_t::component_t::component_t(kaitai::kstream* p__io, jpeg_t::segment_sos_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::segment_sos_t::component_t::_read() {
m_id = static_cast<jpeg_t::component_id_t>(m__io->read_u1());
m_huffman_table = m__io->read_u1();
}
jpeg_t::segment_sos_t::component_t::~component_t() {
_clean_up();
}
void jpeg_t::segment_sos_t::component_t::_clean_up() {
}
jpeg_t::segment_app1_t::segment_app1_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::segment_app1_t::_read() {
m_magic = kaitai::kstream::bytes_to_str(m__io->read_bytes_term(0, false, true, true), std::string("ASCII"));
n_body = true;
{
std::string on = magic();
if (on == std::string("Exif")) {
n_body = false;
m_body = new exif_in_jpeg_t(m__io, this, m__root);
}
}
}
jpeg_t::segment_app1_t::~segment_app1_t() {
_clean_up();
}
void jpeg_t::segment_app1_t::_clean_up() {
if (!n_body) {
if (m_body) {
delete m_body; m_body = 0;
}
}
}
jpeg_t::segment_sof0_t::segment_sof0_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_components = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::segment_sof0_t::_read() {
m_bits_per_sample = m__io->read_u1();
m_image_height = m__io->read_u2be();
m_image_width = m__io->read_u2be();
m_num_components = m__io->read_u1();
m_components = new std::vector<component_t*>();
const int l_components = num_components();
for (int i = 0; i < l_components; i++) {
m_components->push_back(new component_t(m__io, this, m__root));
}
}
jpeg_t::segment_sof0_t::~segment_sof0_t() {
_clean_up();
}
void jpeg_t::segment_sof0_t::_clean_up() {
if (m_components) {
for (std::vector<component_t*>::iterator it = m_components->begin(); it != m_components->end(); ++it) {
delete *it;
}
delete m_components; m_components = 0;
}
}
jpeg_t::segment_sof0_t::component_t::component_t(kaitai::kstream* p__io, jpeg_t::segment_sof0_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
f_sampling_x = false;
f_sampling_y = false;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::segment_sof0_t::component_t::_read() {
m_id = static_cast<jpeg_t::component_id_t>(m__io->read_u1());
m_sampling_factors = m__io->read_u1();
m_quantization_table_id = m__io->read_u1();
}
jpeg_t::segment_sof0_t::component_t::~component_t() {
_clean_up();
}
void jpeg_t::segment_sof0_t::component_t::_clean_up() {
}
int32_t jpeg_t::segment_sof0_t::component_t::sampling_x() {
if (f_sampling_x)
return m_sampling_x;
m_sampling_x = ((sampling_factors() & 240) >> 4);
f_sampling_x = true;
return m_sampling_x;
}
int32_t jpeg_t::segment_sof0_t::component_t::sampling_y() {
if (f_sampling_y)
return m_sampling_y;
m_sampling_y = (sampling_factors() & 15);
f_sampling_y = true;
return m_sampling_y;
}
jpeg_t::exif_in_jpeg_t::exif_in_jpeg_t(kaitai::kstream* p__io, jpeg_t::segment_app1_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
m_data = 0;
m__io__raw_data = 0;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::exif_in_jpeg_t::_read() {
m_extra_zero = m__io->read_bytes(1);
if (!(extra_zero() == std::string("\x00", 1))) {
throw kaitai::validation_not_equal_error<std::string>(std::string("\x00", 1), extra_zero(), _io(), std::string("/types/exif_in_jpeg/seq/0"));
}
m__raw_data = m__io->read_bytes_full();
m__io__raw_data = new kaitai::kstream(m__raw_data);
m_data = new exif_t(m__io__raw_data);
}
jpeg_t::exif_in_jpeg_t::~exif_in_jpeg_t() {
_clean_up();
}
void jpeg_t::exif_in_jpeg_t::_clean_up() {
if (m__io__raw_data) {
delete m__io__raw_data; m__io__raw_data = 0;
}
if (m_data) {
delete m_data; m_data = 0;
}
}
jpeg_t::segment_app0_t::segment_app0_t(kaitai::kstream* p__io, jpeg_t::segment_t* p__parent, jpeg_t* p__root) : kaitai::kstruct(p__io) {
m__parent = p__parent;
m__root = p__root;
try {
_read();
} catch(...) {
_clean_up();
throw;
}
}
void jpeg_t::segment_app0_t::_read() {
m_magic = kaitai::kstream::bytes_to_str(m__io->read_bytes(5), std::string("ASCII"));
m_version_major = m__io->read_u1();
m_version_minor = m__io->read_u1();
m_density_units = static_cast<jpeg_t::segment_app0_t::density_unit_t>(m__io->read_u1());
m_density_x = m__io->read_u2be();
m_density_y = m__io->read_u2be();
m_thumbnail_x = m__io->read_u1();
m_thumbnail_y = m__io->read_u1();
m_thumbnail = m__io->read_bytes(((thumbnail_x() * thumbnail_y()) * 3));
}
jpeg_t::segment_app0_t::~segment_app0_t() {
_clean_up();
}
void jpeg_t::segment_app0_t::_clean_up() {
}