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