blob: 71eaebc20d6bede5787ce6d129603338ec38d7cb [file] [log] [blame]
#include "ExifMetadataBuilder.h"
#define LOG_NDEBUG 0
#define LOG_TAG "ExifMetadataBuilder"
#include <cutils/log.h>
#include <cmath>
#include <stdlib.h>
namespace android {
// All supported EXIF data types.
enum ExifDataType {
ExifUInt8 = 1,
ExifString = 2,
ExifUInt16 = 3,
ExifUInt32 = 4,
ExifRational = 5,
ExifUndefined = 7,
ExifSInt16 = 8,
ExifSInt32 = 9,
ExifFloat = 11,
ExifDouble = 12,
};
enum ExifTagId {
kExifTagGpsLatitudeRef = 0x1,
kExifTagGpsLatitude = 0x2,
kExifTagGpsLongitudeRef = 0x3,
kExifTagGpsLongitude = 0x4,
kExifTagGpsAltitudeRef = 0x5,
kExifTagGpsAltitude = 0x6,
kExifTagGpsTimestamp = 0x7,
kExifTagGpsProcessingMethod = 0x1b,
kExifTagGpsDatestamp = 0x1d,
kExifTagImageWidth = 0x100,
kExifTagImageHeight = 0x101,
kExifTagImageDateTime = 0x132,
kExifTagJpegData = 0x201,
kExifTagJpegLength = 0x202,
kExifTagCameraSubIFD = 0x8769,
kExifTagGpsSubIFD = 0x8825,
kExifTagCameraFocalLength = 0x920a,
};
const char kExifCharArrayAscii[8] = "ASCII";
const char kExifCharArrayUnicode[8] = "UNICODE";
// Structure of an individual EXIF tag.
struct ExifTagInfo {
uint16_t tag; // Describes unique tag type.
uint16_t type; // Describes data type (see ExifDataType).
uint32_t count; // Number of data elements.
uint32_t value; // Value (for shorter items) or offset (for longer).
};
// Interface describing individual EXIF tag.
class ExifTag {
public:
virtual ~ExifTag() {}
virtual size_t DataSize() { return 0; }
virtual void AppendTag(ExifTagInfo* info, size_t data_offset) = 0;
virtual void AppendData(uint8_t* target) {}
};
// EXIF structure.
// This structure describes all types of tags:
// - Main image,
// - Thumbnail image,
// - Sub-IFDs.
class ExifStructure {
public:
typedef std::vector<ExifTag*> TagMap;
ExifStructure() {}
~ExifStructure() {
for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
delete *it;
}
}
size_t TagSize() {
// Target tag structure:
// - uint16_t: mTags.size();
// - ExifTagInfo[mTags.size()]
// - uint32_t: next_structure_available ? self_offset + Size() : NULL
return sizeof(uint16_t) // mTags.size()
+ mTags.size() * sizeof(ExifTagInfo) // [tags]
+ sizeof(uint32_t); // next offset
}
size_t DataSize() {
size_t data_size = 0;
for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
data_size += (*it)->DataSize();
}
return data_size;
}
size_t Size() {
return TagSize() + DataSize();
}
uint32_t Build(
uint8_t* buffer,
const uint32_t self_offset,
const bool next_structure_available) {
// Write number of items.
uint16_t num_elements = mTags.size();
memcpy(buffer, &num_elements, sizeof(num_elements));
buffer += sizeof(num_elements);
// Offset describes where tag data will be placed.
uint32_t offset = self_offset + TagSize();
// Combine EXIF for main image.
for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
// Each tag is exactly 12 bytes long, but data length can be anything.
// We supply the data offset to anyone who wants to use data.
(*it)->AppendTag(reinterpret_cast<ExifTagInfo*>(buffer), offset);
offset += (*it)->DataSize();
buffer += sizeof(ExifTagInfo);
}
// Append information about the second tag offset.
// |offset| holds exactly the right position:
// self_offset + TagSize() + DataSize().
uint32_t next_tag_offset = next_structure_available ? offset : 0;
memcpy(buffer, &next_tag_offset, sizeof(next_tag_offset));
buffer += sizeof(next_tag_offset);
// Combine EXIF data for main image.
for (TagMap::iterator it = mTags.begin(); it != mTags.end(); ++it) {
(*it)->AppendData(buffer);
buffer += (*it)->DataSize();
}
return offset;
}
void PushTag(ExifTag* tag) {
mTags.push_back(tag);
}
private:
TagMap mTags;
};
// EXIF tags.
namespace {
// Tag with 8-bit unsigned integer.
class ExifUInt8Tag : public ExifTag {
public:
ExifUInt8Tag(uint16_t tag, size_t value) : mTag(tag), mValue(value) {}
void AppendTag(ExifTagInfo* info, size_t data_offset) {
info->tag = mTag;
info->type = ExifUInt8;
info->count = 1;
info->value = mValue << 24;
}
private:
uint16_t mTag;
uint32_t mValue;
};
// Tag with 32-bit unsigned integer.
class ExifUInt32Tag : public ExifTag {
public:
ExifUInt32Tag(uint16_t tag, size_t value) : mTag(tag), mValue(value) {}
void AppendTag(ExifTagInfo* info, size_t data_offset) {
info->tag = mTag;
info->type = ExifUInt32;
info->count = 1;
info->value = mValue;
}
private:
uint16_t mTag;
uint32_t mValue;
};
// Char array tag.
class ExifCharArrayTag : public ExifTag {
public:
ExifCharArrayTag(uint16_t tag, const char (&type)[8], const std::string& str)
: mTag(tag),
mType(type),
mString(str) {}
void AppendTag(ExifTagInfo* info, size_t data_offset) {
info->tag = mTag;
info->type = ExifUndefined;
info->count = DataSize();
info->value = data_offset;
}
size_t DataSize() {
return sizeof(mType) + mString.size();
}
void AppendData(uint8_t* data) {
memcpy(data, mType, sizeof(mType));
data += sizeof(mType);
memcpy(data, mString.data(), mString.size());
}
private:
uint16_t mTag;
const char (&mType)[8];
std::string mString;
};
// Data tag; writes LONG (pointer) and appends data.
class ExifPointerTag : public ExifTag {
public:
ExifPointerTag(uint16_t tag, void* data, int size)
: mTag(tag),
mData(data),
mSize(size) {}
~ExifPointerTag() {
free(mData);
}
void AppendTag(ExifTagInfo* info, size_t data_offset) {
info->tag = mTag;
info->type = ExifUInt32;
info->count = 1;
info->value = data_offset;
}
size_t DataSize() {
return mSize;
}
void AppendData(uint8_t* data) {
memcpy(data, mData, mSize);
}
private:
uint16_t mTag;
void* mData;
int mSize;
};
// String tag.
class ExifStringTag : public ExifTag {
public:
ExifStringTag(uint16_t tag, const std::string& str)
: mTag(tag),
mString(str) {}
void AppendTag(ExifTagInfo* info, size_t data_offset) {
info->tag = mTag;
info->type = ExifString;
info->count = DataSize();
info->value = data_offset;
}
size_t DataSize() {
// Include padding \0.
return mString.size() + 1;
}
void AppendData(uint8_t* data) {
memcpy(data, mString.data(), DataSize());
}
private:
uint16_t mTag;
std::string mString;
};
// SubIFD: sub-tags.
class ExifSubIfdTag : public ExifTag {
public:
ExifSubIfdTag(uint16_t tag)
: mTag(tag),
mSubStructure(new ExifStructure) {}
~ExifSubIfdTag() {
delete mSubStructure;
}
void AppendTag(ExifTagInfo* info, size_t data_offset) {
info->tag = mTag;
info->type = ExifUInt32;
info->count = 1;
info->value = data_offset;
mDataOffset = data_offset;
}
size_t DataSize() {
return mSubStructure->Size();
}
void AppendData(uint8_t* data) {
mSubStructure->Build(data, mDataOffset, false);
}
ExifStructure* GetSubStructure() {
return mSubStructure;
}
private:
uint16_t mTag;
ExifStructure* mSubStructure;
size_t mDataOffset;
};
// Unsigned rational tag.
class ExifURationalTag : public ExifTag {
public:
ExifURationalTag(uint16_t tag, double value)
: mTag(tag),
mCount(1) {
DoubleToRational(value,
&mRationals[0].mNumerator, &mRationals[0].mDenominator);
}
ExifURationalTag(uint16_t tag, double value1, double value2, double value3)
: mTag(tag),
mCount(3) {
DoubleToRational(value1,
&mRationals[0].mNumerator, &mRationals[0].mDenominator);
DoubleToRational(value2,
&mRationals[1].mNumerator, &mRationals[1].mDenominator);
DoubleToRational(value3,
&mRationals[2].mNumerator, &mRationals[2].mDenominator);
}
void DoubleToRational(double value,
int32_t* numerator, int32_t* denominator) {
int sign = 1;
if (value < 0) {
sign = -sign;
value = -value;
}
// Take shortcuts. Plenty.
*numerator = value;
*denominator = 1;
// Set some (arbitrary) threshold beyond which we do not proceed.
while (*numerator < (1 << 24)) {
if (std::fabs((*numerator / *denominator) - value) <= 0.0001) break;
*denominator *= 10;
*numerator = value * (*denominator);
}
*numerator *= sign;
}
void AppendTag(ExifTagInfo* info, size_t data_offset) {
info->tag = mTag;
info->type = ExifRational;
info->count = mCount;
info->value = data_offset;
}
size_t DataSize() {
return sizeof(mRationals[0]) * mCount;
}
void AppendData(uint8_t* data) {
memcpy(data, &mRationals[0], DataSize());
}
private:
static const int kMaxSupportedRationals = 3;
uint16_t mTag;
uint16_t mCount;
struct {
int32_t mNumerator;
int32_t mDenominator;
} mRationals[kMaxSupportedRationals];
};
std::string ToAsciiDate(time_t time) {
struct tm loc;
char res[12]; // YYYY:MM:DD\0\0
localtime_r(&time, &loc);
strftime(res, sizeof(res), "%Y:%m:%d", &loc);
return res;
}
std::string ToAsciiTime(time_t time) {
struct tm loc;
char res[10]; // HH:MM:SS\0\0
localtime_r(&time, &loc);
strftime(res, sizeof(res), "%H:%M:%S", &loc);
return res;
}
} // namespace
ExifMetadataBuilder::ExifMetadataBuilder()
: mImageIfd(new ExifStructure),
mThumbnailIfd(new ExifStructure) {
// Mandatory tag: camera details.
ExifSubIfdTag* sub_ifd = new ExifSubIfdTag(kExifTagCameraSubIFD);
// Pass ownership to mImageIfd.
mImageIfd->PushTag(sub_ifd);
mCameraSubIfd = sub_ifd->GetSubStructure();
// Optional (yet, required) tag: GPS data.
sub_ifd = new ExifSubIfdTag(kExifTagGpsSubIFD);
// Pass ownership to mImageIfd.
mImageIfd->PushTag(sub_ifd);
mGpsSubIfd = sub_ifd->GetSubStructure();
}
ExifMetadataBuilder::~ExifMetadataBuilder() {
delete mImageIfd;
delete mThumbnailIfd;
}
void ExifMetadataBuilder::SetWidth(int width) {
mImageIfd->PushTag(new ExifUInt32Tag(kExifTagImageWidth, width));
}
void ExifMetadataBuilder::SetHeight(int height) {
mImageIfd->PushTag(new ExifUInt32Tag(kExifTagImageHeight, height));
}
void ExifMetadataBuilder::SetThumbnailWidth(int width) {
mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagImageWidth, width));
}
void ExifMetadataBuilder::SetThumbnailHeight(int height) {
mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagImageHeight, height));
}
void ExifMetadataBuilder::SetThumbnail(void* thumbnail, int size) {
mThumbnailIfd->PushTag(new ExifUInt32Tag(kExifTagJpegLength, size));
mThumbnailIfd->PushTag(new ExifPointerTag(kExifTagJpegData, thumbnail, size));
}
void ExifMetadataBuilder::SetDateTime(time_t date_time) {
std::string res = ToAsciiDate(date_time) + " " + ToAsciiTime(date_time);
mImageIfd->PushTag(new ExifStringTag(kExifTagImageDateTime, res));
}
void ExifMetadataBuilder::SetGpsLatitude(double latitude) {
if (latitude < 0) {
mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLatitudeRef, "S"));
} else {
mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLatitudeRef, "N"));
}
int degrees = latitude;
latitude = (latitude - degrees) * 60.;
int minutes = latitude;
latitude = (latitude - minutes) * 60.;
double seconds = latitude;
mGpsSubIfd->PushTag(new ExifURationalTag(kExifTagGpsLatitude,
degrees, minutes, seconds));
}
void ExifMetadataBuilder::SetGpsLongitude(double longitude) {
if (longitude < 0) {
mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLongitudeRef, "W"));
} else {
mGpsSubIfd->PushTag(new ExifStringTag(kExifTagGpsLongitudeRef, "E"));
}
int32_t degrees = longitude;
longitude = (longitude - degrees) * 60.;
int32_t minutes = longitude;
longitude = (longitude - minutes) * 60.;
double seconds = longitude;
mGpsSubIfd->PushTag(new ExifURationalTag(kExifTagGpsLongitude,
degrees, minutes, seconds));
}
void ExifMetadataBuilder::SetGpsAltitude(double altitude) {
mGpsSubIfd->PushTag(new ExifUInt8Tag(kExifTagGpsAltitudeRef, 0));
mGpsSubIfd->PushTag(new ExifURationalTag(kExifTagGpsAltitude, altitude));
}
void ExifMetadataBuilder::SetGpsProcessingMethod(const std::string& method) {
mGpsSubIfd->PushTag(new ExifCharArrayTag(
kExifTagGpsProcessingMethod, kExifCharArrayAscii, method));
}
void ExifMetadataBuilder::SetGpsDateTime(time_t timestamp) {
std::string date = ToAsciiDate(timestamp);
int32_t seconds = (timestamp % 60);
timestamp /= 60;
int32_t minutes = (timestamp % 60);
timestamp /= 60;
mGpsSubIfd->PushTag(new ExifURationalTag(
kExifTagGpsTimestamp, timestamp % 24, minutes, seconds));
mGpsSubIfd->PushTag(new ExifStringTag(
kExifTagGpsDatestamp, date));
}
void ExifMetadataBuilder::SetLensFocalLength(double length) {
mCameraSubIfd->PushTag(
new ExifURationalTag(kExifTagCameraFocalLength, length));
}
void ExifMetadataBuilder::Build() {
const uint8_t exif_header[] = {
'E', 'x', 'i', 'f', 0, 0, // EXIF header.
};
const uint8_t tiff_header[] = {
'I', 'I', 0x2a, 0x00, // TIFF Little endian header.
};
// EXIF data should be exactly this much.
size_t exif_size = sizeof(exif_header) + sizeof(tiff_header) +
sizeof(uint32_t) + // Offset of the following descriptors.
mImageIfd->Size() + mThumbnailIfd->Size();
const uint8_t marker[] = {
0xff, 0xd8, 0xff, 0xe1, // EXIF marker.
uint8_t((exif_size + 2) >> 8), // Data length (including the length field)
uint8_t((exif_size + 2) & 0xff),
};
// Reserve data for our exif info.
mData.Resize(sizeof(marker) + exif_size);
uint8_t* b = (uint8_t*)(mData.data());
// Initialize marker & headers.
memcpy(b, &marker, sizeof(marker));
b += sizeof(marker);
memcpy(b, &exif_header, sizeof(exif_header));
b += sizeof(exif_header);
memcpy(b, &tiff_header, sizeof(tiff_header));
uint32_t data_offset = sizeof(tiff_header);
// Write offset of the first IFD item.
uint32_t first_tag_offset = data_offset + sizeof(uint32_t);
memcpy(&b[data_offset], &first_tag_offset, sizeof(first_tag_offset));
data_offset += sizeof(first_tag_offset);
// At this moment |b| points to EXIF structure.
// TIFF header is part of the structure itself and explains endianness of the
// embedded data.
ALOGI("%s: Building Main image EXIF tags", __FUNCTION__);
data_offset = mImageIfd->Build(&b[data_offset], data_offset, true);
ALOGI("%s: Building Thumbnail image EXIF tags", __FUNCTION__);
data_offset = mThumbnailIfd->Build(&b[data_offset], data_offset, false);
ALOGI("%s: EXIF metadata constructed (%d bytes).", __FUNCTION__, data_offset);
}
} // namespace android