Skip to content

Instantly share code, notes, and snippets.

@espresso3389
Last active July 15, 2022 03:24
Show Gist options
  • Save espresso3389/bf88e83cf6120ed61950d69ace4c0516 to your computer and use it in GitHub Desktop.
Save espresso3389/bf88e83cf6120ed61950d69ace4c0516 to your computer and use it in GitHub Desktop.
JPEG parser
#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;
}
@DxExWxExY
Copy link

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

@espresso3389
Copy link
Author

@DxExWxExY Fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment