Imported Upstream version 3.0.1
[platform/upstream/libjpeg-turbo.git] / turbojpeg-jni.c
1 /*
2  * Copyright (C)2011-2023 D. R. Commander.  All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * - Redistributions of source code must retain the above copyright notice,
8  *   this list of conditions and the following disclaimer.
9  * - Redistributions in binary form must reproduce the above copyright notice,
10  *   this list of conditions and the following disclaimer in the documentation
11  *   and/or other materials provided with the distribution.
12  * - Neither the name of the libjpeg-turbo Project nor the names of its
13  *   contributors may be used to endorse or promote products derived from this
14  *   software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <limits.h>
30 #include "turbojpeg.h"
31 #include "jinclude.h"
32 #include <jni.h>
33 #include "java/org_libjpegturbo_turbojpeg_TJCompressor.h"
34 #include "java/org_libjpegturbo_turbojpeg_TJDecompressor.h"
35 #include "java/org_libjpegturbo_turbojpeg_TJTransformer.h"
36 #include "java/org_libjpegturbo_turbojpeg_TJ.h"
37
38 #define BAILIF0(f) { \
39   if (!(f) || (*env)->ExceptionCheck(env)) { \
40     goto bailout; \
41   } \
42 }
43
44 #define BAILIF0NOEC(f) { \
45   if (!(f)) { \
46     goto bailout; \
47   } \
48 }
49
50 #define THROW(msg, exceptionClass) { \
51   jclass _exccls = (*env)->FindClass(env, exceptionClass); \
52   \
53   BAILIF0(_exccls); \
54   (*env)->ThrowNew(env, _exccls, msg); \
55   goto bailout; \
56 }
57
58 #define THROW_TJ() { \
59   jclass _exccls; \
60   jmethodID _excid; \
61   jobject _excobj; \
62   jstring _errstr; \
63   \
64   BAILIF0(_errstr = (*env)->NewStringUTF(env, tj3GetErrorStr(handle))); \
65   BAILIF0(_exccls = (*env)->FindClass(env, \
66     "org/libjpegturbo/turbojpeg/TJException")); \
67   BAILIF0(_excid = (*env)->GetMethodID(env, _exccls, "<init>", \
68                                        "(Ljava/lang/String;I)V")); \
69   BAILIF0(_excobj = (*env)->NewObject(env, _exccls, _excid, _errstr, \
70                                       tj3GetErrorCode(handle))); \
71   (*env)->Throw(env, _excobj); \
72   goto bailout; \
73 }
74
75 #define THROW_ARG(msg)  THROW(msg, "java/lang/IllegalArgumentException")
76
77 #define THROW_MEM() \
78   THROW("Memory allocation failure", "java/lang/OutOfMemoryError");
79
80 #define GET_HANDLE() \
81   jclass _cls = (*env)->GetObjectClass(env, obj); \
82   jfieldID _fid; \
83   \
84   BAILIF0(_cls); \
85   BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "handle", "J")); \
86   handle = (tjhandle)(size_t)(*env)->GetLongField(env, obj, _fid);
87
88 #define SAFE_RELEASE(javaArray, cArray) { \
89   if (javaArray && cArray) \
90     (*env)->ReleasePrimitiveArrayCritical(env, javaArray, (void *)cArray, 0); \
91   cArray = NULL; \
92 }
93
94 /* TurboJPEG 1.2.x: TJ::bufSize() */
95 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize
96   (JNIEnv *env, jclass cls, jint width, jint height, jint jpegSubsamp)
97 {
98   size_t retval = tj3JPEGBufSize(width, height, jpegSubsamp);
99
100   if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
101   if (retval > (size_t)INT_MAX)
102     THROW_ARG("Image is too large");
103
104 bailout:
105   return (jint)retval;
106 }
107
108 /* TurboJPEG 1.4.x: TJ::bufSizeYUV() */
109 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII
110   (JNIEnv *env, jclass cls, jint width, jint align, jint height, jint subsamp)
111 {
112   size_t retval = tj3YUVBufSize(width, align, height, subsamp);
113
114   if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
115   if (retval > (size_t)INT_MAX)
116     THROW_ARG("Image is too large");
117
118 bailout:
119   return (jint)retval;
120 }
121
122 /* TurboJPEG 1.4.x: TJ::planeSizeYUV() */
123 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII
124   (JNIEnv *env, jclass cls, jint componentID, jint width, jint stride,
125    jint height, jint subsamp)
126 {
127   size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp);
128
129   if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
130   if (retval > (size_t)INT_MAX)
131     THROW_ARG("Image is too large");
132
133 bailout:
134   return (jint)retval;
135 }
136
137 /* TurboJPEG 1.4.x: TJ::planeWidth() */
138 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeWidth__III
139   (JNIEnv *env, jclass cls, jint componentID, jint width, jint subsamp)
140 {
141   jint retval = (jint)tj3YUVPlaneWidth(componentID, width, subsamp);
142
143   if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
144
145 bailout:
146   return retval;
147 }
148
149 /* TurboJPEG 1.4.x: TJ::planeHeight() */
150 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeHeight__III
151   (JNIEnv *env, jclass cls, jint componentID, jint height, jint subsamp)
152 {
153   jint retval = (jint)tj3YUVPlaneHeight(componentID, height, subsamp);
154
155   if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
156
157 bailout:
158   return retval;
159 }
160
161 /* TurboJPEG 1.2.x: TJCompressor::init() */
162 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_init
163   (JNIEnv *env, jobject obj)
164 {
165   jclass cls;
166   jfieldID fid;
167   tjhandle handle;
168
169   if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
170     THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
171
172   BAILIF0(cls = (*env)->GetObjectClass(env, obj));
173   BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
174   (*env)->SetLongField(env, obj, fid, (size_t)handle);
175
176 bailout:
177   return;
178 }
179
180 /* TurboJPEG 3: TJCompressor::set() */
181 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_set
182   (JNIEnv *env, jobject obj, jint param, jint value)
183 {
184   tjhandle handle = 0;
185
186   GET_HANDLE();
187
188   if (tj3Set(handle, param, value) == -1)
189     THROW_TJ();
190
191 bailout:
192   return;
193 }
194
195 /* TurboJPEG 3: TJCompressor::get() */
196 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_get
197   (JNIEnv *env, jobject obj, jint param)
198 {
199   tjhandle handle = 0;
200
201   GET_HANDLE();
202
203   return tj3Get(handle, param);
204
205 bailout:
206   return -1;
207 }
208
209 static jint TJCompressor_compress
210   (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint precision,
211    jint x, jint y, jint width, jint pitch, jint height, jint pf,
212    jbyteArray dst)
213 {
214   tjhandle handle = 0;
215   size_t jpegSize = 0;
216   jsize arraySize = 0, actualPitch;
217   void *srcBuf = NULL;
218   unsigned char *jpegBuf = NULL;
219   int jpegSubsamp;
220
221   GET_HANDLE();
222
223   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
224       height < 1 || pitch < 0)
225     THROW_ARG("Invalid argument in compress*()");
226   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
227     THROW_ARG("Mismatch between Java and C API");
228
229   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
230   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
231   if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
232     THROW_ARG("Source buffer is not large enough");
233   jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
234   if (tj3Get(handle, TJPARAM_LOSSLESS) && jpegSubsamp != TJSAMP_GRAY)
235     jpegSubsamp = TJSAMP_444;
236   else if (jpegSubsamp == TJSAMP_UNKNOWN)
237     THROW_ARG("TJPARAM_SUBSAMP must be specified");
238   jpegSize = tj3JPEGBufSize(width, height, jpegSubsamp);
239   if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
240     THROW_ARG("Destination buffer is not large enough");
241
242   if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
243     THROW_TJ();
244
245   BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
246   BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
247
248   if (precision == 8) {
249     if (tj3Compress8(handle, &((unsigned char *)srcBuf)[y * actualPitch +
250                                                         x * tjPixelSize[pf]],
251                      width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
252       SAFE_RELEASE(dst, jpegBuf);
253       SAFE_RELEASE(src, srcBuf);
254       THROW_TJ();
255     }
256   } else if (precision == 12) {
257     if (tj3Compress12(handle, &((short *)srcBuf)[y * actualPitch +
258                                                  x * tjPixelSize[pf]],
259                       width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
260       SAFE_RELEASE(dst, jpegBuf);
261       SAFE_RELEASE(src, srcBuf);
262       THROW_TJ();
263     }
264   } else {
265     if (tj3Compress16(handle, &((unsigned short *)srcBuf)[y * actualPitch +
266                                                           x * tjPixelSize[pf]],
267                       width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
268       SAFE_RELEASE(dst, jpegBuf);
269       SAFE_RELEASE(src, srcBuf);
270       THROW_TJ();
271     }
272   }
273
274 bailout:
275   SAFE_RELEASE(dst, jpegBuf);
276   SAFE_RELEASE(src, srcBuf);
277   return (jint)jpegSize;
278 }
279
280 /* TurboJPEG 3: TJCompressor::compress8() byte source */
281 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3BIIIIII_3B
282   (JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width,
283    jint pitch, jint height, jint pf, jbyteArray dst)
284 {
285   return TJCompressor_compress(env, obj, src, 1, 8, x, y, width, pitch, height,
286                                pf, dst);
287 }
288
289 /* TurboJPEG 3: TJCompressor::compress12() */
290 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress12
291   (JNIEnv *env, jobject obj, jshortArray src, jint x, jint y, jint width,
292    jint pitch, jint height, jint pf, jbyteArray dst)
293 {
294   return TJCompressor_compress(env, obj, src, 1, 12, x, y, width, pitch,
295                                height, pf, dst);
296 }
297
298 /* TurboJPEG 3: TJCompressor::compress16() */
299 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress16
300   (JNIEnv *env, jobject obj, jshortArray src, jint x, jint y, jint width,
301    jint pitch, jint height, jint pf, jbyteArray dst)
302 {
303   return TJCompressor_compress(env, obj, src, 1, 16, x, y, width, pitch,
304                                height, pf, dst);
305 }
306
307 /* TurboJPEG 3: TJCompressor::compress8() int source */
308 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3IIIIIII_3B
309   (JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width,
310    jint stride, jint height, jint pf, jbyteArray dst)
311 {
312   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
313     THROW_ARG("Invalid argument in compress8()");
314   if (tjPixelSize[pf] != sizeof(jint))
315     THROW_ARG("Pixel format must be 32-bit when compressing from an integer buffer.");
316
317   return TJCompressor_compress(env, obj, src, sizeof(jint), 8, x, y, width,
318                                stride * sizeof(jint), height, pf, dst);
319
320 bailout:
321   return 0;
322 }
323
324 /* TurboJPEG 3: TJCompressor::compressFromYUV8() */
325 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV8
326   (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
327    jint width, jintArray jSrcStrides, jint height, jbyteArray dst)
328 {
329   tjhandle handle = 0;
330   size_t jpegSize = 0;
331   jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL };
332   const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL };
333   const unsigned char *srcPlanes[3] = { NULL, NULL, NULL };
334   jint srcOffsetsTmp[3] = { 0, 0, 0 }, srcStridesTmp[3] = { 0, 0, 0 };
335   int srcOffsets[3] = { 0, 0, 0 }, srcStrides[3] = { 0, 0, 0 };
336   unsigned char *jpegBuf = NULL;
337   int nc = 0, i, subsamp;
338
339   GET_HANDLE();
340
341   if (org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
342     THROW_ARG("Mismatch between Java and C API");
343
344   if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
345     THROW_ARG("TJPARAM_SUBSAMP must be specified");
346   nc = subsamp == TJSAMP_GRAY ? 1 : 3;
347   if ((*env)->GetArrayLength(env, srcobjs) < nc)
348     THROW_ARG("Planes array is too small for the subsampling type");
349   if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
350     THROW_ARG("Offsets array is too small for the subsampling type");
351   if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
352     THROW_ARG("Strides array is too small for the subsampling type");
353
354   jpegSize = tj3JPEGBufSize(width, height, subsamp);
355   if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
356     THROW_ARG("Destination buffer is not large enough");
357
358   if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
359     THROW_TJ();
360
361   (*env)->GetIntArrayRegion(env, jSrcOffsets, 0, nc, srcOffsetsTmp);
362   if ((*env)->ExceptionCheck(env)) goto bailout;
363   for (i = 0; i < 3; i++)
364     srcOffsets[i] = srcOffsetsTmp[i];
365
366   (*env)->GetIntArrayRegion(env, jSrcStrides, 0, nc, srcStridesTmp);
367   if ((*env)->ExceptionCheck(env)) goto bailout;
368   for (i = 0; i < 3; i++)
369     srcStrides[i] = srcStridesTmp[i];
370
371   for (i = 0; i < nc; i++) {
372     size_t planeSize = tj3YUVPlaneSize(i, width, srcStrides[i], height,
373                                        subsamp);
374     int pw = tj3YUVPlaneWidth(i, width, subsamp);
375
376     if (planeSize == 0 || pw == 0)
377       THROW_ARG(tj3GetErrorStr(NULL));
378
379     if (planeSize > (size_t)INT_MAX)
380       THROW_ARG("Source plane is too large");
381     if (srcOffsets[i] < 0)
382       THROW_ARG("Invalid argument in compressFromYUV8()");
383     if (srcStrides[i] < 0 && srcOffsets[i] - (int)planeSize + pw < 0)
384       THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
385
386     BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
387     if ((*env)->GetArrayLength(env, jSrcPlanes[i]) <
388         srcOffsets[i] + (int)planeSize)
389       THROW_ARG("Source plane is not large enough");
390   }
391   for (i = 0; i < nc; i++) {
392     BAILIF0NOEC(srcPlanesTmp[i] =
393                 (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
394     srcPlanes[i] = &srcPlanesTmp[i][srcOffsets[i]];
395   }
396   BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
397
398   if (tj3CompressFromYUVPlanes8(handle, srcPlanes, width, srcStrides, height,
399                                 &jpegBuf, &jpegSize) == -1) {
400     SAFE_RELEASE(dst, jpegBuf);
401     for (i = 0; i < nc; i++)
402       SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
403     THROW_TJ();
404   }
405
406 bailout:
407   SAFE_RELEASE(dst, jpegBuf);
408   for (i = 0; i < nc; i++)
409     SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
410   return (jint)jpegSize;
411 }
412
413 static void TJCompressor_encodeYUV8
414   (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint x, jint y,
415    jint width, jint pitch, jint height, jint pf, jobjectArray dstobjs,
416    jintArray jDstOffsets, jintArray jDstStrides)
417 {
418   tjhandle handle = 0;
419   jsize arraySize = 0, actualPitch;
420   unsigned char *srcBuf = NULL;
421   jbyteArray jDstPlanes[3] = { NULL, NULL, NULL };
422   unsigned char *dstPlanesTmp[3] = { NULL, NULL, NULL };
423   unsigned char *dstPlanes[3] = { NULL, NULL, NULL };
424   jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 };
425   int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 };
426   int nc = 0, i, subsamp;
427
428   GET_HANDLE();
429
430   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
431       height < 1 || pitch < 0)
432     THROW_ARG("Invalid argument in encodeYUV8()");
433   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
434       org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
435     THROW_ARG("Mismatch between Java and C API");
436
437   if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
438     THROW_ARG("TJPARAM_SUBSAMP must be specified");
439   nc = subsamp == TJSAMP_GRAY ? 1 : 3;
440   if ((*env)->GetArrayLength(env, dstobjs) < nc)
441     THROW_ARG("Planes array is too small for the subsampling type");
442   if ((*env)->GetArrayLength(env, jDstOffsets) < nc)
443     THROW_ARG("Offsets array is too small for the subsampling type");
444   if ((*env)->GetArrayLength(env, jDstStrides) < nc)
445     THROW_ARG("Strides array is too small for the subsampling type");
446
447   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
448   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
449   if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
450     THROW_ARG("Source buffer is not large enough");
451
452   (*env)->GetIntArrayRegion(env, jDstOffsets, 0, nc, dstOffsetsTmp);
453   if ((*env)->ExceptionCheck(env)) goto bailout;
454   for (i = 0; i < 3; i++)
455     dstOffsets[i] = dstOffsetsTmp[i];
456
457   (*env)->GetIntArrayRegion(env, jDstStrides, 0, nc, dstStridesTmp);
458   if ((*env)->ExceptionCheck(env)) goto bailout;
459   for (i = 0; i < 3; i++)
460     dstStrides[i] = dstStridesTmp[i];
461
462   for (i = 0; i < nc; i++) {
463     size_t planeSize = tj3YUVPlaneSize(i, width, dstStrides[i], height,
464                                        subsamp);
465     int pw = tj3YUVPlaneWidth(i, width, subsamp);
466
467     if (planeSize == 0 || pw == 0)
468       THROW_ARG(tj3GetErrorStr(NULL));
469
470     if (planeSize > (size_t)INT_MAX)
471       THROW_ARG("Destination plane is too large");
472     if (dstOffsets[i] < 0)
473       THROW_ARG("Invalid argument in encodeYUV8()");
474     if (dstStrides[i] < 0 && dstOffsets[i] - (int)planeSize + pw < 0)
475       THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
476
477     BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
478     if ((*env)->GetArrayLength(env, jDstPlanes[i]) <
479         dstOffsets[i] + (int)planeSize)
480       THROW_ARG("Destination plane is not large enough");
481   }
482   for (i = 0; i < nc; i++) {
483     BAILIF0NOEC(dstPlanesTmp[i] =
484                 (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
485     dstPlanes[i] = &dstPlanesTmp[i][dstOffsets[i]];
486   }
487   BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
488
489   if (tj3EncodeYUVPlanes8(handle,
490                           &srcBuf[y * actualPitch + x * tjPixelSize[pf]],
491                           width, pitch, height, pf, dstPlanes,
492                           dstStrides) == -1) {
493     SAFE_RELEASE(src, srcBuf);
494     for (i = 0; i < nc; i++)
495       SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
496     THROW_TJ();
497   }
498
499 bailout:
500   SAFE_RELEASE(src, srcBuf);
501   for (i = 0; i < nc; i++)
502     SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
503 }
504
505 /* TurboJPEG 3: TJCompressor::encodeYUV8() byte source */
506 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3BIIIIII_3_3B_3I_3I
507   (JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width,
508    jint pitch, jint height, jint pf, jobjectArray dstobjs,
509    jintArray jDstOffsets, jintArray jDstStrides)
510 {
511   TJCompressor_encodeYUV8(env, obj, src, 1, x, y, width, pitch, height, pf,
512                           dstobjs, jDstOffsets, jDstStrides);
513 }
514
515 /* TurboJPEG 3: TJCompressor::encodeYUV8() int source */
516 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3IIIIIII_3_3B_3I_3I
517   (JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width,
518    jint stride, jint height, jint pf, jobjectArray dstobjs,
519    jintArray jDstOffsets, jintArray jDstStrides)
520 {
521   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
522     THROW_ARG("Invalid argument in encodeYUV8()");
523   if (tjPixelSize[pf] != sizeof(jint))
524     THROW_ARG("Pixel format must be 32-bit when encoding from an integer buffer.");
525
526   TJCompressor_encodeYUV8(env, obj, src, sizeof(jint), x, y, width,
527                           stride * sizeof(jint), height, pf, dstobjs,
528                           jDstOffsets, jDstStrides);
529
530 bailout:
531   return;
532 }
533
534 /* TurboJPEG 1.2.x: TJCompressor::destroy() */
535 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy
536   (JNIEnv *env, jobject obj)
537 {
538   tjhandle handle = 0;
539
540   GET_HANDLE();
541
542   tj3Destroy(handle);
543   (*env)->SetLongField(env, obj, _fid, 0);
544
545 bailout:
546   return;
547 }
548
549 /* TurboJPEG 1.2.x: TJDecompressor::init() */
550 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_init
551   (JNIEnv *env, jobject obj)
552 {
553   jclass cls;
554   jfieldID fid;
555   tjhandle handle;
556
557   if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL)
558     THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
559
560   BAILIF0(cls = (*env)->GetObjectClass(env, obj));
561   BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
562   (*env)->SetLongField(env, obj, fid, (size_t)handle);
563
564 bailout:
565   return;
566 }
567
568 /* TurboJPEG 3: TJDecompressor::set() */
569 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_set
570   (JNIEnv *env, jobject obj, jint param, jint value)
571 {
572   Java_org_libjpegturbo_turbojpeg_TJCompressor_set(env, obj, param, value);
573 }
574
575 /* TurboJPEG 3: TJDecompressor::get() */
576 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_get
577   (JNIEnv *env, jobject obj, jint param)
578 {
579   return Java_org_libjpegturbo_turbojpeg_TJCompressor_get(env, obj, param);
580 }
581
582 /* TurboJPEG 1.2.x: TJDecompressor::getScalingFactors() */
583 JNIEXPORT jobjectArray JNICALL Java_org_libjpegturbo_turbojpeg_TJ_getScalingFactors
584   (JNIEnv *env, jclass cls)
585 {
586   jclass sfcls = NULL;
587   jfieldID fid = 0;
588   tjscalingfactor *sf = NULL;
589   int n = 0, i;
590   jobject sfobj = NULL;
591   jobjectArray sfjava = NULL;
592
593   if ((sf = tj3GetScalingFactors(&n)) == NULL || n == 0)
594     THROW_ARG(tj3GetErrorStr(NULL));
595
596   BAILIF0(sfcls = (*env)->FindClass(env,
597     "org/libjpegturbo/turbojpeg/TJScalingFactor"));
598   BAILIF0(sfjava = (jobjectArray)(*env)->NewObjectArray(env, n, sfcls, 0));
599
600   for (i = 0; i < n; i++) {
601     BAILIF0(sfobj = (*env)->AllocObject(env, sfcls));
602     BAILIF0(fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
603     (*env)->SetIntField(env, sfobj, fid, sf[i].num);
604     BAILIF0(fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
605     (*env)->SetIntField(env, sfobj, fid, sf[i].denom);
606     (*env)->SetObjectArrayElement(env, sfjava, i, sfobj);
607   }
608
609 bailout:
610   return sfjava;
611 }
612
613 /* TurboJPEG 1.2.x: TJDecompressor::decompressHeader() */
614 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressHeader
615   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize)
616 {
617   tjhandle handle = 0;
618   unsigned char *jpegBuf = NULL;
619
620   GET_HANDLE();
621
622   if ((*env)->GetArrayLength(env, src) < jpegSize)
623     THROW_ARG("Source buffer is not large enough");
624
625   BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
626
627   if (tj3DecompressHeader(handle, jpegBuf, (size_t)jpegSize) == -1) {
628     SAFE_RELEASE(src, jpegBuf);
629     THROW_TJ();
630   }
631
632 bailout:
633   SAFE_RELEASE(src, jpegBuf);
634 }
635
636 /* TurboJPEG 3: TJDecompressor::setCroppingRegion() */
637 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion
638   (JNIEnv *env, jobject obj)
639 {
640   tjhandle handle = 0;
641   jclass sfcls, crcls;
642   jobject sfobj, crobj;
643   tjregion croppingRegion;
644   tjscalingfactor scalingFactor;
645
646   GET_HANDLE();
647
648   BAILIF0(sfcls = (*env)->FindClass(env,
649     "org/libjpegturbo/turbojpeg/TJScalingFactor"));
650   BAILIF0(_fid =
651           (*env)->GetFieldID(env, _cls, "scalingFactor",
652                              "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
653   BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
654   BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
655   scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
656   BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
657   scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
658
659   if (tj3SetScalingFactor(handle, scalingFactor) == -1)
660     THROW_TJ();
661
662   BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle"));
663   BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion",
664                                     "Ljava/awt/Rectangle;"));
665   BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid));
666   BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I"));
667   croppingRegion.x = (*env)->GetIntField(env, crobj, _fid);
668   BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I"));
669   croppingRegion.y = (*env)->GetIntField(env, crobj, _fid);
670   BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I"));
671   croppingRegion.w = (*env)->GetIntField(env, crobj, _fid);
672   BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I"));
673   croppingRegion.h = (*env)->GetIntField(env, crobj, _fid);
674
675   if (tj3SetCroppingRegion(handle, croppingRegion) == -1)
676     THROW_TJ();
677
678 bailout:
679   return;
680 }
681
682 static void TJDecompressor_decompress
683   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jarray dst,
684    jint dstElementSize, int precision, jint x, jint y, jint pitch, jint pf)
685 {
686   tjhandle handle = 0;
687   jsize arraySize = 0, actualPitch;
688   unsigned char *jpegBuf = NULL;
689   void *dstBuf = NULL;
690   jclass sfcls, crcls;
691   jobject sfobj, crobj;
692   tjscalingfactor scalingFactor;
693   tjregion cr;
694   int jpegWidth, jpegHeight, scaledWidth, scaledHeight;
695
696   GET_HANDLE();
697
698   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
699     THROW_ARG("Invalid argument in decompress*()");
700   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
701     THROW_ARG("Mismatch between Java and C API");
702
703   if ((*env)->GetArrayLength(env, src) < jpegSize)
704     THROW_ARG("Source buffer is not large enough");
705   if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
706     THROW_ARG("JPEG header has not yet been read");
707   if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
708     THROW_ARG("JPEG header has not yet been read");
709
710   BAILIF0(sfcls = (*env)->FindClass(env,
711     "org/libjpegturbo/turbojpeg/TJScalingFactor"));
712   BAILIF0(_fid =
713           (*env)->GetFieldID(env, _cls, "scalingFactor",
714                              "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
715   BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
716   BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
717   scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
718   BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
719   scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
720
721   if (tj3SetScalingFactor(handle, scalingFactor) == -1)
722     THROW_TJ();
723   scaledWidth = TJSCALED(jpegWidth, scalingFactor);
724   scaledHeight = TJSCALED(jpegHeight, scalingFactor);
725
726   BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle"));
727   BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion",
728                                     "Ljava/awt/Rectangle;"));
729   BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid));
730   BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I"));
731   cr.x = (*env)->GetIntField(env, crobj, _fid);
732   BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I"));
733   cr.y = (*env)->GetIntField(env, crobj, _fid);
734   BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I"));
735   cr.w = (*env)->GetIntField(env, crobj, _fid);
736   BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I"));
737   cr.h = (*env)->GetIntField(env, crobj, _fid);
738   if (cr.x != 0 || cr.y != 0 || cr.w != 0 || cr.h != 0) {
739     scaledWidth = cr.w ? cr.w : scaledWidth - cr.x;
740     scaledHeight = cr.h ? cr.h : scaledHeight - cr.y;
741   }
742
743   actualPitch = (pitch == 0) ? scaledWidth * tjPixelSize[pf] : pitch;
744   arraySize = (y + scaledHeight - 1) * actualPitch +
745               (x + scaledWidth) * tjPixelSize[pf];
746   if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
747     THROW_ARG("Destination buffer is not large enough");
748
749   BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
750   BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
751
752   if (precision == 8) {
753     if (tj3Decompress8(handle, jpegBuf, (size_t)jpegSize,
754                        &((unsigned char *)dstBuf)[y * actualPitch +
755                                                   x * tjPixelSize[pf]],
756                        pitch, pf) == -1) {
757       SAFE_RELEASE(dst, dstBuf);
758       SAFE_RELEASE(src, jpegBuf);
759       THROW_TJ();
760     }
761   } else if (precision == 12) {
762     if (tj3Decompress12(handle, jpegBuf, (size_t)jpegSize,
763                         &((short *)dstBuf)[y * actualPitch +
764                                            x * tjPixelSize[pf]],
765                         pitch, pf) == -1) {
766       SAFE_RELEASE(dst, dstBuf);
767       SAFE_RELEASE(src, jpegBuf);
768       THROW_TJ();
769     }
770   } else {
771     if (tj3Decompress16(handle, jpegBuf, (size_t)jpegSize,
772                         &((unsigned short *)dstBuf)[y * actualPitch +
773                                                     x * tjPixelSize[pf]],
774                         pitch, pf) == -1) {
775       SAFE_RELEASE(dst, dstBuf);
776       SAFE_RELEASE(src, jpegBuf);
777       THROW_TJ();
778     }
779   }
780
781 bailout:
782   SAFE_RELEASE(dst, dstBuf);
783   SAFE_RELEASE(src, jpegBuf);
784 }
785
786 /* TurboJPEG 3: TJDecompressor::decompress8() byte destination */
787 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3BIIII
788   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst,
789    jint x, jint y, jint pitch, jint pf)
790 {
791   TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 8, x, y, pitch,
792                             pf);
793 }
794
795 /* TurboJPEG 3: TJDecompressor::decompress12() */
796 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress12
797   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jshortArray dst,
798    jint x, jint y, jint pitch, jint pf)
799 {
800   TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 12, x, y, pitch,
801                             pf);
802 }
803
804 /* TurboJPEG 3: TJDecompressor::decompress16() */
805 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress16
806   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jshortArray dst,
807    jint x, jint y, jint pitch, jint pf)
808 {
809   TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 16, x, y, pitch,
810                             pf);
811 }
812
813 /* TurboJPEG 3: TJDecompressor::decompress8() int destination */
814 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3IIIII
815   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jintArray dst,
816    jint x, jint y, jint stride, jint pf)
817 {
818   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
819     THROW_ARG("Invalid argument in decompress8()");
820   if (tjPixelSize[pf] != sizeof(jint))
821     THROW_ARG("Pixel format must be 32-bit when decompressing to an integer buffer.");
822
823   TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), 8, x,
824                             y, stride * sizeof(jint), pf);
825
826 bailout:
827   return;
828 }
829
830 /* TurboJPEG 3: TJDecompressor::decompressToYUV8() */
831 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV8
832   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize,
833    jobjectArray dstobjs, jintArray jDstOffsets, jintArray jDstStrides)
834 {
835   tjhandle handle = 0;
836   unsigned char *jpegBuf = NULL;
837   jbyteArray jDstPlanes[3] = { NULL, NULL, NULL };
838   unsigned char *dstPlanesTmp[3] = { NULL, NULL, NULL };
839   unsigned char *dstPlanes[3] = { NULL, NULL, NULL };
840   jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 };
841   int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 };
842   jclass sfcls;
843   jobject sfobj;
844   int jpegSubsamp, jpegWidth = 0, jpegHeight = 0;
845   int nc = 0, i, scaledWidth, scaledHeight;
846   tjscalingfactor scalingFactor;
847
848   GET_HANDLE();
849
850   if ((*env)->GetArrayLength(env, src) < jpegSize)
851     THROW_ARG("Source buffer is not large enough");
852   if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
853     THROW_ARG("JPEG header has not yet been read");
854   if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
855     THROW_ARG("JPEG header has not yet been read");
856
857   BAILIF0(sfcls = (*env)->FindClass(env,
858     "org/libjpegturbo/turbojpeg/TJScalingFactor"));
859   BAILIF0(_fid =
860           (*env)->GetFieldID(env, _cls, "scalingFactor",
861                              "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
862   BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
863   BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
864   scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
865   BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
866   scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
867
868   if (tj3SetScalingFactor(handle, scalingFactor) == -1)
869     THROW_TJ();
870   scaledWidth = TJSCALED(jpegWidth, scalingFactor);
871   scaledHeight = TJSCALED(jpegHeight, scalingFactor);
872
873   if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
874     THROW_ARG("TJPARAM_SUBSAMP must be specified");
875   nc = jpegSubsamp == TJSAMP_GRAY ? 1 : 3;
876
877   (*env)->GetIntArrayRegion(env, jDstOffsets, 0, nc, dstOffsetsTmp);
878   if ((*env)->ExceptionCheck(env)) goto bailout;
879   for (i = 0; i < 3; i++)
880     dstOffsets[i] = dstOffsetsTmp[i];
881
882   (*env)->GetIntArrayRegion(env, jDstStrides, 0, nc, dstStridesTmp);
883   if ((*env)->ExceptionCheck(env)) goto bailout;
884   for (i = 0; i < 3; i++)
885     dstStrides[i] = dstStridesTmp[i];
886
887   for (i = 0; i < nc; i++) {
888     size_t planeSize = tj3YUVPlaneSize(i, scaledWidth, dstStrides[i],
889                                        scaledHeight, jpegSubsamp);
890     int pw = tj3YUVPlaneWidth(i, scaledWidth, jpegSubsamp);
891
892     if (planeSize == 0 || pw == 0)
893       THROW_ARG(tj3GetErrorStr(NULL));
894
895     if (planeSize > (size_t)INT_MAX)
896       THROW_ARG("Destination plane is too large");
897     if (dstOffsets[i] < 0)
898       THROW_ARG("Invalid argument in decompressToYUV8()");
899     if (dstStrides[i] < 0 && dstOffsets[i] - (int)planeSize + pw < 0)
900       THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
901
902     BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
903     if ((*env)->GetArrayLength(env, jDstPlanes[i]) <
904         dstOffsets[i] + (int)planeSize)
905       THROW_ARG("Destination plane is not large enough");
906   }
907   for (i = 0; i < nc; i++) {
908     BAILIF0NOEC(dstPlanesTmp[i] =
909                 (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
910     dstPlanes[i] = &dstPlanesTmp[i][dstOffsets[i]];
911   }
912   BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
913
914   if (tj3DecompressToYUVPlanes8(handle, jpegBuf, (size_t)jpegSize, dstPlanes,
915                                 dstStrides) == -1) {
916     SAFE_RELEASE(src, jpegBuf);
917     for (i = 0; i < nc; i++)
918       SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
919     THROW_TJ();
920   }
921
922 bailout:
923   SAFE_RELEASE(src, jpegBuf);
924   for (i = 0; i < nc; i++)
925     SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
926 }
927
928 static void TJDecompressor_decodeYUV8
929   (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
930    jintArray jSrcStrides, jarray dst, jint dstElementSize, jint x, jint y,
931    jint width, jint pitch, jint height, jint pf)
932 {
933   tjhandle handle = 0;
934   jsize arraySize = 0, actualPitch;
935   jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL };
936   const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL };
937   const unsigned char *srcPlanes[3] = { NULL, NULL, NULL };
938   jint srcOffsetsTmp[3] = { 0, 0, 0 }, srcStridesTmp[3] = { 0, 0, 0 };
939   int srcOffsets[3] = { 0, 0, 0 }, srcStrides[3] = { 0, 0, 0 };
940   unsigned char *dstBuf = NULL;
941   int nc = 0, i, subsamp;
942
943   GET_HANDLE();
944
945   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
946     THROW_ARG("Invalid argument in decodeYUV8()");
947   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
948       org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
949     THROW_ARG("Mismatch between Java and C API");
950
951   if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
952     THROW_ARG("TJPARAM_SUBSAMP must be specified");
953   nc = subsamp == TJSAMP_GRAY ? 1 : 3;
954   if ((*env)->GetArrayLength(env, srcobjs) < nc)
955     THROW_ARG("Planes array is too small for the subsampling type");
956   if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
957     THROW_ARG("Offsets array is too small for the subsampling type");
958   if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
959     THROW_ARG("Strides array is too small for the subsampling type");
960
961   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
962   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
963   if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
964     THROW_ARG("Destination buffer is not large enough");
965
966   (*env)->GetIntArrayRegion(env, jSrcOffsets, 0, nc, srcOffsetsTmp);
967   if ((*env)->ExceptionCheck(env)) goto bailout;
968   for (i = 0; i < 3; i++)
969     srcOffsets[i] = srcOffsetsTmp[i];
970
971   (*env)->GetIntArrayRegion(env, jSrcStrides, 0, nc, srcStridesTmp);
972   if ((*env)->ExceptionCheck(env)) goto bailout;
973   for (i = 0; i < 3; i++)
974     srcStrides[i] = srcStridesTmp[i];
975
976   for (i = 0; i < nc; i++) {
977     size_t planeSize = tj3YUVPlaneSize(i, width, srcStrides[i], height,
978                                        subsamp);
979     int pw = tj3YUVPlaneWidth(i, width, subsamp);
980
981     if (planeSize == 0 || pw == 0)
982       THROW_ARG(tj3GetErrorStr(NULL));
983
984     if (planeSize > (size_t)INT_MAX)
985       THROW_ARG("Source plane is too large");
986     if (srcOffsets[i] < 0)
987       THROW_ARG("Invalid argument in decodeYUV8()");
988     if (srcStrides[i] < 0 && srcOffsets[i] - (int)planeSize + pw < 0)
989       THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
990
991     BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
992     if ((*env)->GetArrayLength(env, jSrcPlanes[i]) <
993         srcOffsets[i] + (int)planeSize)
994       THROW_ARG("Source plane is not large enough");
995   }
996   for (i = 0; i < nc; i++) {
997     BAILIF0NOEC(srcPlanesTmp[i] =
998                 (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
999     srcPlanes[i] = &srcPlanesTmp[i][srcOffsets[i]];
1000   }
1001   BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
1002
1003   if (tj3DecodeYUVPlanes8(handle, srcPlanes, srcStrides,
1004                           &dstBuf[y * actualPitch + x * tjPixelSize[pf]],
1005                           width, pitch, height, pf) == -1) {
1006     SAFE_RELEASE(dst, dstBuf);
1007     for (i = 0; i < nc; i++)
1008       SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
1009     THROW_TJ();
1010   }
1011
1012 bailout:
1013   SAFE_RELEASE(dst, dstBuf);
1014   for (i = 0; i < nc; i++)
1015     SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
1016 }
1017
1018 /* TurboJPEG 3: TJDecompressor::decodeYUV8() byte destination */
1019 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3BIIIIII
1020   (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
1021    jintArray jSrcStrides, jbyteArray dst, jint x, jint y, jint width,
1022    jint pitch, jint height, jint pf)
1023 {
1024   TJDecompressor_decodeYUV8(env, obj, srcobjs, jSrcOffsets, jSrcStrides, dst,
1025                             1, x, y, width, pitch, height, pf);
1026 }
1027
1028 /* TurboJPEG 3: TJDecompressor::decodeYUV8() int destination */
1029 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3IIIIIII
1030   (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
1031    jintArray jSrcStrides, jintArray dst, jint x, jint y, jint width,
1032    jint stride, jint height, jint pf)
1033 {
1034   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
1035     THROW_ARG("Invalid argument in decodeYUV8()");
1036   if (tjPixelSize[pf] != sizeof(jint))
1037     THROW_ARG("Pixel format must be 32-bit when decoding to an integer buffer.");
1038
1039   TJDecompressor_decodeYUV8(env, obj, srcobjs, jSrcOffsets, jSrcStrides, dst,
1040                             sizeof(jint), x, y, width, stride * sizeof(jint),
1041                             height, pf);
1042
1043 bailout:
1044   return;
1045 }
1046
1047 /* TurboJPEG 1.2.x: TJTransformer::init() */
1048 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_init
1049   (JNIEnv *env, jobject obj)
1050 {
1051   jclass cls;
1052   jfieldID fid;
1053   tjhandle handle;
1054
1055   if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL)
1056     THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
1057
1058   BAILIF0(cls = (*env)->GetObjectClass(env, obj));
1059   BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
1060   (*env)->SetLongField(env, obj, fid, (size_t)handle);
1061
1062 bailout:
1063   return;
1064 }
1065
1066 typedef struct _JNICustomFilterParams {
1067   JNIEnv *env;
1068   jobject tobj;
1069   jobject cfobj;
1070 } JNICustomFilterParams;
1071
1072 static int JNICustomFilter(short *coeffs, tjregion arrayRegion,
1073                            tjregion planeRegion, int componentIndex,
1074                            int transformIndex, tjtransform *transform)
1075 {
1076   JNICustomFilterParams *params = (JNICustomFilterParams *)transform->data;
1077   JNIEnv *env = params->env;
1078   jobject tobj = params->tobj, cfobj = params->cfobj;
1079   jobject arrayRegionObj, planeRegionObj, bufobj, borobj;
1080   jclass cls;
1081   jmethodID mid;
1082   jfieldID fid;
1083
1084   BAILIF0(bufobj = (*env)->NewDirectByteBuffer(env, coeffs,
1085     sizeof(short) * arrayRegion.w * arrayRegion.h));
1086   BAILIF0(cls = (*env)->FindClass(env, "java/nio/ByteOrder"));
1087   BAILIF0(mid = (*env)->GetStaticMethodID(env, cls, "nativeOrder",
1088                                           "()Ljava/nio/ByteOrder;"));
1089   BAILIF0(borobj = (*env)->CallStaticObjectMethod(env, cls, mid));
1090   BAILIF0(cls = (*env)->GetObjectClass(env, bufobj));
1091   BAILIF0(mid = (*env)->GetMethodID(env, cls, "order",
1092     "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"));
1093   (*env)->CallObjectMethod(env, bufobj, mid, borobj);
1094   BAILIF0(mid = (*env)->GetMethodID(env, cls, "asShortBuffer",
1095                                     "()Ljava/nio/ShortBuffer;"));
1096   BAILIF0(bufobj = (*env)->CallObjectMethod(env, bufobj, mid));
1097
1098   BAILIF0(cls = (*env)->FindClass(env, "java/awt/Rectangle"));
1099   BAILIF0(arrayRegionObj = (*env)->AllocObject(env, cls));
1100   BAILIF0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
1101   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.x);
1102   BAILIF0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
1103   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.y);
1104   BAILIF0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
1105   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.w);
1106   BAILIF0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
1107   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.h);
1108
1109   BAILIF0(planeRegionObj = (*env)->AllocObject(env, cls));
1110   BAILIF0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
1111   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.x);
1112   BAILIF0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
1113   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.y);
1114   BAILIF0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
1115   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.w);
1116   BAILIF0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
1117   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.h);
1118
1119   BAILIF0(cls = (*env)->GetObjectClass(env, cfobj));
1120   BAILIF0(mid = (*env)->GetMethodID(env, cls, "customFilter",
1121     "(Ljava/nio/ShortBuffer;Ljava/awt/Rectangle;Ljava/awt/Rectangle;IILorg/libjpegturbo/turbojpeg/TJTransform;)V"));
1122   (*env)->CallVoidMethod(env, cfobj, mid, bufobj, arrayRegionObj,
1123                          planeRegionObj, componentIndex, transformIndex, tobj);
1124
1125   return 0;
1126
1127 bailout:
1128   return -1;
1129 }
1130
1131 /* TurboJPEG 1.2.x: TJTransformer::transform() */
1132 JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transform
1133   (JNIEnv *env, jobject obj, jbyteArray jsrcBuf, jint jpegSize,
1134    jobjectArray dstobjs, jobjectArray tobjs)
1135 {
1136   tjhandle handle = 0;
1137   unsigned char *jpegBuf = NULL, **dstBufs = NULL;
1138   jsize n = 0;
1139   size_t *dstSizes = NULL;
1140   tjtransform *t = NULL;
1141   jbyteArray *jdstBufs = NULL;
1142   int i, jpegWidth = 0, jpegHeight = 0, jpegSubsamp;
1143   jintArray jdstSizes = 0;
1144   jint *dstSizesi = NULL;
1145   JNICustomFilterParams *params = NULL;
1146
1147   GET_HANDLE();
1148
1149   if ((*env)->GetArrayLength(env, jsrcBuf) < jpegSize)
1150     THROW_ARG("Source buffer is not large enough");
1151   if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
1152     THROW_ARG("JPEG header has not yet been read");
1153   if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
1154     THROW_ARG("JPEG header has not yet been read");
1155   if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
1156     THROW_ARG("TJPARAM_SUBSAMP must be specified");
1157
1158   n = (*env)->GetArrayLength(env, dstobjs);
1159   if (n != (*env)->GetArrayLength(env, tobjs))
1160     THROW_ARG("Mismatch between size of transforms array and destination buffers array");
1161
1162   if ((dstBufs =
1163        (unsigned char **)malloc(sizeof(unsigned char *) * n)) == NULL)
1164     THROW_MEM();
1165   if ((jdstBufs = (jbyteArray *)malloc(sizeof(jbyteArray) * n)) == NULL)
1166     THROW_MEM();
1167   if ((dstSizes = (size_t *)malloc(sizeof(size_t) * n)) == NULL)
1168     THROW_MEM();
1169   if ((t = (tjtransform *)malloc(sizeof(tjtransform) * n)) == NULL)
1170     THROW_MEM();
1171   if ((params = (JNICustomFilterParams *)malloc(sizeof(JNICustomFilterParams) *
1172                                                 n)) == NULL)
1173     THROW_MEM();
1174   for (i = 0; i < n; i++) {
1175     dstBufs[i] = NULL;  jdstBufs[i] = NULL;  dstSizes[i] = 0;
1176     memset(&t[i], 0, sizeof(tjtransform));
1177     memset(&params[i], 0, sizeof(JNICustomFilterParams));
1178   }
1179
1180   for (i = 0; i < n; i++) {
1181     jobject tobj, cfobj;
1182
1183     BAILIF0(tobj = (*env)->GetObjectArrayElement(env, tobjs, i));
1184     BAILIF0(_cls = (*env)->GetObjectClass(env, tobj));
1185     BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "op", "I"));
1186     t[i].op = (*env)->GetIntField(env, tobj, _fid);
1187     BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "options", "I"));
1188     t[i].options = (*env)->GetIntField(env, tobj, _fid);
1189     BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "x", "I"));
1190     t[i].r.x = (*env)->GetIntField(env, tobj, _fid);
1191     BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "y", "I"));
1192     t[i].r.y = (*env)->GetIntField(env, tobj, _fid);
1193     BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "width", "I"));
1194     t[i].r.w = (*env)->GetIntField(env, tobj, _fid);
1195     BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "height", "I"));
1196     t[i].r.h = (*env)->GetIntField(env, tobj, _fid);
1197
1198     BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "cf",
1199       "Lorg/libjpegturbo/turbojpeg/TJCustomFilter;"));
1200     cfobj = (*env)->GetObjectField(env, tobj, _fid);
1201     if (cfobj) {
1202       params[i].env = env;
1203       params[i].tobj = tobj;
1204       params[i].cfobj = cfobj;
1205       t[i].customFilter = JNICustomFilter;
1206       t[i].data = (void *)&params[i];
1207     }
1208   }
1209
1210   if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
1211     THROW_TJ();
1212
1213   for (i = 0; i < n; i++) {
1214     int w = jpegWidth, h = jpegHeight;
1215
1216     if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
1217         t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
1218       w = jpegHeight;  h = jpegWidth;
1219     }
1220     if (t[i].r.w != 0) w = t[i].r.w;
1221     if (t[i].r.h != 0) h = t[i].r.h;
1222     BAILIF0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
1223     if ((size_t)(*env)->GetArrayLength(env, jdstBufs[i]) <
1224         tj3JPEGBufSize(w, h, jpegSubsamp))
1225       THROW_ARG("Destination buffer is not large enough");
1226   }
1227   BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
1228   for (i = 0; i < n; i++)
1229     BAILIF0NOEC(dstBufs[i] =
1230                 (*env)->GetPrimitiveArrayCritical(env, jdstBufs[i], 0));
1231
1232   if (tj3Transform(handle, jpegBuf, jpegSize, n, dstBufs, dstSizes, t) == -1) {
1233     for (i = 0; i < n; i++)
1234       SAFE_RELEASE(jdstBufs[i], dstBufs[i]);
1235     SAFE_RELEASE(jsrcBuf, jpegBuf);
1236     THROW_TJ();
1237   }
1238
1239   for (i = 0; i < n; i++)
1240     SAFE_RELEASE(jdstBufs[i], dstBufs[i]);
1241   SAFE_RELEASE(jsrcBuf, jpegBuf);
1242
1243   jdstSizes = (*env)->NewIntArray(env, n);
1244   BAILIF0(dstSizesi = (*env)->GetIntArrayElements(env, jdstSizes, 0));
1245   for (i = 0; i < n; i++) dstSizesi[i] = (int)dstSizes[i];
1246
1247 bailout:
1248   if (dstSizesi) (*env)->ReleaseIntArrayElements(env, jdstSizes, dstSizesi, 0);
1249   if (dstBufs) {
1250     for (i = 0; i < n; i++) {
1251       if (dstBufs[i] && jdstBufs && jdstBufs[i])
1252         (*env)->ReleasePrimitiveArrayCritical(env, jdstBufs[i], dstBufs[i], 0);
1253     }
1254     free(dstBufs);
1255   }
1256   SAFE_RELEASE(jsrcBuf, jpegBuf);
1257   free(jdstBufs);
1258   free(dstSizes);
1259   free(t);
1260   return jdstSizes;
1261 }
1262
1263 /* TurboJPEG 1.2.x: TJDecompressor::destroy() */
1264 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy
1265   (JNIEnv *env, jobject obj)
1266 {
1267   Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy(env, obj);
1268 }
1269
1270 /* Private image I/O routines (used only by TJBench) */
1271 JNIEXPORT jobject JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_loadImage
1272   (JNIEnv *env, jobject obj, jint precision, jstring jfilename,
1273    jintArray jwidth, jint align, jintArray jheight, jintArray jpixelFormat)
1274 {
1275   tjhandle handle = NULL;
1276   void *dstBuf = NULL, *jdstPtr;
1277   int width, *warr, height, *harr, pixelFormat, *pfarr, n;
1278   const char *filename = NULL;
1279   jboolean isCopy;
1280   jobject jdstBuf = NULL;
1281
1282   GET_HANDLE();
1283
1284   if ((precision != 8 && precision != 12 && precision != 16) ||
1285       jfilename == NULL || jwidth == NULL ||
1286       (*env)->GetArrayLength(env, jwidth) < 1 || jheight == NULL ||
1287       (*env)->GetArrayLength(env, jheight) < 1 || jpixelFormat == NULL ||
1288       (*env)->GetArrayLength(env, jpixelFormat) < 1)
1289     THROW_ARG("Invalid argument in loadImage()");
1290
1291   BAILIF0NOEC(warr = (*env)->GetPrimitiveArrayCritical(env, jwidth, 0));
1292   width = warr[0];
1293   (*env)->ReleasePrimitiveArrayCritical(env, jwidth, warr, 0);
1294   BAILIF0NOEC(harr = (*env)->GetPrimitiveArrayCritical(env, jheight, 0));
1295   height = harr[0];
1296   (*env)->ReleasePrimitiveArrayCritical(env, jheight, harr, 0);
1297   BAILIF0NOEC(pfarr = (*env)->GetPrimitiveArrayCritical(env, jpixelFormat, 0));
1298   pixelFormat = pfarr[0];
1299   (*env)->ReleasePrimitiveArrayCritical(env, jpixelFormat, pfarr, 0);
1300   BAILIF0(filename = (*env)->GetStringUTFChars(env, jfilename, &isCopy));
1301
1302   if (precision == 8) {
1303     if ((dstBuf = tj3LoadImage8(handle, filename, &width, align, &height,
1304                                 &pixelFormat)) == NULL)
1305       THROW_TJ();
1306   } else if (precision == 12) {
1307     if ((dstBuf = tj3LoadImage12(handle, filename, &width, align, &height,
1308                                  &pixelFormat)) == NULL)
1309       THROW_TJ();
1310   } else {
1311     if ((dstBuf = tj3LoadImage16(handle, filename, &width, align, &height,
1312                                  &pixelFormat)) == NULL)
1313       THROW_TJ();
1314   }
1315
1316   (*env)->ReleaseStringUTFChars(env, jfilename, filename);
1317   filename = NULL;
1318
1319   if ((unsigned long long)width * (unsigned long long)height *
1320       (unsigned long long)tjPixelSize[pixelFormat] >
1321       (unsigned long long)((unsigned int)-1))
1322     THROW_ARG("Image is too large");
1323
1324   BAILIF0NOEC(warr = (*env)->GetPrimitiveArrayCritical(env, jwidth, 0));
1325   warr[0] = width;
1326   (*env)->ReleasePrimitiveArrayCritical(env, jwidth, warr, 0);
1327   BAILIF0NOEC(harr = (*env)->GetPrimitiveArrayCritical(env, jheight, 0));
1328   harr[0] = height;
1329   (*env)->ReleasePrimitiveArrayCritical(env, jheight, harr, 0);
1330   BAILIF0NOEC(pfarr = (*env)->GetPrimitiveArrayCritical(env, jpixelFormat, 0));
1331   pfarr[0] = pixelFormat;
1332   (*env)->ReleasePrimitiveArrayCritical(env, jpixelFormat, pfarr, 0);
1333
1334   n = width * height * tjPixelSize[pixelFormat];
1335   if (precision == 8)
1336     jdstBuf = (*env)->NewByteArray(env, n);
1337   else
1338     jdstBuf = (*env)->NewShortArray(env, n);
1339   BAILIF0NOEC(jdstPtr = (*env)->GetPrimitiveArrayCritical(env, jdstBuf, 0));
1340   memcpy(jdstPtr, dstBuf, n * (precision > 8 ? 2 : 1));
1341   (*env)->ReleasePrimitiveArrayCritical(env, jdstBuf, jdstPtr, 0);
1342
1343 bailout:
1344   if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);
1345   tj3Free(dstBuf);
1346   return jdstBuf;
1347 }
1348
1349
1350 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_saveImage
1351   (JNIEnv *env, jobject obj, jint precision, jstring jfilename,
1352    jobject jsrcBuf, jint width, jint pitch, jint height, jint pixelFormat)
1353 {
1354   tjhandle handle = NULL;
1355   void *srcBuf = NULL, *jsrcPtr;
1356   const char *filename = NULL;
1357   int n;
1358   jboolean isCopy;
1359
1360   GET_HANDLE();
1361
1362   if ((precision != 8 && precision != 12 && precision != 16) ||
1363       jfilename == NULL || jsrcBuf == NULL || width < 1 || height < 1 ||
1364       pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1365     THROW_ARG("Invalid argument in saveImage()");
1366
1367   if ((unsigned long long)width * (unsigned long long)height *
1368       (unsigned long long)tjPixelSize[pixelFormat] >
1369       (unsigned long long)((unsigned int)-1))
1370     THROW_ARG("Image is too large");
1371   n = width * height * tjPixelSize[pixelFormat];
1372   if ((*env)->GetArrayLength(env, jsrcBuf) < n)
1373     THROW_ARG("Source buffer is not large enough");
1374
1375   if ((srcBuf = malloc(n * (precision > 8 ? 2 : 1))) == NULL)
1376     THROW_MEM();
1377
1378   BAILIF0NOEC(jsrcPtr = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
1379   memcpy(srcBuf, jsrcPtr, n * (precision > 8 ? 2 : 1));
1380   (*env)->ReleasePrimitiveArrayCritical(env, jsrcBuf, jsrcPtr, 0);
1381   BAILIF0(filename = (*env)->GetStringUTFChars(env, jfilename, &isCopy));
1382
1383   if (precision == 8) {
1384     if (tj3SaveImage8(handle, filename, srcBuf, width, pitch, height,
1385                       pixelFormat) == -1)
1386       THROW_TJ();
1387   } else if (precision == 12) {
1388     if (tj3SaveImage12(handle, filename, srcBuf, width, pitch, height,
1389                        pixelFormat) == -1)
1390       THROW_TJ();
1391   } else {
1392     if (tj3SaveImage16(handle, filename, srcBuf, width, pitch, height,
1393                        pixelFormat) == -1)
1394       THROW_TJ();
1395   }
1396
1397 bailout:
1398   if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);
1399   free(srcBuf);
1400 }