blob: 9ad6443a2a65a2c16d5f9e0f80341a3168a371cf [file] [log] [blame]
The Android Open Source Project34a25642009-03-03 19:30:03 -08001/*
2
3Copyright (c) 2008, The Android Open Source Project
4All rights reserved.
5
6Redistribution and use in source and binary forms, with or without
7modification, are permitted provided that the following conditions
8are 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
repo syncdb940f22009-07-06 18:03:36 +080013 the documentation and/or other materials provided with the
The Android Open Source Project34a25642009-03-03 19:30:03 -080014 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
19THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
repo syncdb940f22009-07-06 18:03:36 +080026OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
The Android Open Source Project34a25642009-03-03 19:30:03 -080027AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30SUCH DAMAGE.
31
32*/
33
34#include <nativehelper/JNIHelp.h>
35#include <nativehelper/jni.h>
36
37#include <assert.h>
Dongwon Kang189fc1d2011-04-13 14:39:37 +090038#include <ctype.h>
The Android Open Source Project34a25642009-03-03 19:30:03 -080039#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
58static 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
68extern void ResetJpgfile();
69
70static 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;
repo syncdb940f22009-07-06 18:03:36 +080091
The Android Open Source Project34a25642009-03-03 19:30:03 -080092 // 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
108static 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)) {
repo syncdb940f22009-07-06 18:03:36 +0800136
The Android Open Source Project34a25642009-03-03 19:30:03 -0800137 // 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);
repo syncdb940f22009-07-06 18:03:36 +0800145
The Android Open Source Project34a25642009-03-03 19:30:03 -0800146 mtime.actime = buf.st_mtime;
147 mtime.modtime = buf.st_mtime;
repo syncdb940f22009-07-06 18:03:36 +0800148
The Android Open Source Project34a25642009-03-03 19:30:03 -0800149 utime(filename, &mtime);
150 }
repo syncdb940f22009-07-06 18:03:36 +0800151
The Android Open Source Project34a25642009-03-03 19:30:03 -0800152 // 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
169void 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
195static 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
324exit:
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
The Android Open Source Project34a25642009-03-03 19:30:03 -0800349// 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
390static 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);
repo syncdb940f22009-07-06 18:03:36 +0800414
The Android Open Source Project34a25642009-03-03 19:30:03 -0800415 DiscardData();
416 return JNI_TRUE;
417}
418
419static 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);
Chih-Chung Chang6a3cf8f2009-09-21 10:20:27 +0800427 DiscardData();
The Android Open Source Project34a25642009-03-03 19:30:03 -0800428 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
429 }
430}
431
432static 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);
Jiangping Wueb7bd102010-08-27 10:56:55 +0200462 DiscardData();
The Android Open Source Project34a25642009-03-03 19:30:03 -0800463 return byteArray;
464 }
465noThumbnail:
466 if (filename) {
467 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
468 }
469 DiscardData();
470 return NULL;
471}
472
473static int attributeCount; // keep track of how many attributes we've added
474
475// returns new buffer length
476static 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
507static 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
515static 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
Wu-cheng Li574d52d2010-01-31 22:34:33 +0800522// Returns new buffer length. Rational value will be appended as "numerator/denominator".
523static 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
The Android Open Source Project34a25642009-03-03 19:30:03 -0800529static 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
Ray Chen2856a272010-12-07 18:19:28 +0800580 if (ImageInfo.FlashUsed >= 0) {
581 bufLen = addKeyValueInt(&buf, bufLen, "Flash", ImageInfo.FlashUsed);
582 if (bufLen == 0) return NULL;
583 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800584
Wu-cheng Li574d52d2010-01-31 22:34:33 +0800585 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
586 bufLen = addKeyValueRational(&buf, bufLen, "FocalLength", ImageInfo.FocalLength);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800587 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
Ray Chen2856a272010-12-07 18:19:28 +0800630 if (ImageInfo.Whitebalance >= 0) {
631 bufLen = addKeyValueInt(&buf, bufLen, "WhiteBalance", ImageInfo.Whitebalance);
632 if (bufLen == 0) return NULL;
633 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800634
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) {
Ray Chen8d617232010-03-10 14:59:44 -0800655 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 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800671 if (ImageInfo.GpsAlt[0]) {
Wu-cheng Li1ed81972010-05-21 12:02:32 +0800672 bufLen = addKeyValueRational(&buf, bufLen, "GPSAltitude", ImageInfo.GpsAltRaw);
673 bufLen = addKeyValueInt(&buf, bufLen, "GPSAltitudeRef", ImageInfo.GpsAltRef);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800674 if (bufLen == 0) return NULL;
675 }
Ray Chena39920c2010-01-15 13:58:10 -0800676 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 }
Ray Chen434623a2010-03-10 14:59:44 -0800684 if (ImageInfo.GpsProcessingMethod[0]) {
685 bufLen = addKeyValueString(&buf, bufLen, "GPSProcessingMethod", ImageInfo.GpsProcessingMethod);
686 if (bufLen == 0) return NULL;
687 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800688 }
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++)
Dongwon Kang189fc1d2011-04-13 14:39:37 +0900705 if (!isascii(finalResult[k]))
The Android Open Source Project34a25642009-03-03 19:30:03 -0800706 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
repo syncdb940f22009-07-06 18:03:36 +0800718static const char *classPathName = "android/media/ExifInterface";
The Android Open Source Project34a25642009-03-03 19:30:03 -0800719
720static 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 */
731static 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 */
753static 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 */
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
785bail:
786 return result;
787}