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 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 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 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 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 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 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 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 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 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 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 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". 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 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 */ 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 */ 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 */ 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