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