diff --git a/java/TJUnitTest.java b/java/TJUnitTest.java index 56226e2b..4d57f127 100644 --- a/java/TJUnitTest.java +++ b/java/TJUnitTest.java @@ -538,6 +538,54 @@ public class TJUnitTest { return ((v + (p) - 1) & (~((p) - 1))); } + private static void initBufYUV(byte[] buf, int w, int pad, int h, + int subsamp) throws Exception { + int row, col; + int hsf = TJ.getMCUWidth(subsamp) / 8, vsf = TJ.getMCUHeight(subsamp) / 8; + int pw = PAD(w, hsf), ph = PAD(h, vsf); + int cw = pw / hsf, ch = ph / vsf; + int ypitch = PAD(pw, pad), uvpitch = PAD(cw, pad); + int halfway = 16, blockSize = 8; + + Arrays.fill(buf, (byte)0); + for (row = 0; row < ph; row++) { + for (col = 0; col < pw; col++) { + int index = ypitch * row + col; + if (((row / blockSize) + (col / blockSize)) % 2 == 0) { + if (row < halfway) + buf[index] = (byte)255; + else + buf[index] = 0; + } else { + if (row < halfway) + buf[index] = 76; + else + buf[index] = (byte)226; + } + } + } + if (subsamp != TJ.SAMP_GRAY) { + halfway = 16 / vsf; + for (row = 0; row < ch; row++) { + for (col = 0; col < cw; col++) { + int uindex = ypitch * ph + (uvpitch * row + col), + vindex = ypitch * ph + uvpitch * ch + (uvpitch * row + col); + if (((row * vsf / blockSize) + (col * hsf / blockSize)) % 2 == 0) { + buf[uindex] = buf[vindex] = (byte)128; + } else { + if (row < halfway) { + buf[uindex] = 85; + buf[vindex] = (byte)255; + } else { + buf[uindex] = 0; + buf[vindex] = (byte)149; + } + } + } + } + } + } + private static int checkBufYUV(byte[] buf, int size, int w, int h, int subsamp, TJScalingFactor sf) throws Exception { @@ -646,40 +694,47 @@ public class TJUnitTest { byte[] srcBuf = null; BufferedImage img = null; String pfStr; + String buStrLong = (flags & TJ.FLAG_BOTTOMUP) != 0 ? + "Bottom-Up" : "Top-Down "; + String buStr = (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD"; double t; int size = 0, ps, imgType = pf; - if (bi) { - pf = biTypePF(imgType); - pfStr = biTypeStr(imgType); - } else - pfStr = pixFormatStr[pf]; - ps = TJ.getPixelSize(pf); - - System.out.print(pfStr + " "); - if (bi) - System.out.print("(" + pixFormatStr[pf] + ") "); - if ((flags & TJ.FLAG_BOTTOMUP) != 0) - System.out.print("Bottom-Up"); - else - System.out.print("Top-Down "); - System.out.print(" -> " + subNameLong[subsamp] + " "); - if (yuv == YUVENCODE) - System.out.print("YUV ... "); - else - System.out.print("Q" + jpegQual + " ... "); - - if (bi) { - img = new BufferedImage(w, h, imgType); - initImg(img, pf, flags); - tempstr = baseName + "_enc_" + pfStr + "_" + - (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + - subName[subsamp] + "_Q" + jpegQual + ".png"; - File file = new File(tempstr); - ImageIO.write(img, "png", file); + if (yuv == YUVDECODE) { + System.out.format("YUV %s %s --> JPEG Q%d ... ", subNameLong[subsamp], + buStrLong, jpegQual); + srcBuf = new byte[TJ.bufSizeYUV(w, pad, h, subsamp)]; + initBufYUV(srcBuf, w, pad, h, subsamp); + pfStr = "YUV"; } else { - srcBuf = new byte[w * h * ps + 1]; - initBuf(srcBuf, w, w * ps, h, pf, flags); + if (bi) { + pf = biTypePF(imgType); + pfStr = biTypeStr(imgType); + } else + pfStr = pixFormatStr[pf]; + ps = TJ.getPixelSize(pf); + + System.out.print(pfStr + " "); + if (bi) + System.out.print("(" + pixFormatStr[pf] + ") "); + if (yuv == YUVENCODE) + System.out.format("%s -> %s YUV ... ", buStrLong, + subNameLong[subsamp]); + else + System.out.format("%s -> %s Q%d ... ", buStrLong, subNameLong[subsamp], + jpegQual); + + if (bi) { + img = new BufferedImage(w, h, imgType); + initImg(img, pf, flags); + tempstr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + + subName[subsamp] + "_Q" + jpegQual + ".png"; + File file = new File(tempstr); + ImageIO.write(img, "png", file); + } else { + srcBuf = new byte[w * h * ps + 1]; + initBuf(srcBuf, w, w * ps, h, pf, flags); + } } Arrays.fill(dstBuf, (byte)0); @@ -693,7 +748,10 @@ public class TJUnitTest { else tjc.compress(img, dstBuf, flags); } else { - tjc.setSourceImage(srcBuf, w, 0, h, pf); + if (yuv == YUVDECODE) + tjc.setSourceImageYUV(srcBuf, w, pad, h); + else + tjc.setSourceImage(srcBuf, w, 0, h, pf); if (yuv == YUVENCODE) tjc.encodeYUV(dstBuf, flags); else @@ -703,12 +761,10 @@ public class TJUnitTest { t = getTime() - t; if (yuv == YUVENCODE) - tempstr = baseName + "_enc_" + pfStr + "_" + - (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + + tempstr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + subName[subsamp] + ".yuv"; else - tempstr = baseName + "_enc_" + pfStr + "_" + - (((flags & TJ.FLAG_BOTTOMUP) != 0) ? "BU" : "TD") + "_" + + tempstr = baseName + "_enc_" + pfStr + "_" + buStr + "_" + subName[subsamp] + "_Q" + jpegQual + ".jpg"; writeJPEG(dstBuf, size, tempstr); diff --git a/java/doc/index-all.html b/java/doc/index-all.html index 45553793..916b3c2e 100644 --- a/java/doc/index-all.html +++ b/java/doc/index-all.html @@ -525,6 +525,10 @@ Method in class org.libjpegturbo.turbojpeg.TJCompressor
Deprecated. Use TJCompressor.setSourceImage(byte[], int, int, int, int, int, int) instead. +
setSourceImageYUV(byte[], int, int, int) - +Method in class org.libjpegturbo.turbojpeg.TJCompressor +
Associate an uncompressed YUV planar source image with this compressor + instance.
setSubsamp(int) - Method in class org.libjpegturbo.turbojpeg.TJCompressor
Set the level of chrominance subsampling for subsequent compress/encode diff --git a/java/doc/index.html b/java/doc/index.html index 87aed330..356cd1d9 100644 --- a/java/doc/index.html +++ b/java/doc/index.html @@ -9,8 +9,42 @@ Generated Documentation (Untitled) targetPage = "" + window.location.search; if (targetPage != "" && targetPage != "undefined") targetPage = targetPage.substring(1); - if (targetPage.indexOf(":") != -1) + if (targetPage.indexOf(":") != -1 || (targetPage != "" && !validURL(targetPage))) targetPage = "undefined"; + function validURL(url) { + var pos = url.indexOf(".html"); + if (pos == -1 || pos != url.length - 5) + return false; + var allowNumber = false; + var allowSep = false; + var seenDot = false; + for (var i = 0; i < url.length - 5; i++) { + var ch = url.charAt(i); + if ('a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + ch == '$' || + ch == '_') { + allowNumber = true; + allowSep = true; + } else if ('0' <= ch && ch <= '9' + || ch == '-') { + if (!allowNumber) + return false; + } else if (ch == '/' || ch == '.') { + if (!allowSep) + return false; + allowNumber = false; + allowSep = false; + if (ch == '.') + seenDot = true; + if (ch == '/' && seenDot) + return false; + } else { + return false; + } + } + return true; + } function loadFrames() { if (targetPage != "" && targetPage != "undefined") top.classFrame.location = top.targetPage; diff --git a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html index a41ea065..35114c70 100644 --- a/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html +++ b/java/doc/org/libjpegturbo/turbojpeg/TJCompressor.html @@ -298,6 +298,18 @@ TurboJPEG compressor  void +setSourceImageYUV(byte[] srcImage, + int width, + int pad, + int height) + +
+          Associate an uncompressed YUV planar source image with this compressor + instance. + + + + void setSubsamp(int newSubsamp)
@@ -462,6 +474,38 @@ setSourceImage
+

+setSourceImageYUV

+
+public void setSourceImageYUV(byte[] srcImage,
+                              int width,
+                              int pad,
+                              int height)
+                       throws java.lang.Exception
+
+
Associate an uncompressed YUV planar source image with this compressor + instance. +

+

+
Parameters:
srcImage - image buffer containing a YUV planar image to be + compressed. The Y, U (Cb), and V (Cr) image planes should be stored + sequentially in the buffer, and the size of each plane is determined by + the specified width, height, and padding, as well as the level of + chrominance subsampling (specified using setSubsamp(int).) If the + chrominance components are subsampled along the horizontal dimension, then + the width of the luminance plane should be padded to the nearest multiple + of 2 (same goes for the height of the luminance plane, if the chrominance + components are subsampled along the vertical dimension.) This is + irrespective of any additional padding specified in the pad + parameter.
width - width (in pixels) of the source image
pad - the line padding used in the source image. For instance, if + each line in each plane of the YUV image is padded to the nearest multiple + of 4 bytes, then pad should be set to 4.
height - height (in pixels) of the source image +
Throws: +
java.lang.Exception
+
+
+
+

setSubsamp

@@ -476,6 +520,10 @@ public void setSubsamp(int newSubsamp)
  image with little perceptible loss of image clarity (the human eye is more
  sensitive to small changes in brightness than to small changes in color.)
  This is called "chrominance subsampling".
+ 

+ NOTE: When compressing a YUV planar image into a JPEG image, this method + also specifies the level of chrominance subsampling used in the source + image.

Parameters:
newSubsamp - the new level of chrominance subsampling (one of diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java index e8dd260e..63a7fa5c 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -139,6 +139,7 @@ public class TJCompressor { srcPixelFormat = pixelFormat; srcX = x; srcY = y; + srcIsYUV = false; } /** @@ -152,6 +153,41 @@ public class TJCompressor { srcX = srcY = -1; } + /** + * Associate an uncompressed YUV planar source image with this compressor + * instance. + * + * @param srcImage image buffer containing a YUV planar image to be + * compressed. The Y, U (Cb), and V (Cr) image planes should be stored + * sequentially in the buffer, and the size of each plane is determined by + * the specified width, height, and padding, as well as the level of + * chrominance subsampling (specified using {@link #setSubsamp}.) If the + * chrominance components are subsampled along the horizontal dimension, then + * the width of the luminance plane should be padded to the nearest multiple + * of 2 (same goes for the height of the luminance plane, if the chrominance + * components are subsampled along the vertical dimension.) This is + * irrespective of any additional padding specified in the pad + * parameter. + * + * @param width width (in pixels) of the source image + * + * @param pad the line padding used in the source image. For instance, if + * each line in each plane of the YUV image is padded to the nearest multiple + * of 4 bytes, then pad should be set to 4. + * + * @param height height (in pixels) of the source image + */ + public void setSourceImageYUV(byte[] srcImage, int width, int pad, + int height) throws Exception { + if (handle == 0) init(); + if (srcImage == null || width < 1 || pad < 1 || height < 1) + throw new Exception("Invalid argument in setSourceImageYUV()"); + srcBuf = srcImage; + srcWidth = width; + srcYUVPad = pad; + srcHeight = height; + srcIsYUV = true; + } /** * Set the level of chrominance subsampling for subsequent compress/encode @@ -162,6 +198,10 @@ public class TJCompressor { * image with little perceptible loss of image clarity (the human eye is more * sensitive to small changes in brightness than to small changes in color.) * This is called "chrominance subsampling". + *

+ * NOTE: When compressing a YUV planar image into a JPEG image, this method + * also specifies the level of chrominance subsampling used in the source + * image. * * @param newSubsamp the new level of chrominance subsampling (one of * {@link TJ TJ.SAMP_*}) @@ -203,14 +243,19 @@ public class TJCompressor { throw new Exception("JPEG Quality not set"); if (subsamp < 0) throw new Exception("Subsampling level not set"); - if (srcX >= 0 && srcY >= 0) - compressedSize = compress(srcBuf, srcX, srcY, srcWidth, srcPitch, - srcHeight, srcPixelFormat, dstBuf, subsamp, - jpegQuality, flags); - else - compressedSize = compress(srcBuf, srcWidth, srcPitch, srcHeight, - srcPixelFormat, dstBuf, subsamp, jpegQuality, - flags); + if (srcIsYUV) + compressedSize = compressFromYUV(srcBuf, srcWidth, srcYUVPad, srcHeight, + subsamp, dstBuf, jpegQuality, flags); + else { + if (srcX >= 0 && srcY >= 0) + compressedSize = compress(srcBuf, srcX, srcY, srcWidth, srcPitch, + srcHeight, srcPixelFormat, dstBuf, subsamp, + jpegQuality, flags); + else + compressedSize = compress(srcBuf, srcWidth, srcPitch, srcHeight, + srcPixelFormat, dstBuf, subsamp, jpegQuality, + flags); + } } /** @@ -550,6 +595,10 @@ public class TJCompressor { int stride, int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp, int jpegQual, int flags) throws Exception; + private native int compressFromYUV(byte[] srcBuf, int width, int pad, + int height, int subsamp, byte[] dstBuf, int jpegQual, int flags) + throws Exception; + private native void encodeYUV(byte[] srcBuf, int width, int pitch, int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags) throws Exception; // deprecated @@ -578,6 +627,8 @@ public class TJCompressor { private int srcY = -1; private int srcPitch = 0; private int srcPixelFormat = -1; + private int srcYUVPad = -1; + private boolean srcIsYUV; private int subsamp = -1; private int jpegQuality = -1; private int compressedSize = 0; diff --git a/java/org_libjpegturbo_turbojpeg_TJCompressor.h b/java/org_libjpegturbo_turbojpeg_TJCompressor.h index afec0772..50070efa 100644 --- a/java/org_libjpegturbo_turbojpeg_TJCompressor.h +++ b/java/org_libjpegturbo_turbojpeg_TJCompressor.h @@ -55,6 +55,14 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3 JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3IIIIIII_3BIII (JNIEnv *, jobject, jintArray, jint, jint, jint, jint, jint, jint, jbyteArray, jint, jint, jint); +/* + * Class: org_libjpegturbo_turbojpeg_TJCompressor + * Method: compressFromYUV + * Signature: ([BIIII[BII)I + */ +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3BIIII_3BII + (JNIEnv *, jobject, jbyteArray, jint, jint, jint, jint, jbyteArray, jint, jint); + /* * Class: org_libjpegturbo_turbojpeg_TJCompressor * Method: encodeYUV diff --git a/turbojpeg-jni.c b/turbojpeg-jni.c index 09c557b4..efe55902 100644 --- a/turbojpeg-jni.c +++ b/turbojpeg-jni.c @@ -214,6 +214,42 @@ JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compress___3 flags); } +JNIEXPORT jint JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3BIIII_3BII + (JNIEnv *env, jobject obj, jbyteArray src, jint width, jint pad, jint height, + jint subsamp, jbyteArray dst, jint jpegQual, jint flags) +{ + tjhandle handle=0; + unsigned long jpegSize=0; + jsize arraySize=0; + unsigned char *srcBuf=NULL, *jpegBuf=NULL; + + gethandle(); + + arraySize=tjBufSizeYUV2(width, pad, height, subsamp); + if((*env)->GetArrayLength(env, src)GetArrayLength(env, dst)<(jsize)jpegSize) + _throw("Destination buffer is not large enough"); + + bailif0(srcBuf=(*env)->GetPrimitiveArrayCritical(env, src, 0)); + bailif0(jpegBuf=(*env)->GetPrimitiveArrayCritical(env, dst, 0)); + + if(tjCompressFromYUV(handle, srcBuf, width, pad, height, subsamp, &jpegBuf, + &jpegSize, jpegQual, flags|TJFLAG_NOREALLOC)==-1) + { + (*env)->ReleasePrimitiveArrayCritical(env, dst, jpegBuf, 0); + (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0); + jpegBuf=srcBuf=NULL; + _throw(tjGetErrorStr()); + } + + bailout: + if(jpegBuf) (*env)->ReleasePrimitiveArrayCritical(env, dst, jpegBuf, 0); + if(srcBuf) (*env)->ReleasePrimitiveArrayCritical(env, src, srcBuf, 0); + return (jint)jpegSize; +} + JNIEXPORT void JNICALL Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BIII (JNIEnv *env, jobject obj, jbyteArray src, jint width, jint pitch, jint height, jint pf, jbyteArray dst, jint pad, jint subsamp, jint flags) diff --git a/turbojpeg-mapfile.jni b/turbojpeg-mapfile.jni index 7b050cd2..f90f9434 100755 --- a/turbojpeg-mapfile.jni +++ b/turbojpeg-mapfile.jni @@ -72,6 +72,7 @@ TURBOJPEG_1.4 tjDecompressToYUV2; tjEncodeYUV3; Java_org_libjpegturbo_turbojpeg_TJ_bufSizeYUV__IIII; + Java_org_libjpegturbo_turbojpeg_TJCompressor_compressFromYUV___3BIIII_3BII; Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3BIIII_3BIII; Java_org_libjpegturbo_turbojpeg_TJCompressor_encodeYUV___3IIIII_3BIII; Java_org_libjpegturbo_turbojpeg_TJDecompressor_decompressToYUV___3BI_3BIIII;