From 8d8d5cf2943da78ca5f84d1729b081312c031e6a Mon Sep 17 00:00:00 2001 From: Ruben Brunk Date: Fri, 28 Jun 2013 20:02:54 -0700 Subject: Added jpeg streaming classes. - Provides streaming operations for decompressing/compressing JPEG files. - Allows pixel operations to be performed on large JPEG images without holding the entire bitmap in memory. Change-Id: I597ddf282b59d2ba6d6bca4722208121e3728f94 --- jni_jpegstream/src/jpegstream.cpp | 377 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 jni_jpegstream/src/jpegstream.cpp (limited to 'jni_jpegstream/src/jpegstream.cpp') diff --git a/jni_jpegstream/src/jpegstream.cpp b/jni_jpegstream/src/jpegstream.cpp new file mode 100644 index 000000000..3b9a6830b --- /dev/null +++ b/jni_jpegstream/src/jpegstream.cpp @@ -0,0 +1,377 @@ +/* + * 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 + +#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(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(width); + uint32_t h = static_cast(height); + int32_t q = static_cast(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(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(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(w), + static_cast(h)); + if (env->ExceptionCheck()) { + delete r_ptr; + return J_EXCEPTION; + } + } + // Store C pointer for reader + env->SetLongField(thiz, fidNumber, reinterpret_cast(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(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(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(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(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(in_buf); + int32_t in_len = static_cast(inCount); + int32_t off = static_cast(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(in_buf); + int32_t in_len = static_cast(inCount); + int32_t off = static_cast(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(&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 -- cgit v1.2.3