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 28extern "C" { 29#endif 30 31static 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 84static 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 148static 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 169static 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 190static 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 205static 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 220static 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 246static 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 276static 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 294static const char *outClassPathName = 295 "com/android/gallery3d/jpegstream/JPEGOutputStream"; 296static const char *inClassPathName = 297 "com/android/gallery3d/jpegstream/JPEGInputStream"; 298 299static 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 304static 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 311static 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 326jint 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