diff options
Diffstat (limited to 'java/org/libjpegturbo/turbojpeg')
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJ.java | 369 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJCompressor.java | 552 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJCustomFilter.java | 76 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJDecompressor.java | 635 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJLoader.java.in | 35 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJLoader.java.tmpl | 59 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJScalingFactor.java | 98 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJTransform.java | 208 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJTransformer.java | 160 |
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; +}; |