1 /*
2
3 Copyright (c) 2008, The Android Open Source Project
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in
13 the documentation and/or other materials provided with the
14 distribution.
15 * Neither the name of Google, Inc. nor the names of its contributors
16 may be used to endorse or promote products derived from this
17 software without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 SUCH DAMAGE.
31
32 */
33
34 #include <nativehelper/JNIHelp.h>
35 #include <nativehelper/jni.h>
36
37 #include <assert.h>
38 #include <dlfcn.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <sys/stat.h>
42 #include <utils/Log.h>
43
44 #include "jhead.h"
45
46 #ifndef NELEM
47 #define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0])))
48 #endif
49
50 // Define the line below to turn on poor man's debugging output
51 #undef SUPERDEBUG
52
53 // Various tests
54 #undef REALLOCTEST
55 #undef OUTOFMEMORYTEST1
56
addExifAttibute(JNIEnv * env,jmethodID putMethod,jobject hashMap,char * key,char * value)57 static void addExifAttibute(JNIEnv *env, jmethodID putMethod, jobject hashMap, char* key, char* value) {
58 jstring jkey = (*env)->NewStringUTF(env, key);
59 jstring jvalue = (*env)->NewStringUTF(env, value);
60
61 jobject jobject_of_entryset = (*env)->CallObjectMethod(env, hashMap, putMethod, jkey, jvalue);
62
63 (*env)->ReleaseStringUTFChars(env, jkey, key);
64 (*env)->ReleaseStringUTFChars(env, jvalue, value);
65 }
66
67 extern void ResetJpgfile();
68
loadExifInfo(const char * FileName,int readJPG)69 static int loadExifInfo(const char* FileName, int readJPG) {
70 #ifdef SUPERDEBUG
71 LOGE("loadExifInfo");
72 #endif
73 int Modified = FALSE;
74 ReadMode_t ReadMode = READ_METADATA;
75 if (readJPG) {
76 // Must add READ_IMAGE else we can't write the JPG back out.
77 ReadMode |= READ_IMAGE;
78 }
79
80 #ifdef SUPERDEBUG
81 LOGE("ResetJpgfile");
82 #endif
83 ResetJpgfile();
84
85 // Start with an empty image information structure.
86 memset(&ImageInfo, 0, sizeof(ImageInfo));
87 ImageInfo.FlashUsed = -1;
88 ImageInfo.MeteringMode = -1;
89 ImageInfo.Whitebalance = -1;
90
91 // Store file date/time.
92 {
93 struct stat st;
94 if (stat(FileName, &st) >= 0) {
95 ImageInfo.FileDateTime = st.st_mtime;
96 ImageInfo.FileSize = st.st_size;
97 }
98 }
99
100 strncpy(ImageInfo.FileName, FileName, PATH_MAX);
101 #ifdef SUPERDEBUG
102 LOGE("ReadJpegFile");
103 #endif
104 return ReadJpegFile(FileName, ReadMode);
105 }
106
saveJPGFile(const char * filename)107 static void saveJPGFile(const char* filename) {
108 char backupName[400];
109 struct stat buf;
110
111 #ifdef SUPERDEBUG
112 LOGE("Modified: %s\n", filename);
113 #endif
114
115 strncpy(backupName, filename, 395);
116 strcat(backupName, ".t");
117
118 // Remove any .old file name that may pre-exist
119 #ifdef SUPERDEBUG
120 LOGE("removing backup %s", backupName);
121 #endif
122 unlink(backupName);
123
124 // Rename the old file.
125 #ifdef SUPERDEBUG
126 LOGE("rename %s to %s", filename, backupName);
127 #endif
128 rename(filename, backupName);
129
130 // Write the new file.
131 #ifdef SUPERDEBUG
132 LOGE("WriteJpegFile %s", filename);
133 #endif
134 if (WriteJpegFile(filename)) {
135
136 // Copy the access rights from original file
137 #ifdef SUPERDEBUG
138 LOGE("stating old file %s", backupName);
139 #endif
140 if (stat(backupName, &buf) == 0){
141 // set Unix access rights and time to new file
142 struct utimbuf mtime;
143 chmod(filename, buf.st_mode);
144
145 mtime.actime = buf.st_mtime;
146 mtime.modtime = buf.st_mtime;
147
148 utime(filename, &mtime);
149 }
150
151 // Now that we are done, remove original file.
152 #ifdef SUPERDEBUG
153 LOGE("unlinking old file %s", backupName);
154 #endif
155 unlink(backupName);
156 #ifdef SUPERDEBUG
157 LOGE("returning from saveJPGFile");
158 #endif
159 } else {
160 #ifdef SUPERDEBUG
161 LOGE("WriteJpegFile failed, restoring from backup file");
162 #endif
163 // move back the backup file
164 rename(backupName, filename);
165 }
166 }
167
copyThumbnailData(uchar * thumbnailData,int thumbnailLen)168 void copyThumbnailData(uchar* thumbnailData, int thumbnailLen) {
169 #ifdef SUPERDEBUG
170 LOGE("******************************** copyThumbnailData\n");
171 #endif
172 Section_t* ExifSection = FindSection(M_EXIF);
173 if (ExifSection == NULL) {
174 return;
175 }
176 int NewExifSize = ImageInfo.ThumbnailOffset+8+thumbnailLen;
177 ExifSection->Data = (uchar *)realloc(ExifSection->Data, NewExifSize);
178 if (ExifSection->Data == NULL) {
179 return;
180 }
181 uchar* ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8;
182
183 memcpy(ThumbnailPointer, thumbnailData, thumbnailLen);
184
185 ImageInfo.ThumbnailSize = thumbnailLen;
186
187 Put32u(ExifSection->Data+ImageInfo.ThumbnailSizeOffset+8, thumbnailLen);
188
189 ExifSection->Data[0] = (uchar)(NewExifSize >> 8);
190 ExifSection->Data[1] = (uchar)NewExifSize;
191 ExifSection->Size = NewExifSize;
192 }
193
saveAttributes(JNIEnv * env,jobject jobj,jstring jfilename,jstring jattributes)194 static void saveAttributes(JNIEnv *env, jobject jobj, jstring jfilename, jstring jattributes)
195 {
196 #ifdef SUPERDEBUG
197 LOGE("******************************** saveAttributes\n");
198 #endif
199 // format of attributes string passed from java:
200 // "attrCnt attr1=valueLen value1attr2=value2Len value2..."
201 // example input: "4 ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO"
202 ExifElement_t* exifElementTable = NULL;
203 const char* filename = NULL;
204 uchar* thumbnailData = NULL;
205 int attrCnt = 0;
206 const char* attributes = (*env)->GetStringUTFChars(env, jattributes, NULL);
207 if (attributes == NULL) {
208 goto exit;
209 }
210 #ifdef SUPERDEBUG
211 LOGE("attributes %s\n", attributes);
212 #endif
213
214 // Get the number of attributes - it's the first number in the string.
215 attrCnt = atoi(attributes);
216 char* attrPtr = strchr(attributes, ' ') + 1;
217 #ifdef SUPERDEBUG
218 LOGE("attribute count %d attrPtr %s\n", attrCnt, attrPtr);
219 #endif
220
221 // Load all the hash exif elements into a more c-like structure
222 exifElementTable = malloc(sizeof(ExifElement_t) * attrCnt);
223 if (exifElementTable == NULL) {
224 goto exit;
225 }
226 #ifdef OUTOFMEMORYTEST1
227 goto exit;
228 #endif
229
230 int i;
231 char tag[100];
232 int gpsTagCount = 0;
233 int exifTagCount = 0;
234
235 for (i = 0; i < attrCnt; i++) {
236 // get an element from the attribute string and add it to the c structure
237 // first, extract the attribute name
238 char* tagEnd = strchr(attrPtr, '=');
239 if (tagEnd == 0) {
240 #ifdef SUPERDEBUG
241 LOGE("saveAttributes: couldn't find end of tag");
242 #endif
243 goto exit;
244 }
245 if (tagEnd - attrPtr > 99) {
246 #ifdef SUPERDEBUG
247 LOGE("saveAttributes: attribute tag way too long");
248 #endif
249 goto exit;
250 }
251 memcpy(tag, attrPtr, tagEnd - attrPtr);
252 tag[tagEnd - attrPtr] = 0;
253
254 if (IsGpsTag(tag)) {
255 exifElementTable[i].GpsTag = TRUE;
256 exifElementTable[i].Tag = GpsTagNameToValue(tag);
257 ++gpsTagCount;
258 } else {
259 exifElementTable[i].GpsTag = FALSE;
260 exifElementTable[i].Tag = TagNameToValue(tag);
261 ++exifTagCount;
262 }
263 attrPtr = tagEnd + 1;
264
265 // next get the length of the attribute value
266 int valueLen = atoi(attrPtr);
267 attrPtr = strchr(attrPtr, ' ') + 1;
268 if (attrPtr == 0) {
269 #ifdef SUPERDEBUG
270 LOGE("saveAttributes: couldn't find end of value len");
271 #endif
272 goto exit;
273 }
274 exifElementTable[i].Value = malloc(valueLen + 1);
275 if (exifElementTable[i].Value == NULL) {
276 goto exit;
277 }
278 memcpy(exifElementTable[i].Value, attrPtr, valueLen);
279 exifElementTable[i].Value[valueLen] = 0;
280 exifElementTable[i].DataLength = valueLen;
281
282 attrPtr += valueLen;
283
284 #ifdef SUPERDEBUG
285 LOGE("tag %s id %d value %s data length=%d isGps=%d", tag, exifElementTable[i].Tag,
286 exifElementTable[i].Value, exifElementTable[i].DataLength, exifElementTable[i].GpsTag);
287 #endif
288 }
289
290 filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
291 #ifdef SUPERDEBUG
292 LOGE("Call loadAttributes() with filename is %s. Loading exif info\n", filename);
293 #endif
294 loadExifInfo(filename, TRUE);
295
296 #ifdef SUPERDEBUG
297 // DumpExifMap = TRUE;
298 ShowTags = TRUE;
299 ShowImageInfo(TRUE);
300 LOGE("create exif 2");
301 #endif
302
303 // If the jpg file has a thumbnail, preserve it.
304 int thumbnailLength = ImageInfo.ThumbnailSize;
305 if (ImageInfo.ThumbnailOffset) {
306 Section_t* ExifSection = FindSection(M_EXIF);
307 if (ExifSection) {
308 uchar* thumbnailPointer = ExifSection->Data + ImageInfo.ThumbnailOffset + 8;
309 thumbnailData = (uchar*)malloc(ImageInfo.ThumbnailSize);
310 // if the malloc fails, we just won't copy the thumbnail
311 if (thumbnailData) {
312 memcpy(thumbnailData, thumbnailPointer, thumbnailLength);
313 }
314 }
315 }
316
317 create_EXIF(exifElementTable, exifTagCount, gpsTagCount);
318
319 if (thumbnailData) {
320 copyThumbnailData(thumbnailData, thumbnailLength);
321 }
322
323 exit:
324 #ifdef SUPERDEBUG
325 LOGE("cleaning up now in saveAttributes");
326 #endif
327 // try to clean up resources
328 if (attributes) {
329 (*env)->ReleaseStringUTFChars(env, jattributes, attributes);
330 }
331 if (filename) {
332 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
333 }
334 if (exifElementTable) {
335 // free the table
336 for (i = 0; i < attrCnt; i++) {
337 free(exifElementTable[i].Value);
338 }
339 free(exifElementTable);
340 }
341 if (thumbnailData) {
342 free(thumbnailData);
343 }
344 #ifdef SUPERDEBUG
345 LOGE("returning from saveAttributes");
346 #endif
347
348 // Temporarily saving these commented out lines because they represent a lot of figuring out
349 // patterns for JNI.
350 // // Get link to Method "entrySet"
351 // jmethodID entrySetMethod = (*env)->GetMethodID(env, jclass_of_hashmap, "entrySet", "()Ljava/util/Set;");
352 //
353 // // Invoke the "entrySet" method on the HashMap object
354 // jobject jobject_of_entryset = (*env)->CallObjectMethod(env, hashMap, entrySetMethod);
355 //
356 // // Get the Set Class
357 // jclass jclass_of_set = (*env)->FindClass(env, "java/util/Set");
358 //
359 // if (jclass_of_set == 0) {
360 // printf("java/util/Set lookup failed\n");
361 // return;
362 // }
363 //
364 // // Get link to Method "iterator"
365 // jmethodID iteratorMethod = (*env)->GetMethodID(env, jclass_of_set, "iterator", "()Ljava/util/Iterator;");
366 //
367 // // Invoke the "iterator" method on the jobject_of_entryset variable of type Set
368 // jobject jobject_of_iterator = (*env)->CallObjectMethod(env, jobject_of_entryset, iteratorMethod);
369 //
370 // // Get the "Iterator" class
371 // jclass jclass_of_iterator = (*env)->FindClass(env, "java/util/Iterator");
372 //
373 // // Get link to Method "hasNext"
374 // jmethodID hasNextMethod = (*env)->GetMethodID(env, jclass_of_iterator, "hasNext", "()Z");
375 //
376 // // Invoke - Get the value hasNextMethod
377 // jboolean bHasNext = (*env)->CallBooleanMethod(env, jobject_of_iterator, hasNextMethod);
378
379 // // Get link to Method "hasNext"
380 // jmethodID nextMethod = (*env)->GetMethodID(env, jclass_of_iterator, "next", "()Ljava/util/Map/Entry;");
381 //
382 // jclass jclass_of_mapentry = (*env)->FindClass(env, "java/util/Map/Entry");
383 //
384 // jmethodID getKeyMethod = (*env)->GetMethodID(env, jclass_of_mapentry, "getKey", "()Ljava/lang/Object");
385 //
386 // jmethodID getValueMethod = (*env)->GetMethodID(env, jclass_of_mapentry, "getValue", "()Ljava/lang/Object");
387 }
388
appendThumbnail(JNIEnv * env,jobject jobj,jstring jfilename,jstring jthumbnailfilename)389 static jboolean appendThumbnail(JNIEnv *env, jobject jobj, jstring jfilename, jstring jthumbnailfilename)
390 {
391 #ifdef SUPERDEBUG
392 LOGE("******************************** appendThumbnail\n");
393 #endif
394
395 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
396 if (filename == NULL) {
397 return JNI_FALSE;
398 }
399 const char* thumbnailfilename = (*env)->GetStringUTFChars(env, jthumbnailfilename, NULL);
400 if (thumbnailfilename == NULL) {
401 return JNI_FALSE;
402 }
403 #ifdef SUPERDEBUG
404 LOGE("*******before actual call to ReplaceThumbnail\n");
405 ShowImageInfo(TRUE);
406 #endif
407 ReplaceThumbnail(thumbnailfilename);
408 #ifdef SUPERDEBUG
409 ShowImageInfo(TRUE);
410 #endif
411 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
412 (*env)->ReleaseStringUTFChars(env, jthumbnailfilename, thumbnailfilename);
413
414 DiscardData();
415 return JNI_TRUE;
416 }
417
commitChanges(JNIEnv * env,jobject jobj,jstring jfilename)418 static void commitChanges(JNIEnv *env, jobject jobj, jstring jfilename)
419 {
420 #ifdef SUPERDEBUG
421 LOGE("******************************** commitChanges\n");
422 #endif
423 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
424 if (filename) {
425 saveJPGFile(filename);
426 DiscardData();
427 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
428 }
429 }
430
getThumbnail(JNIEnv * env,jobject jobj,jstring jfilename)431 static jbyteArray getThumbnail(JNIEnv *env, jobject jobj, jstring jfilename)
432 {
433 #ifdef SUPERDEBUG
434 LOGE("******************************** getThumbnail\n");
435 #endif
436
437 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
438 if (filename) {
439 loadExifInfo(filename, FALSE);
440 Section_t* ExifSection = FindSection(M_EXIF);
441 if (ExifSection == NULL || ImageInfo.ThumbnailSize == 0) {
442 #ifdef SUPERDEBUG
443 LOGE("no exif section or size == 0, so no thumbnail\n");
444 #endif
445 goto noThumbnail;
446 }
447 uchar* thumbnailPointer = ExifSection->Data + ImageInfo.ThumbnailOffset + 8;
448
449 jbyteArray byteArray = (*env)->NewByteArray(env, ImageInfo.ThumbnailSize);
450 if (byteArray == NULL) {
451 #ifdef SUPERDEBUG
452 LOGE("couldn't allocate thumbnail memory, so no thumbnail\n");
453 #endif
454 goto noThumbnail;
455 }
456 (*env)->SetByteArrayRegion(env, byteArray, 0, ImageInfo.ThumbnailSize, thumbnailPointer);
457 #ifdef SUPERDEBUG
458 LOGE("thumbnail size %d\n", ImageInfo.ThumbnailSize);
459 #endif
460 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
461 return byteArray;
462 }
463 noThumbnail:
464 if (filename) {
465 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
466 }
467 DiscardData();
468 return NULL;
469 }
470
471 static int attributeCount; // keep track of how many attributes we've added
472
473 // returns new buffer length
addKeyValueString(char ** buf,int bufLen,const char * key,const char * value)474 static int addKeyValueString(char** buf, int bufLen, const char* key, const char* value) {
475 // Appends to buf like this: "ImageLength=4 1024"
476
477 char valueLen[15];
478 snprintf(valueLen, 15, "=%d ", (int)strlen(value));
479
480 // check to see if buf has enough room to append
481 int len = strlen(key) + strlen(valueLen) + strlen(value);
482 int newLen = strlen(*buf) + len;
483 if (newLen >= bufLen) {
484 #ifdef REALLOCTEST
485 bufLen = newLen + 5;
486 LOGE("reallocing to %d", bufLen);
487 #else
488 bufLen = newLen + 500;
489 #endif
490 *buf = realloc(*buf, bufLen);
491 if (*buf == NULL) {
492 return 0;
493 }
494 }
495 // append the new attribute and value
496 snprintf(*buf + strlen(*buf), bufLen, "%s%s%s", key, valueLen, value);
497 #ifdef SUPERDEBUG
498 LOGE("buf %s", *buf);
499 #endif
500 ++attributeCount;
501 return bufLen;
502 }
503
504 // returns new buffer length
addKeyValueInt(char ** buf,int bufLen,const char * key,int value)505 static int addKeyValueInt(char** buf, int bufLen, const char* key, int value) {
506 char valueStr[20];
507 snprintf(valueStr, 20, "%d", value);
508
509 return addKeyValueString(buf, bufLen, key, valueStr);
510 }
511
512 // returns new buffer length
addKeyValueDouble(char ** buf,int bufLen,const char * key,double value,const char * format)513 static int addKeyValueDouble(char** buf, int bufLen, const char* key, double value, const char* format) {
514 char valueStr[30];
515 snprintf(valueStr, 30, format, value);
516
517 return addKeyValueString(buf, bufLen, key, valueStr);
518 }
519
getAttributes(JNIEnv * env,jobject jobj,jstring jfilename)520 static jstring getAttributes(JNIEnv *env, jobject jobj, jstring jfilename)
521 {
522 #ifdef SUPERDEBUG
523 LOGE("******************************** getAttributes\n");
524 #endif
525 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
526 loadExifInfo(filename, FALSE);
527 #ifdef SUPERDEBUG
528 ShowImageInfo(TRUE);
529 #endif
530 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
531
532 attributeCount = 0;
533 #ifdef REALLOCTEST
534 int bufLen = 5;
535 #else
536 int bufLen = 1000;
537 #endif
538 char* buf = malloc(bufLen);
539 if (buf == NULL) {
540 return NULL;
541 }
542 *buf = 0; // start the string out at zero length
543
544 // save a fake "hasThumbnail" tag to pass to the java ExifInterface
545 bufLen = addKeyValueString(&buf, bufLen, "hasThumbnail",
546 ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE || ImageInfo.ThumbnailSize == 0 ?
547 "false" : "true");
548 if (bufLen == 0) return NULL;
549
550 if (ImageInfo.CameraMake[0]) {
551 bufLen = addKeyValueString(&buf, bufLen, "Make", ImageInfo.CameraMake);
552 if (bufLen == 0) return NULL;
553 }
554 if (ImageInfo.CameraModel[0]) {
555 bufLen = addKeyValueString(&buf, bufLen, "Model", ImageInfo.CameraModel);
556 if (bufLen == 0) return NULL;
557 }
558 if (ImageInfo.DateTime[0]) {
559 bufLen = addKeyValueString(&buf, bufLen, "DateTime", ImageInfo.DateTime);
560 if (bufLen == 0) return NULL;
561 }
562 bufLen = addKeyValueInt(&buf, bufLen, "ImageWidth", ImageInfo.Width);
563 if (bufLen == 0) return NULL;
564
565 bufLen = addKeyValueInt(&buf, bufLen, "ImageLength", ImageInfo.Height);
566 if (bufLen == 0) return NULL;
567
568 bufLen = addKeyValueInt(&buf, bufLen, "Orientation", ImageInfo.Orientation);
569 if (bufLen == 0) return NULL;
570
571 bufLen = addKeyValueInt(&buf, bufLen, "Flash", ImageInfo.FlashUsed);
572 if (bufLen == 0) return NULL;
573
574 if (ImageInfo.FocalLength){
575 bufLen = addKeyValueInt(&buf, bufLen, "FocalLength", ImageInfo.FocalLength);
576 if (bufLen == 0) return NULL;
577 }
578
579 if (ImageInfo.DigitalZoomRatio > 1.0){
580 // Digital zoom used. Shame on you!
581 bufLen = addKeyValueDouble(&buf, bufLen, "DigitalZoomRatio", ImageInfo.DigitalZoomRatio, "%1.3f");
582 if (bufLen == 0) return NULL;
583 }
584
585 if (ImageInfo.ExposureTime){
586 const char* format;
587 if (ImageInfo.ExposureTime < 0.010){
588 format = "%6.4f";
589 } else {
590 format = "%5.3f";
591 }
592
593 bufLen = addKeyValueDouble(&buf, bufLen, "ExposureTime", (double)ImageInfo.ExposureTime, format);
594 if (bufLen == 0) return NULL;
595 }
596
597 if (ImageInfo.ApertureFNumber){
598 bufLen = addKeyValueDouble(&buf, bufLen, "FNumber", (double)ImageInfo.ApertureFNumber, "%3.1f");
599 if (bufLen == 0) return NULL;
600 }
601
602 if (ImageInfo.Distance){
603 bufLen = addKeyValueDouble(&buf, bufLen, "SubjectDistance", (double)ImageInfo.Distance, "%4.2f");
604 if (bufLen == 0) return NULL;
605 }
606
607 if (ImageInfo.ISOequivalent){
608 bufLen = addKeyValueInt(&buf, bufLen, "ISOSpeedRatings", ImageInfo.ISOequivalent);
609 if (bufLen == 0) return NULL;
610 }
611
612 if (ImageInfo.ExposureBias){
613 // If exposure bias was specified, but set to zero, presumably its no bias at all,
614 // so only show it if its nonzero.
615 bufLen = addKeyValueDouble(&buf, bufLen, "ExposureBiasValue", (double)ImageInfo.ExposureBias, "%4.2f");
616 if (bufLen == 0) return NULL;
617 }
618
619 bufLen = addKeyValueInt(&buf, bufLen, "WhiteBalance", ImageInfo.Whitebalance);
620 if (bufLen == 0) return NULL;
621
622 bufLen = addKeyValueInt(&buf, bufLen, "LightSource", ImageInfo.LightSource);
623 if (bufLen == 0) return NULL;
624
625
626 if (ImageInfo.MeteringMode) {
627 bufLen = addKeyValueInt(&buf, bufLen, "MeteringMode", ImageInfo.MeteringMode);
628 if (bufLen == 0) return NULL;
629 }
630
631 if (ImageInfo.ExposureProgram) {
632 bufLen = addKeyValueInt(&buf, bufLen, "ExposureProgram", ImageInfo.ExposureProgram);
633 if (bufLen == 0) return NULL;
634 }
635
636 if (ImageInfo.ExposureMode) {
637 bufLen = addKeyValueInt(&buf, bufLen, "ExposureMode", ImageInfo.ExposureMode);
638 if (bufLen == 0) return NULL;
639 }
640
641 if (ImageInfo.GpsInfoPresent) {
642 bufLen = addKeyValueString(&buf, bufLen, "GPSLatitude", ImageInfo.GpsLatRaw);
643 if (bufLen == 0) return NULL;
644 bufLen = addKeyValueString(&buf, bufLen, "GPSLatitudeRef", ImageInfo.GpsLatRef);
645 if (bufLen == 0) return NULL;
646 bufLen = addKeyValueString(&buf, bufLen, "GPSLongitude", ImageInfo.GpsLongRaw);
647 if (bufLen == 0) return NULL;
648 bufLen = addKeyValueString(&buf, bufLen, "GPSLongitudeRef", ImageInfo.GpsLongRef);
649 if (bufLen == 0) return NULL;
650 if (ImageInfo.GpsAlt[0]) {
651 bufLen = addKeyValueString(&buf, bufLen, "GPSAltitude", ImageInfo.GpsAlt);
652 if (bufLen == 0) return NULL;
653 }
654 }
655
656 if (ImageInfo.Comments[0]) {
657 bufLen = addKeyValueString(&buf, bufLen, "UserComment", ImageInfo.Comments);
658 if (bufLen == 0) return NULL;
659 }
660
661 // put the attribute count at the beginnnig of the string
662 int finalBufLen = strlen(buf) + 20;
663 char* finalResult = malloc(finalBufLen);
664 if (finalResult == NULL) {
665 free(buf);
666 return NULL;
667 }
668 snprintf(finalResult, finalBufLen, "%d %s", attributeCount, buf);
669 int k;
670 for (k = 0; k < finalBufLen; k++)
671 if (finalResult[k] > 127)
672 finalResult[k] = '?';
673 free(buf);
674
675 #ifdef SUPERDEBUG
676 LOGE("*********Returning result \"%s\"", finalResult);
677 #endif
678 jstring result = ((*env)->NewStringUTF(env, finalResult));
679 free(finalResult);
680 DiscardData();
681 return result;
682 }
683
684 static const char *classPathName = "android/media/ExifInterface";
685
686 static JNINativeMethod methods[] = {
687 {"saveAttributesNative", "(Ljava/lang/String;Ljava/lang/String;)V", (void*)saveAttributes },
688 {"getAttributesNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAttributes },
689 {"appendThumbnailNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)appendThumbnail },
690 {"commitChangesNative", "(Ljava/lang/String;)V", (void*)commitChanges },
691 {"getThumbnailNative", "(Ljava/lang/String;)[B", (void*)getThumbnail },
692 };
693
694 /*
695 * Register several native methods for one class.
696 */
registerNativeMethods(JNIEnv * env,const char * className,JNINativeMethod * gMethods,int numMethods)697 static int registerNativeMethods(JNIEnv* env, const char* className,
698 JNINativeMethod* gMethods, int numMethods)
699 {
700 jclass clazz;
701
702 clazz = (*env)->FindClass(env, className);
703 if (clazz == NULL) {
704 fprintf(stderr,
705 "Native registration unable to find class '%s'\n", className);
706 return JNI_FALSE;
707 }
708 if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
709 fprintf(stderr, "RegisterNatives failed for '%s'\n", className);
710 return JNI_FALSE;
711 }
712
713 return JNI_TRUE;
714 }
715
716 /*
717 * Register native methods for all classes we know about.
718 */
registerNatives(JNIEnv * env)719 static int registerNatives(JNIEnv* env)
720 {
721 return jniRegisterNativeMethods(env, classPathName,
722 methods, NELEM(methods));
723 }
724
725 /*
726 * Set some test stuff up.
727 *
728 * Returns the JNI version on success, -1 on failure.
729 */
JNI_OnLoad(JavaVM * vm,void * reserved)730 __attribute__ ((visibility("default"))) jint JNI_OnLoad(JavaVM* vm, void* reserved)
731 {
732 JNIEnv* env = NULL;
733 jint result = -1;
734
735 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
736 fprintf(stderr, "ERROR: GetEnv failed\n");
737 goto bail;
738 }
739 assert(env != NULL);
740
741 printf("In mgmain JNI_OnLoad\n");
742
743 if (registerNatives(env) < 0) {
744 fprintf(stderr, "ERROR: Exif native registration failed\n");
745 goto bail;
746 }
747
748 /* success -- return valid version number */
749 result = JNI_VERSION_1_4;
750
751 bail:
752 return result;
753 }
754