| /* |
| * 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) { |
| (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 |