aboutsummaryrefslogtreecommitdiff
path: root/java/org/libjpegturbo/turbojpeg
diff options
context:
space:
mode:
Diffstat (limited to 'java/org/libjpegturbo/turbojpeg')
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJ.java369
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJCompressor.java552
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJCustomFilter.java76
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJDecompressor.java635
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJLoader.java.in35
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl59
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJScalingFactor.java98
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJTransform.java208
-rw-r--r--java/org/libjpegturbo/turbojpeg/TJTransformer.java160
9 files changed, 2192 insertions, 0 deletions
diff --git a/java/org/libjpegturbo/turbojpeg/TJ.java b/java/org/libjpegturbo/turbojpeg/TJ.java
new file mode 100644
index 0000000..9f7c682
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJ.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C)2011-2013 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;
+
+/**
+ * TurboJPEG utility class (cannot be instantiated)
+ */
+public final class TJ {
+
+
+ /**
+ * The number of chrominance subsampling options
+ */
+ public static final int NUMSAMP = 5;
+ /**
+ * 4:4:4 chrominance subsampling (no chrominance subsampling). The JPEG
+ * or YUV image will contain one chrominance component for every pixel in the
+ * source image.
+ */
+ public static final int SAMP_444 = 0;
+ /**
+ * 4:2:2 chrominance subsampling. The JPEG or YUV image will contain one
+ * chrominance component for every 2x1 block of pixels in the source image.
+ */
+ public static final int SAMP_422 = 1;
+ /**
+ * 4:2:0 chrominance subsampling. The JPEG or YUV image will contain one
+ * chrominance component for every 2x2 block of pixels in the source image.
+ */
+ public static final int SAMP_420 = 2;
+ /**
+ * Grayscale. The JPEG or YUV image will contain no chrominance components.
+ */
+ public static final int SAMP_GRAY = 3;
+ /**
+ * 4:4:0 chrominance subsampling. The JPEG or YUV image will contain one
+ * chrominance component for every 1x2 block of pixels in the source image.
+ */
+ public static final int SAMP_440 = 4;
+
+
+ /**
+ * Returns the MCU block width for the given level of chrominance
+ * subsampling.
+ *
+ * @param subsamp the level of chrominance subsampling (one of
+ * <code>SAMP_*</code>)
+ *
+ * @return the MCU block width for the given level of chrominance subsampling
+ */
+ public static int getMCUWidth(int subsamp) throws Exception {
+ if (subsamp < 0 || subsamp >= NUMSAMP)
+ throw new Exception("Invalid subsampling type");
+ return mcuWidth[subsamp];
+ }
+
+ private static final int[] mcuWidth = {
+ 8, 16, 16, 8, 8
+ };
+
+
+ /**
+ * Returns the MCU block height for the given level of chrominance
+ * subsampling.
+ *
+ * @param subsamp the level of chrominance subsampling (one of
+ * <code>SAMP_*</code>)
+ *
+ * @return the MCU block height for the given level of chrominance
+ * subsampling
+ */
+ public static int getMCUHeight(int subsamp) throws Exception {
+ if (subsamp < 0 || subsamp >= NUMSAMP)
+ throw new Exception("Invalid subsampling type");
+ return mcuHeight[subsamp];
+ }
+
+ private static final int[] mcuHeight = {
+ 8, 8, 16, 8, 16
+ };
+
+
+ /**
+ * The number of pixel formats
+ */
+ public static final int NUMPF = 11;
+ /**
+ * RGB pixel format. The red, green, and blue components in the image are
+ * stored in 3-byte pixels in the order R, G, B from lowest to highest byte
+ * address within each pixel.
+ */
+ public static final int PF_RGB = 0;
+ /**
+ * BGR pixel format. The red, green, and blue components in the image are
+ * stored in 3-byte pixels in the order B, G, R from lowest to highest byte
+ * address within each pixel.
+ */
+ public static final int PF_BGR = 1;
+ /**
+ * RGBX pixel format. The red, green, and blue components in the image are
+ * stored in 4-byte pixels in the order R, G, B from lowest to highest byte
+ * address within each pixel. The X component is ignored when compressing
+ * and undefined when decompressing.
+ */
+ public static final int PF_RGBX = 2;
+ /**
+ * BGRX pixel format. The red, green, and blue components in the image are
+ * stored in 4-byte pixels in the order B, G, R from lowest to highest byte
+ * address within each pixel. The X component is ignored when compressing
+ * and undefined when decompressing.
+ */
+ public static final int PF_BGRX = 3;
+ /**
+ * XBGR pixel format. The red, green, and blue components in the image are
+ * stored in 4-byte pixels in the order R, G, B from highest to lowest byte
+ * address within each pixel. The X component is ignored when compressing
+ * and undefined when decompressing.
+ */
+ public static final int PF_XBGR = 4;
+ /**
+ * XRGB pixel format. The red, green, and blue components in the image are
+ * stored in 4-byte pixels in the order B, G, R from highest to lowest byte
+ * address within each pixel. The X component is ignored when compressing
+ * and undefined when decompressing.
+ */
+ public static final int PF_XRGB = 5;
+ /**
+ * Grayscale pixel format. Each 1-byte pixel represents a luminance
+ * (brightness) level from 0 to 255.
+ */
+ public static final int PF_GRAY = 6;
+ /**
+ * RGBA pixel format. This is the same as {@link #PF_RGBX}, except that when
+ * decompressing, the X byte is guaranteed to be 0xFF, which can be
+ * interpreted as an opaque alpha channel.
+ */
+ public static final int PF_RGBA = 7;
+ /**
+ * BGRA pixel format. This is the same as {@link #PF_BGRX}, except that when
+ * decompressing, the X byte is guaranteed to be 0xFF, which can be
+ * interpreted as an opaque alpha channel.
+ */
+ public static final int PF_BGRA = 8;
+ /**
+ * ABGR pixel format. This is the same as {@link #PF_XBGR}, except that when
+ * decompressing, the X byte is guaranteed to be 0xFF, which can be
+ * interpreted as an opaque alpha channel.
+ */
+ public static final int PF_ABGR = 9;
+ /**
+ * ARGB pixel format. This is the same as {@link #PF_XRGB}, except that when
+ * decompressing, the X byte is guaranteed to be 0xFF, which can be
+ * interpreted as an opaque alpha channel.
+ */
+ public static final int PF_ARGB = 10;
+
+
+ /**
+ * Returns the pixel size (in bytes) for the given pixel format.
+ *
+ * @param pixelFormat the pixel format (one of <code>PF_*</code>)
+ *
+ * @return the pixel size (in bytes) for the given pixel format
+ */
+ public static int getPixelSize(int pixelFormat) throws Exception {
+ if (pixelFormat < 0 || pixelFormat >= NUMPF)
+ throw new Exception("Invalid pixel format");
+ return pixelSize[pixelFormat];
+ }
+
+ private static final int[] pixelSize = {
+ 3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4
+ };
+
+
+ /**
+ * For the given pixel format, returns the number of bytes that the red
+ * component is offset from the start of the pixel. For instance, if a pixel
+ * of format <code>TJ.PF_BGRX</code> is stored in <code>char pixel[]</code>,
+ * then the red component will be
+ * <code>pixel[TJ.getRedOffset(TJ.PF_BGRX)]</code>.
+ *
+ * @param pixelFormat the pixel format (one of <code>PF_*</code>)
+ *
+ * @return the red offset for the given pixel format
+ */
+ public static int getRedOffset(int pixelFormat) throws Exception {
+ if (pixelFormat < 0 || pixelFormat >= NUMPF)
+ throw new Exception("Invalid pixel format");
+ return redOffset[pixelFormat];
+ }
+
+ private static final int[] redOffset = {
+ 0, 2, 0, 2, 3, 1, 0, 0, 2, 3, 1
+ };
+
+
+ /**
+ * For the given pixel format, returns the number of bytes that the green
+ * component is offset from the start of the pixel. For instance, if a pixel
+ * of format <code>TJ.PF_BGRX</code> is stored in <code>char pixel[]</code>,
+ * then the green component will be
+ * <code>pixel[TJ.getGreenOffset(TJ.PF_BGRX)]</code>.
+ *
+ * @param pixelFormat the pixel format (one of <code>PF_*</code>)
+ *
+ * @return the green offset for the given pixel format
+ */
+ public static int getGreenOffset(int pixelFormat) throws Exception {
+ if (pixelFormat < 0 || pixelFormat >= NUMPF)
+ throw new Exception("Invalid pixel format");
+ return greenOffset[pixelFormat];
+ }
+
+ private static final int[] greenOffset = {
+ 1, 1, 1, 1, 2, 2, 0, 1, 1, 2, 2
+ };
+
+
+ /**
+ * For the given pixel format, returns the number of bytes that the blue
+ * component is offset from the start of the pixel. For instance, if a pixel
+ * of format <code>TJ.PF_BGRX</code> is stored in <code>char pixel[]</code>,
+ * then the blue component will be
+ * <code>pixel[TJ.getBlueOffset(TJ.PF_BGRX)]</code>.
+ *
+ * @param pixelFormat the pixel format (one of <code>PF_*</code>)
+ *
+ * @return the blue offset for the given pixel format
+ */
+ public static int getBlueOffset(int pixelFormat) throws Exception {
+ if (pixelFormat < 0 || pixelFormat >= NUMPF)
+ throw new Exception("Invalid pixel format");
+ return blueOffset[pixelFormat];
+ }
+
+ private static final int[] blueOffset = {
+ 2, 0, 2, 0, 1, 3, 0, 2, 0, 1, 3
+ };
+
+
+ /**
+ * The uncompressed source/destination image is stored in bottom-up (Windows,
+ * OpenGL) order, not top-down (X11) order.
+ */
+ public static final int FLAG_BOTTOMUP = 2;
+ /**
+ * Turn off CPU auto-detection and force TurboJPEG to use MMX code
+ * (if the underlying codec supports it.)
+ */
+ public static final int FLAG_FORCEMMX = 8;
+ /**
+ * Turn off CPU auto-detection and force TurboJPEG to use SSE code
+ * (if the underlying codec supports it.)
+ */
+ public static final int FLAG_FORCESSE = 16;
+ /**
+ * Turn off CPU auto-detection and force TurboJPEG to use SSE2 code
+ * (if the underlying codec supports it.)
+ */
+ public static final int FLAG_FORCESSE2 = 32;
+ /**
+ * Turn off CPU auto-detection and force TurboJPEG to use SSE3 code
+ * (if the underlying codec supports it.)
+ */
+ public static final int FLAG_FORCESSE3 = 128;
+ /**
+ * When decompressing an image that was compressed using chrominance
+ * subsampling, use the fastest chrominance upsampling algorithm available in
+ * the underlying codec. The default is to use smooth upsampling, which
+ * creates a smooth transition between neighboring chrominance components in
+ * order to reduce upsampling artifacts in the decompressed image.
+ */
+ public static final int FLAG_FASTUPSAMPLE = 256;
+ /**
+ * Use the fastest DCT/IDCT algorithm available in the underlying codec. The
+ * default if this flag is not specified is implementation-specific. The
+ * libjpeg implementation, for example, uses the fast algorithm by default
+ * when compressing, because this has been shown to have only a very slight
+ * effect on accuracy, but it uses the accurate algorithm when decompressing,
+ * because this has been shown to have a larger effect.
+ */
+ public static final int FLAG_FASTDCT = 2048;
+ /**
+ * Use the most accurate DCT/IDCT algorithm available in the underlying
+ * codec. The default if this flag is not specified is
+ * implementation-specific. The libjpeg implementation, for example, uses
+ * the fast algorithm by default when compressing, because this has been
+ * shown to have only a very slight effect on accuracy, but it uses the
+ * accurate algorithm when decompressing, because this has been shown to have
+ * a larger effect.
+ */
+ public static final int FLAG_ACCURATEDCT = 4096;
+
+
+ /**
+ * Returns the maximum size of the buffer (in bytes) required to hold a JPEG
+ * image with the given width, height, and level of chrominance subsampling.
+ *
+ * @param width the width (in pixels) of the JPEG image
+ *
+ * @param height the height (in pixels) of the JPEG image
+ *
+ * @param jpegSubsamp the level of chrominance subsampling to be used when
+ * generating the JPEG image (one of {@link TJ TJ.SAMP_*})
+ *
+ * @return the maximum size of the buffer (in bytes) required to hold a JPEG
+ * image with the given width, height, and level of chrominance subsampling
+ */
+ public static native int bufSize(int width, int height, int jpegSubsamp)
+ throws Exception;
+
+ /**
+ * Returns the size of the buffer (in bytes) required to hold a YUV planar
+ * image with the given width, height, and level of chrominance subsampling.
+ *
+ * @param width the width (in pixels) of the YUV image
+ *
+ * @param height the height (in pixels) of the YUV image
+ *
+ * @param subsamp the level of chrominance subsampling used in the YUV
+ * image (one of {@link TJ TJ.SAMP_*})
+ *
+ * @return the size of the buffer (in bytes) required to hold a YUV planar
+ * image with the given width, height, and level of chrominance subsampling
+ */
+ public static native int bufSizeYUV(int width, int height, int subsamp)
+ throws Exception;
+
+ /**
+ * Returns a list of fractional scaling factors that the JPEG decompressor in
+ * this implementation of TurboJPEG supports.
+ *
+ * @return a list of fractional scaling factors that the JPEG decompressor in
+ * this implementation of TurboJPEG supports
+ */
+ public static native TJScalingFactor[] getScalingFactors()
+ throws Exception;
+
+ static {
+ TJLoader.load();
+ }
+};
diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java
new file mode 100644
index 0000000..f8f82ac
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java
@@ -0,0 +1,552 @@
+/*
+ * Copyright (C)2011-2013 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;
+
+import java.awt.image.*;
+import java.nio.*;
+
+/**
+ * TurboJPEG compressor
+ */
+public class TJCompressor {
+
+ private static final String NO_ASSOC_ERROR =
+ "No source image is associated with this instance";
+
+ /**
+ * Create a TurboJPEG compressor instance.
+ */
+ public TJCompressor() throws Exception {
+ init();
+ }
+
+ /**
+ * Create a TurboJPEG compressor instance and associate the uncompressed
+ * source image stored in <code>srcImage</code> with the newly-created
+ * instance.
+ *
+ * @param srcImage see {@link #setSourceImage} for description
+ *
+ * @param width see {@link #setSourceImage} for description
+ *
+ * @param pitch see {@link #setSourceImage} for description
+ *
+ * @param height see {@link #setSourceImage} for description
+ *
+ * @param pixelFormat pixel format of the source image (one of
+ * {@link TJ TJ.PF_*})
+ */
+ public TJCompressor(byte[] srcImage, int width, int pitch, int height,
+ int pixelFormat) throws Exception {
+ setSourceImage(srcImage, width, pitch, height, pixelFormat);
+ }
+
+ /**
+ * Create a TurboJPEG compressor instance and associate the uncompressed
+ * source image stored in <code>srcImage</code> with the newly-created
+ * instance.
+ *
+ * @param srcImage see {@link #setSourceImage} for description
+ *
+ * @param x see {@link #setSourceImage} for description
+ *
+ * @param y see {@link #setSourceImage} for description
+ *
+ * @param width see {@link #setSourceImage} for description
+ *
+ * @param pitch see {@link #setSourceImage} for description
+ *
+ * @param height see {@link #setSourceImage} for description
+ *
+ * @param pixelFormat pixel format of the source image (one of
+ * {@link TJ TJ.PF_*})
+ */
+ public TJCompressor(byte[] srcImage, int x, int y, int width, int pitch,
+ int height, int pixelFormat) throws Exception {
+ setSourceImage(srcImage, x, y, width, pitch, height, pixelFormat);
+ }
+
+ /**
+ * Associate an uncompressed source image with this compressor instance.
+ *
+ * @param srcImage image buffer containing RGB or grayscale pixels to be
+ * compressed
+ *
+ * @param x x offset (in pixels) of the region from which the JPEG image
+ * should be compressed, relative to the start of <code>srcImage</code>.
+ *
+ * @param y y offset (in pixels) of the region from which the JPEG image
+ * should be compressed, relative to the start of <code>srcImage</code>.
+ *
+ * @param width width (in pixels) of the region in the source image from
+ * which the JPEG image should be compressed.
+ *
+ * @param pitch bytes per line of the source image. Normally, this should be
+ * <code>width * TJ.pixelSize(pixelFormat)</code> if the source image is
+ * unpadded, but you can use this parameter to, for instance, specify that
+ * the scanlines in the source image are padded to a 4-byte boundary or to
+ * compress a JPEG image from a region of a larger source image. You can
+ * also be clever and use this parameter to skip lines, etc. Setting this
+ * parameter to 0 is the equivalent of setting it to <code>width *
+ * TJ.pixelSize(pixelFormat)</code>.
+ *
+ * @param height height (in pixels) of the region in the source image from
+ * which the JPEG image should be compressed.
+ *
+ * @param pixelFormat pixel format of the source image (one of
+ * {@link TJ TJ.PF_*})
+ */
+ public void setSourceImage(byte[] srcImage, int x, int y, int width,
+ int pitch, int height, int pixelFormat)
+ throws Exception {
+ if (handle == 0) init();
+ if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 ||
+ pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
+ throw new Exception("Invalid argument in setSourceImage()");
+ srcBuf = srcImage;
+ srcWidth = width;
+ if (pitch == 0)
+ srcPitch = width * TJ.getPixelSize(pixelFormat);
+ else
+ srcPitch = pitch;
+ srcHeight = height;
+ srcPixelFormat = pixelFormat;
+ srcX = x;
+ srcY = y;
+ }
+
+ /**
+ * @deprecated Use
+ * {@link #setSourceImage(byte[], int, int, int, int, int, int)} instead.
+ */
+ public void setSourceImage(byte[] srcImage, int width, int pitch,
+ int height, int pixelFormat) throws Exception {
+ setSourceImage(srcImage, 0, 0, width, pitch, height, pixelFormat);
+ srcX = srcY = -1;
+ }
+
+
+ /**
+ * Set the level of chrominance subsampling for subsequent compress/encode
+ * operations.
+ *
+ * @param newSubsamp the new level of chrominance subsampling (one of
+ * {@link TJ TJ.SAMP_*})
+ */
+ public void setSubsamp(int newSubsamp) throws Exception {
+ if (newSubsamp < 0 || newSubsamp >= TJ.NUMSAMP)
+ throw new Exception("Invalid argument in setSubsamp()");
+ subsamp = newSubsamp;
+ }
+
+ /**
+ * Set the JPEG image quality level for subsequent compress operations.
+ *
+ * @param quality the new JPEG image quality level (1 to 100, 1 = worst,
+ * 100 = best)
+ */
+ public void setJPEGQuality(int quality) throws Exception {
+ if (quality < 1 || quality > 100)
+ throw new Exception("Invalid argument in setJPEGQuality()");
+ jpegQuality = quality;
+ }
+
+ /**
+ * Compress the uncompressed source image associated with this compressor
+ * instance and output a JPEG image to the given destination buffer.
+ *
+ * @param dstBuf buffer that will receive the JPEG image. Use
+ * {@link TJ#bufSize} to determine the maximum size for this buffer based on
+ * the image width, height, and level of chrominance subsampling.
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ public void compress(byte[] dstBuf, int flags) throws Exception {
+ if (dstBuf == null || flags < 0)
+ throw new Exception("Invalid argument in compress()");
+ if (srcBuf == null)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (jpegQuality < 0)
+ 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);
+ }
+
+ /**
+ * Compress the uncompressed source image associated with this compressor
+ * instance and return a buffer containing a JPEG image.
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ *
+ * @return a buffer containing a JPEG image. The length of this buffer will
+ * not be equal to the size of the JPEG image. Use {@link
+ * #getCompressedSize} to obtain the size of the JPEG image.
+ */
+ public byte[] compress(int flags) throws Exception {
+ if (srcWidth < 1 || srcHeight < 1)
+ throw new Exception(NO_ASSOC_ERROR);
+ byte[] buf = new byte[TJ.bufSize(srcWidth, srcHeight, subsamp)];
+ compress(buf, flags);
+ return buf;
+ }
+
+ /**
+ * Compress the uncompressed source image stored in <code>srcImage</code>
+ * and output a JPEG image to the given destination buffer.
+ *
+ * @param srcImage a <code>BufferedImage</code> instance containing RGB or
+ * grayscale pixels to be compressed
+ *
+ * @param dstBuf buffer that will receive the JPEG image. Use
+ * {@link TJ#bufSize} to determine the maximum size for this buffer based on
+ * the image width, height, and level of chrominance subsampling.
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ public void compress(BufferedImage srcImage, byte[] dstBuf, int flags)
+ throws Exception {
+ if (srcImage == null || dstBuf == null || flags < 0)
+ throw new Exception("Invalid argument in compress()");
+ int width = srcImage.getWidth();
+ int height = srcImage.getHeight();
+ int pixelFormat;
+ boolean intPixels = false;
+ if (byteOrder == null)
+ byteOrder = ByteOrder.nativeOrder();
+ switch(srcImage.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:
+ case BufferedImage.TYPE_INT_ARGB:
+ case BufferedImage.TYPE_INT_ARGB_PRE:
+ if (byteOrder == ByteOrder.BIG_ENDIAN)
+ pixelFormat = TJ.PF_XRGB;
+ else
+ pixelFormat = TJ.PF_BGRX;
+ intPixels = true; break;
+ default:
+ throw new Exception("Unsupported BufferedImage format");
+ }
+ WritableRaster wr = srcImage.getRaster();
+ if (jpegQuality < 0)
+ throw new Exception("JPEG Quality not set");
+ if (subsamp < 0)
+ throw new Exception("Subsampling level not set");
+ if (intPixels) {
+ SinglePixelPackedSampleModel sm =
+ (SinglePixelPackedSampleModel)srcImage.getSampleModel();
+ int stride = sm.getScanlineStride();
+ DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
+ int[] buf = db.getData();
+ if (srcX >= 0 && srcY >= 0)
+ compressedSize = compress(buf, srcX, srcY, width, stride, height,
+ pixelFormat, dstBuf, subsamp, jpegQuality,
+ flags);
+ else
+ compressedSize = compress(buf, width, stride, height, pixelFormat,
+ dstBuf, subsamp, jpegQuality, flags);
+ } else {
+ ComponentSampleModel sm =
+ (ComponentSampleModel)srcImage.getSampleModel();
+ int pixelSize = sm.getPixelStride();
+ if (pixelSize != TJ.getPixelSize(pixelFormat))
+ throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage");
+ int pitch = sm.getScanlineStride();
+ DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
+ byte[] buf = db.getData();
+ if (srcX >= 0 && srcY >= 0)
+ compressedSize = compress(buf, srcX, srcY, width, pitch, height,
+ pixelFormat, dstBuf, subsamp, jpegQuality,
+ flags);
+ else
+ compressedSize = compress(buf, width, pitch, height, pixelFormat,
+ dstBuf, subsamp, jpegQuality, flags);
+ }
+ }
+
+ /**
+ * Compress the uncompressed source image stored in <code>srcImage</code>
+ * and return a buffer containing a JPEG image.
+ *
+ * @param srcImage a <code>BufferedImage</code> instance containing RGB or
+ * grayscale pixels to be compressed
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ *
+ * @return a buffer containing a JPEG image. The length of this buffer will
+ * not be equal to the size of the JPEG image. Use {@link
+ * #getCompressedSize} to obtain the size of the JPEG image.
+ */
+ public byte[] compress(BufferedImage srcImage, int flags) throws Exception {
+ int width = srcImage.getWidth();
+ int height = srcImage.getHeight();
+ byte[] buf = new byte[TJ.bufSize(width, height, subsamp)];
+ compress(srcImage, buf, flags);
+ return buf;
+ }
+
+ /**
+ * Encode the uncompressed source image associated with this compressor
+ * instance and output a YUV planar image to the given destination buffer.
+ * This method uses the accelerated color conversion routines in
+ * TurboJPEG's underlying codec to produce a planar YUV image that is
+ * suitable for direct video display. Specifically, if the chrominance
+ * components are subsampled along the horizontal dimension, then the width
+ * of the luminance plane is padded to the nearest multiple of 2 in the
+ * output image (same goes for the height of the luminance plane, if the
+ * chrominance components are subsampled along the vertical dimension.)
+ * Also, each line of each plane in the output image is padded to 4 bytes.
+ * Although this will work with any subsampling option, it is really only
+ * useful in combination with {@link TJ#SAMP_420}, which produces an image
+ * compatible with the I420 (AKA "YUV420P") format.
+ *
+ * @param dstBuf buffer that will receive the YUV planar image. Use
+ * {@link TJ#bufSizeYUV} to determine the appropriate size for this buffer
+ * based on the image width, height, and level of chrominance subsampling.
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ public void encodeYUV(byte[] dstBuf, int flags) throws Exception {
+ if (dstBuf == null || flags < 0)
+ throw new Exception("Invalid argument in compress()");
+ if (srcBuf == null)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (subsamp < 0)
+ throw new Exception("Subsampling level not set");
+ encodeYUV(srcBuf, srcWidth, srcPitch, srcHeight,
+ srcPixelFormat, dstBuf, subsamp, flags);
+ compressedSize = TJ.bufSizeYUV(srcWidth, srcHeight, subsamp);
+ }
+
+ /**
+ * Encode the uncompressed source image associated with this compressor
+ * instance and return a buffer containing a YUV planar image. See
+ * {@link #encodeYUV(byte[], int)} for more detail.
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ *
+ * @return a buffer containing a YUV planar image
+ */
+ public byte[] encodeYUV(int flags) throws Exception {
+ if (srcWidth < 1 || srcHeight < 1)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (subsamp < 0)
+ throw new Exception("Subsampling level not set");
+ byte[] buf = new byte[TJ.bufSizeYUV(srcWidth, srcHeight, subsamp)];
+ encodeYUV(buf, flags);
+ return buf;
+ }
+
+ /**
+ * Encode the uncompressed source image stored in <code>srcImage</code>
+ * and output a YUV planar image to the given destination buffer. See
+ * {@link #encodeYUV(byte[], int)} for more detail.
+ *
+ * @param srcImage a <code>BufferedImage</code> instance containing RGB or
+ * grayscale pixels to be encoded
+ *
+ * @param dstBuf buffer that will receive the YUV planar image. Use
+ * {@link TJ#bufSizeYUV} to determine the appropriate size for this buffer
+ * based on the image width, height, and level of chrominance subsampling.
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ public void encodeYUV(BufferedImage srcImage, byte[] dstBuf, int flags)
+ throws Exception {
+ if (srcImage == null || dstBuf == null || flags < 0)
+ throw new Exception("Invalid argument in encodeYUV()");
+ int width = srcImage.getWidth();
+ int height = srcImage.getHeight();
+ int pixelFormat; boolean intPixels = false;
+ if (byteOrder == null)
+ byteOrder = ByteOrder.nativeOrder();
+ switch(srcImage.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:
+ case BufferedImage.TYPE_INT_ARGB:
+ case BufferedImage.TYPE_INT_ARGB_PRE:
+ if (byteOrder == ByteOrder.BIG_ENDIAN)
+ pixelFormat = TJ.PF_XRGB;
+ else
+ pixelFormat = TJ.PF_BGRX;
+ intPixels = true; break;
+ default:
+ throw new Exception("Unsupported BufferedImage format");
+ }
+ WritableRaster wr = srcImage.getRaster();
+ if (subsamp < 0) throw new Exception("Subsampling level not set");
+ if (intPixels) {
+ SinglePixelPackedSampleModel sm =
+ (SinglePixelPackedSampleModel)srcImage.getSampleModel();
+ int stride = sm.getScanlineStride();
+ DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
+ int[] buf = db.getData();
+ encodeYUV(buf, width, stride, height, pixelFormat, dstBuf, subsamp,
+ flags);
+ } else {
+ ComponentSampleModel sm =
+ (ComponentSampleModel)srcImage.getSampleModel();
+ int pixelSize = sm.getPixelStride();
+ if (pixelSize != TJ.getPixelSize(pixelFormat))
+ throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage");
+ int pitch = sm.getScanlineStride();
+ DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
+ byte[] buf = db.getData();
+ encodeYUV(buf, width, pitch, height, pixelFormat, dstBuf, subsamp,
+ flags);
+ }
+ compressedSize = TJ.bufSizeYUV(width, height, subsamp);
+ }
+
+ /**
+ * Encode the uncompressed source image stored in <code>srcImage</code>
+ * and return a buffer containing a YUV planar image. See
+ * {@link #encodeYUV(byte[], int)} for more detail.
+ *
+ * @param srcImage a <code>BufferedImage</code> instance containing RGB or
+ * grayscale pixels to be encoded
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ *
+ * @return a buffer containing a YUV planar image
+ */
+ public byte[] encodeYUV(BufferedImage srcImage, int flags) throws Exception {
+ if (subsamp < 0)
+ throw new Exception("Subsampling level not set");
+ int width = srcImage.getWidth();
+ int height = srcImage.getHeight();
+ byte[] buf = new byte[TJ.bufSizeYUV(width, height, subsamp)];
+ encodeYUV(srcImage, buf, flags);
+ return buf;
+ }
+
+ /**
+ * Returns the size of the image (in bytes) generated by the most recent
+ * compress/encode operation.
+ *
+ * @return the size of the image (in bytes) generated by the most recent
+ * compress/encode operation
+ */
+ public int getCompressedSize() {
+ return compressedSize;
+ }
+
+ /**
+ * Free the native structures associated with this compressor instance.
+ */
+ public void close() throws Exception {
+ destroy();
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } catch(Exception e) {
+ } finally {
+ super.finalize();
+ }
+ };
+
+ private native void init() throws Exception;
+
+ private native void destroy() throws Exception;
+
+ // JPEG size in bytes is returned
+ private native int compress(byte[] srcBuf, int width, int pitch,
+ int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp, int jpegQual,
+ int flags) throws Exception; // deprecated
+
+ private native int compress(byte[] srcBuf, int x, int y, int width,
+ int pitch, int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp,
+ int jpegQual, int flags) throws Exception;
+
+ private native int compress(int[] srcBuf, int width, int stride,
+ int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp, int jpegQual,
+ int flags) throws Exception; // deprecated
+
+ private native int compress(int[] srcBuf, int x, int y, int width,
+ int stride, int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp,
+ 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;
+
+ private native void encodeYUV(int[] srcBuf, int width, int stride,
+ int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags)
+ throws Exception;
+
+ static {
+ TJLoader.load();
+ }
+
+ private long handle = 0;
+ private byte[] srcBuf = null;
+ private int srcWidth = 0;
+ private int srcHeight = 0;
+ private int srcX = -1;
+ private int srcY = -1;
+ private int srcPitch = 0;
+ private int srcPixelFormat = -1;
+ private int subsamp = -1;
+ private int jpegQuality = -1;
+ private int compressedSize = 0;
+ private ByteOrder byteOrder = null;
+};
diff --git a/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java
new file mode 100644
index 0000000..6e46fa1
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJCustomFilter.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C)2011, 2013 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;
+
+import java.awt.*;
+import java.nio.*;
+
+/**
+ * Custom filter callback interface
+ */
+public interface TJCustomFilter {
+
+ /**
+ * A callback function that can be used to modify the DCT coefficients after
+ * they are losslessly transformed but before they are transcoded to a new
+ * JPEG file. This allows for custom filters or other transformations to be
+ * applied in the frequency domain.
+ *
+ * @param coeffBuffer a buffer containing transformed DCT coefficients.
+ * (NOTE: this buffer is not guaranteed to be valid once the callback
+ * returns, so applications wishing to hand off the DCT coefficients to
+ * another function or library should make a copy of them within the body of
+ * the callback.)
+ *
+ * @param bufferRegion rectangle containing the width and height of
+ * <code>coeffBuffer</code> as well as its offset relative to the component
+ * plane. TurboJPEG implementations may choose to split each component plane
+ * into multiple DCT coefficient buffers and call the callback function once
+ * for each buffer.
+ *
+ * @param planeRegion rectangle containing the width and height of the
+ * component plane to which <code>coeffBuffer</code> belongs
+ *
+ * @param componentID ID number of the component plane to which
+ * <code>coeffBuffer</code> belongs (Y, U, and V have, respectively, ID's of
+ * 0, 1, and 2 in typical JPEG images.)
+ *
+ * @param transformID ID number of the transformed image to which
+ * <code>coeffBuffer</code> belongs. This is the same as the index of the
+ * transform in the <code>transforms</code> array that was passed to {@link
+ * TJTransformer#transform TJTransformer.transform()}.
+ *
+ * @param transform a {@link TJTransform} instance that specifies the
+ * parameters and/or cropping region for this transform
+ */
+ void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion,
+ Rectangle planeRegion, int componentID, int transformID,
+ TJTransform transform)
+ throws Exception;
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java
new file mode 100644
index 0000000..c2d361e
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java
@@ -0,0 +1,635 @@
+/*
+ * Copyright (C)2011-2013 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;
+
+import java.awt.image.*;
+import java.nio.*;
+
+/**
+ * TurboJPEG decompressor
+ */
+public class TJDecompressor {
+
+ private static final String NO_ASSOC_ERROR =
+ "No JPEG image is associated with this instance";
+
+ /**
+ * Create a TurboJPEG decompresssor instance.
+ */
+ public TJDecompressor() throws Exception {
+ init();
+ }
+
+ /**
+ * Create a TurboJPEG decompressor instance and associate the JPEG 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)
+ */
+ public TJDecompressor(byte[] jpegImage) throws Exception {
+ init();
+ setJPEGImage(jpegImage, jpegImage.length);
+ }
+
+ /**
+ * Create a TurboJPEG decompressor instance and associate the JPEG image
+ * of length <code>imageSize</code> bytes stored in <code>jpegImage</code>
+ * with the newly-created instance.
+ *
+ * @param jpegImage JPEG image buffer
+ *
+ * @param imageSize size of the JPEG image (in bytes)
+ */
+ public TJDecompressor(byte[] jpegImage, int imageSize) throws Exception {
+ init();
+ setJPEGImage(jpegImage, imageSize);
+ }
+
+ /**
+ * 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
+ *
+ * @param imageSize size of the JPEG image (in bytes)
+ */
+ public void setJPEGImage(byte[] jpegImage, int imageSize) throws Exception {
+ if (jpegImage == null || imageSize < 1)
+ throw new Exception("Invalid argument in setJPEGImage()");
+ jpegBuf = jpegImage;
+ jpegBufSize = imageSize;
+ decompressHeader(jpegBuf, jpegBufSize);
+ }
+
+ /**
+ * Returns the width of the JPEG image associated with this decompressor
+ * instance.
+ *
+ * @return the width of the JPEG image associated with this decompressor
+ * instance
+ */
+ public int getWidth() throws Exception {
+ if (jpegWidth < 1)
+ throw new Exception(NO_ASSOC_ERROR);
+ return jpegWidth;
+ }
+
+ /**
+ * Returns the height of the JPEG image associated with this decompressor
+ * instance.
+ *
+ * @return the height of the JPEG image associated with this decompressor
+ * instance
+ */
+ public int getHeight() throws Exception {
+ if (jpegHeight < 1)
+ throw new Exception(NO_ASSOC_ERROR);
+ return jpegHeight;
+ }
+
+ /**
+ * Returns the level of chrominance subsampling used in the JPEG image
+ * associated with this decompressor instance.
+ *
+ * @return the level of chrominance subsampling used in the JPEG image
+ * associated with this decompressor instance
+ */
+ public int getSubsamp() throws Exception {
+ if (jpegSubsamp < 0)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (jpegSubsamp >= TJ.NUMSAMP)
+ throw new Exception("JPEG header information is invalid");
+ return jpegSubsamp;
+ }
+
+ /**
+ * Returns the JPEG image buffer associated with this decompressor instance.
+ *
+ * @return the JPEG image buffer associated with this decompressor instance
+ */
+ public byte[] getJPEGBuf() throws Exception {
+ if (jpegBuf == null)
+ throw new Exception(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() throws Exception {
+ if (jpegBufSize < 1)
+ throw new Exception(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)
+ throws Exception {
+ if (jpegWidth < 1 || jpegHeight < 1)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (desiredWidth < 0 || desiredHeight < 0)
+ throw new Exception("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 Exception("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)
+ throws Exception {
+ if (jpegWidth < 1 || jpegHeight < 1)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (desiredWidth < 0 || desiredHeight < 0)
+ throw new Exception("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 Exception("Could not scale down to desired image dimensions");
+ return scaledHeight;
+ }
+
+ /**
+ * Decompress the JPEG source image associated with this decompressor
+ * instance and output a decompressed image to the given destination buffer.
+ *
+ * @param dstBuf buffer that will receive the decompressed image. 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}. 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>pitch</code>
+ * parameters can be used to specify the region into which the JPEG image
+ * should be decompressed.
+ *
+ * @param x x offset (in pixels) of the region into which the JPEG image
+ * should be decompressed, relative to the start of <code>dstBuf</code>.
+ *
+ * @param y y offset (in pixels) of the region into which the JPEG image
+ * should be decompressed, relative to the start of <code>dstBuf</code>.
+ *
+ * @param desiredWidth desired width (in pixels) of the decompressed image
+ * (or image region.) 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 pitch bytes per line of the destination image. Normally, this
+ * should be set to <code>scaledWidth * TJ.pixelSize(pixelFormat)</code> if
+ * the decompressed image is unpadded, but you can use this to, for instance,
+ * pad each line of the decompressed image to a 4-byte boundary or to
+ * decompress the JPEG image into a region of a larger image. NOTE:
+ * <code>scaledWidth</code> can be determined by calling <code>
+ * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth)
+ * </code> or by calling {@link #getScaledWidth}. Setting this parameter to
+ * 0 is the equivalent of setting it to <code>scaledWidth *
+ * TJ.pixelSize(pixelFormat)</code>.
+ *
+ * @param desiredHeight desired height (in pixels) of the decompressed image
+ * (or image region.) 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 pixelFormat pixel format of the decompressed image (one of
+ * {@link TJ TJ.PF_*})
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ public void decompress(byte[] dstBuf, int x, int y, int desiredWidth,
+ int pitch, int desiredHeight, int pixelFormat,
+ int flags) throws Exception {
+ if (jpegBuf == null)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (dstBuf == null || x < 0 || y < 0 || desiredWidth < 0 || pitch < 0 ||
+ desiredHeight < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF ||
+ flags < 0)
+ throw new Exception("Invalid argument in decompress()");
+ 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.
+ */
+ public void decompress(byte[] dstBuf, int desiredWidth, int pitch,
+ int desiredHeight, int pixelFormat, int flags)
+ throws Exception {
+ 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 TJ.PF_*})
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ *
+ * @return a buffer containing the decompressed image
+ */
+ public byte[] decompress(int desiredWidth, int pitch, int desiredHeight,
+ int pixelFormat, int flags) throws Exception {
+ if (desiredWidth < 0 || pitch < 0 || desiredHeight < 0 ||
+ pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
+ throw new Exception("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 and output a YUV planar image to the given destination buffer.
+ * This method performs JPEG decompression but leaves out the color
+ * conversion step, so a planar YUV image is generated instead of an RGB
+ * image. The padding of the planes in this image is the same as in the
+ * images generated by {@link TJCompressor#encodeYUV(byte[], int)}. Note
+ * that, if the width or height of the image is not an even multiple of the
+ * MCU block size (see {@link TJ#getMCUWidth} and {@link TJ#getMCUHeight}),
+ * then an intermediate buffer copy will be performed within TurboJPEG.
+ *
+ * @param dstBuf buffer that will receive the YUV planar image. Use
+ * {@link TJ#bufSizeYUV} to determine the appropriate size for this buffer
+ * based on the image width, height, and level of chrominance subsampling.
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ public void decompressToYUV(byte[] dstBuf, int flags) throws Exception {
+ if (jpegBuf == null)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (dstBuf == null || flags < 0)
+ throw new Exception("Invalid argument in decompressToYUV()");
+ decompressToYUV(jpegBuf, jpegBufSize, dstBuf, flags);
+ }
+
+
+ /**
+ * Decompress the JPEG source image associated with this decompressor
+ * instance and return a buffer containing a YUV planar image. See {@link
+ * #decompressToYUV(byte[], int)} for more detail.
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ *
+ * @return a buffer containing a YUV planar image
+ */
+ public byte[] decompressToYUV(int flags) throws Exception {
+ if (flags < 0)
+ throw new Exception("Invalid argument in decompressToYUV()");
+ if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (jpegSubsamp >= TJ.NUMSAMP)
+ throw new Exception("JPEG header information is invalid");
+ byte[] buf = new byte[TJ.bufSizeYUV(jpegWidth, jpegHeight, jpegSubsamp)];
+ decompressToYUV(buf, flags);
+ return buf;
+ }
+
+ /**
+ * Decompress the JPEG source image associated with this decompressor
+ * instance and output a decompressed image to the given destination buffer.
+ *
+ * @param dstBuf buffer that will receive the decompressed image. 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}. 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 JPEG image
+ * should be decompressed.
+ *
+ * @param x x offset (in pixels) of the region into which the JPEG image
+ * should be decompressed, relative to the start of <code>dstBuf</code>.
+ *
+ * @param y y offset (in pixels) of the region into which the JPEG image
+ * should be decompressed, relative to the start of <code>dstBuf</code>.
+ *
+ * @param desiredWidth desired width (in pixels) of the decompressed image
+ * (or image region.) 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 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: <code>scaledWidth</code> can be determined by calling <code>
+ * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth)
+ * </code> or by calling {@link #getScaledWidth}. Setting this parameter to
+ * 0 is the equivalent of setting it to <code>scaledWidth</code>.
+ *
+ * @param desiredHeight desired height (in pixels) of the decompressed image
+ * (or image region.) 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 pixelFormat pixel format of the decompressed image (one of
+ * {@link TJ TJ.PF_*})
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ public void decompress(int[] dstBuf, int x, int y, int desiredWidth,
+ int stride, int desiredHeight, int pixelFormat,
+ int flags) throws Exception {
+ if (jpegBuf == null)
+ throw new Exception(NO_ASSOC_ERROR);
+ if (dstBuf == null || x < 0 || y < 0 || desiredWidth < 0 || stride < 0 ||
+ desiredHeight < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF ||
+ flags < 0)
+ throw new Exception("Invalid argument in decompress()");
+ decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, stride,
+ desiredHeight, pixelFormat, flags);
+ }
+
+ /**
+ * Decompress the JPEG source image associated with this decompressor
+ * instance and output a decompressed image to the given
+ * <code>BufferedImage</code> instance.
+ *
+ * @param dstImage a <code>BufferedImage</code> instance that will receive
+ * the decompressed image
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ public void decompress(BufferedImage dstImage, int flags) throws Exception {
+ if (dstImage == null || flags < 0)
+ throw new Exception("Invalid argument in decompress()");
+ int desiredWidth = dstImage.getWidth();
+ int desiredHeight = dstImage.getHeight();
+ int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
+ int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
+ if (scaledWidth != desiredWidth || scaledHeight != desiredHeight)
+ throw new Exception("BufferedImage dimensions do not match a scaled image size 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 Exception("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 (jpegBuf == null)
+ throw new Exception(NO_ASSOC_ERROR);
+ decompress(jpegBuf, jpegBufSize, buf, scaledWidth, stride, scaledHeight,
+ pixelFormat, flags);
+ } else {
+ ComponentSampleModel sm =
+ (ComponentSampleModel)dstImage.getSampleModel();
+ int pixelSize = sm.getPixelStride();
+ if (pixelSize != TJ.getPixelSize(pixelFormat))
+ throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage");
+ int pitch = sm.getScanlineStride();
+ DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
+ byte[] buf = db.getData();
+ decompress(buf, scaledWidth, pitch, scaledHeight, pixelFormat, flags);
+ }
+ }
+
+ /**
+ * Decompress the JPEG source image associated with this decompressor
+ * instance and return a <code>BufferedImage</code> instance containing the
+ * decompressed 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 newly-created
+ * <code>BufferedImage</code> instance (for instance,
+ * <code>BufferedImage.TYPE_INT_RGB</code>)
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ *
+ * @return a <code>BufferedImage</code> instance containing the
+ * decompressed image
+ */
+ public BufferedImage decompress(int desiredWidth, int desiredHeight,
+ int bufferedImageType, int flags)
+ throws Exception {
+ if (desiredWidth < 0 || desiredHeight < 0 || flags < 0)
+ throw new Exception("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.
+ */
+ public void close() throws Exception {
+ destroy();
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } catch(Exception e) {
+ } finally {
+ super.finalize();
+ }
+ };
+
+ private native void init() throws Exception;
+
+ private native void destroy() throws Exception;
+
+ private native void decompressHeader(byte[] srcBuf, int size)
+ throws Exception;
+
+ private native void decompress(byte[] srcBuf, int size, byte[] dstBuf,
+ int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)
+ throws Exception; // deprecated
+
+ 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 Exception;
+
+ private native void decompress(byte[] srcBuf, int size, int[] dstBuf,
+ int desiredWidth, int stride, int desiredHeight, int pixelFormat,
+ int flags) throws Exception; // deprecated
+
+ 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 Exception;
+
+ private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf,
+ int flags) throws Exception;
+
+ static {
+ TJLoader.load();
+ }
+
+ protected long handle = 0;
+ protected byte[] jpegBuf = null;
+ protected int jpegBufSize = 0;
+ protected int jpegWidth = 0;
+ protected int jpegHeight = 0;
+ protected int jpegSubsamp = -1;
+ private ByteOrder byteOrder = null;
+};
diff --git a/java/org/libjpegturbo/turbojpeg/TJLoader.java.in b/java/org/libjpegturbo/turbojpeg/TJLoader.java.in
new file mode 100644
index 0000000..22353a5
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJLoader.java.in
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+final class TJLoader {
+ static void load() {
+ System.loadLibrary("@TURBOJPEG_DLL_NAME@");
+ }
+};
diff --git a/java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl b/java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl
new file mode 100644
index 0000000..a4f1c87
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C)2011-2013 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;
+
+final class TJLoader {
+ static void load() {
+ try {
+ System.loadLibrary("turbojpeg");
+ } catch (java.lang.UnsatisfiedLinkError e) {
+ String os = System.getProperty("os.name").toLowerCase();
+ if (os.indexOf("mac") >= 0) {
+ try {
+ System.load("%{__libdir}/libturbojpeg.jnilib");
+ } catch (java.lang.UnsatisfiedLinkError e2) {
+ System.load("/usr/lib/libturbojpeg.jnilib");
+ }
+ } else {
+ try {
+ System.load("%{__libdir}/libturbojpeg.so");
+ } catch (java.lang.UnsatisfiedLinkError e3) {
+ String libdir = "%{__libdir}";
+ if (libdir.equals("/opt/libjpeg-turbo/lib64")) {
+ System.load("/opt/libjpeg-turbo/lib32/libturbojpeg.so");
+ } else if (libdir.equals("/opt/libjpeg-turbo/lib32")) {
+ System.load("/opt/libjpeg-turbo/lib64/libturbojpeg.so");
+ } else {
+ throw e3;
+ }
+ }
+ }
+ }
+ }
+};
diff --git a/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java b/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java
new file mode 100644
index 0000000..4e7363f
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java
@@ -0,0 +1,98 @@
+/*
+ * 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;
+
+/**
+ * Fractional scaling factor
+ */
+public class TJScalingFactor {
+
+ public TJScalingFactor(int num, int denom) throws Exception {
+ if (num < 1 || denom < 1)
+ throw new Exception("Numerator and denominator must be >= 1");
+ this.num = num;
+ this.denom = denom;
+ }
+
+ /**
+ * Returns numerator
+ * @return numerator
+ */
+ public int getNum() {
+ return num;
+ }
+
+ /**
+ * Returns denominator
+ * @return denominator
+ */
+ public int getDenom() {
+ return denom;
+ }
+
+ /**
+ * Returns the scaled value of <code>dimension</code>. This function
+ * performs the integer equivalent of
+ * <code>ceil(dimension * scalingFactor)</code>.
+ * @return the scaled value of <code>dimension</code>
+ */
+ public int getScaled(int dimension) {
+ return (dimension * num + denom - 1) / denom;
+ }
+
+ /**
+ * Returns true or false, depending on whether this instance and
+ * <code>other</code> have the same numerator and denominator.
+ * @return true or false, depending on whether this instance and
+ * <code>other</code> have the same numerator and denominator
+ */
+ public boolean equals(TJScalingFactor other) {
+ return (this.num == other.num && this.denom == other.denom);
+ }
+
+ /**
+ * Returns true or false, depending on whether this instance is equal to
+ * 1/1.
+ * @return true or false, depending on whether this instance is equal to
+ * 1/1
+ */
+ public boolean isOne() {
+ return (num == 1 && denom == 1);
+ }
+
+ /**
+ * Numerator
+ */
+ private int num = 1;
+
+ /**
+ * Denominator
+ */
+ private int denom = 1;
+};
diff --git a/java/org/libjpegturbo/turbojpeg/TJTransform.java b/java/org/libjpegturbo/turbojpeg/TJTransform.java
new file mode 100644
index 0000000..b464ffd
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJTransform.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C)2011, 2013 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;
+
+import java.awt.*;
+
+/**
+ * Lossless transform parameters
+ */
+public class TJTransform extends Rectangle {
+
+ private static final long serialVersionUID = -127367705761430371L;
+
+ /**
+ * The number of lossless transform operations
+ */
+ public static final int NUMOP = 8;
+ /**
+ * Do not transform the position of the image pixels.
+ */
+ public static final int OP_NONE = 0;
+ /**
+ * Flip (mirror) image horizontally. This transform is imperfect if there
+ * are any partial MCU blocks on the right edge.
+ * @see #OPT_PERFECT
+ */
+ public static final int OP_HFLIP = 1;
+ /**
+ * Flip (mirror) image vertically. This transform is imperfect if there are
+ * any partial MCU blocks on the bottom edge.
+ * @see #OPT_PERFECT
+ */
+ public static final int OP_VFLIP = 2;
+ /**
+ * Transpose image (flip/mirror along upper left to lower right axis). This
+ * transform is always perfect.
+ * @see #OPT_PERFECT
+ */
+ public static final int OP_TRANSPOSE = 3;
+ /**
+ * Transverse transpose image (flip/mirror along upper right to lower left
+ * axis). This transform is imperfect if there are any partial MCU blocks in
+ * the image.
+ * @see #OPT_PERFECT
+ */
+ public static final int OP_TRANSVERSE = 4;
+ /**
+ * Rotate image clockwise by 90 degrees. This transform is imperfect if
+ * there are any partial MCU blocks on the bottom edge.
+ * @see #OPT_PERFECT
+ */
+ public static final int OP_ROT90 = 5;
+ /**
+ * Rotate image 180 degrees. This transform is imperfect if there are any
+ * partial MCU blocks in the image.
+ * @see #OPT_PERFECT
+ */
+ public static final int OP_ROT180 = 6;
+ /**
+ * Rotate image counter-clockwise by 90 degrees. This transform is imperfect
+ * if there are any partial MCU blocks on the right edge.
+ * @see #OPT_PERFECT
+ */
+ public static final int OP_ROT270 = 7;
+
+
+ /**
+ * This option will cause {@link TJTransformer#transform
+ * TJTransformer.transform()} to throw an exception if the transform is not
+ * perfect. Lossless transforms operate on MCU blocks, whose size depends on
+ * the level of chrominance subsampling used. If the image's width or height
+ * is not evenly divisible by the MCU block size (see {@link TJ#getMCUWidth}
+ * and {@link TJ#getMCUHeight}), then there will be partial MCU blocks on the
+ * right and/or bottom edges. It is not possible to move these partial MCU
+ * blocks to the top or left of the image, so any transform that would
+ * require that is "imperfect." If this option is not specified, then any
+ * partial MCU blocks that cannot be transformed will be left in place, which
+ * will create odd-looking strips on the right or bottom edge of the image.
+ */
+ public static final int OPT_PERFECT = 1;
+ /**
+ * This option will discard any partial MCU blocks that cannot be
+ * transformed.
+ */
+ public static final int OPT_TRIM = 2;
+ /**
+ * This option will enable lossless cropping.
+ */
+ public static final int OPT_CROP = 4;
+ /**
+ * This option will discard the color data in the input image and produce
+ * a grayscale output image.
+ */
+ public static final int OPT_GRAY = 8;
+ /**
+ * This option will prevent {@link TJTransformer#transform
+ * TJTransformer.transform()} from outputting a JPEG image for this
+ * particular transform. This can be used in conjunction with a custom
+ * filter to capture the transformed DCT coefficients without transcoding
+ * them.
+ */
+ public static final int OPT_NOOUTPUT = 16;
+
+
+ /**
+ * Create a new lossless transform instance.
+ */
+ public TJTransform() {
+ }
+
+ /**
+ * Create a new lossless transform instance with the given parameters.
+ *
+ * @param x the left boundary of the cropping region. This must be evenly
+ * divisible by the MCU block width (see {@link TJ#getMCUWidth})
+ *
+ * @param y the upper boundary of the cropping region. This must be evenly
+ * divisible by the MCU block height (see {@link TJ#getMCUHeight})
+ *
+ * @param w the width of the cropping region. Setting this to 0 is the
+ * equivalent of setting it to (width of the source JPEG image -
+ * <code>x</code>).
+ *
+ * @param h the height of the cropping region. Setting this to 0 is the
+ * equivalent of setting it to (height of the source JPEG image -
+ * <code>y</code>).
+ *
+ * @param op one of the transform operations (<code>OP_*</code>)
+ *
+ * @param options the bitwise OR of one or more of the transform options
+ * (<code>OPT_*</code>)
+ *
+ * @param cf an instance of an object that implements the {@link
+ * TJCustomFilter} interface, or null if no custom filter is needed
+ */
+ public TJTransform(int x, int y, int w, int h, int op, int options,
+ TJCustomFilter cf) throws Exception {
+ super(x, y, w, h);
+ this.op = op;
+ this.options = options;
+ this.cf = cf;
+ }
+
+ /**
+ * Create a new lossless transform instance with the given parameters.
+ *
+ * @param r a <code>Rectangle</code> instance that specifies the cropping
+ * region. See {@link
+ * #TJTransform(int, int, int, int, int, int, TJCustomFilter)} for more
+ * detail.
+ *
+ * @param op one of the transform operations (<code>OP_*</code>)
+ *
+ * @param options the bitwise OR of one or more of the transform options
+ * (<code>OPT_*</code>)
+ *
+ * @param cf an instance of an object that implements the {@link
+ * TJCustomFilter} interface, or null if no custom filter is needed
+ */
+ public TJTransform(Rectangle r, int op, int options,
+ TJCustomFilter cf) throws Exception {
+ super(r);
+ this.op = op;
+ this.options = options;
+ this.cf = cf;
+ }
+
+ /**
+ * Transform operation (one of <code>OP_*</code>)
+ */
+ public int op = 0;
+
+ /**
+ * Transform options (bitwise OR of one or more of <code>OPT_*</code>)
+ */
+ public int options = 0;
+
+ /**
+ * Custom filter instance
+ */
+ public TJCustomFilter cf = null;
+}
diff --git a/java/org/libjpegturbo/turbojpeg/TJTransformer.java b/java/org/libjpegturbo/turbojpeg/TJTransformer.java
new file mode 100644
index 0000000..f84eaa1
--- /dev/null
+++ b/java/org/libjpegturbo/turbojpeg/TJTransformer.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C)2011, 2013 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;
+
+/**
+ * TurboJPEG lossless transformer
+ */
+public class TJTransformer extends TJDecompressor {
+
+ /**
+ * Create a TurboJPEG lossless transformer instance.
+ */
+ public TJTransformer() throws Exception {
+ init();
+ }
+
+ /**
+ * Create a TurboJPEG lossless transformer instance and associate the JPEG
+ * 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)
+ */
+ public TJTransformer(byte[] jpegImage) throws Exception {
+ init();
+ setJPEGImage(jpegImage, jpegImage.length);
+ }
+
+ /**
+ * Create a TurboJPEG lossless transformer instance and associate the JPEG
+ * image of length <code>imageSize</code> bytes stored in
+ * <code>jpegImage</code> with the newly-created instance.
+ *
+ * @param jpegImage JPEG image buffer
+ *
+ * @param imageSize size of the JPEG image (in bytes)
+ */
+ public TJTransformer(byte[] jpegImage, int imageSize) throws Exception {
+ init();
+ setJPEGImage(jpegImage, imageSize);
+ }
+
+ /**
+ * Losslessly transform the JPEG image associated with this transformer
+ * instance into one or more JPEG images stored in the given destination
+ * buffers. Lossless transforms work by moving the raw coefficients from one
+ * JPEG image structure to another without altering the values of the
+ * coefficients. While this is typically faster than decompressing the
+ * image, transforming it, and re-compressing it, lossless transforms are not
+ * free. Each lossless transform requires reading and performing Huffman
+ * decoding on all of the coefficients in the source image, regardless of the
+ * size of the destination image. Thus, this method provides a means of
+ * generating multiple transformed images from the same source or of applying
+ * multiple transformations simultaneously, in order to eliminate the need to
+ * read the source coefficients multiple times.
+ *
+ * @param dstBufs an array of image buffers. <code>dstbufs[i]</code> will
+ * receive a JPEG image that has been transformed using the parameters in
+ * <code>transforms[i]</code>. Use {@link TJ#bufSize} to determine the
+ * maximum size for each buffer based on the transformed or cropped width and
+ * height.
+ *
+ * @param transforms an array of {@link TJTransform} instances, each of
+ * which specifies the transform parameters and/or cropping region for the
+ * corresponding transformed output image
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ 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);
+ }
+
+ /**
+ * Losslessly transform the JPEG image associated with this transformer
+ * instance and return an array of {@link TJDecompressor} instances, each of
+ * which has a transformed JPEG image associated with it.
+ *
+ * @param transforms an array of {@link TJTransform} instances, each of
+ * which specifies the transform parameters and/or cropping region for the
+ * corresponding transformed output image
+ *
+ * @return an array of {@link TJDecompressor} instances, each of
+ * which has a transformed JPEG image associated with it
+ *
+ * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
+ */
+ 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 & TJTransform.OPT_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, jpegSubsamp)];
+ }
+ 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;
+ }
+
+ /**
+ * Returns an array containing the sizes of the transformed JPEG images from
+ * the most recent call to {@link #transform transform()}.
+ *
+ * @return an array containing the sizes of the transformed JPEG images from
+ * the most recent call to {@link #transform transform()}
+ */
+ 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 {
+ TJLoader.load();
+ }
+
+ private int[] transformedSizes = null;
+};