/*
* Copyright Samsung Electronics Co.,LTD.
* Copyright (C) 2010 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 <utils/Log.h>
#include "ExynosJpegEncoderForCamera.h"
static const char ExifAsciiPrefix[] = { 0x41, 0x53, 0x43, 0x49, 0x49, 0x0, 0x0, 0x0 };
#define JPEG_ERROR_LOG ALOGE
#define LOG_TAG "ExynosJpegForCamera"
#define JPEG_THUMBNAIL_QUALITY (60)
#define EXIF_LIMIT_SIZE (64*1024)
#define THUMBNAIL_IMAGE_PIXEL_SIZE (4)
#define MAX_JPG_WIDTH (8192)
#define MAX_JPG_HEIGHT (8192)
#define MAX_INPUT_BUFFER_PLANE_NUM (1)
#define MAX_OUTPUT_BUFFER_PLANE_NUM (1)
ExynosJpegEncoderForCamera::ExynosJpegEncoderForCamera()
{
m_flagCreate = false;
m_jpegMain = NULL;
m_jpegThumb = NULL;
m_thumbnailW = 0;
m_thumbnailH = 0;
m_thumbnailQuality = JPEG_THUMBNAIL_QUALITY;
m_ionJpegClient = 0;
initJpegMemory(&m_stThumbInBuf, MAX_IMAGE_PLANE_NUM);
initJpegMemory(&m_stThumbOutBuf, MAX_IMAGE_PLANE_NUM);
initJpegMemory(&m_stMainInBuf, MAX_IMAGE_PLANE_NUM);
initJpegMemory(&m_stMainOutBuf, MAX_IMAGE_PLANE_NUM);
}
ExynosJpegEncoderForCamera::~ExynosJpegEncoderForCamera()
{
if (m_flagCreate == true) {
this->destroy();
}
}
bool ExynosJpegEncoderForCamera::flagCreate(void)
{
return m_flagCreate;
}
int ExynosJpegEncoderForCamera::create(void)
{
int ret = ERROR_NONE;
if (m_flagCreate == true) {
return ERROR_ALREADY_CREATE;
}
if (m_jpegMain == NULL) {
m_jpegMain = new ExynosJpegEncoder;
if (m_jpegMain == NULL) {
JPEG_ERROR_LOG("ERR(%s):Cannot create ExynosJpegEncoder class\n", __func__);
return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
}
ret = m_jpegMain->create();
if (ret) {
return ret;
}
ret = m_jpegMain->setCache(JPEG_CACHE_ON);
if (ret) {
m_jpegMain->destroy();
return ret;
}
}
m_ionJpegClient = createIonClient(m_ionJpegClient);
if(m_ionJpegClient == 0) {
return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
}
m_stMainOutBuf.ionClient = m_stMainInBuf.ionClient = m_stThumbInBuf.ionClient = m_stThumbOutBuf.ionClient = m_ionJpegClient;
m_flagCreate = true;
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::destroy(void)
{
if (m_flagCreate == false) {
return ERROR_ALREADY_DESTROY;
}
if (m_jpegMain != NULL) {
m_jpegMain->destroy();
delete m_jpegMain;
m_jpegMain = NULL;
}
if (m_jpegThumb != NULL) {
int iSize = sizeof(char)*m_thumbnailW*m_thumbnailH*4;
freeJpegMemory(&m_stThumbInBuf, MAX_IMAGE_PLANE_NUM);
freeJpegMemory(&m_stThumbOutBuf, MAX_IMAGE_PLANE_NUM);
initJpegMemory(&m_stMainInBuf, MAX_IMAGE_PLANE_NUM);
initJpegMemory(&m_stMainOutBuf, MAX_IMAGE_PLANE_NUM);
m_ionJpegClient = deleteIonClient(m_ionJpegClient);
m_stMainOutBuf.ionClient = m_stMainInBuf.ionClient = m_stThumbInBuf.ionClient = m_stThumbOutBuf.ionClient = m_ionJpegClient;
m_jpegThumb->destroy();
delete m_jpegThumb;
m_jpegThumb = NULL;
}
m_flagCreate = false;
m_thumbnailW = 0;
m_thumbnailH = 0;
m_thumbnailQuality = JPEG_THUMBNAIL_QUALITY;
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::setSize(int w, int h)
{
if (m_flagCreate == false) {
return ERROR_NOT_YET_CREATED;
}
return m_jpegMain->setSize(w, h);
}
int ExynosJpegEncoderForCamera::setQuality(int quality)
{
if (m_flagCreate == false) {
return ERROR_NOT_YET_CREATED;
}
return m_jpegMain->setQuality(quality);
}
int ExynosJpegEncoderForCamera::setColorFormat(int colorFormat)
{
if (m_flagCreate == false) {
return ERROR_NOT_YET_CREATED;
}
return m_jpegMain->setColorFormat(colorFormat);
}
int ExynosJpegEncoderForCamera::setJpegFormat(int jpegFormat)
{
if (m_flagCreate == false) {
return ERROR_NOT_YET_CREATED;
}
return m_jpegMain->setJpegFormat(jpegFormat);
}
int ExynosJpegEncoderForCamera::updateConfig(void)
{
if (m_flagCreate == false) {
return ERROR_NOT_YET_CREATED;
}
return m_jpegMain->updateConfig();
}
int ExynosJpegEncoderForCamera::setInBuf(int *buf, char** vBuf, int *size)
{
if (m_flagCreate == false) {
return ERROR_NOT_YET_CREATED;
}
if (buf == NULL) {
return ERROR_BUFFR_IS_NULL;
}
if (size == NULL) {
return ERROR_BUFFR_IS_NULL;
}
int ret = ERROR_NONE;
ret = m_jpegMain->setInBuf(buf, size);
if (ret) {
JPEG_ERROR_LOG("%s::Fail to JPEG input buffer!!\n", __func__);
return ret;
}
m_stMainInBuf.ionBuffer[0] = buf[0];
m_stMainInBuf.iSize[0] = size[0];
m_stMainInBuf.pcBuf[0] = vBuf[0];
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::setOutBuf(int buf, char* vBuf, int size)
{
if (m_flagCreate == false) {
return ERROR_NOT_YET_CREATED;
}
if (buf == NULL) {
return ERROR_BUFFR_IS_NULL;
}
if (size<=0) {
return ERROR_BUFFER_TOO_SMALL;
}
int ret = ERROR_NONE;
ret = m_jpegMain->setOutBuf(buf, size);
if (ret) {
JPEG_ERROR_LOG("%s::Fail to JPEG output buffer!!\n", __func__);
return ret;
}
m_stMainOutBuf.ionBuffer[0] = buf;
m_stMainOutBuf.iSize[0] = size;
m_stMainOutBuf.pcBuf[0] = vBuf;
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::encode(int *size, exif_attribute_t *exifInfo)
{
int ret = ERROR_NONE;
unsigned char *exifOut = NULL;
if (m_flagCreate == false) {
return ERROR_NOT_YET_CREATED;
}
ret = m_jpegMain->encode();
if (ret) {
JPEG_ERROR_LOG("encode failed\n");
return ret;
}
int iJpegSize = m_jpegMain->getJpegSize();
if (iJpegSize<=0) {
JPEG_ERROR_LOG("%s:: output_size is too small(%d)!!\n", __func__, iJpegSize);
return ERROR_OUT_BUFFER_SIZE_TOO_SMALL;
}
int iOutputSize = m_stMainOutBuf.iSize[0];
int iJpegBuffer = m_stMainOutBuf.ionBuffer[0];
char *pcJpegBuffer = m_stMainOutBuf.pcBuf[0];
if (pcJpegBuffer[0] == NULL) {
JPEG_ERROR_LOG("%s::pcJpegBuffer[0] is null!!\n", __func__);
return ERROR_OUT_BUFFER_CREATE_FAIL;
}
if (exifInfo != NULL) {
unsigned int thumbLen, exifLen;
unsigned int bufSize = 0;
if (exifInfo->enableThumb) {
if (encodeThumbnail(&thumbLen)) {
bufSize = EXIF_FILE_SIZE;
exifInfo->enableThumb = false;
} else {
if (thumbLen > EXIF_LIMIT_SIZE) {
bufSize = EXIF_FILE_SIZE;
exifInfo->enableThumb = false;
}
else {
bufSize = EXIF_FILE_SIZE + thumbLen;
}
}
} else {
bufSize = EXIF_FILE_SIZE;
exifInfo->enableThumb = false;
}
exifOut = new unsigned char[bufSize];
if (exifOut == NULL) {
JPEG_ERROR_LOG("%s::Failed to allocate for exifOut\n", __func__);
delete[] exifOut;
return ERROR_EXIFOUT_ALLOC_FAIL;
}
memset(exifOut, 0, bufSize);
if (makeExif (exifOut, exifInfo, &exifLen)) {
JPEG_ERROR_LOG("%s::Failed to make EXIF\n", __func__);
delete[] exifOut;
return ERROR_MAKE_EXIF_FAIL;
}
if (exifLen <= EXIF_LIMIT_SIZE) {
memmove(pcJpegBuffer+exifLen+2, pcJpegBuffer+2, iJpegSize - 2);
memcpy(pcJpegBuffer+2, exifOut, exifLen);
iJpegSize += exifLen;
}
delete[] exifOut;
}
*size = iJpegSize;
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::makeExif (unsigned char *exifOut,
exif_attribute_t *exifInfo,
unsigned int *size,
bool useMainbufForThumb)
{
unsigned char *pCur, *pApp1Start, *pIfdStart, *pGpsIfdPtr, *pNextIfdOffset;
unsigned int tmp, LongerTagOffest = 0, exifSizeExceptThumb;
pApp1Start = pCur = exifOut;
//2 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;
//2 0th IFD TIFF Tags
if (exifInfo->enableGps)
tmp = NUM_0TH_IFD_TIFF;
else
tmp = NUM_0TH_IFD_TIFF - 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);
writeExifIfd(&pCur, EXIF_TAG_SOFTWARE, EXIF_TYPE_ASCII,
strlen((char *)exifInfo->software) + 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;
pCur += IFD_SIZE; // Skip a ifd size for gps IFD pointer
}
pNextIfdOffset = pCur; // Skip a offset size for next IFD offset
pCur += OFFSET_SIZE;
//2 0th IFD Exif Private Tags
pCur = pIfdStart + LongerTagOffest;
tmp = NUM_0TH_IFD_EXIF;
memcpy(pCur, &tmp , NUM_SIZE);
pCur += NUM_SIZE;
LongerTagOffest += NUM_SIZE + NUM_0TH_IFD_EXIF*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);
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_METERING_MODE, EXIF_TYPE_SHORT,
1, exifInfo->metering_mode);
writeExifIfd(&pCur, EXIF_TAG_FLASH, EXIF_TYPE_SHORT,
1, exifInfo->flash);
writeExifIfd(&pCur, EXIF_TAG_FOCAL_LENGTH, EXIF_TYPE_RATIONAL,
1, &exifInfo->focal_length, &LongerTagOffest, pIfdStart);
char code[8] = { 0x00, 0x00, 0x00, 0x49, 0x49, 0x43, 0x53, 0x41 };
int commentsLen = strlen((char *)exifInfo->user_comment) + 1;
memmove(exifInfo->user_comment + sizeof(code), exifInfo->user_comment, commentsLen);
memcpy(exifInfo->user_comment, code, sizeof(code));
writeExifIfd(&pCur, EXIF_TAG_USER_COMMENT, EXIF_TYPE_UNDEFINED,
commentsLen + sizeof(code), exifInfo->user_comment, &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);
writeExifIfd(&pCur, EXIF_TAG_SCENCE_CAPTURE_TYPE, EXIF_TYPE_LONG,
1, exifInfo->scene_capture_type);
tmp = 0;
memcpy(pCur, &tmp, OFFSET_SIZE); // next IFD offset
pCur += OFFSET_SIZE;
//2 0th IFD GPS Info Tags
if (exifInfo->enableGps) {
writeExifIfd(&pGpsIfdPtr, EXIF_TAG_GPS_IFD_POINTER, EXIF_TYPE_LONG,
1, LongerTagOffest); // GPS IFD pointer skipped on 0th IFD
pCur = pIfdStart + LongerTagOffest;
if (exifInfo->gps_processing_method[0] == 0) {
// don't create GPS_PROCESSING_METHOD tag if there isn't any
tmp = NUM_0TH_IFD_GPS - 1;
} else {
tmp = NUM_0TH_IFD_GPS;
}
memcpy(pCur, &tmp, NUM_SIZE);
pCur += NUM_SIZE;
LongerTagOffest += NUM_SIZE + tmp*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);
tmp = strlen((char*)exifInfo->gps_processing_method);
if (tmp > 0) {
if (tmp > 100) {
tmp = 100;
}
unsigned char tmp_buf[100+sizeof(ExifAsciiPrefix)];
memcpy(tmp_buf, ExifAsciiPrefix, sizeof(ExifAsciiPrefix));
memcpy(&tmp_buf[sizeof(ExifAsciiPrefix)], exifInfo->gps_processing_method, tmp);
writeExifIfd(&pCur, EXIF_TAG_GPS_PROCESSING_METHOD, EXIF_TYPE_UNDEFINED,
tmp+sizeof(ExifAsciiPrefix), tmp_buf, &LongerTagOffest, pIfdStart);
}
writeExifIfd(&pCur, EXIF_TAG_GPS_DATESTAMP, EXIF_TYPE_ASCII,
11, exifInfo->gps_datestamp, &LongerTagOffest, pIfdStart);
tmp = 0;
memcpy(pCur, &tmp, OFFSET_SIZE); // next IFD offset
pCur += OFFSET_SIZE;
}
//2 1th IFD TIFF Tags
int iThumbFd = 0;
char *thumbBuf = NULL;
unsigned int thumbSize = 0;
int thumbBufSize = 0;
int ret = ERROR_NONE;
if (useMainbufForThumb) {
if (m_jpegMain) {
ret = m_jpegMain->getOutBuf((int *)&iThumbFd, (int *)&thumbBufSize);
if (ret != ERROR_NONE) {
iThumbFd = -1;
}
thumbSize = (unsigned int)m_jpegMain->getJpegSize();
thumbBuf = m_stMainOutBuf.pcBuf[0];
}
} else {
if (m_jpegThumb) {
ret = m_jpegThumb->getOutBuf((int *)&iThumbFd, (int *)&thumbBufSize);
if (ret != ERROR_NONE) {
iThumbFd = -1;
}
thumbSize = (unsigned int)m_jpegThumb->getJpegSize();
thumbBuf = m_stThumbOutBuf.pcBuf[0];
}
}
if (exifInfo->enableThumb && (thumbBuf != NULL) && (thumbSize != 0)) {
exifSizeExceptThumb = tmp = LongerTagOffest;
memcpy(pNextIfdOffset, &tmp, OFFSET_SIZE); // NEXT IFD offset skipped on 0th IFD
pCur = pIfdStart + LongerTagOffest;
tmp = NUM_1TH_IFD_TIFF;
memcpy(pCur, &tmp, NUM_SIZE);
pCur += NUM_SIZE;
LongerTagOffest += NUM_SIZE + NUM_1TH_IFD_TIFF*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;
memcpy(pCur, &tmp, OFFSET_SIZE); // next IFD offset
pCur += OFFSET_SIZE;
memcpy(pIfdStart + LongerTagOffest,
thumbBuf, thumbSize);
LongerTagOffest += thumbSize;
if (LongerTagOffest > EXIF_LIMIT_SIZE) {
LongerTagOffest = exifSizeExceptThumb;
tmp = 0;
memcpy(pNextIfdOffset, &tmp, OFFSET_SIZE); // NEXT IFD offset skipped on 0th IFD
}
} else {
tmp = 0;
memcpy(pNextIfdOffset, &tmp, OFFSET_SIZE); // NEXT IFD offset skipped on 0th IFD
}
unsigned char App1Marker[2] = { 0xff, 0xe1 };
memcpy(pApp1Start, App1Marker, 2);
pApp1Start += 2;
*size = 10 + LongerTagOffest;
tmp = *size - 2; // APP1 Maker isn't counted
unsigned char size_mm[2] = {(tmp >> 8) & 0xFF, tmp & 0xFF};
memcpy(pApp1Start, size_mm, 2);
return ERROR_NONE;
}
/*
* private member functions
*/
inline void ExynosJpegEncoderForCamera::writeExifIfd(unsigned char **pCur,
unsigned short tag,
unsigned short type,
unsigned int count,
unsigned int 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 ExynosJpegEncoderForCamera::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 ExynosJpegEncoderForCamera::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 ExynosJpegEncoderForCamera::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;
}
int ExynosJpegEncoderForCamera::scaleDownYuv422(char **srcBuf, unsigned int srcW, unsigned int srcH, char **dstBuf, unsigned int dstW, unsigned int dstH)
{
int step_x, step_y;
int src_y_start_pos, dst_pos, src_pos;
char *src_buf = srcBuf[0];
char *dst_buf = dstBuf[0];
if (dstW & 0x01 || dstH & 0x01) {
return ERROR_INVALID_SCALING_WIDTH_HEIGHT;
}
step_x = srcW / dstW;
step_y = srcH / dstH;
unsigned int srcWStride = srcW * 2;
unsigned int stepXStride = step_x * 2;
dst_pos = 0;
for (unsigned int y = 0; y < dstH; y++) {
src_y_start_pos = srcWStride * step_y * y;
for (unsigned int x = 0; x < dstW; x += 2) {
src_pos = src_y_start_pos + (stepXStride * x);
dst_buf[dst_pos++] = src_buf[src_pos ];
dst_buf[dst_pos++] = src_buf[src_pos + 1];
dst_buf[dst_pos++] = src_buf[src_pos + 2];
dst_buf[dst_pos++] = src_buf[src_pos + 3];
}
}
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::scaleDownYuv422_2p(char **srcBuf, unsigned int srcW, unsigned int srcH, char **dstBuf, unsigned int dstW, unsigned int dstH)
{
int32_t step_x, step_y;
int32_t src_y_start_pos, dst_pos, src_pos;
int32_t src_Y_offset;
char *src_buf;
char *dst_buf;
if (dstW % 2 != 0 || dstH % 2 != 0) {
return ERROR_INVALID_SCALING_WIDTH_HEIGHT;
}
step_x = srcW / dstW;
step_y = srcH / dstH;
// Y scale down
src_buf = srcBuf[0];
dst_buf = dstBuf[0];
dst_pos = 0;
for (uint32_t y = 0; y < dstH; y++) {
src_y_start_pos = y * step_y * srcW;
for (uint32_t x = 0; x < dstW; x++) {
src_pos = src_y_start_pos + (x * step_x);
dst_buf[dst_pos++] = src_buf[src_pos];
}
}
// UV scale down
for (uint32_t i = 0; i < dstH; i++) {
src_y_start_pos = i * step_y * srcW + (srcW*srcH);
for (uint32_t j = 0; j < dstW; j += 2) {
src_pos = src_y_start_pos + (j * step_x);
dst_buf[dst_pos++] = src_buf[src_pos ];
dst_buf[dst_pos++] = src_buf[src_pos + 1];
}
}
return ERROR_NONE;
}
// thumbnail
int ExynosJpegEncoderForCamera::setThumbnailSize(int w, int h)
{
if (m_flagCreate == false) {
return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
}
if (w < 0 || MAX_JPG_WIDTH < w) {
return false;
}
if (h < 0 || MAX_JPG_HEIGHT < h) {
return false;
}
m_thumbnailW = w;
m_thumbnailH = h;
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::setThumbnailQuality(int quality)
{
if (m_flagCreate == false) {
return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
}
if (quality < 1 || 100 < quality) {
return false;
}
m_thumbnailQuality = quality;
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::encodeThumbnail(unsigned int *size, bool useMain)
{
int ret = ERROR_NONE;
if (m_flagCreate == false) {
return ERROR_CANNOT_CREATE_EXYNOS_JPEG_ENC_HAL;
}
// create jpeg thumbnail class
if (m_jpegThumb == NULL) {
m_jpegThumb = new ExynosJpegEncoder;
if (m_jpegThumb == NULL) {
JPEG_ERROR_LOG("ERR(%s):Cannot open a jpeg device file\n", __func__);
return ERROR_CANNOT_CREATE_SEC_THUMB;
}
}
ret = m_jpegThumb->create();
if (ret) {
JPEG_ERROR_LOG("ERR(%s):Fail create\n", __func__);
return ret;
}
ret = m_jpegThumb->setCache(JPEG_CACHE_ON);
if (ret) {
JPEG_ERROR_LOG("ERR(%s):Fail cache set\n", __func__);
return ret;
}
void *pConfig = m_jpegMain->getJpegConfig();
if (pConfig == NULL) {
JPEG_ERROR_LOG("ERR(%s):Fail getJpegConfig\n", __func__);
return ERROR_BUFFR_IS_NULL;
}
ret = m_jpegThumb->setJpegConfig(pConfig);
if (ret) {
JPEG_ERROR_LOG("ERR(%s):Fail setJpegConfig\n", __func__);
return ret;
}
/* TODO: Currently we fix the thumbnail quality */
ret = m_jpegThumb->setQuality(JPEG_THUMBNAIL_QUALITY);
if (ret) {
JPEG_ERROR_LOG("ERR(%s):Fail setQuality\n", __func__);
return ret;
}
ret = m_jpegThumb->setSize(m_thumbnailW, m_thumbnailH);
if (ret) {
JPEG_ERROR_LOG("ERR(%s):Fail setSize\n", __func__);
return ret;
}
freeJpegMemory(&m_stThumbInBuf, MAX_IMAGE_PLANE_NUM);
freeJpegMemory(&m_stThumbOutBuf, MAX_IMAGE_PLANE_NUM);
if (m_jpegThumb->setColorBufSize(m_stThumbInBuf.iSize, MAX_IMAGE_PLANE_NUM) != ERROR_NONE) {
return ERROR_INVALID_COLOR_FORMAT;
}
m_stThumbOutBuf.iSize[0] = sizeof(char)*m_thumbnailW*m_thumbnailH*THUMBNAIL_IMAGE_PIXEL_SIZE;
if (allocJpegMemory(&m_stThumbInBuf, MAX_IMAGE_PLANE_NUM) != ERROR_NONE) {
return ERROR_MEM_ALLOC_FAIL;
}
if (allocJpegMemory(&m_stThumbOutBuf, MAX_IMAGE_PLANE_NUM) != ERROR_NONE) {
return ERROR_MEM_ALLOC_FAIL;
}
ret = m_jpegThumb->setInBuf(m_stThumbInBuf.ionBuffer, m_stThumbInBuf.iSize);
if (ret) {
JPEG_ERROR_LOG("ERR(%s):Fail setInBuf\n", __func__);
return ret;
}
ret = m_jpegThumb->setOutBuf(m_stThumbOutBuf.ionBuffer[0], m_stThumbOutBuf.iSize[0]);
if (ret) {
JPEG_ERROR_LOG("ERR(%s):Fail setOutBuf\n", __func__);
return ret;
}
ret = m_jpegThumb->updateConfig();
if (ret) {
JPEG_ERROR_LOG("update config failed\n");
return ret;
}
if (useMain) {
int iTempWidth=0;
int iTempHeight=0;
int iTempColorformat = 0;
iTempColorformat = m_jpegMain->getColorFormat();
ret = m_jpegMain->getSize(&iTempWidth, &iTempHeight);
if (ret) {
JPEG_ERROR_LOG("ERR(%s):Fail getSize\n", __func__);
return ret;
}
switch (iTempColorformat) {
case V4L2_PIX_FMT_YUYV:
ret = scaleDownYuv422(m_stMainInBuf.pcBuf,
iTempWidth,
iTempHeight,
m_stThumbInBuf.pcBuf,
m_thumbnailW,
m_thumbnailH);
break;
case V4L2_PIX_FMT_NV16:
ret = scaleDownYuv422_2p(m_stMainInBuf.pcBuf,
iTempWidth,
iTempHeight,
m_stThumbInBuf.pcBuf,
m_thumbnailW,
m_thumbnailH);
break;
default:
return ERROR_INVALID_COLOR_FORMAT;
break;
}
if (ret) {
JPEG_ERROR_LOG("%s::scaleDown(%d, %d, %d, %d) fail", __func__, iTempWidth, iTempHeight, m_thumbnailW, m_thumbnailH);
return ret;
}
}
else {
return ERROR_IMPLEMENT_NOT_YET;
}
int iOutSizeThumb;
ret = m_jpegThumb->encode();
if (ret) {
JPEG_ERROR_LOG("encode failed\n");
return ret;
}
iOutSizeThumb = m_jpegThumb->getJpegSize();
if (iOutSizeThumb<=0) {
JPEG_ERROR_LOG("jpeg size is too small\n");
return ERROR_THUMB_JPEG_SIZE_TOO_SMALL;
}
*size = (unsigned int)iOutSizeThumb;
return ERROR_NONE;
}
int ExynosJpegEncoderForCamera::createIonClient(ion_client ionClient)
{
if (ionClient == 0) {
ionClient = ion_client_create();
if (ionClient < 0) {
JPEG_ERROR_LOG("[%s]src ion client create failed, value = %d\n", __func__, ionClient);
return 0;
}
}
return ionClient;
}
int ExynosJpegEncoderForCamera::deleteIonClient(ion_client ionClient)
{
if (ionClient != 0) {
if (ionClient > 0) {
ion_client_destroy(ionClient);
}
ionClient = 0;
}
return ionClient;
}
int ExynosJpegEncoderForCamera::allocJpegMemory(struct stJpegMem *pstMem, int iMemoryNum)
{
int ret = ERROR_NONE;
int i = 0;
if (pstMem->ionClient == 0) {
JPEG_ERROR_LOG("[%s] i = %d , ionClient is zero (%d)\n", __func__, i, pstMem->ionClient);
return ERROR_BUFFR_IS_NULL;
}
for (i=0;i<iMemoryNum;i++) {
if (pstMem->iSize[i] == 0) {
break;
}
pstMem->ionBuffer[i] = ion_alloc(pstMem->ionClient, \
pstMem->iSize[i], 0, ION_HEAP_SYSTEM_MASK, 0);
if ((pstMem->ionBuffer[i] == -1) ||(pstMem->ionBuffer[i] == 0)) {
JPEG_ERROR_LOG("[%s]ion_alloc(%d) failed\n", __func__, pstMem->iSize[i]);
pstMem->ionBuffer[i] = -1;
freeJpegMemory(pstMem, iMemoryNum);
return ERROR_MEM_ALLOC_FAIL;
}
pstMem->pcBuf[i] = (char *)ion_map(pstMem->ionBuffer[i], \
pstMem->iSize[i], 0);
if ((pstMem->pcBuf[i] == (char *)MAP_FAILED) || (pstMem->pcBuf[i] == NULL)) {
JPEG_ERROR_LOG("[%s]src ion map failed(%d)\n", __func__, pstMem->iSize[i]);
pstMem->pcBuf[i] = (char *)MAP_FAILED;
freeJpegMemory(pstMem, iMemoryNum);
return ERROR_MEM_ALLOC_FAIL;
}
}
return ERROR_NONE;
}
void ExynosJpegEncoderForCamera::freeJpegMemory(struct stJpegMem *pstMem, int iMemoryNum)
{
int i =0 ;
if (pstMem->ionClient == 0) {
return;
}
for (i=0;i<iMemoryNum;i++) {
if (pstMem->ionBuffer[i] != -1) {
if (pstMem->pcBuf[i] != (char *)MAP_FAILED) {
ion_unmap(pstMem->pcBuf[i], pstMem->iSize[i]);
}
ion_free(pstMem->ionBuffer[i]);
}
pstMem->ionBuffer[i] = -1;
pstMem->pcBuf[i] = (char *)MAP_FAILED;
pstMem->iSize[i] = 0;
}
}
void ExynosJpegEncoderForCamera::initJpegMemory(struct stJpegMem *pstMem, int iMemoryNum)
{
int i =0 ;
for (i=0;i<iMemoryNum;i++) {
pstMem->pcBuf[i] = (char *)MAP_FAILED;
pstMem->ionBuffer[i] = -1;
pstMem->iSize[i] = 0;
}
pstMem->ionClient = 0;
}