2016-01-15 14:21:11 +09:00
|
|
|
/*
|
|
|
|
* Copyright 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 "ExifInterface_JNI"
|
|
|
|
|
Enable reading a thumbnail from RAW image files in MtpDatabase
Also introduce new supported RAW image file formats, PEF and SRW.
RAW image file formats are not defined in PTP 1.2 specification except
for DNG. They are mostly built on top of TIFF or TIFF/EP. (Fuji's RAF
is the exception).
In this CL, image file formats are classified newly as below:
DNG: dng
TIFF: cr2, nrw, arw, rw2, orf, pef, srw
TIFF/EP: nef
Unknown Image Formats(FORMAT_DEFINED): wbmap, webp, raf
I referred to the following documents for defining MTP formats of RAW
images:
* http://www.rags-int-inc.com/PhotoTechStuff/RawStandards/RawSummary.html
* https://en.wikipedia.org/wiki/Raw_image_format
Bug: 26552863, Bug: 26626825
Change-Id: Ia218f6320c4c1ff051a23ca0060ceac46134b0d7
2016-01-19 10:48:30 +09:00
|
|
|
#include "android_media_Utils.h"
|
|
|
|
|
|
|
|
#include "src/piex_types.h"
|
|
|
|
#include "src/piex.h"
|
2016-01-15 14:21:11 +09:00
|
|
|
|
|
|
|
#include <jni.h>
|
|
|
|
#include <JNIHelp.h>
|
Enable reading a thumbnail from RAW image files in MtpDatabase
Also introduce new supported RAW image file formats, PEF and SRW.
RAW image file formats are not defined in PTP 1.2 specification except
for DNG. They are mostly built on top of TIFF or TIFF/EP. (Fuji's RAF
is the exception).
In this CL, image file formats are classified newly as below:
DNG: dng
TIFF: cr2, nrw, arw, rw2, orf, pef, srw
TIFF/EP: nef
Unknown Image Formats(FORMAT_DEFINED): wbmap, webp, raf
I referred to the following documents for defining MTP formats of RAW
images:
* http://www.rags-int-inc.com/PhotoTechStuff/RawStandards/RawSummary.html
* https://en.wikipedia.org/wiki/Raw_image_format
Bug: 26552863, Bug: 26626825
Change-Id: Ia218f6320c4c1ff051a23ca0060ceac46134b0d7
2016-01-19 10:48:30 +09:00
|
|
|
#include <android_runtime/AndroidRuntime.h>
|
2016-01-15 14:21:11 +09:00
|
|
|
#include <nativehelper/ScopedLocalRef.h>
|
|
|
|
|
Enable reading a thumbnail from RAW image files in MtpDatabase
Also introduce new supported RAW image file formats, PEF and SRW.
RAW image file formats are not defined in PTP 1.2 specification except
for DNG. They are mostly built on top of TIFF or TIFF/EP. (Fuji's RAF
is the exception).
In this CL, image file formats are classified newly as below:
DNG: dng
TIFF: cr2, nrw, arw, rw2, orf, pef, srw
TIFF/EP: nef
Unknown Image Formats(FORMAT_DEFINED): wbmap, webp, raf
I referred to the following documents for defining MTP formats of RAW
images:
* http://www.rags-int-inc.com/PhotoTechStuff/RawStandards/RawSummary.html
* https://en.wikipedia.org/wiki/Raw_image_format
Bug: 26552863, Bug: 26626825
Change-Id: Ia218f6320c4c1ff051a23ca0060ceac46134b0d7
2016-01-19 10:48:30 +09:00
|
|
|
#include <utils/Log.h>
|
|
|
|
#include <utils/String8.h>
|
|
|
|
#include <utils/KeyedVector.h>
|
2016-01-15 14:21:11 +09:00
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
using namespace android;
|
|
|
|
|
|
|
|
#define FIND_CLASS(var, className) \
|
|
|
|
var = env->FindClass(className); \
|
|
|
|
LOG_FATAL_IF(! var, "Unable to find class " className);
|
|
|
|
|
|
|
|
#define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
|
|
|
|
var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
|
|
|
|
LOG_FATAL_IF(! var, "Unable to find method " fieldName);
|
|
|
|
|
|
|
|
struct HashMapFields {
|
|
|
|
jmethodID init;
|
|
|
|
jmethodID put;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct fields_t {
|
|
|
|
HashMapFields hashMap;
|
|
|
|
jclass hashMapClassId;
|
|
|
|
};
|
|
|
|
|
|
|
|
static fields_t gFields;
|
|
|
|
|
|
|
|
static jobject KeyedVectorToHashMap(JNIEnv *env, KeyedVector<String8, String8> const &map) {
|
|
|
|
jclass clazz = gFields.hashMapClassId;
|
|
|
|
jobject hashMap = env->NewObject(clazz, gFields.hashMap.init);
|
|
|
|
for (size_t i = 0; i < map.size(); ++i) {
|
|
|
|
jstring jkey = env->NewStringUTF(map.keyAt(i).string());
|
|
|
|
jstring jvalue = env->NewStringUTF(map.valueAt(i).string());
|
|
|
|
env->CallObjectMethod(hashMap, gFields.hashMap.put, jkey, jvalue);
|
|
|
|
env->DeleteLocalRef(jkey);
|
|
|
|
env->DeleteLocalRef(jvalue);
|
|
|
|
}
|
|
|
|
return hashMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
|
|
|
// -------------------------- ExifInterface methods ---------------------------
|
|
|
|
|
|
|
|
static void ExifInterface_initRaw(JNIEnv *env) {
|
|
|
|
jclass clazz;
|
|
|
|
FIND_CLASS(clazz, "java/util/HashMap");
|
|
|
|
gFields.hashMapClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
|
|
|
|
|
|
|
|
GET_METHOD_ID(gFields.hashMap.init, clazz, "<init>", "()V");
|
|
|
|
GET_METHOD_ID(gFields.hashMap.put, clazz, "put",
|
|
|
|
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
|
|
|
|
}
|
|
|
|
|
|
|
|
static jobject ExifInterface_getRawMetadata(
|
|
|
|
JNIEnv* env, jclass /* clazz */, jstring jfilename) {
|
|
|
|
const char* filenameChars = env->GetStringUTFChars(jfilename, NULL);
|
|
|
|
if (filenameChars == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
String8 filename(filenameChars);
|
|
|
|
env->ReleaseStringUTFChars(jfilename, filenameChars);
|
|
|
|
|
|
|
|
piex::PreviewImageData image_data;
|
|
|
|
std::unique_ptr<FileStream> stream(new FileStream(filename));
|
|
|
|
|
Enable reading a thumbnail from RAW image files in MtpDatabase
Also introduce new supported RAW image file formats, PEF and SRW.
RAW image file formats are not defined in PTP 1.2 specification except
for DNG. They are mostly built on top of TIFF or TIFF/EP. (Fuji's RAF
is the exception).
In this CL, image file formats are classified newly as below:
DNG: dng
TIFF: cr2, nrw, arw, rw2, orf, pef, srw
TIFF/EP: nef
Unknown Image Formats(FORMAT_DEFINED): wbmap, webp, raf
I referred to the following documents for defining MTP formats of RAW
images:
* http://www.rags-int-inc.com/PhotoTechStuff/RawStandards/RawSummary.html
* https://en.wikipedia.org/wiki/Raw_image_format
Bug: 26552863, Bug: 26626825
Change-Id: Ia218f6320c4c1ff051a23ca0060ceac46134b0d7
2016-01-19 10:48:30 +09:00
|
|
|
if (!GetExifFromRawImage(stream.get(), filename, image_data)) {
|
|
|
|
ALOGI("Raw image not detected: %s", filename.string());
|
2016-01-15 14:21:11 +09:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyedVector<String8, String8> map;
|
|
|
|
|
|
|
|
if (image_data.thumbnail_length > 0) {
|
|
|
|
map.add(String8("hasThumbnail"), String8("true"));
|
|
|
|
map.add(String8("thumbnailOffset"), String8::format("%d", image_data.thumbnail_offset));
|
|
|
|
map.add(String8("thumbnailLength"), String8::format("%d", image_data.thumbnail_length));
|
|
|
|
} else {
|
|
|
|
map.add(String8("hasThumbnail"), String8("false"));
|
|
|
|
}
|
|
|
|
|
|
|
|
map.add(
|
|
|
|
String8("Orientation"),
|
|
|
|
String8::format("%u", image_data.exif_orientation));
|
|
|
|
map.add(
|
|
|
|
String8("ImageWidth"),
|
|
|
|
String8::format("%u", image_data.full_width));
|
|
|
|
map.add(
|
|
|
|
String8("ImageLength"),
|
|
|
|
String8::format("%u", image_data.full_height));
|
|
|
|
|
|
|
|
// Current PIEX does not have LightSource information while JPEG version of
|
|
|
|
// EXIFInterface always declares the light source field. For the
|
|
|
|
// compatibility, it provides the default value of the light source field.
|
|
|
|
map.add(String8("LightSource"), String8("0"));
|
|
|
|
|
|
|
|
if (!image_data.maker.empty()) {
|
|
|
|
map.add(String8("Make"), String8(image_data.maker.c_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!image_data.model.empty()) {
|
|
|
|
map.add(String8("Model"), String8(image_data.model.c_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!image_data.date_time.empty()) {
|
|
|
|
map.add(String8("DateTime"), String8(image_data.date_time.c_str()));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.iso) {
|
|
|
|
map.add(
|
|
|
|
String8("ISOSpeedRatings"),
|
|
|
|
String8::format("%u", image_data.iso));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.exposure_time.numerator != 0
|
|
|
|
&& image_data.exposure_time.denominator != 0) {
|
|
|
|
double exposureTime =
|
|
|
|
(double)image_data.exposure_time.numerator
|
|
|
|
/ image_data.exposure_time.denominator;
|
|
|
|
|
|
|
|
const char* format;
|
|
|
|
if (exposureTime < 0.01) {
|
|
|
|
format = "%6.4f";
|
|
|
|
} else {
|
|
|
|
format = "%5.3f";
|
|
|
|
}
|
|
|
|
map.add(String8("ExposureTime"), String8::format(format, exposureTime));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.fnumber.numerator != 0
|
|
|
|
&& image_data.fnumber.denominator != 0) {
|
|
|
|
double fnumber =
|
|
|
|
(double)image_data.fnumber.numerator
|
|
|
|
/ image_data.fnumber.denominator;
|
|
|
|
map.add(String8("FNumber"), String8::format("%5.3f", fnumber));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.focal_length.numerator != 0
|
|
|
|
&& image_data.focal_length.denominator != 0) {
|
|
|
|
map.add(
|
|
|
|
String8("FocalLength"),
|
|
|
|
String8::format(
|
|
|
|
"%u/%u",
|
|
|
|
image_data.focal_length.numerator,
|
|
|
|
image_data.focal_length.denominator));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.gps.is_valid) {
|
|
|
|
if (image_data.gps.latitude[0].denominator != 0
|
|
|
|
&& image_data.gps.latitude[1].denominator != 0
|
|
|
|
&& image_data.gps.latitude[2].denominator != 0) {
|
|
|
|
map.add(
|
|
|
|
String8("GPSLatitude"),
|
|
|
|
String8::format(
|
|
|
|
"%u/%u,%u/%u,%u/%u",
|
|
|
|
image_data.gps.latitude[0].numerator,
|
|
|
|
image_data.gps.latitude[0].denominator,
|
|
|
|
image_data.gps.latitude[1].numerator,
|
|
|
|
image_data.gps.latitude[1].denominator,
|
|
|
|
image_data.gps.latitude[2].numerator,
|
|
|
|
image_data.gps.latitude[2].denominator));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.gps.latitude_ref) {
|
|
|
|
char str[2];
|
|
|
|
str[0] = image_data.gps.latitude_ref;
|
|
|
|
str[1] = 0;
|
|
|
|
map.add(String8("GPSLatitudeRef"), String8(str));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.gps.longitude[0].denominator != 0
|
|
|
|
&& image_data.gps.longitude[1].denominator != 0
|
|
|
|
&& image_data.gps.longitude[2].denominator != 0) {
|
|
|
|
map.add(
|
|
|
|
String8("GPSLongitude"),
|
|
|
|
String8::format(
|
|
|
|
"%u/%u,%u/%u,%u/%u",
|
|
|
|
image_data.gps.longitude[0].numerator,
|
|
|
|
image_data.gps.longitude[0].denominator,
|
|
|
|
image_data.gps.longitude[1].numerator,
|
|
|
|
image_data.gps.longitude[1].denominator,
|
|
|
|
image_data.gps.longitude[2].numerator,
|
|
|
|
image_data.gps.longitude[2].denominator));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.gps.longitude_ref) {
|
|
|
|
char str[2];
|
|
|
|
str[0] = image_data.gps.longitude_ref;
|
|
|
|
str[1] = 0;
|
|
|
|
map.add(String8("GPSLongitudeRef"), String8(str));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.gps.altitude.denominator != 0) {
|
|
|
|
map.add(
|
|
|
|
String8("GPSAltitude"),
|
|
|
|
String8::format("%u/%u",
|
|
|
|
image_data.gps.altitude.numerator,
|
|
|
|
image_data.gps.altitude.denominator));
|
|
|
|
|
|
|
|
map.add(
|
|
|
|
String8("GPSAltitudeRef"),
|
|
|
|
String8(image_data.gps.altitude_ref ? "1" : "0"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (image_data.gps.time_stamp[0].denominator != 0
|
|
|
|
&& image_data.gps.time_stamp[1].denominator != 0
|
|
|
|
&& image_data.gps.time_stamp[2].denominator != 0) {
|
|
|
|
map.add(
|
|
|
|
String8("GPSTimeStamp"),
|
|
|
|
String8::format(
|
2016-01-28 01:31:18 +09:00
|
|
|
"%02u:%02u:%02u",
|
2016-01-15 14:21:11 +09:00
|
|
|
image_data.gps.time_stamp[0].numerator
|
|
|
|
/ image_data.gps.time_stamp[0].denominator,
|
|
|
|
image_data.gps.time_stamp[1].numerator
|
|
|
|
/ image_data.gps.time_stamp[1].denominator,
|
|
|
|
image_data.gps.time_stamp[2].numerator
|
|
|
|
/ image_data.gps.time_stamp[2].denominator));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!image_data.gps.date_stamp.empty()) {
|
|
|
|
map.add(
|
|
|
|
String8("GPSDateStamp"),
|
|
|
|
String8(image_data.gps.date_stamp.c_str()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return KeyedVectorToHashMap(env, map);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // extern "C"
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static JNINativeMethod gMethods[] = {
|
|
|
|
{ "initRawNative", "()V", (void *)ExifInterface_initRaw },
|
|
|
|
{ "getRawAttributesNative", "(Ljava/lang/String;)Ljava/util/HashMap;",
|
|
|
|
(void*)ExifInterface_getRawMetadata },
|
|
|
|
};
|
|
|
|
|
|
|
|
int register_android_media_ExifInterface(JNIEnv *env) {
|
|
|
|
return AndroidRuntime::registerNativeMethods(
|
|
|
|
env,
|
|
|
|
"android/media/ExifInterface",
|
|
|
|
gMethods,
|
|
|
|
NELEM(gMethods));
|
|
|
|
}
|