Last active
July 15, 2022 03:24
-
-
Save espresso3389/bf88e83cf6120ed61950d69ace4c0516 to your computer and use it in GitHub Desktop.
JPEG parser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#include <cstring> | |
#include <vector> | |
#include <map> | |
#define APP(n) (0xe0 + n) | |
struct HUFF_ENTRY | |
{ | |
unsigned char len; | |
unsigned char chr; | |
}; | |
typedef std::vector<HUFF_ENTRY> HUFF_TABLE; | |
typedef std::map<int, HUFF_TABLE> HUFF_TABLE_MAP; | |
bool createHuffTable(HUFF_TABLE_MAP& tblMap, const unsigned char* data, int datSize) | |
{ | |
const unsigned char* p = data; | |
const unsigned char* end = data + datSize; | |
while(p < end) | |
{ | |
if(p + 17 > end) | |
{ | |
std::printf("Invalid DHT entry.\n"); | |
return false; | |
} | |
int tcn = p[0] >> 4; //0=DC,1=AC | |
int thn = p[0] & 15; // Huffman Table Number 0-3 | |
std::printf(" %s Table #%u\n", tcn ? "AC" : "DC", thn); | |
const unsigned char* counts = p + 1; | |
p = counts + 16; | |
for(int i = 0; i < 16; i++) | |
{ | |
int count = counts[i]; | |
if(p + count > end) | |
{ | |
std::printf("Invalid DHT entry.\n"); | |
return false; | |
} | |
std::printf(" %2u-bit:%s", i + 1, count == 0 ? " --" : " "); | |
for(int j = 0; j < count; j++) | |
{ | |
const char* trailer; | |
if(j + 1 == count) | |
trailer = ""; | |
else if((j & 15) == 15) | |
trailer = "\n "; | |
else | |
trailer = ","; | |
std::printf("%02X%s", *p++, trailer); | |
} | |
std::printf(" (%u entries)\n", count); | |
} | |
} | |
return true; | |
} | |
size_t read16(const unsigned char* data, bool bigEndian) | |
{ | |
if(bigEndian) | |
return data[0] * 256 + data[1]; | |
return data[1] * 256 + data[0]; | |
} | |
size_t read32(const unsigned char* data, bool bigEndian) | |
{ | |
if(bigEndian) | |
return data[0] * 0x1000000 + data[1] * 0x10000 + data[2] * 0x100 + data[3]; | |
return data[3] * 0x1000000 + data[2] * 0x10000 + data[1] * 0x100 + data[0]; | |
} | |
enum FieldType { | |
ft_Byte = 1, | |
ft_Ascii = 2, | |
ft_Short = 3, | |
ft_Long = 4, | |
ft_Rational = 5, | |
ft_SByte = 6, | |
ft_Undefined = 7, | |
ft_SShort = 8, | |
ft_SLong = 9, | |
ft_SRational = 10, | |
ft_Float = 11, | |
ft_Double = 12, | |
ft_Ifd = 13 | |
}; | |
static const size_t fieldSizes[] = {0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8, 4}; | |
enum TagType | |
{ | |
tt_Normal = 0, | |
tt_Pointer = 1, | |
tt_Size = 2, | |
tt_IFD = 3, | |
}; | |
struct TagInfo | |
{ | |
TagType type; | |
unsigned short partner; | |
const char* name; | |
bool canShort; | |
TagInfo(TagType _type, unsigned short _partner, const char* _name, bool _canShort) : | |
type(_type), partner(_partner), name(_name), canShort(_canShort) | |
{ | |
} | |
TagInfo() : type(tt_Normal), partner(0), name(NULL), canShort(false) | |
{ | |
} | |
explicit TagInfo(const char* _name) : | |
type(tt_Normal), partner(0), name(_name), canShort(false) | |
{ | |
} | |
}; | |
typedef std::map<unsigned short, TagInfo> TagInfoMap; | |
const TagInfoMap& tagInfo(); | |
bool loadAllIfds(const unsigned char* base, size_t ifdOffset, bool bigEndian, size_t level, size_t baseAddr); | |
void spc(size_t level) | |
{ | |
size_t co = level * 2; | |
for(size_t i = 0; i < co; i++) | |
std::fputc(' ', stdout); | |
} | |
template<FieldType T> void dump(const unsigned char*& data, bool bigEndian); | |
template<> void dump<ft_Byte>(const unsigned char*& data, bool bigEndian) | |
{ | |
std::printf("%02u", *data++); | |
} | |
template<> void dump<ft_Short>(const unsigned char*& data, bool bigEndian) | |
{ | |
std::printf("%u", read16(data, bigEndian)); | |
data += 2; | |
} | |
template<> void dump<ft_Long>(const unsigned char*& data, bool bigEndian) | |
{ | |
std::printf("%u", read32(data, bigEndian)); | |
data += 4; | |
} | |
template<> void dump<ft_Rational>(const unsigned char*& data, bool bigEndian) | |
{ | |
size_t num = read32(data, bigEndian); data += 4; | |
size_t den = read32(data, bigEndian); data += 4; | |
std::printf("%u/%u", num, den); | |
} | |
template<> void dump<ft_SByte>(const unsigned char*& data, bool bigEndian) | |
{ | |
std::printf("%d", *data++); | |
} | |
template<> void dump<ft_SShort>(const unsigned char*& data, bool bigEndian) | |
{ | |
std::printf("%d", read16(data, bigEndian)); | |
data += 2; | |
} | |
template<> void dump<ft_SLong>(const unsigned char*& data, bool bigEndian) | |
{ | |
std::printf("%d", read32(data, bigEndian)); | |
data += 4; | |
} | |
template<> void dump<ft_SRational>(const unsigned char*& data, bool bigEndian) | |
{ | |
int num = read32(data, bigEndian); data += 4; | |
int den = read32(data, bigEndian); data += 4; | |
std::printf("%d/%d", num, den); | |
} | |
template<FieldType T> void dumpAll(const unsigned char* data, size_t count, bool bigEndian) | |
{ | |
if(count > 16) | |
{ | |
std::printf("...\n"); | |
return; | |
} | |
for(size_t i = 0; i < count; i++) | |
{ | |
dump<T>(data, bigEndian); | |
std::fputc(i + 1 == count ? '\n' : ',', stdout); | |
} | |
} | |
void dumpData(const unsigned char* data, FieldType type, size_t count, bool bigEndian) | |
{ | |
switch(type) | |
{ | |
case ft_Byte: | |
case ft_Undefined: | |
dumpAll<ft_Byte>(data, count, bigEndian); break; | |
case ft_Ascii: std::printf("\"%s\"\n", data); break; | |
case ft_Short: dumpAll<ft_Short>(data, count, bigEndian); break; | |
case ft_Long: dumpAll<ft_Long>(data, count, bigEndian); break; | |
case ft_Rational: dumpAll<ft_Rational>(data, count, bigEndian); break; | |
case ft_SByte: dumpAll<ft_SByte>(data, count, bigEndian); break; | |
case ft_SShort: dumpAll<ft_SShort>(data, count, bigEndian); break; | |
case ft_SLong: dumpAll<ft_SLong>(data, count, bigEndian); break; | |
case ft_SRational: dumpAll<ft_SRational>(data, count, bigEndian); break; | |
default: | |
std::printf("\n"); | |
} | |
} | |
size_t loadIfd(const unsigned char* base, size_t ifdOffset, bool bigEndian, size_t level, size_t baseAddr) | |
{ | |
std::map<size_t, size_t> ptr; | |
const TagInfoMap& ti = tagInfo(); | |
const unsigned char* p = base + ifdOffset; | |
size_t entryCount = read16(p, bigEndian); p += 2; | |
for(size_t i = 0; i < entryCount; i++) | |
{ | |
std::printf("%08X ", p - base + baseAddr); | |
size_t tag = read16(p, bigEndian); p += 2; | |
FieldType type = (FieldType)read16(p, bigEndian); p += 2; | |
if(type < ft_Byte || type > ft_Ifd) | |
{ | |
// Ignore the field | |
std::printf("\n"); | |
p += 8; | |
continue; | |
} | |
size_t count = read32(p, bigEndian); p += 4; | |
size_t size = fieldSizes[type] * count; | |
const unsigned char* pData = size <= 4 ? p : base + read32(p, bigEndian); | |
p += 4; | |
spc(level); | |
TagInfoMap::const_iterator it = ti.find(tag); | |
if(it != ti.end()) | |
{ | |
const TagInfo& info = it->second; | |
std::printf("%s ", info.name); | |
size_t v = read32(pData, bigEndian); | |
if(info.type == tt_IFD) | |
{ | |
std::printf("ptr=%08Xh\n", v); | |
loadAllIfds(base, v, bigEndian, level + 1, baseAddr); | |
} | |
else if(info.type == tt_Size) | |
{ | |
std::printf("size=08Xh\n", v); | |
if(tag == 0x0202) // EXIF Thumbnail | |
{ | |
FILE* fp = fopen("thumb.jpg", "wb"); | |
if(fp) | |
{ | |
fwrite(base + ptr[tag], v, 1, fp); | |
fclose(fp); | |
} | |
} | |
} | |
else if(info.type == tt_Pointer) | |
{ | |
std::printf("ptr=%08Xh\n", v); | |
ptr[info.partner] = v; | |
} | |
else | |
{ | |
dumpData(pData, type, count, bigEndian); | |
} | |
} | |
else | |
{ | |
std::printf("%04X ", tag); | |
dumpData(pData, type, count, bigEndian); | |
} | |
} | |
return read32(p, bigEndian); | |
} | |
bool loadAllIfds(const unsigned char* base, size_t ifdOffset, bool bigEndian, size_t level, size_t baseAddr) | |
{ | |
while(ifdOffset != 0) | |
ifdOffset = loadIfd(base, ifdOffset, bigEndian, level, baseAddr); | |
return true; | |
} | |
bool processExif(const unsigned char* data, int datSize, int baseAddr) | |
{ | |
// Skip Exif signature | |
if(datSize < 6) | |
return false; | |
data += 6; | |
datSize -= 6; | |
baseAddr += 6; | |
// II or MM and signature | |
static const unsigned char leSig[] = {0x49, 0x49, 0x2a, 0}; | |
static const unsigned char beSig[] = {0x4d, 0x4d, 0, 0x2a}; | |
bool bigEndian; | |
if(memcmp(data, leSig, 4) == 0) | |
bigEndian = false; | |
else if(memcmp(data, beSig, 4) == 0) | |
bigEndian = true; | |
else | |
return false; | |
size_t ifdOffset = read32(data + 4, bigEndian); | |
return loadAllIfds(data, ifdOffset, bigEndian, 0, baseAddr); | |
} | |
bool processsJpeg(FILE* fp, FILE* fpOut = NULL) | |
{ | |
HUFF_TABLE_MAP tblMap; | |
const int BUF_SIZE = 256 * 256; | |
bool isDataStream = false; | |
int pos = 0; | |
char buf[BUF_SIZE * 2]; | |
char* work = buf + BUF_SIZE; | |
for(;;) | |
{ | |
const char* name = NULL; | |
int m; | |
for(;;) | |
{ | |
pos = std::ftell(fp); | |
int c = std::fgetc(fp); | |
if(c < 0) | |
return false; | |
if(c == 0xff) | |
{ | |
m = fgetc(fp); | |
if(m == 0) | |
{ | |
if(fpOut) | |
{ | |
std::fputc(0xff, fpOut); | |
std::fputc(m, fpOut); | |
} | |
continue; // escape for FF occurrence | |
} | |
isDataStream = false; | |
break; | |
} | |
else if(isDataStream) | |
{ | |
if(fpOut) std::fputc(c, fpOut); | |
} | |
else | |
{ | |
std::printf("Unknown char: %02X\n", c); | |
} | |
} | |
int size = -1; | |
if(m == 0xd8) | |
{ | |
name = "SOI"; | |
size = 0; | |
} | |
else if(m == 0xd9) | |
{ | |
name = "EOI"; | |
size = 0; | |
} | |
else if(m >= 0xe0 && m <= 0xef) | |
{ | |
std::sprintf(work, "APP%u", m - 0xe0); | |
name = work; | |
} | |
else if(m >= 0xd0 && m <= 0xd7) | |
{ | |
std::sprintf(buf, "RST%u", m - 0xd0); | |
name = buf; | |
isDataStream = true; | |
size = 0; | |
} | |
else if(m == 0xda) | |
{ | |
name = "SOS"; | |
isDataStream = true; | |
} | |
else if(m == 0xdb) | |
{ | |
name = "DQT"; | |
} | |
else if(m == 0xc4) | |
{ | |
name = "DHT"; | |
} | |
else if(m >= 0xc0 && m <= 0xc7) // except C4, C8, CC | |
{ | |
std::sprintf(buf, "SOF%u", m - 0xc0); | |
name = buf; | |
} | |
else | |
name = "???"; | |
if(size < 0) | |
{ | |
size = std::fgetc(fp) * 256; | |
size += std::fgetc(fp); | |
} | |
int datSize = size - 2; | |
if(datSize > 0) | |
{ | |
work[datSize] = 0; | |
if(std::fread(work, 1, datSize, fp) < datSize) | |
return false; | |
} | |
bool keep = true; | |
if(m >= APP(0) && m <= APP(15)) | |
{ | |
static const struct AppMarker | |
{ | |
int m; | |
int len; | |
const char* marker; | |
bool keep; | |
} | |
am[] = { | |
{APP(0), 0, "JFIF", true}, | |
{APP(1), 0, "Exif", false}, | |
{APP(1), 0, "http://ns.adobe.com/xap/1.0/", false}, | |
{APP(2), 0, "ICC_PROFILE", true}, | |
{APP(2), 0, "MPF", false}, | |
{APP(3), 0, "META", false}, | |
{APP(3), 0, "Meta", false}, | |
{APP(12), 5, "Type=", false}, | |
{APP(12), 5, "\x0a\x09\x09\x09\x09", false}, | |
{APP(12), 0, "Agfa Gevaert ", false}, | |
{APP(12), 0, "OLYMPUS OPTICAL CO.,LTD.", false}, | |
{APP(12), 0, "SanyoElectricDSC", false}, | |
{APP(12), 0, "SEIKO EPSON CORP. ", false}, | |
{APP(13), 0, "Photoshop 3.0", false}, | |
{APP(14), 0, "Adobe", false}, | |
{0, 0, NULL, false} | |
}; | |
for(int i = 0; am[i].m != 0; i++) | |
{ | |
int len = am[i].len ? am[i].len : strlen(am[i].marker) + 1; | |
if(m == am[i].m && memcmp(work, am[i].marker, len) == 0) | |
{ | |
keep = am[i].keep; | |
break; | |
} | |
} | |
name = buf; | |
std::sprintf(buf, "APP%u ", m - APP(0)); | |
char* p = buf + strlen(buf); | |
const char* pw = work; | |
while(*pw && *pw < 127) | |
*p++ = *pw++; | |
*p = 0; | |
} | |
std::printf("%08X %04X FF%02X %s%s\n", pos, size, m, name, | |
keep ? "" : " (Disposed)"); | |
if(m ==APP(1) && strcmp(work, "Exif") == 0) | |
processExif((const unsigned char*)work, datSize, pos); | |
if(!keep) | |
continue; | |
if(m == 0xc4) // DHT | |
{ | |
if(!createHuffTable(tblMap, (const unsigned char*)work, datSize)) | |
return false; | |
} | |
else if(m >= 0xc0 && m <= 0xc7) // SOF except C4, C8, CC | |
{ | |
if(datSize < 6) | |
{ | |
std::printf("SOF size too small; size=%u < 6\n", datSize); | |
return false; | |
} | |
const char* jfifType = ""; | |
if(m == 0xc0) jfifType = "Baseline"; | |
else if(m == 0xc2) jfifType = "Progressive"; | |
std::printf(" %s\n", jfifType); | |
unsigned char* p = (unsigned char*)work; | |
int bitsPerComp = p[0]; | |
int y = p[1] * 256 + p[2]; | |
int x = p[3] * 256 + p[4]; | |
int comps = p[5]; | |
std::printf(" %u x %u, %u bits (%u components, %u bits-per-component)\n", | |
x, y, bitsPerComp * comps, comps, bitsPerComp); | |
if(datSize < 6 + 3 * comps) | |
{ | |
std::printf("SOF size too small; size=%u < %u\n", | |
datSize, 6 + 3 * comps); | |
return false; | |
} | |
p += 6; | |
for(int i = 0; i < comps; i++) | |
{ | |
int cn = p[0]; | |
int hn = p[1] >> 4; | |
int vn = p[1] & 15; | |
int tqn = p[2]; | |
p += 3; | |
std::printf(" #%u, H=%u, V=%u, Tq=%u\n", | |
cn, hn, vn, tqn); | |
} | |
} | |
else if(m == 0xda) // SOS | |
{ | |
} | |
if(fpOut) | |
{ | |
std::fputc(0xff, fpOut); | |
std::fputc(m, fpOut); | |
if(size > 2) | |
{ | |
std::fputc(size >> 8, fpOut); | |
std::fputc(size & 255, fpOut); | |
std::fwrite(work, 1, datSize, fpOut); | |
} | |
} | |
if(m == 0xd9) | |
break; // EOI | |
} | |
return true; | |
} | |
int main(int argc, char* argv[]) | |
{ | |
std::FILE* fp = std::fopen(argv[1], "rb"); | |
std::FILE* fpOut = argv[2] ? std::fopen(argv[2], "wb") : NULL; | |
if(fp) | |
{ | |
processsJpeg(fp, fpOut); | |
} | |
return 0; | |
} | |
const TagInfoMap& tagInfo() | |
{ | |
static std::map<unsigned short, TagInfo> ti; | |
if(ti.size() == 0) | |
{ | |
ti[0] = TagInfo("GPSVersionID"); | |
ti[1] = TagInfo("GPSLatitudeRef"); | |
ti[2] = TagInfo("GPSLatitude"); | |
ti[3] = TagInfo("GPSLongitudeRef"); | |
ti[4] = TagInfo("GPSLongitude"); | |
ti[5] = TagInfo("GPSAltitudeRef"); | |
ti[6] = TagInfo("GPSAltitude"); | |
ti[7] = TagInfo("GPSTimeStamp"); | |
ti[8] = TagInfo("GPSSatellites"); | |
ti[9] = TagInfo("GPSStatus"); | |
ti[10] = TagInfo("GPSMeasureMode"); | |
ti[11] = TagInfo("GPSDOP"); | |
ti[12] = TagInfo("GPSSpeedRef"); | |
ti[13] = TagInfo("GPSSpeed"); | |
ti[14] = TagInfo("GPSTrackRef"); | |
ti[15] = TagInfo("GPSTrack"); | |
ti[16] = TagInfo("GPSImgDirectionRef"); | |
ti[17] = TagInfo("GPSImgDirection"); | |
ti[18] = TagInfo("GPSMapDatum"); | |
ti[19] = TagInfo("GPSDestLatitudeRef"); | |
ti[20] = TagInfo("GPSDestLatitude"); | |
ti[21] = TagInfo("GPSDestLongitudeRef"); | |
ti[22] = TagInfo("GPSDestLongitude"); | |
ti[23] = TagInfo("GPSDestBearingRef"); | |
ti[24] = TagInfo("GPSDestBearing"); | |
ti[25] = TagInfo("GPSDestDistanceRef"); | |
ti[26] = TagInfo("GPSDestDistance"); | |
ti[27] = TagInfo("GPSProcessingMethod"); | |
ti[28] = TagInfo("GPSAreaInformation"); | |
ti[29] = TagInfo("GPSDateStamp"); | |
ti[30] = TagInfo("GPSDifferential"); | |
ti[254] = TagInfo("NewSubfileType"); | |
ti[255] = TagInfo("SubfileType"); | |
ti[256] = TagInfo("ImageWidth"); | |
ti[257] = TagInfo("ImageLength"); | |
ti[258] = TagInfo("BitsPerSample"); | |
ti[259] = TagInfo("Compression"); | |
ti[262] = TagInfo("PhotometricInterpretation"); | |
ti[263] = TagInfo("Threshholding"); | |
ti[264] = TagInfo("CellWidth"); | |
ti[265] = TagInfo("CellLength"); | |
ti[266] = TagInfo("FillOrder"); | |
ti[269] = TagInfo("DocumentName"); | |
ti[270] = TagInfo("ImageDescription"); | |
ti[271] = TagInfo("Make"); | |
ti[272] = TagInfo("Model"); | |
ti[273] = TagInfo(tt_Pointer, 279, "StripOffsets", true); | |
ti[274] = TagInfo("Orientation"); | |
ti[277] = TagInfo("SamplesPerPixel"); | |
ti[278] = TagInfo("RowsPerStrip"); | |
ti[279] = TagInfo(tt_Size, 273, "StripByteCounts", true); | |
ti[280] = TagInfo("MinSampleValue"); | |
ti[281] = TagInfo("MaxSampleValue"); | |
ti[282] = TagInfo("XResolution"); | |
ti[283] = TagInfo("YResolution"); | |
ti[284] = TagInfo("PlanarConfiguration"); | |
ti[285] = TagInfo("PageName"); | |
ti[286] = TagInfo("XPosition"); | |
ti[287] = TagInfo("YPosition"); | |
ti[288] = TagInfo("FreeOffsets"); | |
ti[289] = TagInfo("FreeByteCounts"); | |
ti[290] = TagInfo("GrayResponseUnit"); | |
ti[291] = TagInfo("GrayResponseCurve"); | |
ti[292] = TagInfo("T4Options"); | |
ti[293] = TagInfo("T6Options"); | |
ti[296] = TagInfo("ResolutionUnit"); | |
ti[297] = TagInfo("PageNumber"); | |
ti[301] = TagInfo("TransferFunction"); | |
ti[305] = TagInfo("Software"); | |
ti[306] = TagInfo("DateTime"); | |
ti[315] = TagInfo("Artist"); | |
ti[316] = TagInfo("HostComputer"); | |
ti[317] = TagInfo("Predictor"); | |
ti[318] = TagInfo("WhitePoint"); | |
ti[319] = TagInfo("PrimaryChromaticities"); | |
ti[320] = TagInfo("ColorMap"); | |
ti[321] = TagInfo("HalftoneHints"); | |
ti[322] = TagInfo("TileWidth"); | |
ti[323] = TagInfo("TileLength"); | |
ti[324] = TagInfo(tt_Pointer, 325, "TileOffsets", true); | |
ti[325] = TagInfo(tt_Size, 324, "TileByteCounts", true); | |
ti[326] = TagInfo("BadFaxLines"); | |
ti[327] = TagInfo("CleanFaxData"); | |
ti[328] = TagInfo("ConsecutiveBadFaxLines"); | |
ti[330] = TagInfo(tt_IFD, 0, "SubIFDs", false); | |
ti[332] = TagInfo("InkSet"); | |
ti[333] = TagInfo("InkNames"); | |
ti[334] = TagInfo("NumberOfInks"); | |
ti[336] = TagInfo("DotRange"); | |
ti[337] = TagInfo("TargetPrinter"); | |
ti[338] = TagInfo("ExtraSamples"); | |
ti[339] = TagInfo("SampleFormat"); | |
ti[340] = TagInfo("SMinSampleValue"); | |
ti[341] = TagInfo("SMaxSampleValue"); | |
ti[342] = TagInfo("TransferRange"); | |
ti[343] = TagInfo("ClipPath"); | |
ti[344] = TagInfo("XClipPathUnits"); | |
ti[345] = TagInfo("YClipPathUnits"); | |
ti[346] = TagInfo("Indexed"); | |
ti[347] = TagInfo("JPEGTables"); | |
ti[351] = TagInfo("OPIProxy"); | |
ti[512] = TagInfo("JPEGProc"); | |
ti[513] = TagInfo(tt_Pointer, 514, "JPEGInterchangeFormat", false); | |
ti[514] = TagInfo(tt_Size, 513, "JPEGInterchangeFormatLength", false); | |
ti[515] = TagInfo("JPEGRestartInterval"); | |
ti[517] = TagInfo("JPEGLosslessPredictors"); | |
ti[518] = TagInfo("JPEGPointTransforms"); | |
ti[519] = TagInfo("JPEGQTables"); | |
ti[520] = TagInfo("JPEGDCTables"); | |
ti[521] = TagInfo("JPEGACTables"); | |
ti[529] = TagInfo("YCbCrCoefficients"); | |
ti[530] = TagInfo("YCbCrSubSampling"); | |
ti[531] = TagInfo("YCbCrPositioning"); | |
ti[532] = TagInfo("ReferenceBlackWhite"); | |
ti[700] = TagInfo("XML Packets"); | |
ti[32781] = TagInfo("ImageID"); | |
ti[33421] = TagInfo("CFARepeatPatternDim"); | |
ti[33422] = TagInfo("CFAPattern_EP"); | |
ti[33423] = TagInfo("BatteryLevel"); | |
ti[33432] = TagInfo("Copyright"); | |
ti[33434] = TagInfo("ExposureTime"); | |
ti[33437] = TagInfo("FNumber"); | |
ti[33550] = TagInfo("ModelPixelScaleTag"); | |
ti[33723] = TagInfo("IPTC/NAA"); | |
ti[33920] = TagInfo("IntergraphMatrixTag"); | |
ti[33922] = TagInfo("ModelTiepointTag"); | |
ti[34016] = TagInfo("Site"); | |
ti[34017] = TagInfo("ColorSequence"); | |
ti[34018] = TagInfo("IT8Header"); | |
ti[34019] = TagInfo("RasterPadding"); | |
ti[34020] = TagInfo("BitsPerRunLength"); | |
ti[34021] = TagInfo("BitsPerExtendedRunLength"); | |
ti[34022] = TagInfo("ColorTable"); | |
ti[34023] = TagInfo("ImageColorIndicator"); | |
ti[34024] = TagInfo("BackgroundColorIndicator"); | |
ti[34025] = TagInfo("ImageColorValue"); | |
ti[34026] = TagInfo("BackgroundColorValue"); | |
ti[34027] = TagInfo("PixelIntensityRange"); | |
ti[34028] = TagInfo("TransparencyIndicator"); | |
ti[34029] = TagInfo("ColorCharacterization"); | |
ti[34030] = TagInfo("HCUsage"); | |
ti[34264] = TagInfo("ModelTransformationTag"); | |
ti[34377] = TagInfo("Image Resource Blocks"); | |
ti[34665] = TagInfo(tt_IFD, 0, "ExifIFDPointer", false); | |
ti[34675] = TagInfo("InterColorProfile"); | |
ti[34735] = TagInfo("GeoKeyDirectoryTag"); | |
ti[34736] = TagInfo("GeoDoubleParamsTag"); | |
ti[34737] = TagInfo("GeoAsciiParamsTag"); | |
ti[34850] = TagInfo("ExposureProgram"); | |
ti[34852] = TagInfo("SpectralSensitivity"); | |
ti[34853] = TagInfo(tt_IFD, 0, "GPSInfoIFDPointer", false); | |
ti[34855] = TagInfo("ISOSpeedRatings"); | |
ti[34856] = TagInfo("OECF"); | |
ti[34857] = TagInfo("Interlace"); | |
ti[34858] = TagInfo("TimeZoneOffset"); | |
ti[34859] = TagInfo("SelfTimerMode"); | |
ti[36864] = TagInfo("ExifVersion"); | |
ti[36867] = TagInfo("DateTimeOriginal"); | |
ti[36868] = TagInfo("DateTimeDigitized"); | |
ti[37121] = TagInfo("ComponentsConfiguration"); | |
ti[37122] = TagInfo("CompressedBitsPerPixel"); | |
ti[37377] = TagInfo("ShutterSpeedValue"); | |
ti[37378] = TagInfo("ApertureValue"); | |
ti[37379] = TagInfo("BrightnessValue"); | |
ti[37380] = TagInfo("ExposureBiasValue"); | |
ti[37381] = TagInfo("MaxApertureValue"); | |
ti[37382] = TagInfo("SubjectDistance"); | |
ti[37383] = TagInfo("MeteringMode"); | |
ti[37384] = TagInfo("LightSource"); | |
ti[37385] = TagInfo("Flash"); | |
ti[37386] = TagInfo("FocalLength"); | |
ti[37387] = TagInfo("FlashEnergy_EP"); | |
ti[37388] = TagInfo("SpatialFrequencyResponse_EP"); | |
ti[37389] = TagInfo("Noise"); | |
ti[37390] = TagInfo("FocalPlaneXResolution_EP"); | |
ti[37391] = TagInfo("FocalPlaneYResolution_EP"); | |
ti[37392] = TagInfo("FocalPlaneResolutionUnit_EP"); | |
ti[37393] = TagInfo("ImageNumber"); | |
ti[37394] = TagInfo("SecurityClassification"); | |
ti[37395] = TagInfo("ImageHistory"); | |
ti[37396] = TagInfo("SubjectLocation_EP"); | |
ti[37397] = TagInfo("ExposureIndex_EP"); | |
ti[37398] = TagInfo("TIFF/EPStandardID"); | |
ti[37399] = TagInfo("SensingMethod_EP"); | |
ti[37500] = TagInfo("MakerNote"); | |
ti[37510] = TagInfo("UserComment"); | |
ti[37520] = TagInfo("SubsecTime"); | |
ti[37521] = TagInfo("SubsecTimeOriginal"); | |
ti[37522] = TagInfo("SubsecTimeDigitized"); | |
ti[37724] = TagInfo("ImageSourceData"); | |
ti[40960] = TagInfo("FlashpixVersion"); | |
ti[40961] = TagInfo("ColorSpace"); | |
ti[40962] = TagInfo("PixelXDimension"); | |
ti[40963] = TagInfo("PixelYDimension"); | |
ti[40964] = TagInfo("RelatedSoundFile"); | |
ti[40965] = TagInfo(tt_IFD, 0, "InteroperabilityIFDPointer", false); | |
ti[41483] = TagInfo("FlashEnergy"); | |
ti[41484] = TagInfo("SpatialFrequencyResponse"); | |
ti[41486] = TagInfo("FocalPlaneXResolution"); | |
ti[41487] = TagInfo("FocalPlaneYResolution"); | |
ti[41488] = TagInfo("FocalPlaneResolutionUnit"); | |
ti[41492] = TagInfo("SubjectLocation"); | |
ti[41493] = TagInfo("ExposureIndex"); | |
ti[41495] = TagInfo("SensingMethod"); | |
ti[41728] = TagInfo("FileSource"); | |
ti[41729] = TagInfo("SceneType"); | |
ti[41730] = TagInfo("CFAPattern"); | |
ti[41985] = TagInfo("CustomRendered"); | |
ti[41986] = TagInfo("ExposureMode"); | |
ti[41987] = TagInfo("WhiteBalance"); | |
ti[41988] = TagInfo("DigitalZoomRatio"); | |
ti[41989] = TagInfo("FocalLengthIn35mmFilm"); | |
ti[41990] = TagInfo("SceneCaptureType"); | |
ti[41991] = TagInfo("GainControl"); | |
ti[41992] = TagInfo("Contrast"); | |
ti[41993] = TagInfo("Saturation"); | |
ti[41994] = TagInfo("Sharpness"); | |
ti[41995] = TagInfo("DeviceSettingDescription"); | |
ti[41996] = TagInfo("SubjectDistanceRange"); | |
ti[42016] = TagInfo("ImageUniqueID"); | |
ti[50255] = TagInfo("Annotations"); | |
ti[50341] = TagInfo("PrintImageMatching"); | |
} | |
return ti; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great tool! I did come across one syntax issue at line 439 where the last entry in the
am
array requires an extra argument. It is currently stated as{0, NULL, false}
and my work around for it was to declare it as{0, 0, NULL, false}
instead