Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "error_codes.h"
     18 #include "jni_defines.h"
     19 #include "jpeg_writer.h"
     20 #include "jpeg_reader.h"
     21 #include "jpeg_config.h"
     22 #include "outputstream_wrapper.h"
     23 #include "inputstream_wrapper.h"
     24 
     25 #include <stdint.h>
     26 
     27 #ifdef __cplusplus
     28 extern "C" {
     29 #endif
     30 
     31 static jint OutputStream_setup(JNIEnv* env, jobject thiz, jobject out,
     32         jint width, jint height, jint format, jint quality) {
     33     // Get a reference to this object's class
     34     jclass thisClass = env->GetObjectClass(thiz);
     35     if (env->ExceptionCheck() || thisClass == NULL) {
     36         return J_EXCEPTION;
     37     }
     38     // Get field for storing C pointer
     39     jfieldID fidNumber = env->GetFieldID(thisClass, "JNIPointer", "J");
     40     if (NULL == fidNumber || env->ExceptionCheck()) {
     41         return J_EXCEPTION;
     42     }
     43 
     44     // Check size
     45     if (width <= 0 || height <= 0) {
     46         return J_ERROR_BAD_ARGS;
     47     }
     48     Jpeg_Config::Format fmt = static_cast<Jpeg_Config::Format>(format);
     49     // Check format
     50     switch (fmt) {
     51     case Jpeg_Config::FORMAT_GRAYSCALE:
     52     case Jpeg_Config::FORMAT_RGB:
     53     case Jpeg_Config::FORMAT_RGBA:
     54     case Jpeg_Config::FORMAT_ABGR:
     55         break;
     56     default:
     57         return J_ERROR_BAD_ARGS;
     58     }
     59 
     60     uint32_t w = static_cast<uint32_t>(width);
     61     uint32_t h = static_cast<uint32_t>(height);
     62     int32_t q = static_cast<int32_t>(quality);
     63     // Clamp quality to (0, 100]
     64     q = (q > 100) ? 100 : ((q < 1) ? 1 : q);
     65 
     66     JpegWriter* w_ptr = new JpegWriter();
     67 
     68     // Do JpegWriter setup.
     69     int32_t errorFlag = w_ptr->setup(env, out, w, h, fmt, q);
     70     if (env->ExceptionCheck() || errorFlag != J_SUCCESS) {
     71         delete w_ptr;
     72         return errorFlag;
     73     }
     74 
     75     // Store C pointer for writer
     76     env->SetLongField(thiz, fidNumber, reinterpret_cast<jlong>(w_ptr));
     77     if (env->ExceptionCheck()) {
     78         delete w_ptr;
     79         return J_EXCEPTION;
     80     }
     81     return J_SUCCESS;
     82 }
     83 
     84 static jint InputStream_setup(JNIEnv* env, jobject thiz, jobject dimens,
     85         jobject in, jint format) {
     86     // Get a reference to this object's class
     87     jclass thisClass = env->GetObjectClass(thiz);
     88     if (env->ExceptionCheck() || thisClass == NULL) {
     89         return J_EXCEPTION;
     90     }
     91     jmethodID setMethod = NULL;
     92 
     93     // Get dimensions object setter method
     94     if (dimens != NULL) {
     95         jclass pointClass = env->GetObjectClass(dimens);
     96         if (env->ExceptionCheck() || pointClass == NULL) {
     97             return J_EXCEPTION;
     98         }
     99         setMethod = env->GetMethodID(pointClass, "set", "(II)V");
    100         if (env->ExceptionCheck() || setMethod == NULL) {
    101             return J_EXCEPTION;
    102         }
    103     }
    104     // Get field for storing C pointer
    105     jfieldID fidNumber = env->GetFieldID(thisClass, "JNIPointer", "J");
    106     if (NULL == fidNumber || env->ExceptionCheck()) {
    107         return J_EXCEPTION;
    108     }
    109     Jpeg_Config::Format fmt = static_cast<Jpeg_Config::Format>(format);
    110     // Check format
    111     switch (fmt) {
    112     case Jpeg_Config::FORMAT_GRAYSCALE:
    113     case Jpeg_Config::FORMAT_RGB:
    114     case Jpeg_Config::FORMAT_RGBA:
    115     case Jpeg_Config::FORMAT_ABGR:
    116         break;
    117     default:
    118         return J_ERROR_BAD_ARGS;
    119     }
    120 
    121     JpegReader* r_ptr = new JpegReader();
    122     int32_t w = 0, h = 0;
    123     // Do JpegReader setup.
    124     int32_t errorFlag = r_ptr->setup(env, in, &w, &h, fmt);
    125     if (env->ExceptionCheck() || errorFlag != J_SUCCESS) {
    126         delete r_ptr;
    127         return errorFlag;
    128     }
    129 
    130     // Set dimensions to return
    131     if (dimens != NULL) {
    132         env->CallVoidMethod(dimens, setMethod, static_cast<jint>(w),
    133                 static_cast<jint>(h));
    134         if (env->ExceptionCheck()) {
    135             delete r_ptr;
    136             return J_EXCEPTION;
    137         }
    138     }
    139     // Store C pointer for reader
    140     env->SetLongField(thiz, fidNumber, reinterpret_cast<jlong>(r_ptr));
    141     if (env->ExceptionCheck()) {
    142         delete r_ptr;
    143         return J_EXCEPTION;
    144     }
    145     return J_SUCCESS;
    146 }
    147 
    148 static JpegWriter* getWPtr(JNIEnv* env, jobject thiz, jfieldID* fid) {
    149     jclass thisClass = env->GetObjectClass(thiz);
    150     if (env->ExceptionCheck() || thisClass == NULL) {
    151         return NULL;
    152     }
    153     jfieldID fidNumber = env->GetFieldID(thisClass, "JNIPointer", "J");
    154     if (NULL == fidNumber || env->ExceptionCheck()) {
    155         return NULL;
    156     }
    157     jlong ptr = env->GetLongField(thiz, fidNumber);
    158     if (env->ExceptionCheck()) {
    159         return NULL;
    160     }
    161     // Get writer C pointer out of java field.
    162     JpegWriter* w_ptr = reinterpret_cast<JpegWriter*>(ptr);
    163     if (fid != NULL) {
    164         *fid = fidNumber;
    165     }
    166     return w_ptr;
    167 }
    168 
    169 static JpegReader* getRPtr(JNIEnv* env, jobject thiz, jfieldID* fid) {
    170     jclass thisClass = env->GetObjectClass(thiz);
    171     if (env->ExceptionCheck() || thisClass == NULL) {
    172         return NULL;
    173     }
    174     jfieldID fidNumber = env->GetFieldID(thisClass, "JNIPointer", "J");
    175     if (NULL == fidNumber || env->ExceptionCheck()) {
    176         return NULL;
    177     }
    178     jlong ptr = env->GetLongField(thiz, fidNumber);
    179     if (env->ExceptionCheck()) {
    180         return NULL;
    181     }
    182     // Get reader C pointer out of java field.
    183     JpegReader* r_ptr = reinterpret_cast<JpegReader*>(ptr);
    184     if (fid != NULL) {
    185         *fid = fidNumber;
    186     }
    187     return r_ptr;
    188 }
    189 
    190 static void OutputStream_cleanup(JNIEnv* env, jobject thiz) {
    191     jfieldID fidNumber = NULL;
    192     JpegWriter* w_ptr = getWPtr(env, thiz, &fidNumber);
    193     if (w_ptr == NULL) {
    194         return;
    195     }
    196     // Update environment
    197     w_ptr->updateEnv(env);
    198     // Destroy writer object
    199     delete w_ptr;
    200     w_ptr = NULL;
    201     // Set the java field to null
    202     env->SetLongField(thiz, fidNumber, reinterpret_cast<jlong>(w_ptr));
    203 }
    204 
    205 static void InputStream_cleanup(JNIEnv* env, jobject thiz) {
    206     jfieldID fidNumber = NULL;
    207     JpegReader* r_ptr = getRPtr(env, thiz, &fidNumber);
    208     if (r_ptr == NULL) {
    209         return;
    210     }
    211     // Update environment
    212     r_ptr->updateEnv(env);
    213     // Destroy the reader object
    214     delete r_ptr;
    215     r_ptr = NULL;
    216     // Set the java field to null
    217     env->SetLongField(thiz, fidNumber, reinterpret_cast<jlong>(r_ptr));
    218 }
    219 
    220 static jint OutputStream_writeInputBytes(JNIEnv* env, jobject thiz,
    221         jbyteArray inBuffer, jint offset, jint inCount) {
    222     JpegWriter* w_ptr = getWPtr(env, thiz, NULL);
    223     if (w_ptr == NULL) {
    224         return J_EXCEPTION;
    225     }
    226     // Pin input buffer
    227     jbyte* in_buf = (jbyte*) env->GetByteArrayElements(inBuffer, 0);
    228     if (env->ExceptionCheck() || in_buf == NULL) {
    229         return J_EXCEPTION;
    230     }
    231 
    232     int8_t* in_bytes = static_cast<int8_t*>(in_buf);
    233     int32_t in_len = static_cast<int32_t>(inCount);
    234     int32_t off = static_cast<int32_t>(offset);
    235     in_bytes += off;
    236     int32_t written = 0;
    237 
    238     // Update environment
    239     w_ptr->updateEnv(env);
    240     // Write out and unpin buffer.
    241     written = w_ptr->write(in_bytes, in_len);
    242     env->ReleaseByteArrayElements(inBuffer, in_buf, JNI_ABORT);
    243     return written;
    244 }
    245 
    246 static jint InputStream_readDecodedBytes(JNIEnv* env, jobject thiz,
    247         jbyteArray inBuffer, jint offset, jint inCount) {
    248     JpegReader* r_ptr = getRPtr(env, thiz, NULL);
    249     if (r_ptr == NULL) {
    250         return J_EXCEPTION;
    251     }
    252     // Pin input buffer
    253     jbyte* in_buf = (jbyte*) env->GetByteArrayElements(inBuffer, 0);
    254     if (env->ExceptionCheck() || in_buf == NULL) {
    255         return J_EXCEPTION;
    256     }
    257     int8_t* in_bytes = static_cast<int8_t*>(in_buf);
    258     int32_t in_len = static_cast<int32_t>(inCount);
    259     int32_t off = static_cast<int32_t>(offset);
    260     int32_t read = 0;
    261 
    262     // Update environment
    263     r_ptr->updateEnv(env);
    264     // Read into buffer
    265     read = r_ptr->read(in_bytes, off, in_len);
    266 
    267     // Unpin buffer
    268     if (read < 0) {
    269         env->ReleaseByteArrayElements(inBuffer, in_buf, JNI_ABORT);
    270     } else {
    271         env->ReleaseByteArrayElements(inBuffer, in_buf, JNI_COMMIT);
    272     }
    273     return read;
    274 }
    275 
    276 static jint InputStream_skipDecodedBytes(JNIEnv* env, jobject thiz,
    277         jint bytes) {
    278     if (bytes <= 0) {
    279         return J_ERROR_BAD_ARGS;
    280     }
    281     JpegReader* r_ptr = getRPtr(env, thiz, NULL);
    282     if (r_ptr == NULL) {
    283         return J_EXCEPTION;
    284     }
    285 
    286     // Update environment
    287     r_ptr->updateEnv(env);
    288     int32_t skip = 0;
    289     // Read with null buffer to skip
    290     skip = r_ptr->read(NULL, 0, bytes);
    291     return skip;
    292 }
    293 
    294 static const char *outClassPathName =
    295         "com/android/gallery3d/jpegstream/JPEGOutputStream";
    296 static const char *inClassPathName =
    297         "com/android/gallery3d/jpegstream/JPEGInputStream";
    298 
    299 static JNINativeMethod writeMethods[] = { { "setup",
    300         "(Ljava/io/OutputStream;IIII)I", (void*) OutputStream_setup }, {
    301         "cleanup", "()V", (void*) OutputStream_cleanup }, { "writeInputBytes",
    302         "([BII)I", (void*) OutputStream_writeInputBytes } };
    303 
    304 static JNINativeMethod readMethods[] = { { "setup",
    305         "(Landroid/graphics/Point;Ljava/io/InputStream;I)I",
    306         (void*) InputStream_setup }, { "cleanup", "()V",
    307         (void*) InputStream_cleanup }, { "readDecodedBytes", "([BII)I",
    308         (void*) InputStream_readDecodedBytes }, { "skipDecodedBytes", "(I)I",
    309         (void*) InputStream_skipDecodedBytes } };
    310 
    311 static int registerNativeMethods(JNIEnv* env, const char* className,
    312         JNINativeMethod* gMethods, int numMethods) {
    313     jclass clazz;
    314     clazz = env->FindClass(className);
    315     if (clazz == NULL) {
    316         LOGE("Native registration unable to find class '%s'", className);
    317         return JNI_FALSE;
    318     }
    319     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
    320         LOGE("RegisterNatives failed for '%s'", className);
    321         return JNI_FALSE;
    322     }
    323     return JNI_TRUE;
    324 }
    325 
    326 jint JNI_OnLoad(JavaVM* vm, void* reserved __unused) {
    327     JNIEnv* env;
    328     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
    329         LOGE("Error: GetEnv failed in JNI_OnLoad");
    330         return -1;
    331     }
    332     if (!registerNativeMethods(env, outClassPathName, writeMethods,
    333             sizeof(writeMethods) / sizeof(writeMethods[0]))) {
    334         LOGE("Error: could not register native methods for JPEGOutputStream");
    335         return -1;
    336     }
    337     if (!registerNativeMethods(env, inClassPathName, readMethods,
    338             sizeof(readMethods) / sizeof(readMethods[0]))) {
    339         LOGE("Error: could not register native methods for JPEGInputStream");
    340         return -1;
    341     }
    342     // cache method IDs for OutputStream
    343     jclass outCls = env->FindClass("java/io/OutputStream");
    344     if (outCls == NULL) {
    345         LOGE("Unable to find class 'OutputStream'");
    346         return -1;
    347     }
    348     jmethodID cachedWriteFun = env->GetMethodID(outCls, "write", "([BII)V");
    349     if (cachedWriteFun == NULL) {
    350         LOGE("Unable to find write function in class 'OutputStream'");
    351         return -1;
    352     }
    353     OutputStreamWrapper::setWriteMethodID(cachedWriteFun);
    354 
    355     // cache method IDs for InputStream
    356     jclass inCls = env->FindClass("java/io/InputStream");
    357     if (inCls == NULL) {
    358         LOGE("Unable to find class 'InputStream'");
    359         return -1;
    360     }
    361     jmethodID cachedReadFun = env->GetMethodID(inCls, "read", "([BII)I");
    362     if (cachedReadFun == NULL) {
    363         LOGE("Unable to find read function in class 'InputStream'");
    364         return -1;
    365     }
    366     jmethodID cachedSkipFun = env->GetMethodID(inCls, "skip", "(J)J");
    367     if (cachedSkipFun == NULL) {
    368         LOGE("Unable to find skip function in class 'InputStream'");
    369         return -1;
    370     }
    371     InputStreamWrapper::setReadSkipMethodIDs(cachedReadFun, cachedSkipFun);
    372     return JNI_VERSION_1_6;
    373 }
    374 
    375 #ifdef __cplusplus
    376 }
    377 #endif
    378