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