/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "error_codes.h" #include "jni_defines.h" #include "jpeg_writer.h" #include "jpeg_reader.h" #include "jpeg_config.h" #include "outputstream_wrapper.h" #include "inputstream_wrapper.h" #include <stdint.h> #ifdef __cplusplus extern "C" { #endif static jint OutputStream_setup(JNIEnv* env, jobject thiz, jobject out, jint width, jint height, jint format, jint quality) { // Get a reference to this object's class jclass thisClass = env->GetObjectClass(thiz); if (env->ExceptionCheck() || thisClass == NULL) { return J_EXCEPTION; } // Get field for storing C pointer jfieldID fidNumber = env->GetFieldID(thisClass, "JNIPointer", "J"); if (NULL == fidNumber || env->ExceptionCheck()) { return J_EXCEPTION; } // Check size if (width <= 0 || height <= 0) { return J_ERROR_BAD_ARGS; } Jpeg_Config::Format fmt = static_cast<Jpeg_Config::Format>(format); // Check format switch (fmt) { case Jpeg_Config::FORMAT_GRAYSCALE: case Jpeg_Config::FORMAT_RGB: case Jpeg_Config::FORMAT_RGBA: case Jpeg_Config::FORMAT_ABGR: break; default: return J_ERROR_BAD_ARGS; } uint32_t w = static_cast<uint32_t>(width); uint32_t h = static_cast<uint32_t>(height); int32_t q = static_cast<int32_t>(quality); // Clamp quality to (0, 100] q = (q > 100) ? 100 : ((q < 1) ? 1 : q); JpegWriter* w_ptr = new JpegWriter(); // Do JpegWriter setup. int32_t errorFlag = w_ptr->setup(env, out, w, h, fmt, q); if (env->ExceptionCheck() || errorFlag != J_SUCCESS) { delete w_ptr; return errorFlag; } // Store C pointer for writer env->SetLongField(thiz, fidNumber, reinterpret_cast<jlong>(w_ptr)); if (env->ExceptionCheck()) { delete w_ptr; return J_EXCEPTION; } return J_SUCCESS; } static jint InputStream_setup(JNIEnv* env, jobject thiz, jobject dimens, jobject in, jint format) { // Get a reference to this object's class jclass thisClass = env->GetObjectClass(thiz); if (env->ExceptionCheck() || thisClass == NULL) { return J_EXCEPTION; } jmethodID setMethod = NULL; // Get dimensions object setter method if (dimens != NULL) { jclass pointClass = env->GetObjectClass(dimens); if (env->ExceptionCheck() || pointClass == NULL) { return J_EXCEPTION; } setMethod = env->GetMethodID(pointClass, "set", "(II)V"); if (env->ExceptionCheck() || setMethod == NULL) { return J_EXCEPTION; } } // Get field for storing C pointer jfieldID fidNumber = env->GetFieldID(thisClass, "JNIPointer", "J"); if (NULL == fidNumber || env->ExceptionCheck()) { return J_EXCEPTION; } Jpeg_Config::Format fmt = static_cast<Jpeg_Config::Format>(format); // Check format switch (fmt) { case Jpeg_Config::FORMAT_GRAYSCALE: case Jpeg_Config::FORMAT_RGB: case Jpeg_Config::FORMAT_RGBA: case Jpeg_Config::FORMAT_ABGR: break; default: return J_ERROR_BAD_ARGS; } JpegReader* r_ptr = new JpegReader(); int32_t w = 0, h = 0; // Do JpegReader setup. int32_t errorFlag = r_ptr->setup(env, in, &w, &h, fmt); if (env->ExceptionCheck() || errorFlag != J_SUCCESS) { delete r_ptr; return errorFlag; } // Set dimensions to return if (dimens != NULL) { env->CallVoidMethod(dimens, setMethod, static_cast<jint>(w), static_cast<jint>(h)); if (env->ExceptionCheck()) { delete r_ptr; return J_EXCEPTION; } } // Store C pointer for reader env->SetLongField(thiz, fidNumber, reinterpret_cast<jlong>(r_ptr)); if (env->ExceptionCheck()) { delete r_ptr; return J_EXCEPTION; } return J_SUCCESS; } static JpegWriter* getWPtr(JNIEnv* env, jobject thiz, jfieldID* fid) { jclass thisClass = env->GetObjectClass(thiz); if (env->ExceptionCheck() || thisClass == NULL) { return NULL; } jfieldID fidNumber = env->GetFieldID(thisClass, "JNIPointer", "J"); if (NULL == fidNumber || env->ExceptionCheck()) { return NULL; } jlong ptr = env->GetLongField(thiz, fidNumber); if (env->ExceptionCheck()) { return NULL; } // Get writer C pointer out of java field. JpegWriter* w_ptr = reinterpret_cast<JpegWriter*>(ptr); if (fid != NULL) { *fid = fidNumber; } return w_ptr; } static JpegReader* getRPtr(JNIEnv* env, jobject thiz, jfieldID* fid) { jclass thisClass = env->GetObjectClass(thiz); if (env->ExceptionCheck() || thisClass == NULL) { return NULL; } jfieldID fidNumber = env->GetFieldID(thisClass, "JNIPointer", "J"); if (NULL == fidNumber || env->ExceptionCheck()) { return NULL; } jlong ptr = env->GetLongField(thiz, fidNumber); if (env->ExceptionCheck()) { return NULL; } // Get reader C pointer out of java field. JpegReader* r_ptr = reinterpret_cast<JpegReader*>(ptr); if (fid != NULL) { *fid = fidNumber; } return r_ptr; } static void OutputStream_cleanup(JNIEnv* env, jobject thiz) { jfieldID fidNumber = NULL; JpegWriter* w_ptr = getWPtr(env, thiz, &fidNumber); if (w_ptr == NULL) { return; } // Update environment w_ptr->updateEnv(env); // Destroy writer object delete w_ptr; w_ptr = NULL; // Set the java field to null env->SetLongField(thiz, fidNumber, reinterpret_cast<jlong>(w_ptr)); } static void InputStream_cleanup(JNIEnv* env, jobject thiz) { jfieldID fidNumber = NULL; JpegReader* r_ptr = getRPtr(env, thiz, &fidNumber); if (r_ptr == NULL) { return; } // Update environment r_ptr->updateEnv(env); // Destroy the reader object delete r_ptr; r_ptr = NULL; // Set the java field to null env->SetLongField(thiz, fidNumber, reinterpret_cast<jlong>(r_ptr)); } static jint OutputStream_writeInputBytes(JNIEnv* env, jobject thiz, jbyteArray inBuffer, jint offset, jint inCount) { JpegWriter* w_ptr = getWPtr(env, thiz, NULL); if (w_ptr == NULL) { return J_EXCEPTION; } // Pin input buffer jbyte* in_buf = (jbyte*) env->GetByteArrayElements(inBuffer, 0); if (env->ExceptionCheck() || in_buf == NULL) { return J_EXCEPTION; } int8_t* in_bytes = static_cast<int8_t*>(in_buf); int32_t in_len = static_cast<int32_t>(inCount); int32_t off = static_cast<int32_t>(offset); in_bytes += off; int32_t written = 0; // Update environment w_ptr->updateEnv(env); // Write out and unpin buffer. written = w_ptr->write(in_bytes, in_len); env->ReleaseByteArrayElements(inBuffer, in_buf, JNI_ABORT); return written; } static jint InputStream_readDecodedBytes(JNIEnv* env, jobject thiz, jbyteArray inBuffer, jint offset, jint inCount) { JpegReader* r_ptr = getRPtr(env, thiz, NULL); if (r_ptr == NULL) { return J_EXCEPTION; } // Pin input buffer jbyte* in_buf = (jbyte*) env->GetByteArrayElements(inBuffer, 0); if (env->ExceptionCheck() || in_buf == NULL) { return J_EXCEPTION; } int8_t* in_bytes = static_cast<int8_t*>(in_buf); int32_t in_len = static_cast<int32_t>(inCount); int32_t off = static_cast<int32_t>(offset); int32_t read = 0; // Update environment r_ptr->updateEnv(env); // Read into buffer read = r_ptr->read(in_bytes, off, in_len); // Unpin buffer if (read < 0) { env->ReleaseByteArrayElements(inBuffer, in_buf, JNI_ABORT); } else { env->ReleaseByteArrayElements(inBuffer, in_buf, JNI_COMMIT); } return read; } static jint InputStream_skipDecodedBytes(JNIEnv* env, jobject thiz, jint bytes) { if (bytes <= 0) { return J_ERROR_BAD_ARGS; } JpegReader* r_ptr = getRPtr(env, thiz, NULL); if (r_ptr == NULL) { return J_EXCEPTION; } // Update environment r_ptr->updateEnv(env); int32_t skip = 0; // Read with null buffer to skip skip = r_ptr->read(NULL, 0, bytes); return skip; } static const char *outClassPathName = "com/android/gallery3d/jpegstream/JPEGOutputStream"; static const char *inClassPathName = "com/android/gallery3d/jpegstream/JPEGInputStream"; static JNINativeMethod writeMethods[] = { { "setup", "(Ljava/io/OutputStream;IIII)I", (void*) OutputStream_setup }, { "cleanup", "()V", (void*) OutputStream_cleanup }, { "writeInputBytes", "([BII)I", (void*) OutputStream_writeInputBytes } }; static JNINativeMethod readMethods[] = { { "setup", "(Landroid/graphics/Point;Ljava/io/InputStream;I)I", (void*) InputStream_setup }, { "cleanup", "()V", (void*) InputStream_cleanup }, { "readDecodedBytes", "([BII)I", (void*) InputStream_readDecodedBytes }, { "skipDecodedBytes", "(I)I", (void*) InputStream_skipDecodedBytes } }; static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz = env->FindClass(className); if (clazz == NULL) { LOGE("Native registration unable to find class '%s'", className); return JNI_FALSE; } if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { LOGE("RegisterNatives failed for '%s'", className); return JNI_FALSE; } return JNI_TRUE; } jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { LOGE("Error: GetEnv failed in JNI_OnLoad"); return -1; } if (!registerNativeMethods(env, outClassPathName, writeMethods, sizeof(writeMethods) / sizeof(writeMethods[0]))) { LOGE("Error: could not register native methods for JPEGOutputStream"); return -1; } if (!registerNativeMethods(env, inClassPathName, readMethods, sizeof(readMethods) / sizeof(readMethods[0]))) { LOGE("Error: could not register native methods for JPEGInputStream"); return -1; } // cache method IDs for OutputStream jclass outCls = env->FindClass("java/io/OutputStream"); if (outCls == NULL) { LOGE("Unable to find class 'OutputStream'"); return -1; } jmethodID cachedWriteFun = env->GetMethodID(outCls, "write", "([BII)V"); if (cachedWriteFun == NULL) { LOGE("Unable to find write function in class 'OutputStream'"); return -1; } OutputStreamWrapper::setWriteMethodID(cachedWriteFun); // cache method IDs for InputStream jclass inCls = env->FindClass("java/io/InputStream"); if (inCls == NULL) { LOGE("Unable to find class 'InputStream'"); return -1; } jmethodID cachedReadFun = env->GetMethodID(inCls, "read", "([BII)I"); if (cachedReadFun == NULL) { LOGE("Unable to find read function in class 'InputStream'"); return -1; } jmethodID cachedSkipFun = env->GetMethodID(inCls, "skip", "(J)J"); if (cachedSkipFun == NULL) { LOGE("Unable to find skip function in class 'InputStream'"); return -1; } InputStreamWrapper::setReadSkipMethodIDs(cachedReadFun, cachedSkipFun); return JNI_VERSION_1_6; } #ifdef __cplusplus } #endif