imagecore/formats/exif/exifcommon.h (222 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.
*/
#pragma once
#include <inttypes.h>
#include "imagecore/image/image.h"
#include "imagecore/utils/memorystream.h"
#include "imagecore/imagecore.h"
#include "imagecore/utils/mathtypes.h"
#define TFIF_MARKER 0x2A
#define EXIF_BIG_ENDIAN 0x4D4D
#define EXIF_MARKER 0xE1
namespace imagecore {
class ExifCommon
{
public:
enum class kTagId : uint16_t
{
kMake = 0,
kModel,
kOrientation,
kXResolution,
kYResolution,
kResolutionUnit,
kSoftware,
kModifyDate,
kExposureTime,
kExifSubDirectory,
kGPSSubDirectory,
kGPSLatitudeRef,
kGPSLatitude,
kGPSLongitudeRef,
kGPSLongitude,
kGPSAltitudeRef,
kGPSAltitude,
kGPSTimeStamp,
kGPSSpeedRef,
kGPSSpeed,
kGPSImgDirectionRef,
kGPSImgDirection,
kGPSDestBearingRef,
kGPSDestBearing,
kGPSDateStamp,
kTagIdMax
};
enum class kTagType : unsigned short
{
kUnsignedByte = 1, // (1 byte per component)
kASCIIString = 2, // (1 bpc)
kUnsignedShort = 3, // (2 bpc)
kUnsignedLong = 4, // (4 bpc)
kUnsignedRational = 5, // (8 bpc, 4 for the numerator and 4 for the denominator)
kSignedByte = 6,
kUndefined = 7, // (1 bpc)
kSignedShort = 8,
kSignedLong = 9,
kSignedRational = 10,
kSingle = 11, // (4 bpc)
kDouble = 12 // (8 bpc)
};
// This is needed, because the same exif tag can be used to id different data depending on which subdirectory its in.
enum class kDirectoryType
{
kDirectoryExif = 0,
kDirectoryGPS,
kDirectoryMaxDirs
};
typedef bool (*validateRange)(const void*, uint32_t);
struct TagHeaderType
{
uint16_t m_exifId; // Exif ID
kDirectoryType m_dirType;
kTagType m_type;
uint32_t m_count;
uint16_t m_internalId; // our ID
validateRange m_rangeValidator;
bool verifyTypeAndCount(uint16_t exifType, uint32_t count) const;
bool relaxedTypeAndCountCheck(uint16_t exifType, uint32_t count, uint16_t signedType, uint16_t unsignedType) const;
};
static const uint32_t kMaxExifStringLength = 256;
struct ExifString
{
ExifString()
{
m_string[0] = 0;
m_length = 0;
}
ExifString(const char* str)
{
m_length = strlen(str) + 1;
SECURE_ASSERT(m_length < kMaxExifStringLength)
strcpy(m_string, str);
m_string[m_length - 1] = 0; // zero terminate
}
char m_string[kMaxExifStringLength];
uint16_t m_length;
};
struct ExifU64Rational3 // Array of 3 rationals used in GPS data
{
ExifU64Rational3()
{
}
ExifU64Rational3(Rational<uint32_t> v0, Rational<uint32_t> v1, Rational<uint32_t> v2)
{
m_value[0] = v0;
m_value[1] = v1;
m_value[2] = v2;
}
bool operator == (const ExifU64Rational3 &other) const {
return (m_value[0] == other.m_value[0]) && (m_value[1] == other.m_value[1]) && (m_value[2] == other.m_value[2]);
}
void setSign(bool isSigned) {
m_value[0].m_Signed = isSigned;
m_value[1].m_Signed = isSigned;
m_value[2].m_Signed = isSigned;
}
static uint32_t sizeOf() {
return 3 * Rational<uint32_t>::sizeOf();
}
Rational<uint32_t> m_value[3];
};
static const uint32_t kMaxUniqueTags = 64 * 1024;
enum class kExifTagID : uint16_t
{
kExifMake = 0x010f,
kExifModel = 0x0110,
kExifOrientation = 0x0112,
kExifXResolution = 0x011A,
kExifYResolution = 0x011B,
kExifResolutionUnit = 0x0128,
kExifSoftware = 0x0131,
kExifModifyDate = 0x0132,
kExifYCbCrPositioning = 0x0213,
kExifOffset = 0x8769,
kExifGPSInfo = 0x8825,
kExifExposureTime = 0x829a,
kExifFNumber = 0x829d,
kExifExposureProgram = 0x8822,
kExifISO = 0x8827,
kExifVersion = 0x9000,
kExifDateTimeOriginal = 0x9003,
kExifCreateDate = 0x9004,
kExifComponentsConfiguration = 0x9101,
kExifShutterSpeedValue = 0x9201,
kExifApertureValue = 0x9202,
kExifBrightnessValue = 0x9203,
kExifExposureCompensation = 0x9204,
kExifMeteringMode = 0x9207,
kExifFlash = 0x9209,
kExifFocalLength = 0x920a,
kExifSubjectArea = 0x9214,
kExifManufacturerTags = 0x927c,
kExifSubSecTimeOriginal = 0x9291,
kExifSubSecTimeDigitized = 0x9292,
kExifFlashpixVersion = 0xa000,
kExifColorSpace = 0xa001,
kExifImageWidth = 0xa002,
kExifImageHeight = 0xa003,
kExifSensingMethod = 0xa217,
kExifSceneType = 0xa301,
kExifExposureMode = 0xa402,
kExifWhiteBalance = 0xa403,
kExifFocalLengthIn35mmFormat = 0xa405,
kExifSceneCaptureType = 0xa406,
kExifLensInfo = 0xa432,
kExifLensMake = 0xa433,
kExifLensModel = 0xa434,
};
enum class kExifGPSTagID : uint16_t
{
kExifGPSLatitudeRef = 0x1,
kExifGPSLatitude = 0x2,
kExifGPSLongitudeRef = 0x3,
kExifGPSLongitude = 0x4,
kExifGPSAltitudeRef = 0x5,
kExifGPSAltitude = 0x6,
kExifGPSTimeStamp = 0x7,
kExifGPSSpeedRef = 0xc,
kExifGPSSpeed = 0xd,
kExifGPSImgDirectionRef = 0x10,
kExifGPSImgDirection = 0x11,
kExifGPSDestBearingRef = 0x17,
kExifGPSDestBearing = 0x18,
kExifGPSDateStamp = 0x1d,
};
class IFDStructure
{
public:
void write(MemoryStreamWriter& memoryStream, uint32_t offset);
uint16_t tag;
uint16_t type;
uint32_t count;
uint32_t value_offset;
bool isOffset;
};
static const uint32_t kMaxExifDirectoryEntries = 256;
static const uint32_t kMaxMetaDataSize = 64 * 1024;
class ExifDirectory
{
public:
ExifDirectory(bool is_be);
~ExifDirectory();
IFDStructure* getNewEntry();
void write(MemoryStreamWriter& memoryStream, uint32_t& offset) const;
uint16_t m_numEntries;
IFDStructure* m_entries;
uint8_t* metaDataMemory;
MemoryStreamWriter* metaDataWriter;
};
ExifCommon();
static ExifCommon& Instance();
const TagHeaderType* getTagHeader(kTagId tagId);
kDirectoryType getDirectoryType(kExifTagID exifDirectoryTag);
int32_t getTagId(uint32_t dirType, uint32_t exifDirectoryTag);
private:
// validators
static bool validateOrientation(const void* valueAddress, uint32_t count);
static bool validateString(const void* valueAddress, uint32_t count);
static bool validateU64Rational(const void* valueAddress, uint32_t count);
static bool validateU64Rational3(const void* valueAddress, uint32_t count);
static bool validateResolutionUnit(const void* valueAddress, uint32_t count);
static bool validateOffset(const void* valueAddress, uint32_t count);
static bool validateAltitude(const void* valueAddress, uint32_t count);
// static headers defining parameters for all supported query types
static TagHeaderType s_mTagHeaders[(uint32_t)kTagId::kTagIdMax];
int32_t m_TagHeaderLookup[(uint32_t)kDirectoryType::kDirectoryMaxDirs][kMaxUniqueTags];
};
}