commit a467b687dc900c69ab2ff8ae1cebc6067fcccb53 Author: selim mustafaev Date: Wed Sep 12 14:20:55 2018 +0300 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f11b75 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..e9090f0 --- /dev/null +++ b/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/TIFF.cpp b/TIFF.cpp new file mode 100644 index 0000000..d405625 --- /dev/null +++ b/TIFF.cpp @@ -0,0 +1,236 @@ +#include "TIFF.h" +#include +#include +#include + +static const std::map 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 +std::ostream& operator<<(std::ostream& stream, const RationalType& 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 +std::ostream& operator<<(std::ostream& stream, const std::vector& vector) { + stream << " (" + std::to_string(vector.size()) + ")"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const std::vector& vector) { + stream << std::to_string(vector[0]) + "x" + std::to_string(vector[1]); + return stream; +} + +template +void ImageFileDirectory::parseValueImpl(char *filePtr, IfdEntry *entry) { + size_t sizeOfValue = sizeOfType(entry->type)*entry->numberOfValues; + bool isArray = entry->numberOfValues > 1; + T* dataPtr = reinterpret_cast(sizeOfValue <= 4 ? (char*)&entry->valueOrOffset : filePtr + entry->valueOrOffset); + + EntryValueType value; + if (isArray) { + value = std::vector(dataPtr, dataPtr + entry->numberOfValues); + } else { + value = *dataPtr; + } + + _entriesMap.emplace(entry->tag, value); +} + +template +void ImageFileDirectory::parseValue(char *filePtr, IfdEntry *entry) { + switch (entry->tag) { + case COMPRESSION: parseValueImpl(filePtr, entry); break; + case PHOTOMETRIC_INTERPRETATION: parseValueImpl(filePtr, entry); break; + case RESOLUTION_UNIT: parseValueImpl(filePtr, entry); break; + case ORIENTATION: parseValueImpl(filePtr, entry); break; + case PLANAR_CONFIGURATION: parseValueImpl(filePtr, entry); break; + case CFA_REPEAT_PATTERN_DIM: parseValueImpl(filePtr, entry); break; + default: parseValueImpl(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(filePtr, &entry); break; + case SHORT: parseValue(filePtr, &entry); break; + case LONG: parseValue(filePtr, &entry); break; + case RATIONAL: parseValue(filePtr, &entry); break; + case SBYTE: parseValue(filePtr, &entry); break; + case SSHORT: parseValue(filePtr, &entry); break; + case SLONG: parseValue(filePtr, &entry); break; + case SRATIONAL: parseValue(filePtr, &entry); break; + case UNDEFINED: parseValue(filePtr, &entry); break; + case FLOAT: parseValue(filePtr, &entry); break; + case DOUBLE: parseValue(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::operator[](TiffTag tag) const { + auto iter = _entriesMap.find(tag); + if(iter != _entriesMap.end()) + return std::optional(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(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 TIFF::ifds() const { + return _ifds; +} diff --git a/TIFF.h b/TIFF.h new file mode 100644 index 0000000..a5f2ffe --- /dev/null +++ b/TIFF.h @@ -0,0 +1,168 @@ +#ifndef DNGINFO_TIFF_H +#define DNGINFO_TIFF_H + +#include +#include +#include +#include +#include +#include + +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 +struct RationalType { + T numerator; + T denominator; +}; + +using Rational = RationalType; +using SRational = RationalType; +typedef uint16_t CFAPatternDim; + +std::ostream& operator<<(std::ostream& stream, Compression compression); +std::ostream& operator<<(std::ostream& stream, PhotometricInterpretation pr); +template std::ostream& operator<<(std::ostream& stream, const RationalType& 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 std::ostream& operator<<(std::ostream& stream, const std::vector& vector); + +class ImageFileDirectory { +public: + using EntryValueType = std::variant, std::vector, + ResolutionUnit, std::vector, Orientation, std::vector, + PlanarConfiguration, std::vector, + std::vector, std::vector, std::vector, std::vector, + std::vector, std::vector, std::vector, std::vector, + std::vector, std::vector>; + +public: + explicit ImageFileDirectory(char* ptr, char* filePtr); + uint32_t nextOffset() const; + std::optional operator[](TiffTag tag) const; + void dumpInfo() const; + +private: + size_t sizeOfType(FieldType type) const; + template void parseValue(char* filePtr, IfdEntry* entry); + template void parseValueImpl(char* filePtr, IfdEntry* entry); + +private: + uint16_t _entriesCount; + IfdEntry* _entries; + uint32_t _nextIfdOffset; + std::map _entriesMap; +}; + +class TIFF { +public: + explicit TIFF(std::string fileName); + std::vector ifds() const; + +private: + std::unique_ptr _data; + std::vector _ifds; +}; + +#endif //DNGINFO_TIFF_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..7479d0a --- /dev/null +++ b/main.cpp @@ -0,0 +1,20 @@ +#include +#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; +} \ No newline at end of file