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