imagecore/formats/exif/exifreader.cpp (169 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 "imagecore.h"
#include "exifreader.h"
#include <new>
namespace imagecore {
ExifReader::ExifReader() : m_cacheOffset(0),
m_TIFFHeader(nullptr),
m_dataSize(0),
m_valid(false)
{
m_cacheData = new uint8_t[kCacheSize];
}
ExifReader::~ExifReader()
{
if(m_cacheData) {
delete [] m_cacheData;
}
}
void ExifReader::initialize(const uint8_t* EXIFData, uint32_t dataSize)
{
SECURE_ASSERT(m_TIFFHeader == nullptr);
m_TIFFHeader = EXIFData;
m_dataSize = dataSize;
memset(m_cachedEntries, 0, sizeof(m_cachedEntries));
m_isBe = ((uint32_t)m_TIFFHeader[0] << 8 | ((uint32_t)m_TIFFHeader[1])) == EXIF_BIG_ENDIAN;
MemoryStreamReader memoryStream(m_TIFFHeader, m_dataSize, m_isBe);
memoryStream.advance(2);
uint32_t tfif_marker = memoryStream.get_short_advance();
if( tfif_marker != TFIF_MARKER ) {
return;
}
uint32_t length = memoryStream.get_uint_advance();
if( length != kHeaderSize) {
m_valid = false;
return;
}
m_valid = true;
m_rootDirectory = directoryInfoType(m_TIFFHeader + kHeaderSize, ExifCommon::kDirectoryType::kDirectoryExif);
m_directoryInfo = &m_rootDirectory;
// IDF1 offset
MemoryStreamReader IDF1Stream(m_directoryInfo->m_data, (uint16_t)(m_dataSize - (m_directoryInfo->m_data - m_TIFFHeader)), m_isBe);
uint16_t dirSize = IDF1Stream.get_short_advance();
IDF1Stream.seek(2 + dirSize * kDirectoryEntrySize); // advance past the last entry
uint32_t IDF1Offset = IDF1Stream.get_uint();
// The 0th IFD Offset of Next IFD indicates the start address of the 1st IFD (thumbnail images). When the 1st IFD is
// not recorded, the 0th IFD Offset of Next IFD terminates with 00000000.H.
// In some sample images it wasn't 0, but contained invalid offset
if(IDF1Stream.isLastReadValid() && (IDF1Offset != 0) && (validateOffset(IDF1Offset, sizeof(uint16_t) + kDirectoryEntrySize))) {
m_directoryQueue.push(m_TIFFHeader + IDF1Offset, ExifCommon::kDirectoryType::kDirectoryExif);
}
m_tagIndex = 0;
m_cacheOffset = 0;
}
void ExifReader::readValue(uint8_t* buffer, uint32_t& size, MemoryStreamReader& memoryStream, uint16_t tagType, uint32_t tagCount)
{
switch ((ExifCommon::kTagType)tagType) {
case ExifCommon::kTagType::kASCIIString : {
size = sizeof(ExifCommon::ExifString);
ExifCommon::ExifString* value = new(buffer) ExifCommon::ExifString();
readValue(*value, memoryStream, tagCount);
break;
}
case ExifCommon::kTagType::kSignedByte:
case ExifCommon::kTagType::kUnsignedByte: {
size = sizeof(uint8_t);
int8_t* value = (int8_t*)buffer;
readValue(*value, memoryStream, tagCount);
break;
}
case ExifCommon::kTagType::kSignedShort:
case ExifCommon::kTagType::kUnsignedShort: {
size = sizeof(uint16_t);
uint16_t* value = (uint16_t*)buffer;
readValue(*value, memoryStream, tagCount);
break;
}
case ExifCommon::kTagType::kUnsignedRational: {
if(tagCount == 1) {
size = sizeof(Rational<uint32_t>); // use sizeof here, because that is how many bytes will be copied to the final return type
Rational<uint32_t>* value = new(buffer) Rational<uint32_t>(); // use placement new so that constructor gets called and 'value' is initialized, since readValue can fail (Valgrind fix)
readValue(*value, memoryStream, tagCount);
value->m_Signed = false; // explicitly set the sign flag
} else {
size = sizeof(ExifCommon::ExifU64Rational3);
ExifCommon::ExifU64Rational3* value = new(buffer) ExifCommon::ExifU64Rational3();
readValue(*value, memoryStream, tagCount);
value->setSign(false);
}
break;
}
case ExifCommon::kTagType::kSignedRational: {
if(tagCount == 1) {
size = sizeof(Rational<uint32_t>);
Rational<uint32_t>* value = new(buffer) Rational<uint32_t>();
readValue(*value, memoryStream, tagCount);
value->m_Signed = true;
} else {
size = sizeof(ExifCommon::ExifU64Rational3);
ExifCommon::ExifU64Rational3* value = new(buffer) ExifCommon::ExifU64Rational3();
readValue(*value, memoryStream, tagCount);
value->setSign(true);
}
break;
}
default:
SECURE_ASSERT(0); // We have nothing that requires support for these types yet
break;
}
}
void ExifReader::readValue(int8_t& value, MemoryStreamReader& memoryStream, uint32_t) const
{
value = memoryStream.get_byte();
}
void ExifReader::readValue(uint16_t& value, MemoryStreamReader& memoryStream, uint32_t) const
{
value = memoryStream.get_short();
}
void ExifReader::readValue(ExifCommon::ExifString& value, MemoryStreamReader& memoryStream, uint32_t count) const
{
if(count <= 4) {// values of 4 bytes in length or less are stored directly
for( uint32_t i = 0; i < count; i++) {
value.m_string[i] = memoryStream.get_byte_advance();
}
} else {
count = count < ExifCommon::kMaxExifStringLength ? count : ExifCommon::kMaxExifStringLength;
uint32_t offset = memoryStream.get_uint();
if(!validateOffset(offset, count)) {
return;
}
MemoryStreamReader mstream(&m_TIFFHeader[offset], count, m_isBe);
for( uint32_t i = 0; i < count; i++) {
value.m_string[i] = mstream.get_byte_advance();
}
}
value.m_length = count;
}
void ExifReader::readValue(Rational<uint32_t>& value, MemoryStreamReader& memoryStream, uint32_t count) const
{
uint32_t offset = memoryStream.get_uint();
if(validateOffset(offset, count * Rational<uint32_t>::sizeOf())) {
MemoryStreamReader mstream(&m_TIFFHeader[offset], count * Rational<uint32_t>::sizeOf(), m_isBe);
value.m_Nominator = mstream.get_uint_advance();
value.m_Denominator = mstream.get_uint();
}
}
void ExifReader::readValue(ExifCommon::ExifU64Rational3& value, MemoryStreamReader& memoryStream, uint32_t count) const
{
uint32_t offset = memoryStream.get_uint();
if(validateOffset(offset, ExifCommon::ExifU64Rational3::sizeOf())) {
MemoryStreamReader mstream(&m_TIFFHeader[offset], ExifCommon::ExifU64Rational3::sizeOf(), m_isBe);
value.m_value[0].m_Nominator = mstream.get_uint_advance();
value.m_value[0].m_Denominator = mstream.get_uint_advance();
value.m_value[1].m_Nominator = mstream.get_uint_advance();
value.m_value[1].m_Denominator = mstream.get_uint_advance();
value.m_value[2].m_Nominator = mstream.get_uint_advance();
value.m_value[2].m_Denominator = mstream.get_uint();
}
}
bool ExifReader::validateOffset(uint32_t offset, uint32_t count) const
{
return ((uint64_t)offset + count) < (uint64_t)m_dataSize; // uint64_t to avoid overflow
}
void* ExifReader::createCacheEntry(uint32_t entrySize, uint8_t* entryData)
{
uint32_t alignedNewOffset = align(m_cacheOffset + entrySize, 4); // always align to 4 byte boundary
if(alignedNewOffset <= kCacheSize) {
void* entry = &m_cacheData[m_cacheOffset];
memcpy(entry, entryData, entrySize);
m_cacheOffset = alignedNewOffset;
return entry;
}
return nullptr;
}
}