diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-10-21 07:00:00 -0700 |
commit | 426377d9ce95606bac741d99b5b0a3e2038924fe (patch) | |
tree | e369cfdda7dfd42203aee8100cad25e2033f1cc8 /main.c | |
download | jhead-426377d9ce95606bac741d99b5b0a3e2038924fe.tar.gz |
Initial Contributionandroid-1.0release-1.0cdma-import
Diffstat (limited to 'main.c')
-rw-r--r-- | main.c | 759 |
1 files changed, 759 insertions, 0 deletions
@@ -0,0 +1,759 @@ +/* + +Copyright (c) 2008, The Android Open Source Project +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Google, Inc. nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +*/ + +#include <nativehelper/JNIHelp.h> +#include <nativehelper/jni.h> + +#include <assert.h> +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <sys/stat.h> +#include <utils/Log.h> + +#include "jhead.h" + +#ifndef NELEM +#define NELEM(x) ((int)(sizeof(x) / sizeof((x)[0]))) +#endif + +// Define the line below to turn on poor man's debugging output +#undef SUPERDEBUG + +// Various tests +#undef REALLOCTEST +#undef OUTOFMEMORYTEST1 + +static void addExifAttibute(JNIEnv *env, jmethodID putMethod, jobject hashMap, char* key, char* value) { + jstring jkey = (*env)->NewStringUTF(env, key); + jstring jvalue = (*env)->NewStringUTF(env, value); + + jobject jobject_of_entryset = (*env)->CallObjectMethod(env, hashMap, putMethod, jkey, jvalue); + + (*env)->ReleaseStringUTFChars(env, jkey, key); + (*env)->ReleaseStringUTFChars(env, jvalue, value); +} + +extern void ResetJpgfile(); + +static int loadExifInfo(const char* FileName, int readJPG) { +#ifdef SUPERDEBUG + LOGE("loadExifInfo"); +#endif + int Modified = FALSE; + ReadMode_t ReadMode = READ_METADATA; + if (readJPG) { + // Must add READ_IMAGE else we can't write the JPG back out. + ReadMode |= READ_IMAGE; + } + +#ifdef SUPERDEBUG + LOGE("ResetJpgfile"); +#endif + ResetJpgfile(); + + // Start with an empty image information structure. + memset(&ImageInfo, 0, sizeof(ImageInfo)); + ImageInfo.FlashUsed = -1; + ImageInfo.MeteringMode = -1; + ImageInfo.Whitebalance = -1; + + // Store file date/time. + { + struct stat st; + if (stat(FileName, &st) >= 0) { + ImageInfo.FileDateTime = st.st_mtime; + ImageInfo.FileSize = st.st_size; + } + } + + strncpy(ImageInfo.FileName, FileName, PATH_MAX); +#ifdef SUPERDEBUG + LOGE("ReadJpegFile"); +#endif + return ReadJpegFile(FileName, ReadMode); +} + +static void saveJPGFile(const char* filename) { + char backupName[400]; + struct stat buf; + +#ifdef SUPERDEBUG + LOGE("Modified: %s\n", filename); +#endif + + strncpy(backupName, filename, 395); + strcat(backupName, ".t"); + + // Remove any .old file name that may pre-exist +#ifdef SUPERDEBUG + LOGE("removing backup %s", backupName); +#endif + unlink(backupName); + + // Rename the old file. +#ifdef SUPERDEBUG + LOGE("rename %s to %s", filename, backupName); +#endif + rename(filename, backupName); + + // Write the new file. +#ifdef SUPERDEBUG + LOGE("WriteJpegFile %s", filename); +#endif + if (WriteJpegFile(filename)) { + + // Copy the access rights from original file +#ifdef SUPERDEBUG + LOGE("stating old file %s", backupName); +#endif + if (stat(backupName, &buf) == 0){ + // set Unix access rights and time to new file + struct utimbuf mtime; + chmod(filename, buf.st_mode); + + mtime.actime = buf.st_mtime; + mtime.modtime = buf.st_mtime; + + utime(filename, &mtime); + } + + // Now that we are done, remove original file. +#ifdef SUPERDEBUG + LOGE("unlinking old file %s", backupName); +#endif + unlink(backupName); +#ifdef SUPERDEBUG + LOGE("returning from saveJPGFile"); +#endif + } else { +#ifdef SUPERDEBUG + LOGE("WriteJpegFile failed, restoring from backup file"); +#endif + // move back the backup file + rename(backupName, filename); + } +} + +void copyThumbnailData(uchar* thumbnailData, int thumbnailLen) { +#ifdef SUPERDEBUG + LOGE("******************************** copyThumbnailData\n"); +#endif + Section_t* ExifSection = FindSection(M_EXIF); + if (ExifSection == NULL) { + return; + } + int NewExifSize = ImageInfo.ThumbnailOffset+8+thumbnailLen; + ExifSection->Data = (uchar *)realloc(ExifSection->Data, NewExifSize); + if (ExifSection->Data == NULL) { + return; + } + uchar* ThumbnailPointer = ExifSection->Data+ImageInfo.ThumbnailOffset+8; + + memcpy(ThumbnailPointer, thumbnailData, thumbnailLen); + + ImageInfo.ThumbnailSize = thumbnailLen; + + Put32u(ExifSection->Data+ImageInfo.ThumbnailSizeOffset+8, thumbnailLen); + + ExifSection->Data[0] = (uchar)(NewExifSize >> 8); + ExifSection->Data[1] = (uchar)NewExifSize; + ExifSection->Size = NewExifSize; +} + +static void saveAttributes(JNIEnv *env, jobject jobj, jstring jfilename, jstring jattributes) +{ +#ifdef SUPERDEBUG + LOGE("******************************** saveAttributes\n"); +#endif + // format of attributes string passed from java: + // "attrCnt attr1=valueLen value1attr2=value2Len value2..." + // example input: "4 ImageLength=4 1024Model=6 FooImageWidth=4 1280Make=3 FOO" + ExifElement_t* exifElementTable = NULL; + const char* filename = NULL; + uchar* thumbnailData = NULL; + int attrCnt = 0; + const char* attributes = (*env)->GetStringUTFChars(env, jattributes, NULL); + if (attributes == NULL) { + goto exit; + } +#ifdef SUPERDEBUG + LOGE("attributes %s\n", attributes); +#endif + + // Get the number of attributes - it's the first number in the string. + attrCnt = atoi(attributes); + char* attrPtr = strchr(attributes, ' ') + 1; +#ifdef SUPERDEBUG + LOGE("attribute count %d attrPtr %s\n", attrCnt, attrPtr); +#endif + + // Load all the hash exif elements into a more c-like structure + exifElementTable = malloc(sizeof(ExifElement_t) * attrCnt); + if (exifElementTable == NULL) { + goto exit; + } +#ifdef OUTOFMEMORYTEST1 + goto exit; +#endif + + int i; + char tag[100]; + int gpsTagCount = 0; + int exifTagCount = 0; + + for (i = 0; i < attrCnt; i++) { + // get an element from the attribute string and add it to the c structure + // first, extract the attribute name + char* tagEnd = strchr(attrPtr, '='); + if (tagEnd == 0) { +#ifdef SUPERDEBUG + LOGE("saveAttributes: couldn't find end of tag"); +#endif + goto exit; + } + if (tagEnd - attrPtr > 99) { +#ifdef SUPERDEBUG + LOGE("saveAttributes: attribute tag way too long"); +#endif + goto exit; + } + memcpy(tag, attrPtr, tagEnd - attrPtr); + tag[tagEnd - attrPtr] = 0; + + if (IsGpsTag(tag)) { + exifElementTable[i].GpsTag = TRUE; + exifElementTable[i].Tag = GpsTagNameToValue(tag); + ++gpsTagCount; + } else { + exifElementTable[i].GpsTag = FALSE; + exifElementTable[i].Tag = TagNameToValue(tag); + ++exifTagCount; + } + attrPtr = tagEnd + 1; + + // next get the length of the attribute value + int valueLen = atoi(attrPtr); + attrPtr = strchr(attrPtr, ' ') + 1; + if (attrPtr == 0) { +#ifdef SUPERDEBUG + LOGE("saveAttributes: couldn't find end of value len"); +#endif + goto exit; + } + exifElementTable[i].Value = malloc(valueLen + 1); + if (exifElementTable[i].Value == NULL) { + goto exit; + } + memcpy(exifElementTable[i].Value, attrPtr, valueLen); + exifElementTable[i].Value[valueLen] = 0; + exifElementTable[i].DataLength = valueLen; + + attrPtr += valueLen; + +#ifdef SUPERDEBUG + LOGE("tag %s id %d value %s data length=%d isGps=%d", tag, exifElementTable[i].Tag, + exifElementTable[i].Value, exifElementTable[i].DataLength, exifElementTable[i].GpsTag); +#endif + } + + filename = (*env)->GetStringUTFChars(env, jfilename, NULL); +#ifdef SUPERDEBUG + LOGE("Call loadAttributes() with filename is %s. Loading exif info\n", filename); +#endif + loadExifInfo(filename, TRUE); + +#ifdef SUPERDEBUG +// DumpExifMap = TRUE; + ShowTags = TRUE; + ShowImageInfo(TRUE); + LOGE("create exif 2"); +#endif + + // If the jpg file has a thumbnail, preserve it. + int thumbnailLength = ImageInfo.ThumbnailSize; + if (ImageInfo.ThumbnailOffset) { + Section_t* ExifSection = FindSection(M_EXIF); + if (ExifSection) { + uchar* thumbnailPointer = ExifSection->Data + ImageInfo.ThumbnailOffset + 8; + thumbnailData = (uchar*)malloc(ImageInfo.ThumbnailSize); + // if the malloc fails, we just won't copy the thumbnail + if (thumbnailData) { + memcpy(thumbnailData, thumbnailPointer, thumbnailLength); + } + } + } + + create_EXIF(exifElementTable, exifTagCount, gpsTagCount); + + if (thumbnailData) { + copyThumbnailData(thumbnailData, thumbnailLength); + } + +exit: +#ifdef SUPERDEBUG + LOGE("cleaning up now in saveAttributes"); +#endif + // try to clean up resources + if (attributes) { + (*env)->ReleaseStringUTFChars(env, jattributes, attributes); + } + if (filename) { + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + } + if (exifElementTable) { + // free the table + for (i = 0; i < attrCnt; i++) { + free(exifElementTable[i].Value); + } + free(exifElementTable); + } + if (thumbnailData) { + free(thumbnailData); + } +#ifdef SUPERDEBUG + LOGE("returning from saveAttributes"); +#endif + + DiscardData(); + +// Temporarily saving these commented out lines because they represent a lot of figuring out +// patterns for JNI. +// // Get link to Method "entrySet" +// jmethodID entrySetMethod = (*env)->GetMethodID(env, jclass_of_hashmap, "entrySet", "()Ljava/util/Set;"); +// +// // Invoke the "entrySet" method on the HashMap object +// jobject jobject_of_entryset = (*env)->CallObjectMethod(env, hashMap, entrySetMethod); +// +// // Get the Set Class +// jclass jclass_of_set = (*env)->FindClass(env, "java/util/Set"); +// +// if (jclass_of_set == 0) { +// printf("java/util/Set lookup failed\n"); +// return; +// } +// +// // Get link to Method "iterator" +// jmethodID iteratorMethod = (*env)->GetMethodID(env, jclass_of_set, "iterator", "()Ljava/util/Iterator;"); +// +// // Invoke the "iterator" method on the jobject_of_entryset variable of type Set +// jobject jobject_of_iterator = (*env)->CallObjectMethod(env, jobject_of_entryset, iteratorMethod); +// +// // Get the "Iterator" class +// jclass jclass_of_iterator = (*env)->FindClass(env, "java/util/Iterator"); +// +// // Get link to Method "hasNext" +// jmethodID hasNextMethod = (*env)->GetMethodID(env, jclass_of_iterator, "hasNext", "()Z"); +// +// // Invoke - Get the value hasNextMethod +// jboolean bHasNext = (*env)->CallBooleanMethod(env, jobject_of_iterator, hasNextMethod); + +// // Get link to Method "hasNext" +// jmethodID nextMethod = (*env)->GetMethodID(env, jclass_of_iterator, "next", "()Ljava/util/Map/Entry;"); +// +// jclass jclass_of_mapentry = (*env)->FindClass(env, "java/util/Map/Entry"); +// +// jmethodID getKeyMethod = (*env)->GetMethodID(env, jclass_of_mapentry, "getKey", "()Ljava/lang/Object"); +// +// jmethodID getValueMethod = (*env)->GetMethodID(env, jclass_of_mapentry, "getValue", "()Ljava/lang/Object"); +} + +static jboolean appendThumbnail(JNIEnv *env, jobject jobj, jstring jfilename, jstring jthumbnailfilename) +{ +#ifdef SUPERDEBUG + LOGE("******************************** appendThumbnail\n"); +#endif + + const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + if (filename == NULL) { + return JNI_FALSE; + } + const char* thumbnailfilename = (*env)->GetStringUTFChars(env, jthumbnailfilename, NULL); + if (thumbnailfilename == NULL) { + return JNI_FALSE; + } + #ifdef SUPERDEBUG + LOGE("*******before actual call to ReplaceThumbnail\n"); + ShowImageInfo(TRUE); + #endif + ReplaceThumbnail(thumbnailfilename); + #ifdef SUPERDEBUG + ShowImageInfo(TRUE); + #endif + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + (*env)->ReleaseStringUTFChars(env, jthumbnailfilename, thumbnailfilename); + + DiscardData(); + return JNI_TRUE; +} + +static void commitChanges(JNIEnv *env, jobject jobj, jstring jfilename) +{ +#ifdef SUPERDEBUG + LOGE("******************************** commitChanges\n"); +#endif + const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + if (filename) { + saveJPGFile(filename); + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + } +} + +static jbyteArray getThumbnail(JNIEnv *env, jobject jobj, jstring jfilename) +{ +#ifdef SUPERDEBUG + LOGE("******************************** getThumbnail\n"); +#endif + + const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + if (filename) { + loadExifInfo(filename, FALSE); + Section_t* ExifSection = FindSection(M_EXIF); + if (ExifSection == NULL || ImageInfo.ThumbnailSize == 0) { +#ifdef SUPERDEBUG + LOGE("no exif section or size == 0, so no thumbnail\n"); +#endif + goto noThumbnail; + } + uchar* thumbnailPointer = ExifSection->Data + ImageInfo.ThumbnailOffset + 8; + + jbyteArray byteArray = (*env)->NewByteArray(env, ImageInfo.ThumbnailSize); + if (byteArray == NULL) { +#ifdef SUPERDEBUG + LOGE("couldn't allocate thumbnail memory, so no thumbnail\n"); +#endif + goto noThumbnail; + } + jboolean isCopy; + jbyte* thumbnailDataPtr = (*env)->GetByteArrayElements(env, byteArray, &isCopy); + memcpy(thumbnailDataPtr, thumbnailPointer, ImageInfo.ThumbnailSize); +#ifdef SUPERDEBUG + LOGE("thumbnail size %d\n", ImageInfo.ThumbnailSize); +#endif + if (isCopy == JNI_TRUE) { + (*env)->ReleaseByteArrayElements(env, byteArray, thumbnailDataPtr, 0); + } + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + return byteArray; + } +noThumbnail: + if (filename) { + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + } + DiscardData(); + return NULL; +} + +static int attributeCount; // keep track of how many attributes we've added + +// returns new buffer length +static int addKeyValueString(char** buf, int bufLen, const char* key, const char* value) { + // Appends to buf like this: "ImageLength=4 1024" + + char valueLen[15]; + snprintf(valueLen, 15, "=%d ", (int)strlen(value)); + + // check to see if buf has enough room to append + int len = strlen(key) + strlen(valueLen) + strlen(value); + int newLen = strlen(*buf) + len; + if (newLen >= bufLen) { +#ifdef REALLOCTEST + bufLen = newLen + 5; + LOGE("reallocing to %d", bufLen); +#else + bufLen = newLen + 500; +#endif + *buf = realloc(*buf, bufLen); + if (*buf == NULL) { + return 0; + } + } + // append the new attribute and value + snprintf(*buf + strlen(*buf), bufLen, "%s%s%s", key, valueLen, value); +#ifdef SUPERDEBUG + LOGE("buf %s", *buf); +#endif + ++attributeCount; + return bufLen; +} + +// returns new buffer length +static int addKeyValueInt(char** buf, int bufLen, const char* key, int value) { + char valueStr[20]; + snprintf(valueStr, 20, "%d", value); + + return addKeyValueString(buf, bufLen, key, valueStr); +} + +// returns new buffer length +static int addKeyValueDouble(char** buf, int bufLen, const char* key, double value, const char* format) { + char valueStr[30]; + snprintf(valueStr, 30, format, value); + + return addKeyValueString(buf, bufLen, key, valueStr); +} + +static jstring getAttributes(JNIEnv *env, jobject jobj, jstring jfilename) +{ +#ifdef SUPERDEBUG + LOGE("******************************** getAttributes\n"); +#endif + const char* filename = (*env)->GetStringUTFChars(env, jfilename, NULL); + loadExifInfo(filename, FALSE); +#ifdef SUPERDEBUG + ShowImageInfo(TRUE); +#endif + (*env)->ReleaseStringUTFChars(env, jfilename, filename); + + attributeCount = 0; +#ifdef REALLOCTEST + int bufLen = 5; +#else + int bufLen = 1000; +#endif + char* buf = malloc(bufLen); + if (buf == NULL) { + return NULL; + } + *buf = 0; // start the string out at zero length + + // save a fake "hasThumbnail" tag to pass to the java ExifInterface + bufLen = addKeyValueString(&buf, bufLen, "hasThumbnail", + ImageInfo.ThumbnailOffset == 0 || ImageInfo.ThumbnailAtEnd == FALSE || ImageInfo.ThumbnailSize == 0 ? + "false" : "true"); + if (bufLen == 0) return NULL; + + if (ImageInfo.CameraMake[0]) { + bufLen = addKeyValueString(&buf, bufLen, "Make", ImageInfo.CameraMake); + if (bufLen == 0) return NULL; + } + if (ImageInfo.CameraModel[0]) { + bufLen = addKeyValueString(&buf, bufLen, "Model", ImageInfo.CameraModel); + if (bufLen == 0) return NULL; + } + if (ImageInfo.DateTime[0]) { + bufLen = addKeyValueString(&buf, bufLen, "DateTime", ImageInfo.DateTime); + if (bufLen == 0) return NULL; + } + bufLen = addKeyValueInt(&buf, bufLen, "ImageWidth", ImageInfo.Width); + if (bufLen == 0) return NULL; + + bufLen = addKeyValueInt(&buf, bufLen, "ImageLength", ImageInfo.Height); + if (bufLen == 0) return NULL; + + bufLen = addKeyValueInt(&buf, bufLen, "Orientation", ImageInfo.Orientation); + if (bufLen == 0) return NULL; + + bufLen = addKeyValueInt(&buf, bufLen, "Flash", ImageInfo.FlashUsed); + if (bufLen == 0) return NULL; + + if (ImageInfo.FocalLength){ + bufLen = addKeyValueInt(&buf, bufLen, "FocalLength", ImageInfo.FocalLength); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.DigitalZoomRatio > 1.0){ + // Digital zoom used. Shame on you! + bufLen = addKeyValueDouble(&buf, bufLen, "DigitalZoomRatio", ImageInfo.DigitalZoomRatio, "%1.3f"); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.ExposureTime){ + const char* format; + if (ImageInfo.ExposureTime < 0.010){ + format = "%6.4f"; + } else { + format = "%5.3f"; + } + + bufLen = addKeyValueDouble(&buf, bufLen, "ExposureTime", (double)ImageInfo.ExposureTime, format); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.ApertureFNumber){ + bufLen = addKeyValueDouble(&buf, bufLen, "FNumber", (double)ImageInfo.ApertureFNumber, "%3.1f"); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.Distance){ + bufLen = addKeyValueDouble(&buf, bufLen, "SubjectDistance", (double)ImageInfo.Distance, "%4.2f"); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.ISOequivalent){ + bufLen = addKeyValueInt(&buf, bufLen, "ISOSpeedRatings", ImageInfo.ISOequivalent); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.ExposureBias){ + // If exposure bias was specified, but set to zero, presumably its no bias at all, + // so only show it if its nonzero. + bufLen = addKeyValueDouble(&buf, bufLen, "ExposureBiasValue", (double)ImageInfo.ExposureBias, "%4.2f"); + if (bufLen == 0) return NULL; + } + + bufLen = addKeyValueInt(&buf, bufLen, "WhiteBalance", ImageInfo.Whitebalance); + if (bufLen == 0) return NULL; + + bufLen = addKeyValueInt(&buf, bufLen, "LightSource", ImageInfo.LightSource); + if (bufLen == 0) return NULL; + + + if (ImageInfo.MeteringMode) { + bufLen = addKeyValueInt(&buf, bufLen, "MeteringMode", ImageInfo.MeteringMode); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.ExposureProgram) { + bufLen = addKeyValueInt(&buf, bufLen, "ExposureProgram", ImageInfo.ExposureProgram); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.ExposureMode) { + bufLen = addKeyValueInt(&buf, bufLen, "ExposureMode", ImageInfo.ExposureMode); + if (bufLen == 0) return NULL; + } + + if (ImageInfo.GpsInfoPresent) { + bufLen = addKeyValueString(&buf, bufLen, "GPSLatitude", ImageInfo.GpsLatRaw); + if (bufLen == 0) return NULL; + bufLen = addKeyValueString(&buf, bufLen, "GPSLatitudeRef", ImageInfo.GpsLatRef); + if (bufLen == 0) return NULL; + bufLen = addKeyValueString(&buf, bufLen, "GPSLongitude", ImageInfo.GpsLongRaw); + if (bufLen == 0) return NULL; + bufLen = addKeyValueString(&buf, bufLen, "GPSLongitudeRef", ImageInfo.GpsLongRef); + if (bufLen == 0) return NULL; + if (ImageInfo.GpsAlt[0]) { + bufLen = addKeyValueString(&buf, bufLen, "GPSAltitude", ImageInfo.GpsAlt); + if (bufLen == 0) return NULL; + } + } + + if (ImageInfo.Comments[0]) { + bufLen = addKeyValueString(&buf, bufLen, "UserComment", ImageInfo.Comments); + if (bufLen == 0) return NULL; + } + + // put the attribute count at the beginnnig of the string + int finalBufLen = strlen(buf) + 20; + char* finalResult = malloc(finalBufLen); + if (finalResult == NULL) { + free(buf); + return NULL; + } + snprintf(finalResult, finalBufLen, "%d %s", attributeCount, buf); + int k; + for (k = 0; k < finalBufLen; k++) + if (finalResult[k] > 127) + finalResult[k] = '?'; + free(buf); + +#ifdef SUPERDEBUG + LOGE("*********Returning result \"%s\"", finalResult); +#endif + jstring result = ((*env)->NewStringUTF(env, finalResult)); + free(finalResult); + DiscardData(); + return result; +} + +static const char *classPathName = "com/android/camera/ExifInterface"; + +static JNINativeMethod methods[] = { + {"saveAttributesNative", "(Ljava/lang/String;Ljava/lang/String;)V", (void*)saveAttributes }, + {"getAttributesNative", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getAttributes }, + {"appendThumbnailNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)appendThumbnail }, + {"commitChangesNative", "(Ljava/lang/String;)V", (void*)commitChanges }, + {"getThumbnailNative", "(Ljava/lang/String;)[B", (void*)getThumbnail }, +}; + +/* + * Register several native methods for one class. + */ +static int registerNativeMethods(JNIEnv* env, const char* className, + JNINativeMethod* gMethods, int numMethods) +{ + jclass clazz; + + clazz = (*env)->FindClass(env, className); + if (clazz == NULL) { + fprintf(stderr, + "Native registration unable to find class '%s'\n", className); + return JNI_FALSE; + } + if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { + fprintf(stderr, "RegisterNatives failed for '%s'\n", className); + return JNI_FALSE; + } + + return JNI_TRUE; +} + +/* + * Register native methods for all classes we know about. + */ +static int registerNatives(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, classPathName, + methods, NELEM(methods)); +} + +/* + * Set some test stuff up. + * + * Returns the JNI version on success, -1 on failure. + */ +__attribute__ ((visibility("default"))) jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = NULL; + jint result = -1; + + if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) { + fprintf(stderr, "ERROR: GetEnv failed\n"); + goto bail; + } + assert(env != NULL); + + printf("In mgmain JNI_OnLoad\n"); + + if (registerNatives(env) < 0) { + fprintf(stderr, "ERROR: Exif native registration failed\n"); + goto bail; + } + + /* success -- return valid version number */ + result = JNI_VERSION_1_4; + +bail: + return result; +} |