blob: 363a6028cfe94fd10969d14b3909bae7981b7d6f [file] [log] [blame]
/*
* Copyright 2008, The Android Open Source Project
* Copyright 2013, Samsung Electronics Co. LTD
*
* 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.
*/
/*!
* \file Exif.cpp
* \brief source file for Android Camera Ext HAL
* \author teahyung kim (tkon.kim@samsung.com)
* \date 2013/04/30
*
*/
#define LOG_TAG "Exif"
#include <cutils/compiler.h>
#include <utils/Log.h>
#include <fcntl.h>
#include <camera/Camera.h>
#include "Exif.h"
namespace android {
#define CLEAR(x) memset(&(x), 0, sizeof(x))
const char Exif::DEFAULT_MAKER[] = "SAMSUNG";
const char Exif::DEFAULT_MODEL[] = "Exynos";
const char Exif::DEFAULT_SOFTWARE[] = "Exif Software Version 1.0.2.0";
const char Exif::DEFAULT_EXIF_VERSION[] = "0220";
const char Exif::DEFAULT_USERCOMMENTS[] = "User comments";
const int Exif::DEFAULT_YCBCR_POSITIONING = 1;
const int Exif::DEFAULT_EXPOSURE_PROGRAM = 2;
const int Exif::DEFAULT_FLASH = 0;
const int Exif::DEFAULT_COLOR_SPACE = 1;
const int Exif::DEFAULT_EXPOSURE_MODE = EXIF_EXPOSURE_AUTO;
const int Exif::DEFAULT_APEX_DEN = 100;
const int Exif::DEFAULT_SENSING_METHOD = 2;
const int Exif::DEFAULT_COMPRESSION = 6;
const int Exif::DEFAULT_RESOLUTION_NUM = 72;
const int Exif::DEFAULT_RESOLUTION_DEN = 1;
const int Exif::DEFAULT_RESOLUTION_UNIT = 2;
const int Exif::DEFAULT_CONTINUOUS_SHOT_INFO = 0;
Exif::Exif(int cameraId, int CameraType)
{
mCameraId = cameraId;
mCameraType = CameraType;
mNum0thIfdTiff = 10;
if (cameraId == CAMERA_FACING_BACK) {
if (mCameraType == CAMERA_TYPE_SOC)
mNum0thIfdExif = 23;
else
mNum0thIfdExif = 27;
} else {
mNum0thIfdExif = 14;
}
mNum0thIfdGps = 10;
mNum1thIfdTiff = 9;
}
Exif::~Exif()
{
}
uint32_t Exif::make(void *exifOutBuf,
exif_attribute_t *exifInfo,
unsigned int exifOutBufSize,
unsigned char *thumbBuf,
unsigned int thumbSize)
{
ALOGV("makeExif E");
unsigned char *pCur, *pApp1Start, *pIfdStart, *pGpsIfdPtr, *pNextIfdOffset;
unsigned int tmp, LongerTagOffest = 0;
pApp1Start = pCur = (unsigned char *)exifOutBuf;
/* Exif Identifier Code & TIFF Header */
pCur += 4;
/* Skip 4 Byte for APP1 marker and length */
unsigned char ExifIdentifierCode[6] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
memcpy(pCur, ExifIdentifierCode, 6);
pCur += 6;
/* Byte Order - little endian, Offset of IFD - 0x00000008.H */
unsigned char TiffHeader[8] = {0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00};
memcpy(pCur, TiffHeader, 8);
pIfdStart = pCur;
pCur += 8;
const char asciiPrefix[] = {0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0};
unsigned char tmpBuf[256] = {0, };
size_t len;
/* 0th IFD TIFF Tags */
if (exifInfo->enableGps)
tmp = mNum0thIfdTiff;
else
tmp = mNum0thIfdTiff - 1;
memcpy(pCur, &tmp, NUM_SIZE);
pCur += NUM_SIZE;
LongerTagOffest += 8 + NUM_SIZE + tmp * IFD_SIZE + OFFSET_SIZE;
writeExifIfd(&pCur, EXIF_TAG_IMAGE_WIDTH, EXIF_TYPE_LONG,
1, exifInfo->width);
writeExifIfd(&pCur, EXIF_TAG_IMAGE_HEIGHT, EXIF_TYPE_LONG,
1, exifInfo->height);
writeExifIfd(&pCur, EXIF_TAG_MAKE, EXIF_TYPE_ASCII,
strlen((char *)exifInfo->maker) + 1, exifInfo->maker, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_MODEL, EXIF_TYPE_ASCII,
strlen((char *)exifInfo->model) + 1, exifInfo->model, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_ORIENTATION, EXIF_TYPE_SHORT,
1, exifInfo->orientation);
if (CAMERA_TYPE_ISP == mCameraType) {
writeExifIfd(&pCur, EXIF_TAG_SOFTWARE, EXIF_TYPE_ASCII,
strlen((char *)exifInfo->software) + 1, exifInfo->software, &LongerTagOffest, pIfdStart);
} else {
writeExifIfd(&pCur, EXIF_TAG_SOFTWARE, EXIF_TYPE_ASCII,
1, exifInfo->software, &LongerTagOffest, pIfdStart);
}
writeExifIfd(&pCur, EXIF_TAG_DATE_TIME, EXIF_TYPE_ASCII,
20, exifInfo->date_time, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_YCBCR_POSITIONING, EXIF_TYPE_SHORT,
1, exifInfo->ycbcr_positioning);
writeExifIfd(&pCur, EXIF_TAG_EXIF_IFD_POINTER, EXIF_TYPE_LONG,
1, LongerTagOffest);
if (exifInfo->enableGps) {
pGpsIfdPtr = pCur;
/* Skip a ifd size for gps IFD pointer */
pCur += IFD_SIZE;
}
/* Skip a offset size for next IFD offset */
pNextIfdOffset = pCur;
pCur += OFFSET_SIZE;
/* 0th IFD Exif Private Tags */
pCur = pIfdStart + LongerTagOffest;
tmp = mNum0thIfdExif;
memcpy(pCur, &tmp , NUM_SIZE);
pCur += NUM_SIZE;
LongerTagOffest += NUM_SIZE + mNum0thIfdExif*IFD_SIZE + OFFSET_SIZE;
writeExifIfd(&pCur, EXIF_TAG_EXPOSURE_TIME, EXIF_TYPE_RATIONAL,
1, &exifInfo->exposure_time, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_FNUMBER, EXIF_TYPE_RATIONAL,
1, &exifInfo->fnumber, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_EXPOSURE_PROGRAM, EXIF_TYPE_SHORT,
1, exifInfo->exposure_program);
writeExifIfd(&pCur, EXIF_TAG_ISO_SPEED_RATING, EXIF_TYPE_SHORT,
1, exifInfo->iso_speed_rating);
writeExifIfd(&pCur, EXIF_TAG_EXIF_VERSION, EXIF_TYPE_UNDEFINED,
4, exifInfo->exif_version);
writeExifIfd(&pCur, EXIF_TAG_DATE_TIME_ORG, EXIF_TYPE_ASCII,
20, exifInfo->date_time, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_DATE_TIME_DIGITIZE, EXIF_TYPE_ASCII,
20, exifInfo->date_time, &LongerTagOffest, pIfdStart);
if (mCameraId == CAMERA_FACING_BACK) {
if (CAMERA_TYPE_ISP == mCameraType) {
writeExifIfd(&pCur, EXIF_TAG_SHUTTER_SPEED, EXIF_TYPE_SRATIONAL,
1, (rational_t *)&exifInfo->shutter_speed, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_APERTURE, EXIF_TYPE_RATIONAL,
1, &exifInfo->aperture, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_BRIGHTNESS, EXIF_TYPE_SRATIONAL,
1, (rational_t *)&exifInfo->brightness, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_EXPOSURE_BIAS, EXIF_TYPE_SRATIONAL,
1, (rational_t *)&exifInfo->exposure_bias, &LongerTagOffest, pIfdStart);
}
writeExifIfd(&pCur, EXIF_TAG_MAX_APERTURE, EXIF_TYPE_RATIONAL,
1, &exifInfo->max_aperture, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_DIGITAL_ZOOM_RATIO, EXIF_TYPE_RATIONAL,
1, &exifInfo->digital_zoom_ratio, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_METERING_MODE, EXIF_TYPE_SHORT,
1, exifInfo->metering_mode);
writeExifIfd(&pCur, EXIF_TAG_LIGHT_SOURCE, EXIF_TYPE_SHORT,
1, exifInfo->light_source);
writeExifIfd(&pCur, EXIF_TAG_FLASH, EXIF_TYPE_SHORT,
1, exifInfo->flash);
writeExifIfd(&pCur, EXIF_TAG_FOCAL_35mm_LENGTH, EXIF_TYPE_SHORT,
1, exifInfo->focal_35mm_length);
}
writeExifIfd(&pCur, EXIF_TAG_FOCAL_LENGTH, EXIF_TYPE_RATIONAL,
1, &exifInfo->focal_length, &LongerTagOffest, pIfdStart);
CLEAR(tmpBuf);
len = strlen((char *)exifInfo->user_comment) + 1;
memcpy(tmpBuf, asciiPrefix, sizeof(asciiPrefix));
memcpy(tmpBuf + sizeof(asciiPrefix), exifInfo->user_comment, len);
writeExifIfd(&pCur, EXIF_TAG_USER_COMMENT, EXIF_TYPE_UNDEFINED,
len + sizeof(asciiPrefix), tmpBuf, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_COLOR_SPACE, EXIF_TYPE_SHORT,
1, exifInfo->color_space);
writeExifIfd(&pCur, EXIF_TAG_PIXEL_X_DIMENSION, EXIF_TYPE_LONG,
1, exifInfo->width);
writeExifIfd(&pCur, EXIF_TAG_PIXEL_Y_DIMENSION, EXIF_TYPE_LONG,
1, exifInfo->height);
writeExifIfd(&pCur, EXIF_TAG_EXPOSURE_MODE, EXIF_TYPE_LONG,
1, exifInfo->exposure_mode);
writeExifIfd(&pCur, EXIF_TAG_WHITE_BALANCE, EXIF_TYPE_LONG,
1, exifInfo->white_balance);
if (mCameraId == CAMERA_FACING_BACK) {
writeExifIfd(&pCur, EXIF_TAG_SCENCE_CAPTURE_TYPE, EXIF_TYPE_LONG,
1, exifInfo->scene_capture_type);
writeExifIfd(&pCur, EXIF_TAG_IMAGE_UNIQUE_ID, EXIF_TYPE_ASCII,
12, exifInfo->unique_id, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_SENSING_METHOD, EXIF_TYPE_SHORT,
1, exifInfo->sensing_method);
}
tmp = 0;
/* next IFD offset */
memcpy(pCur, &tmp, OFFSET_SIZE);
pCur += OFFSET_SIZE;
/* 0th IFD GPS Info Tags */
if (exifInfo->enableGps) {
/* GPS IFD pointer skipped on 0th IFD */
writeExifIfd(&pGpsIfdPtr, EXIF_TAG_GPS_IFD_POINTER, EXIF_TYPE_LONG,
1, LongerTagOffest);
pCur = pIfdStart + LongerTagOffest;
tmp = mNum0thIfdGps;
memcpy(pCur, &tmp, NUM_SIZE);
pCur += NUM_SIZE;
LongerTagOffest += NUM_SIZE + mNum0thIfdGps * IFD_SIZE + OFFSET_SIZE;
writeExifIfd(&pCur, EXIF_TAG_GPS_VERSION_ID, EXIF_TYPE_BYTE,
4, exifInfo->gps_version_id);
writeExifIfd(&pCur, EXIF_TAG_GPS_LATITUDE_REF, EXIF_TYPE_ASCII,
2, exifInfo->gps_latitude_ref);
writeExifIfd(&pCur, EXIF_TAG_GPS_LATITUDE, EXIF_TYPE_RATIONAL,
3, exifInfo->gps_latitude, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_GPS_LONGITUDE_REF, EXIF_TYPE_ASCII,
2, exifInfo->gps_longitude_ref);
writeExifIfd(&pCur, EXIF_TAG_GPS_LONGITUDE, EXIF_TYPE_RATIONAL,
3, exifInfo->gps_longitude, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_GPS_ALTITUDE_REF, EXIF_TYPE_BYTE,
1, exifInfo->gps_altitude_ref);
writeExifIfd(&pCur, EXIF_TAG_GPS_ALTITUDE, EXIF_TYPE_RATIONAL,
1, &exifInfo->gps_altitude, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_GPS_TIMESTAMP, EXIF_TYPE_RATIONAL,
3, exifInfo->gps_timestamp, &LongerTagOffest, pIfdStart);
CLEAR(tmpBuf);
len = strlen((char *)exifInfo->gps_processing_method);
memcpy(tmpBuf, asciiPrefix, sizeof(asciiPrefix));
memcpy(tmpBuf + sizeof(asciiPrefix), exifInfo->gps_processing_method, len);
writeExifIfd(&pCur, EXIF_TAG_GPS_PROCESSING_METHOD, EXIF_TYPE_UNDEFINED,
len + sizeof(asciiPrefix), tmpBuf, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_GPS_DATESTAMP, EXIF_TYPE_ASCII,
11, exifInfo->gps_datestamp, &LongerTagOffest, pIfdStart);
tmp = 0;
/* next IFD offset */
memcpy(pCur, &tmp, OFFSET_SIZE);
pCur += OFFSET_SIZE;
}
/* 1th IFD TIFF Tags */
if ((thumbBuf != NULL) && (thumbSize > 0)) {
if (CC_UNLIKELY(!exifOutBufSize)) {
ALOGE("makeExif: error, exifOutBufSize is zero");
return 0;
}
tmp = LongerTagOffest;
/* NEXT IFD offset skipped on 0th IFD */
memcpy(pNextIfdOffset, &tmp, OFFSET_SIZE);
pCur = pIfdStart + LongerTagOffest;
tmp = mNum1thIfdTiff;
memcpy(pCur, &tmp, NUM_SIZE);
pCur += NUM_SIZE;
LongerTagOffest += NUM_SIZE + mNum1thIfdTiff * IFD_SIZE + OFFSET_SIZE;
writeExifIfd(&pCur, EXIF_TAG_IMAGE_WIDTH, EXIF_TYPE_LONG,
1, exifInfo->widthThumb);
writeExifIfd(&pCur, EXIF_TAG_IMAGE_HEIGHT, EXIF_TYPE_LONG,
1, exifInfo->heightThumb);
writeExifIfd(&pCur, EXIF_TAG_COMPRESSION_SCHEME, EXIF_TYPE_SHORT,
1, exifInfo->compression_scheme);
writeExifIfd(&pCur, EXIF_TAG_ORIENTATION, EXIF_TYPE_SHORT,
1, exifInfo->orientation);
writeExifIfd(&pCur, EXIF_TAG_X_RESOLUTION, EXIF_TYPE_RATIONAL,
1, &exifInfo->x_resolution, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_Y_RESOLUTION, EXIF_TYPE_RATIONAL,
1, &exifInfo->y_resolution, &LongerTagOffest, pIfdStart);
writeExifIfd(&pCur, EXIF_TAG_RESOLUTION_UNIT, EXIF_TYPE_SHORT,
1, exifInfo->resolution_unit);
writeExifIfd(&pCur, EXIF_TAG_JPEG_INTERCHANGE_FORMAT, EXIF_TYPE_LONG,
1, LongerTagOffest);
writeExifIfd(&pCur, EXIF_TAG_JPEG_INTERCHANGE_FORMAT_LEN, EXIF_TYPE_LONG,
1, thumbSize);
tmp = 0;
/* next IFD offset */
memcpy(pCur, &tmp, OFFSET_SIZE);
pCur += OFFSET_SIZE;
if (CC_UNLIKELY(pIfdStart + LongerTagOffest + thumbSize >= (void *)((uint32_t)exifOutBuf + exifOutBufSize))) {
ALOGE("makeExif: error, thumbnail size(%d bytes) is too big ", thumbSize);
return 0;
}
memcpy(pIfdStart + LongerTagOffest,
thumbBuf, thumbSize);
LongerTagOffest += thumbSize;
} else {
tmp = 0;
/* NEXT IFD offset skipped on 0th IFD */
memcpy(pNextIfdOffset, &tmp, OFFSET_SIZE);
}
unsigned char App1Marker[2] = {0xff, 0xe1};
memcpy(pApp1Start, App1Marker, 2);
pApp1Start += 2;
uint32_t size = 10 + LongerTagOffest;
/* APP1 Maker isn't counted */
tmp = size - 2;
unsigned char size_le[2] = {(unsigned char)((tmp >> 8) & 0xFF), (unsigned char)(tmp & 0xFF)};
memcpy(pApp1Start, size_le, 2);
ALOGV("makeExif X: size %d byte", size);
return size;
}
inline void Exif::writeExifIfd(unsigned char **pCur,
unsigned short tag,
unsigned short type,
unsigned int count,
uint32_t value)
{
memcpy(*pCur, &tag, 2);
*pCur += 2;
memcpy(*pCur, &type, 2);
*pCur += 2;
memcpy(*pCur, &count, 4);
*pCur += 4;
memcpy(*pCur, &value, 4);
*pCur += 4;
}
inline void Exif::writeExifIfd(unsigned char **pCur,
unsigned short tag,
unsigned short type,
unsigned int count,
unsigned char *pValue)
{
char buf[4] = {0,};
memcpy(buf, pValue, count);
memcpy(*pCur, &tag, 2);
*pCur += 2;
memcpy(*pCur, &type, 2);
*pCur += 2;
memcpy(*pCur, &count, 4);
*pCur += 4;
memcpy(*pCur, buf, 4);
*pCur += 4;
}
inline void Exif::writeExifIfd(unsigned char **pCur,
unsigned short tag,
unsigned short type,
unsigned int count,
unsigned char *pValue,
unsigned int *offset,
unsigned char *start)
{
memcpy(*pCur, &tag, 2);
*pCur += 2;
memcpy(*pCur, &type, 2);
*pCur += 2;
memcpy(*pCur, &count, 4);
*pCur += 4;
memcpy(*pCur, offset, 4);
*pCur += 4;
memcpy(start + *offset, pValue, count);
*offset += count;
}
inline void Exif::writeExifIfd(unsigned char **pCur,
unsigned short tag,
unsigned short type,
unsigned int count,
rational_t *pValue,
unsigned int *offset,
unsigned char *start)
{
memcpy(*pCur, &tag, 2);
*pCur += 2;
memcpy(*pCur, &type, 2);
*pCur += 2;
memcpy(*pCur, &count, 4);
*pCur += 4;
memcpy(*pCur, offset, 4);
*pCur += 4;
memcpy(start + *offset, pValue, 8 * count);
*offset += 8 * count;
}
};