Initial commit
This commit is contained in:
commit
a467b687dc
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.idea/
|
||||
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(dngInfo)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(dngInfo main.cpp TIFF.cpp TIFF.h)
|
||||
236
TIFF.cpp
Normal file
236
TIFF.cpp
Normal file
@ -0,0 +1,236 @@
|
||||
#include "TIFF.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
static const std::map<TiffTag, std::string> tagDescriptions = {
|
||||
{NEW_SUBFILE_TYPE, "New subfile type"},
|
||||
{IMAGE_WIDTH, "Image width"},
|
||||
{IMAGE_LENGTH, "Image height"},
|
||||
{BITS_PER_SAMPLE, "Bits per sample"},
|
||||
{COMPRESSION, "Compression"},
|
||||
{PHOTOMETRIC_INTERPRETATION, "Photometric interpretation"},
|
||||
{IMAGE_DESCRIPTION, "Image description"},
|
||||
{MANUFACTURER, "Manufacturer"},
|
||||
{MODEL, "Model"},
|
||||
{STRIP_OFFSETS, "Strip offsets"},
|
||||
{ORIENTATION, "Orientation"},
|
||||
{SAMPLES_PER_PIXEL, "Samples per pixel"},
|
||||
{ROWS_PER_STRIP, "Rows per strip"},
|
||||
{STRIP_BYTE_COUNTS, "Strip byte counts"},
|
||||
{X_RESOLUTION, "X resolution"},
|
||||
{Y_RESOLUTION, "Y resolution"},
|
||||
{PLANAR_CONFIGURATION, "Planar configuration"},
|
||||
{RESOLUTION_UNIT, "Resolution unit"},
|
||||
{SOFTWARE, "Software"},
|
||||
{DATE_TIME, "Date/Time"},
|
||||
{XML_PACKET, "XML Packet"},
|
||||
{CFA_REPEAT_PATTERN_DIM, "CFA repeat pattern"},
|
||||
{CFA_PATTERN, "CFA pattern"}
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &stream, Compression compression) {
|
||||
switch (compression) {
|
||||
case NO_COMPRESSION: stream << "no compression"; break;
|
||||
case HUFFMAN: stream << "1-Dimensional modified huffman run length encoding"; break;
|
||||
case PACK_BITS: stream << "PackBits"; break;
|
||||
default: stream << "undefined"; break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, PhotometricInterpretation pr) {
|
||||
switch(pr) {
|
||||
case WHITE_IS_ZERO: stream << "white is zero"; break;
|
||||
case BLACK_IS_ZERO: stream << "black is zero"; break;
|
||||
case RGB: stream << "RGB"; break;
|
||||
case PALETTE_COLOR: stream << "palette color"; break;
|
||||
case TRANSPARENCY_MASK: stream << "transparency mask"; break;
|
||||
case YCBCR: stream << "YCbCr (YUV)"; break;
|
||||
case COLOR_FILTER_ARRAY: stream << "color filter array"; break;
|
||||
case LINEAR_RAW: stream << "linear raw"; break;
|
||||
default: stream << "undefined"; break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
template<typename T>
|
||||
std::ostream& operator<<(std::ostream& stream, const RationalType<T>& rational) {
|
||||
stream << std::to_string(rational.numerator) + "/" + std::to_string(rational.denominator);
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, ResolutionUnit unit) {
|
||||
switch(unit) {
|
||||
case NO_UNIT: stream << "no unit"; break;
|
||||
case INCH: stream << "inch"; break;
|
||||
case CENTIMETER: stream << "centimeter"; break;
|
||||
default: stream << "undefined"; break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, Orientation orientation) {
|
||||
switch (orientation) {
|
||||
case NORMAL: stream << "normal"; break;
|
||||
case FLIP_HORIZONTAL: stream << "flip horizontal"; break;
|
||||
case FLIP_BOTH: stream << "flip both"; break;
|
||||
case FLIP_VERTICAL: stream << "flip vertical"; break;
|
||||
case NORMAL_ROTATED: stream << "normal (rotated)"; break;
|
||||
case FLIP_HORIZONTAL_ROTATED: stream << "flip horizontal (rotated)"; break;
|
||||
case FLIP_BOTH_ROTATED: stream << "flip both (rotated)"; break;
|
||||
case FLIP_VERTICAL_ROTATED: stream << "flip vertical (rotated)"; break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, PlanarConfiguration conf) {
|
||||
switch (conf) {
|
||||
case CHUNKY: stream << "chunky"; break;
|
||||
case PLANAR: stream << "planar"; break;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vector) {
|
||||
stream << "<vector> (" + std::to_string(vector.size()) + ")";
|
||||
return stream;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const std::vector<CFAPatternDim>& vector) {
|
||||
stream << std::to_string(vector[0]) + "x" + std::to_string(vector[1]);
|
||||
return stream;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ImageFileDirectory::parseValueImpl(char *filePtr, IfdEntry *entry) {
|
||||
size_t sizeOfValue = sizeOfType(entry->type)*entry->numberOfValues;
|
||||
bool isArray = entry->numberOfValues > 1;
|
||||
T* dataPtr = reinterpret_cast<T*>(sizeOfValue <= 4 ? (char*)&entry->valueOrOffset : filePtr + entry->valueOrOffset);
|
||||
|
||||
EntryValueType value;
|
||||
if (isArray) {
|
||||
value = std::vector<T>(dataPtr, dataPtr + entry->numberOfValues);
|
||||
} else {
|
||||
value = *dataPtr;
|
||||
}
|
||||
|
||||
_entriesMap.emplace(entry->tag, value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ImageFileDirectory::parseValue(char *filePtr, IfdEntry *entry) {
|
||||
switch (entry->tag) {
|
||||
case COMPRESSION: parseValueImpl<Compression>(filePtr, entry); break;
|
||||
case PHOTOMETRIC_INTERPRETATION: parseValueImpl<PhotometricInterpretation>(filePtr, entry); break;
|
||||
case RESOLUTION_UNIT: parseValueImpl<ResolutionUnit>(filePtr, entry); break;
|
||||
case ORIENTATION: parseValueImpl<Orientation>(filePtr, entry); break;
|
||||
case PLANAR_CONFIGURATION: parseValueImpl<PlanarConfiguration>(filePtr, entry); break;
|
||||
case CFA_REPEAT_PATTERN_DIM: parseValueImpl<CFAPatternDim>(filePtr, entry); break;
|
||||
default: parseValueImpl<T>(filePtr, entry); break;
|
||||
}
|
||||
}
|
||||
|
||||
ImageFileDirectory::ImageFileDirectory(char* ptr, char* filePtr) {
|
||||
_entriesCount = *(uint16_t*)ptr;
|
||||
_entries = (IfdEntry*)(ptr + 2);
|
||||
_nextIfdOffset = *(uint32_t*)(ptr + 2 + _entriesCount * sizeof(IfdEntry));
|
||||
|
||||
for(size_t i = 0; i < _entriesCount; ++i) {
|
||||
auto entry = _entries[i];
|
||||
|
||||
switch (entry.type) {
|
||||
case ASCII: _entriesMap.emplace(entry.tag, EntryValueType(std::string(filePtr + entry.valueOrOffset))); break;
|
||||
case BYTE: parseValue<uint8_t>(filePtr, &entry); break;
|
||||
case SHORT: parseValue<uint16_t>(filePtr, &entry); break;
|
||||
case LONG: parseValue<uint32_t>(filePtr, &entry); break;
|
||||
case RATIONAL: parseValue<Rational>(filePtr, &entry); break;
|
||||
case SBYTE: parseValue<int8_t>(filePtr, &entry); break;
|
||||
case SSHORT: parseValue<int16_t>(filePtr, &entry); break;
|
||||
case SLONG: parseValue<int32_t>(filePtr, &entry); break;
|
||||
case SRATIONAL: parseValue<SRational>(filePtr, &entry); break;
|
||||
case UNDEFINED: parseValue<uint8_t>(filePtr, &entry); break;
|
||||
case FLOAT: parseValue<float>(filePtr, &entry); break;
|
||||
case DOUBLE: parseValue<double>(filePtr, &entry); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ImageFileDirectory::nextOffset() const {
|
||||
return _nextIfdOffset;
|
||||
}
|
||||
|
||||
size_t ImageFileDirectory::sizeOfType(FieldType type) const {
|
||||
switch (type) {
|
||||
case BYTE: return 1;
|
||||
case ASCII: return 1;
|
||||
case SHORT: return 2;
|
||||
case LONG: return 4;
|
||||
case RATIONAL: return 8;
|
||||
case SBYTE: return 1;
|
||||
case UNDEFINED: return 1;
|
||||
case SSHORT: return 2;
|
||||
case SLONG: return 4;
|
||||
case SRATIONAL: return 8;
|
||||
case FLOAT: return 4;
|
||||
case DOUBLE: return 8;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::optional<ImageFileDirectory::EntryValueType> ImageFileDirectory::operator[](TiffTag tag) const {
|
||||
auto iter = _entriesMap.find(tag);
|
||||
if(iter != _entriesMap.end())
|
||||
return std::optional<ImageFileDirectory::EntryValueType>(iter->second);
|
||||
else
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void ImageFileDirectory::dumpInfo() const {
|
||||
std::cout << "=== IFD ====================================================================================================================" << std::endl;
|
||||
std::cout << "| Tag | Description | Value |" << std::endl;
|
||||
std::cout << "----------------------------------------------------------------------------------------------------------------------------" << std::endl;
|
||||
std::ios state(nullptr);
|
||||
state.copyfmt(std::cout);
|
||||
for(auto const& [key, val]: _entriesMap) {
|
||||
std::visit([key](auto&& arg) {
|
||||
auto iter = tagDescriptions.find(key);
|
||||
std::string description = iter != tagDescriptions.end() ? iter->second : "";
|
||||
std::cout << "| " << std::left << std::setw(5) << key << " | " << std::setw(29) << description << " | " << std::setw(80) << arg << " |" << std::endl;
|
||||
}, val);
|
||||
}
|
||||
std::cout.copyfmt(state);
|
||||
}
|
||||
|
||||
TIFF::TIFF(std::string fileName) {
|
||||
std::ifstream stream(fileName, std::ios::binary | std::ios::ate);
|
||||
std::streamsize size = stream.tellg();
|
||||
stream.seekg(0, std::ios::beg);
|
||||
_data = std::make_unique<char[]>(size);
|
||||
|
||||
if(!stream.read((char*)_data.get(), size)) {
|
||||
throw std::runtime_error("error opening TIFF file: " + fileName);
|
||||
}
|
||||
|
||||
auto header = (TIFFHeader*)_data.get();
|
||||
if(header->magic != TIFF_MAGIC) {
|
||||
throw std::runtime_error("wrong magic number");
|
||||
}
|
||||
|
||||
uint32_t ifdOffset = header->firstIfdOffset;
|
||||
while(ifdOffset != 0) {
|
||||
auto ifd = ImageFileDirectory(_data.get() + ifdOffset, _data.get());
|
||||
_ifds.push_back(ifd);
|
||||
ifdOffset = ifd.nextOffset();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<ImageFileDirectory> TIFF::ifds() const {
|
||||
return _ifds;
|
||||
}
|
||||
168
TIFF.h
Normal file
168
TIFF.h
Normal file
@ -0,0 +1,168 @@
|
||||
#ifndef DNGINFO_TIFF_H
|
||||
#define DNGINFO_TIFF_H
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <variant>
|
||||
|
||||
constexpr int16_t TIFF_MAGIC = 42;
|
||||
|
||||
enum ByteOrder: uint16_t {
|
||||
LE = 0x4949,
|
||||
BE = 0x4d4d
|
||||
};
|
||||
|
||||
enum FieldType: uint16_t {
|
||||
BYTE = 1,
|
||||
ASCII = 2,
|
||||
SHORT = 3,
|
||||
LONG = 4,
|
||||
RATIONAL = 5,
|
||||
SBYTE = 6,
|
||||
UNDEFINED = 7,
|
||||
SSHORT = 8,
|
||||
SLONG = 9,
|
||||
SRATIONAL = 10,
|
||||
FLOAT = 11,
|
||||
DOUBLE = 12
|
||||
};
|
||||
|
||||
enum TiffTag: uint16_t {
|
||||
NEW_SUBFILE_TYPE = 0xFE,
|
||||
IMAGE_WIDTH = 0x100,
|
||||
IMAGE_LENGTH = 0x101,
|
||||
BITS_PER_SAMPLE = 0x102,
|
||||
COMPRESSION = 0x103,
|
||||
PHOTOMETRIC_INTERPRETATION = 0x106,
|
||||
IMAGE_DESCRIPTION = 0x10E,
|
||||
MANUFACTURER = 0x10F,
|
||||
MODEL = 0x110,
|
||||
STRIP_OFFSETS = 0x111,
|
||||
ORIENTATION = 0x112,
|
||||
SAMPLES_PER_PIXEL = 0x115,
|
||||
ROWS_PER_STRIP = 0x116,
|
||||
STRIP_BYTE_COUNTS = 0x117,
|
||||
X_RESOLUTION = 0x11A,
|
||||
Y_RESOLUTION = 0x11B,
|
||||
PLANAR_CONFIGURATION = 0x11C,
|
||||
RESOLUTION_UNIT = 0x128,
|
||||
SOFTWARE = 0x131,
|
||||
DATE_TIME = 0x132,
|
||||
XML_PACKET = 0x2BC,
|
||||
CFA_REPEAT_PATTERN_DIM = 0x828D,
|
||||
CFA_PATTERN = 0x828E
|
||||
|
||||
};
|
||||
|
||||
enum PhotometricInterpretation: uint16_t {
|
||||
WHITE_IS_ZERO = 0,
|
||||
BLACK_IS_ZERO = 1,
|
||||
RGB = 2,
|
||||
PALETTE_COLOR = 3,
|
||||
TRANSPARENCY_MASK = 4,
|
||||
YCBCR = 6,
|
||||
COLOR_FILTER_ARRAY = 32803,
|
||||
LINEAR_RAW = 34892
|
||||
};
|
||||
|
||||
enum Compression: uint16_t {
|
||||
NO_COMPRESSION = 1,
|
||||
HUFFMAN = 2,
|
||||
PACK_BITS = 32773
|
||||
};
|
||||
|
||||
enum ResolutionUnit: uint16_t {
|
||||
NO_UNIT = 1,
|
||||
INCH = 2,
|
||||
CENTIMETER = 3
|
||||
};
|
||||
|
||||
enum Orientation: uint16_t {
|
||||
NORMAL = 1,
|
||||
FLIP_HORIZONTAL = 2,
|
||||
FLIP_BOTH = 3,
|
||||
FLIP_VERTICAL = 4,
|
||||
NORMAL_ROTATED = 5,
|
||||
FLIP_HORIZONTAL_ROTATED = 6,
|
||||
FLIP_BOTH_ROTATED = 7,
|
||||
FLIP_VERTICAL_ROTATED = 8
|
||||
};
|
||||
|
||||
enum PlanarConfiguration : uint16_t {
|
||||
CHUNKY = 1,
|
||||
PLANAR = 2
|
||||
};
|
||||
|
||||
struct TIFFHeader {
|
||||
ByteOrder byteOrder;
|
||||
uint16_t magic;
|
||||
uint32_t firstIfdOffset;
|
||||
};
|
||||
|
||||
struct IfdEntry {
|
||||
TiffTag tag;
|
||||
FieldType type;
|
||||
uint32_t numberOfValues;
|
||||
uint32_t valueOrOffset;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct RationalType {
|
||||
T numerator;
|
||||
T denominator;
|
||||
};
|
||||
|
||||
using Rational = RationalType<uint32_t>;
|
||||
using SRational = RationalType<int32_t>;
|
||||
typedef uint16_t CFAPatternDim;
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, Compression compression);
|
||||
std::ostream& operator<<(std::ostream& stream, PhotometricInterpretation pr);
|
||||
template<typename T> std::ostream& operator<<(std::ostream& stream, const RationalType<T>& rational);
|
||||
std::ostream& operator<<(std::ostream& stream, ResolutionUnit unit);
|
||||
std::ostream& operator<<(std::ostream& stream, Orientation orientation);
|
||||
std::ostream& operator<<(std::ostream& stream, PlanarConfiguration conf);
|
||||
template<typename T> std::ostream& operator<<(std::ostream& stream, const std::vector<T>& vector);
|
||||
|
||||
class ImageFileDirectory {
|
||||
public:
|
||||
using EntryValueType = std::variant<uint8_t, uint16_t, uint32_t, Rational, int8_t, int16_t, int32_t, SRational, float, double, std::string,
|
||||
Compression, PhotometricInterpretation, std::vector<Compression>, std::vector<PhotometricInterpretation>,
|
||||
ResolutionUnit, std::vector<ResolutionUnit>, Orientation, std::vector<Orientation>,
|
||||
PlanarConfiguration, std::vector<PlanarConfiguration>,
|
||||
std::vector<uint8_t>, std::vector<uint16_t>, std::vector<uint32_t>, std::vector<Rational>,
|
||||
std::vector<int8_t>, std::vector<int16_t>, std::vector<int32_t>, std::vector<SRational>,
|
||||
std::vector<float>, std::vector<double>>;
|
||||
|
||||
public:
|
||||
explicit ImageFileDirectory(char* ptr, char* filePtr);
|
||||
uint32_t nextOffset() const;
|
||||
std::optional<EntryValueType> operator[](TiffTag tag) const;
|
||||
void dumpInfo() const;
|
||||
|
||||
private:
|
||||
size_t sizeOfType(FieldType type) const;
|
||||
template<typename T> void parseValue(char* filePtr, IfdEntry* entry);
|
||||
template<typename T> void parseValueImpl(char* filePtr, IfdEntry* entry);
|
||||
|
||||
private:
|
||||
uint16_t _entriesCount;
|
||||
IfdEntry* _entries;
|
||||
uint32_t _nextIfdOffset;
|
||||
std::map<TiffTag, EntryValueType> _entriesMap;
|
||||
};
|
||||
|
||||
class TIFF {
|
||||
public:
|
||||
explicit TIFF(std::string fileName);
|
||||
std::vector<ImageFileDirectory> ifds() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> _data;
|
||||
std::vector<ImageFileDirectory> _ifds;
|
||||
};
|
||||
|
||||
#endif //DNGINFO_TIFF_H
|
||||
20
main.cpp
Normal file
20
main.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
#include <iostream>
|
||||
#include "TIFF.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "too few arguments" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string fileName = argv[1];
|
||||
|
||||
TIFF tiff(fileName);
|
||||
for(auto const& ifd: tiff.ifds()) {
|
||||
ifd.dumpInfo();
|
||||
}
|
||||
|
||||
// system("pause");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user