When used with TJPARAM_NOREALLOC and with TJXOP_TRANSPOSE, TJXOP_TRANSVERSE, TJXOP_ROT90, or TJXOP_ROT270, tj3Transform() incorrectly based the destination buffer size for a transform on the source image dimensions rather than the transformed image dimensions. This was apparently a long-standing bug that had existed in the tj*Transform() function since its inception. As initially implemented in the evolving libjpeg-turbo v1.2 code base, tjTransform() required dstSizes[i] to be set regardless of whether TJFLAG_NOREALLOC (the predecessor to TJPARAM_NOREALLOC) was set.ff78e37595, which was introduced later in the evolving libjpeg-turbo v1.2 code base, removed that requirement and planted the seed for the bug. However, the bug was not activated until9b49f0e4c7was introduced still later in the evolving libjpeg-turbo v1.2 code base, adding a subsampling type argument to the (new at the time) tjBufSize() function and thus making the width and height arguments no longer commutative. The bug opened up the possibility that a JPEG source image could cause tj3Transform() to overflow the destination buffer for a transform if all of the following were true: - The JPEG source image used 4:2:2, 4:4:0, 4:1:1, or 4:4:1 subsampling. (These are the only subsampling types for which the width and height arguments to tj3JPEGBufSize() are not commutative.) - The width and height of the JPEG source image were such that tj3JPEGBufSize(height, width, subsamplingType) returned a smaller value than tj3JPEGBufSize(width, height, subsamplingType). - The JPEG source image contained enough metadata that the size of the transformed image was larger than tj3JPEGBufSize(height, width, subsamplingType). - TJPARAM_NOREALLOC was set. - TJXOP_TRANSPOSE, TJXOP_TRANSVERSE, TJXOP_ROT90, or TJXOP_ROT270 was used. - TJXOPT_COPYNONE was not set. - TJXOPT_CROP was not set. - The calling program allocated tj3JPEGBufSize(height, width, subsamplingType) bytes for the destination buffer, as the API documentation instructs. The API documentation cautions that JPEG source images containing a large amount of extraneous metadata (EXIF, IPTC, ICC, etc.) cannot reliably be transformed if TJPARAM_NOREALLOC is set and TJXOPT_COPYNONE is not set. Irrespective of the bug, there are still cases in which a JPEG source image with a large amount of metadata can, when transformed, exceed the worst-case transformed JPEG image size. For instance, if you try to losslessly crop a JPEG image with 3 kB of EXIF data to 16x16 pixels, then you are guaranteed to exceed the worst-case 16x16 JPEG image size unless you discard the EXIF data. Even without the bug, tj3Transform() will still fail with "Buffer passed to JPEG library is too small" when attempting to transform JPEG source images that meet the aforementioned criteria. The bug is that the function segfaults rather than failing gracefully, but the chances of that occurring in a real-world application are very slim. Any real-world application developers who attempted to transform arbitrary JPEG source images with TJPARAM_NOREALLOC set would very quickly realize that they cannot reliably do that without also setting TJXOPT_COPYNONE. Thus, I posit that the actual risk posed by this bug is low. Applications such as web browsers that are the most exposed to security risks from arbitrary JPEG source images do not use the TurboJPEG lossless transform feature. (None of those applications even use the TurboJPEG API, to the best of my knowledge, and the public libjpeg API has no equivalent transform function.) Our only command-line interface to the tj3Transform() function, TJBench, was not exposed to the bug because it had a compatible bug whereby it allocated the JPEG destination buffer to the same size that tj3Transform() erroneously expected. The TurboJPEG Java API was also not exposed to the bug because of a similar compatible bug in the Java_org_libjpegturbo_turbojpeg_TJTransformer_transform() JNI function. (This commit fixes both compatible bugs.) In short, best practices for tj3Transform() are to use TJPARAM_NOREALLOC only with JPEG source images that are known to be free of metadata (such as images generated by tj3Compress*()) or to use TJXOPT_COPYNONE along with TJPARAM_NOREALLOC. Still, however, the function shouldn't segfault as long as the calling program allocates the suggested amount of space for the JPEG destination buffer. Usability notes: tj3Transform() could hypothetically require dstSizes[i] to be set regardless of the value of TJPARAM_NOREALLOC, but there are usability pitfalls either way. The main pitfall I sought to avoid withff78e37595was a calling program failing to set dstSizes[i] at all, thus leaving its value undefined. It could be argued that requiring dstSizes[i] to be set in all cases is more consistent, but it could also be argued that not requiring it to be set when TJPARAM_NOREALLOC is set is more user-proof. tj3Transform() could also hypothetically set TJXOPT_COPYNONE automatically when TJPARAM_NOREALLOC is set, but that could lead to user confusion. Ultimately, I would like to address these issues in TurboJPEG v4 by using managed buffer objects, but that would be an extensive overhaul.
1401 lines
51 KiB
C
1401 lines
51 KiB
C
/*
|
|
* Copyright (C)2011-2023 D. R. Commander. All Rights Reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* - Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* - Neither the name of the libjpeg-turbo Project nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <limits.h>
|
|
#include "turbojpeg.h"
|
|
#include "jinclude.h"
|
|
#include <jni.h>
|
|
#include "java/org_libjpegturbo_turbojpeg_TJCompressor.h"
|
|
#include "java/org_libjpegturbo_turbojpeg_TJDecompressor.h"
|
|
#include "java/org_libjpegturbo_turbojpeg_TJTransformer.h"
|
|
#include "java/org_libjpegturbo_turbojpeg_TJ.h"
|
|
|
|
#define BAILIF0(f) { \
|
|
if (!(f) || (*env)->ExceptionCheck(env)) { \
|
|
goto bailout; \
|
|
} \
|
|
}
|
|
|
|
#define BAILIF0NOEC(f) { \
|
|
if (!(f)) { \
|
|
goto bailout; \
|
|
} \
|
|
}
|
|
|
|
#define THROW(msg, exceptionClass) { \
|
|
jclass _exccls = (*env)->FindClass(env, exceptionClass); \
|
|
\
|
|
BAILIF0(_exccls); \
|
|
(*env)->ThrowNew(env, _exccls, msg); \
|
|
goto bailout; \
|
|
}
|
|
|
|
#define THROW_TJ() { \
|
|
jclass _exccls; \
|
|
jmethodID _excid; \
|
|
jobject _excobj; \
|
|
jstring _errstr; \
|
|
\
|
|
BAILIF0(_errstr = (*env)->NewStringUTF(env, tj3GetErrorStr(handle))); \
|
|
BAILIF0(_exccls = (*env)->FindClass(env, \
|
|
"org/libjpegturbo/turbojpeg/TJException")); \
|
|
BAILIF0(_excid = (*env)->GetMethodID(env, _exccls, "<init>", \
|
|
"(Ljava/lang/String;I)V")); \
|
|
BAILIF0(_excobj = (*env)->NewObject(env, _exccls, _excid, _errstr, \
|
|
tj3GetErrorCode(handle))); \
|
|
(*env)->Throw(env, _excobj); \
|
|
goto bailout; \
|
|
}
|
|
|
|
#define THROW_ARG(msg) THROW(msg, "java/lang/IllegalArgumentException")
|
|
|
|
#define THROW_MEM() \
|
|
THROW("Memory allocation failure", "java/lang/OutOfMemoryError");
|
|
|
|
#define GET_HANDLE() \
|
|
jclass _cls = (*env)->GetObjectClass(env, obj); \
|
|
jfieldID _fid; \
|
|
\
|
|
BAILIF0(_cls); \
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "handle", "J")); \
|
|
handle = (tjhandle)(size_t)(*env)->GetLongField(env, obj, _fid);
|
|
|
|
#define SAFE_RELEASE(javaArray, cArray) { \
|
|
if (javaArray && cArray) \
|
|
(*env)->ReleasePrimitiveArrayCritical(env, javaArray, (void *)cArray, 0); \
|
|
cArray = NULL; \
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJ::bufSize() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSize
|
|
(JNIEnv *env, jclass cls, jint width, jint height, jint jpegSubsamp)
|
|
{
|
|
size_t retval = tj3JPEGBufSize(width, height, jpegSubsamp);
|
|
|
|
if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
|
|
if (retval > (size_t)INT_MAX)
|
|
THROW_ARG("Image is too large");
|
|
|
|
bailout:
|
|
return (jint)retval;
|
|
}
|
|
|
|
/* TurboJPEG 1.4.x: TJ::bufSizeYUV() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII
|
|
(JNIEnv *env, jclass cls, jint width, jint align, jint height, jint subsamp)
|
|
{
|
|
size_t retval = tj3YUVBufSize(width, align, height, subsamp);
|
|
|
|
if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
|
|
if (retval > (size_t)INT_MAX)
|
|
THROW_ARG("Image is too large");
|
|
|
|
bailout:
|
|
return (jint)retval;
|
|
}
|
|
|
|
/* TurboJPEG 1.4.x: TJ::planeSizeYUV() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeSizeYUV__IIIII
|
|
(JNIEnv *env, jclass cls, jint componentID, jint width, jint stride,
|
|
jint height, jint subsamp)
|
|
{
|
|
size_t retval = tj3YUVPlaneSize(componentID, width, stride, height, subsamp);
|
|
|
|
if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
|
|
if (retval > (size_t)INT_MAX)
|
|
THROW_ARG("Image is too large");
|
|
|
|
bailout:
|
|
return (jint)retval;
|
|
}
|
|
|
|
/* TurboJPEG 1.4.x: TJ::planeWidth() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeWidth__III
|
|
(JNIEnv *env, jclass cls, jint componentID, jint width, jint subsamp)
|
|
{
|
|
jint retval = (jint)tj3YUVPlaneWidth(componentID, width, subsamp);
|
|
|
|
if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
|
|
|
|
bailout:
|
|
return retval;
|
|
}
|
|
|
|
/* TurboJPEG 1.4.x: TJ::planeHeight() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJ_planeHeight__III
|
|
(JNIEnv *env, jclass cls, jint componentID, jint height, jint subsamp)
|
|
{
|
|
jint retval = (jint)tj3YUVPlaneHeight(componentID, height, subsamp);
|
|
|
|
if (retval == 0) THROW_ARG(tj3GetErrorStr(NULL));
|
|
|
|
bailout:
|
|
return retval;
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJCompressor::init() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_init
|
|
(JNIEnv *env, jobject obj)
|
|
{
|
|
jclass cls;
|
|
jfieldID fid;
|
|
tjhandle handle;
|
|
|
|
if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
|
|
THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
|
|
|
|
BAILIF0(cls = (*env)->GetObjectClass(env, obj));
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
|
|
(*env)->SetLongField(env, obj, fid, (size_t)handle);
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::set() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_set
|
|
(JNIEnv *env, jobject obj, jint param, jint value)
|
|
{
|
|
tjhandle handle = 0;
|
|
|
|
GET_HANDLE();
|
|
|
|
if (tj3Set(handle, param, value) == -1)
|
|
THROW_TJ();
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::get() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_get
|
|
(JNIEnv *env, jobject obj, jint param)
|
|
{
|
|
tjhandle handle = 0;
|
|
|
|
GET_HANDLE();
|
|
|
|
return tj3Get(handle, param);
|
|
|
|
bailout:
|
|
return -1;
|
|
}
|
|
|
|
static jint TJCompressor_compress
|
|
(JNIEnv *env, jobject obj, jarray src, jint srcElementSize, jint precision,
|
|
jint x, jint y, jint width, jint pitch, jint height, jint pf,
|
|
jbyteArray dst)
|
|
{
|
|
tjhandle handle = 0;
|
|
size_t jpegSize = 0;
|
|
jsize arraySize = 0, actualPitch;
|
|
void *srcBuf = NULL;
|
|
unsigned char *jpegBuf = NULL;
|
|
int jpegSubsamp;
|
|
|
|
GET_HANDLE();
|
|
|
|
if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
|
|
height < 1 || pitch < 0)
|
|
THROW_ARG("Invalid argument in compress*()");
|
|
if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
|
|
THROW_ARG("Mismatch between Java and C API");
|
|
|
|
actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
|
|
arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
|
|
if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
|
|
THROW_ARG("Source buffer is not large enough");
|
|
jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
|
|
if (tj3Get(handle, TJPARAM_LOSSLESS) && jpegSubsamp != TJSAMP_GRAY)
|
|
jpegSubsamp = TJSAMP_444;
|
|
else if (jpegSubsamp == TJSAMP_UNKNOWN)
|
|
THROW_ARG("TJPARAM_SUBSAMP must be specified");
|
|
jpegSize = tj3JPEGBufSize(width, height, jpegSubsamp);
|
|
if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
|
|
THROW_ARG("Destination buffer is not large enough");
|
|
|
|
if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
|
|
THROW_TJ();
|
|
|
|
BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
|
|
BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
|
|
|
|
if (precision == 8) {
|
|
if (tj3Compress8(handle, &((unsigned char *)srcBuf)[y * actualPitch +
|
|
x * tjPixelSize[pf]],
|
|
width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
|
|
SAFE_RELEASE(dst, jpegBuf);
|
|
SAFE_RELEASE(src, srcBuf);
|
|
THROW_TJ();
|
|
}
|
|
} else if (precision == 12) {
|
|
if (tj3Compress12(handle, &((short *)srcBuf)[y * actualPitch +
|
|
x * tjPixelSize[pf]],
|
|
width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
|
|
SAFE_RELEASE(dst, jpegBuf);
|
|
SAFE_RELEASE(src, srcBuf);
|
|
THROW_TJ();
|
|
}
|
|
} else {
|
|
if (tj3Compress16(handle, &((unsigned short *)srcBuf)[y * actualPitch +
|
|
x * tjPixelSize[pf]],
|
|
width, pitch, height, pf, &jpegBuf, &jpegSize) == -1) {
|
|
SAFE_RELEASE(dst, jpegBuf);
|
|
SAFE_RELEASE(src, srcBuf);
|
|
THROW_TJ();
|
|
}
|
|
}
|
|
|
|
bailout:
|
|
SAFE_RELEASE(dst, jpegBuf);
|
|
SAFE_RELEASE(src, srcBuf);
|
|
return (jint)jpegSize;
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::compress8() byte source */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3BIIIIII_3B
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width,
|
|
jint pitch, jint height, jint pf, jbyteArray dst)
|
|
{
|
|
return TJCompressor_compress(env, obj, src, 1, 8, x, y, width, pitch, height,
|
|
pf, dst);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::compress12() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress12
|
|
(JNIEnv *env, jobject obj, jshortArray src, jint x, jint y, jint width,
|
|
jint pitch, jint height, jint pf, jbyteArray dst)
|
|
{
|
|
return TJCompressor_compress(env, obj, src, 1, 12, x, y, width, pitch,
|
|
height, pf, dst);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::compress16() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress16
|
|
(JNIEnv *env, jobject obj, jshortArray src, jint x, jint y, jint width,
|
|
jint pitch, jint height, jint pf, jbyteArray dst)
|
|
{
|
|
return TJCompressor_compress(env, obj, src, 1, 16, x, y, width, pitch,
|
|
height, pf, dst);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::compress8() int source */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress8___3IIIIIII_3B
|
|
(JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width,
|
|
jint stride, jint height, jint pf, jbyteArray dst)
|
|
{
|
|
if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
|
|
THROW_ARG("Invalid argument in compress8()");
|
|
if (tjPixelSize[pf] != sizeof(jint))
|
|
THROW_ARG("Pixel format must be 32-bit when compressing from an integer buffer.");
|
|
|
|
return TJCompressor_compress(env, obj, src, sizeof(jint), 8, x, y, width,
|
|
stride * sizeof(jint), height, pf, dst);
|
|
|
|
bailout:
|
|
return 0;
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::compressFromYUV8() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV8
|
|
(JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
|
|
jint width, jintArray jSrcStrides, jint height, jbyteArray dst)
|
|
{
|
|
tjhandle handle = 0;
|
|
size_t jpegSize = 0;
|
|
jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL };
|
|
const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL };
|
|
const unsigned char *srcPlanes[3] = { NULL, NULL, NULL };
|
|
jint srcOffsetsTmp[3] = { 0, 0, 0 }, srcStridesTmp[3] = { 0, 0, 0 };
|
|
int srcOffsets[3] = { 0, 0, 0 }, srcStrides[3] = { 0, 0, 0 };
|
|
unsigned char *jpegBuf = NULL;
|
|
int nc = 0, i, subsamp;
|
|
|
|
GET_HANDLE();
|
|
|
|
if (org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
|
|
THROW_ARG("Mismatch between Java and C API");
|
|
|
|
if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
|
|
THROW_ARG("TJPARAM_SUBSAMP must be specified");
|
|
nc = subsamp == TJSAMP_GRAY ? 1 : 3;
|
|
if ((*env)->GetArrayLength(env, srcobjs) < nc)
|
|
THROW_ARG("Planes array is too small for the subsampling type");
|
|
if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
|
|
THROW_ARG("Offsets array is too small for the subsampling type");
|
|
if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
|
|
THROW_ARG("Strides array is too small for the subsampling type");
|
|
|
|
jpegSize = tj3JPEGBufSize(width, height, subsamp);
|
|
if ((*env)->GetArrayLength(env, dst) < (jsize)jpegSize)
|
|
THROW_ARG("Destination buffer is not large enough");
|
|
|
|
if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
|
|
THROW_TJ();
|
|
|
|
(*env)->GetIntArrayRegion(env, jSrcOffsets, 0, nc, srcOffsetsTmp);
|
|
if ((*env)->ExceptionCheck(env)) goto bailout;
|
|
for (i = 0; i < 3; i++)
|
|
srcOffsets[i] = srcOffsetsTmp[i];
|
|
|
|
(*env)->GetIntArrayRegion(env, jSrcStrides, 0, nc, srcStridesTmp);
|
|
if ((*env)->ExceptionCheck(env)) goto bailout;
|
|
for (i = 0; i < 3; i++)
|
|
srcStrides[i] = srcStridesTmp[i];
|
|
|
|
for (i = 0; i < nc; i++) {
|
|
size_t planeSize = tj3YUVPlaneSize(i, width, srcStrides[i], height,
|
|
subsamp);
|
|
int pw = tj3YUVPlaneWidth(i, width, subsamp);
|
|
|
|
if (planeSize == 0 || pw == 0)
|
|
THROW_ARG(tj3GetErrorStr(NULL));
|
|
|
|
if (planeSize > (size_t)INT_MAX)
|
|
THROW_ARG("Source plane is too large");
|
|
if (srcOffsets[i] < 0)
|
|
THROW_ARG("Invalid argument in compressFromYUV8()");
|
|
if (srcStrides[i] < 0 && srcOffsets[i] - (int)planeSize + pw < 0)
|
|
THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
|
|
|
|
BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
|
|
if ((*env)->GetArrayLength(env, jSrcPlanes[i]) <
|
|
srcOffsets[i] + (int)planeSize)
|
|
THROW_ARG("Source plane is not large enough");
|
|
}
|
|
for (i = 0; i < nc; i++) {
|
|
BAILIF0NOEC(srcPlanesTmp[i] =
|
|
(*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
|
|
srcPlanes[i] = &srcPlanesTmp[i][srcOffsets[i]];
|
|
}
|
|
BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
|
|
|
|
if (tj3CompressFromYUVPlanes8(handle, srcPlanes, width, srcStrides, height,
|
|
&jpegBuf, &jpegSize) == -1) {
|
|
SAFE_RELEASE(dst, jpegBuf);
|
|
for (i = 0; i < nc; i++)
|
|
SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
|
|
THROW_TJ();
|
|
}
|
|
|
|
bailout:
|
|
SAFE_RELEASE(dst, jpegBuf);
|
|
for (i = 0; i < nc; i++)
|
|
SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
|
|
return (jint)jpegSize;
|
|
}
|
|
|
|
static void TJCompressor_encodeYUV8
|
|
(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)
|
|
{
|
|
tjhandle handle = 0;
|
|
jsize arraySize = 0, actualPitch;
|
|
unsigned char *srcBuf = NULL;
|
|
jbyteArray jDstPlanes[3] = { NULL, NULL, NULL };
|
|
unsigned char *dstPlanesTmp[3] = { NULL, NULL, NULL };
|
|
unsigned char *dstPlanes[3] = { NULL, NULL, NULL };
|
|
jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 };
|
|
int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 };
|
|
int nc = 0, i, subsamp;
|
|
|
|
GET_HANDLE();
|
|
|
|
if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF || width < 1 ||
|
|
height < 1 || pitch < 0)
|
|
THROW_ARG("Invalid argument in encodeYUV8()");
|
|
if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
|
|
org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
|
|
THROW_ARG("Mismatch between Java and C API");
|
|
|
|
if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
|
|
THROW_ARG("TJPARAM_SUBSAMP must be specified");
|
|
nc = subsamp == TJSAMP_GRAY ? 1 : 3;
|
|
if ((*env)->GetArrayLength(env, dstobjs) < nc)
|
|
THROW_ARG("Planes array is too small for the subsampling type");
|
|
if ((*env)->GetArrayLength(env, jDstOffsets) < nc)
|
|
THROW_ARG("Offsets array is too small for the subsampling type");
|
|
if ((*env)->GetArrayLength(env, jDstStrides) < nc)
|
|
THROW_ARG("Strides array is too small for the subsampling type");
|
|
|
|
actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
|
|
arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
|
|
if ((*env)->GetArrayLength(env, src) * srcElementSize < arraySize)
|
|
THROW_ARG("Source buffer is not large enough");
|
|
|
|
(*env)->GetIntArrayRegion(env, jDstOffsets, 0, nc, dstOffsetsTmp);
|
|
if ((*env)->ExceptionCheck(env)) goto bailout;
|
|
for (i = 0; i < 3; i++)
|
|
dstOffsets[i] = dstOffsetsTmp[i];
|
|
|
|
(*env)->GetIntArrayRegion(env, jDstStrides, 0, nc, dstStridesTmp);
|
|
if ((*env)->ExceptionCheck(env)) goto bailout;
|
|
for (i = 0; i < 3; i++)
|
|
dstStrides[i] = dstStridesTmp[i];
|
|
|
|
for (i = 0; i < nc; i++) {
|
|
size_t planeSize = tj3YUVPlaneSize(i, width, dstStrides[i], height,
|
|
subsamp);
|
|
int pw = tj3YUVPlaneWidth(i, width, subsamp);
|
|
|
|
if (planeSize == 0 || pw == 0)
|
|
THROW_ARG(tj3GetErrorStr(NULL));
|
|
|
|
if (planeSize > (size_t)INT_MAX)
|
|
THROW_ARG("Destination plane is too large");
|
|
if (dstOffsets[i] < 0)
|
|
THROW_ARG("Invalid argument in encodeYUV8()");
|
|
if (dstStrides[i] < 0 && dstOffsets[i] - (int)planeSize + pw < 0)
|
|
THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
|
|
|
|
BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
|
|
if ((*env)->GetArrayLength(env, jDstPlanes[i]) <
|
|
dstOffsets[i] + (int)planeSize)
|
|
THROW_ARG("Destination plane is not large enough");
|
|
}
|
|
for (i = 0; i < nc; i++) {
|
|
BAILIF0NOEC(dstPlanesTmp[i] =
|
|
(*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
|
|
dstPlanes[i] = &dstPlanesTmp[i][dstOffsets[i]];
|
|
}
|
|
BAILIF0NOEC(srcBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
|
|
|
|
if (tj3EncodeYUVPlanes8(handle,
|
|
&srcBuf[y * actualPitch + x * tjPixelSize[pf]],
|
|
width, pitch, height, pf, dstPlanes,
|
|
dstStrides) == -1) {
|
|
SAFE_RELEASE(src, srcBuf);
|
|
for (i = 0; i < nc; i++)
|
|
SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
|
|
THROW_TJ();
|
|
}
|
|
|
|
bailout:
|
|
SAFE_RELEASE(src, srcBuf);
|
|
for (i = 0; i < nc; i++)
|
|
SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::encodeYUV8() byte source */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3BIIIIII_3_3B_3I_3I
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint x, jint y, jint width,
|
|
jint pitch, jint height, jint pf, jobjectArray dstobjs,
|
|
jintArray jDstOffsets, jintArray jDstStrides)
|
|
{
|
|
TJCompressor_encodeYUV8(env, obj, src, 1, x, y, width, pitch, height, pf,
|
|
dstobjs, jDstOffsets, jDstStrides);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJCompressor::encodeYUV8() int source */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV8___3IIIIIII_3_3B_3I_3I
|
|
(JNIEnv *env, jobject obj, jintArray src, jint x, jint y, jint width,
|
|
jint stride, jint height, jint pf, jobjectArray dstobjs,
|
|
jintArray jDstOffsets, jintArray jDstStrides)
|
|
{
|
|
if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
|
|
THROW_ARG("Invalid argument in encodeYUV8()");
|
|
if (tjPixelSize[pf] != sizeof(jint))
|
|
THROW_ARG("Pixel format must be 32-bit when encoding from an integer buffer.");
|
|
|
|
TJCompressor_encodeYUV8(env, obj, src, sizeof(jint), x, y, width,
|
|
stride * sizeof(jint), height, pf, dstobjs,
|
|
jDstOffsets, jDstStrides);
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJCompressor::destroy() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy
|
|
(JNIEnv *env, jobject obj)
|
|
{
|
|
tjhandle handle = 0;
|
|
|
|
GET_HANDLE();
|
|
|
|
tj3Destroy(handle);
|
|
(*env)->SetLongField(env, obj, _fid, 0);
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJDecompressor::init() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_init
|
|
(JNIEnv *env, jobject obj)
|
|
{
|
|
jclass cls;
|
|
jfieldID fid;
|
|
tjhandle handle;
|
|
|
|
if ((handle = tj3Init(TJINIT_DECOMPRESS)) == NULL)
|
|
THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
|
|
|
|
BAILIF0(cls = (*env)->GetObjectClass(env, obj));
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
|
|
(*env)->SetLongField(env, obj, fid, (size_t)handle);
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::set() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_set
|
|
(JNIEnv *env, jobject obj, jint param, jint value)
|
|
{
|
|
Java_org_libjpegturbo_turbojpeg_TJCompressor_set(env, obj, param, value);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::get() */
|
|
JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_get
|
|
(JNIEnv *env, jobject obj, jint param)
|
|
{
|
|
return Java_org_libjpegturbo_turbojpeg_TJCompressor_get(env, obj, param);
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJDecompressor::getScalingFactors() */
|
|
JNIEXPORT jobjectArray JNICALL Java_org_libjpegturbo_turbojpeg_TJ_getScalingFactors
|
|
(JNIEnv *env, jclass cls)
|
|
{
|
|
jclass sfcls = NULL;
|
|
jfieldID fid = 0;
|
|
tjscalingfactor *sf = NULL;
|
|
int n = 0, i;
|
|
jobject sfobj = NULL;
|
|
jobjectArray sfjava = NULL;
|
|
|
|
if ((sf = tj3GetScalingFactors(&n)) == NULL || n == 0)
|
|
THROW_ARG(tj3GetErrorStr(NULL));
|
|
|
|
BAILIF0(sfcls = (*env)->FindClass(env,
|
|
"org/libjpegturbo/turbojpeg/TJScalingFactor"));
|
|
BAILIF0(sfjava = (jobjectArray)(*env)->NewObjectArray(env, n, sfcls, 0));
|
|
|
|
for (i = 0; i < n; i++) {
|
|
BAILIF0(sfobj = (*env)->AllocObject(env, sfcls));
|
|
BAILIF0(fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
|
|
(*env)->SetIntField(env, sfobj, fid, sf[i].num);
|
|
BAILIF0(fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
|
|
(*env)->SetIntField(env, sfobj, fid, sf[i].denom);
|
|
(*env)->SetObjectArrayElement(env, sfjava, i, sfobj);
|
|
}
|
|
|
|
bailout:
|
|
return sfjava;
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJDecompressor::decompressHeader() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressHeader
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize)
|
|
{
|
|
tjhandle handle = 0;
|
|
unsigned char *jpegBuf = NULL;
|
|
|
|
GET_HANDLE();
|
|
|
|
if ((*env)->GetArrayLength(env, src) < jpegSize)
|
|
THROW_ARG("Source buffer is not large enough");
|
|
|
|
BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
|
|
|
|
if (tj3DecompressHeader(handle, jpegBuf, (size_t)jpegSize) == -1) {
|
|
SAFE_RELEASE(src, jpegBuf);
|
|
THROW_TJ();
|
|
}
|
|
|
|
bailout:
|
|
SAFE_RELEASE(src, jpegBuf);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::setCroppingRegion() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_setCroppingRegion
|
|
(JNIEnv *env, jobject obj)
|
|
{
|
|
tjhandle handle = 0;
|
|
jclass sfcls, crcls;
|
|
jobject sfobj, crobj;
|
|
tjregion croppingRegion;
|
|
tjscalingfactor scalingFactor;
|
|
|
|
GET_HANDLE();
|
|
|
|
BAILIF0(sfcls = (*env)->FindClass(env,
|
|
"org/libjpegturbo/turbojpeg/TJScalingFactor"));
|
|
BAILIF0(_fid =
|
|
(*env)->GetFieldID(env, _cls, "scalingFactor",
|
|
"Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
|
|
BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
|
|
scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
|
|
scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
|
|
|
|
if (tj3SetScalingFactor(handle, scalingFactor) == -1)
|
|
THROW_TJ();
|
|
|
|
BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle"));
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion",
|
|
"Ljava/awt/Rectangle;"));
|
|
BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid));
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I"));
|
|
croppingRegion.x = (*env)->GetIntField(env, crobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I"));
|
|
croppingRegion.y = (*env)->GetIntField(env, crobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I"));
|
|
croppingRegion.w = (*env)->GetIntField(env, crobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I"));
|
|
croppingRegion.h = (*env)->GetIntField(env, crobj, _fid);
|
|
|
|
if (tj3SetCroppingRegion(handle, croppingRegion) == -1)
|
|
THROW_TJ();
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
static void TJDecompressor_decompress
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jarray dst,
|
|
jint dstElementSize, int precision, jint x, jint y, jint pitch, jint pf)
|
|
{
|
|
tjhandle handle = 0;
|
|
jsize arraySize = 0, actualPitch;
|
|
unsigned char *jpegBuf = NULL;
|
|
void *dstBuf = NULL;
|
|
jclass sfcls, crcls;
|
|
jobject sfobj, crobj;
|
|
tjscalingfactor scalingFactor;
|
|
tjregion cr;
|
|
int jpegWidth, jpegHeight, scaledWidth, scaledHeight;
|
|
|
|
GET_HANDLE();
|
|
|
|
if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
|
|
THROW_ARG("Invalid argument in decompress*()");
|
|
if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF)
|
|
THROW_ARG("Mismatch between Java and C API");
|
|
|
|
if ((*env)->GetArrayLength(env, src) < jpegSize)
|
|
THROW_ARG("Source buffer is not large enough");
|
|
if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
|
|
THROW_ARG("JPEG header has not yet been read");
|
|
if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
|
|
THROW_ARG("JPEG header has not yet been read");
|
|
|
|
BAILIF0(sfcls = (*env)->FindClass(env,
|
|
"org/libjpegturbo/turbojpeg/TJScalingFactor"));
|
|
BAILIF0(_fid =
|
|
(*env)->GetFieldID(env, _cls, "scalingFactor",
|
|
"Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
|
|
BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
|
|
scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
|
|
scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
|
|
|
|
if (tj3SetScalingFactor(handle, scalingFactor) == -1)
|
|
THROW_TJ();
|
|
scaledWidth = TJSCALED(jpegWidth, scalingFactor);
|
|
scaledHeight = TJSCALED(jpegHeight, scalingFactor);
|
|
|
|
BAILIF0(crcls = (*env)->FindClass(env, "java/awt/Rectangle"));
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "croppingRegion",
|
|
"Ljava/awt/Rectangle;"));
|
|
BAILIF0(crobj = (*env)->GetObjectField(env, obj, _fid));
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "x", "I"));
|
|
cr.x = (*env)->GetIntField(env, crobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "y", "I"));
|
|
cr.y = (*env)->GetIntField(env, crobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "width", "I"));
|
|
cr.w = (*env)->GetIntField(env, crobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, crcls, "height", "I"));
|
|
cr.h = (*env)->GetIntField(env, crobj, _fid);
|
|
if (cr.x != 0 || cr.y != 0 || cr.w != 0 || cr.h != 0) {
|
|
scaledWidth = cr.w ? cr.w : scaledWidth - cr.x;
|
|
scaledHeight = cr.h ? cr.h : scaledHeight - cr.y;
|
|
}
|
|
|
|
actualPitch = (pitch == 0) ? scaledWidth * tjPixelSize[pf] : pitch;
|
|
arraySize = (y + scaledHeight - 1) * actualPitch +
|
|
(x + scaledWidth) * tjPixelSize[pf];
|
|
if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
|
|
THROW_ARG("Destination buffer is not large enough");
|
|
|
|
BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
|
|
BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
|
|
|
|
if (precision == 8) {
|
|
if (tj3Decompress8(handle, jpegBuf, (size_t)jpegSize,
|
|
&((unsigned char *)dstBuf)[y * actualPitch +
|
|
x * tjPixelSize[pf]],
|
|
pitch, pf) == -1) {
|
|
SAFE_RELEASE(dst, dstBuf);
|
|
SAFE_RELEASE(src, jpegBuf);
|
|
THROW_TJ();
|
|
}
|
|
} else if (precision == 12) {
|
|
if (tj3Decompress12(handle, jpegBuf, (size_t)jpegSize,
|
|
&((short *)dstBuf)[y * actualPitch +
|
|
x * tjPixelSize[pf]],
|
|
pitch, pf) == -1) {
|
|
SAFE_RELEASE(dst, dstBuf);
|
|
SAFE_RELEASE(src, jpegBuf);
|
|
THROW_TJ();
|
|
}
|
|
} else {
|
|
if (tj3Decompress16(handle, jpegBuf, (size_t)jpegSize,
|
|
&((unsigned short *)dstBuf)[y * actualPitch +
|
|
x * tjPixelSize[pf]],
|
|
pitch, pf) == -1) {
|
|
SAFE_RELEASE(dst, dstBuf);
|
|
SAFE_RELEASE(src, jpegBuf);
|
|
THROW_TJ();
|
|
}
|
|
}
|
|
|
|
bailout:
|
|
SAFE_RELEASE(dst, dstBuf);
|
|
SAFE_RELEASE(src, jpegBuf);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::decompress8() byte destination */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3BIIII
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jbyteArray dst,
|
|
jint x, jint y, jint pitch, jint pf)
|
|
{
|
|
TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 8, x, y, pitch,
|
|
pf);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::decompress12() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress12
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jshortArray dst,
|
|
jint x, jint y, jint pitch, jint pf)
|
|
{
|
|
TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 12, x, y, pitch,
|
|
pf);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::decompress16() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress16
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jshortArray dst,
|
|
jint x, jint y, jint pitch, jint pf)
|
|
{
|
|
TJDecompressor_decompress(env, obj, src, jpegSize, dst, 1, 16, x, y, pitch,
|
|
pf);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::decompress8() int destination */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress8___3BI_3IIIII
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize, jintArray dst,
|
|
jint x, jint y, jint stride, jint pf)
|
|
{
|
|
if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
|
|
THROW_ARG("Invalid argument in decompress8()");
|
|
if (tjPixelSize[pf] != sizeof(jint))
|
|
THROW_ARG("Pixel format must be 32-bit when decompressing to an integer buffer.");
|
|
|
|
TJDecompressor_decompress(env, obj, src, jpegSize, dst, sizeof(jint), 8, x,
|
|
y, stride * sizeof(jint), pf);
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::decompressToYUV8() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV8
|
|
(JNIEnv *env, jobject obj, jbyteArray src, jint jpegSize,
|
|
jobjectArray dstobjs, jintArray jDstOffsets, jintArray jDstStrides)
|
|
{
|
|
tjhandle handle = 0;
|
|
unsigned char *jpegBuf = NULL;
|
|
jbyteArray jDstPlanes[3] = { NULL, NULL, NULL };
|
|
unsigned char *dstPlanesTmp[3] = { NULL, NULL, NULL };
|
|
unsigned char *dstPlanes[3] = { NULL, NULL, NULL };
|
|
jint dstOffsetsTmp[3] = { 0, 0, 0 }, dstStridesTmp[3] = { 0, 0, 0 };
|
|
int dstOffsets[3] = { 0, 0, 0 }, dstStrides[3] = { 0, 0, 0 };
|
|
jclass sfcls;
|
|
jobject sfobj;
|
|
int jpegSubsamp, jpegWidth = 0, jpegHeight = 0;
|
|
int nc = 0, i, scaledWidth, scaledHeight;
|
|
tjscalingfactor scalingFactor;
|
|
|
|
GET_HANDLE();
|
|
|
|
if ((*env)->GetArrayLength(env, src) < jpegSize)
|
|
THROW_ARG("Source buffer is not large enough");
|
|
if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
|
|
THROW_ARG("JPEG header has not yet been read");
|
|
if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
|
|
THROW_ARG("JPEG header has not yet been read");
|
|
|
|
BAILIF0(sfcls = (*env)->FindClass(env,
|
|
"org/libjpegturbo/turbojpeg/TJScalingFactor"));
|
|
BAILIF0(_fid =
|
|
(*env)->GetFieldID(env, _cls, "scalingFactor",
|
|
"Lorg/libjpegturbo/turbojpeg/TJScalingFactor;"));
|
|
BAILIF0(sfobj = (*env)->GetObjectField(env, obj, _fid));
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "num", "I"));
|
|
scalingFactor.num = (*env)->GetIntField(env, sfobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, sfcls, "denom", "I"));
|
|
scalingFactor.denom = (*env)->GetIntField(env, sfobj, _fid);
|
|
|
|
if (tj3SetScalingFactor(handle, scalingFactor) == -1)
|
|
THROW_TJ();
|
|
scaledWidth = TJSCALED(jpegWidth, scalingFactor);
|
|
scaledHeight = TJSCALED(jpegHeight, scalingFactor);
|
|
|
|
if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
|
|
THROW_ARG("TJPARAM_SUBSAMP must be specified");
|
|
nc = jpegSubsamp == TJSAMP_GRAY ? 1 : 3;
|
|
|
|
(*env)->GetIntArrayRegion(env, jDstOffsets, 0, nc, dstOffsetsTmp);
|
|
if ((*env)->ExceptionCheck(env)) goto bailout;
|
|
for (i = 0; i < 3; i++)
|
|
dstOffsets[i] = dstOffsetsTmp[i];
|
|
|
|
(*env)->GetIntArrayRegion(env, jDstStrides, 0, nc, dstStridesTmp);
|
|
if ((*env)->ExceptionCheck(env)) goto bailout;
|
|
for (i = 0; i < 3; i++)
|
|
dstStrides[i] = dstStridesTmp[i];
|
|
|
|
for (i = 0; i < nc; i++) {
|
|
size_t planeSize = tj3YUVPlaneSize(i, scaledWidth, dstStrides[i],
|
|
scaledHeight, jpegSubsamp);
|
|
int pw = tj3YUVPlaneWidth(i, scaledWidth, jpegSubsamp);
|
|
|
|
if (planeSize == 0 || pw == 0)
|
|
THROW_ARG(tj3GetErrorStr(NULL));
|
|
|
|
if (planeSize > (size_t)INT_MAX)
|
|
THROW_ARG("Destination plane is too large");
|
|
if (dstOffsets[i] < 0)
|
|
THROW_ARG("Invalid argument in decompressToYUV8()");
|
|
if (dstStrides[i] < 0 && dstOffsets[i] - (int)planeSize + pw < 0)
|
|
THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
|
|
|
|
BAILIF0(jDstPlanes[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
|
|
if ((*env)->GetArrayLength(env, jDstPlanes[i]) <
|
|
dstOffsets[i] + (int)planeSize)
|
|
THROW_ARG("Destination plane is not large enough");
|
|
}
|
|
for (i = 0; i < nc; i++) {
|
|
BAILIF0NOEC(dstPlanesTmp[i] =
|
|
(*env)->GetPrimitiveArrayCritical(env, jDstPlanes[i], 0));
|
|
dstPlanes[i] = &dstPlanesTmp[i][dstOffsets[i]];
|
|
}
|
|
BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, src, 0));
|
|
|
|
if (tj3DecompressToYUVPlanes8(handle, jpegBuf, (size_t)jpegSize, dstPlanes,
|
|
dstStrides) == -1) {
|
|
SAFE_RELEASE(src, jpegBuf);
|
|
for (i = 0; i < nc; i++)
|
|
SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
|
|
THROW_TJ();
|
|
}
|
|
|
|
bailout:
|
|
SAFE_RELEASE(src, jpegBuf);
|
|
for (i = 0; i < nc; i++)
|
|
SAFE_RELEASE(jDstPlanes[i], dstPlanesTmp[i]);
|
|
}
|
|
|
|
static void TJDecompressor_decodeYUV8
|
|
(JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
|
|
jintArray jSrcStrides, jarray dst, jint dstElementSize, jint x, jint y,
|
|
jint width, jint pitch, jint height, jint pf)
|
|
{
|
|
tjhandle handle = 0;
|
|
jsize arraySize = 0, actualPitch;
|
|
jbyteArray jSrcPlanes[3] = { NULL, NULL, NULL };
|
|
const unsigned char *srcPlanesTmp[3] = { NULL, NULL, NULL };
|
|
const unsigned char *srcPlanes[3] = { NULL, NULL, NULL };
|
|
jint srcOffsetsTmp[3] = { 0, 0, 0 }, srcStridesTmp[3] = { 0, 0, 0 };
|
|
int srcOffsets[3] = { 0, 0, 0 }, srcStrides[3] = { 0, 0, 0 };
|
|
unsigned char *dstBuf = NULL;
|
|
int nc = 0, i, subsamp;
|
|
|
|
GET_HANDLE();
|
|
|
|
if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
|
|
THROW_ARG("Invalid argument in decodeYUV8()");
|
|
if (org_libjpegturbo_turbojpeg_TJ_NUMPF != TJ_NUMPF ||
|
|
org_libjpegturbo_turbojpeg_TJ_NUMSAMP != TJ_NUMSAMP)
|
|
THROW_ARG("Mismatch between Java and C API");
|
|
|
|
if ((subsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
|
|
THROW_ARG("TJPARAM_SUBSAMP must be specified");
|
|
nc = subsamp == TJSAMP_GRAY ? 1 : 3;
|
|
if ((*env)->GetArrayLength(env, srcobjs) < nc)
|
|
THROW_ARG("Planes array is too small for the subsampling type");
|
|
if ((*env)->GetArrayLength(env, jSrcOffsets) < nc)
|
|
THROW_ARG("Offsets array is too small for the subsampling type");
|
|
if ((*env)->GetArrayLength(env, jSrcStrides) < nc)
|
|
THROW_ARG("Strides array is too small for the subsampling type");
|
|
|
|
actualPitch = (pitch == 0) ? width * tjPixelSize[pf] : pitch;
|
|
arraySize = (y + height - 1) * actualPitch + (x + width) * tjPixelSize[pf];
|
|
if ((*env)->GetArrayLength(env, dst) * dstElementSize < arraySize)
|
|
THROW_ARG("Destination buffer is not large enough");
|
|
|
|
(*env)->GetIntArrayRegion(env, jSrcOffsets, 0, nc, srcOffsetsTmp);
|
|
if ((*env)->ExceptionCheck(env)) goto bailout;
|
|
for (i = 0; i < 3; i++)
|
|
srcOffsets[i] = srcOffsetsTmp[i];
|
|
|
|
(*env)->GetIntArrayRegion(env, jSrcStrides, 0, nc, srcStridesTmp);
|
|
if ((*env)->ExceptionCheck(env)) goto bailout;
|
|
for (i = 0; i < 3; i++)
|
|
srcStrides[i] = srcStridesTmp[i];
|
|
|
|
for (i = 0; i < nc; i++) {
|
|
size_t planeSize = tj3YUVPlaneSize(i, width, srcStrides[i], height,
|
|
subsamp);
|
|
int pw = tj3YUVPlaneWidth(i, width, subsamp);
|
|
|
|
if (planeSize == 0 || pw == 0)
|
|
THROW_ARG(tj3GetErrorStr(NULL));
|
|
|
|
if (planeSize > (size_t)INT_MAX)
|
|
THROW_ARG("Source plane is too large");
|
|
if (srcOffsets[i] < 0)
|
|
THROW_ARG("Invalid argument in decodeYUV8()");
|
|
if (srcStrides[i] < 0 && srcOffsets[i] - (int)planeSize + pw < 0)
|
|
THROW_ARG("Negative plane stride would cause memory to be accessed below plane boundary");
|
|
|
|
BAILIF0(jSrcPlanes[i] = (*env)->GetObjectArrayElement(env, srcobjs, i));
|
|
if ((*env)->GetArrayLength(env, jSrcPlanes[i]) <
|
|
srcOffsets[i] + (int)planeSize)
|
|
THROW_ARG("Source plane is not large enough");
|
|
}
|
|
for (i = 0; i < nc; i++) {
|
|
BAILIF0NOEC(srcPlanesTmp[i] =
|
|
(*env)->GetPrimitiveArrayCritical(env, jSrcPlanes[i], 0));
|
|
srcPlanes[i] = &srcPlanesTmp[i][srcOffsets[i]];
|
|
}
|
|
BAILIF0NOEC(dstBuf = (*env)->GetPrimitiveArrayCritical(env, dst, 0));
|
|
|
|
if (tj3DecodeYUVPlanes8(handle, srcPlanes, srcStrides,
|
|
&dstBuf[y * actualPitch + x * tjPixelSize[pf]],
|
|
width, pitch, height, pf) == -1) {
|
|
SAFE_RELEASE(dst, dstBuf);
|
|
for (i = 0; i < nc; i++)
|
|
SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
|
|
THROW_TJ();
|
|
}
|
|
|
|
bailout:
|
|
SAFE_RELEASE(dst, dstBuf);
|
|
for (i = 0; i < nc; i++)
|
|
SAFE_RELEASE(jSrcPlanes[i], srcPlanesTmp[i]);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::decodeYUV8() byte destination */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3BIIIIII
|
|
(JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
|
|
jintArray jSrcStrides, jbyteArray dst, jint x, jint y, jint width,
|
|
jint pitch, jint height, jint pf)
|
|
{
|
|
TJDecompressor_decodeYUV8(env, obj, srcobjs, jSrcOffsets, jSrcStrides, dst,
|
|
1, x, y, width, pitch, height, pf);
|
|
}
|
|
|
|
/* TurboJPEG 3: TJDecompressor::decodeYUV8() int destination */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decodeYUV8___3_3B_3I_3I_3IIIIIII
|
|
(JNIEnv *env, jobject obj, jobjectArray srcobjs, jintArray jSrcOffsets,
|
|
jintArray jSrcStrides, jintArray dst, jint x, jint y, jint width,
|
|
jint stride, jint height, jint pf)
|
|
{
|
|
if (pf < 0 || pf >= org_libjpegturbo_turbojpeg_TJ_NUMPF)
|
|
THROW_ARG("Invalid argument in decodeYUV8()");
|
|
if (tjPixelSize[pf] != sizeof(jint))
|
|
THROW_ARG("Pixel format must be 32-bit when decoding to an integer buffer.");
|
|
|
|
TJDecompressor_decodeYUV8(env, obj, srcobjs, jSrcOffsets, jSrcStrides, dst,
|
|
sizeof(jint), x, y, width, stride * sizeof(jint),
|
|
height, pf);
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJTransformer::init() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_init
|
|
(JNIEnv *env, jobject obj)
|
|
{
|
|
jclass cls;
|
|
jfieldID fid;
|
|
tjhandle handle;
|
|
|
|
if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL)
|
|
THROW(tj3GetErrorStr(NULL), "org/libjpegturbo/turbojpeg/TJException");
|
|
|
|
BAILIF0(cls = (*env)->GetObjectClass(env, obj));
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "handle", "J"));
|
|
(*env)->SetLongField(env, obj, fid, (size_t)handle);
|
|
|
|
bailout:
|
|
return;
|
|
}
|
|
|
|
typedef struct _JNICustomFilterParams {
|
|
JNIEnv *env;
|
|
jobject tobj;
|
|
jobject cfobj;
|
|
} JNICustomFilterParams;
|
|
|
|
static int JNICustomFilter(short *coeffs, tjregion arrayRegion,
|
|
tjregion planeRegion, int componentIndex,
|
|
int transformIndex, tjtransform *transform)
|
|
{
|
|
JNICustomFilterParams *params = (JNICustomFilterParams *)transform->data;
|
|
JNIEnv *env = params->env;
|
|
jobject tobj = params->tobj, cfobj = params->cfobj;
|
|
jobject arrayRegionObj, planeRegionObj, bufobj, borobj;
|
|
jclass cls;
|
|
jmethodID mid;
|
|
jfieldID fid;
|
|
|
|
BAILIF0(bufobj = (*env)->NewDirectByteBuffer(env, coeffs,
|
|
sizeof(short) * arrayRegion.w * arrayRegion.h));
|
|
BAILIF0(cls = (*env)->FindClass(env, "java/nio/ByteOrder"));
|
|
BAILIF0(mid = (*env)->GetStaticMethodID(env, cls, "nativeOrder",
|
|
"()Ljava/nio/ByteOrder;"));
|
|
BAILIF0(borobj = (*env)->CallStaticObjectMethod(env, cls, mid));
|
|
BAILIF0(cls = (*env)->GetObjectClass(env, bufobj));
|
|
BAILIF0(mid = (*env)->GetMethodID(env, cls, "order",
|
|
"(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"));
|
|
(*env)->CallObjectMethod(env, bufobj, mid, borobj);
|
|
BAILIF0(mid = (*env)->GetMethodID(env, cls, "asShortBuffer",
|
|
"()Ljava/nio/ShortBuffer;"));
|
|
BAILIF0(bufobj = (*env)->CallObjectMethod(env, bufobj, mid));
|
|
|
|
BAILIF0(cls = (*env)->FindClass(env, "java/awt/Rectangle"));
|
|
BAILIF0(arrayRegionObj = (*env)->AllocObject(env, cls));
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
|
|
(*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.x);
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
|
|
(*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.y);
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
|
|
(*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.w);
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
|
|
(*env)->SetIntField(env, arrayRegionObj, fid, arrayRegion.h);
|
|
|
|
BAILIF0(planeRegionObj = (*env)->AllocObject(env, cls));
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "x", "I"));
|
|
(*env)->SetIntField(env, planeRegionObj, fid, planeRegion.x);
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "y", "I"));
|
|
(*env)->SetIntField(env, planeRegionObj, fid, planeRegion.y);
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "width", "I"));
|
|
(*env)->SetIntField(env, planeRegionObj, fid, planeRegion.w);
|
|
BAILIF0(fid = (*env)->GetFieldID(env, cls, "height", "I"));
|
|
(*env)->SetIntField(env, planeRegionObj, fid, planeRegion.h);
|
|
|
|
BAILIF0(cls = (*env)->GetObjectClass(env, cfobj));
|
|
BAILIF0(mid = (*env)->GetMethodID(env, cls, "customFilter",
|
|
"(Ljava/nio/ShortBuffer;Ljava/awt/Rectangle;Ljava/awt/Rectangle;IILorg/libjpegturbo/turbojpeg/TJTransform;)V"));
|
|
(*env)->CallVoidMethod(env, cfobj, mid, bufobj, arrayRegionObj,
|
|
planeRegionObj, componentIndex, transformIndex, tobj);
|
|
|
|
return 0;
|
|
|
|
bailout:
|
|
return -1;
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJTransformer::transform() */
|
|
JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transform
|
|
(JNIEnv *env, jobject obj, jbyteArray jsrcBuf, jint jpegSize,
|
|
jobjectArray dstobjs, jobjectArray tobjs)
|
|
{
|
|
tjhandle handle = 0;
|
|
unsigned char *jpegBuf = NULL, **dstBufs = NULL;
|
|
jsize n = 0;
|
|
size_t *dstSizes = NULL;
|
|
tjtransform *t = NULL;
|
|
jbyteArray *jdstBufs = NULL;
|
|
int i, jpegWidth = 0, jpegHeight = 0, jpegSubsamp;
|
|
jintArray jdstSizes = 0;
|
|
jint *dstSizesi = NULL;
|
|
JNICustomFilterParams *params = NULL;
|
|
|
|
GET_HANDLE();
|
|
|
|
if ((*env)->GetArrayLength(env, jsrcBuf) < jpegSize)
|
|
THROW_ARG("Source buffer is not large enough");
|
|
if ((jpegWidth = tj3Get(handle, TJPARAM_JPEGWIDTH)) == -1)
|
|
THROW_ARG("JPEG header has not yet been read");
|
|
if ((jpegHeight = tj3Get(handle, TJPARAM_JPEGHEIGHT)) == -1)
|
|
THROW_ARG("JPEG header has not yet been read");
|
|
if ((jpegSubsamp = tj3Get(handle, TJPARAM_SUBSAMP)) == TJSAMP_UNKNOWN)
|
|
THROW_ARG("TJPARAM_SUBSAMP must be specified");
|
|
|
|
n = (*env)->GetArrayLength(env, dstobjs);
|
|
if (n != (*env)->GetArrayLength(env, tobjs))
|
|
THROW_ARG("Mismatch between size of transforms array and destination buffers array");
|
|
|
|
if ((dstBufs =
|
|
(unsigned char **)malloc(sizeof(unsigned char *) * n)) == NULL)
|
|
THROW_MEM();
|
|
if ((jdstBufs = (jbyteArray *)malloc(sizeof(jbyteArray) * n)) == NULL)
|
|
THROW_MEM();
|
|
if ((dstSizes = (size_t *)malloc(sizeof(size_t) * n)) == NULL)
|
|
THROW_MEM();
|
|
if ((t = (tjtransform *)malloc(sizeof(tjtransform) * n)) == NULL)
|
|
THROW_MEM();
|
|
if ((params = (JNICustomFilterParams *)malloc(sizeof(JNICustomFilterParams) *
|
|
n)) == NULL)
|
|
THROW_MEM();
|
|
for (i = 0; i < n; i++) {
|
|
dstBufs[i] = NULL; jdstBufs[i] = NULL; dstSizes[i] = 0;
|
|
memset(&t[i], 0, sizeof(tjtransform));
|
|
memset(¶ms[i], 0, sizeof(JNICustomFilterParams));
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
jobject tobj, cfobj;
|
|
|
|
BAILIF0(tobj = (*env)->GetObjectArrayElement(env, tobjs, i));
|
|
BAILIF0(_cls = (*env)->GetObjectClass(env, tobj));
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "op", "I"));
|
|
t[i].op = (*env)->GetIntField(env, tobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "options", "I"));
|
|
t[i].options = (*env)->GetIntField(env, tobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "x", "I"));
|
|
t[i].r.x = (*env)->GetIntField(env, tobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "y", "I"));
|
|
t[i].r.y = (*env)->GetIntField(env, tobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "width", "I"));
|
|
t[i].r.w = (*env)->GetIntField(env, tobj, _fid);
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "height", "I"));
|
|
t[i].r.h = (*env)->GetIntField(env, tobj, _fid);
|
|
|
|
BAILIF0(_fid = (*env)->GetFieldID(env, _cls, "cf",
|
|
"Lorg/libjpegturbo/turbojpeg/TJCustomFilter;"));
|
|
cfobj = (*env)->GetObjectField(env, tobj, _fid);
|
|
if (cfobj) {
|
|
params[i].env = env;
|
|
params[i].tobj = tobj;
|
|
params[i].cfobj = cfobj;
|
|
t[i].customFilter = JNICustomFilter;
|
|
t[i].data = (void *)¶ms[i];
|
|
}
|
|
}
|
|
|
|
if (tj3Set(handle, TJPARAM_NOREALLOC, 1) == -1)
|
|
THROW_TJ();
|
|
|
|
for (i = 0; i < n; i++) {
|
|
int w = jpegWidth, h = jpegHeight;
|
|
|
|
if (t[i].op == TJXOP_TRANSPOSE || t[i].op == TJXOP_TRANSVERSE ||
|
|
t[i].op == TJXOP_ROT90 || t[i].op == TJXOP_ROT270) {
|
|
w = jpegHeight; h = jpegWidth;
|
|
}
|
|
if (t[i].r.w != 0) w = t[i].r.w;
|
|
if (t[i].r.h != 0) h = t[i].r.h;
|
|
BAILIF0(jdstBufs[i] = (*env)->GetObjectArrayElement(env, dstobjs, i));
|
|
if ((size_t)(*env)->GetArrayLength(env, jdstBufs[i]) <
|
|
tj3JPEGBufSize(w, h, jpegSubsamp))
|
|
THROW_ARG("Destination buffer is not large enough");
|
|
}
|
|
BAILIF0NOEC(jpegBuf = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
|
|
for (i = 0; i < n; i++)
|
|
BAILIF0NOEC(dstBufs[i] =
|
|
(*env)->GetPrimitiveArrayCritical(env, jdstBufs[i], 0));
|
|
|
|
if (tj3Transform(handle, jpegBuf, jpegSize, n, dstBufs, dstSizes, t) == -1) {
|
|
for (i = 0; i < n; i++)
|
|
SAFE_RELEASE(jdstBufs[i], dstBufs[i]);
|
|
SAFE_RELEASE(jsrcBuf, jpegBuf);
|
|
THROW_TJ();
|
|
}
|
|
|
|
for (i = 0; i < n; i++)
|
|
SAFE_RELEASE(jdstBufs[i], dstBufs[i]);
|
|
SAFE_RELEASE(jsrcBuf, jpegBuf);
|
|
|
|
jdstSizes = (*env)->NewIntArray(env, n);
|
|
BAILIF0(dstSizesi = (*env)->GetIntArrayElements(env, jdstSizes, 0));
|
|
for (i = 0; i < n; i++) dstSizesi[i] = (int)dstSizes[i];
|
|
|
|
bailout:
|
|
if (dstSizesi) (*env)->ReleaseIntArrayElements(env, jdstSizes, dstSizesi, 0);
|
|
if (dstBufs) {
|
|
for (i = 0; i < n; i++) {
|
|
if (dstBufs[i] && jdstBufs && jdstBufs[i])
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jdstBufs[i], dstBufs[i], 0);
|
|
}
|
|
free(dstBufs);
|
|
}
|
|
SAFE_RELEASE(jsrcBuf, jpegBuf);
|
|
free(jdstBufs);
|
|
free(dstSizes);
|
|
free(t);
|
|
return jdstSizes;
|
|
}
|
|
|
|
/* TurboJPEG 1.2.x: TJDecompressor::destroy() */
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy
|
|
(JNIEnv *env, jobject obj)
|
|
{
|
|
Java_org_libjpegturbo_turbojpeg_TJCompressor_destroy(env, obj);
|
|
}
|
|
|
|
/* Private image I/O routines (used only by TJBench) */
|
|
JNIEXPORT jobject JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_loadImage
|
|
(JNIEnv *env, jobject obj, jint precision, jstring jfilename,
|
|
jintArray jwidth, jint align, jintArray jheight, jintArray jpixelFormat)
|
|
{
|
|
tjhandle handle = NULL;
|
|
void *dstBuf = NULL, *jdstPtr;
|
|
int width, *warr, height, *harr, pixelFormat, *pfarr, n;
|
|
const char *filename = NULL;
|
|
jboolean isCopy;
|
|
jobject jdstBuf = NULL;
|
|
|
|
GET_HANDLE();
|
|
|
|
if ((precision != 8 && precision != 12 && precision != 16) ||
|
|
jfilename == NULL || jwidth == NULL ||
|
|
(*env)->GetArrayLength(env, jwidth) < 1 || jheight == NULL ||
|
|
(*env)->GetArrayLength(env, jheight) < 1 || jpixelFormat == NULL ||
|
|
(*env)->GetArrayLength(env, jpixelFormat) < 1)
|
|
THROW_ARG("Invalid argument in loadImage()");
|
|
|
|
BAILIF0NOEC(warr = (*env)->GetPrimitiveArrayCritical(env, jwidth, 0));
|
|
width = warr[0];
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jwidth, warr, 0);
|
|
BAILIF0NOEC(harr = (*env)->GetPrimitiveArrayCritical(env, jheight, 0));
|
|
height = harr[0];
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jheight, harr, 0);
|
|
BAILIF0NOEC(pfarr = (*env)->GetPrimitiveArrayCritical(env, jpixelFormat, 0));
|
|
pixelFormat = pfarr[0];
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jpixelFormat, pfarr, 0);
|
|
BAILIF0(filename = (*env)->GetStringUTFChars(env, jfilename, &isCopy));
|
|
|
|
if (precision == 8) {
|
|
if ((dstBuf = tj3LoadImage8(handle, filename, &width, align, &height,
|
|
&pixelFormat)) == NULL)
|
|
THROW_TJ();
|
|
} else if (precision == 12) {
|
|
if ((dstBuf = tj3LoadImage12(handle, filename, &width, align, &height,
|
|
&pixelFormat)) == NULL)
|
|
THROW_TJ();
|
|
} else {
|
|
if ((dstBuf = tj3LoadImage16(handle, filename, &width, align, &height,
|
|
&pixelFormat)) == NULL)
|
|
THROW_TJ();
|
|
}
|
|
|
|
(*env)->ReleaseStringUTFChars(env, jfilename, filename);
|
|
filename = NULL;
|
|
|
|
if ((unsigned long long)width * (unsigned long long)height *
|
|
(unsigned long long)tjPixelSize[pixelFormat] >
|
|
(unsigned long long)((unsigned int)-1))
|
|
THROW_ARG("Image is too large");
|
|
|
|
BAILIF0NOEC(warr = (*env)->GetPrimitiveArrayCritical(env, jwidth, 0));
|
|
warr[0] = width;
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jwidth, warr, 0);
|
|
BAILIF0NOEC(harr = (*env)->GetPrimitiveArrayCritical(env, jheight, 0));
|
|
harr[0] = height;
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jheight, harr, 0);
|
|
BAILIF0NOEC(pfarr = (*env)->GetPrimitiveArrayCritical(env, jpixelFormat, 0));
|
|
pfarr[0] = pixelFormat;
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jpixelFormat, pfarr, 0);
|
|
|
|
n = width * height * tjPixelSize[pixelFormat];
|
|
if (precision == 8)
|
|
jdstBuf = (*env)->NewByteArray(env, n);
|
|
else
|
|
jdstBuf = (*env)->NewShortArray(env, n);
|
|
BAILIF0NOEC(jdstPtr = (*env)->GetPrimitiveArrayCritical(env, jdstBuf, 0));
|
|
memcpy(jdstPtr, dstBuf, n * (precision > 8 ? 2 : 1));
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jdstBuf, jdstPtr, 0);
|
|
|
|
bailout:
|
|
if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);
|
|
tj3Free(dstBuf);
|
|
return jdstBuf;
|
|
}
|
|
|
|
|
|
JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_saveImage
|
|
(JNIEnv *env, jobject obj, jint precision, jstring jfilename,
|
|
jobject jsrcBuf, jint width, jint pitch, jint height, jint pixelFormat)
|
|
{
|
|
tjhandle handle = NULL;
|
|
void *srcBuf = NULL, *jsrcPtr;
|
|
const char *filename = NULL;
|
|
int n;
|
|
jboolean isCopy;
|
|
|
|
GET_HANDLE();
|
|
|
|
if ((precision != 8 && precision != 12 && precision != 16) ||
|
|
jfilename == NULL || jsrcBuf == NULL || width < 1 || height < 1 ||
|
|
pixelFormat < 0 || pixelFormat >= TJ_NUMPF)
|
|
THROW_ARG("Invalid argument in saveImage()");
|
|
|
|
if ((unsigned long long)width * (unsigned long long)height *
|
|
(unsigned long long)tjPixelSize[pixelFormat] >
|
|
(unsigned long long)((unsigned int)-1))
|
|
THROW_ARG("Image is too large");
|
|
n = width * height * tjPixelSize[pixelFormat];
|
|
if ((*env)->GetArrayLength(env, jsrcBuf) < n)
|
|
THROW_ARG("Source buffer is not large enough");
|
|
|
|
if ((srcBuf = malloc(n * (precision > 8 ? 2 : 1))) == NULL)
|
|
THROW_MEM();
|
|
|
|
BAILIF0NOEC(jsrcPtr = (*env)->GetPrimitiveArrayCritical(env, jsrcBuf, 0));
|
|
memcpy(srcBuf, jsrcPtr, n * (precision > 8 ? 2 : 1));
|
|
(*env)->ReleasePrimitiveArrayCritical(env, jsrcBuf, jsrcPtr, 0);
|
|
BAILIF0(filename = (*env)->GetStringUTFChars(env, jfilename, &isCopy));
|
|
|
|
if (precision == 8) {
|
|
if (tj3SaveImage8(handle, filename, srcBuf, width, pitch, height,
|
|
pixelFormat) == -1)
|
|
THROW_TJ();
|
|
} else if (precision == 12) {
|
|
if (tj3SaveImage12(handle, filename, srcBuf, width, pitch, height,
|
|
pixelFormat) == -1)
|
|
THROW_TJ();
|
|
} else {
|
|
if (tj3SaveImage16(handle, filename, srcBuf, width, pitch, height,
|
|
pixelFormat) == -1)
|
|
THROW_TJ();
|
|
}
|
|
|
|
bailout:
|
|
if (filename) (*env)->ReleaseStringUTFChars(env, jfilename, filename);
|
|
free(srcBuf);
|
|
}
|