#include "exif.h" #include #include #include #include using namespace std; urational_t::urational_t(): p(0), q(0) {} urational_t::urational_t(uint32_t p, uint32_t q): p(p), q(q) {} srational_t::srational_t(): p(0), q(0) {} srational_t::srational_t(int32_t p, int32_t q): p(p), q(q) {} ExifTags::ExifTags(): ifd0_parsed_ok(false), exififd_parsed_ok(false), exififd_offset(0) { } static uint16_t read_be_uint16(istream &stream) { unsigned char buf[2]; stream.read((char*)buf, 2); return ((uint16_t)buf[0]) * 256 + (uint16_t)buf[1]; } static uint16_t read_le_uint16(istream &stream) { unsigned char buf[2]; stream.read((char*)buf, 2); return (uint16_t)buf[0] + ((uint16_t)buf[1]) * 256; } static uint16_t read_uint16(istream &stream, bool is_little_endian) { if (is_little_endian) { return read_le_uint16(stream); } else { return read_be_uint16(stream); } } static uint32_t read_be_uint32(istream &stream) { unsigned char buf[4]; stream.read((char*)buf, 4); return ((uint32_t)buf[0]) * 16777216 + ((uint32_t)buf[1]) * 65536 + ((uint32_t)buf[2]) * 256 + (uint32_t)buf[3]; } static uint32_t read_le_uint32(istream &stream) { unsigned char buf[4]; stream.read((char*)buf, 4); return ((uint32_t)buf[3]) * 16777216 + ((uint32_t)buf[2]) * 65536 + ((uint32_t)buf[1]) * 256 + (uint32_t)buf[0]; } static uint32_t read_uint32(istream &stream, bool is_little_endian) { if (is_little_endian) { return read_le_uint32(stream); } else { return read_be_uint32(stream); } } static int32_t read_be_int32(istream &stream) { unsigned char buf[4]; stream.read((char*)buf, 4); return (int32_t)(((uint32_t)buf[0]) * 16777216 + ((uint32_t)buf[1]) * 65536 + ((uint32_t)buf[2]) * 256 + (uint32_t)buf[3]); } static int32_t read_le_int32(istream &stream) { unsigned char buf[4]; stream.read((char*)buf, 4); return (int32_t)(((uint32_t)buf[3]) * 16777216 + ((uint32_t)buf[2]) * 65536 + ((uint32_t)buf[1]) * 256 + (uint32_t)buf[0]); } /* static int32_t read_int32(istream &stream, bool is_little_endian) { if (is_little_endian) { return read_le_int32(stream); } else { return read_be_int32(stream); } } */ static urational_t read_urational(istream &stream, bool is_little_endian) { if (is_little_endian) { uint32_t p = read_le_uint32(stream); uint32_t q = read_le_uint32(stream); return urational_t(p, q); } else { uint32_t p = read_be_uint32(stream); uint32_t q = read_be_uint32(stream); return urational_t(p, q); } } static srational_t read_srational(istream &stream, bool is_little_endian) { if (is_little_endian) { int32_t p = read_le_int32(stream); int32_t q = read_le_int32(stream); return srational_t(p, q); } else { int32_t p = read_be_int32(stream); int32_t q = read_be_int32(stream); return srational_t(p, q); } } static bool read_exif_tags(istream &stream, unsigned int num_entries, ExifTags &tags, uint64_t tiff_start_pos, bool is_little_endian) { for (unsigned int i = 0; i < num_entries; i++) { uint16_t tag_num = read_uint16(stream, is_little_endian); uint16_t data_format = read_uint16(stream, is_little_endian); uint32_t num_components = read_uint32(stream, is_little_endian); uint32_t tag_data = read_uint32(stream, is_little_endian); if (tag_num == 34665) { if (data_format == 4 && num_components == 1) { if (tag_data > 65535) { cerr << "Exif IFD offset too big (" << tag_data << ")." << endl; return false; } tags.exififd_offset = tag_data; } else { cerr << "Exif IFD offset has strange format." << endl; return false; } } if (tag_num == 0x829a) { uint64_t cur = stream.tellg(); stream.seekg(tiff_start_pos + tag_data, std::ios_base::beg); tags.exposure_time = read_urational(stream, is_little_endian); stream.seekg(cur, std::ios_base::beg); } if (tag_num == 0x9201) { uint64_t cur = stream.tellg(); stream.seekg(tiff_start_pos + tag_data, std::ios_base::beg); tags.shutter_speed_value = read_srational(stream, is_little_endian); stream.seekg(cur, std::ios_base::beg); } } return true; } static bool read_exif_ifd(istream &stream, uint64_t tiff_start_pos, uint16_t ifd_offset, ExifTags &tags, bool is_little_endian) { if (ifd_offset == 0) { return true; } { uint64_t cur = stream.tellg(); if (cur < tiff_start_pos) { cerr << "Read backwards?" << endl; return false; } if (ifd_offset < (cur - tiff_start_pos)) { cerr << "IFD offset too small." << endl; return false; } } stream.seekg(tiff_start_pos + ifd_offset, std::ios_base::beg); uint16_t num_entries = read_le_uint16(stream); return read_exif_tags(stream, num_entries, tags, tiff_start_pos, is_little_endian); } static bool parse_exif(istream &stream, uint16_t length, ExifTags &output) { if (length < 8) { return false; } { char buf[6]; const char *compare = "Exif\0\0"; stream.read(buf, 6); for (unsigned int i = 0; i < 6; i++) { if (buf[i] != compare[i]) { return false; } } } uint64_t tiff_start_pos = stream.tellg(); bool is_little_endian = false; { char buf[2]; stream.read(buf, 2); if (buf[0] == 'I' && buf[1] == 'I') { is_little_endian = true; } else if (buf[0] == 'M' && buf[1] == 'M') { is_little_endian = false; } else { cerr << "Endian fail" << endl; return false; } } { uint16_t magic = read_uint16(stream, is_little_endian); if (magic != 0x002a) { cerr << "TIFF fail" << endl; return false; } } uint32_t ifd0_offset = read_uint32(stream, is_little_endian); // Read IFD0 output.ifd0_parsed_ok = read_exif_ifd(stream, tiff_start_pos, ifd0_offset, output, is_little_endian); if (output.exififd_offset != 0) { // Read Exif IFD output.exififd_parsed_ok = read_exif_ifd(stream, tiff_start_pos, output.exififd_offset, output, is_little_endian); } if (stream.fail()) { cerr << "stream failed" << endl; return false; } else { return true; } } bool ExifTags::read_from_jpeg(istream &stream, ExifTags &output) { unsigned char buf[2]; stream.read((char*)buf, 2); if (!(buf[0] == 0xff && buf[1] == 0xd8)) { return false; } while (stream.good()) { stream.read((char*)buf, 2); if (buf[0] != 0xff) { cerr << "not a tag" << endl; return false; } if (buf[1] == 0xff || buf[1] == 0x00) { cerr << "not a marker" << endl; return false; } if (buf[1] == 0xd9) { break; } if (buf[1] == 0xda || (buf[1] >= 0xd0 && buf[1] <= 0xd7)) { // SOS marker or segment without length unsigned char c = 0; while(stream.good()) { c = stream.get(); if (c == 0xff) { c = stream.get(); if (c != 0x00) { // end of section stream.seekg(-2, std::ios_base::cur); break; } } } } else { // read size, seek to next marker uint16_t s = read_be_uint16(stream); if (s < 2) { cerr << "invalid size" << endl; return false; } if (buf[1] == 0xe1) { return parse_exif(stream, s, output); } stream.seekg(s - 2, std::ios_base::cur); } } if (stream.fail()) { cerr << "stream failed" << endl; return false; } else { return true; } } bool ExifTags::get_exposure_time(double &output) { if (exposure_time.p != 0 or exposure_time.q != 0) { output = (double)exposure_time.p / (double)exposure_time.q; return true; } else if (shutter_speed_value.p != 0 or shutter_speed_value.q != 0) { output = 1.0 / pow(2.0, (double)shutter_speed_value.p / (double)shutter_speed_value.q); return true; } else { return false; } }