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
OutputStream_setup(JNIEnv * env,jobject thiz,jobject out,jint width,jint height,jint format,jint quality)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
InputStream_setup(JNIEnv * env,jobject thiz,jobject dimens,jobject in,jint format)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
getWPtr(JNIEnv * env,jobject thiz,jfieldID * fid)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
getRPtr(JNIEnv * env,jobject thiz,jfieldID * fid)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
OutputStream_cleanup(JNIEnv * env,jobject thiz)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
InputStream_cleanup(JNIEnv * env,jobject thiz)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
OutputStream_writeInputBytes(JNIEnv * env,jobject thiz,jbyteArray inBuffer,jint offset,jint inCount)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
InputStream_readDecodedBytes(JNIEnv * env,jobject thiz,jbyteArray inBuffer,jint offset,jint inCount)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
InputStream_skipDecodedBytes(JNIEnv * env,jobject thiz,jint bytes)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
registerNativeMethods(JNIEnv * env,const char * className,JNINativeMethod * gMethods,int numMethods)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
JNI_OnLoad(JavaVM * vm,void * reserved __unused)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