Home | History | Annotate | Download | only in libjpeg-turbo
      1 /*
      2  * Copyright (C)2011-2018 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 <stdlib.h>
     30 #include <string.h>
     31 #include "turbojpeg.h"
     32 #ifdef WIN32
     33 #include "tjutil.h"
     34 #endif
     35 #include <jni.h>
     36 #include "java/org_libjpegturbo_turbojpeg_TJCompressor.h"
     37 #include "java/org_libjpegturbo_turbojpeg_TJDecompressor.h"
     38 #include "java/org_libjpegturbo_turbojpeg_TJ.h"
     39 
     40 #define PAD(v, p)  ((v + (p) - 1) & (~((p) - 1)))
     41 
     42 #define bailif0(f) { \
     43   if (!(f) || (*env)->ExceptionCheck(env)) { \
     44     goto bailout; \
     45   } \
     46 }
     47 
     48 #define _throw(msg, exceptionClass) { \
     49   jclass _exccls = (*env)->FindClass(env, exceptionClass); \
     50   \
     51   bailif0(_exccls); \
     52   (*env)->ThrowNew(env, _exccls, msg); \
     53   goto bailout; \
     54 }
     55 
     56 #define _throwtj() { \
     57   jclass _exccls; \
     58   jmethodID _excid; \
     59   jobject _excobj; \
     60   jstring _errstr; \
     61   \
     62   bailif0(_errstr = (*env)->NewStringUTF(env, tjGetErrorStr2(handle))); \
     63   bailif0(_exccls = (*env)->FindClass(env, \
     64     "org/libjpegturbo/turbojpeg/TJException")); \
     65   bailif0(_excid = (*env)->GetMethodID(env, _exccls, "<init>", \
     66                                        "(Ljava/lang/String;I)V")); \
     67   bailif0(_excobj = (*env)->NewObject(env, _exccls, _excid, _errstr, \
     68                                       tjGetErrorCode(handle))); \
     69   (*env)->Throw(env, _excobj); \
     70   goto bailout; \
     71 }
     72 
     73 #define _throwarg(msg)  _throw(msg, "java/lang/IllegalArgumentException")
     74 
     75 #define _throwmem() \
     76   _throw("Memory allocation failure", "java/lang/OutOfMemoryError");
     77 
     78 #define gethandle() \
     79   jclass _cls = (*env)->GetObjectClass(env, obj); \
     80   jfieldID _fid; \
     81   \
     82   bailif0(_cls); \
     83   bailif0(_fid = (*env)->GetFieldID(env, _cls, "handle", "J")); \
     84   handle = (tjhandle)(size_t)(*env)->GetLongField(env, obj, _fid);
     85 
     86 #ifdef _WIN32
     87 #define setenv(envvar, value, dummy)  _putenv_s(envvar, value)
     88 #endif
     89 
     90 #define prop2env(property, envvar) { \
     91   if ((jName = (*env)->NewStringUTF(env, property)) != NULL && \
     92       (jValue = (*env)->CallStaticObjectMethod(env, cls, mid, \
     93                                                jName)) != NULL) { \
     94     if ((value = (*env)->GetStringUTFChars(env, jValue, 0)) != NULL) { \
     95       setenv(envvar, value, 1); \
     96       (*env)->ReleaseStringUTFChars(env, jValue, value); \
     97     } \
     98   } \
     99 }
    100 
    101 int ProcessSystemProperties(JNIEnv *env)
    102 {
    103   jclass cls;
    104   jmethodID mid;
    105   jstring jName, jValue;
    106   const char *value;
    107 
    108   bailif0(cls = (*env)->FindClass(env, "java/lang/System"));
    109   bailif0(mid = (*env)->GetStaticMethodID(env, cls, "getProperty",
    110     "(Ljava/lang/String;)Ljava/lang/String;"));
    111 
    112   prop2env("turbojpeg.optimize", "TJ_OPTIMIZE");
    113   prop2env("turbojpeg.arithmetic", "TJ_ARITHMETIC");
    114   prop2env("turbojpeg.restart", "TJ_RESTART");
    115   prop2env("turbojpeg.progressive", "TJ_PROGRESSIVE");
    116   return 0;
    117 
    118 bailout:
    119   return -1;
    120 }
    121 
    122 /* TurboJPEG 1.2.x: TJ::bufSize() */
    123 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize
    124   (JNIEnv *env, jclass cls, jint width, jint height, jint jpegSubsamp)
    125 {
    126   jint retval = (jint)tjBufSize(width, height, jpegSubsamp);
    127 
    128   if (retval == -1) _throwarg(tjGetErrorStr());
    129 
    130 bailout:
    131   return retval;
    132 }
    133 
    134 /* TurboJPEG 1.4.x: TJ::bufSizeYUV() */
    135 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII
    136   (JNIEnv *env, jclass cls, jint width, jint pad, jint height, jint subsamp)
    137 {
    138   jint retval = (jint)tjBufSizeYUV2(width, pad, height, subsamp);
    139 
    140   if (retval == -1) _throwarg(tjGetErrorStr());
    141 
    142 bailout:
    143   return retval;
    144 }
    145 
    146 /* TurboJPEG 1.2.x: TJ::bufSizeYUV() */
    147 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__III
    148   (JNIEnv *env, jclass cls, jint width, jint height, jint subsamp)
    149 {
    150   return Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII(env, cls, width,
    151                                                              4, height,
    152                                                              subsamp);
    153 }
    154 
    155 /* TurboJPEG 1.4.x: TJ::planeSizeYUV() */
    156 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII
    157   (JNIEnv *env, jclass cls, jint componentID, jint width, jint stride,
    158    jint height, jint subsamp)
    159 {
    160   jint retval = (jint)tjPlaneSizeYUV(componentID, width, stride, height,
    161                                      subsamp);
    162 
    163   if (retval == -1) _throwarg(tjGetErrorStr());
    164 
    165 bailout:
    166   return retval;
    167 }
    168 
    169 /* TurboJPEG 1.4.x: TJ::planeWidth() */
    170 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeWidth__III
    171   (JNIEnv *env, jclass cls, jint componentID, jint width, jint subsamp)
    172 {
    173   jint retval = (jint)tjPlaneWidth(componentID, width, subsamp);
    174 
    175   if (retval == -1) _throwarg(tjGetErrorStr());
    176 
    177 bailout:
    178   return retval;
    179 }
    180 
    181 /* TurboJPEG 1.4.x: TJ::planeHeight() */
    182 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeHeight__III
    183   (JNIEnv *env, jclass cls, jint componentID, jint height, jint subsamp)
    184 {
    185   jint retval = (jint)tjPlaneHeight(componentID, height, subsamp);
    186 
    187   if (retval == -1) _throwarg(tjGetErrorStr());
    188 
    189 bailout:
    190   return retval;
    191 }
    192 
    193 /* TurboJPEG 1.2.x: TJCompressor::init() */
    194 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_init
    195   (JNIEnv *env, jobject obj)
    196 {
    197   jclass cls;
    198   jfieldID fid;
    199   tjhandle handle;
    200 
    201   if ((handle = tjInitCompress()) == NULL)
    202     _throw(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
    203 
    204   bailif0(cls = (*env)->GetObjectClass(env, obj));
    205   bailif0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
    206   (*env)->SetLongField(env, obj, fid, (size_t)handle);
    207 
    208 bailout:
    209   return;
    210 }
    211 
    212 static jint TJCompressor_compress
    213   (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint x, jint y,
    214    jint width, jint pitch, jint height, jint pf, jbyteArray dst,
    215    jint jpegSubsamp, jint jpegQual, jint flags)
    216 {
    217   tjhandle handle = 0;
    218   unsigned long jpegSize = 0;
    219   jsize arraySize = 0, actualPitch;
    220   unsigned char *srcBuf = NULL, *jpegBuf = NULL;
    221 
    222   gethandle();
    223 
    224   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
    225       height < 1 || pitch < 0)
    226     _throwarg("Invalid argument in compress()");
    227   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
    228     _throwarg("Mismatch between Java and C API");
    229 
    230   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
    231   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
    232   if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
    233     _throwarg("Source buffer is not large enough");
    234   jpegSize = tjBufSize(width, height, jpegSubsamp);
    235   if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
    236     _throwarg("Destination buffer is not large enough");
    237 
    238   if (ProcessSystemProperties(env) < 0) goto bailout;
    239 
    240   bailif0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
    241   bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
    242 
    243   if (tjCompress2(handle, &srcBuf[y * actualPitch + x * tjPixelSize[pf]],
    244                   width, pitch, height, pf, &jpegBuf, &jpegSize, jpegSubsamp,
    245                   jpegQual, flags | TJFLAG_NOREALLOC) == -1)
    246     _throwtj();
    247 
    248 bailout:
    249   if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, jpegBuf, 0);
    250   if (srcBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0);
    251   return (jint)jpegSize;
    252 }
    253 
    254 /* TurboJPEG 1.3.x: TJCompressor::compress() byte source */
    255 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIIIII_3BIII
    256   (JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width,
    257    jint pitch, jint height, jint pf, jbyteArray dst, jint jpegSubsamp,
    258    jint jpegQual, jint flags)
    259 {
    260   return TJCompressor_compress(env, obj, src, 1, x, y, width, pitch, height,
    261                                pf, dst, jpegSubsamp, jpegQual, flags);
    262 }
    263 
    264 /* TurboJPEG 1.2.x: TJCompressor::compress() byte source */
    265 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIII_3BIII
    266   (JNIEnv *env, jobject obj, jbyteArray src, jint width, jint pitch,
    267    jint height, jint pf, jbyteArray dst, jint jpegSubsamp, jint jpegQual,
    268    jint flags)
    269 {
    270   return TJCompressor_compress(env, obj, src, 1, 0, 0, width, pitch, height,
    271                                pf, dst, jpegSubsamp, jpegQual, flags);
    272 }
    273 
    274 /* TurboJPEG 1.3.x: TJCompressor::compress() int source */
    275 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIIIII_3BIII
    276   (JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width,
    277    jint stride, jint height, jint pf, jbyteArray dst, jint jpegSubsamp,
    278    jint jpegQual, jint flags)
    279 {
    280   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
    281     _throwarg("Invalid argument in compress()");
    282   if (tjPixelSize[pf] != sizeof(jint))
    283     _throwarg("Pixel format must be 32-bit when compressing from an integer buffer.");
    284 
    285   return TJCompressor_compress(env, obj, src, sizeof(jint), x, y, width,
    286                                stride * sizeof(jint), height, pf, dst,
    287                                jpegSubsamp, jpegQual, flags);
    288 
    289 bailout:
    290   return 0;
    291 }
    292 
    293 /* TurboJPEG 1.2.x: TJCompressor::compress() int source */
    294 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIII_3BIII
    295   (JNIEnv *env, jobject obj, jintArray src, jint width, jint stride,
    296    jint height, jint pf, jbyteArray dst, jint jpegSubsamp, jint jpegQual,
    297    jint flags)
    298 {
    299   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
    300     _throwarg("Invalid argument in compress()");
    301   if (tjPixelSize[pf] != sizeof(jint))
    302     _throwarg("Pixel format must be 32-bit when compressing from an integer buffer.");
    303 
    304   return TJCompressor_compress(env, obj, src, sizeof(jint), 0, 0, width,
    305                                stride * sizeof(jint), height, pf, dst,
    306                                jpegSubsamp, jpegQual, flags);
    307 
    308 bailout:
    309   return 0;
    310 }
    311 
    312 /* TurboJPEG 1.4.x: TJCompressor::compressFromYUV() */
    313 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3_3B_3II_3III_3BII
    314   (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
    315    jint width, jintArray jSrcStrides, jint height, jint subsamp,
    316    jbyteArray dst, jint jpegQual, jint flags)
    317 {
    318   tjhandle handle = 0;
    319   unsigned long jpegSize = 0;
    320   jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL };
    321   const unsigned char *srcPlanes[3];
    322   unsigned char *jpegBuf = NULL;
    323   int *srcOffsets = NULL, *srcStrides = NULL;
    324   int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i;
    325 
    326   gethandle();
    327 
    328   if (subsamp < 0 || subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP)
    329     _throwarg("Invalid argument in compressFromYUV()");
    330   if (org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
    331     _throwarg("Mismatch between Java and C API");
    332 
    333   if ((*env)->GetArrayLength(env, srcobjs) < nc)
    334     _throwarg("Planes array is too small for the subsampling type");
    335   if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
    336     _throwarg("Offsets array is too small for the subsampling type");
    337   if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
    338     _throwarg("Strides array is too small for the subsampling type");
    339 
    340   jpegSize = tjBufSize(width, height, subsamp);
    341   if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
    342     _throwarg("Destination buffer is not large enough");
    343 
    344   if (ProcessSystemProperties(env) < 0) goto bailout;
    345 
    346   bailif0(srcOffsets = (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0));
    347   bailif0(srcStrides = (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0));
    348   for (i = 0; i < nc; i++) {
    349     int planeSize = tjPlaneSizeYUV(i, width, srcStrides[i], height, subsamp);
    350     int pw = tjPlaneWidth(i, width, subsamp);
    351 
    352     if (planeSize < 0 || pw < 0)
    353       _throwarg(tjGetErrorStr());
    354 
    355     if (srcOffsets[i] < 0)
    356       _throwarg("Invalid argument in compressFromYUV()");
    357     if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0)
    358       _throwarg("Negative plane stride would cause memory to be accessed below plane boundary");
    359 
    360     bailif0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
    361     if ((*env)->GetArrayLength(env, jSrcPlanes[i]) < srcOffsets[i] + planeSize)
    362       _throwarg("Source plane is not large enough");
    363 
    364     bailif0(srcPlanes[i] =
    365             (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
    366     srcPlanes[i] = &srcPlanes[i][srcOffsets[i]];
    367   }
    368   bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
    369 
    370   if (tjCompressFromYUVPlanes(handle, srcPlanes, width, srcStrides, height,
    371                               subsamp, &jpegBuf, &jpegSize, jpegQual,
    372                               flags | TJFLAG_NOREALLOC) == -1)
    373     _throwtj();
    374 
    375 bailout:
    376   if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, jpegBuf, 0);
    377   for (i = 0; i < nc; i++) {
    378     if (srcPlanes[i] && jSrcPlanes[i])
    379       (*env)->ReleasePrimitiveArrayCritical(env, jSrcPlanes[i],
    380                                             (unsigned char *)srcPlanes[i], 0);
    381   }
    382   if (srcStrides)
    383     (*env)->ReleasePrimitiveArrayCritical(env, jSrcStrides, srcStrides, 0);
    384   if (srcOffsets)
    385     (*env)->ReleasePrimitiveArrayCritical(env, jSrcOffsets, srcOffsets, 0);
    386   return (jint)jpegSize;
    387 }
    388 
    389 static void TJCompressor_encodeYUV
    390   (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint x, jint y,
    391    jint width, jint pitch, jint height, jint pf, jobjectArray dstobjs,
    392    jintArray jDstOffsets, jintArray jDstStrides, jint subsamp, jint flags)
    393 {
    394   tjhandle handle = 0;
    395   jsize arraySize = 0, actualPitch;
    396   jbyteArray jDstPlanes[3] = { NULL, NULL, NULL };
    397   unsigned char *srcBuf = NULL, *dstPlanes[3];
    398   int *dstOffsets = NULL, *dstStrides = NULL;
    399   int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i;
    400 
    401   gethandle();
    402 
    403   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
    404       height < 1 || pitch < 0 || subsamp < 0 ||
    405       subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP)
    406     _throwarg("Invalid argument in encodeYUV()");
    407   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
    408       org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
    409     _throwarg("Mismatch between Java and C API");
    410 
    411   if ((*env)->GetArrayLength(env, dstobjs) < nc)
    412     _throwarg("Planes array is too small for the subsampling type");
    413   if ((*env)->GetArrayLength(env, jDstOffsets) < nc)
    414     _throwarg("Offsets array is too small for the subsampling type");
    415   if ((*env)->GetArrayLength(env, jDstStrides) < nc)
    416     _throwarg("Strides array is too small for the subsampling type");
    417 
    418   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
    419   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
    420   if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
    421     _throwarg("Source buffer is not large enough");
    422 
    423   bailif0(dstOffsets = (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0));
    424   bailif0(dstStrides = (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0));
    425   for (i = 0; i < nc; i++) {
    426     int planeSize = tjPlaneSizeYUV(i, width, dstStrides[i], height, subsamp);
    427     int pw = tjPlaneWidth(i, width, subsamp);
    428 
    429     if (planeSize < 0 || pw < 0)
    430       _throwarg(tjGetErrorStr());
    431 
    432     if (dstOffsets[i] < 0)
    433       _throwarg("Invalid argument in encodeYUV()");
    434     if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0)
    435       _throwarg("Negative plane stride would cause memory to be accessed below plane boundary");
    436 
    437     bailif0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
    438     if ((*env)->GetArrayLength(env, jDstPlanes[i]) < dstOffsets[i] + planeSize)
    439       _throwarg("Destination plane is not large enough");
    440 
    441     bailif0(dstPlanes[i] =
    442             (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
    443     dstPlanes[i] = &dstPlanes[i][dstOffsets[i]];
    444   }
    445   bailif0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
    446 
    447   if (tjEncodeYUVPlanes(handle, &srcBuf[y * actualPitch + x * tjPixelSize[pf]],
    448                         width, pitch, height, pf, dstPlanes, dstStrides,
    449                         subsamp, flags) == -1)
    450     _throwtj();
    451 
    452 bailout:
    453   if (srcBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0);
    454   for (i = 0; i < nc; i++) {
    455     if (dstPlanes[i] && jDstPlanes[i])
    456       (*env)->ReleasePrimitiveArrayCritical(env, jDstPlanes[i], dstPlanes[i],
    457                                             0);
    458   }
    459   if (dstStrides)
    460     (*env)->ReleasePrimitiveArrayCritical(env, jDstStrides, dstStrides, 0);
    461   if (dstOffsets)
    462     (*env)->ReleasePrimitiveArrayCritical(env, jDstOffsets, dstOffsets, 0);
    463 }
    464 
    465 /* TurboJPEG 1.4.x: TJCompressor::encodeYUV() byte source */
    466 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIIIII_3_3B_3I_3III
    467   (JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width,
    468    jint pitch, jint height, jint pf, jobjectArray dstobjs,
    469    jintArray jDstOffsets, jintArray jDstStrides, jint subsamp, jint flags)
    470 {
    471   TJCompressor_encodeYUV(env, obj, src, 1, x, y, width, pitch, height, pf,
    472                          dstobjs, jDstOffsets, jDstStrides, subsamp, flags);
    473 }
    474 
    475 /* TurboJPEG 1.4.x: TJCompressor::encodeYUV() int source */
    476 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIIIII_3_3B_3I_3III
    477   (JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width,
    478    jint stride, jint height, jint pf, jobjectArray dstobjs,
    479    jintArray jDstOffsets, jintArray jDstStrides, jint subsamp, jint flags)
    480 {
    481   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
    482     _throwarg("Invalid argument in encodeYUV()");
    483   if (tjPixelSize[pf] != sizeof(jint))
    484     _throwarg("Pixel format must be 32-bit when encoding from an integer buffer.");
    485 
    486   TJCompressor_encodeYUV(env, obj, src, sizeof(jint), x, y, width,
    487                          stride * sizeof(jint), height, pf, dstobjs,
    488                          jDstOffsets, jDstStrides, subsamp, flags);
    489 
    490 bailout:
    491   return;
    492 }
    493 
    494 JNIEXPORT void JNICALL TJCompressor_encodeYUV_12
    495   (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint width,
    496    jint pitch, jint height, jint pf, jbyteArray dst, jint subsamp, jint flags)
    497 {
    498   tjhandle handle = 0;
    499   jsize arraySize = 0;
    500   unsigned char *srcBuf = NULL, *dstBuf = NULL;
    501 
    502   gethandle();
    503 
    504   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
    505       height < 1 || pitch < 0)
    506     _throwarg("Invalid argument in encodeYUV()");
    507   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
    508     _throwarg("Mismatch between Java and C API");
    509 
    510   arraySize = (pitch == 0) ? width * tjPixelSize[pf] * height : pitch * height;
    511   if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
    512     _throwarg("Source buffer is not large enough");
    513   if ((*env)->GetArrayLength(env, dst) <
    514       (jsize)tjBufSizeYUV(width, height, subsamp))
    515     _throwarg("Destination buffer is not large enough");
    516 
    517   bailif0(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
    518   bailif0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
    519 
    520   if (tjEncodeYUV2(handle, srcBuf, width, pitch, height, pf, dstBuf, subsamp,
    521                    flags) == -1)
    522     _throwtj();
    523 
    524 bailout:
    525   if (dstBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, dstBuf, 0);
    526   if (srcBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0);
    527 }
    528 
    529 /* TurboJPEG 1.2.x: TJCompressor::encodeYUV() byte source */
    530 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BII
    531   (JNIEnv *env, jobject obj, jbyteArray src, jint width, jint pitch,
    532    jint height, jint pf, jbyteArray dst, jint subsamp, jint flags)
    533 {
    534   TJCompressor_encodeYUV_12(env, obj, src, 1, width, pitch, height, pf, dst,
    535                             subsamp, flags);
    536 }
    537 
    538 /* TurboJPEG 1.2.x: TJCompressor::encodeYUV() int source */
    539 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIII_3BII
    540   (JNIEnv *env, jobject obj, jintArray src, jint width, jint stride,
    541    jint height, jint pf, jbyteArray dst, jint subsamp, jint flags)
    542 {
    543   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
    544     _throwarg("Invalid argument in encodeYUV()");
    545   if (tjPixelSize[pf] != sizeof(jint))
    546     _throwarg("Pixel format must be 32-bit when encoding from an integer buffer.");
    547 
    548   TJCompressor_encodeYUV_12(env, obj, src, sizeof(jint), width,
    549                             stride * sizeof(jint), height, pf, dst, subsamp,
    550                             flags);
    551 
    552 bailout:
    553   return;
    554 }
    555 
    556 /* TurboJPEG 1.2.x: TJCompressor::destroy() */
    557 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy
    558   (JNIEnv *env, jobject obj)
    559 {
    560   tjhandle handle = 0;
    561 
    562   gethandle();
    563 
    564   if (tjDestroy(handle) == -1) _throwtj();
    565   (*env)->SetLongField(env, obj, _fid, 0);
    566 
    567 bailout:
    568   return;
    569 }
    570 
    571 /* TurboJPEG 1.2.x: TJDecompressor::init() */
    572 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_init
    573   (JNIEnv *env, jobject obj)
    574 {
    575   jclass cls;
    576   jfieldID fid;
    577   tjhandle handle;
    578 
    579   if ((handle = tjInitDecompress()) == NULL)
    580     _throw(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
    581 
    582   bailif0(cls = (*env)->GetObjectClass(env, obj));
    583   bailif0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
    584   (*env)->SetLongField(env, obj, fid, (size_t)handle);
    585 
    586 bailout:
    587   return;
    588 }
    589 
    590 /* TurboJPEG 1.2.x: TJDecompressor::getScalingFactors() */
    591 JNIEXPORT jobjectArray JNICALL Java_org_libjpegturbo_turbojpeg_TJ_getScalingFactors
    592   (JNIEnv *env, jclass cls)
    593 {
    594   jclass sfcls = NULL;
    595   jfieldID fid = 0;
    596   tjscalingfactor *sf = NULL;
    597   int n = 0, i;
    598   jobject sfobj = NULL;
    599   jobjectArray sfjava = NULL;
    600 
    601   if ((sf = tjGetScalingFactors(&n)) == NULL || n == 0)
    602     _throwarg(tjGetErrorStr());
    603 
    604   bailif0(sfcls = (*env)->FindClass(env,
    605     "org/libjpegturbo/turbojpeg/TJScalingFactor"));
    606   bailif0(sfjava = (jobjectArray)(*env)->NewObjectArray(env, n, sfcls, 0));
    607 
    608   for (i = 0; i < n; i++) {
    609     bailif0(sfobj = (*env)->AllocObject(env, sfcls));
    610     bailif0(fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
    611     (*env)->SetIntField(env, sfobj, fid, sf[i].num);
    612     bailif0(fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
    613     (*env)->SetIntField(env, sfobj, fid, sf[i].denom);
    614     (*env)->SetObjectArrayElement(env, sfjava, i, sfobj);
    615   }
    616 
    617 bailout:
    618   return sfjava;
    619 }
    620 
    621 /* TurboJPEG 1.2.x: TJDecompressor::decompressHeader() */
    622 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressHeader
    623   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize)
    624 {
    625   tjhandle handle = 0;
    626   unsigned char *jpegBuf = NULL;
    627   int width = 0, height = 0, jpegSubsamp = -1, jpegColorspace = -1;
    628 
    629   gethandle();
    630 
    631   if ((*env)->GetArrayLength(env, src) < jpegSize)
    632     _throwarg("Source buffer is not large enough");
    633 
    634   bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
    635 
    636   if (tjDecompressHeader3(handle, jpegBuf, (unsigned long)jpegSize, &width,
    637                           &height, &jpegSubsamp, &jpegColorspace) == -1)
    638     _throwtj();
    639 
    640   (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
    641   jpegBuf = NULL;
    642 
    643   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
    644   (*env)->SetIntField(env, obj, _fid, jpegSubsamp);
    645   if ((_fid = (*env)->GetFieldID(env, _cls, "jpegColorspace", "I")) == 0)
    646     (*env)->ExceptionClear(env);
    647   else
    648     (*env)->SetIntField(env, obj, _fid, jpegColorspace);
    649   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
    650   (*env)->SetIntField(env, obj, _fid, width);
    651   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
    652   (*env)->SetIntField(env, obj, _fid, height);
    653 
    654 bailout:
    655   if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
    656 }
    657 
    658 static void TJDecompressor_decompress
    659   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jarray dst,
    660    jint dstElementSize, jint x, jint y, jint width, jint pitch, jint height,
    661    jint pf, jint flags)
    662 {
    663   tjhandle handle = 0;
    664   jsize arraySize = 0, actualPitch;
    665   unsigned char *jpegBuf = NULL, *dstBuf = NULL;
    666 
    667   gethandle();
    668 
    669   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
    670     _throwarg("Invalid argument in decompress()");
    671   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
    672     _throwarg("Mismatch between Java and C API");
    673 
    674   if ((*env)->GetArrayLength(env, src) < jpegSize)
    675     _throwarg("Source buffer is not large enough");
    676   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
    677   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
    678   if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
    679     _throwarg("Destination buffer is not large enough");
    680 
    681   bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
    682   bailif0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
    683 
    684   if (tjDecompress2(handle, jpegBuf, (unsigned long)jpegSize,
    685                     &dstBuf[y * actualPitch + x * tjPixelSize[pf]], width,
    686                     pitch, height, pf, flags) == -1)
    687     _throwtj();
    688 
    689 bailout:
    690   if (dstBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, dstBuf, 0);
    691   if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
    692 }
    693 
    694 /* TurboJPEG 1.3.x: TJDecompressor::decompress() byte destination */
    695 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIIIII
    696   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst,
    697    jint x, jint y, jint width, jint pitch, jint height, jint pf, jint flags)
    698 {
    699   TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, x, y, width,
    700                             pitch, height, pf, flags);
    701 }
    702 
    703 /* TurboJPEG 1.2.x: TJDecompressor::decompress() byte destination */
    704 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIII
    705   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst,
    706    jint width, jint pitch, jint height, jint pf, jint flags)
    707 {
    708   TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 0, 0, width,
    709                             pitch, height, pf, flags);
    710 }
    711 
    712 /* TurboJPEG 1.3.x: TJDecompressor::decompress() int destination */
    713 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIIIII
    714   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jintArray dst,
    715    jint x, jint y, jint width, jint stride, jint height, jint pf, jint flags)
    716 {
    717   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
    718     _throwarg("Invalid argument in decompress()");
    719   if (tjPixelSize[pf] != sizeof(jint))
    720     _throwarg("Pixel format must be 32-bit when decompressing to an integer buffer.");
    721 
    722   TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), x, y,
    723                             width, stride * sizeof(jint), height, pf, flags);
    724 
    725 bailout:
    726   return;
    727 }
    728 
    729 /* TurboJPEG 1.2.x: TJDecompressor::decompress() int destination */
    730 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIII
    731   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jintArray dst,
    732    jint width, jint stride, jint height, jint pf, jint flags)
    733 {
    734   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
    735     _throwarg("Invalid argument in decompress()");
    736   if (tjPixelSize[pf] != sizeof(jint))
    737     _throwarg("Pixel format must be 32-bit when decompressing to an integer buffer.");
    738 
    739   TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), 0, 0,
    740                             width, stride * sizeof(jint), height, pf, flags);
    741 
    742 bailout:
    743   return;
    744 }
    745 
    746 /* TurboJPEG 1.4.x: TJDecompressor::decompressToYUV() */
    747 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3_3B_3II_3III
    748   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize,
    749    jobjectArray dstobjs, jintArray jDstOffsets, jint desiredWidth,
    750    jintArray jDstStrides, jint desiredHeight, jint flags)
    751 {
    752   tjhandle handle = 0;
    753   jbyteArray jDstPlanes[3] = { NULL, NULL, NULL };
    754   unsigned char *jpegBuf = NULL, *dstPlanes[3];
    755   int *dstOffsets = NULL, *dstStrides = NULL;
    756   int jpegSubsamp = -1, jpegWidth = 0, jpegHeight = 0;
    757   int nc = 0, i, width, height, scaledWidth, scaledHeight, nsf = 0;
    758   tjscalingfactor *sf;
    759 
    760   gethandle();
    761 
    762   if ((*env)->GetArrayLength(env, src) < jpegSize)
    763     _throwarg("Source buffer is not large enough");
    764   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
    765   jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid);
    766   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
    767   jpegWidth = (int)(*env)->GetIntField(env, obj, _fid);
    768   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
    769   jpegHeight = (int)(*env)->GetIntField(env, obj, _fid);
    770 
    771   nc = (jpegSubsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3);
    772 
    773   width = desiredWidth;
    774   height = desiredHeight;
    775   if (width == 0) width = jpegWidth;
    776   if (height == 0) height = jpegHeight;
    777   sf = tjGetScalingFactors(&nsf);
    778   if (!sf || nsf < 1)
    779     _throwarg(tjGetErrorStr());
    780   for (i = 0; i < nsf; i++) {
    781     scaledWidth = TJSCALED(jpegWidth, sf[i]);
    782     scaledHeight = TJSCALED(jpegHeight, sf[i]);
    783     if (scaledWidth <= width && scaledHeight <= height)
    784       break;
    785   }
    786   if (i >= nsf)
    787     _throwarg("Could not scale down to desired image dimensions");
    788 
    789   bailif0(dstOffsets = (*env)->GetPrimitiveArrayCritical(env, jDstOffsets, 0));
    790   bailif0(dstStrides = (*env)->GetPrimitiveArrayCritical(env, jDstStrides, 0));
    791   for (i = 0; i < nc; i++) {
    792     int planeSize = tjPlaneSizeYUV(i, scaledWidth, dstStrides[i], scaledHeight,
    793                                    jpegSubsamp);
    794     int pw = tjPlaneWidth(i, scaledWidth, jpegSubsamp);
    795 
    796     if (planeSize < 0 || pw < 0)
    797       _throwarg(tjGetErrorStr());
    798 
    799     if (dstOffsets[i] < 0)
    800       _throwarg("Invalid argument in decompressToYUV()");
    801     if (dstStrides[i] < 0 && dstOffsets[i] - planeSize + pw < 0)
    802       _throwarg("Negative plane stride would cause memory to be accessed below plane boundary");
    803 
    804     bailif0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
    805     if ((*env)->GetArrayLength(env, jDstPlanes[i]) < dstOffsets[i] + planeSize)
    806       _throwarg("Destination plane is not large enough");
    807 
    808     bailif0(dstPlanes[i] =
    809             (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
    810     dstPlanes[i] = &dstPlanes[i][dstOffsets[i]];
    811   }
    812   bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
    813 
    814   if (tjDecompressToYUVPlanes(handle, jpegBuf, (unsigned long)jpegSize,
    815                               dstPlanes, desiredWidth, dstStrides,
    816                               desiredHeight, flags) == -1)
    817     _throwtj();
    818 
    819 bailout:
    820   if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
    821   for (i = 0; i < nc; i++) {
    822     if (dstPlanes[i] && jDstPlanes[i])
    823       (*env)->ReleasePrimitiveArrayCritical(env, jDstPlanes[i], dstPlanes[i],
    824                                             0);
    825   }
    826   if (dstStrides)
    827     (*env)->ReleasePrimitiveArrayCritical(env, jDstStrides, dstStrides, 0);
    828   if (dstOffsets)
    829     (*env)->ReleasePrimitiveArrayCritical(env, jDstOffsets, dstOffsets, 0);
    830 }
    831 
    832 /* TurboJPEG 1.2.x: TJDecompressor::decompressToYUV() */
    833 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3BI
    834   (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst,
    835    jint flags)
    836 {
    837   tjhandle handle = 0;
    838   unsigned char *jpegBuf = NULL, *dstBuf = NULL;
    839   int jpegSubsamp = -1, jpegWidth = 0, jpegHeight = 0;
    840 
    841   gethandle();
    842 
    843   if ((*env)->GetArrayLength(env, src) < jpegSize)
    844     _throwarg("Source buffer is not large enough");
    845   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
    846   jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid);
    847   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
    848   jpegWidth = (int)(*env)->GetIntField(env, obj, _fid);
    849   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
    850   jpegHeight = (int)(*env)->GetIntField(env, obj, _fid);
    851   if ((*env)->GetArrayLength(env, dst) <
    852       (jsize)tjBufSizeYUV(jpegWidth, jpegHeight, jpegSubsamp))
    853     _throwarg("Destination buffer is not large enough");
    854 
    855   bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
    856   bailif0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
    857 
    858   if (tjDecompressToYUV(handle, jpegBuf, (unsigned long)jpegSize, dstBuf,
    859                         flags) == -1)
    860     _throwtj();
    861 
    862 bailout:
    863   if (dstBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, dstBuf, 0);
    864   if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, jpegBuf, 0);
    865 }
    866 
    867 static void TJDecompressor_decodeYUV
    868   (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
    869    jintArray jSrcStrides, jint subsamp, jarray dst, jint dstElementSize,
    870    jint x, jint y, jint width, jint pitch, jint height, jint pf, jint flags)
    871 {
    872   tjhandle handle = 0;
    873   jsize arraySize = 0, actualPitch;
    874   jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL };
    875   const unsigned char *srcPlanes[3];
    876   unsigned char *dstBuf = NULL;
    877   int *srcOffsets = NULL, *srcStrides = NULL;
    878   int nc = (subsamp == org_libjpegturbo_turbojpeg_TJ_SAMP_GRAY ? 1 : 3), i;
    879 
    880   gethandle();
    881 
    882   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || subsamp < 0 ||
    883       subsamp >= org_libjpegturbo_turbojpeg_TJ_NUMSAMP)
    884     _throwarg("Invalid argument in decodeYUV()");
    885   if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
    886       org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
    887     _throwarg("Mismatch between Java and C API");
    888 
    889   if ((*env)->GetArrayLength(env, srcobjs) < nc)
    890     _throwarg("Planes array is too small for the subsampling type");
    891   if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
    892     _throwarg("Offsets array is too small for the subsampling type");
    893   if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
    894     _throwarg("Strides array is too small for the subsampling type");
    895 
    896   actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
    897   arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
    898   if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
    899     _throwarg("Destination buffer is not large enough");
    900 
    901   bailif0(srcOffsets = (*env)->GetPrimitiveArrayCritical(env, jSrcOffsets, 0));
    902   bailif0(srcStrides = (*env)->GetPrimitiveArrayCritical(env, jSrcStrides, 0));
    903   for (i = 0; i < nc; i++) {
    904     int planeSize = tjPlaneSizeYUV(i, width, srcStrides[i], height, subsamp);
    905     int pw = tjPlaneWidth(i, width, subsamp);
    906 
    907     if (planeSize < 0 || pw < 0)
    908       _throwarg(tjGetErrorStr());
    909 
    910     if (srcOffsets[i] < 0)
    911       _throwarg("Invalid argument in decodeYUV()");
    912     if (srcStrides[i] < 0 && srcOffsets[i] - planeSize + pw < 0)
    913       _throwarg("Negative plane stride would cause memory to be accessed below plane boundary");
    914 
    915     bailif0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
    916     if ((*env)->GetArrayLength(env, jSrcPlanes[i]) < srcOffsets[i] + planeSize)
    917       _throwarg("Source plane is not large enough");
    918 
    919     bailif0(srcPlanes[i] =
    920             (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
    921     srcPlanes[i] = &srcPlanes[i][srcOffsets[i]];
    922   }
    923   bailif0(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
    924 
    925   if (tjDecodeYUVPlanes(handle, srcPlanes, srcStrides, subsamp,
    926                         &dstBuf[y * actualPitch + x * tjPixelSize[pf]], width,
    927                         pitch, height, pf, flags) == -1)
    928     _throwtj();
    929 
    930 bailout:
    931   if (dstBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, dstBuf, 0);
    932   for (i = 0; i < nc; i++) {
    933     if (srcPlanes[i] && jSrcPlanes[i])
    934       (*env)->ReleasePrimitiveArrayCritical(env, jSrcPlanes[i],
    935                                             (unsigned char *)srcPlanes[i], 0);
    936   }
    937   if (srcStrides)
    938     (*env)->ReleasePrimitiveArrayCritical(env, jSrcStrides, srcStrides, 0);
    939   if (srcOffsets)
    940     (*env)->ReleasePrimitiveArrayCritical(env, jSrcOffsets, srcOffsets, 0);
    941 }
    942 
    943 /* TurboJPEG 1.4.x: TJDecompressor::decodeYUV() byte destination */
    944 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3BIIIIIII
    945   (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
    946    jintArray jSrcStrides, jint subsamp, jbyteArray dst, jint x, jint y,
    947    jint width, jint pitch, jint height, jint pf, jint flags)
    948 {
    949   TJDecompressor_decodeYUV(env, obj, srcobjs, jSrcOffsets, jSrcStrides,
    950                            subsamp, dst, 1, x, y, width, pitch, height, pf,
    951                            flags);
    952 }
    953 
    954 /* TurboJPEG 1.4.x: TJDecompressor::decodeYUV() int destination */
    955 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3IIIIIIII
    956   (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
    957    jintArray jSrcStrides, jint subsamp, jintArray dst, jint x, jint y,
    958    jint width, jint stride, jint height, jint pf, jint flags)
    959 {
    960   if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
    961     _throwarg("Invalid argument in decodeYUV()");
    962   if (tjPixelSize[pf] != sizeof(jint))
    963     _throwarg("Pixel format must be 32-bit when decoding to an integer buffer.");
    964 
    965   TJDecompressor_decodeYUV(env, obj, srcobjs, jSrcOffsets, jSrcStrides,
    966                            subsamp, dst, sizeof(jint), x, y, width,
    967                            stride * sizeof(jint), height, pf, flags);
    968 
    969 bailout:
    970   return;
    971 }
    972 
    973 /* TurboJPEG 1.2.x: TJTransformer::init() */
    974 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_init
    975   (JNIEnv *env, jobject obj)
    976 {
    977   jclass cls;
    978   jfieldID fid;
    979   tjhandle handle;
    980 
    981   if ((handle = tjInitTransform()) == NULL)
    982     _throw(tjGetErrorStr(), "org/libjpegturbo/turbojpeg/TJException");
    983 
    984   bailif0(cls = (*env)->GetObjectClass(env, obj));
    985   bailif0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
    986   (*env)->SetLongField(env, obj, fid, (size_t)handle);
    987 
    988 bailout:
    989   return;
    990 }
    991 
    992 typedef struct _JNICustomFilterParams {
    993   JNIEnv *env;
    994   jobject tobj;
    995   jobject cfobj;
    996 } JNICustomFilterParams;
    997 
    998 static int JNICustomFilter(short *coeffs, tjregion arrayRegion,
    999                            tjregion planeRegion, int componentIndex,
   1000                            int transformIndex, tjtransform *transform)
   1001 {
   1002   JNICustomFilterParams *params = (JNICustomFilterParams *)transform->data;
   1003   JNIEnv *env = params->env;
   1004   jobject tobj = params->tobj, cfobj = params->cfobj;
   1005   jobject arrayRegionObj, planeRegionObj, bufobj, borobj;
   1006   jclass cls;
   1007   jmethodID mid;
   1008   jfieldID fid;
   1009 
   1010   bailif0(bufobj = (*env)->NewDirectByteBuffer(env, coeffs,
   1011     sizeof(short) * arrayRegion.w * arrayRegion.h));
   1012   bailif0(cls = (*env)->FindClass(env, "java/nio/ByteOrder"));
   1013   bailif0(mid = (*env)->GetStaticMethodID(env, cls, "nativeOrder",
   1014                                           "()Ljava/nio/ByteOrder;"));
   1015   bailif0(borobj = (*env)->CallStaticObjectMethod(env, cls, mid));
   1016   bailif0(cls = (*env)->GetObjectClass(env, bufobj));
   1017   bailif0(mid = (*env)->GetMethodID(env, cls, "order",
   1018     "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"));
   1019   (*env)->CallObjectMethod(env, bufobj, mid, borobj);
   1020   bailif0(mid = (*env)->GetMethodID(env, cls, "asShortBuffer",
   1021                                     "()Ljava/nio/ShortBuffer;"));
   1022   bailif0(bufobj = (*env)->CallObjectMethod(env, bufobj, mid));
   1023 
   1024   bailif0(cls = (*env)->FindClass(env, "java/awt/Rectangle"));
   1025   bailif0(arrayRegionObj = (*env)->AllocObject(env, cls));
   1026   bailif0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
   1027   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.x);
   1028   bailif0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
   1029   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.y);
   1030   bailif0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
   1031   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.w);
   1032   bailif0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
   1033   (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.h);
   1034 
   1035   bailif0(planeRegionObj = (*env)->AllocObject(env, cls));
   1036   bailif0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
   1037   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.x);
   1038   bailif0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
   1039   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.y);
   1040   bailif0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
   1041   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.w);
   1042   bailif0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
   1043   (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.h);
   1044 
   1045   bailif0(cls = (*env)->GetObjectClass(env, cfobj));
   1046   bailif0(mid = (*env)->GetMethodID(env, cls, "customFilter",
   1047     "(Ljava/nio/ShortBuffer;Ljava/awt/Rectangle;Ljava/awt/Rectangle;IILorg/libjpegturbo/turbojpeg/TJTransform;)V"));
   1048   (*env)->CallVoidMethod(env, cfobj, mid, bufobj, arrayRegionObj,
   1049                          planeRegionObj, componentIndex, transformIndex, tobj);
   1050 
   1051   return 0;
   1052 
   1053 bailout:
   1054   return -1;
   1055 }
   1056 
   1057 /* TurboJPEG 1.2.x: TJTransformer::transform() */
   1058 JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transform
   1059   (JNIEnv *env, jobject obj, jbyteArray jsrcBuf, jint jpegSize,
   1060    jobjectArray dstobjs, jobjectArray tobjs, jint flags)
   1061 {
   1062   tjhandle handle = 0;
   1063   unsigned char *jpegBuf = NULL, **dstBufs = NULL;
   1064   jsize n = 0;
   1065   unsigned long *dstSizes = NULL;
   1066   tjtransform *t = NULL;
   1067   jbyteArray *jdstBufs = NULL;
   1068   int i, jpegWidth = 0, jpegHeight = 0, jpegSubsamp;
   1069   jintArray jdstSizes = 0;
   1070   jint *dstSizesi = NULL;
   1071   JNICustomFilterParams *params = NULL;
   1072 
   1073   gethandle();
   1074 
   1075   if ((*env)->GetArrayLength(env, jsrcBuf) < jpegSize)
   1076     _throwarg("Source buffer is not large enough");
   1077   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegWidth", "I"));
   1078   jpegWidth = (int)(*env)->GetIntField(env, obj, _fid);
   1079   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegHeight", "I"));
   1080   jpegHeight = (int)(*env)->GetIntField(env, obj, _fid);
   1081   bailif0(_fid = (*env)->GetFieldID(env, _cls, "jpegSubsamp", "I"));
   1082   jpegSubsamp = (int)(*env)->GetIntField(env, obj, _fid);
   1083 
   1084   n = (*env)->GetArrayLength(env, dstobjs);
   1085   if (n != (*env)->GetArrayLength(env, tobjs))
   1086     _throwarg("Mismatch between size of transforms array and destination buffers array");
   1087 
   1088   if ((dstBufs =
   1089        (unsigned char **)malloc(sizeof(unsigned char *) * n)) == NULL)
   1090     _throwmem();
   1091   if ((jdstBufs = (jbyteArray *)malloc(sizeof(jbyteArray) * n)) == NULL)
   1092     _throwmem();
   1093   if ((dstSizes = (unsigned long *)malloc(sizeof(unsigned long) * n)) == NULL)
   1094     _throwmem();
   1095   if ((t = (tjtransform *)malloc(sizeof(tjtransform) * n)) == NULL)
   1096     _throwmem();
   1097   if ((params = (JNICustomFilterParams *)malloc(sizeof(JNICustomFilterParams) *
   1098                                                 n)) == NULL)
   1099     _throwmem();
   1100   for (i = 0; i < n; i++) {
   1101     dstBufs[i] = NULL;  jdstBufs[i] = NULL;  dstSizes[i] = 0;
   1102     memset(&t[i], 0, sizeof(tjtransform));
   1103     memset(&params[i], 0, sizeof(JNICustomFilterParams));
   1104   }
   1105 
   1106   for (i = 0; i < n; i++) {
   1107     jobject tobj, cfobj;
   1108 
   1109     bailif0(tobj = (*env)->GetObjectArrayElement(env, tobjs, i));
   1110     bailif0(_cls = (*env)->GetObjectClass(env, tobj));
   1111     bailif0(_fid = (*env)->GetFieldID(env, _cls, "op", "I"));
   1112     t[i].op = (*env)->GetIntField(env, tobj, _fid);
   1113     bailif0(_fid = (*env)->GetFieldID(env, _cls, "options", "I"));
   1114     t[i].options = (*env)->GetIntField(env, tobj, _fid);
   1115     bailif0(_fid = (*env)->GetFieldID(env, _cls, "x", "I"));
   1116     t[i].r.x = (*env)->GetIntField(env, tobj, _fid);
   1117     bailif0(_fid = (*env)->GetFieldID(env, _cls, "y", "I"));
   1118     t[i].r.y = (*env)->GetIntField(env, tobj, _fid);
   1119     bailif0(_fid = (*env)->GetFieldID(env, _cls, "width", "I"));
   1120     t[i].r.w = (*env)->GetIntField(env, tobj, _fid);
   1121     bailif0(_fid = (*env)->GetFieldID(env, _cls, "height", "I"));
   1122     t[i].r.h = (*env)->GetIntField(env, tobj, _fid);
   1123 
   1124     bailif0(_fid = (*env)->GetFieldID(env, _cls, "cf",
   1125       "Lorg/libjpegturbo/turbojpeg/TJCustomFilter;"));
   1126     cfobj = (*env)->GetObjectField(env, tobj, _fid);
   1127     if (cfobj) {
   1128       params[i].env = env;
   1129       params[i].tobj = tobj;
   1130       params[i].cfobj = cfobj;
   1131       t[i].customFilter = JNICustomFilter;
   1132       t[i].data = (void *)&params[i];
   1133     }
   1134   }
   1135 
   1136   for (i = 0; i < n; i++) {
   1137     int w = jpegWidth, h = jpegHeight;
   1138 
   1139     if (t[i].r.w != 0) w = t[i].r.w;
   1140     if (t[i].r.h != 0) h = t[i].r.h;
   1141     bailif0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
   1142     if ((unsigned long)(*env)->GetArrayLength(env, jdstBufs[i]) <
   1143         tjBufSize(w, h, jpegSubsamp))
   1144       _throwarg("Destination buffer is not large enough");
   1145   }
   1146   bailif0(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
   1147   for (i = 0; i < n; i++)
   1148     bailif0(dstBufs[i] =
   1149             (*env)->GetPrimitiveArrayCritical(env, jdstBufs[i], 0));
   1150 
   1151   if (tjTransform(handle, jpegBuf, jpegSize, n, dstBufs, dstSizes, t,
   1152                   flags | TJFLAG_NOREALLOC) == -1)
   1153     _throwtj();
   1154 
   1155   for (i = 0; i < n; i++) {
   1156     (*env)->ReleasePrimitiveArrayCritical(env, jdstBufs[i], dstBufs[i], 0);
   1157     dstBufs[i] = NULL;
   1158   }
   1159   (*env)->ReleasePrimitiveArrayCritical(env, jsrcBuf, jpegBuf, 0);
   1160   jpegBuf = NULL;
   1161 
   1162   jdstSizes = (*env)->NewIntArray(env, n);
   1163   bailif0(dstSizesi = (*env)->GetIntArrayElements(env, jdstSizes, 0));
   1164   for (i = 0; i < n; i++) dstSizesi[i] = (int)dstSizes[i];
   1165 
   1166 bailout:
   1167   if (dstSizesi) (*env)->ReleaseIntArrayElements(env, jdstSizes, dstSizesi, 0);
   1168   if (dstBufs) {
   1169     for (i = 0; i < n; i++) {
   1170       if (dstBufs[i] && jdstBufs && jdstBufs[i])
   1171         (*env)->ReleasePrimitiveArrayCritical(env, jdstBufs[i], dstBufs[i], 0);
   1172     }
   1173     free(dstBufs);
   1174   }
   1175   if (jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, jsrcBuf, jpegBuf, 0);
   1176   if (jdstBufs) free(jdstBufs);
   1177   if (dstSizes) free(dstSizes);
   1178   if (t) free(t);
   1179   return jdstSizes;
   1180 }
   1181 
   1182 /* TurboJPEG 1.2.x: TJDecompressor::destroy() */
   1183 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy
   1184   (JNIEnv *env, jobject obj)
   1185 {
   1186   Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy(env, obj);
   1187 }
   1188