blob: a1fcb07ca3fa8c232862efc54a9ccc1a77915eed [file] [log] [blame]
Jaesung Chungfe968df2016-01-15 14:21:11 +09001/*
2 * Copyright 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "ExifInterface_JNI"
Jaesung Chungfe968df2016-01-15 14:21:11 +090019
Jaesung Chung8409c062016-01-19 10:48:30 +090020#include "android_media_Utils.h"
Jaesung Chungfe968df2016-01-15 14:21:11 +090021
22#include "src/piex_types.h"
23#include "src/piex.h"
24
Jaesung Chung8409c062016-01-19 10:48:30 +090025#include <jni.h>
26#include <JNIHelp.h>
27#include <android_runtime/AndroidRuntime.h>
28#include <nativehelper/ScopedLocalRef.h>
29
30#include <utils/Log.h>
31#include <utils/String8.h>
32#include <utils/KeyedVector.h>
33
Jaesung Chungfe968df2016-01-15 14:21:11 +090034// ----------------------------------------------------------------------------
35
36using namespace android;
37
Jaesung Chungfe968df2016-01-15 14:21:11 +090038#define FIND_CLASS(var, className) \
39 var = env->FindClass(className); \
40 LOG_FATAL_IF(! var, "Unable to find class " className);
41
42#define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
43 var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
44 LOG_FATAL_IF(! var, "Unable to find method " fieldName);
45
46struct HashMapFields {
47 jmethodID init;
48 jmethodID put;
49};
50
51struct fields_t {
52 HashMapFields hashMap;
53 jclass hashMapClassId;
54};
55
56static fields_t gFields;
57
58static jobject KeyedVectorToHashMap(JNIEnv *env, KeyedVector<String8, String8> const &map) {
59 jclass clazz = gFields.hashMapClassId;
60 jobject hashMap = env->NewObject(clazz, gFields.hashMap.init);
61 for (size_t i = 0; i < map.size(); ++i) {
62 jstring jkey = env->NewStringUTF(map.keyAt(i).string());
63 jstring jvalue = env->NewStringUTF(map.valueAt(i).string());
64 env->CallObjectMethod(hashMap, gFields.hashMap.put, jkey, jvalue);
65 env->DeleteLocalRef(jkey);
66 env->DeleteLocalRef(jvalue);
67 }
68 return hashMap;
69}
70
71extern "C" {
72
73// -------------------------- ExifInterface methods ---------------------------
74
75static void ExifInterface_initRaw(JNIEnv *env) {
76 jclass clazz;
77 FIND_CLASS(clazz, "java/util/HashMap");
78 gFields.hashMapClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
79
80 GET_METHOD_ID(gFields.hashMap.init, clazz, "<init>", "()V");
81 GET_METHOD_ID(gFields.hashMap.put, clazz, "put",
82 "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
83}
84
85static jobject ExifInterface_getRawMetadata(
Jaesung Chung15ef59e2016-02-25 14:12:32 +000086 JNIEnv* env, jclass /* clazz */, jobject jfileDescriptor) {
87 int fd = jniGetFDFromFileDescriptor(env, jfileDescriptor);
88 if (fd < 0) {
89 ALOGI("Invalid file descriptor");
Jaesung Chungfe968df2016-01-15 14:21:11 +090090 return NULL;
91 }
Jaesung Chungfe968df2016-01-15 14:21:11 +090092
93 piex::PreviewImageData image_data;
Jaesung Chung15ef59e2016-02-25 14:12:32 +000094 std::unique_ptr<FileStream> stream(new FileStream(fd));
Jaesung Chungfe968df2016-01-15 14:21:11 +090095
Jaesung Chung15ef59e2016-02-25 14:12:32 +000096 if (!GetExifFromRawImage(stream.get(), String8("[file descriptor]"), image_data)) {
97 ALOGI("Raw image not detected");
Jaesung Chungfe968df2016-01-15 14:21:11 +090098 return NULL;
99 }
100
101 KeyedVector<String8, String8> map;
102
103 if (image_data.thumbnail_length > 0) {
104 map.add(String8("hasThumbnail"), String8("true"));
105 map.add(String8("thumbnailOffset"), String8::format("%d", image_data.thumbnail_offset));
106 map.add(String8("thumbnailLength"), String8::format("%d", image_data.thumbnail_length));
107 } else {
108 map.add(String8("hasThumbnail"), String8("false"));
109 }
110
111 map.add(
112 String8("Orientation"),
113 String8::format("%u", image_data.exif_orientation));
114 map.add(
115 String8("ImageWidth"),
116 String8::format("%u", image_data.full_width));
117 map.add(
118 String8("ImageLength"),
119 String8::format("%u", image_data.full_height));
120
121 // Current PIEX does not have LightSource information while JPEG version of
122 // EXIFInterface always declares the light source field. For the
123 // compatibility, it provides the default value of the light source field.
124 map.add(String8("LightSource"), String8("0"));
125
126 if (!image_data.maker.empty()) {
127 map.add(String8("Make"), String8(image_data.maker.c_str()));
128 }
129
130 if (!image_data.model.empty()) {
131 map.add(String8("Model"), String8(image_data.model.c_str()));
132 }
133
134 if (!image_data.date_time.empty()) {
135 map.add(String8("DateTime"), String8(image_data.date_time.c_str()));
136 }
137
138 if (image_data.iso) {
139 map.add(
140 String8("ISOSpeedRatings"),
141 String8::format("%u", image_data.iso));
142 }
143
144 if (image_data.exposure_time.numerator != 0
145 && image_data.exposure_time.denominator != 0) {
146 double exposureTime =
147 (double)image_data.exposure_time.numerator
148 / image_data.exposure_time.denominator;
149
150 const char* format;
151 if (exposureTime < 0.01) {
152 format = "%6.4f";
153 } else {
154 format = "%5.3f";
155 }
156 map.add(String8("ExposureTime"), String8::format(format, exposureTime));
157 }
158
159 if (image_data.fnumber.numerator != 0
160 && image_data.fnumber.denominator != 0) {
161 double fnumber =
162 (double)image_data.fnumber.numerator
163 / image_data.fnumber.denominator;
164 map.add(String8("FNumber"), String8::format("%5.3f", fnumber));
165 }
166
167 if (image_data.focal_length.numerator != 0
168 && image_data.focal_length.denominator != 0) {
169 map.add(
170 String8("FocalLength"),
171 String8::format(
172 "%u/%u",
173 image_data.focal_length.numerator,
174 image_data.focal_length.denominator));
175 }
176
177 if (image_data.gps.is_valid) {
178 if (image_data.gps.latitude[0].denominator != 0
179 && image_data.gps.latitude[1].denominator != 0
180 && image_data.gps.latitude[2].denominator != 0) {
181 map.add(
182 String8("GPSLatitude"),
183 String8::format(
184 "%u/%u,%u/%u,%u/%u",
185 image_data.gps.latitude[0].numerator,
186 image_data.gps.latitude[0].denominator,
187 image_data.gps.latitude[1].numerator,
188 image_data.gps.latitude[1].denominator,
189 image_data.gps.latitude[2].numerator,
190 image_data.gps.latitude[2].denominator));
191 }
192
193 if (image_data.gps.latitude_ref) {
194 char str[2];
195 str[0] = image_data.gps.latitude_ref;
196 str[1] = 0;
197 map.add(String8("GPSLatitudeRef"), String8(str));
198 }
199
200 if (image_data.gps.longitude[0].denominator != 0
201 && image_data.gps.longitude[1].denominator != 0
202 && image_data.gps.longitude[2].denominator != 0) {
203 map.add(
204 String8("GPSLongitude"),
205 String8::format(
206 "%u/%u,%u/%u,%u/%u",
207 image_data.gps.longitude[0].numerator,
208 image_data.gps.longitude[0].denominator,
209 image_data.gps.longitude[1].numerator,
210 image_data.gps.longitude[1].denominator,
211 image_data.gps.longitude[2].numerator,
212 image_data.gps.longitude[2].denominator));
213 }
214
215 if (image_data.gps.longitude_ref) {
216 char str[2];
217 str[0] = image_data.gps.longitude_ref;
218 str[1] = 0;
219 map.add(String8("GPSLongitudeRef"), String8(str));
220 }
221
222 if (image_data.gps.altitude.denominator != 0) {
223 map.add(
224 String8("GPSAltitude"),
225 String8::format("%u/%u",
226 image_data.gps.altitude.numerator,
227 image_data.gps.altitude.denominator));
228
229 map.add(
230 String8("GPSAltitudeRef"),
231 String8(image_data.gps.altitude_ref ? "1" : "0"));
232 }
233
234 if (image_data.gps.time_stamp[0].denominator != 0
235 && image_data.gps.time_stamp[1].denominator != 0
236 && image_data.gps.time_stamp[2].denominator != 0) {
237 map.add(
238 String8("GPSTimeStamp"),
239 String8::format(
Jaesung Chung04e18bb2016-01-28 01:31:18 +0900240 "%02u:%02u:%02u",
Jaesung Chungfe968df2016-01-15 14:21:11 +0900241 image_data.gps.time_stamp[0].numerator
242 / image_data.gps.time_stamp[0].denominator,
243 image_data.gps.time_stamp[1].numerator
244 / image_data.gps.time_stamp[1].denominator,
245 image_data.gps.time_stamp[2].numerator
246 / image_data.gps.time_stamp[2].denominator));
247 }
248
249 if (!image_data.gps.date_stamp.empty()) {
250 map.add(
251 String8("GPSDateStamp"),
252 String8(image_data.gps.date_stamp.c_str()));
253 }
254 }
255
256 return KeyedVectorToHashMap(env, map);
257}
258
259} // extern "C"
260
261// ----------------------------------------------------------------------------
262
263static JNINativeMethod gMethods[] = {
264 { "initRawNative", "()V", (void *)ExifInterface_initRaw },
Jaesung Chung15ef59e2016-02-25 14:12:32 +0000265 { "getRawAttributesNative", "(Ljava/io/FileDescriptor;)Ljava/util/HashMap;",
Jaesung Chungfe968df2016-01-15 14:21:11 +0900266 (void*)ExifInterface_getRawMetadata },
267};
268
269int register_android_media_ExifInterface(JNIEnv *env) {
270 return AndroidRuntime::registerNativeMethods(
271 env,
272 "android/media/ExifInterface",
273 gMethods,
274 NELEM(gMethods));
275}