blob: ab820e37ab08c59e95535f7701581365e994fbd6 [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>
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
57static 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
67extern void ResetJpgfile();
68
69static 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;
repo syncdb940f22009-07-06 18:03:36 +080090
The Android Open Source Project34a25642009-03-03 19:30:03 -080091 // 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
107static 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)) {
repo syncdb940f22009-07-06 18:03:36 +0800135
The Android Open Source Project34a25642009-03-03 19:30:03 -0800136 // 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);
repo syncdb940f22009-07-06 18:03:36 +0800144
The Android Open Source Project34a25642009-03-03 19:30:03 -0800145 mtime.actime = buf.st_mtime;
146 mtime.modtime = buf.st_mtime;
repo syncdb940f22009-07-06 18:03:36 +0800147
The Android Open Source Project34a25642009-03-03 19:30:03 -0800148 utime(filename, &mtime);
149 }
repo syncdb940f22009-07-06 18:03:36 +0800150
The Android Open Source Project34a25642009-03-03 19:30:03 -0800151 // 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
168void 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
194static 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
323exit:
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
The Android Open Source Project34a25642009-03-03 19:30:03 -0800348// 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
389static 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);
repo syncdb940f22009-07-06 18:03:36 +0800413
The Android Open Source Project34a25642009-03-03 19:30:03 -0800414 DiscardData();
415 return JNI_TRUE;
416}
417
418static 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);
Chih-Chung Chang6a3cf8f2009-09-21 10:20:27 +0800426 DiscardData();
The Android Open Source Project34a25642009-03-03 19:30:03 -0800427 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
428 }
429}
430
431static 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);
Jiangping Wueb7bd102010-08-27 10:56:55 +0200461 DiscardData();
The Android Open Source Project34a25642009-03-03 19:30:03 -0800462 return byteArray;
463 }
464noThumbnail:
465 if (filename) {
466 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
467 }
468 DiscardData();
469 return NULL;
470}
471
472static int attributeCount; // keep track of how many attributes we've added
473
474// returns new buffer length
475static int addKeyValueString(char** buf, int bufLen, const char* key, const char* value) {
476 // Appends to buf like this: "ImageLength=4 1024"
477
478 char valueLen[15];
479 snprintf(valueLen, 15, "=%d ", (int)strlen(value));
480
481 // check to see if buf has enough room to append
482 int len = strlen(key) + strlen(valueLen) + strlen(value);
483 int newLen = strlen(*buf) + len;
484 if (newLen >= bufLen) {
485#ifdef REALLOCTEST
486 bufLen = newLen + 5;
487 LOGE("reallocing to %d", bufLen);
488#else
489 bufLen = newLen + 500;
490#endif
491 *buf = realloc(*buf, bufLen);
492 if (*buf == NULL) {
493 return 0;
494 }
495 }
496 // append the new attribute and value
497 snprintf(*buf + strlen(*buf), bufLen, "%s%s%s", key, valueLen, value);
498#ifdef SUPERDEBUG
499 LOGE("buf %s", *buf);
500#endif
501 ++attributeCount;
502 return bufLen;
503}
504
505// returns new buffer length
506static int addKeyValueInt(char** buf, int bufLen, const char* key, int value) {
507 char valueStr[20];
508 snprintf(valueStr, 20, "%d", value);
509
510 return addKeyValueString(buf, bufLen, key, valueStr);
511}
512
513// returns new buffer length
514static int addKeyValueDouble(char** buf, int bufLen, const char* key, double value, const char* format) {
515 char valueStr[30];
516 snprintf(valueStr, 30, format, value);
517
518 return addKeyValueString(buf, bufLen, key, valueStr);
519}
520
Wu-cheng Li574d52d2010-01-31 22:34:33 +0800521// Returns new buffer length. Rational value will be appended as "numerator/denominator".
522static int addKeyValueRational(char** buf, int bufLen, const char* key, rat_t value) {
523 char valueStr[25];
524 snprintf(valueStr, sizeof(valueStr), "%u/%u", value.num, value.denom);
525 return addKeyValueString(buf, bufLen, key, valueStr);
526}
527
The Android Open Source Project34a25642009-03-03 19:30:03 -0800528static jstring getAttributes(JNIEnv *env, jobject jobj, jstring jfilename)
529{
530#ifdef SUPERDEBUG
531 LOGE("******************************** getAttributes\n");
532#endif
533 const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL);
534 loadExifInfo(filename, FALSE);
535#ifdef SUPERDEBUG
536 ShowImageInfo(TRUE);
537#endif
538 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
539
540 attributeCount = 0;
541#ifdef REALLOCTEST
542 int bufLen = 5;
543#else
544 int bufLen = 1000;
545#endif
546 char* buf = malloc(bufLen);
547 if (buf == NULL) {
548 return NULL;
549 }
550 *buf = 0; // start the string out at zero length
551
552 // save a fake "hasThumbnail" tag to pass to the java ExifInterface
553 bufLen = addKeyValueString(&buf, bufLen, "hasThumbnail",
554 ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE || ImageInfo.ThumbnailSize == 0 ?
555 "false" : "true");
556 if (bufLen == 0) return NULL;
557
558 if (ImageInfo.CameraMake[0]) {
559 bufLen = addKeyValueString(&buf, bufLen, "Make", ImageInfo.CameraMake);
560 if (bufLen == 0) return NULL;
561 }
562 if (ImageInfo.CameraModel[0]) {
563 bufLen = addKeyValueString(&buf, bufLen, "Model", ImageInfo.CameraModel);
564 if (bufLen == 0) return NULL;
565 }
566 if (ImageInfo.DateTime[0]) {
567 bufLen = addKeyValueString(&buf, bufLen, "DateTime", ImageInfo.DateTime);
568 if (bufLen == 0) return NULL;
569 }
570 bufLen = addKeyValueInt(&buf, bufLen, "ImageWidth", ImageInfo.Width);
571 if (bufLen == 0) return NULL;
572
573 bufLen = addKeyValueInt(&buf, bufLen, "ImageLength", ImageInfo.Height);
574 if (bufLen == 0) return NULL;
575
576 bufLen = addKeyValueInt(&buf, bufLen, "Orientation", ImageInfo.Orientation);
577 if (bufLen == 0) return NULL;
578
579 bufLen = addKeyValueInt(&buf, bufLen, "Flash", ImageInfo.FlashUsed);
580 if (bufLen == 0) return NULL;
581
Wu-cheng Li574d52d2010-01-31 22:34:33 +0800582 if (ImageInfo.FocalLength.num != 0 && ImageInfo.FocalLength.denom != 0) {
583 bufLen = addKeyValueRational(&buf, bufLen, "FocalLength", ImageInfo.FocalLength);
The Android Open Source Project34a25642009-03-03 19:30:03 -0800584 if (bufLen == 0) return NULL;
585 }
586
587 if (ImageInfo.DigitalZoomRatio > 1.0){
588 // Digital zoom used. Shame on you!
589 bufLen = addKeyValueDouble(&buf, bufLen, "DigitalZoomRatio", ImageInfo.DigitalZoomRatio, "%1.3f");
590 if (bufLen == 0) return NULL;
591 }
592
593 if (ImageInfo.ExposureTime){
594 const char* format;
595 if (ImageInfo.ExposureTime < 0.010){
596 format = "%6.4f";
597 } else {
598 format = "%5.3f";
599 }
600
601 bufLen = addKeyValueDouble(&buf, bufLen, "ExposureTime", (double)ImageInfo.ExposureTime, format);
602 if (bufLen == 0) return NULL;
603 }
604
605 if (ImageInfo.ApertureFNumber){
606 bufLen = addKeyValueDouble(&buf, bufLen, "FNumber", (double)ImageInfo.ApertureFNumber, "%3.1f");
607 if (bufLen == 0) return NULL;
608 }
609
610 if (ImageInfo.Distance){
611 bufLen = addKeyValueDouble(&buf, bufLen, "SubjectDistance", (double)ImageInfo.Distance, "%4.2f");
612 if (bufLen == 0) return NULL;
613 }
614
615 if (ImageInfo.ISOequivalent){
616 bufLen = addKeyValueInt(&buf, bufLen, "ISOSpeedRatings", ImageInfo.ISOequivalent);
617 if (bufLen == 0) return NULL;
618 }
619
620 if (ImageInfo.ExposureBias){
621 // If exposure bias was specified, but set to zero, presumably its no bias at all,
622 // so only show it if its nonzero.
623 bufLen = addKeyValueDouble(&buf, bufLen, "ExposureBiasValue", (double)ImageInfo.ExposureBias, "%4.2f");
624 if (bufLen == 0) return NULL;
625 }
626
627 bufLen = addKeyValueInt(&buf, bufLen, "WhiteBalance", ImageInfo.Whitebalance);
628 if (bufLen == 0) return NULL;
629
630 bufLen = addKeyValueInt(&buf, bufLen, "LightSource", ImageInfo.LightSource);
631 if (bufLen == 0) return NULL;
632
633
634 if (ImageInfo.MeteringMode) {
635 bufLen = addKeyValueInt(&buf, bufLen, "MeteringMode", ImageInfo.MeteringMode);
636 if (bufLen == 0) return NULL;
637 }
638
639 if (ImageInfo.ExposureProgram) {
640 bufLen = addKeyValueInt(&buf, bufLen, "ExposureProgram", ImageInfo.ExposureProgram);
641 if (bufLen == 0) return NULL;
642 }
643
644 if (ImageInfo.ExposureMode) {
645 bufLen = addKeyValueInt(&buf, bufLen, "ExposureMode", ImageInfo.ExposureMode);
646 if (bufLen == 0) return NULL;
647 }
648
649 if (ImageInfo.GpsInfoPresent) {
Ray Chen8d617232010-03-10 14:59:44 -0800650 if (ImageInfo.GpsLatRaw[0]) {
651 bufLen = addKeyValueString(&buf, bufLen, "GPSLatitude", ImageInfo.GpsLatRaw);
652 if (bufLen == 0) return NULL;
653 }
654 if (ImageInfo.GpsLatRef[0]) {
655 bufLen = addKeyValueString(&buf, bufLen, "GPSLatitudeRef", ImageInfo.GpsLatRef);
656 if (bufLen == 0) return NULL;
657 }
658 if (ImageInfo.GpsLongRaw[0]) {
659 bufLen = addKeyValueString(&buf, bufLen, "GPSLongitude", ImageInfo.GpsLongRaw);
660 if (bufLen == 0) return NULL;
661 }
662 if (ImageInfo.GpsLongRef[0]) {
663 bufLen = addKeyValueString(&buf, bufLen, "GPSLongitudeRef", ImageInfo.GpsLongRef);
664 if (bufLen == 0) return NULL;
665 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800666 if (ImageInfo.GpsAlt[0]) {
667 bufLen = addKeyValueString(&buf, bufLen, "GPSAltitude", ImageInfo.GpsAlt);
668 if (bufLen == 0) return NULL;
669 }
Ray Chena39920c2010-01-15 13:58:10 -0800670 if (ImageInfo.GpsDateStamp[0]) {
671 bufLen = addKeyValueString(&buf, bufLen, "GPSDateStamp", ImageInfo.GpsDateStamp);
672 if (bufLen == 0) return NULL;
673 }
674 if (ImageInfo.GpsTimeStamp[0]) {
675 bufLen = addKeyValueString(&buf, bufLen, "GPSTimeStamp", ImageInfo.GpsTimeStamp);
676 if (bufLen == 0) return NULL;
677 }
Ray Chen434623a2010-03-10 14:59:44 -0800678 if (ImageInfo.GpsProcessingMethod[0]) {
679 bufLen = addKeyValueString(&buf, bufLen, "GPSProcessingMethod", ImageInfo.GpsProcessingMethod);
680 if (bufLen == 0) return NULL;
681 }
The Android Open Source Project34a25642009-03-03 19:30:03 -0800682 }
683
684 if (ImageInfo.Comments[0]) {
685 bufLen = addKeyValueString(&buf, bufLen, "UserComment", ImageInfo.Comments);
686 if (bufLen == 0) return NULL;
687 }
688
689 // put the attribute count at the beginnnig of the string
690 int finalBufLen = strlen(buf) + 20;
691 char* finalResult = malloc(finalBufLen);
692 if (finalResult == NULL) {
693 free(buf);
694 return NULL;
695 }
696 snprintf(finalResult, finalBufLen, "%d %s", attributeCount, buf);
697 int k;
698 for (k = 0; k < finalBufLen; k++)
699 if (finalResult[k] > 127)
700 finalResult[k] = '?';
701 free(buf);
702
703#ifdef SUPERDEBUG
704 LOGE("*********Returning result \"%s\"", finalResult);
705#endif
706 jstring result = ((*env)->NewStringUTF(env, finalResult));
707 free(finalResult);
708 DiscardData();
709 return result;
710}
711
repo syncdb940f22009-07-06 18:03:36 +0800712static const char *classPathName = "android/media/ExifInterface";
The Android Open Source Project34a25642009-03-03 19:30:03 -0800713
714static JNINativeMethod methods[] = {
715 {"saveAttributesNative", "(Ljava/lang/String;Ljava/lang/String;)V", (void*)saveAttributes },
716 {"getAttributesNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAttributes },
717 {"appendThumbnailNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)appendThumbnail },
718 {"commitChangesNative", "(Ljava/lang/String;)V", (void*)commitChanges },
719 {"getThumbnailNative", "(Ljava/lang/String;)[B", (void*)getThumbnail },
720};
721
722/*
723 * Register several native methods for one class.
724 */
725static int registerNativeMethods(JNIEnv* env, const char* className,
726 JNINativeMethod* gMethods, int numMethods)
727{
728 jclass clazz;
729
730 clazz = (*env)->FindClass(env, className);
731 if (clazz == NULL) {
732 fprintf(stderr,
733 "Native registration unable to find class '%s'\n", className);
734 return JNI_FALSE;
735 }
736 if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
737 fprintf(stderr, "RegisterNatives failed for '%s'\n", className);
738 return JNI_FALSE;
739 }
740
741 return JNI_TRUE;
742}
743
744/*
745 * Register native methods for all classes we know about.
746 */
747static int registerNatives(JNIEnv* env)
748{
749 return jniRegisterNativeMethods(env, classPathName,
750 methods, NELEM(methods));
751}
752
753/*
754 * Set some test stuff up.
755 *
756 * Returns the JNI version on success, -1 on failure.
757 */
758__attribute__ ((visibility("default"))) jint JNI_OnLoad(JavaVM* vm, void* reserved)
759{
760 JNIEnv* env = NULL;
761 jint result = -1;
762
763 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
764 fprintf(stderr, "ERROR: GetEnv failed\n");
765 goto bail;
766 }
767 assert(env != NULL);
768
769 printf("In mgmain JNI_OnLoad\n");
770
771 if (registerNatives(env) < 0) {
772 fprintf(stderr, "ERROR: Exif native registration failed\n");
773 goto bail;
774 }
775
776 /* success -- return valid version number */
777 result = JNI_VERSION_1_4;
778
779bail:
780 return result;
781}