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 
ProcessSystemProperties(JNIEnv * env)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() */
Java_org_libjpegturbo_turbojpeg_TJ_bufSize(JNIEnv * env,jclass cls,jint width,jint height,jint jpegSubsamp)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() */
Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII(JNIEnv * env,jclass cls,jint width,jint pad,jint height,jint subsamp)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() */
Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__III(JNIEnv * env,jclass cls,jint width,jint height,jint subsamp)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() */
Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII(JNIEnv * env,jclass cls,jint componentID,jint width,jint stride,jint height,jint subsamp)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() */
Java_org_libjpegturbo_turbojpeg_TJ_planeWidth__III(JNIEnv * env,jclass cls,jint componentID,jint width,jint subsamp)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() */
Java_org_libjpegturbo_turbojpeg_TJ_planeHeight__III(JNIEnv * env,jclass cls,jint componentID,jint height,jint subsamp)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() */
Java_org_libjpegturbo_turbojpeg_TJCompressor_init(JNIEnv * env,jobject obj)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 
TJCompressor_compress(JNIEnv * env,jobject obj,jarray src,jint srcElementSize,jint x,jint y,jint width,jint pitch,jint height,jint pf,jbyteArray dst,jint jpegSubsamp,jint jpegQual,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIIIII_3BIII(JNIEnv * env,jobject obj,jbyteArray src,jint x,jint y,jint width,jint pitch,jint height,jint pf,jbyteArray dst,jint jpegSubsamp,jint jpegQual,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3BIIII_3BIII(JNIEnv * env,jobject obj,jbyteArray src,jint width,jint pitch,jint height,jint pf,jbyteArray dst,jint jpegSubsamp,jint jpegQual,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIIIII_3BIII(JNIEnv * env,jobject obj,jintArray src,jint x,jint y,jint width,jint stride,jint height,jint pf,jbyteArray dst,jint jpegSubsamp,jint jpegQual,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIII_3BIII(JNIEnv * env,jobject obj,jintArray src,jint width,jint stride,jint height,jint pf,jbyteArray dst,jint jpegSubsamp,jint jpegQual,jint flags)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() */
Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3_3B_3II_3III_3BII(JNIEnv * env,jobject obj,jobjectArray srcobjs,jintArray jSrcOffsets,jint width,jintArray jSrcStrides,jint height,jint subsamp,jbyteArray dst,jint jpegQual,jint flags)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 
TJCompressor_encodeYUV(JNIEnv * env,jobject obj,jarray src,jint srcElementSize,jint x,jint y,jint width,jint pitch,jint height,jint pf,jobjectArray dstobjs,jintArray jDstOffsets,jintArray jDstStrides,jint subsamp,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIIIII_3_3B_3I_3III(JNIEnv * env,jobject obj,jbyteArray src,jint x,jint y,jint width,jint pitch,jint height,jint pf,jobjectArray dstobjs,jintArray jDstOffsets,jintArray jDstStrides,jint subsamp,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIIIII_3_3B_3I_3III(JNIEnv * env,jobject obj,jintArray src,jint x,jint y,jint width,jint stride,jint height,jint pf,jobjectArray dstobjs,jintArray jDstOffsets,jintArray jDstStrides,jint subsamp,jint flags)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 
TJCompressor_encodeYUV_12(JNIEnv * env,jobject obj,jarray src,jint srcElementSize,jint width,jint pitch,jint height,jint pf,jbyteArray dst,jint subsamp,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BII(JNIEnv * env,jobject obj,jbyteArray src,jint width,jint pitch,jint height,jint pf,jbyteArray dst,jint subsamp,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIII_3BII(JNIEnv * env,jobject obj,jintArray src,jint width,jint stride,jint height,jint pf,jbyteArray dst,jint subsamp,jint flags)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() */
Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy(JNIEnv * env,jobject obj)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() */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_init(JNIEnv * env,jobject obj)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() */
Java_org_libjpegturbo_turbojpeg_TJ_getScalingFactors(JNIEnv * env,jclass cls)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() */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressHeader(JNIEnv * env,jobject obj,jbyteArray src,jint jpegSize)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 
TJDecompressor_decompress(JNIEnv * env,jobject obj,jbyteArray src,jint jpegSize,jarray dst,jint dstElementSize,jint x,jint y,jint width,jint pitch,jint height,jint pf,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIIIII(JNIEnv * env,jobject obj,jbyteArray src,jint jpegSize,jbyteArray dst,jint x,jint y,jint width,jint pitch,jint height,jint pf,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3BIIIII(JNIEnv * env,jobject obj,jbyteArray src,jint jpegSize,jbyteArray dst,jint width,jint pitch,jint height,jint pf,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIIIII(JNIEnv * env,jobject obj,jbyteArray src,jint jpegSize,jintArray dst,jint x,jint y,jint width,jint stride,jint height,jint pf,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress___3BI_3IIIIII(JNIEnv * env,jobject obj,jbyteArray src,jint jpegSize,jintArray dst,jint width,jint stride,jint height,jint pf,jint flags)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() */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3_3B_3II_3III(JNIEnv * env,jobject obj,jbyteArray src,jint jpegSize,jobjectArray dstobjs,jintArray jDstOffsets,jint desiredWidth,jintArray jDstStrides,jint desiredHeight,jint flags)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() */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3BI(JNIEnv * env,jobject obj,jbyteArray src,jint jpegSize,jbyteArray dst,jint flags)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 
TJDecompressor_decodeYUV(JNIEnv * env,jobject obj,jobjectArray srcobjs,jintArray jSrcOffsets,jintArray jSrcStrides,jint subsamp,jarray dst,jint dstElementSize,jint x,jint y,jint width,jint pitch,jint height,jint pf,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3BIIIIIII(JNIEnv * env,jobject obj,jobjectArray srcobjs,jintArray jSrcOffsets,jintArray jSrcStrides,jint subsamp,jbyteArray dst,jint x,jint y,jint width,jint pitch,jint height,jint pf,jint flags)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 */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV___3_3B_3I_3II_3IIIIIIII(JNIEnv * env,jobject obj,jobjectArray srcobjs,jintArray jSrcOffsets,jintArray jSrcStrides,jint subsamp,jintArray dst,jint x,jint y,jint width,jint stride,jint height,jint pf,jint flags)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() */
Java_org_libjpegturbo_turbojpeg_TJTransformer_init(JNIEnv * env,jobject obj)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 
JNICustomFilter(short * coeffs,tjregion arrayRegion,tjregion planeRegion,int componentIndex,int transformIndex,tjtransform * transform)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() */
Java_org_libjpegturbo_turbojpeg_TJTransformer_transform(JNIEnv * env,jobject obj,jbyteArray jsrcBuf,jint jpegSize,jobjectArray dstobjs,jobjectArray tobjs,jint flags)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() */
Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy(JNIEnv * env,jobject obj)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