Index: java/org/libjpegturbo/turbojpeg/TJDecompressor.java |
diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..bd0e6943d977aa8243360efb181f9e2230475f48 |
--- /dev/null |
+++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java |
@@ -0,0 +1,909 @@ |
+/* |
+ * Copyright (C)2011-2015 D. R. Commander. All Rights Reserved. |
+ * Copyright (C)2015 Viktor Szathmáry. 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; |
+ |
+import java.awt.image.*; |
+import java.nio.*; |
+import java.io.*; |
+ |
+/** |
+ * TurboJPEG decompressor |
+ */ |
+public class TJDecompressor implements Closeable { |
+ |
+ private static final String NO_ASSOC_ERROR = |
+ "No JPEG image is associated with this instance"; |
+ |
+ /** |
+ * Create a TurboJPEG decompresssor instance. |
+ */ |
+ public TJDecompressor() throws TJException { |
+ init(); |
+ } |
+ |
+ /** |
+ * Create a TurboJPEG decompressor instance and associate the JPEG source |
+ * image stored in <code>jpegImage</code> with the newly created instance. |
+ * |
+ * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to |
+ * be the length of the array.) This buffer is not modified. |
+ */ |
+ public TJDecompressor(byte[] jpegImage) throws TJException { |
+ init(); |
+ setSourceImage(jpegImage, jpegImage.length); |
+ } |
+ |
+ /** |
+ * Create a TurboJPEG decompressor instance and associate the JPEG source |
+ * image of length <code>imageSize</code> bytes stored in |
+ * <code>jpegImage</code> with the newly created instance. |
+ * |
+ * @param jpegImage JPEG image buffer. This buffer is not modified. |
+ * |
+ * @param imageSize size of the JPEG image (in bytes) |
+ */ |
+ public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException { |
+ init(); |
+ setSourceImage(jpegImage, imageSize); |
+ } |
+ |
+ /** |
+ * Create a TurboJPEG decompressor instance and associate the YUV planar |
+ * source image stored in <code>yuvImage</code> with the newly created |
+ * instance. |
+ * |
+ * @param yuvImage {@link YUVImage} instance containing a YUV planar |
+ * image to be decoded. This image is not modified. |
+ */ |
+ public TJDecompressor(YUVImage yuvImage) throws TJException { |
+ init(); |
+ setSourceImage(yuvImage); |
+ } |
+ |
+ /** |
+ * Associate the JPEG image of length <code>imageSize</code> bytes stored in |
+ * <code>jpegImage</code> with this decompressor instance. This image will |
+ * be used as the source image for subsequent decompress operations. |
+ * |
+ * @param jpegImage JPEG image buffer. This buffer is not modified. |
+ * |
+ * @param imageSize size of the JPEG image (in bytes) |
+ */ |
+ public void setSourceImage(byte[] jpegImage, int imageSize) |
+ throws TJException { |
+ if (jpegImage == null || imageSize < 1) |
+ throw new IllegalArgumentException("Invalid argument in setSourceImage()"); |
+ jpegBuf = jpegImage; |
+ jpegBufSize = imageSize; |
+ decompressHeader(jpegBuf, jpegBufSize); |
+ yuvImage = null; |
+ } |
+ |
+ /** |
+ * @deprecated Use {@link #setSourceImage(byte[], int)} instead. |
+ */ |
+ @Deprecated |
+ public void setJPEGImage(byte[] jpegImage, int imageSize) |
+ throws TJException { |
+ setSourceImage(jpegImage, imageSize); |
+ } |
+ |
+ /** |
+ * Associate the specified YUV planar source image with this decompressor |
+ * instance. Subsequent decompress operations will decode this image into an |
+ * RGB or grayscale destination image. |
+ * |
+ * @param srcImage {@link YUVImage} instance containing a YUV planar image to |
+ * be decoded. This image is not modified. |
+ */ |
+ public void setSourceImage(YUVImage srcImage) { |
+ if (srcImage == null) |
+ throw new IllegalArgumentException("Invalid argument in setSourceImage()"); |
+ yuvImage = srcImage; |
+ jpegBuf = null; |
+ jpegBufSize = 0; |
+ } |
+ |
+ |
+ /** |
+ * Returns the width of the source image (JPEG or YUV) associated with this |
+ * decompressor instance. |
+ * |
+ * @return the width of the source image (JPEG or YUV) associated with this |
+ * decompressor instance. |
+ */ |
+ public int getWidth() { |
+ if (yuvImage != null) |
+ return yuvImage.getWidth(); |
+ if (jpegWidth < 1) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ return jpegWidth; |
+ } |
+ |
+ /** |
+ * Returns the height of the source image (JPEG or YUV) associated with this |
+ * decompressor instance. |
+ * |
+ * @return the height of the source image (JPEG or YUV) associated with this |
+ * decompressor instance. |
+ */ |
+ public int getHeight() { |
+ if (yuvImage != null) |
+ return yuvImage.getHeight(); |
+ if (jpegHeight < 1) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ return jpegHeight; |
+ } |
+ |
+ /** |
+ * Returns the level of chrominance subsampling used in the source image |
+ * (JPEG or YUV) associated with this decompressor instance. See |
+ * {@link TJ#SAMP_444 TJ.SAMP_*}. |
+ * |
+ * @return the level of chrominance subsampling used in the source image |
+ * (JPEG or YUV) associated with this decompressor instance. |
+ */ |
+ public int getSubsamp() { |
+ if (yuvImage != null) |
+ return yuvImage.getSubsamp(); |
+ if (jpegSubsamp < 0) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (jpegSubsamp >= TJ.NUMSAMP) |
+ throw new IllegalStateException("JPEG header information is invalid"); |
+ return jpegSubsamp; |
+ } |
+ |
+ /** |
+ * Returns the colorspace used in the source image (JPEG or YUV) associated |
+ * with this decompressor instance. See {@link TJ#CS_RGB TJ.CS_*}. If the |
+ * source image is YUV, then this always returns {@link TJ#CS_YCbCr}. |
+ * |
+ * @return the colorspace used in the source image (JPEG or YUV) associated |
+ * with this decompressor instance. |
+ */ |
+ public int getColorspace() { |
+ if (yuvImage != null) |
+ return TJ.CS_YCbCr; |
+ if (jpegColorspace < 0) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (jpegColorspace >= TJ.NUMCS) |
+ throw new IllegalStateException("JPEG header information is invalid"); |
+ return jpegColorspace; |
+ } |
+ |
+ /** |
+ * Returns the JPEG image buffer associated with this decompressor instance. |
+ * |
+ * @return the JPEG image buffer associated with this decompressor instance. |
+ */ |
+ public byte[] getJPEGBuf() { |
+ if (jpegBuf == null) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ return jpegBuf; |
+ } |
+ |
+ /** |
+ * Returns the size of the JPEG image (in bytes) associated with this |
+ * decompressor instance. |
+ * |
+ * @return the size of the JPEG image (in bytes) associated with this |
+ * decompressor instance. |
+ */ |
+ public int getJPEGSize() { |
+ if (jpegBufSize < 1) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ return jpegBufSize; |
+ } |
+ |
+ /** |
+ * Returns the width of the largest scaled-down image that the TurboJPEG |
+ * decompressor can generate without exceeding the desired image width and |
+ * height. |
+ * |
+ * @param desiredWidth desired width (in pixels) of the decompressed image. |
+ * Setting this to 0 is the same as setting it to the width of the JPEG image |
+ * (in other words, the width will not be considered when determining the |
+ * scaled image size.) |
+ * |
+ * @param desiredHeight desired height (in pixels) of the decompressed image. |
+ * Setting this to 0 is the same as setting it to the height of the JPEG |
+ * image (in other words, the height will not be considered when determining |
+ * the scaled image size.) |
+ * |
+ * @return the width of the largest scaled-down image that the TurboJPEG |
+ * decompressor can generate without exceeding the desired image width and |
+ * height. |
+ */ |
+ public int getScaledWidth(int desiredWidth, int desiredHeight) { |
+ if (jpegWidth < 1 || jpegHeight < 1) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (desiredWidth < 0 || desiredHeight < 0) |
+ throw new IllegalArgumentException("Invalid argument in getScaledWidth()"); |
+ TJScalingFactor[] sf = TJ.getScalingFactors(); |
+ if (desiredWidth == 0) |
+ desiredWidth = jpegWidth; |
+ if (desiredHeight == 0) |
+ desiredHeight = jpegHeight; |
+ int scaledWidth = jpegWidth, scaledHeight = jpegHeight; |
+ for (int i = 0; i < sf.length; i++) { |
+ scaledWidth = sf[i].getScaled(jpegWidth); |
+ scaledHeight = sf[i].getScaled(jpegHeight); |
+ if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight) |
+ break; |
+ } |
+ if (scaledWidth > desiredWidth || scaledHeight > desiredHeight) |
+ throw new IllegalArgumentException("Could not scale down to desired image dimensions"); |
+ return scaledWidth; |
+ } |
+ |
+ /** |
+ * Returns the height of the largest scaled-down image that the TurboJPEG |
+ * decompressor can generate without exceeding the desired image width and |
+ * height. |
+ * |
+ * @param desiredWidth desired width (in pixels) of the decompressed image. |
+ * Setting this to 0 is the same as setting it to the width of the JPEG image |
+ * (in other words, the width will not be considered when determining the |
+ * scaled image size.) |
+ * |
+ * @param desiredHeight desired height (in pixels) of the decompressed image. |
+ * Setting this to 0 is the same as setting it to the height of the JPEG |
+ * image (in other words, the height will not be considered when determining |
+ * the scaled image size.) |
+ * |
+ * @return the height of the largest scaled-down image that the TurboJPEG |
+ * decompressor can generate without exceeding the desired image width and |
+ * height. |
+ */ |
+ public int getScaledHeight(int desiredWidth, int desiredHeight) { |
+ if (jpegWidth < 1 || jpegHeight < 1) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (desiredWidth < 0 || desiredHeight < 0) |
+ throw new IllegalArgumentException("Invalid argument in getScaledHeight()"); |
+ TJScalingFactor[] sf = TJ.getScalingFactors(); |
+ if (desiredWidth == 0) |
+ desiredWidth = jpegWidth; |
+ if (desiredHeight == 0) |
+ desiredHeight = jpegHeight; |
+ int scaledWidth = jpegWidth, scaledHeight = jpegHeight; |
+ for (int i = 0; i < sf.length; i++) { |
+ scaledWidth = sf[i].getScaled(jpegWidth); |
+ scaledHeight = sf[i].getScaled(jpegHeight); |
+ if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight) |
+ break; |
+ } |
+ if (scaledWidth > desiredWidth || scaledHeight > desiredHeight) |
+ throw new IllegalArgumentException("Could not scale down to desired image dimensions"); |
+ return scaledHeight; |
+ } |
+ |
+ /** |
+ * Decompress the JPEG source image or decode the YUV source image associated |
+ * with this decompressor instance and output a grayscale, RGB, or CMYK image |
+ * to the given destination buffer. |
+ * |
+ * @param dstBuf buffer that will receive the decompressed/decoded image. |
+ * If the source image is a JPEG image, then this buffer should normally be |
+ * <code>pitch * scaledHeight</code> bytes in size, where |
+ * <code>scaledHeight</code> can be determined by calling <code> |
+ * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) |
+ * </code> with one of the scaling factors returned from {@link |
+ * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the |
+ * source image is a YUV image, then this buffer should normally be |
+ * <code>pitch * height</code> bytes in size, where <code>height</code> is |
+ * the height of the YUV image. However, the buffer may also be larger than |
+ * the dimensions of the source image, in which case the <code>x</code>, |
+ * <code>y</code>, and <code>pitch</code> parameters can be used to specify |
+ * the region into which the source image should be decompressed/decoded. |
+ * |
+ * @param x x offset (in pixels) of the region in the destination image into |
+ * which the source image should be decompressed/decoded |
+ * |
+ * @param y y offset (in pixels) of the region in the destination image into |
+ * which the source image should be decompressed/decoded |
+ * |
+ * @param desiredWidth If the source image is a JPEG image, then this |
+ * specifies the desired width (in pixels) of the decompressed image (or |
+ * image region.) If the desired destination image dimensions are different |
+ * than the source image dimensions, then TurboJPEG will use scaling in the |
+ * JPEG decompressor to generate the largest possible image that will fit |
+ * within the desired dimensions. Setting this to 0 is the same as setting |
+ * it to the width of the JPEG image (in other words, the width will not be |
+ * considered when determining the scaled image size.) This parameter is |
+ * ignored if the source image is a YUV image. |
+ * |
+ * @param pitch bytes per line of the destination image. Normally, this |
+ * should be set to <code>scaledWidth * TJ.pixelSize(pixelFormat)</code> if |
+ * the destination image is unpadded, but you can use this to, for instance, |
+ * pad each line of the destination image to a 4-byte boundary or to |
+ * decompress/decode the source image into a region of a larger image. NOTE: |
+ * if the source image is a JPEG image, then <code>scaledWidth</code> can be |
+ * determined by calling <code> |
+ * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) |
+ * </code> or by calling {@link #getScaledWidth}. If the source image is a |
+ * YUV image, then <code>scaledWidth</code> is the width of the YUV image. |
+ * Setting this parameter to 0 is the equivalent of setting it to |
+ * <code>scaledWidth * TJ.pixelSize(pixelFormat)</code>. |
+ * |
+ * @param desiredHeight If the source image is a JPEG image, then this |
+ * specifies the desired height (in pixels) of the decompressed image (or |
+ * image region.) If the desired destination image dimensions are different |
+ * than the source image dimensions, then TurboJPEG will use scaling in the |
+ * JPEG decompressor to generate the largest possible image that will fit |
+ * within the desired dimensions. Setting this to 0 is the same as setting |
+ * it to the height of the JPEG image (in other words, the height will not be |
+ * considered when determining the scaled image size.) This parameter is |
+ * ignored if the source image is a YUV image. |
+ * |
+ * @param pixelFormat pixel format of the decompressed/decoded image (one of |
+ * {@link TJ#PF_RGB TJ.PF_*}) |
+ * |
+ * @param flags the bitwise OR of one or more of |
+ * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} |
+ */ |
+ public void decompress(byte[] dstBuf, int x, int y, int desiredWidth, |
+ int pitch, int desiredHeight, int pixelFormat, |
+ int flags) throws TJException { |
+ if (jpegBuf == null && yuvImage == null) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || |
+ (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || |
+ pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) |
+ throw new IllegalArgumentException("Invalid argument in decompress()"); |
+ if (yuvImage != null) |
+ decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), |
+ yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y, |
+ yuvImage.getWidth(), pitch, yuvImage.getHeight(), pixelFormat, |
+ flags); |
+ else { |
+ if (x > 0 || y > 0) |
+ decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, pitch, |
+ desiredHeight, pixelFormat, flags); |
+ else |
+ decompress(jpegBuf, jpegBufSize, dstBuf, desiredWidth, pitch, |
+ desiredHeight, pixelFormat, flags); |
+ } |
+ } |
+ |
+ /** |
+ * @deprecated Use |
+ * {@link #decompress(byte[], int, int, int, int, int, int, int)} instead. |
+ */ |
+ @Deprecated |
+ public void decompress(byte[] dstBuf, int desiredWidth, int pitch, |
+ int desiredHeight, int pixelFormat, int flags) |
+ throws TJException { |
+ decompress(dstBuf, 0, 0, desiredWidth, pitch, desiredHeight, pixelFormat, |
+ flags); |
+ } |
+ |
+ /** |
+ * Decompress the JPEG source image associated with this decompressor |
+ * instance and return a buffer containing the decompressed image. |
+ * |
+ * @param desiredWidth see |
+ * {@link #decompress(byte[], int, int, int, int, int, int, int)} |
+ * for description |
+ * |
+ * @param pitch see |
+ * {@link #decompress(byte[], int, int, int, int, int, int, int)} |
+ * for description |
+ * |
+ * @param desiredHeight see |
+ * {@link #decompress(byte[], int, int, int, int, int, int, int)} |
+ * for description |
+ * |
+ * @param pixelFormat pixel format of the decompressed image (one of |
+ * {@link TJ#PF_RGB TJ.PF_*}) |
+ * |
+ * @param flags the bitwise OR of one or more of |
+ * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} |
+ * |
+ * @return a buffer containing the decompressed image. |
+ */ |
+ public byte[] decompress(int desiredWidth, int pitch, int desiredHeight, |
+ int pixelFormat, int flags) throws TJException { |
+ if (pitch < 0 || |
+ (yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) || |
+ pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) |
+ throw new IllegalArgumentException("Invalid argument in decompress()"); |
+ int pixelSize = TJ.getPixelSize(pixelFormat); |
+ int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); |
+ int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); |
+ if (pitch == 0) |
+ pitch = scaledWidth * pixelSize; |
+ byte[] buf = new byte[pitch * scaledHeight]; |
+ decompress(buf, desiredWidth, pitch, desiredHeight, pixelFormat, flags); |
+ return buf; |
+ } |
+ |
+ /** |
+ * Decompress the JPEG source image associated with this decompressor |
+ * instance into a YUV planar image and store it in the given |
+ * <code>YUVImage</code> instance. This method performs JPEG decompression |
+ * but leaves out the color conversion step, so a planar YUV image is |
+ * generated instead of an RGB or grayscale image. This method cannot be |
+ * used to decompress JPEG source images with the CMYK or YCCK colorspace. |
+ * |
+ * @param dstImage {@link YUVImage} instance that will receive the YUV planar |
+ * image. The level of subsampling specified in this <code>YUVImage</code> |
+ * instance must match that of the JPEG image, and the width and height |
+ * specified in the <code>YUVImage</code> instance must match one of the |
+ * scaled image sizes that TurboJPEG is capable of generating from the JPEG |
+ * source image. |
+ * |
+ * @param flags the bitwise OR of one or more of |
+ * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} |
+ */ |
+ public void decompressToYUV(YUVImage dstImage, int flags) |
+ throws TJException { |
+ if (jpegBuf == null) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (dstImage == null || flags < 0) |
+ throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); |
+ int scaledWidth = getScaledWidth(dstImage.getWidth(), |
+ dstImage.getHeight()); |
+ int scaledHeight = getScaledHeight(dstImage.getWidth(), |
+ dstImage.getHeight()); |
+ if (scaledWidth != dstImage.getWidth() || |
+ scaledHeight != dstImage.getHeight()) |
+ throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating."); |
+ if (jpegSubsamp != dstImage.getSubsamp()) |
+ throw new IllegalArgumentException("YUVImage subsampling level does not match that of the JPEG image"); |
+ |
+ decompressToYUV(jpegBuf, jpegBufSize, dstImage.getPlanes(), |
+ dstImage.getOffsets(), dstImage.getWidth(), |
+ dstImage.getStrides(), dstImage.getHeight(), flags); |
+ } |
+ |
+ /** |
+ * @deprecated Use {@link #decompressToYUV(YUVImage, int)} instead. |
+ */ |
+ @Deprecated |
+ public void decompressToYUV(byte[] dstBuf, int flags) throws TJException { |
+ YUVImage dstImage = new YUVImage(dstBuf, jpegWidth, 4, jpegHeight, |
+ jpegSubsamp); |
+ decompressToYUV(dstImage, flags); |
+ } |
+ |
+ /** |
+ * Decompress the JPEG source image associated with this decompressor |
+ * instance into a set of Y, U (Cb), and V (Cr) image planes and return a |
+ * <code>YUVImage</code> instance containing the decompressed image planes. |
+ * This method performs JPEG decompression but leaves out the color |
+ * conversion step, so a planar YUV image is generated instead of an RGB or |
+ * grayscale image. This method cannot be used to decompress JPEG source |
+ * images with the CMYK or YCCK colorspace. |
+ * |
+ * @param desiredWidth desired width (in pixels) of the YUV image. If the |
+ * desired image dimensions are different than the dimensions of the JPEG |
+ * image being decompressed, then TurboJPEG will use scaling in the JPEG |
+ * decompressor to generate the largest possible image that will fit within |
+ * the desired dimensions. Setting this to 0 is the same as setting it to |
+ * the width of the JPEG image (in other words, the width will not be |
+ * considered when determining the scaled image size.) |
+ * |
+ * @param strides an array of integers, each specifying the number of bytes |
+ * per line in the corresponding plane of the output image. Setting the |
+ * stride for any plane to 0 is the same as setting it to the scaled |
+ * component width of the plane. If <tt>strides</tt> is NULL, then the |
+ * strides for all planes will be set to their respective scaled component |
+ * widths. You can adjust the strides in order to add an arbitrary amount of |
+ * line padding to each plane. |
+ * |
+ * @param desiredHeight desired height (in pixels) of the YUV image. If the |
+ * desired image dimensions are different than the dimensions of the JPEG |
+ * image being decompressed, then TurboJPEG will use scaling in the JPEG |
+ * decompressor to generate the largest possible image that will fit within |
+ * the desired dimensions. Setting this to 0 is the same as setting it to |
+ * the height of the JPEG image (in other words, the height will not be |
+ * considered when determining the scaled image size.) |
+ * |
+ * @param flags the bitwise OR of one or more of |
+ * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} |
+ * |
+ * @return a YUV planar image. |
+ */ |
+ public YUVImage decompressToYUV(int desiredWidth, int[] strides, |
+ int desiredHeight, |
+ int flags) throws TJException { |
+ if (flags < 0) |
+ throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); |
+ if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (jpegSubsamp >= TJ.NUMSAMP) |
+ throw new IllegalStateException("JPEG header information is invalid"); |
+ if (yuvImage != null) |
+ throw new IllegalStateException("Source image is the wrong type"); |
+ |
+ int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); |
+ int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); |
+ YUVImage yuvImage = new YUVImage(scaledWidth, null, scaledHeight, |
+ jpegSubsamp); |
+ decompressToYUV(yuvImage, flags); |
+ return yuvImage; |
+ } |
+ |
+ /** |
+ * Decompress the JPEG source image associated with this decompressor |
+ * instance into a unified YUV planar image buffer and return a |
+ * <code>YUVImage</code> instance containing the decompressed image. This |
+ * method performs JPEG decompression but leaves out the color conversion |
+ * step, so a planar YUV image is generated instead of an RGB or grayscale |
+ * image. This method cannot be used to decompress JPEG source images with |
+ * the CMYK or YCCK colorspace. |
+ * |
+ * @param desiredWidth desired width (in pixels) of the YUV image. If the |
+ * desired image dimensions are different than the dimensions of the JPEG |
+ * image being decompressed, then TurboJPEG will use scaling in the JPEG |
+ * decompressor to generate the largest possible image that will fit within |
+ * the desired dimensions. Setting this to 0 is the same as setting it to |
+ * the width of the JPEG image (in other words, the width will not be |
+ * considered when determining the scaled image size.) |
+ * |
+ * @param pad the width of each line in each plane of the YUV image will be |
+ * padded to the nearest multiple of this number of bytes (must be a power of |
+ * 2.) |
+ * |
+ * @param desiredHeight desired height (in pixels) of the YUV image. If the |
+ * desired image dimensions are different than the dimensions of the JPEG |
+ * image being decompressed, then TurboJPEG will use scaling in the JPEG |
+ * decompressor to generate the largest possible image that will fit within |
+ * the desired dimensions. Setting this to 0 is the same as setting it to |
+ * the height of the JPEG image (in other words, the height will not be |
+ * considered when determining the scaled image size.) |
+ * |
+ * @param flags the bitwise OR of one or more of |
+ * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} |
+ * |
+ * @return a YUV planar image. |
+ */ |
+ public YUVImage decompressToYUV(int desiredWidth, int pad, int desiredHeight, |
+ int flags) throws TJException { |
+ if (flags < 0) |
+ throw new IllegalArgumentException("Invalid argument in decompressToYUV()"); |
+ if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (jpegSubsamp >= TJ.NUMSAMP) |
+ throw new IllegalStateException("JPEG header information is invalid"); |
+ if (yuvImage != null) |
+ throw new IllegalStateException("Source image is the wrong type"); |
+ |
+ int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); |
+ int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); |
+ YUVImage yuvImage = new YUVImage(scaledWidth, pad, scaledHeight, |
+ jpegSubsamp); |
+ decompressToYUV(yuvImage, flags); |
+ return yuvImage; |
+ } |
+ |
+ /** |
+ * @deprecated Use {@link #decompressToYUV(int, int, int, int)} instead. |
+ */ |
+ @Deprecated |
+ public byte[] decompressToYUV(int flags) throws TJException { |
+ YUVImage dstImage = new YUVImage(jpegWidth, 4, jpegHeight, jpegSubsamp); |
+ decompressToYUV(dstImage, flags); |
+ return dstImage.getBuf(); |
+ } |
+ |
+ /** |
+ * Decompress the JPEG source image or decode the YUV source image associated |
+ * with this decompressor instance and output a grayscale, RGB, or CMYK image |
+ * to the given destination buffer. |
+ * |
+ * @param dstBuf buffer that will receive the decompressed/decoded image. |
+ * If the source image is a JPEG image, then this buffer should normally be |
+ * <code>stride * scaledHeight</code> pixels in size, where |
+ * <code>scaledHeight</code> can be determined by calling <code> |
+ * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight) |
+ * </code> with one of the scaling factors returned from {@link |
+ * TJ#getScalingFactors} or by calling {@link #getScaledHeight}. If the |
+ * source image is a YUV image, then this buffer should normally be |
+ * <code>stride * height</code> pixels in size, where <code>height</code> is |
+ * the height of the YUV image. However, the buffer may also be larger than |
+ * the dimensions of the JPEG image, in which case the <code>x</code>, |
+ * <code>y</code>, and <code>stride</code> parameters can be used to specify |
+ * the region into which the source image should be decompressed. |
+ * |
+ * @param x x offset (in pixels) of the region in the destination image into |
+ * which the source image should be decompressed/decoded |
+ * |
+ * @param y y offset (in pixels) of the region in the destination image into |
+ * which the source image should be decompressed/decoded |
+ * |
+ * @param desiredWidth If the source image is a JPEG image, then this |
+ * specifies the desired width (in pixels) of the decompressed image (or |
+ * image region.) If the desired destination image dimensions are different |
+ * than the source image dimensions, then TurboJPEG will use scaling in the |
+ * JPEG decompressor to generate the largest possible image that will fit |
+ * within the desired dimensions. Setting this to 0 is the same as setting |
+ * it to the width of the JPEG image (in other words, the width will not be |
+ * considered when determining the scaled image size.) This parameter is |
+ * ignored if the source image is a YUV image. |
+ * |
+ * @param stride pixels per line of the destination image. Normally, this |
+ * should be set to <code>scaledWidth</code>, but you can use this to, for |
+ * instance, decompress the JPEG image into a region of a larger image. |
+ * NOTE: if the source image is a JPEG image, then <code>scaledWidth</code> |
+ * can be determined by calling <code> |
+ * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth) |
+ * </code> or by calling {@link #getScaledWidth}. If the source image is a |
+ * YUV image, then <code>scaledWidth</code> is the width of the YUV image. |
+ * Setting this parameter to 0 is the equivalent of setting it to |
+ * <code>scaledWidth</code>. |
+ * |
+ * @param desiredHeight If the source image is a JPEG image, then this |
+ * specifies the desired height (in pixels) of the decompressed image (or |
+ * image region.) If the desired destination image dimensions are different |
+ * than the source image dimensions, then TurboJPEG will use scaling in the |
+ * JPEG decompressor to generate the largest possible image that will fit |
+ * within the desired dimensions. Setting this to 0 is the same as setting |
+ * it to the height of the JPEG image (in other words, the height will not be |
+ * considered when determining the scaled image size.) This parameter is |
+ * ignored if the source image is a YUV image. |
+ * |
+ * @param pixelFormat pixel format of the decompressed image (one of |
+ * {@link TJ#PF_RGB TJ.PF_*}) |
+ * |
+ * @param flags the bitwise OR of one or more of |
+ * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} |
+ */ |
+ public void decompress(int[] dstBuf, int x, int y, int desiredWidth, |
+ int stride, int desiredHeight, int pixelFormat, |
+ int flags) throws TJException { |
+ if (jpegBuf == null && yuvImage == null) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ if (dstBuf == null || x < 0 || y < 0 || stride < 0 || |
+ (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) || |
+ pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) |
+ throw new IllegalArgumentException("Invalid argument in decompress()"); |
+ if (yuvImage != null) |
+ decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), |
+ yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y, |
+ yuvImage.getWidth(), stride, yuvImage.getHeight(), pixelFormat, |
+ flags); |
+ else |
+ decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, stride, |
+ desiredHeight, pixelFormat, flags); |
+ } |
+ |
+ /** |
+ * Decompress the JPEG source image or decode the YUV source image associated |
+ * with this decompressor instance and output a decompressed/decoded image to |
+ * the given <code>BufferedImage</code> instance. |
+ * |
+ * @param dstImage a <code>BufferedImage</code> instance that will receive |
+ * the decompressed/decoded image. If the source image is a JPEG image, then |
+ * the width and height of the <code>BufferedImage</code> instance must match |
+ * one of the scaled image sizes that TurboJPEG is capable of generating from |
+ * the JPEG image. If the source image is a YUV image, then the width and |
+ * height of the <code>BufferedImage</code> instance must match the width and |
+ * height of the YUV image. |
+ * |
+ * @param flags the bitwise OR of one or more of |
+ * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} |
+ */ |
+ public void decompress(BufferedImage dstImage, int flags) |
+ throws TJException { |
+ if (dstImage == null || flags < 0) |
+ throw new IllegalArgumentException("Invalid argument in decompress()"); |
+ int desiredWidth = dstImage.getWidth(); |
+ int desiredHeight = dstImage.getHeight(); |
+ int scaledWidth, scaledHeight; |
+ |
+ if (yuvImage != null) { |
+ if (desiredWidth != yuvImage.getWidth() || |
+ desiredHeight != yuvImage.getHeight()) |
+ throw new IllegalArgumentException("BufferedImage dimensions do not match the dimensions of the source image."); |
+ scaledWidth = yuvImage.getWidth(); |
+ scaledHeight = yuvImage.getHeight(); |
+ } else { |
+ scaledWidth = getScaledWidth(desiredWidth, desiredHeight); |
+ scaledHeight = getScaledHeight(desiredWidth, desiredHeight); |
+ if (scaledWidth != desiredWidth || scaledHeight != desiredHeight) |
+ throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating."); |
+ } |
+ int pixelFormat; boolean intPixels = false; |
+ if (byteOrder == null) |
+ byteOrder = ByteOrder.nativeOrder(); |
+ switch(dstImage.getType()) { |
+ case BufferedImage.TYPE_3BYTE_BGR: |
+ pixelFormat = TJ.PF_BGR; break; |
+ case BufferedImage.TYPE_4BYTE_ABGR: |
+ case BufferedImage.TYPE_4BYTE_ABGR_PRE: |
+ pixelFormat = TJ.PF_XBGR; break; |
+ case BufferedImage.TYPE_BYTE_GRAY: |
+ pixelFormat = TJ.PF_GRAY; break; |
+ case BufferedImage.TYPE_INT_BGR: |
+ if (byteOrder == ByteOrder.BIG_ENDIAN) |
+ pixelFormat = TJ.PF_XBGR; |
+ else |
+ pixelFormat = TJ.PF_RGBX; |
+ intPixels = true; break; |
+ case BufferedImage.TYPE_INT_RGB: |
+ if (byteOrder == ByteOrder.BIG_ENDIAN) |
+ pixelFormat = TJ.PF_XRGB; |
+ else |
+ pixelFormat = TJ.PF_BGRX; |
+ intPixels = true; break; |
+ case BufferedImage.TYPE_INT_ARGB: |
+ case BufferedImage.TYPE_INT_ARGB_PRE: |
+ if (byteOrder == ByteOrder.BIG_ENDIAN) |
+ pixelFormat = TJ.PF_ARGB; |
+ else |
+ pixelFormat = TJ.PF_BGRA; |
+ intPixels = true; break; |
+ default: |
+ throw new IllegalArgumentException("Unsupported BufferedImage format"); |
+ } |
+ WritableRaster wr = dstImage.getRaster(); |
+ if (intPixels) { |
+ SinglePixelPackedSampleModel sm = |
+ (SinglePixelPackedSampleModel)dstImage.getSampleModel(); |
+ int stride = sm.getScanlineStride(); |
+ DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); |
+ int[] buf = db.getData(); |
+ if (yuvImage != null) |
+ decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), |
+ yuvImage.getStrides(), yuvImage.getSubsamp(), buf, 0, 0, |
+ yuvImage.getWidth(), stride, yuvImage.getHeight(), |
+ pixelFormat, flags); |
+ else { |
+ if (jpegBuf == null) |
+ throw new IllegalStateException(NO_ASSOC_ERROR); |
+ decompress(jpegBuf, jpegBufSize, buf, 0, 0, scaledWidth, stride, |
+ scaledHeight, pixelFormat, flags); |
+ } |
+ } else { |
+ ComponentSampleModel sm = |
+ (ComponentSampleModel)dstImage.getSampleModel(); |
+ int pixelSize = sm.getPixelStride(); |
+ if (pixelSize != TJ.getPixelSize(pixelFormat)) |
+ throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage"); |
+ int pitch = sm.getScanlineStride(); |
+ DataBufferByte db = (DataBufferByte)wr.getDataBuffer(); |
+ byte[] buf = db.getData(); |
+ decompress(buf, 0, 0, scaledWidth, pitch, scaledHeight, pixelFormat, |
+ flags); |
+ } |
+ } |
+ |
+ /** |
+ * Decompress the JPEG source image or decode the YUV source image associated |
+ * with this decompressor instance and return a <code>BufferedImage</code> |
+ * instance containing the decompressed/decoded image. |
+ * |
+ * @param desiredWidth see |
+ * {@link #decompress(byte[], int, int, int, int, int, int, int)} for |
+ * description |
+ * |
+ * @param desiredHeight see |
+ * {@link #decompress(byte[], int, int, int, int, int, int, int)} for |
+ * description |
+ * |
+ * @param bufferedImageType the image type of the <code>BufferedImage</code> |
+ * instance that will be created (for instance, |
+ * <code>BufferedImage.TYPE_INT_RGB</code>) |
+ * |
+ * @param flags the bitwise OR of one or more of |
+ * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} |
+ * |
+ * @return a <code>BufferedImage</code> instance containing the |
+ * decompressed/decoded image. |
+ */ |
+ public BufferedImage decompress(int desiredWidth, int desiredHeight, |
+ int bufferedImageType, int flags) |
+ throws TJException { |
+ if ((yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) || |
+ flags < 0) |
+ throw new IllegalArgumentException("Invalid argument in decompress()"); |
+ int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); |
+ int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); |
+ BufferedImage img = new BufferedImage(scaledWidth, scaledHeight, |
+ bufferedImageType); |
+ decompress(img, flags); |
+ return img; |
+ } |
+ |
+ /** |
+ * Free the native structures associated with this decompressor instance. |
+ */ |
+ @Override |
+ public void close() throws TJException { |
+ if (handle != 0) |
+ destroy(); |
+ } |
+ |
+ @Override |
+ protected void finalize() throws Throwable { |
+ try { |
+ close(); |
+ } catch(TJException e) { |
+ } finally { |
+ super.finalize(); |
+ } |
+ }; |
+ |
+ private native void init() throws TJException; |
+ |
+ private native void destroy() throws TJException; |
+ |
+ private native void decompressHeader(byte[] srcBuf, int size) |
+ throws TJException; |
+ |
+ @Deprecated |
+ private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, |
+ int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) |
+ throws TJException; |
+ |
+ private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, int x, |
+ int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, |
+ int flags) throws TJException; |
+ |
+ @Deprecated |
+ private native void decompress(byte[] srcBuf, int size, int[] dstBuf, |
+ int desiredWidth, int stride, int desiredHeight, int pixelFormat, |
+ int flags) throws TJException; |
+ |
+ private native void decompress(byte[] srcBuf, int size, int[] dstBuf, int x, |
+ int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat, |
+ int flags) throws TJException; |
+ |
+ @Deprecated |
+ private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf, |
+ int flags) throws TJException; |
+ |
+ private native void decompressToYUV(byte[] srcBuf, int size, |
+ byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides, |
+ int desiredheight, int flags) throws TJException; |
+ |
+ private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets, |
+ int[] srcStrides, int subsamp, byte[] dstBuf, int x, int y, int width, |
+ int pitch, int height, int pixelFormat, int flags) throws TJException; |
+ |
+ private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets, |
+ int[] srcStrides, int subsamp, int[] dstBuf, int x, int y, int width, |
+ int stride, int height, int pixelFormat, int flags) throws TJException; |
+ |
+ static { |
+ TJLoader.load(); |
+ } |
+ |
+ protected long handle = 0; |
+ protected byte[] jpegBuf = null; |
+ protected int jpegBufSize = 0; |
+ protected YUVImage yuvImage = null; |
+ protected int jpegWidth = 0; |
+ protected int jpegHeight = 0; |
+ protected int jpegSubsamp = -1; |
+ protected int jpegColorspace = -1; |
+ private ByteOrder byteOrder = null; |
+} |