2 * Copyright (C)2011-2023 D. R. Commander. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
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.
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.
30 #include "turbojpeg.h"
33 #include "java/org_libjpegturbo_turbojpeg_TJCompressor.h"
34 #include "java/org_libjpegturbo_turbojpeg_TJDecompressor.h"
35 #include "java/org_libjpegturbo_turbojpeg_TJTransformer.h"
36 #include "java/org_libjpegturbo_turbojpeg_TJ.h"
38 #define BAILIF0(f) { \
39 if (!(f) || (*env)->ExceptionCheck(env)) { \
44 #define BAILIF0NOEC(f) { \
50 #define THROW(msg, exceptionClass) { \
51 jclass _exccls = (*env)->FindClass(env, exceptionClass); \
54 (*env)->ThrowNew(env, _exccls, msg); \
58 #define THROW_TJ() { \
64 BAILIF0(_errstr = (*env)->NewStringUTF(env, tj3GetErrorStr(handle))); \
65 BAILIF0(_exccls = (*env)->FindClass(env, \
66 "org/libjpegturbo/turbojpeg/TJException")); \
67 BAILIF0(_excid = (*env)->GetMethodID(env, _exccls, "<init>", \
68 "(Ljava/lang/String;I)V")); \
69 BAILIF0(_excobj = (*env)->NewObject(env, _exccls, _excid, _errstr, \
70 tj3GetErrorCode(handle))); \
71 (*env)->Throw(env, _excobj); \
75 #define THROW_ARG(msg) THROW(msg, "java/lang/IllegalArgumentException")
78 THROW("Memory allocation failure", "java/lang/OutOfMemoryError");
80 #define GET_HANDLE() \
81 jclass _cls = (*env)->GetObjectClass(env, obj); \
85 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "handle", "J")); \
86 handle = (tjhandle)(size_t)(*env)->GetLongField(env, obj, _fid);
88 #define SAFE_RELEASE(javaArray, cArray) { \
89 if (javaArray && cArray) \
90 (*env)->ReleasePrimitiveArrayCritical(env, javaArray, (void *)cArray, 0); \
94 /* TurboJPEG 1.2.x: TJ::bufSize() */
95 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize
96 (JNIEnv *env, jclass cls, jint width, jint height, jint jpegSubsamp)
98 size_t retval = tj3JPEGBufSize(width, height, jpegSubsamp);
100 if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
101 if (retval > (size_t)INT_MAX)
102 THROW_ARG("Image is too large");
108 /* TurboJPEG 1.4.x: TJ::bufSizeYUV() */
109 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII
110 (JNIEnv *env, jclass cls, jint width, jint align, jint height, jint subsamp)
112 size_t retval = tj3YUVBufSize(width, align, height, subsamp);
114 if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
115 if (retval > (size_t)INT_MAX)
116 THROW_ARG("Image is too large");
122 /* TurboJPEG 1.4.x: TJ::planeSizeYUV() */
123 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII
124 (JNIEnv *env, jclass cls, jint componentID, jint width, jint stride,
125 jint height, jint subsamp)
127 size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp);
129 if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
130 if (retval > (size_t)INT_MAX)
131 THROW_ARG("Image is too large");
137 /* TurboJPEG 1.4.x: TJ::planeWidth() */
138 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeWidth__III
139 (JNIEnv *env, jclass cls, jint componentID, jint width, jint subsamp)
141 jint retval = (jint)tj3YUVPlaneWidth(componentID, width, subsamp);
143 if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
149 /* TurboJPEG 1.4.x: TJ::planeHeight() */
150 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeHeight__III
151 (JNIEnv *env, jclass cls, jint componentID, jint height, jint subsamp)
153 jint retval = (jint)tj3YUVPlaneHeight(componentID, height, subsamp);
155 if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
161 /* TurboJPEG 1.2.x: TJCompressor::init() */
162 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_init
163 (JNIEnv *env, jobject obj)
169 if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
170 THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
172 BAILIF0(cls = (*env)->GetObjectClass(env, obj));
173 BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
174 (*env)->SetLongField(env, obj, fid, (size_t)handle);
180 /* TurboJPEG 3: TJCompressor::set() */
181 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_set
182 (JNIEnv *env, jobject obj, jint param, jint value)
188 if (tj3Set(handle, param, value) == -1)
195 /* TurboJPEG 3: TJCompressor::get() */
196 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_get
197 (JNIEnv *env, jobject obj, jint param)
203 return tj3Get(handle, param);
209 static jint TJCompressor_compress
210 (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint precision,
211 jint x, jint y, jint width, jint pitch, jint height, jint pf,
216 jsize arraySize = 0, actualPitch;
218 unsigned char *jpegBuf = NULL;
223 if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
224 height < 1 || pitch < 0)
225 THROW_ARG("Invalid argument in compress*()");
226 if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
227 THROW_ARG("Mismatch between Java and C API");
229 actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
230 arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
231 if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
232 THROW_ARG("Source buffer is not large enough");
233 jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
234 if (tj3Get(handle, TJPARAM_LOSSLESS) && jpegSubsamp != TJSAMP_GRAY)
235 jpegSubsamp = TJSAMP_444;
236 else if (jpegSubsamp == TJSAMP_UNKNOWN)
237 THROW_ARG("TJPARAM_SUBSAMP must be specified");
238 jpegSize = tj3JPEGBufSize(width, height, jpegSubsamp);
239 if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
240 THROW_ARG("Destination buffer is not large enough");
242 if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
245 BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
246 BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
248 if (precision == 8) {
249 if (tj3Compress8(handle, &((unsigned char *)srcBuf)[y * actualPitch +
250 x * tjPixelSize[pf]],
251 width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
252 SAFE_RELEASE(dst, jpegBuf);
253 SAFE_RELEASE(src, srcBuf);
256 } else if (precision == 12) {
257 if (tj3Compress12(handle, &((short *)srcBuf)[y * actualPitch +
258 x * tjPixelSize[pf]],
259 width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
260 SAFE_RELEASE(dst, jpegBuf);
261 SAFE_RELEASE(src, srcBuf);
265 if (tj3Compress16(handle, &((unsigned short *)srcBuf)[y * actualPitch +
266 x * tjPixelSize[pf]],
267 width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
268 SAFE_RELEASE(dst, jpegBuf);
269 SAFE_RELEASE(src, srcBuf);
275 SAFE_RELEASE(dst, jpegBuf);
276 SAFE_RELEASE(src, srcBuf);
277 return (jint)jpegSize;
280 /* TurboJPEG 3: TJCompressor::compress8() byte source */
281 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3BIIIIII_3B
282 (JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width,
283 jint pitch, jint height, jint pf, jbyteArray dst)
285 return TJCompressor_compress(env, obj, src, 1, 8, x, y, width, pitch, height,
289 /* TurboJPEG 3: TJCompressor::compress12() */
290 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress12
291 (JNIEnv *env, jobject obj, jshortArray src, jint x, jint y, jint width,
292 jint pitch, jint height, jint pf, jbyteArray dst)
294 return TJCompressor_compress(env, obj, src, 1, 12, x, y, width, pitch,
298 /* TurboJPEG 3: TJCompressor::compress16() */
299 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress16
300 (JNIEnv *env, jobject obj, jshortArray src, jint x, jint y, jint width,
301 jint pitch, jint height, jint pf, jbyteArray dst)
303 return TJCompressor_compress(env, obj, src, 1, 16, x, y, width, pitch,
307 /* TurboJPEG 3: TJCompressor::compress8() int source */
308 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3IIIIIII_3B
309 (JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width,
310 jint stride, jint height, jint pf, jbyteArray dst)
312 if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
313 THROW_ARG("Invalid argument in compress8()");
314 if (tjPixelSize[pf] != sizeof(jint))
315 THROW_ARG("Pixel format must be 32-bit when compressing from an integer buffer.");
317 return TJCompressor_compress(env, obj, src, sizeof(jint), 8, x, y, width,
318 stride * sizeof(jint), height, pf, dst);
324 /* TurboJPEG 3: TJCompressor::compressFromYUV8() */
325 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV8
326 (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
327 jint width, jintArray jSrcStrides, jint height, jbyteArray dst)
331 jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL };
332 const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL };
333 const unsigned char *srcPlanes[3] = { NULL, NULL, NULL };
334 jint srcOffsetsTmp[3] = { 0, 0, 0 }, srcStridesTmp[3] = { 0, 0, 0 };
335 int srcOffsets[3] = { 0, 0, 0 }, srcStrides[3] = { 0, 0, 0 };
336 unsigned char *jpegBuf = NULL;
337 int nc = 0, i, subsamp;
341 if (org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
342 THROW_ARG("Mismatch between Java and C API");
344 if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
345 THROW_ARG("TJPARAM_SUBSAMP must be specified");
346 nc = subsamp == TJSAMP_GRAY ? 1 : 3;
347 if ((*env)->GetArrayLength(env, srcobjs) < nc)
348 THROW_ARG("Planes array is too small for the subsampling type");
349 if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
350 THROW_ARG("Offsets array is too small for the subsampling type");
351 if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
352 THROW_ARG("Strides array is too small for the subsampling type");
354 jpegSize = tj3JPEGBufSize(width, height, subsamp);
355 if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
356 THROW_ARG("Destination buffer is not large enough");
358 if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
361 (*env)->GetIntArrayRegion(env, jSrcOffsets, 0, nc, srcOffsetsTmp);
362 if ((*env)->ExceptionCheck(env)) goto bailout;
363 for (i = 0; i < 3; i++)
364 srcOffsets[i] = srcOffsetsTmp[i];
366 (*env)->GetIntArrayRegion(env, jSrcStrides, 0, nc, srcStridesTmp);
367 if ((*env)->ExceptionCheck(env)) goto bailout;
368 for (i = 0; i < 3; i++)
369 srcStrides[i] = srcStridesTmp[i];
371 for (i = 0; i < nc; i++) {
372 size_t planeSize = tj3YUVPlaneSize(i, width, srcStrides[i], height,
374 int pw = tj3YUVPlaneWidth(i, width, subsamp);
376 if (planeSize == 0 || pw == 0)
377 THROW_ARG(tj3GetErrorStr(NULL));
379 if (planeSize > (size_t)INT_MAX)
380 THROW_ARG("Source plane is too large");
381 if (srcOffsets[i] < 0)
382 THROW_ARG("Invalid argument in compressFromYUV8()");
383 if (srcStrides[i] < 0 && srcOffsets[i] - (int)planeSize + pw < 0)
384 THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
386 BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
387 if ((*env)->GetArrayLength(env, jSrcPlanes[i]) <
388 srcOffsets[i] + (int)planeSize)
389 THROW_ARG("Source plane is not large enough");
391 for (i = 0; i < nc; i++) {
392 BAILIF0NOEC(srcPlanesTmp[i] =
393 (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
394 srcPlanes[i] = &srcPlanesTmp[i][srcOffsets[i]];
396 BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
398 if (tj3CompressFromYUVPlanes8(handle, srcPlanes, width, srcStrides, height,
399 &jpegBuf, &jpegSize) == -1) {
400 SAFE_RELEASE(dst, jpegBuf);
401 for (i = 0; i < nc; i++)
402 SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
407 SAFE_RELEASE(dst, jpegBuf);
408 for (i = 0; i < nc; i++)
409 SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
410 return (jint)jpegSize;
413 static void TJCompressor_encodeYUV8
414 (JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint x, jint y,
415 jint width, jint pitch, jint height, jint pf, jobjectArray dstobjs,
416 jintArray jDstOffsets, jintArray jDstStrides)
419 jsize arraySize = 0, actualPitch;
420 unsigned char *srcBuf = NULL;
421 jbyteArray jDstPlanes[3] = { NULL, NULL, NULL };
422 unsigned char *dstPlanesTmp[3] = { NULL, NULL, NULL };
423 unsigned char *dstPlanes[3] = { NULL, NULL, NULL };
424 jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 };
425 int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 };
426 int nc = 0, i, subsamp;
430 if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
431 height < 1 || pitch < 0)
432 THROW_ARG("Invalid argument in encodeYUV8()");
433 if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
434 org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
435 THROW_ARG("Mismatch between Java and C API");
437 if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
438 THROW_ARG("TJPARAM_SUBSAMP must be specified");
439 nc = subsamp == TJSAMP_GRAY ? 1 : 3;
440 if ((*env)->GetArrayLength(env, dstobjs) < nc)
441 THROW_ARG("Planes array is too small for the subsampling type");
442 if ((*env)->GetArrayLength(env, jDstOffsets) < nc)
443 THROW_ARG("Offsets array is too small for the subsampling type");
444 if ((*env)->GetArrayLength(env, jDstStrides) < nc)
445 THROW_ARG("Strides array is too small for the subsampling type");
447 actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
448 arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
449 if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
450 THROW_ARG("Source buffer is not large enough");
452 (*env)->GetIntArrayRegion(env, jDstOffsets, 0, nc, dstOffsetsTmp);
453 if ((*env)->ExceptionCheck(env)) goto bailout;
454 for (i = 0; i < 3; i++)
455 dstOffsets[i] = dstOffsetsTmp[i];
457 (*env)->GetIntArrayRegion(env, jDstStrides, 0, nc, dstStridesTmp);
458 if ((*env)->ExceptionCheck(env)) goto bailout;
459 for (i = 0; i < 3; i++)
460 dstStrides[i] = dstStridesTmp[i];
462 for (i = 0; i < nc; i++) {
463 size_t planeSize = tj3YUVPlaneSize(i, width, dstStrides[i], height,
465 int pw = tj3YUVPlaneWidth(i, width, subsamp);
467 if (planeSize == 0 || pw == 0)
468 THROW_ARG(tj3GetErrorStr(NULL));
470 if (planeSize > (size_t)INT_MAX)
471 THROW_ARG("Destination plane is too large");
472 if (dstOffsets[i] < 0)
473 THROW_ARG("Invalid argument in encodeYUV8()");
474 if (dstStrides[i] < 0 && dstOffsets[i] - (int)planeSize + pw < 0)
475 THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
477 BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
478 if ((*env)->GetArrayLength(env, jDstPlanes[i]) <
479 dstOffsets[i] + (int)planeSize)
480 THROW_ARG("Destination plane is not large enough");
482 for (i = 0; i < nc; i++) {
483 BAILIF0NOEC(dstPlanesTmp[i] =
484 (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
485 dstPlanes[i] = &dstPlanesTmp[i][dstOffsets[i]];
487 BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
489 if (tj3EncodeYUVPlanes8(handle,
490 &srcBuf[y * actualPitch + x * tjPixelSize[pf]],
491 width, pitch, height, pf, dstPlanes,
493 SAFE_RELEASE(src, srcBuf);
494 for (i = 0; i < nc; i++)
495 SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
500 SAFE_RELEASE(src, srcBuf);
501 for (i = 0; i < nc; i++)
502 SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
505 /* TurboJPEG 3: TJCompressor::encodeYUV8() byte source */
506 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3BIIIIII_3_3B_3I_3I
507 (JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width,
508 jint pitch, jint height, jint pf, jobjectArray dstobjs,
509 jintArray jDstOffsets, jintArray jDstStrides)
511 TJCompressor_encodeYUV8(env, obj, src, 1, x, y, width, pitch, height, pf,
512 dstobjs, jDstOffsets, jDstStrides);
515 /* TurboJPEG 3: TJCompressor::encodeYUV8() int source */
516 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3IIIIIII_3_3B_3I_3I
517 (JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width,
518 jint stride, jint height, jint pf, jobjectArray dstobjs,
519 jintArray jDstOffsets, jintArray jDstStrides)
521 if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
522 THROW_ARG("Invalid argument in encodeYUV8()");
523 if (tjPixelSize[pf] != sizeof(jint))
524 THROW_ARG("Pixel format must be 32-bit when encoding from an integer buffer.");
526 TJCompressor_encodeYUV8(env, obj, src, sizeof(jint), x, y, width,
527 stride * sizeof(jint), height, pf, dstobjs,
528 jDstOffsets, jDstStrides);
534 /* TurboJPEG 1.2.x: TJCompressor::destroy() */
535 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy
536 (JNIEnv *env, jobject obj)
543 (*env)->SetLongField(env, obj, _fid, 0);
549 /* TurboJPEG 1.2.x: TJDecompressor::init() */
550 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_init
551 (JNIEnv *env, jobject obj)
557 if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL)
558 THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
560 BAILIF0(cls = (*env)->GetObjectClass(env, obj));
561 BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
562 (*env)->SetLongField(env, obj, fid, (size_t)handle);
568 /* TurboJPEG 3: TJDecompressor::set() */
569 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_set
570 (JNIEnv *env, jobject obj, jint param, jint value)
572 Java_org_libjpegturbo_turbojpeg_TJCompressor_set(env, obj, param, value);
575 /* TurboJPEG 3: TJDecompressor::get() */
576 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_get
577 (JNIEnv *env, jobject obj, jint param)
579 return Java_org_libjpegturbo_turbojpeg_TJCompressor_get(env, obj, param);
582 /* TurboJPEG 1.2.x: TJDecompressor::getScalingFactors() */
583 JNIEXPORT jobjectArray JNICALL Java_org_libjpegturbo_turbojpeg_TJ_getScalingFactors
584 (JNIEnv *env, jclass cls)
588 tjscalingfactor *sf = NULL;
590 jobject sfobj = NULL;
591 jobjectArray sfjava = NULL;
593 if ((sf = tj3GetScalingFactors(&n)) == NULL || n == 0)
594 THROW_ARG(tj3GetErrorStr(NULL));
596 BAILIF0(sfcls = (*env)->FindClass(env,
597 "org/libjpegturbo/turbojpeg/TJScalingFactor"));
598 BAILIF0(sfjava = (jobjectArray)(*env)->NewObjectArray(env, n, sfcls, 0));
600 for (i = 0; i < n; i++) {
601 BAILIF0(sfobj = (*env)->AllocObject(env, sfcls));
602 BAILIF0(fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
603 (*env)->SetIntField(env, sfobj, fid, sf[i].num);
604 BAILIF0(fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
605 (*env)->SetIntField(env, sfobj, fid, sf[i].denom);
606 (*env)->SetObjectArrayElement(env, sfjava, i, sfobj);
613 /* TurboJPEG 1.2.x: TJDecompressor::decompressHeader() */
614 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressHeader
615 (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize)
618 unsigned char *jpegBuf = NULL;
622 if ((*env)->GetArrayLength(env, src) < jpegSize)
623 THROW_ARG("Source buffer is not large enough");
625 BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
627 if (tj3DecompressHeader(handle, jpegBuf, (size_t)jpegSize) == -1) {
628 SAFE_RELEASE(src, jpegBuf);
633 SAFE_RELEASE(src, jpegBuf);
636 /* TurboJPEG 3: TJDecompressor::setCroppingRegion() */
637 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion
638 (JNIEnv *env, jobject obj)
642 jobject sfobj, crobj;
643 tjregion croppingRegion;
644 tjscalingfactor scalingFactor;
648 BAILIF0(sfcls = (*env)->FindClass(env,
649 "org/libjpegturbo/turbojpeg/TJScalingFactor"));
651 (*env)->GetFieldID(env, _cls, "scalingFactor",
652 "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
653 BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
654 BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
655 scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
656 BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
657 scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
659 if (tj3SetScalingFactor(handle, scalingFactor) == -1)
662 BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle"));
663 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion",
664 "Ljava/awt/Rectangle;"));
665 BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid));
666 BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I"));
667 croppingRegion.x = (*env)->GetIntField(env, crobj, _fid);
668 BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I"));
669 croppingRegion.y = (*env)->GetIntField(env, crobj, _fid);
670 BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I"));
671 croppingRegion.w = (*env)->GetIntField(env, crobj, _fid);
672 BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I"));
673 croppingRegion.h = (*env)->GetIntField(env, crobj, _fid);
675 if (tj3SetCroppingRegion(handle, croppingRegion) == -1)
682 static void TJDecompressor_decompress
683 (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jarray dst,
684 jint dstElementSize, int precision, jint x, jint y, jint pitch, jint pf)
687 jsize arraySize = 0, actualPitch;
688 unsigned char *jpegBuf = NULL;
691 jobject sfobj, crobj;
692 tjscalingfactor scalingFactor;
694 int jpegWidth, jpegHeight, scaledWidth, scaledHeight;
698 if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
699 THROW_ARG("Invalid argument in decompress*()");
700 if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
701 THROW_ARG("Mismatch between Java and C API");
703 if ((*env)->GetArrayLength(env, src) < jpegSize)
704 THROW_ARG("Source buffer is not large enough");
705 if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
706 THROW_ARG("JPEG header has not yet been read");
707 if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
708 THROW_ARG("JPEG header has not yet been read");
710 BAILIF0(sfcls = (*env)->FindClass(env,
711 "org/libjpegturbo/turbojpeg/TJScalingFactor"));
713 (*env)->GetFieldID(env, _cls, "scalingFactor",
714 "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
715 BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
716 BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
717 scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
718 BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
719 scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
721 if (tj3SetScalingFactor(handle, scalingFactor) == -1)
723 scaledWidth = TJSCALED(jpegWidth, scalingFactor);
724 scaledHeight = TJSCALED(jpegHeight, scalingFactor);
726 BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle"));
727 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion",
728 "Ljava/awt/Rectangle;"));
729 BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid));
730 BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I"));
731 cr.x = (*env)->GetIntField(env, crobj, _fid);
732 BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I"));
733 cr.y = (*env)->GetIntField(env, crobj, _fid);
734 BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I"));
735 cr.w = (*env)->GetIntField(env, crobj, _fid);
736 BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I"));
737 cr.h = (*env)->GetIntField(env, crobj, _fid);
738 if (cr.x != 0 || cr.y != 0 || cr.w != 0 || cr.h != 0) {
739 scaledWidth = cr.w ? cr.w : scaledWidth - cr.x;
740 scaledHeight = cr.h ? cr.h : scaledHeight - cr.y;
743 actualPitch = (pitch == 0) ? scaledWidth * tjPixelSize[pf] : pitch;
744 arraySize = (y + scaledHeight - 1) * actualPitch +
745 (x + scaledWidth) * tjPixelSize[pf];
746 if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
747 THROW_ARG("Destination buffer is not large enough");
749 BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
750 BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
752 if (precision == 8) {
753 if (tj3Decompress8(handle, jpegBuf, (size_t)jpegSize,
754 &((unsigned char *)dstBuf)[y * actualPitch +
755 x * tjPixelSize[pf]],
757 SAFE_RELEASE(dst, dstBuf);
758 SAFE_RELEASE(src, jpegBuf);
761 } else if (precision == 12) {
762 if (tj3Decompress12(handle, jpegBuf, (size_t)jpegSize,
763 &((short *)dstBuf)[y * actualPitch +
764 x * tjPixelSize[pf]],
766 SAFE_RELEASE(dst, dstBuf);
767 SAFE_RELEASE(src, jpegBuf);
771 if (tj3Decompress16(handle, jpegBuf, (size_t)jpegSize,
772 &((unsigned short *)dstBuf)[y * actualPitch +
773 x * tjPixelSize[pf]],
775 SAFE_RELEASE(dst, dstBuf);
776 SAFE_RELEASE(src, jpegBuf);
782 SAFE_RELEASE(dst, dstBuf);
783 SAFE_RELEASE(src, jpegBuf);
786 /* TurboJPEG 3: TJDecompressor::decompress8() byte destination */
787 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3BIIII
788 (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst,
789 jint x, jint y, jint pitch, jint pf)
791 TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 8, x, y, pitch,
795 /* TurboJPEG 3: TJDecompressor::decompress12() */
796 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress12
797 (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jshortArray dst,
798 jint x, jint y, jint pitch, jint pf)
800 TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 12, x, y, pitch,
804 /* TurboJPEG 3: TJDecompressor::decompress16() */
805 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress16
806 (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jshortArray dst,
807 jint x, jint y, jint pitch, jint pf)
809 TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 16, x, y, pitch,
813 /* TurboJPEG 3: TJDecompressor::decompress8() int destination */
814 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3IIIII
815 (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jintArray dst,
816 jint x, jint y, jint stride, jint pf)
818 if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
819 THROW_ARG("Invalid argument in decompress8()");
820 if (tjPixelSize[pf] != sizeof(jint))
821 THROW_ARG("Pixel format must be 32-bit when decompressing to an integer buffer.");
823 TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), 8, x,
824 y, stride * sizeof(jint), pf);
830 /* TurboJPEG 3: TJDecompressor::decompressToYUV8() */
831 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV8
832 (JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize,
833 jobjectArray dstobjs, jintArray jDstOffsets, jintArray jDstStrides)
836 unsigned char *jpegBuf = NULL;
837 jbyteArray jDstPlanes[3] = { NULL, NULL, NULL };
838 unsigned char *dstPlanesTmp[3] = { NULL, NULL, NULL };
839 unsigned char *dstPlanes[3] = { NULL, NULL, NULL };
840 jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 };
841 int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 };
844 int jpegSubsamp, jpegWidth = 0, jpegHeight = 0;
845 int nc = 0, i, scaledWidth, scaledHeight;
846 tjscalingfactor scalingFactor;
850 if ((*env)->GetArrayLength(env, src) < jpegSize)
851 THROW_ARG("Source buffer is not large enough");
852 if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
853 THROW_ARG("JPEG header has not yet been read");
854 if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
855 THROW_ARG("JPEG header has not yet been read");
857 BAILIF0(sfcls = (*env)->FindClass(env,
858 "org/libjpegturbo/turbojpeg/TJScalingFactor"));
860 (*env)->GetFieldID(env, _cls, "scalingFactor",
861 "Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
862 BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
863 BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
864 scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
865 BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
866 scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
868 if (tj3SetScalingFactor(handle, scalingFactor) == -1)
870 scaledWidth = TJSCALED(jpegWidth, scalingFactor);
871 scaledHeight = TJSCALED(jpegHeight, scalingFactor);
873 if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
874 THROW_ARG("TJPARAM_SUBSAMP must be specified");
875 nc = jpegSubsamp == TJSAMP_GRAY ? 1 : 3;
877 (*env)->GetIntArrayRegion(env, jDstOffsets, 0, nc, dstOffsetsTmp);
878 if ((*env)->ExceptionCheck(env)) goto bailout;
879 for (i = 0; i < 3; i++)
880 dstOffsets[i] = dstOffsetsTmp[i];
882 (*env)->GetIntArrayRegion(env, jDstStrides, 0, nc, dstStridesTmp);
883 if ((*env)->ExceptionCheck(env)) goto bailout;
884 for (i = 0; i < 3; i++)
885 dstStrides[i] = dstStridesTmp[i];
887 for (i = 0; i < nc; i++) {
888 size_t planeSize = tj3YUVPlaneSize(i, scaledWidth, dstStrides[i],
889 scaledHeight, jpegSubsamp);
890 int pw = tj3YUVPlaneWidth(i, scaledWidth, jpegSubsamp);
892 if (planeSize == 0 || pw == 0)
893 THROW_ARG(tj3GetErrorStr(NULL));
895 if (planeSize > (size_t)INT_MAX)
896 THROW_ARG("Destination plane is too large");
897 if (dstOffsets[i] < 0)
898 THROW_ARG("Invalid argument in decompressToYUV8()");
899 if (dstStrides[i] < 0 && dstOffsets[i] - (int)planeSize + pw < 0)
900 THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
902 BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
903 if ((*env)->GetArrayLength(env, jDstPlanes[i]) <
904 dstOffsets[i] + (int)planeSize)
905 THROW_ARG("Destination plane is not large enough");
907 for (i = 0; i < nc; i++) {
908 BAILIF0NOEC(dstPlanesTmp[i] =
909 (*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
910 dstPlanes[i] = &dstPlanesTmp[i][dstOffsets[i]];
912 BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
914 if (tj3DecompressToYUVPlanes8(handle, jpegBuf, (size_t)jpegSize, dstPlanes,
916 SAFE_RELEASE(src, jpegBuf);
917 for (i = 0; i < nc; i++)
918 SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
923 SAFE_RELEASE(src, jpegBuf);
924 for (i = 0; i < nc; i++)
925 SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
928 static void TJDecompressor_decodeYUV8
929 (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
930 jintArray jSrcStrides, jarray dst, jint dstElementSize, jint x, jint y,
931 jint width, jint pitch, jint height, jint pf)
934 jsize arraySize = 0, actualPitch;
935 jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL };
936 const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL };
937 const unsigned char *srcPlanes[3] = { NULL, NULL, NULL };
938 jint srcOffsetsTmp[3] = { 0, 0, 0 }, srcStridesTmp[3] = { 0, 0, 0 };
939 int srcOffsets[3] = { 0, 0, 0 }, srcStrides[3] = { 0, 0, 0 };
940 unsigned char *dstBuf = NULL;
941 int nc = 0, i, subsamp;
945 if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
946 THROW_ARG("Invalid argument in decodeYUV8()");
947 if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
948 org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
949 THROW_ARG("Mismatch between Java and C API");
951 if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
952 THROW_ARG("TJPARAM_SUBSAMP must be specified");
953 nc = subsamp == TJSAMP_GRAY ? 1 : 3;
954 if ((*env)->GetArrayLength(env, srcobjs) < nc)
955 THROW_ARG("Planes array is too small for the subsampling type");
956 if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
957 THROW_ARG("Offsets array is too small for the subsampling type");
958 if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
959 THROW_ARG("Strides array is too small for the subsampling type");
961 actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
962 arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
963 if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
964 THROW_ARG("Destination buffer is not large enough");
966 (*env)->GetIntArrayRegion(env, jSrcOffsets, 0, nc, srcOffsetsTmp);
967 if ((*env)->ExceptionCheck(env)) goto bailout;
968 for (i = 0; i < 3; i++)
969 srcOffsets[i] = srcOffsetsTmp[i];
971 (*env)->GetIntArrayRegion(env, jSrcStrides, 0, nc, srcStridesTmp);
972 if ((*env)->ExceptionCheck(env)) goto bailout;
973 for (i = 0; i < 3; i++)
974 srcStrides[i] = srcStridesTmp[i];
976 for (i = 0; i < nc; i++) {
977 size_t planeSize = tj3YUVPlaneSize(i, width, srcStrides[i], height,
979 int pw = tj3YUVPlaneWidth(i, width, subsamp);
981 if (planeSize == 0 || pw == 0)
982 THROW_ARG(tj3GetErrorStr(NULL));
984 if (planeSize > (size_t)INT_MAX)
985 THROW_ARG("Source plane is too large");
986 if (srcOffsets[i] < 0)
987 THROW_ARG("Invalid argument in decodeYUV8()");
988 if (srcStrides[i] < 0 && srcOffsets[i] - (int)planeSize + pw < 0)
989 THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
991 BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
992 if ((*env)->GetArrayLength(env, jSrcPlanes[i]) <
993 srcOffsets[i] + (int)planeSize)
994 THROW_ARG("Source plane is not large enough");
996 for (i = 0; i < nc; i++) {
997 BAILIF0NOEC(srcPlanesTmp[i] =
998 (*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
999 srcPlanes[i] = &srcPlanesTmp[i][srcOffsets[i]];
1001 BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
1003 if (tj3DecodeYUVPlanes8(handle, srcPlanes, srcStrides,
1004 &dstBuf[y * actualPitch + x * tjPixelSize[pf]],
1005 width, pitch, height, pf) == -1) {
1006 SAFE_RELEASE(dst, dstBuf);
1007 for (i = 0; i < nc; i++)
1008 SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
1013 SAFE_RELEASE(dst, dstBuf);
1014 for (i = 0; i < nc; i++)
1015 SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
1018 /* TurboJPEG 3: TJDecompressor::decodeYUV8() byte destination */
1019 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3BIIIIII
1020 (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
1021 jintArray jSrcStrides, jbyteArray dst, jint x, jint y, jint width,
1022 jint pitch, jint height, jint pf)
1024 TJDecompressor_decodeYUV8(env, obj, srcobjs, jSrcOffsets, jSrcStrides, dst,
1025 1, x, y, width, pitch, height, pf);
1028 /* TurboJPEG 3: TJDecompressor::decodeYUV8() int destination */
1029 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3IIIIIII
1030 (JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
1031 jintArray jSrcStrides, jintArray dst, jint x, jint y, jint width,
1032 jint stride, jint height, jint pf)
1034 if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
1035 THROW_ARG("Invalid argument in decodeYUV8()");
1036 if (tjPixelSize[pf] != sizeof(jint))
1037 THROW_ARG("Pixel format must be 32-bit when decoding to an integer buffer.");
1039 TJDecompressor_decodeYUV8(env, obj, srcobjs, jSrcOffsets, jSrcStrides, dst,
1040 sizeof(jint), x, y, width, stride * sizeof(jint),
1047 /* TurboJPEG 1.2.x: TJTransformer::init() */
1048 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_init
1049 (JNIEnv *env, jobject obj)
1055 if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL)
1056 THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
1058 BAILIF0(cls = (*env)->GetObjectClass(env, obj));
1059 BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
1060 (*env)->SetLongField(env, obj, fid, (size_t)handle);
1066 typedef struct _JNICustomFilterParams {
1070 } JNICustomFilterParams;
1072 static int JNICustomFilter(short *coeffs, tjregion arrayRegion,
1073 tjregion planeRegion, int componentIndex,
1074 int transformIndex, tjtransform *transform)
1076 JNICustomFilterParams *params = (JNICustomFilterParams *)transform->data;
1077 JNIEnv *env = params->env;
1078 jobject tobj = params->tobj, cfobj = params->cfobj;
1079 jobject arrayRegionObj, planeRegionObj, bufobj, borobj;
1084 BAILIF0(bufobj = (*env)->NewDirectByteBuffer(env, coeffs,
1085 sizeof(short) * arrayRegion.w * arrayRegion.h));
1086 BAILIF0(cls = (*env)->FindClass(env, "java/nio/ByteOrder"));
1087 BAILIF0(mid = (*env)->GetStaticMethodID(env, cls, "nativeOrder",
1088 "()Ljava/nio/ByteOrder;"));
1089 BAILIF0(borobj = (*env)->CallStaticObjectMethod(env, cls, mid));
1090 BAILIF0(cls = (*env)->GetObjectClass(env, bufobj));
1091 BAILIF0(mid = (*env)->GetMethodID(env, cls, "order",
1092 "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"));
1093 (*env)->CallObjectMethod(env, bufobj, mid, borobj);
1094 BAILIF0(mid = (*env)->GetMethodID(env, cls, "asShortBuffer",
1095 "()Ljava/nio/ShortBuffer;"));
1096 BAILIF0(bufobj = (*env)->CallObjectMethod(env, bufobj, mid));
1098 BAILIF0(cls = (*env)->FindClass(env, "java/awt/Rectangle"));
1099 BAILIF0(arrayRegionObj = (*env)->AllocObject(env, cls));
1100 BAILIF0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
1101 (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.x);
1102 BAILIF0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
1103 (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.y);
1104 BAILIF0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
1105 (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.w);
1106 BAILIF0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
1107 (*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.h);
1109 BAILIF0(planeRegionObj = (*env)->AllocObject(env, cls));
1110 BAILIF0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
1111 (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.x);
1112 BAILIF0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
1113 (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.y);
1114 BAILIF0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
1115 (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.w);
1116 BAILIF0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
1117 (*env)->SetIntField(env, planeRegionObj, fid, planeRegion.h);
1119 BAILIF0(cls = (*env)->GetObjectClass(env, cfobj));
1120 BAILIF0(mid = (*env)->GetMethodID(env, cls, "customFilter",
1121 "(Ljava/nio/ShortBuffer;Ljava/awt/Rectangle;Ljava/awt/Rectangle;IILorg/libjpegturbo/turbojpeg/TJTransform;)V"));
1122 (*env)->CallVoidMethod(env, cfobj, mid, bufobj, arrayRegionObj,
1123 planeRegionObj, componentIndex, transformIndex, tobj);
1131 /* TurboJPEG 1.2.x: TJTransformer::transform() */
1132 JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transform
1133 (JNIEnv *env, jobject obj, jbyteArray jsrcBuf, jint jpegSize,
1134 jobjectArray dstobjs, jobjectArray tobjs)
1136 tjhandle handle = 0;
1137 unsigned char *jpegBuf = NULL, **dstBufs = NULL;
1139 size_t *dstSizes = NULL;
1140 tjtransform *t = NULL;
1141 jbyteArray *jdstBufs = NULL;
1142 int i, jpegWidth = 0, jpegHeight = 0, jpegSubsamp;
1143 jintArray jdstSizes = 0;
1144 jint *dstSizesi = NULL;
1145 JNICustomFilterParams *params = NULL;
1149 if ((*env)->GetArrayLength(env, jsrcBuf) < jpegSize)
1150 THROW_ARG("Source buffer is not large enough");
1151 if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
1152 THROW_ARG("JPEG header has not yet been read");
1153 if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
1154 THROW_ARG("JPEG header has not yet been read");
1155 if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
1156 THROW_ARG("TJPARAM_SUBSAMP must be specified");
1158 n = (*env)->GetArrayLength(env, dstobjs);
1159 if (n != (*env)->GetArrayLength(env, tobjs))
1160 THROW_ARG("Mismatch between size of transforms array and destination buffers array");
1163 (unsigned char **)malloc(sizeof(unsigned char *) * n)) == NULL)
1165 if ((jdstBufs = (jbyteArray *)malloc(sizeof(jbyteArray) * n)) == NULL)
1167 if ((dstSizes = (size_t *)malloc(sizeof(size_t) * n)) == NULL)
1169 if ((t = (tjtransform *)malloc(sizeof(tjtransform) * n)) == NULL)
1171 if ((params = (JNICustomFilterParams *)malloc(sizeof(JNICustomFilterParams) *
1174 for (i = 0; i < n; i++) {
1175 dstBufs[i] = NULL; jdstBufs[i] = NULL; dstSizes[i] = 0;
1176 memset(&t[i], 0, sizeof(tjtransform));
1177 memset(¶ms[i], 0, sizeof(JNICustomFilterParams));
1180 for (i = 0; i < n; i++) {
1181 jobject tobj, cfobj;
1183 BAILIF0(tobj = (*env)->GetObjectArrayElement(env, tobjs, i));
1184 BAILIF0(_cls = (*env)->GetObjectClass(env, tobj));
1185 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "op", "I"));
1186 t[i].op = (*env)->GetIntField(env, tobj, _fid);
1187 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "options", "I"));
1188 t[i].options = (*env)->GetIntField(env, tobj, _fid);
1189 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "x", "I"));
1190 t[i].r.x = (*env)->GetIntField(env, tobj, _fid);
1191 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "y", "I"));
1192 t[i].r.y = (*env)->GetIntField(env, tobj, _fid);
1193 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "width", "I"));
1194 t[i].r.w = (*env)->GetIntField(env, tobj, _fid);
1195 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "height", "I"));
1196 t[i].r.h = (*env)->GetIntField(env, tobj, _fid);
1198 BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "cf",
1199 "Lorg/libjpegturbo/turbojpeg/TJCustomFilter;"));
1200 cfobj = (*env)->GetObjectField(env, tobj, _fid);
1202 params[i].env = env;
1203 params[i].tobj = tobj;
1204 params[i].cfobj = cfobj;
1205 t[i].customFilter = JNICustomFilter;
1206 t[i].data = (void *)¶ms[i];
1210 if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
1213 for (i = 0; i < n; i++) {
1214 int w = jpegWidth, h = jpegHeight;
1216 if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
1217 t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
1218 w = jpegHeight; h = jpegWidth;
1220 if (t[i].r.w != 0) w = t[i].r.w;
1221 if (t[i].r.h != 0) h = t[i].r.h;
1222 BAILIF0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
1223 if ((size_t)(*env)->GetArrayLength(env, jdstBufs[i]) <
1224 tj3JPEGBufSize(w, h, jpegSubsamp))
1225 THROW_ARG("Destination buffer is not large enough");
1227 BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
1228 for (i = 0; i < n; i++)
1229 BAILIF0NOEC(dstBufs[i] =
1230 (*env)->GetPrimitiveArrayCritical(env, jdstBufs[i], 0));
1232 if (tj3Transform(handle, jpegBuf, jpegSize, n, dstBufs, dstSizes, t) == -1) {
1233 for (i = 0; i < n; i++)
1234 SAFE_RELEASE(jdstBufs[i], dstBufs[i]);
1235 SAFE_RELEASE(jsrcBuf, jpegBuf);
1239 for (i = 0; i < n; i++)
1240 SAFE_RELEASE(jdstBufs[i], dstBufs[i]);
1241 SAFE_RELEASE(jsrcBuf, jpegBuf);
1243 jdstSizes = (*env)->NewIntArray(env, n);
1244 BAILIF0(dstSizesi = (*env)->GetIntArrayElements(env, jdstSizes, 0));
1245 for (i = 0; i < n; i++) dstSizesi[i] = (int)dstSizes[i];
1248 if (dstSizesi) (*env)->ReleaseIntArrayElements(env, jdstSizes, dstSizesi, 0);
1250 for (i = 0; i < n; i++) {
1251 if (dstBufs[i] && jdstBufs && jdstBufs[i])
1252 (*env)->ReleasePrimitiveArrayCritical(env, jdstBufs[i], dstBufs[i], 0);
1256 SAFE_RELEASE(jsrcBuf, jpegBuf);
1263 /* TurboJPEG 1.2.x: TJDecompressor::destroy() */
1264 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy
1265 (JNIEnv *env, jobject obj)
1267 Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy(env, obj);
1270 /* Private image I/O routines (used only by TJBench) */
1271 JNIEXPORT jobject JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_loadImage
1272 (JNIEnv *env, jobject obj, jint precision, jstring jfilename,
1273 jintArray jwidth, jint align, jintArray jheight, jintArray jpixelFormat)
1275 tjhandle handle = NULL;
1276 void *dstBuf = NULL, *jdstPtr;
1277 int width, *warr, height, *harr, pixelFormat, *pfarr, n;
1278 const char *filename = NULL;
1280 jobject jdstBuf = NULL;
1284 if ((precision != 8 && precision != 12 && precision != 16) ||
1285 jfilename == NULL || jwidth == NULL ||
1286 (*env)->GetArrayLength(env, jwidth) < 1 || jheight == NULL ||
1287 (*env)->GetArrayLength(env, jheight) < 1 || jpixelFormat == NULL ||
1288 (*env)->GetArrayLength(env, jpixelFormat) < 1)
1289 THROW_ARG("Invalid argument in loadImage()");
1291 BAILIF0NOEC(warr = (*env)->GetPrimitiveArrayCritical(env, jwidth, 0));
1293 (*env)->ReleasePrimitiveArrayCritical(env, jwidth, warr, 0);
1294 BAILIF0NOEC(harr = (*env)->GetPrimitiveArrayCritical(env, jheight, 0));
1296 (*env)->ReleasePrimitiveArrayCritical(env, jheight, harr, 0);
1297 BAILIF0NOEC(pfarr = (*env)->GetPrimitiveArrayCritical(env, jpixelFormat, 0));
1298 pixelFormat = pfarr[0];
1299 (*env)->ReleasePrimitiveArrayCritical(env, jpixelFormat, pfarr, 0);
1300 BAILIF0(filename = (*env)->GetStringUTFChars(env, jfilename, &isCopy));
1302 if (precision == 8) {
1303 if ((dstBuf = tj3LoadImage8(handle, filename, &width, align, &height,
1304 &pixelFormat)) == NULL)
1306 } else if (precision == 12) {
1307 if ((dstBuf = tj3LoadImage12(handle, filename, &width, align, &height,
1308 &pixelFormat)) == NULL)
1311 if ((dstBuf = tj3LoadImage16(handle, filename, &width, align, &height,
1312 &pixelFormat)) == NULL)
1316 (*env)->ReleaseStringUTFChars(env, jfilename, filename);
1319 if ((unsigned long long)width * (unsigned long long)height *
1320 (unsigned long long)tjPixelSize[pixelFormat] >
1321 (unsigned long long)((unsigned int)-1))
1322 THROW_ARG("Image is too large");
1324 BAILIF0NOEC(warr = (*env)->GetPrimitiveArrayCritical(env, jwidth, 0));
1326 (*env)->ReleasePrimitiveArrayCritical(env, jwidth, warr, 0);
1327 BAILIF0NOEC(harr = (*env)->GetPrimitiveArrayCritical(env, jheight, 0));
1329 (*env)->ReleasePrimitiveArrayCritical(env, jheight, harr, 0);
1330 BAILIF0NOEC(pfarr = (*env)->GetPrimitiveArrayCritical(env, jpixelFormat, 0));
1331 pfarr[0] = pixelFormat;
1332 (*env)->ReleasePrimitiveArrayCritical(env, jpixelFormat, pfarr, 0);
1334 n = width * height * tjPixelSize[pixelFormat];
1336 jdstBuf = (*env)->NewByteArray(env, n);
1338 jdstBuf = (*env)->NewShortArray(env, n);
1339 BAILIF0NOEC(jdstPtr = (*env)->GetPrimitiveArrayCritical(env, jdstBuf, 0));
1340 memcpy(jdstPtr, dstBuf, n * (precision > 8 ? 2 : 1));
1341 (*env)->ReleasePrimitiveArrayCritical(env, jdstBuf, jdstPtr, 0);
1344 if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);
1350 JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_saveImage
1351 (JNIEnv *env, jobject obj, jint precision, jstring jfilename,
1352 jobject jsrcBuf, jint width, jint pitch, jint height, jint pixelFormat)
1354 tjhandle handle = NULL;
1355 void *srcBuf = NULL, *jsrcPtr;
1356 const char *filename = NULL;
1362 if ((precision != 8 && precision != 12 && precision != 16) ||
1363 jfilename == NULL || jsrcBuf == NULL || width < 1 || height < 1 ||
1364 pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
1365 THROW_ARG("Invalid argument in saveImage()");
1367 if ((unsigned long long)width * (unsigned long long)height *
1368 (unsigned long long)tjPixelSize[pixelFormat] >
1369 (unsigned long long)((unsigned int)-1))
1370 THROW_ARG("Image is too large");
1371 n = width * height * tjPixelSize[pixelFormat];
1372 if ((*env)->GetArrayLength(env, jsrcBuf) < n)
1373 THROW_ARG("Source buffer is not large enough");
1375 if ((srcBuf = malloc(n * (precision > 8 ? 2 : 1))) == NULL)
1378 BAILIF0NOEC(jsrcPtr = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
1379 memcpy(srcBuf, jsrcPtr, n * (precision > 8 ? 2 : 1));
1380 (*env)->ReleasePrimitiveArrayCritical(env, jsrcBuf, jsrcPtr, 0);
1381 BAILIF0(filename = (*env)->GetStringUTFChars(env, jfilename, &isCopy));
1383 if (precision == 8) {
1384 if (tj3SaveImage8(handle, filename, srcBuf, width, pitch, height,
1387 } else if (precision == 12) {
1388 if (tj3SaveImage12(handle, filename, srcBuf, width, pitch, height,
1392 if (tj3SaveImage16(handle, filename, srcBuf, width, pitch, height,
1398 if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);