You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
686 lines
25 KiB
686 lines
25 KiB
/*
|
|
* Copyright Samsung Electronics Co.,LTD.
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "hwjpeg-internal.h"
|
|
#include "AppMarkerWriter.h"
|
|
#include "IFDWriter.h"
|
|
|
|
static const char ExifAsciiPrefix[] = { 'A', 'S', 'C', 'I', 'I', 0x0, 0x0, 0x0 };
|
|
static const char ExifIdentifierCode[6] = { 'E', 'x', 'i', 'f', 0x00, 0x00 };
|
|
static char TiffHeader[8] = { 'I', 'I', 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00 };
|
|
static const unsigned char ComponentsConfiguration[4] = { 1, 2, 3, 0 }; // YCbCr
|
|
static const unsigned char SceneType[4] = { 1, 0, 0, 0 }; // A directly photographed image
|
|
|
|
#ifndef __LITTLE_ENDIAN__
|
|
CEndianessChecker __LITTLE_ENDIAN__;
|
|
#endif
|
|
|
|
CEndianessChecker::CEndianessChecker()
|
|
{
|
|
int num = 1;
|
|
__little = (*reinterpret_cast<char *>(&num) == 1);
|
|
if (__little) {
|
|
TiffHeader[0] = 'I';
|
|
TiffHeader[1] = 'I';
|
|
} else {
|
|
TiffHeader[0] = 'M';
|
|
TiffHeader[1] = 'M';
|
|
}
|
|
}
|
|
|
|
|
|
CAppMarkerWriter::CAppMarkerWriter()
|
|
: m_pAppBase(NULL), m_pApp1End(NULL), m_pExif(NULL), m_pExtra(NULL)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
CAppMarkerWriter::CAppMarkerWriter(char *base, exif_attribute_t *exif, debug_attribute_t *debug)
|
|
{
|
|
extra_appinfo_t extraInfo;
|
|
app_info_t appInfo[15];
|
|
|
|
memset(&extraInfo, 0, sizeof(extraInfo));
|
|
memset(&appInfo, 0, sizeof(appInfo));
|
|
|
|
extraInfo.appInfo = appInfo;
|
|
|
|
ExtractDebugAttributeInfo(debug, &extraInfo);
|
|
|
|
PrepareAppWriter(base, exif, &extraInfo);
|
|
}
|
|
|
|
void CAppMarkerWriter::Init()
|
|
{
|
|
m_pApp1End = NULL;
|
|
|
|
m_szApp1 = 0;
|
|
|
|
m_n0thIFDFields = 0;
|
|
m_n1stIFDFields = 0;
|
|
m_nExifIFDFields = 0;
|
|
m_nGPSIFDFields = 0;
|
|
|
|
m_szMake = 0;
|
|
m_szSoftware = 0;
|
|
m_szModel = 0;
|
|
m_szUniqueID = 0;
|
|
|
|
m_pThumbBase = NULL;
|
|
m_szMaxThumbSize = 0;
|
|
m_pThumbSizePlaceholder = NULL;
|
|
}
|
|
|
|
void CAppMarkerWriter::PrepareAppWriter(char *base, exif_attribute_t *exif, extra_appinfo_t *extra)
|
|
{
|
|
m_pAppBase = base;
|
|
m_pExif = exif;
|
|
|
|
Init();
|
|
|
|
size_t applen = 0;
|
|
|
|
if (exif) {
|
|
// APP1
|
|
applen += JPEG_SEGMENT_LENFIELD_SIZE +
|
|
ARRSIZE(ExifIdentifierCode) + ARRSIZE(TiffHeader);
|
|
|
|
// 0th IFD: Make, Model, Orientation, Software,
|
|
// DateTime, YCbCrPositioning, X/Y Resolutions, Exif and GPS
|
|
applen += IFD_FIELDCOUNT_SIZE + IFD_VALOFF_SIZE;
|
|
// Orientation, YCbCrPos, XYRes/Unit, DateTime and Exif
|
|
m_n0thIFDFields = 7;
|
|
applen += IFD_FIELD_SIZE * m_n0thIFDFields;
|
|
applen += sizeof(rational_t) * 2; // values of XResolution and YResolution
|
|
applen += EXIF_DATETIME_LENGTH;
|
|
|
|
m_szMake = strlen(m_pExif->maker);
|
|
if (m_szMake > 0) {
|
|
m_n0thIFDFields++;
|
|
applen += IFD_FIELD_SIZE;
|
|
if (m_szMake > 3)
|
|
applen += m_szMake + 1;
|
|
}
|
|
|
|
m_szSoftware = strlen(m_pExif->software);
|
|
if (m_szSoftware > 0) {
|
|
m_n0thIFDFields++;
|
|
applen += IFD_FIELD_SIZE;
|
|
if (m_szSoftware > 3)
|
|
applen += m_szSoftware + 1;
|
|
}
|
|
|
|
m_szModel = strlen(m_pExif->model);
|
|
if (m_szModel > 0) {
|
|
m_n0thIFDFields++;
|
|
applen += IFD_FIELD_SIZE;
|
|
if (m_szModel > 3)
|
|
applen += m_szModel + 1;
|
|
}
|
|
|
|
if (m_pExif->enableGps) {
|
|
m_n0thIFDFields++;
|
|
applen += IFD_FIELD_SIZE;
|
|
}
|
|
|
|
/*
|
|
* Exif SubIFD: 37 fields
|
|
* Fields with no data offset: 19
|
|
* - ExposureProgram, PhotographicSensitivity, ExifVersion, MeteringMode,
|
|
* - Flash, FlashPixVersion, ColorSpace, PixelXDimension, PixelYDimension,
|
|
* - ExposureMode, WhiteBalance, FocalLengthIn35mmFilm, SceneCaptureType,
|
|
* - ComponentsConfiguration
|
|
* - SceneType, CustomRendered, Contrast, Saturation, Sharpness
|
|
* (S)Rational Fields: 9
|
|
* - ExposureTime, FNumber, ShutterSpeedValue, ApertureValue,
|
|
* - BrightnessValue, ExposureBiasValue, MaxApertureValue, FocalLength
|
|
* - DigitalZoomRatio
|
|
* ASCII Fields: 6
|
|
* - DateTimeOriginal, DateTimeDigitized, SubsecTime, SubsecTimeOriginal,
|
|
* - SubsecTimeDigitized, ImageUniqueID
|
|
* Undefined Long Fields: 2
|
|
* - MakerNote, UserComment
|
|
* SubIFD: 1
|
|
* - Interoperability IFD
|
|
*/
|
|
m_nExifIFDFields = 28; // rational fields and fields withouth data offset
|
|
applen += IFD_FIELDCOUNT_SIZE + IFD_VALOFF_SIZE;
|
|
applen += IFD_FIELD_SIZE * m_nExifIFDFields;
|
|
applen += sizeof(rational_t) * 9; // 9 rational values
|
|
|
|
// DateTime*
|
|
m_nExifIFDFields += 2;
|
|
applen += (IFD_FIELD_SIZE + EXIF_DATETIME_LENGTH) * 2;
|
|
|
|
// SubSecTime*
|
|
m_nExifIFDFields += 3;
|
|
applen += (IFD_FIELD_SIZE + EXIF_SUBSECTIME_LENGTH) * 3;
|
|
|
|
m_szUniqueID = strlen(m_pExif->unique_id); // len should be 32!
|
|
if (m_szUniqueID > 0) {
|
|
m_nExifIFDFields++;
|
|
applen += IFD_FIELD_SIZE;
|
|
if (m_szUniqueID > 3)
|
|
applen += m_szUniqueID + 1;
|
|
}
|
|
|
|
if (m_pExif->maker_note_size > 0) {
|
|
m_nExifIFDFields++;
|
|
applen += IFD_FIELD_SIZE;
|
|
if (m_pExif->maker_note_size > 4)
|
|
applen += m_pExif->maker_note_size;
|
|
}
|
|
|
|
if (m_pExif->user_comment_size > 0) {
|
|
m_nExifIFDFields++;
|
|
applen += IFD_FIELD_SIZE;
|
|
if (m_pExif->user_comment_size > 4)
|
|
applen += m_pExif->user_comment_size;
|
|
}
|
|
|
|
// Interoperability SubIFD
|
|
m_nExifIFDFields++; // Interoperability is sub IFD of Exif sub IFD
|
|
applen += IFD_FIELD_SIZE +
|
|
IFD_FIELDCOUNT_SIZE + IFD_VALOFF_SIZE + IFD_FIELD_SIZE * 2;
|
|
|
|
if (m_pExif->enableGps) {
|
|
size_t len;
|
|
/*
|
|
* GPS SubIFD: 10 fields
|
|
* Fields with no data offset: 4
|
|
* - GPSVersionID, GPSLattitudeRef, GPSLongitudeRev, GPSAltitudeRef
|
|
* Rational Fields: 4 (total 10 rational values)
|
|
* - GPSLatitude(3), GPSLongitude(3), GPSAltitude(1), GPSTImeStamp(3)
|
|
* ASCII or Undefined fields: 2
|
|
* - PGSProcessingMethod, GPSDateStamp
|
|
*/
|
|
m_nGPSIFDFields = 8;
|
|
applen += IFD_FIELDCOUNT_SIZE + IFD_VALOFF_SIZE;
|
|
applen += IFD_FIELD_SIZE * m_nGPSIFDFields;
|
|
applen += sizeof(rational_t) * 10;
|
|
|
|
// gps date stamp
|
|
m_nGPSIFDFields += 1;
|
|
applen += IFD_FIELD_SIZE + EXIF_GPSDATESTAMP_LENGTH;
|
|
|
|
len = min(strlen(m_pExif->gps_processing_method),
|
|
MAX_GPS_PROCESSINGMETHOD_SIZE - sizeof(ExifAsciiPrefix) - 1);
|
|
if (len > 0) {
|
|
m_nGPSIFDFields++;
|
|
applen += IFD_FIELD_SIZE + len + sizeof(ExifAsciiPrefix) + 1;
|
|
}
|
|
}
|
|
|
|
if (m_pExif->enableThumb) {
|
|
/*
|
|
* 1st IFD: 6
|
|
* Fields with no data offset: 6
|
|
* - ImageWidth, ImageHeight, Compression, Orientation,
|
|
* - JPEGInterchangeFormat, JPEGInterchangeFormatLength
|
|
*/
|
|
if ((m_pExif->widthThumb < 16) || (m_pExif->heightThumb < 16)) {
|
|
ALOGE("Insufficient thumbnail information %dx%d",
|
|
m_pExif->widthThumb, m_pExif->heightThumb);
|
|
return;
|
|
}
|
|
|
|
m_n1stIFDFields = 6;
|
|
applen += IFD_FIELDCOUNT_SIZE + IFD_VALOFF_SIZE;
|
|
applen += IFD_FIELD_SIZE * m_n1stIFDFields;
|
|
|
|
m_pThumbBase = m_pAppBase + JPEG_MARKER_SIZE + applen;
|
|
m_szMaxThumbSize = JPEG_MAX_SEGMENT_SIZE - applen - JPEG_APP1_OEM_RESERVED;
|
|
}
|
|
|
|
m_szApp1 = applen;
|
|
}
|
|
|
|
if (extra) {
|
|
for (int idx = 0; idx < extra->num_of_appmarker; idx++) {
|
|
if ((extra->appInfo[idx].appid < EXTRA_APPMARKER_MIN) || (extra->appInfo[idx].appid >= EXTRA_APPMARKER_LIMIT)) {
|
|
ALOGE("Invalid extra APP segment ID %d", extra->appInfo[idx].appid);
|
|
return;
|
|
}
|
|
|
|
if ((extra->appInfo[idx].dataSize == 0) || (extra->appInfo[idx].dataSize > (JPEG_MAX_SEGMENT_SIZE - JPEG_SEGMENT_LENFIELD_SIZE))) {
|
|
ALOGE("Invalid APP%d segment size, %u bytes", extra->appInfo[idx].appid, extra->appInfo[idx].dataSize);
|
|
return;
|
|
}
|
|
|
|
ALOGD("APP%d: %u bytes", extra->appInfo[idx].appid, extra->appInfo[idx].dataSize);
|
|
}
|
|
}
|
|
|
|
m_pExtra = extra;
|
|
|
|
// |<- m_szApp1 ->|<- m_szMaxThumbSize ->|<-m_szAppX->|
|
|
// |<----- size of total APP1 and APP4 segments ----->|<-APP11->|<-- main image
|
|
// m_pAppBase m_pThumbBase | | return
|
|
// | | | | ||
|
|
// v v | | v|
|
|
// --|--------------------------------------------------|---------|-----------
|
|
// ^ ^ ^ ^ | ^^
|
|
// | | | | | ||
|
|
// |APP1 SOIofThumb APPX SOIofMain
|
|
// | |
|
|
// SOI DHTofMain
|
|
|
|
ALOGD("APP1: %u bytes(ThumbMax %zu)", m_szApp1, m_szMaxThumbSize);
|
|
}
|
|
|
|
#define APPMARKLEN (JPEG_MARKER_SIZE + JPEG_SEGMENT_LENFIELD_SIZE)
|
|
char *CAppMarkerWriter::WriteAPP11(char *current, size_t dummy, size_t align)
|
|
{
|
|
ALOG_ASSERT((align & ~align) == 0);
|
|
|
|
if ((dummy == 0) && (align == 1))
|
|
return current;
|
|
|
|
if (!m_pExif && !m_pExtra)
|
|
return current;
|
|
|
|
uint16_t len = PTR_TO_ULONG(current + APPMARKLEN) & (align - 1);
|
|
|
|
if (len)
|
|
len = align - len;
|
|
|
|
len += dummy + JPEG_SEGMENT_LENFIELD_SIZE;
|
|
|
|
*current++ = 0xFF;
|
|
*current++ = 0xEB;
|
|
WriteDataInBig(current, len);
|
|
|
|
return current + len;
|
|
}
|
|
|
|
char *CAppMarkerWriter::WriteAPPX(char *current, bool just_reserve)
|
|
{
|
|
if (!m_pExtra)
|
|
return current;
|
|
|
|
for (int idx = 0; idx < m_pExtra->num_of_appmarker; idx++) {
|
|
int appid = m_pExtra->appInfo[idx].appid;
|
|
uint16_t len = m_pExtra->appInfo[idx].dataSize + JPEG_SEGMENT_LENFIELD_SIZE;
|
|
|
|
// APPx marker
|
|
*current++ = 0xFF;
|
|
*current++ = 0xE0 + (appid & 0xF);
|
|
// APPx length
|
|
current = WriteDataInBig(current, len);
|
|
// APPx data
|
|
if (!just_reserve)
|
|
memcpy(current, m_pExtra->appInfo[idx].appData, m_pExtra->appInfo[idx].dataSize);
|
|
current += m_pExtra->appInfo[idx].dataSize;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
char *CAppMarkerWriter::WriteAPP1(char *current, bool reserve_thumbnail_space, bool updating)
|
|
{
|
|
if (!m_pExif)
|
|
return current;
|
|
|
|
// APP1 Marker
|
|
*current++ = 0xFF;
|
|
*current++ = 0xE1;
|
|
|
|
// APP1 length
|
|
if (updating) {
|
|
current += JPEG_SEGMENT_LENFIELD_SIZE;
|
|
} else {
|
|
uint16_t len = m_szApp1;
|
|
if (reserve_thumbnail_space)
|
|
len += m_szMaxThumbSize + JPEG_APP1_OEM_RESERVED;
|
|
current = WriteDataInBig(current, len);
|
|
}
|
|
|
|
// Exif Identifier
|
|
for (size_t i = 0; i < ARRSIZE(ExifIdentifierCode); i++)
|
|
*current++ = ExifIdentifierCode[i];
|
|
|
|
char *tiffheader = current;
|
|
for (size_t i = 0; i < ARRSIZE(TiffHeader); i++)
|
|
*current++ = TiffHeader[i];
|
|
|
|
CIFDWriter writer(tiffheader, current, m_n0thIFDFields);
|
|
|
|
writer.WriteShort(EXIF_TAG_ORIENTATION, 1, &m_pExif->orientation);
|
|
writer.WriteShort(EXIF_TAG_YCBCR_POSITIONING, 1, &m_pExif->ycbcr_positioning);
|
|
writer.WriteRational(EXIF_TAG_X_RESOLUTION, 1, &m_pExif->x_resolution);
|
|
writer.WriteRational(EXIF_TAG_Y_RESOLUTION, 1, &m_pExif->y_resolution);
|
|
writer.WriteShort(EXIF_TAG_RESOLUTION_UNIT, 1, &m_pExif->resolution_unit);
|
|
if (m_szMake > 0)
|
|
writer.WriteASCII(EXIF_TAG_MAKE, m_szMake + 1, m_pExif->maker);
|
|
if (m_szModel > 0)
|
|
writer.WriteASCII(EXIF_TAG_MODEL, m_szModel + 1, m_pExif->model);
|
|
if (m_szSoftware > 0)
|
|
writer.WriteASCII(EXIF_TAG_SOFTWARE, m_szSoftware + 1, m_pExif->software);
|
|
writer.WriteCString(EXIF_TAG_DATE_TIME, EXIF_DATETIME_LENGTH, m_pExif->date_time);
|
|
|
|
char *pSubIFDBase = writer.BeginSubIFD(EXIF_TAG_EXIF_IFD_POINTER);
|
|
if (pSubIFDBase) { // This should be always true!!
|
|
CIFDWriter exifwriter(tiffheader, pSubIFDBase, m_nExifIFDFields);
|
|
exifwriter.WriteRational(EXIF_TAG_EXPOSURE_TIME, 1, &m_pExif->exposure_time);
|
|
exifwriter.WriteRational(EXIF_TAG_FNUMBER, 1, &m_pExif->fnumber);
|
|
exifwriter.WriteShort(EXIF_TAG_EXPOSURE_PROGRAM, 1, &m_pExif->exposure_program);
|
|
exifwriter.WriteShort(EXIF_TAG_ISO_SPEED_RATING, 1, &m_pExif->iso_speed_rating);
|
|
exifwriter.WriteUndef(EXIF_TAG_EXIF_VERSION, 4, reinterpret_cast<unsigned char *>(m_pExif->exif_version));
|
|
exifwriter.WriteCString(EXIF_TAG_DATE_TIME_ORG, EXIF_DATETIME_LENGTH, m_pExif->date_time);
|
|
exifwriter.WriteCString(EXIF_TAG_DATE_TIME_DIGITIZE, EXIF_DATETIME_LENGTH, m_pExif->date_time);
|
|
exifwriter.WriteSRational(EXIF_TAG_SHUTTER_SPEED, 1, &m_pExif->shutter_speed);
|
|
exifwriter.WriteRational(EXIF_TAG_APERTURE, 1, &m_pExif->aperture);
|
|
exifwriter.WriteSRational(EXIF_TAG_BRIGHTNESS, 1, &m_pExif->brightness);
|
|
exifwriter.WriteSRational(EXIF_TAG_EXPOSURE_BIAS, 1, &m_pExif->exposure_bias);
|
|
exifwriter.WriteRational(EXIF_TAG_MAX_APERTURE, 1, &m_pExif->max_aperture);
|
|
exifwriter.WriteShort(EXIF_TAG_METERING_MODE, 1, &m_pExif->metering_mode);
|
|
exifwriter.WriteShort(EXIF_TAG_FLASH, 1, &m_pExif->flash);
|
|
exifwriter.WriteUndef(EXIF_TAG_FLASHPIX_VERSION, 4, reinterpret_cast<const unsigned char *>("0100"));
|
|
exifwriter.WriteUndef(EXIF_TAG_COMPONENTS_CONFIGURATION, 4, ComponentsConfiguration);
|
|
exifwriter.WriteRational(EXIF_TAG_FOCAL_LENGTH, 1, &m_pExif->focal_length);
|
|
exifwriter.WriteCString(EXIF_TAG_SUBSEC_TIME, EXIF_SUBSECTIME_LENGTH, m_pExif->sec_time);
|
|
exifwriter.WriteCString(EXIF_TAG_SUBSEC_TIME_ORIG, EXIF_SUBSECTIME_LENGTH, m_pExif->sec_time);
|
|
exifwriter.WriteCString(EXIF_TAG_SUBSEC_TIME_DIG, EXIF_SUBSECTIME_LENGTH, m_pExif->sec_time);
|
|
if (m_pExif->maker_note_size > 0)
|
|
exifwriter.WriteUndef(EXIF_TAG_MAKER_NOTE, m_pExif->maker_note_size, m_pExif->maker_note);
|
|
if (m_pExif->user_comment_size > 0)
|
|
exifwriter.WriteUndef(EXIF_TAG_USER_COMMENT, m_pExif->user_comment_size, m_pExif->user_comment);
|
|
exifwriter.WriteShort(EXIF_TAG_COLOR_SPACE, 1, &m_pExif->color_space);
|
|
exifwriter.WriteLong(EXIF_TAG_PIXEL_X_DIMENSION, 1, &m_pExif->width);
|
|
exifwriter.WriteLong(EXIF_TAG_PIXEL_Y_DIMENSION, 1, &m_pExif->height);
|
|
exifwriter.WriteUndef(EXIF_TAG_SCENE_TYPE, sizeof(SceneType), SceneType);
|
|
exifwriter.WriteShort(EXIF_TAG_CUSTOM_RENDERED, 1, &m_pExif->custom_rendered);
|
|
exifwriter.WriteShort(EXIF_TAG_EXPOSURE_MODE, 1, &m_pExif->exposure_mode);
|
|
exifwriter.WriteShort(EXIF_TAG_WHITE_BALANCE, 1, &m_pExif->white_balance);
|
|
exifwriter.WriteRational(EXIF_TAG_DIGITAL_ZOOM_RATIO, 1, &m_pExif->digital_zoom_ratio);
|
|
exifwriter.WriteShort(EXIF_TAG_FOCA_LENGTH_IN_35MM_FILM, 1, &m_pExif->focal_length_in_35mm_length);
|
|
exifwriter.WriteShort(EXIF_TAG_SCENCE_CAPTURE_TYPE, 1, &m_pExif->scene_capture_type);
|
|
exifwriter.WriteShort(EXIF_TAG_CONTRAST, 1, &m_pExif->contrast);
|
|
exifwriter.WriteShort(EXIF_TAG_SATURATION, 1, &m_pExif->saturation);
|
|
exifwriter.WriteShort(EXIF_TAG_SHARPNESS, 1, &m_pExif->sharpness);
|
|
if (m_szUniqueID > 0)
|
|
exifwriter.WriteASCII(EXIF_TAG_IMAGE_UNIQUE_ID, m_szUniqueID + 1, m_pExif->unique_id);
|
|
pSubIFDBase = exifwriter.BeginSubIFD(EXIF_TAG_INTEROPERABILITY);
|
|
if (pSubIFDBase) {
|
|
CIFDWriter interopwriter(tiffheader, pSubIFDBase, 2);
|
|
interopwriter.WriteASCII(EXIF_TAG_INTEROPERABILITY_INDEX, 4,
|
|
m_pExif->interoperability_index ? "THM" : "R98");
|
|
interopwriter.WriteUndef(EXIF_TAG_INTEROPERABILITY_VERSION, 4,
|
|
reinterpret_cast<const unsigned char *>("0100"));
|
|
interopwriter.Finish(true);
|
|
exifwriter.EndSubIFD(interopwriter.GetNextIFDBase());
|
|
} else {
|
|
exifwriter.CancelSubIFD();
|
|
}
|
|
exifwriter.Finish(true);
|
|
writer.EndSubIFD(exifwriter.GetNextIFDBase());
|
|
} else {
|
|
writer.CancelSubIFD();
|
|
}
|
|
|
|
if (m_pExif->enableGps) {
|
|
pSubIFDBase = writer.BeginSubIFD(EXIF_TAG_GPS_IFD_POINTER);
|
|
if (pSubIFDBase) { // This should be always true!!
|
|
CIFDWriter gpswriter(tiffheader, pSubIFDBase, m_nGPSIFDFields);
|
|
gpswriter.WriteByte(EXIF_TAG_GPS_VERSION_ID, 4, m_pExif->gps_version_id);
|
|
gpswriter.WriteASCII(EXIF_TAG_GPS_LATITUDE_REF, 2, m_pExif->gps_latitude_ref);
|
|
gpswriter.WriteRational(EXIF_TAG_GPS_LATITUDE, 3, m_pExif->gps_latitude);
|
|
gpswriter.WriteASCII(EXIF_TAG_GPS_LONGITUDE_REF, 2, m_pExif->gps_longitude_ref);
|
|
gpswriter.WriteRational(EXIF_TAG_GPS_LONGITUDE, 3, m_pExif->gps_longitude);
|
|
gpswriter.WriteByte(EXIF_TAG_GPS_ALTITUDE_REF, 1, &m_pExif->gps_altitude_ref);
|
|
gpswriter.WriteRational(EXIF_TAG_GPS_ALTITUDE, 1, &m_pExif->gps_altitude);
|
|
gpswriter.WriteCString(EXIF_TAG_GPS_DATESTAMP, EXIF_GPSDATESTAMP_LENGTH,
|
|
m_pExif->gps_datestamp);
|
|
gpswriter.WriteRational(EXIF_TAG_GPS_TIMESTAMP, 3, m_pExif->gps_timestamp);
|
|
size_t len = strlen(m_pExif->gps_processing_method);
|
|
if (len > 0) {
|
|
size_t idx;
|
|
len = min(len, static_cast<size_t>(99UL));
|
|
unsigned char buf[sizeof(ExifAsciiPrefix) + len + 1];
|
|
for (idx = 0; idx < sizeof(ExifAsciiPrefix); idx++)
|
|
buf[idx] = ExifAsciiPrefix[idx];
|
|
strncpy(reinterpret_cast<char *>(buf) + idx, m_pExif->gps_processing_method, len + 1);
|
|
len += idx;
|
|
buf[len] = '\0';
|
|
gpswriter.WriteUndef(EXIF_TAG_GPS_PROCESSING_METHOD, len + 1, buf);
|
|
}
|
|
gpswriter.Finish(true);
|
|
writer.EndSubIFD(gpswriter.GetNextIFDBase());
|
|
} else {
|
|
writer.CancelSubIFD();
|
|
}
|
|
}
|
|
|
|
// thumbnail and the next IFD pointer is never updated.
|
|
if (updating)
|
|
return NULL;
|
|
|
|
if (m_pExif->enableThumb) {
|
|
writer.Finish(false);
|
|
|
|
CIFDWriter thumbwriter(tiffheader, writer.GetNextIFDBase(), m_n1stIFDFields);
|
|
thumbwriter.WriteLong(EXIF_TAG_IMAGE_WIDTH, 1, &m_pExif->widthThumb);
|
|
thumbwriter.WriteLong(EXIF_TAG_IMAGE_HEIGHT, 1, &m_pExif->heightThumb);
|
|
thumbwriter.WriteShort(EXIF_TAG_COMPRESSION_SCHEME, 1, &m_pExif->compression_scheme);
|
|
thumbwriter.WriteShort(EXIF_TAG_ORIENTATION, 1, &m_pExif->orientation);
|
|
|
|
ALOG_ASSERT(thumbwriter.GetNextIFDBase() != m_pThumbBase);
|
|
uint32_t offset = thumbwriter.Offset(m_pThumbBase);
|
|
thumbwriter.WriteLong(EXIF_TAG_JPEG_INTERCHANGE_FORMAT, 1, &offset);
|
|
offset = 0; // temporarilly 0 byte
|
|
thumbwriter.WriteLong(EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LEN, 1, &offset);
|
|
m_pThumbSizePlaceholder = thumbwriter.GetNextTagAddress() - 4;
|
|
thumbwriter.Finish(true);
|
|
|
|
size_t thumbspace = reserve_thumbnail_space ? m_szMaxThumbSize + JPEG_APP1_OEM_RESERVED : 0;
|
|
|
|
return thumbwriter.GetNextIFDBase() + thumbspace;
|
|
}
|
|
|
|
writer.Finish(true);
|
|
|
|
return writer.GetNextIFDBase();
|
|
}
|
|
|
|
void CAppMarkerWriter::Finalize(size_t thumbsize)
|
|
{
|
|
if (m_pThumbSizePlaceholder) {
|
|
uint32_t len = static_cast<uint32_t>(thumbsize);
|
|
WriteData(m_pThumbSizePlaceholder, len);
|
|
m_pThumbSizePlaceholder = NULL;
|
|
}
|
|
}
|
|
void CAppMarkerWriter::UpdateApp1Size(size_t amount)
|
|
{
|
|
if (m_pAppBase) {
|
|
uint16_t len = m_szApp1 + amount;
|
|
WriteDataInBig(m_pAppBase + JPEG_MARKER_SIZE, len);
|
|
}
|
|
}
|
|
|
|
static const char *dbgerrmsg = "Updating debug data failed";
|
|
|
|
static inline size_t GetSegLen(char *p)
|
|
{
|
|
size_t len = (*reinterpret_cast<unsigned char *>(p) & 0xFF) << 8;
|
|
return len | (*reinterpret_cast<unsigned char *>(p + 1) & 0xFF);
|
|
}
|
|
|
|
static inline size_t GetExtraAPPSize(extra_appinfo_t *info)
|
|
{
|
|
size_t len = 0;
|
|
|
|
for (int idx = 0; idx < info->num_of_appmarker; idx++) {
|
|
if ((info->appInfo[idx].appid < EXTRA_APPMARKER_MIN) || (info->appInfo[idx].appid >= EXTRA_APPMARKER_LIMIT)) {
|
|
ALOGE("%s: Invalid extra APP segment ID %d", dbgerrmsg, info->appInfo[idx].appid);
|
|
return 0;
|
|
}
|
|
|
|
if ((info->appInfo[idx].dataSize == 0) || (info->appInfo[idx].dataSize > (JPEG_MAX_SEGMENT_SIZE - JPEG_SEGMENT_LENFIELD_SIZE))) {
|
|
ALOGE("%s: Invalid APP%d segment size, %u bytes.", dbgerrmsg, info->appInfo[idx].appid, info->appInfo[idx].dataSize);
|
|
return 0;
|
|
}
|
|
|
|
len += info->appInfo[idx].dataSize + JPEG_MARKER_SIZE + JPEG_SEGMENT_LENFIELD_SIZE;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
bool UpdateDebugData(char *jpeg, size_t jpeglen, debug_attribute_t *debug) // include/ExynosExif.h
|
|
{
|
|
extra_appinfo_t extraInfo;
|
|
app_info_t appInfo[15];
|
|
|
|
memset(&extraInfo, 0, sizeof(extraInfo));
|
|
memset(&appInfo, 0, sizeof(appInfo));
|
|
|
|
extraInfo.appInfo = appInfo;
|
|
|
|
ExtractDebugAttributeInfo(debug, &extraInfo);
|
|
|
|
UpdateDebugData(jpeg, jpeglen, &extraInfo);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool UpdateDebugData(char *jpeg, size_t jpeglen, extra_appinfo_t *extra) // include/ExynosExif.h
|
|
{
|
|
if (!extra) {
|
|
ALOGI("No data to update in APPx");
|
|
return true;
|
|
}
|
|
|
|
size_t validlen = GetExtraAPPSize(extra);
|
|
|
|
if (jpeglen < (validlen + JPEG_MARKER_SIZE)) {
|
|
ALOGE("%s: Too small JPEG stream length %zu", dbgerrmsg, jpeglen);
|
|
return false;
|
|
}
|
|
|
|
if ((*jpeg++ != 0xFF) || (*jpeg++ != 0xD8)) {
|
|
ALOGE("%s: %p is not a valid JPEG stream", dbgerrmsg, jpeg);
|
|
return false;
|
|
}
|
|
jpeglen -= 2;
|
|
|
|
int idx = 0;
|
|
|
|
while ((*jpeg++ == 0xFF) && (validlen > 0) && (jpeglen > validlen)) {
|
|
size_t seglen;
|
|
char marker;
|
|
int appid;
|
|
|
|
marker = *jpeg++;
|
|
jpeglen -= 2;
|
|
|
|
if ((marker == 0xDA) || (marker == 0xD9)) { // SOS and EOI
|
|
ALOGE("%s: No further space found for APPx metadata", dbgerrmsg);
|
|
return false;
|
|
}
|
|
|
|
appid = marker & 0xF;
|
|
if (((marker & 0xF0) == 0xE0) && ((appid >= EXTRA_APPMARKER_MIN) && (appid <= EXTRA_APPMARKER_LIMIT))) {
|
|
if (appid != extra->appInfo[idx].appid) {
|
|
ALOGE("%s: stored appid(%d) is different with updated appid(%d)",
|
|
dbgerrmsg, appid, extra->appInfo[idx].appid);
|
|
return false;
|
|
}
|
|
|
|
seglen = GetSegLen(jpeg);
|
|
if (seglen < (extra->appInfo[idx].dataSize + JPEG_SEGMENT_LENFIELD_SIZE)) {
|
|
ALOGE("%s: too small APP%d length %zu to store %u bytes",
|
|
dbgerrmsg, appid, seglen, extra->appInfo[idx].dataSize);
|
|
return false;
|
|
}
|
|
|
|
memcpy(jpeg + JPEG_SEGMENT_LENFIELD_SIZE,
|
|
extra->appInfo[idx].appData, extra->appInfo[idx].dataSize);
|
|
ALOGD("Successfully updated %u bytes to APP%d", extra->appInfo[idx].dataSize, appid);
|
|
|
|
validlen -= extra->appInfo[idx].dataSize + JPEG_MARKER_SIZE + JPEG_SEGMENT_LENFIELD_SIZE;
|
|
idx++;
|
|
} else {
|
|
// just skip all other segments
|
|
seglen = GetSegLen(jpeg);
|
|
if (seglen == 0)
|
|
seglen++; // fixup for invalid segment lengths
|
|
if (jpeglen < seglen)
|
|
seglen = jpeglen;
|
|
|
|
}
|
|
|
|
jpeg += seglen;
|
|
jpeglen -= seglen;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static const char *exiferrmsg = "Updating exif failed";
|
|
|
|
bool UpdateExif(char *jpeg, size_t jpeglen, exif_attribute_t *exif)
|
|
{
|
|
if (!exif) {
|
|
ALOGI("No Exif to update");
|
|
return true;
|
|
}
|
|
|
|
if (jpeglen < (JPEG_MARKER_SIZE * 2 + JPEG_SEGMENT_LENFIELD_SIZE)) {
|
|
ALOGE("%s: Too small stream length %zu", exiferrmsg, jpeglen);
|
|
return false;
|
|
}
|
|
|
|
if ((*jpeg++ != 0xFF) || (*jpeg++ != 0xD8)) {
|
|
ALOGE("%s: %p is not a valid JPEG stream", exiferrmsg, jpeg);
|
|
return false;
|
|
}
|
|
|
|
if ((*jpeg != 0xFF) || (*(jpeg + 1) != 0xE1)) {
|
|
ALOGE("%s: APP1 marker is not found", exiferrmsg);
|
|
return false;
|
|
}
|
|
|
|
if (jpeglen < GetSegLen(jpeg + JPEG_MARKER_SIZE)) {
|
|
ALOGE("%s: Too small stream length %zu", exiferrmsg, jpeglen);
|
|
return false;
|
|
}
|
|
|
|
CAppMarkerWriter writer(jpeg, exif, NULL);
|
|
writer.Update();
|
|
|
|
ALOGD("Successfully updated Exif");
|
|
|
|
return true;
|
|
}
|
|
|
|
void ExtractDebugAttributeInfo(debug_attribute_t *debug, extra_appinfo_t *extra)
|
|
{
|
|
if (!debug) {
|
|
extra->num_of_appmarker = 0;
|
|
return;
|
|
}
|
|
|
|
extra->num_of_appmarker = debug->num_of_appmarker;
|
|
for (int idx = 0; idx < debug->num_of_appmarker; idx++) {
|
|
int appid = debug->idx[idx][0];
|
|
extra->appInfo[idx].appid = appid;
|
|
extra->appInfo[idx].appData = debug->debugData[appid];
|
|
extra->appInfo[idx].dataSize = debug->debugSize[appid];
|
|
}
|
|
}
|