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