diff --git a/java/TJExample.java b/java/TJExample.java index 8913c726..4718db00 100644 --- a/java/TJExample.java +++ b/java/TJExample.java @@ -64,6 +64,18 @@ public class TJExample { System.out.println(" file, or 4:4:4 otherwise.\n"); System.out.println("-q <1-100> = If the output image is a JPEG file, this specifies the JPEG"); System.out.println(" quality to use when recompressing it (default = 95).\n"); + System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 ="); + System.out.println(" If the input image is a JPEG file, perform the corresponding lossless"); + System.out.println(" transform prior to decompression (these options are mutually exclusive)\n"); + System.out.println("-grayscale = If the input image is a JPEG file, perform lossless grayscale"); + System.out.println(" conversion prior to decompression (can be combined with the other"); + System.out.println(" transforms above)\n"); + System.out.println("-crop X,Y,WxH = If the input image is a JPEG file, perform lossless cropping"); + System.out.println(" prior to decompression. X,Y specifies the upper left corner of the"); + System.out.println(" cropping region, and WxH specifies its width and height. X,Y must be"); + System.out.println(" evenly divible by the MCU block size (8x8 if the source image was"); + System.out.println(" compressed using no subsampling or grayscale, or 16x8 for 4:2:2 or 16x16"); + System.out.println(" for 4:2:0.)\n"); System.exit(1); } @@ -74,6 +86,7 @@ public class TJExample { public static void main(String argv[]) { BufferedImage img = null; byte[] bmpBuf = null; + TJTransform xform = new TJTransform(); try { @@ -133,6 +146,38 @@ public class TJExample { } else usage(); } + if(argv[i].substring(0, 2).equalsIgnoreCase("-g")) + xform.options |= TJ.XFORM_GRAY; + if(argv[i].equalsIgnoreCase("-hflip")) + xform.op = TJ.XFORM_HFLIP; + if(argv[i].equalsIgnoreCase("-vflip")) + xform.op = TJ.XFORM_VFLIP; + if(argv[i].equalsIgnoreCase("-transpose")) + xform.op = TJ.XFORM_TRANSPOSE; + if(argv[i].equalsIgnoreCase("-transverse")) + xform.op = TJ.XFORM_TRANSVERSE; + if(argv[i].equalsIgnoreCase("-rot90")) + xform.op = TJ.XFORM_ROT90; + if(argv[i].equalsIgnoreCase("-rot180")) + xform.op = TJ.XFORM_ROT180; + if(argv[i].equalsIgnoreCase("-rot270")) + xform.op = TJ.XFORM_ROT270; + if(argv[i].length() > 2 + && argv[i].substring(0, 2).equalsIgnoreCase("-c")) { + if(i >= argv.length - 1) usage(); + String[] cropArg = argv[++i].split(","); + if(cropArg.length != 3) usage(); + String[] dimArg = cropArg[2].split("[xX]"); + if(dimArg.length != 2) usage(); + int tempx = Integer.parseInt(cropArg[0]); + int tempy = Integer.parseInt(cropArg[1]); + int tempw = Integer.parseInt(dimArg[0]); + int temph = Integer.parseInt(dimArg[1]); + if(tempx < 0 || tempy < 0 || tempw < 1 || temph < 1) usage(); + xform.x = tempx; xform.y = tempy; + xform.width = tempw; xform.height = temph; + xform.options |= TJ.XFORM_CROP; + } } } String[] inFileTokens = argv[0].split("\\."); @@ -156,7 +201,18 @@ public class TJExample { fis.read(inputBuf); fis.close(); - TJDecompressor tjd = new TJDecompressor(inputBuf); + TJDecompressor tjd; + TJ.ScalingFactor sf; + if(xform.op != TJ.XFORM_NONE || xform.options != 0) { + TJTransformer tjt = new TJTransformer(inputBuf); + TJTransform t[] = new TJTransform[1]; + t[0] = xform; + t[0].options |= TJ.XFORM_TRIM; + TJDecompressor[] tjdx = tjt.transform(t, 0); + tjd = tjdx[0]; + } + else tjd = new TJDecompressor(inputBuf); + width = tjd.getWidth(); height = tjd.getHeight(); int inSubsamp = tjd.getSubsamp(); @@ -184,6 +240,7 @@ public class TJExample { else outSubsamp = TJ.SAMP_444; } } + System.gc(); System.out.print("Dest. Image (" + outFormat + "): " + width + " x " + height + " pixels"); @@ -218,7 +275,8 @@ public class TJExample { } catch(Exception e) { - System.out.println(e); + e.printStackTrace(); + System.exit(-1); } } diff --git a/java/org/libjpegturbo/turbojpeg/TJ.java b/java/org/libjpegturbo/turbojpeg/TJ.java index 9963cddf..be8fe5ea 100644 --- a/java/org/libjpegturbo/turbojpeg/TJ.java +++ b/java/org/libjpegturbo/turbojpeg/TJ.java @@ -88,12 +88,31 @@ final public class TJ { 16, 0, 16, 0, 8, 24, 0 }; - final public static int getBlueShift(int pixelFormat) throws Exception { + public static int getBlueShift(int pixelFormat) throws Exception { if(pixelFormat < 0 || pixelFormat >= NUMPFOPT) throw new Exception("Invalid pixel format"); return blueShift[pixelFormat]; } + // Transform operations + final public static int + NUMXFORMOPT = 8, + XFORM_NONE = 0, + XFORM_HFLIP = 1, + XFORM_VFLIP = 2, + XFORM_TRANSPOSE = 3, + XFORM_TRANSVERSE = 4, + XFORM_ROT90 = 5, + XFORM_ROT180 = 6, + XFORM_ROT270 = 7; + + // Transform options + final public static int + XFORM_PERFECT = 1, + XFORM_TRIM = 2, + XFORM_CROP = 4, + XFORM_GRAY = 8; + // Flags final public static int BOTTOMUP = 2, diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java index 7173db1f..6b8592f2 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -37,15 +37,16 @@ public class TJDecompressor { } public TJDecompressor(byte[] buf) throws Exception { + init(); setJPEGBuffer(buf, buf.length); } public TJDecompressor(byte[] buf, int bufSize) throws Exception { + init(); setJPEGBuffer(buf, bufSize); } public void setJPEGBuffer(byte[] buf, int bufSize) throws Exception { - if(handle == 0) init(); if(buf == null || bufSize < 1) throw new Exception("Invalid argument in setJPEGBuffer()"); jpegBuf = buf; @@ -250,10 +251,10 @@ public class TJDecompressor { System.loadLibrary("turbojpeg"); } - private long handle = 0; - private byte[] jpegBuf = null; - private int jpegBufSize = 0; - private int jpegWidth = 0; - private int jpegHeight = 0; - private int jpegSubsamp = -1; + protected long handle = 0; + protected byte[] jpegBuf = null; + protected int jpegBufSize = 0; + protected int jpegWidth = 0; + protected int jpegHeight = 0; + protected int jpegSubsamp = -1; }; diff --git a/java/org/libjpegturbo/turbojpeg/TJTransformer.java b/java/org/libjpegturbo/turbojpeg/TJTransformer.java new file mode 100644 index 00000000..59783654 --- /dev/null +++ b/java/org/libjpegturbo/turbojpeg/TJTransformer.java @@ -0,0 +1,90 @@ +/* + * Copyright (C)2011 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. + */ + +package org.libjpegturbo.turbojpeg; + +public class TJTransformer extends TJDecompressor { + + public TJTransformer() throws Exception { + init(); + } + + public TJTransformer(byte[] buf) throws Exception { + init(); + setJPEGBuffer(buf, buf.length); + } + + public TJTransformer(byte[] buf, int bufSize) throws Exception { + init(); + setJPEGBuffer(buf, bufSize); + } + + public void transform(byte[][] dstBufs, TJTransform[] transforms, + int flags) throws Exception { + if(jpegBuf == null) throw new Exception("JPEG buffer not initialized"); + transformedSizes = transform(jpegBuf, jpegBufSize, dstBufs, transforms, + flags); + } + + public TJDecompressor[] transform(TJTransform[] transforms, int flags) + throws Exception { + byte[][] dstBufs = new byte[transforms.length][]; + if(jpegWidth < 1 || jpegHeight < 1) + throw new Exception("JPEG buffer not initialized"); + for(int i = 0; i < transforms.length; i++) { + int w = jpegWidth, h = jpegHeight; + if((transforms[i].options & TJ.XFORM_CROP) != 0) { + if(transforms[i].width != 0) w = transforms[i].width; + if(transforms[i].height != 0) h = transforms[i].height; + } + dstBufs[i] = new byte[TJ.bufSize(w, h)]; + } + TJDecompressor[] tjd = new TJDecompressor[transforms.length]; + transform(dstBufs, transforms, flags); + for(int i = 0; i < transforms.length; i++) + tjd[i] = new TJDecompressor(dstBufs[i], transformedSizes[i]); + return tjd; + } + + public int[] getTransformedSizes() throws Exception { + if(transformedSizes == null) + throw new Exception("No image has been transformed yet"); + return transformedSizes; + } + + private native void init() throws Exception; + + private native int[] transform(byte[] srcBuf, int srcSize, byte[][] dstBufs, + TJTransform[] transforms, int flags) throws Exception; + + static { + System.loadLibrary("turbojpeg"); + } + + private int[] transformedSizes = null; +}; diff --git a/java/org_libjpegturbo_turbojpeg_TJTransformer.h b/java/org_libjpegturbo_turbojpeg_TJTransformer.h new file mode 100644 index 00000000..33a7fe2f --- /dev/null +++ b/java/org_libjpegturbo_turbojpeg_TJTransformer.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_libjpegturbo_turbojpeg_TJTransformer */ + +#ifndef _Included_org_libjpegturbo_turbojpeg_TJTransformer +#define _Included_org_libjpegturbo_turbojpeg_TJTransformer +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_libjpegturbo_turbojpeg_TJTransformer + * Method: init + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_init + (JNIEnv *, jobject); + +/* + * Class: org_libjpegturbo_turbojpeg_TJTransformer + * Method: transform + * Signature: ([BI[[B[Lorg/libjpegturbo/turbojpeg/TJ/Transform;I)[I + */ +JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transform + (JNIEnv *, jobject, jbyteArray, jint, jobjectArray, jobjectArray, jint); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index b190f049..3db38b68 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -26,6 +26,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include "turbojpeg.h" #include #include "java/org_libjpegturbo_turbojpeg_TJCompressor.h" @@ -123,6 +125,7 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3 { (*env)->ReleasePrimitiveArrayCritical(env, dst, dstbuf, 0); (*env)->ReleasePrimitiveArrayCritical(env, src, srcbuf, 0); + dstbuf=srcbuf=NULL; _throw(tjGetErrorStr()); } @@ -164,6 +167,7 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3 { (*env)->ReleasePrimitiveArrayCritical(env, dst, dstbuf, 0); (*env)->ReleasePrimitiveArrayCritical(env, src, srcbuf, 0); + dstbuf=srcbuf=NULL; _throw(tjGetErrorStr()); } @@ -204,6 +208,7 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___ { (*env)->ReleasePrimitiveArrayCritical(env, dst, dstbuf, 0); (*env)->ReleasePrimitiveArrayCritical(env, src, srcbuf, 0); + dstbuf=srcbuf=NULL; _throw(tjGetErrorStr()); } @@ -245,6 +250,7 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___ { (*env)->ReleasePrimitiveArrayCritical(env, dst, dstbuf, 0); (*env)->ReleasePrimitiveArrayCritical(env, src, srcbuf, 0); + dstbuf=srcbuf=NULL; _throw(tjGetErrorStr()); } @@ -375,6 +381,7 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress { (*env)->ReleasePrimitiveArrayCritical(env, dst, dstbuf, 0); (*env)->ReleasePrimitiveArrayCritical(env, src, srcbuf, 0); + dstbuf=srcbuf=NULL; _throw(tjGetErrorStr()); } @@ -414,6 +421,7 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress { (*env)->ReleasePrimitiveArrayCritical(env, dst, dstbuf, 0); (*env)->ReleasePrimitiveArrayCritical(env, src, srcbuf, 0); + dstbuf=srcbuf=NULL; _throw(tjGetErrorStr()); } @@ -452,6 +460,7 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress { (*env)->ReleasePrimitiveArrayCritical(env, dst, dstbuf, 0); (*env)->ReleasePrimitiveArrayCritical(env, src, srcbuf, 0); + dstbuf=srcbuf=NULL; _throw(tjGetErrorStr()); } @@ -461,6 +470,127 @@ JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompress return; } +JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_init + (JNIEnv *env, jobject obj) +{ + jclass cls; + jfieldID fid; + tjhandle handle; + + if((handle=tjInitTransform())==NULL) _throw(tjGetErrorStr()); + + bailif0(cls=(*env)->GetObjectClass(env, obj)); + bailif0(fid=(*env)->GetFieldID(env, cls, "handle", "J")); + (*env)->SetLongField(env, obj, fid, (long)handle); + + bailout: + return; +} + +JNIEXPORT jintArray JNICALL Java_org_libjpegturbo_turbojpeg_TJTransformer_transform + (JNIEnv *env, jobject obj, jbyteArray jsrcbuf, jint srcsize, + jobjectArray dstobjs, jobjectArray tobjs, jint flags) +{ + tjhandle handle=0; int i; + unsigned char *srcbuf=NULL, **dstbufs=NULL; jsize n=0; + unsigned long *dstsizes=NULL; tjtransform *t=NULL; + jbyteArray *jdstbufs=NULL; + int jpegWidth=0, jpegHeight=0; + jintArray jdstsizes=0; jint *dstsizesi=NULL; + + gethandle(); + + if((*env)->GetArrayLength(env, jsrcbuf)GetFieldID(env, _cls, "jpegWidth", "I")); + jpegWidth=(int)(*env)->GetIntField(env, obj, _fid); + bailif0(_fid=(*env)->GetFieldID(env, _cls, "jpegHeight", "I")); + jpegHeight=(int)(*env)->GetIntField(env, obj, _fid); + + n=(*env)->GetArrayLength(env, dstobjs); + if(n!=(*env)->GetArrayLength(env, tobjs)) + _throw("Mismatch between size of transforms array and destination buffers array"); + + if((dstbufs=(unsigned char **)malloc(sizeof(unsigned char *)*n))==NULL) + _throw("Memory allocation failure"); + if((jdstbufs=(jbyteArray *)malloc(sizeof(jbyteArray)*n))==NULL) + _throw("Memory allocation failure"); + if((dstsizes=(unsigned long *)malloc(sizeof(unsigned long)*n))==NULL) + _throw("Memory allocation failure"); + if((t=(tjtransform *)malloc(sizeof(tjtransform)*n))==NULL) + _throw("Memory allocation failure"); + for(i=0; iGetObjectArrayElement(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(srcbuf=(*env)->GetPrimitiveArrayCritical(env, jsrcbuf, 0)); + for(i=0; iGetObjectArrayElement(env, dstobjs, i)); + if((*env)->GetArrayLength(env, jdstbufs[i])GetPrimitiveArrayCritical(env, jdstbufs[i], 0)); + } + + if(tjTransform(handle, srcbuf, srcsize, n, dstbufs, dstsizes, t, flags)==-1) + { + (*env)->ReleasePrimitiveArrayCritical(env, jsrcbuf, srcbuf, 0); + srcbuf=NULL; + for(i=0; iReleasePrimitiveArrayCritical(env, jdstbufs[i], dstbufs[i], 0); + dstbufs[i]=NULL; + } + _throw(tjGetErrorStr()); + } + + jdstsizes=(*env)->NewIntArray(env, n); + bailif0(dstsizesi=(*env)->GetIntArrayElements(env, jdstsizes, 0)); + for(i=0; iReleasePrimitiveArrayCritical(env, jsrcbuf, srcbuf, 0); + if(dstbufs) + { + for(i=0; iReleasePrimitiveArrayCritical(env, jdstbufs[i], dstbufs[i], 0); + } + free(dstbufs); + } + if(jdstbufs) free(jdstbufs); + if(dstsizes) free(dstsizes); + if(dstsizesi) (*env)->ReleaseIntArrayElements(env, jdstsizes, dstsizesi, 0); + if(t) free(t); + return jdstsizes; +} + JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJDecompressor_destroy (JNIEnv *env, jobject obj) {