upload android base code part6
This commit is contained in:
parent
421e214c7d
commit
4e516ec6ed
35396 changed files with 9188716 additions and 0 deletions
413
android/device/generic/goldfish/camera/Exif.cpp
Normal file
413
android/device/generic/goldfish/camera/Exif.cpp
Normal file
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#define LOG_NDEBUG 0
|
||||
#define LOG_TAG "EmulatedCamera_Exif"
|
||||
#include <cutils/log.h>
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <CameraParameters.h>
|
||||
|
||||
using ::android::hardware::camera::common::V1_0::helper::CameraParameters;
|
||||
using ::android::hardware::camera::common::V1_0::helper::Size;
|
||||
|
||||
#include "Exif.h"
|
||||
|
||||
#include <libexif/exif-data.h>
|
||||
#include <libexif/exif-entry.h>
|
||||
#include <libexif/exif-ifd.h>
|
||||
#include <libexif/exif-tag.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// For GPS timestamping we want to ensure we use a 64-bit time_t, 32-bit
|
||||
// platforms have time64_t but 64-bit platforms do not.
|
||||
#if defined(__LP64__)
|
||||
#include <time.h>
|
||||
using Timestamp = time_t;
|
||||
#define TIMESTAMP_TO_TM(timestamp, tm) gmtime_r(timestamp, tm)
|
||||
#else
|
||||
#include <time64.h>
|
||||
using Timestamp = time64_t;
|
||||
#define TIMESTAMP_TO_TM(timestamp, tm) gmtime64_r(timestamp, tm)
|
||||
#endif
|
||||
|
||||
namespace android {
|
||||
|
||||
// A prefix that is used for tags with the "undefined" format to indicate that
|
||||
// the contents are ASCII encoded. See the user comment section of the EXIF spec
|
||||
// for more details http://www.exif.org/Exif2-2.PDF
|
||||
static const unsigned char kAsciiPrefix[] = {
|
||||
0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00 // "ASCII\0\0\0"
|
||||
};
|
||||
|
||||
// Remove an existing EXIF entry from |exifData| if it exists. This is useful
|
||||
// when replacing existing data, it's easier to just remove the data and
|
||||
// re-allocate it than to adjust the amount of allocated data.
|
||||
static void removeExistingEntry(ExifData* exifData, ExifIfd ifd, int tag) {
|
||||
ExifEntry* entry = exif_content_get_entry(exifData->ifd[ifd],
|
||||
static_cast<ExifTag>(tag));
|
||||
if (entry) {
|
||||
exif_content_remove_entry(exifData->ifd[ifd], entry);
|
||||
}
|
||||
}
|
||||
|
||||
static ExifEntry* allocateEntry(int tag,
|
||||
ExifFormat format,
|
||||
unsigned int numComponents) {
|
||||
ExifMem* mem = exif_mem_new_default();
|
||||
ExifEntry* entry = exif_entry_new_mem(mem);
|
||||
|
||||
unsigned int size = numComponents * exif_format_get_size(format);
|
||||
entry->data = reinterpret_cast<unsigned char*>(exif_mem_alloc(mem, size));
|
||||
entry->size = size;
|
||||
entry->tag = static_cast<ExifTag>(tag);
|
||||
entry->components = numComponents;
|
||||
entry->format = format;
|
||||
|
||||
exif_mem_unref(mem);
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Create an entry and place it in |exifData|, the entry is initialized with an
|
||||
// array of floats from |values|
|
||||
template<size_t N>
|
||||
static bool createEntry(ExifData* exifData,
|
||||
ExifIfd ifd,
|
||||
int tag,
|
||||
const float (&values)[N],
|
||||
float denominator = 1000.0) {
|
||||
removeExistingEntry(exifData, ifd, tag);
|
||||
ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
|
||||
ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_RATIONAL, N);
|
||||
exif_content_add_entry(exifData->ifd[ifd], entry);
|
||||
unsigned int rationalSize = exif_format_get_size(EXIF_FORMAT_RATIONAL);
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
ExifRational rational = {
|
||||
static_cast<uint32_t>(values[i] * denominator),
|
||||
static_cast<uint32_t>(denominator)
|
||||
};
|
||||
|
||||
exif_set_rational(&entry->data[i * rationalSize], byteOrder, rational);
|
||||
}
|
||||
|
||||
// Unref entry after changing owner to the ExifData struct
|
||||
exif_entry_unref(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create an entry with a single float |value| in it and place it in |exifData|
|
||||
static bool createEntry(ExifData* exifData,
|
||||
ExifIfd ifd,
|
||||
int tag,
|
||||
const float value,
|
||||
float denominator = 1000.0) {
|
||||
float values[1] = { value };
|
||||
// Recycling functions is good for the environment
|
||||
return createEntry(exifData, ifd, tag, values, denominator);
|
||||
}
|
||||
|
||||
// Create an entry and place it in |exifData|, the entry contains the raw data
|
||||
// pointed to by |data| of length |size|.
|
||||
static bool createEntry(ExifData* exifData,
|
||||
ExifIfd ifd,
|
||||
int tag,
|
||||
const unsigned char* data,
|
||||
size_t size,
|
||||
ExifFormat format = EXIF_FORMAT_UNDEFINED) {
|
||||
removeExistingEntry(exifData, ifd, tag);
|
||||
ExifEntry* entry = allocateEntry(tag, format, size);
|
||||
memcpy(entry->data, data, size);
|
||||
exif_content_add_entry(exifData->ifd[ifd], entry);
|
||||
// Unref entry after changing owner to the ExifData struct
|
||||
exif_entry_unref(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create an entry and place it in |exifData|, the entry is initialized with
|
||||
// the string provided in |value|
|
||||
static bool createEntry(ExifData* exifData,
|
||||
ExifIfd ifd,
|
||||
int tag,
|
||||
const char* value) {
|
||||
unsigned int length = strlen(value) + 1;
|
||||
const unsigned char* data = reinterpret_cast<const unsigned char*>(value);
|
||||
return createEntry(exifData, ifd, tag, data, length, EXIF_FORMAT_ASCII);
|
||||
}
|
||||
|
||||
// Create an entry and place it in |exifData|, the entry is initialized with a
|
||||
// single byte in |value|
|
||||
static bool createEntry(ExifData* exifData,
|
||||
ExifIfd ifd,
|
||||
int tag,
|
||||
uint8_t value) {
|
||||
return createEntry(exifData, ifd, tag, &value, 1, EXIF_FORMAT_BYTE);
|
||||
}
|
||||
|
||||
// Create an entry and place it in |exifData|, the entry is default initialized
|
||||
// by the exif library based on |tag|
|
||||
static bool createEntry(ExifData* exifData,
|
||||
ExifIfd ifd,
|
||||
int tag) {
|
||||
removeExistingEntry(exifData, ifd, tag);
|
||||
ExifEntry* entry = exif_entry_new();
|
||||
exif_content_add_entry(exifData->ifd[ifd], entry);
|
||||
exif_entry_initialize(entry, static_cast<ExifTag>(tag));
|
||||
// Unref entry after changing owner to the ExifData struct
|
||||
exif_entry_unref(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create an entry with a single EXIF LONG (32-bit value) and place it in
|
||||
// |exifData|.
|
||||
static bool createEntry(ExifData* exifData,
|
||||
ExifIfd ifd,
|
||||
int tag,
|
||||
int value) {
|
||||
removeExistingEntry(exifData, ifd, tag);
|
||||
ExifByteOrder byteOrder = exif_data_get_byte_order(exifData);
|
||||
ExifEntry* entry = allocateEntry(tag, EXIF_FORMAT_LONG, 1);
|
||||
exif_content_add_entry(exifData->ifd[ifd], entry);
|
||||
exif_set_long(entry->data, byteOrder, value);
|
||||
|
||||
// Unref entry after changing owner to the ExifData struct
|
||||
exif_entry_unref(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getCameraParam(const CameraParameters& parameters,
|
||||
const char* parameterKey,
|
||||
const char** outValue) {
|
||||
const char* value = parameters.get(parameterKey);
|
||||
if (value) {
|
||||
*outValue = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getCameraParam(const CameraParameters& parameters,
|
||||
const char* parameterKey,
|
||||
float* outValue) {
|
||||
const char* value = parameters.get(parameterKey);
|
||||
if (value) {
|
||||
*outValue = parameters.getFloat(parameterKey);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool getCameraParam(const CameraParameters& parameters,
|
||||
const char* parameterKey,
|
||||
int64_t* outValue) {
|
||||
const char* value = parameters.get(parameterKey);
|
||||
if (value) {
|
||||
char dummy = 0;
|
||||
// Attempt to scan an extra character and then make sure it was not
|
||||
// scanned by checking that the return value indicates only one item.
|
||||
// This way we fail on any trailing characters
|
||||
if (sscanf(value, "%" SCNd64 "%c", outValue, &dummy) == 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert a GPS coordinate represented as a decimal degree value to sexagesimal
|
||||
// GPS coordinates comprised of <degrees> <minutes>' <seconds>"
|
||||
static void convertGpsCoordinate(float degrees, float (*result)[3]) {
|
||||
float absDegrees = fabs(degrees);
|
||||
// First value is degrees without any decimal digits
|
||||
(*result)[0] = floor(absDegrees);
|
||||
|
||||
// Subtract degrees so we only have the fraction left, then multiply by
|
||||
// 60 to get the minutes
|
||||
float minutes = (absDegrees - (*result)[0]) * 60.0f;
|
||||
(*result)[1] = floor(minutes);
|
||||
|
||||
// Same thing for seconds but here we store seconds with the fraction
|
||||
float seconds = (minutes - (*result)[1]) * 60.0f;
|
||||
(*result)[2] = seconds;
|
||||
}
|
||||
|
||||
// Convert a UNIX epoch timestamp to a timestamp comprised of three floats for
|
||||
// hour, minute and second, and a date part that is represented as a string.
|
||||
static bool convertTimestampToTimeAndDate(int64_t timestamp,
|
||||
float (*timeValues)[3],
|
||||
std::string* date) {
|
||||
Timestamp time = timestamp;
|
||||
struct tm utcTime;
|
||||
if (TIMESTAMP_TO_TM(&time, &utcTime) == nullptr) {
|
||||
ALOGE("Could not decompose timestamp into components");
|
||||
return false;
|
||||
}
|
||||
(*timeValues)[0] = utcTime.tm_hour;
|
||||
(*timeValues)[1] = utcTime.tm_min;
|
||||
(*timeValues)[2] = utcTime.tm_sec;
|
||||
|
||||
char buffer[64] = {};
|
||||
if (strftime(buffer, sizeof(buffer), "%Y:%m:%d", &utcTime) == 0) {
|
||||
ALOGE("Could not construct date string from timestamp");
|
||||
return false;
|
||||
}
|
||||
*date = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
ExifData* createExifData(const CameraParameters& params) {
|
||||
ExifData* exifData = exif_data_new();
|
||||
|
||||
exif_data_set_option(exifData, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
|
||||
exif_data_set_data_type(exifData, EXIF_DATA_TYPE_COMPRESSED);
|
||||
exif_data_set_byte_order(exifData, EXIF_BYTE_ORDER_INTEL);
|
||||
|
||||
// Create mandatory exif fields and set their default values
|
||||
exif_data_fix(exifData);
|
||||
|
||||
float triplet[3];
|
||||
float floatValue = 0.0f;
|
||||
const char* stringValue;
|
||||
int64_t degrees;
|
||||
|
||||
// Datetime, creating and initializing a datetime tag will automatically
|
||||
// set the current date and time in the tag so just do that.
|
||||
createEntry(exifData, EXIF_IFD_0, EXIF_TAG_DATE_TIME);
|
||||
|
||||
// Make and model
|
||||
createEntry(exifData, EXIF_IFD_0, EXIF_TAG_MAKE, "Emulator-Goldfish");
|
||||
createEntry(exifData, EXIF_IFD_0, EXIF_TAG_MODEL, "Emulator-Goldfish");
|
||||
|
||||
// Picture size
|
||||
int width = -1, height = -1;
|
||||
params.getPictureSize(&width, &height);
|
||||
if (width >= 0 && height >= 0) {
|
||||
createEntry(exifData, EXIF_IFD_EXIF,
|
||||
EXIF_TAG_PIXEL_X_DIMENSION, width);
|
||||
createEntry(exifData, EXIF_IFD_EXIF,
|
||||
EXIF_TAG_PIXEL_Y_DIMENSION, height);
|
||||
}
|
||||
// Orientation
|
||||
if (getCameraParam(params,
|
||||
CameraParameters::KEY_ROTATION,
|
||||
°rees)) {
|
||||
// Exif orientation values, please refer to
|
||||
// http://www.exif.org/Exif2-2.PDF, Section 4.6.4-A-Orientation
|
||||
// Or these websites:
|
||||
// http://sylvana.net/jpegcrop/exif_orientation.html
|
||||
// http://www.impulseadventure.com/photo/exif-orientation.html
|
||||
enum {
|
||||
EXIF_ROTATE_CAMERA_CW0 = 1,
|
||||
EXIF_ROTATE_CAMERA_CW90 = 6,
|
||||
EXIF_ROTATE_CAMERA_CW180 = 3,
|
||||
EXIF_ROTATE_CAMERA_CW270 = 8,
|
||||
};
|
||||
uint16_t exifOrien = 1;
|
||||
switch (degrees) {
|
||||
case 0:
|
||||
exifOrien = EXIF_ROTATE_CAMERA_CW0;
|
||||
break;
|
||||
case 90:
|
||||
exifOrien = EXIF_ROTATE_CAMERA_CW90;
|
||||
break;
|
||||
case 180:
|
||||
exifOrien = EXIF_ROTATE_CAMERA_CW180;
|
||||
break;
|
||||
case 270:
|
||||
exifOrien = EXIF_ROTATE_CAMERA_CW270;
|
||||
break;
|
||||
}
|
||||
createEntry(exifData, EXIF_IFD_0, EXIF_TAG_ORIENTATION, exifOrien);
|
||||
}
|
||||
// Focal length
|
||||
if (getCameraParam(params,
|
||||
CameraParameters::KEY_FOCAL_LENGTH,
|
||||
&floatValue)) {
|
||||
createEntry(exifData, EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH, floatValue);
|
||||
}
|
||||
// GPS latitude and reference, reference indicates sign, store unsigned
|
||||
if (getCameraParam(params,
|
||||
CameraParameters::KEY_GPS_LATITUDE,
|
||||
&floatValue)) {
|
||||
convertGpsCoordinate(floatValue, &triplet);
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE, triplet);
|
||||
|
||||
const char* ref = floatValue < 0.0f ? "S" : "N";
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, ref);
|
||||
}
|
||||
// GPS longitude and reference, reference indicates sign, store unsigned
|
||||
if (getCameraParam(params,
|
||||
CameraParameters::KEY_GPS_LONGITUDE,
|
||||
&floatValue)) {
|
||||
convertGpsCoordinate(floatValue, &triplet);
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE, triplet);
|
||||
|
||||
const char* ref = floatValue < 0.0f ? "W" : "E";
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, ref);
|
||||
}
|
||||
// GPS altitude and reference, reference indicates sign, store unsigned
|
||||
if (getCameraParam(params,
|
||||
CameraParameters::KEY_GPS_ALTITUDE,
|
||||
&floatValue)) {
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE,
|
||||
static_cast<float>(fabs(floatValue)));
|
||||
|
||||
// 1 indicated below sea level, 0 indicates above sea level
|
||||
uint8_t ref = floatValue < 0.0f ? 1 : 0;
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE_REF, ref);
|
||||
}
|
||||
// GPS timestamp and datestamp
|
||||
int64_t timestamp = 0;
|
||||
if (getCameraParam(params,
|
||||
CameraParameters::KEY_GPS_TIMESTAMP,
|
||||
×tamp)) {
|
||||
std::string date;
|
||||
if (convertTimestampToTimeAndDate(timestamp, &triplet, &date)) {
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_TIME_STAMP,
|
||||
triplet, 1.0f);
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_DATE_STAMP,
|
||||
date.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// GPS processing method
|
||||
if (getCameraParam(params,
|
||||
CameraParameters::KEY_GPS_PROCESSING_METHOD,
|
||||
&stringValue)) {
|
||||
std::vector<unsigned char> data;
|
||||
// Because this is a tag with an undefined format it has to be prefixed
|
||||
// with the encoding type. Insert an ASCII prefix first, then the
|
||||
// actual string. Undefined tags do not have to be null terminated.
|
||||
data.insert(data.end(),
|
||||
std::begin(kAsciiPrefix),
|
||||
std::end(kAsciiPrefix));
|
||||
data.insert(data.end(), stringValue, stringValue + strlen(stringValue));
|
||||
createEntry(exifData, EXIF_IFD_GPS, EXIF_TAG_GPS_PROCESSING_METHOD,
|
||||
&data[0], data.size());
|
||||
}
|
||||
|
||||
return exifData;
|
||||
}
|
||||
|
||||
void freeExifData(ExifData* exifData) {
|
||||
exif_data_free(exifData);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue