blob: ba38569af8e88f20d4ec45be04a3d4215e02c47a [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(
86 JNIEnv* env, jclass /* clazz */, jstring jfilename) {
87 const char* filenameChars = env->GetStringUTFChars(jfilename, NULL);
88 if (filenameChars == NULL) {
89 return NULL;
90 }
91 String8 filename(filenameChars);
92 env->ReleaseStringUTFChars(jfilename, filenameChars);
93
94 piex::PreviewImageData image_data;
Jaesung Chungfe968df2016-01-15 14:21:11 +090095 std::unique_ptr<FileStream> stream(new FileStream(filename));
96
Jaesung Chung8409c062016-01-19 10:48:30 +090097 if (!GetExifFromRawImage(stream.get(), filename, image_data)) {
98 ALOGI("Raw image not detected: %s", filename.string());
Jaesung Chungfe968df2016-01-15 14:21:11 +090099 return NULL;
100 }
101
102 KeyedVector<String8, String8> map;
103
104 if (image_data.thumbnail_length > 0) {
105 map.add(String8("hasThumbnail"), String8("true"));
106 map.add(String8("thumbnailOffset"), String8::format("%d", image_data.thumbnail_offset));
107 map.add(String8("thumbnailLength"), String8::format("%d", image_data.thumbnail_length));
108 } else {
109 map.add(String8("hasThumbnail"), String8("false"));
110 }
111
112 map.add(
113 String8("Orientation"),
114 String8::format("%u", image_data.exif_orientation));
115 map.add(
116 String8("ImageWidth"),
117 String8::format("%u", image_data.full_width));
118 map.add(
119 String8("ImageLength"),
120 String8::format("%u", image_data.full_height));
121
122 // Current PIEX does not have LightSource information while JPEG version of
123 // EXIFInterface always declares the light source field. For the
124 // compatibility, it provides the default value of the light source field.
125 map.add(String8("LightSource"), String8("0"));
126
127 if (!image_data.maker.empty()) {
128 map.add(String8("Make"), String8(image_data.maker.c_str()));
129 }
130
131 if (!image_data.model.empty()) {
132 map.add(String8("Model"), String8(image_data.model.c_str()));
133 }
134
135 if (!image_data.date_time.empty()) {
136 map.add(String8("DateTime"), String8(image_data.date_time.c_str()));
137 }
138
139 if (image_data.iso) {
140 map.add(
141 String8("ISOSpeedRatings"),
142 String8::format("%u", image_data.iso));
143 }
144
145 if (image_data.exposure_time.numerator != 0
146 && image_data.exposure_time.denominator != 0) {
147 double exposureTime =
148 (double)image_data.exposure_time.numerator
149 / image_data.exposure_time.denominator;
150
151 const char* format;
152 if (exposureTime < 0.01) {
153 format = "%6.4f";
154 } else {
155 format = "%5.3f";
156 }
157 map.add(String8("ExposureTime"), String8::format(format, exposureTime));
158 }
159
160 if (image_data.fnumber.numerator != 0
161 && image_data.fnumber.denominator != 0) {
162 double fnumber =
163 (double)image_data.fnumber.numerator
164 / image_data.fnumber.denominator;
165 map.add(String8("FNumber"), String8::format("%5.3f", fnumber));
166 }
167
168 if (image_data.focal_length.numerator != 0
169 && image_data.focal_length.denominator != 0) {
170 map.add(
171 String8("FocalLength"),
172 String8::format(
173 "%u/%u",
174 image_data.focal_length.numerator,
175 image_data.focal_length.denominator));
176 }
177
178 if (image_data.gps.is_valid) {
179 if (image_data.gps.latitude[0].denominator != 0
180 && image_data.gps.latitude[1].denominator != 0
181 && image_data.gps.latitude[2].denominator != 0) {
182 map.add(
183 String8("GPSLatitude"),
184 String8::format(
185 "%u/%u,%u/%u,%u/%u",
186 image_data.gps.latitude[0].numerator,
187 image_data.gps.latitude[0].denominator,
188 image_data.gps.latitude[1].numerator,
189 image_data.gps.latitude[1].denominator,
190 image_data.gps.latitude[2].numerator,
191 image_data.gps.latitude[2].denominator));
192 }
193
194 if (image_data.gps.latitude_ref) {
195 char str[2];
196 str[0] = image_data.gps.latitude_ref;
197 str[1] = 0;
198 map.add(String8("GPSLatitudeRef"), String8(str));
199 }
200
201 if (image_data.gps.longitude[0].denominator != 0
202 && image_data.gps.longitude[1].denominator != 0
203 && image_data.gps.longitude[2].denominator != 0) {
204 map.add(
205 String8("GPSLongitude"),
206 String8::format(
207 "%u/%u,%u/%u,%u/%u",
208 image_data.gps.longitude[0].numerator,
209 image_data.gps.longitude[0].denominator,
210 image_data.gps.longitude[1].numerator,
211 image_data.gps.longitude[1].denominator,
212 image_data.gps.longitude[2].numerator,
213 image_data.gps.longitude[2].denominator));
214 }
215
216 if (image_data.gps.longitude_ref) {
217 char str[2];
218 str[0] = image_data.gps.longitude_ref;
219 str[1] = 0;
220 map.add(String8("GPSLongitudeRef"), String8(str));
221 }
222
223 if (image_data.gps.altitude.denominator != 0) {
224 map.add(
225 String8("GPSAltitude"),
226 String8::format("%u/%u",
227 image_data.gps.altitude.numerator,
228 image_data.gps.altitude.denominator));
229
230 map.add(
231 String8("GPSAltitudeRef"),
232 String8(image_data.gps.altitude_ref ? "1" : "0"));
233 }
234
235 if (image_data.gps.time_stamp[0].denominator != 0
236 && image_data.gps.time_stamp[1].denominator != 0
237 && image_data.gps.time_stamp[2].denominator != 0) {
238 map.add(
239 String8("GPSTimeStamp"),
240 String8::format(
Jaesung Chung04e18bb2016-01-28 01:31:18 +0900241 "%02u:%02u:%02u",
Jaesung Chungfe968df2016-01-15 14:21:11 +0900242 image_data.gps.time_stamp[0].numerator
243 / image_data.gps.time_stamp[0].denominator,
244 image_data.gps.time_stamp[1].numerator
245 / image_data.gps.time_stamp[1].denominator,
246 image_data.gps.time_stamp[2].numerator
247 / image_data.gps.time_stamp[2].denominator));
248 }
249
250 if (!image_data.gps.date_stamp.empty()) {
251 map.add(
252 String8("GPSDateStamp"),
253 String8(image_data.gps.date_stamp.c_str()));
254 }
255 }
256
257 return KeyedVectorToHashMap(env, map);
258}
259
260} // extern "C"
261
262// ----------------------------------------------------------------------------
263
264static JNINativeMethod gMethods[] = {
265 { "initRawNative", "()V", (void *)ExifInterface_initRaw },
266 { "getRawAttributesNative", "(Ljava/lang/String;)Ljava/util/HashMap;",
267 (void*)ExifInterface_getRawMetadata },
268};
269
270int register_android_media_ExifInterface(JNIEnv *env) {
271 return AndroidRuntime::registerNativeMethods(
272 env,
273 "android/media/ExifInterface",
274 gMethods,
275 NELEM(gMethods));
276}