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"
|
|
|
|
|
2016-02-25 15:04:56 +09:00
|
|
|
#include "android/graphics/CreateJavaOutputStreamAdaptor.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 "src/piex_types.h"
|
|
|
|
#include "src/piex.h"
|
2016-01-15 14:21:11 +09:00
|
|
|
|
|
|
|
#include <jni.h>
|
|
|
|
#include <JNIHelp.h>
|
2016-02-25 15:04:56 +09:00
|
|
|
#include <androidfw/Asset.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-02-25 15:04:56 +09:00
|
|
|
#include <android/graphics/Utils.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;
|
|
|
|
|
2016-02-25 15:04:56 +09:00
|
|
|
static const char kJpegSignatureChars[] = {(char)0xff, (char)0xd8, (char)0xff};
|
|
|
|
static const int kJpegSignatureSize = 3;
|
|
|
|
|
2016-01-15 14:21:11 +09:00
|
|
|
#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;");
|
|
|
|
}
|
|
|
|
|
2016-02-25 15:04:56 +09:00
|
|
|
static bool is_asset_stream(const SkStream& stream) {
|
|
|
|
return stream.hasLength() && stream.hasPosition();
|
|
|
|
}
|
|
|
|
|
|
|
|
static jobject ExifInterface_getThumbnailFromAsset(
|
|
|
|
JNIEnv* env, jclass /* clazz */, jlong jasset, jint jthumbnailOffset,
|
|
|
|
jint jthumbnailLength) {
|
|
|
|
Asset* asset = reinterpret_cast<Asset*>(jasset);
|
|
|
|
std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset));
|
|
|
|
|
|
|
|
std::unique_ptr<jbyte[]> thumbnailData(new jbyte[(int)jthumbnailLength]);
|
|
|
|
if (thumbnailData.get() == NULL) {
|
|
|
|
ALOGI("No memory to get thumbnail");
|
2016-01-15 14:21:11 +09:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-02-25 15:04:56 +09:00
|
|
|
// Do not know the current offset. So rewind it.
|
|
|
|
stream->rewind();
|
|
|
|
|
|
|
|
// Read thumbnail.
|
|
|
|
stream->skip((int)jthumbnailOffset);
|
|
|
|
stream->read((void*)thumbnailData.get(), (int)jthumbnailLength);
|
|
|
|
|
|
|
|
// Copy to the byte array.
|
|
|
|
jbyteArray byteArray = env->NewByteArray(jthumbnailLength);
|
|
|
|
env->SetByteArrayRegion(byteArray, 0, jthumbnailLength, thumbnailData.get());
|
|
|
|
return byteArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
static jobject getRawAttributes(JNIEnv* env, SkStream* stream, bool returnThumbnail) {
|
|
|
|
std::unique_ptr<SkStream> streamDeleter(stream);
|
|
|
|
|
|
|
|
std::unique_ptr<::piex::StreamInterface> piexStream;
|
|
|
|
if (is_asset_stream(*stream)) {
|
|
|
|
piexStream.reset(new AssetStream(streamDeleter.release()));
|
|
|
|
} else {
|
|
|
|
piexStream.reset(new BufferedStream(streamDeleter.release()));
|
|
|
|
}
|
|
|
|
|
2016-01-15 14:21:11 +09:00
|
|
|
piex::PreviewImageData image_data;
|
|
|
|
|
2016-02-25 15:04:56 +09:00
|
|
|
if (!GetExifFromRawImage(piexStream.get(), String8("[piex stream]"), image_data)) {
|
2016-02-25 14:12:32 +00:00
|
|
|
ALOGI("Raw image not detected");
|
2016-01-15 14:21:11 +09:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyedVector<String8, String8> map;
|
|
|
|
|
2016-04-13 14:13:10 +09:00
|
|
|
if (image_data.thumbnail.length > 0
|
|
|
|
&& image_data.thumbnail.format == ::piex::Image::kJpegCompressed) {
|
2016-04-07 00:45:51 +09:00
|
|
|
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));
|
2016-01-15 14:21:11 +09:00
|
|
|
} else {
|
2016-04-07 00:45:51 +09:00
|
|
|
map.add(String8("HasThumbnail"), String8("false"));
|
2016-01-15 14:21:11 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
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()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-25 15:04:56 +09:00
|
|
|
jobject hashMap = KeyedVectorToHashMap(env, map);
|
|
|
|
|
|
|
|
if (returnThumbnail) {
|
|
|
|
std::unique_ptr<jbyte[]> thumbnailData(new jbyte[image_data.thumbnail.length]);
|
|
|
|
if (thumbnailData.get() == NULL) {
|
|
|
|
ALOGE("No memory to parse a thumbnail");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
jbyteArray jthumbnailByteArray = env->NewByteArray(image_data.thumbnail.length);
|
|
|
|
if (jthumbnailByteArray == NULL) {
|
|
|
|
ALOGE("No memory to parse a thumbnail");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
piexStream.get()->GetData(image_data.thumbnail.offset, image_data.thumbnail.length,
|
|
|
|
(uint8_t*)thumbnailData.get());
|
|
|
|
env->SetByteArrayRegion(
|
|
|
|
jthumbnailByteArray, 0, image_data.thumbnail.length, thumbnailData.get());
|
2016-04-07 00:45:51 +09:00
|
|
|
jstring jkey = env->NewStringUTF(String8("ThumbnailData"));
|
2016-02-25 15:04:56 +09:00
|
|
|
env->CallObjectMethod(hashMap, gFields.hashMap.put, jkey, jthumbnailByteArray);
|
|
|
|
env->DeleteLocalRef(jkey);
|
|
|
|
env->DeleteLocalRef(jthumbnailByteArray);
|
|
|
|
}
|
|
|
|
return hashMap;
|
|
|
|
}
|
|
|
|
|
|
|
|
static jobject ExifInterface_getRawAttributesFromAsset(
|
|
|
|
JNIEnv* env, jclass /* clazz */, jlong jasset) {
|
|
|
|
std::unique_ptr<char[]> jpegSignature(new char[kJpegSignatureSize]);
|
|
|
|
if (jpegSignature.get() == NULL) {
|
|
|
|
ALOGE("No enough memory to parse");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Asset* asset = reinterpret_cast<Asset*>(jasset);
|
|
|
|
std::unique_ptr<AssetStreamAdaptor> stream(new AssetStreamAdaptor(asset));
|
|
|
|
|
|
|
|
if (stream.get()->read(jpegSignature.get(), kJpegSignatureSize) != kJpegSignatureSize) {
|
|
|
|
// Rewind the stream.
|
|
|
|
stream.get()->rewind();
|
|
|
|
|
|
|
|
ALOGI("Corrupted image.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rewind the stream.
|
|
|
|
stream.get()->rewind();
|
|
|
|
|
|
|
|
if (memcmp(jpegSignature.get(), kJpegSignatureChars, kJpegSignatureSize) == 0) {
|
|
|
|
ALOGI("Should be a JPEG stream.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to parse from the given stream.
|
|
|
|
jobject result = getRawAttributes(env, stream.get(), false);
|
|
|
|
|
|
|
|
// Rewind the stream for the chance to read JPEG.
|
|
|
|
if (result == NULL) {
|
|
|
|
stream.get()->rewind();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static jobject ExifInterface_getRawAttributesFromFileDescriptor(
|
|
|
|
JNIEnv* env, jclass /* clazz */, jobject jfileDescriptor) {
|
|
|
|
std::unique_ptr<char[]> jpegSignature(new char[kJpegSignatureSize]);
|
|
|
|
if (jpegSignature.get() == NULL) {
|
|
|
|
ALOGE("No enough memory to parse");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int fd = jniGetFDFromFileDescriptor(env, jfileDescriptor);
|
|
|
|
if (fd < 0) {
|
|
|
|
ALOGI("Invalid file descriptor");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restore the file descriptor's offset on exiting this function.
|
|
|
|
AutoFDSeek autoRestore(fd);
|
|
|
|
|
|
|
|
int dupFd = dup(fd);
|
|
|
|
|
|
|
|
FILE* file = fdopen(dupFd, "r");
|
|
|
|
if (file == NULL) {
|
|
|
|
ALOGI("Failed to open the file descriptor");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fgets(jpegSignature.get(), kJpegSignatureSize, file) == NULL) {
|
|
|
|
ALOGI("Corrupted image.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memcmp(jpegSignature.get(), kJpegSignatureChars, kJpegSignatureSize) == 0) {
|
|
|
|
ALOGI("Should be a JPEG stream.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rewind the file descriptor.
|
|
|
|
fseek(file, 0L, SEEK_SET);
|
|
|
|
|
|
|
|
std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file,
|
|
|
|
SkFILEStream::kCallerPasses_Ownership));
|
|
|
|
return getRawAttributes(env, fileStream.get(), false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static jobject ExifInterface_getRawAttributesFromInputStream(
|
|
|
|
JNIEnv* env, jclass /* clazz */, jobject jinputStream) {
|
|
|
|
jbyteArray byteArray = env->NewByteArray(8*1024);
|
|
|
|
ScopedLocalRef<jbyteArray> scoper(env, byteArray);
|
|
|
|
std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, jinputStream, scoper.get()));
|
|
|
|
return getRawAttributes(env, stream.get(), true);
|
2016-01-15 14:21:11 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
} // extern "C"
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
static JNINativeMethod gMethods[] = {
|
2016-02-25 15:04:56 +09:00
|
|
|
{ "nativeInitRaw", "()V", (void *)ExifInterface_initRaw },
|
|
|
|
{ "nativeGetThumbnailFromAsset", "(JII)[B", (void *)ExifInterface_getThumbnailFromAsset },
|
|
|
|
{ "nativeGetRawAttributesFromAsset", "(J)Ljava/util/HashMap;",
|
|
|
|
(void*)ExifInterface_getRawAttributesFromAsset },
|
|
|
|
{ "nativeGetRawAttributesFromFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/util/HashMap;",
|
|
|
|
(void*)ExifInterface_getRawAttributesFromFileDescriptor },
|
|
|
|
{ "nativeGetRawAttributesFromInputStream", "(Ljava/io/InputStream;)Ljava/util/HashMap;",
|
|
|
|
(void*)ExifInterface_getRawAttributesFromInputStream },
|
2016-01-15 14:21:11 +09:00
|
|
|
};
|
|
|
|
|
|
|
|
int register_android_media_ExifInterface(JNIEnv *env) {
|
|
|
|
return AndroidRuntime::registerNativeMethods(
|
|
|
|
env,
|
|
|
|
"android/media/ExifInterface",
|
|
|
|
gMethods,
|
|
|
|
NELEM(gMethods));
|
|
|
|
}
|