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