imagecore/formats/exif/exifcommon.cpp (246 lines of code) (raw):

/* * MIT License * * Copyright (c) 2017 Twitter * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "exifcommon.h" namespace imagecore { // Various exif tags ExifCommon::TagHeaderType ExifCommon::s_mTagHeaders[(int) ExifCommon::kTagId::kTagIdMax] = // table for mapping our internal types to actual exif IDs { {(uint16_t)ExifCommon::kExifTagID::kExifMake, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kMake, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifTagID::kExifModel, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kModel, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifTagID::kExifOrientation, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kUnsignedShort, 1, (uint16_t)ExifCommon::kTagId::kOrientation, &ExifCommon::validateOrientation}, {(uint16_t)ExifCommon::kExifTagID::kExifXResolution, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kUnsignedRational, 1, (uint16_t)ExifCommon::kTagId::kXResolution, &ExifCommon::validateU64Rational}, {(uint16_t)ExifCommon::kExifTagID::kExifYResolution, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kUnsignedRational, 1, (uint16_t)ExifCommon::kTagId::kYResolution, &ExifCommon::validateU64Rational}, {(uint16_t)ExifCommon::kExifTagID::kExifResolutionUnit, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kUnsignedShort, 1, (uint16_t)ExifCommon::kTagId::kResolutionUnit, &ExifCommon::validateResolutionUnit}, {(uint16_t)ExifCommon::kExifTagID::kExifSoftware, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kSoftware, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifTagID::kExifModifyDate, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kModifyDate, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifTagID::kExifExposureTime, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kUnsignedRational, 1, (uint16_t)ExifCommon::kTagId::kExposureTime, &ExifCommon::validateU64Rational}, {(uint16_t)ExifCommon::kExifTagID::kExifOffset, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kUnsignedLong, 1, (uint16_t)ExifCommon::kTagId::kExifSubDirectory, &ExifCommon::validateOffset}, {(uint16_t)ExifCommon::kExifTagID::kExifGPSInfo, ExifCommon::kDirectoryType::kDirectoryExif, ExifCommon::kTagType::kUnsignedLong, 1, (uint16_t)ExifCommon::kTagId::kGPSSubDirectory, &ExifCommon::validateOffset}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSLatitudeRef, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kGPSLatitudeRef, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSLatitude, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kUnsignedRational, 3, (uint16_t)ExifCommon::kTagId::kGPSLatitude, &ExifCommon::validateU64Rational3}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSLongitudeRef, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kGPSLongitudeRef, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSLongitude, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kUnsignedRational, 3, (uint16_t)ExifCommon::kTagId::kGPSLongitude, &ExifCommon::validateU64Rational3}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSAltitudeRef, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kSignedByte, 1, (uint16_t)ExifCommon::kTagId::kGPSAltitudeRef, &ExifCommon::validateAltitude}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSAltitude, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kUnsignedRational, 1, (uint16_t)ExifCommon::kTagId::kGPSAltitude, &ExifCommon::validateU64Rational}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSTimeStamp, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kUnsignedRational, 3, (uint16_t)ExifCommon::kTagId::kGPSTimeStamp, &ExifCommon::validateU64Rational3}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSSpeedRef, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kGPSSpeedRef, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSSpeed, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kUnsignedRational, 1, (uint16_t)ExifCommon::kTagId::kGPSSpeed, &ExifCommon::validateU64Rational}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSImgDirectionRef, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kGPSImgDirectionRef, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSImgDirection, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kUnsignedRational, 1, (uint16_t)ExifCommon::kTagId::kGPSImgDirection, &ExifCommon::validateU64Rational}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSDestBearingRef, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kGPSDestBearingRef, &ExifCommon::validateString}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSDestBearing, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kUnsignedRational, 1, (uint16_t)ExifCommon::kTagId::kGPSDestBearing, &ExifCommon::validateU64Rational}, {(uint16_t)ExifCommon::kExifGPSTagID::kExifGPSDateStamp, ExifCommon::kDirectoryType::kDirectoryGPS, ExifCommon::kTagType::kASCIIString, 1, (uint16_t)ExifCommon::kTagId::kGPSDateStamp, &ExifCommon::validateString}, }; ExifCommon::ExifCommon() { memset(m_TagHeaderLookup, 0xFF, sizeof(m_TagHeaderLookup)); for(uint32_t tagIndex = 0; tagIndex < (uint32_t)kTagId::kTagIdMax; tagIndex++) { // This verification will catch any tag headers that were added out of order #if DEBUG SECURE_ASSERT(s_mTagHeaders[tagIndex].m_internalId == tagIndex); // Sanity check #endif m_TagHeaderLookup[(uint32_t)s_mTagHeaders[tagIndex].m_dirType][s_mTagHeaders[tagIndex].m_exifId] = tagIndex; // create reverse lookup from exifId to our internal id. } } ExifCommon& ExifCommon::Instance() { static ExifCommon s_exifcommon; return s_exifcommon; } bool ExifCommon::TagHeaderType::relaxedTypeAndCountCheck(uint16_t exifType, uint32_t count, uint16_t signedType, uint16_t unsignedType) const { return (count == m_count) && ((exifType == signedType) || (exifType == unsignedType)); } bool ExifCommon::TagHeaderType::verifyTypeAndCount(unsigned short exifType, uint32_t count) const { switch(m_type) { // relax type-checking for signed vs unsigned of the same type, since there are images out there, that don't stick to the exact spec case kTagType::kUnsignedByte: case kTagType::kSignedByte: { // (1 byte per component) return relaxedTypeAndCountCheck(exifType, count, (uint16_t)kTagType::kUnsignedByte, (uint16_t)kTagType::kSignedByte); break; } case kTagType::kUnsignedShort: case kTagType::kSignedShort: { // (2 bytes per component) return relaxedTypeAndCountCheck(exifType, count, (uint16_t)kTagType::kUnsignedShort, (uint16_t)kTagType::kSignedShort); break; } case kTagType::kUnsignedLong: case kTagType::kSignedLong: { // (4 bytes per component) return relaxedTypeAndCountCheck(exifType, count, (uint16_t)kTagType::kUnsignedLong, (uint16_t)kTagType::kSignedLong); break; } case kTagType::kUnsignedRational: case kTagType::kSignedRational: { // (8 bytes per component) return relaxedTypeAndCountCheck(exifType, count, (uint16_t)kTagType::kUnsignedRational, (uint16_t)kTagType::kSignedRational); break; } case kTagType::kSingle: { // (4 bytes per component) return relaxedTypeAndCountCheck(exifType, count, (uint16_t)kTagType::kSingle, (uint16_t)kTagType::kSingle); break; } case kTagType::kDouble: { // (8 bytes per component) return relaxedTypeAndCountCheck(exifType, count, (uint16_t)kTagType::kDouble, (uint16_t)kTagType::kDouble); break; } case kTagType::kASCIIString: { if(((ExifCommon::kTagType)exifType != kTagType::kASCIIString) || (count >= kMaxExifStringLength) || (count == 0)) { return false; } break; } default: { return false; } } return true; } const ExifCommon::TagHeaderType* ExifCommon::getTagHeader(kTagId tagId) { return &s_mTagHeaders[(uint16_t)tagId]; } ExifCommon::kDirectoryType ExifCommon::getDirectoryType(kExifTagID exifDirectoryTag) { if(exifDirectoryTag == ExifCommon::kExifTagID::kExifOffset) { return ExifCommon::kDirectoryType::kDirectoryExif; } else { return ExifCommon::kDirectoryType::kDirectoryGPS; } } int32_t ExifCommon::getTagId(uint32_t dirType, uint32_t exifDirectoryTag) { return m_TagHeaderLookup[dirType][exifDirectoryTag]; } //----------------------------------------------------------------------------------------- // All validation functions bool ExifCommon::validateOrientation(const void* valueAddress, uint32_t) { unsigned short value = *(const unsigned short*)valueAddress; if( value >= kImageOrientation_Up && value <= kImageOrientation_Right ) { return true; } return false; } bool ExifCommon::validateString(const void* valueAddress, uint32_t count) { const ExifCommon::ExifString* string = (const ExifCommon::ExifString*)valueAddress; if (string->m_length != count) { // failed to read the string return false; } if(string->m_length > 0) { return (string->m_string[count - 1] == 0); // should be null terminated } else { return false; // string of 0 length } } bool ExifCommon::validateU64Rational(const void* valueAddress, uint32_t count) { Rational<uint32_t> value = *(Rational<uint32_t>*)valueAddress; if(value.m_Denominator == 0) { // divide by 0 return false; } if(((int32_t)value.m_Nominator < 0) || ((int32_t)value.m_Denominator < 0)) { return false; } return true; } bool ExifCommon::validateU64Rational3(const void* valueAddress, uint32_t count) { Rational<uint32_t>* value = (Rational<uint32_t>*)valueAddress; if(!validateU64Rational(value, count)) { return false; } value++; if(!validateU64Rational(value, count)) { return false; } value++; if(!validateU64Rational(value, count)) { return false; } return true; } bool ExifCommon::validateResolutionUnit(const void* valueAddress, uint32_t count) { unsigned short value = *(const unsigned short*)valueAddress; if( value >= EResolutionUnit::kNone && value <= EResolutionUnit::kCm ) { return true; } return false; } bool ExifCommon::validateOffset(const void* valueAddress, uint32_t count) { return true; } bool ExifCommon::validateAltitude(const void* valueAddress, uint32_t count) { int8_t value = *(int8_t*)valueAddress; if( value >= EAltitudeRef::kAboveSeaLevel && value <= EAltitudeRef::kBelowSeaLevel ) { return true; } return false; } void ExifCommon::IFDStructure::write(MemoryStreamWriter& memoryStream, uint32_t offset) { memoryStream.put_short_advance(tag); memoryStream.put_short_advance(type); memoryStream.put_uint_advance(count); if(isOffset) { memoryStream.put_uint_advance(value_offset + offset); } else { switch(type) { case (int)ExifCommon::kTagType::kUnsignedByte: case (int)ExifCommon::kTagType::kSignedByte:{ memoryStream.put_byte_advance((uint8_t)value_offset); memoryStream.put_byte_advance((uint8_t)0); memoryStream.put_short_advance((uint16_t)0); break; } case (int)ExifCommon::kTagType::kUnsignedShort: { memoryStream.put_short_advance((uint16_t)value_offset); memoryStream.put_short_advance((uint16_t)0); break; } case (int)ExifCommon::kTagType::kUnsignedLong: { memoryStream.put_uint_advance(value_offset); break; } case (int)ExifCommon::kTagType::kASCIIString: { // string of length 4 or less const uint8_t* pStr = (const uint8_t*)&value_offset; memoryStream.put_raw_data_advance(pStr, 4); break; } default: { memoryStream.put_uint_advance(value_offset); break; } } } } ExifCommon::ExifDirectory::ExifDirectory(bool is_be) : m_numEntries(0) { m_entries = new IFDStructure[kMaxExifDirectoryEntries]; metaDataMemory = new uint8_t[kMaxMetaDataSize]; metaDataWriter = new MemoryStreamWriter(metaDataMemory, kMaxMetaDataSize, is_be); } ExifCommon::ExifDirectory::~ExifDirectory() { delete[] m_entries; delete[] metaDataMemory; delete metaDataWriter; } ExifCommon::IFDStructure* ExifCommon::ExifDirectory::getNewEntry() { SECURE_ASSERT(m_numEntries < kMaxExifDirectoryEntries); IFDStructure* entry = &m_entries[m_numEntries]; m_numEntries++; return entry; } void ExifCommon::ExifDirectory::write(MemoryStreamWriter& memoryStream, uint32_t& offset) const { // write all directory info if(m_numEntries > 0) { offset += (2 + m_numEntries * 12); memoryStream.put_short_advance(m_numEntries); for(uint16_t entryIndex = 0; entryIndex < m_numEntries; entryIndex++) { m_entries[entryIndex].write(memoryStream, offset); } memoryStream.put_raw_data_advance(metaDataWriter->getData(), metaDataWriter->getSize()); offset += metaDataWriter->getSize(); } } }