Update code to v1.0.14 (10)

This commit is contained in:
Caten
2024-02-29 19:35:00 +08:00
parent c2ee3b694c
commit a956d26f6d
3188 changed files with 2317293 additions and 146 deletions

View File

@@ -0,0 +1,601 @@
/*
* Copyright (C)2011-2013, 2017-2018, 2020-2021, 2023 D. R. Commander.
* All Rights Reserved.
* Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the libjpeg-turbo Project nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.libjpegturbo.turbojpeg;
/**
* TurboJPEG utility class (cannot be instantiated)
*/
public final class TJ {
private TJ() {}
/**
* The number of chrominance subsampling options
*/
public static final int NUMSAMP = 6;
/**
* 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.
* Note that 4:4:0 subsampling is not fully accelerated in libjpeg-turbo.
*/
public static final int SAMP_440 = 4;
/**
* 4:1:1 chrominance subsampling. The JPEG or YUV image will contain one
* chrominance component for every 4x1 block of pixels in the source image.
* JPEG images compressed with 4:1:1 subsampling will be almost exactly the
* same size as those compressed with 4:2:0 subsampling, and in the
* aggregate, both subsampling methods produce approximately the same
* perceptual quality. However, 4:1:1 is better able to reproduce sharp
* horizontal features. Note that 4:1:1 subsampling is not fully accelerated
* in libjpeg-turbo.
*/
public static final int SAMP_411 = 5;
/**
* Returns the MCU block width for the given level of chrominance
* subsampling.
*
* @param subsamp the level of chrominance subsampling (one of
* {@link #SAMP_444 SAMP_*})
*
* @return the MCU block width for the given level of chrominance
* subsampling.
*/
public static int getMCUWidth(int subsamp) {
checkSubsampling(subsamp);
return MCU_WIDTH[subsamp];
}
private static final int[] MCU_WIDTH = {
8, 16, 16, 8, 8, 32
};
/**
* Returns the MCU block height for the given level of chrominance
* subsampling.
*
* @param subsamp the level of chrominance subsampling (one of
* {@link #SAMP_444 SAMP_*})
*
* @return the MCU block height for the given level of chrominance
* subsampling.
*/
public static int getMCUHeight(int subsamp) {
checkSubsampling(subsamp);
return MCU_HEIGHT[subsamp];
}
private static final int[] MCU_HEIGHT = {
8, 8, 16, 8, 16, 8
};
/**
* The number of pixel formats
*/
public static final int NUMPF = 12;
/**
* 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;
/**
* CMYK pixel format. Unlike RGB, which is an additive color model used
* primarily for display, CMYK (Cyan/Magenta/Yellow/Key) is a subtractive
* color model used primarily for printing. In the CMYK color model, the
* value of each color component typically corresponds to an amount of cyan,
* magenta, yellow, or black ink that is applied to a white background. In
* order to convert between CMYK and RGB, it is necessary to use a color
* management system (CMS.) A CMS will attempt to map colors within the
* printer's gamut to perceptually similar colors in the display's gamut and
* vice versa, but the mapping is typically not 1:1 or reversible, nor can it
* be defined with a simple formula. Thus, such a conversion is out of scope
* for a codec library. However, the TurboJPEG API allows for compressing
* packed-pixel CMYK images into YCCK JPEG images (see {@link #CS_YCCK}) and
* decompressing YCCK JPEG images into packed-pixel CMYK images.
*/
public static final int PF_CMYK = 11;
/**
* Returns the pixel size (in bytes) for the given pixel format.
*
* @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*})
*
* @return the pixel size (in bytes) for the given pixel format.
*/
public static int getPixelSize(int pixelFormat) {
checkPixelFormat(pixelFormat);
return PIXEL_SIZE[pixelFormat];
}
private static final int[] PIXEL_SIZE = {
3, 3, 4, 4, 4, 4, 1, 4, 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 {@link #PF_RGB PF_*})
*
* @return the red offset for the given pixel format, or -1 if the pixel
* format does not have a red component.
*/
public static int getRedOffset(int pixelFormat) {
checkPixelFormat(pixelFormat);
return RED_OFFSET[pixelFormat];
}
private static final int[] RED_OFFSET = {
0, 2, 0, 2, 3, 1, -1, 0, 2, 3, 1, -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 {@link #PF_RGB PF_*})
*
* @return the green offset for the given pixel format, or -1 if the pixel
* format does not have a green component.
*/
public static int getGreenOffset(int pixelFormat) {
checkPixelFormat(pixelFormat);
return GREEN_OFFSET[pixelFormat];
}
private static final int[] GREEN_OFFSET = {
1, 1, 1, 1, 2, 2, -1, 1, 1, 2, 2, -1
};
/**
* 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 {@link #PF_RGB PF_*})
*
* @return the blue offset for the given pixel format, or -1 if the pixel
* format does not have a blue component.
*/
public static int getBlueOffset(int pixelFormat) {
checkPixelFormat(pixelFormat);
return BLUE_OFFSET[pixelFormat];
}
private static final int[] BLUE_OFFSET = {
2, 0, 2, 0, 1, 3, -1, 2, 0, 1, 3, -1
};
/**
* For the given pixel format, returns the number of bytes that the alpha
* component is offset from the start of the pixel. For instance, if a pixel
* of format <code>TJ.PF_BGRA</code> is stored in <code>char pixel[]</code>,
* then the alpha component will be
* <code>pixel[TJ.getAlphaOffset(TJ.PF_BGRA)]</code>.
*
* @param pixelFormat the pixel format (one of {@link #PF_RGB PF_*})
*
* @return the alpha offset for the given pixel format, or -1 if the pixel
* format does not have a alpha component.
*/
public static int getAlphaOffset(int pixelFormat) {
checkPixelFormat(pixelFormat);
return ALPHA_OFFSET[pixelFormat];
}
private static final int[] ALPHA_OFFSET = {
-1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0, -1
};
/**
* The number of JPEG colorspaces
*/
public static final int NUMCS = 5;
/**
* RGB colorspace. When compressing the JPEG image, the R, G, and B
* components in the source image are reordered into image planes, but no
* colorspace conversion or subsampling is performed. RGB JPEG images can be
* decompressed to packed-pixel images with any of the extended RGB or
* grayscale pixel formats, but they cannot be decompressed to planar YUV
* images.
*/
public static final int CS_RGB = 0;
/**
* YCbCr colorspace. YCbCr is not an absolute colorspace but rather a
* mathematical transformation of RGB designed solely for storage and
* transmission. YCbCr images must be converted to RGB before they can
* actually be displayed. In the YCbCr colorspace, the Y (luminance)
* component represents the black &amp; white portion of the original image,
* and the Cb and Cr (chrominance) components represent the color portion of
* the original image. Originally, the analog equivalent of this
* transformation allowed the same signal to drive both black &amp; white and
* color televisions, but JPEG images use YCbCr primarily because it allows
* the color data to be optionally subsampled for the purposes of reducing
* network or disk usage. YCbCr is the most common JPEG colorspace, and
* YCbCr JPEG images can be compressed from and decompressed to packed-pixel
* images with any of the extended RGB or grayscale pixel formats. YCbCr
* JPEG images can also be compressed from and decompressed to planar YUV
* images.
*/
@SuppressWarnings("checkstyle:ConstantName")
public static final int CS_YCbCr = 1;
/**
* Grayscale colorspace. The JPEG image retains only the luminance data (Y
* component), and any color data from the source image is discarded.
* Grayscale JPEG images can be compressed from and decompressed to
* packed-pixel images with any of the extended RGB or grayscale pixel
* formats, or they can be compressed from and decompressed to planar YUV
* images.
*/
public static final int CS_GRAY = 2;
/**
* CMYK colorspace. When compressing the JPEG image, the C, M, Y, and K
* components in the source image are reordered into image planes, but no
* colorspace conversion or subsampling is performed. CMYK JPEG images can
* only be decompressed to packed-pixel images with the CMYK pixel format.
*/
public static final int CS_CMYK = 3;
/**
* YCCK colorspace. YCCK (AKA "YCbCrK") is not an absolute colorspace but
* rather a mathematical transformation of CMYK designed solely for storage
* and transmission. It is to CMYK as YCbCr is to RGB. CMYK pixels can be
* reversibly transformed into YCCK, and as with YCbCr, the chrominance
* components in the YCCK pixels can be subsampled without incurring major
* perceptual loss. YCCK JPEG images can only be compressed from and
* decompressed to packed-pixel images with the CMYK pixel format.
*/
public static final int CS_YCCK = 4;
/**
* Rows in the packed-pixel source/destination image are stored in bottom-up
* (Windows, OpenGL) order rather than in top-down (X11) order.
*/
public static final int FLAG_BOTTOMUP = 2;
@SuppressWarnings("checkstyle:JavadocVariable")
@Deprecated
public static final int FLAG_FORCEMMX = 8;
@SuppressWarnings("checkstyle:JavadocVariable")
@Deprecated
public static final int FLAG_FORCESSE = 16;
@SuppressWarnings("checkstyle:JavadocVariable")
@Deprecated
public static final int FLAG_FORCESSE2 = 32;
@SuppressWarnings("checkstyle:JavadocVariable")
@Deprecated
public static final int FLAG_FORCESSE3 = 128;
/**
* When decompressing an image that was compressed using chrominance
* subsampling, use the fastest chrominance upsampling algorithm available.
* 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. The default if this flag is
* not specified is implementation-specific. For example, the implementation
* of the TurboJPEG API in libjpeg-turbo 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. The default if this
* flag is not specified is implementation-specific. For example, the
* implementation of the TurboJPEG API in libjpeg-turbo 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;
/**
* Immediately discontinue the current compression/decompression/transform
* operation if a warning (non-fatal error) occurs. The default behavior is
* to allow the operation to complete unless a fatal error is encountered.
* <p>
* NOTE: due to the design of the TurboJPEG Java API, only certain methods
* (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods
* with a void return type) will complete and leave the destination image in
* a fully recoverable state after a non-fatal error occurs.
*/
public static final int FLAG_STOPONWARNING = 8192;
/**
* Use progressive entropy coding in JPEG images generated by compression and
* transform operations. Progressive entropy coding will generally improve
* compression relative to baseline entropy coding (the default), but it will
* reduce compression and decompression performance considerably.
*/
public static final int FLAG_PROGRESSIVE = 16384;
/**
* Limit the number of progressive JPEG scans that the decompression and
* transform operations will process. If a progressive JPEG image contains
* an unreasonably large number of scans, then this flag will cause the
* decompression and transform operations to throw an error. The primary
* purpose of this is to allow security-critical applications to guard
* against an exploit of the progressive JPEG format described in
* <a href="https://libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf" target="_blank">this report</a>.
*/
public static final int FLAG_LIMITSCANS = 32768;
/**
* The number of error codes
*/
public static final int NUMERR = 2;
/**
* The error was non-fatal and recoverable, but the destination image may
* still be corrupt.
* <p>
* NOTE: due to the design of the TurboJPEG Java API, only certain methods
* (specifically, {@link TJDecompressor TJDecompressor.decompress*()} methods
* with a void return type) will complete and leave the destination image in
* a fully recoverable state after a non-fatal error occurs.
*/
public static final int ERR_WARNING = 0;
/**
* The error was fatal and non-recoverable.
*/
public static final int ERR_FATAL = 1;
/**
* 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 #SAMP_444 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);
/**
* Returns the size of the buffer (in bytes) required to hold a unified
* planar YUV image with the given width, height, and level of chrominance
* subsampling.
*
* @param width the width (in pixels) of the YUV image
*
* @param align row alignment (in bytes) of the YUV image (must be a power of
* 2.) Setting this parameter to n specifies that each row in each plane of
* the YUV image will be padded to the nearest multiple of n bytes
* (1 = unpadded.)
*
* @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 #SAMP_444 TJ.SAMP_*})
*
* @return the size of the buffer (in bytes) required to hold a unified
* planar YUV image with the given width, height, and level of chrominance
* subsampling.
*/
public static native int bufSizeYUV(int width, int align, int height,
int subsamp);
/**
* @deprecated Use {@link #bufSizeYUV(int, int, int, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public static native int bufSizeYUV(int width, int height, int subsamp);
/**
* Returns the size of the buffer (in bytes) required to hold a YUV image
* plane with the given parameters.
*
* @param componentID ID number of the image plane (0 = Y, 1 = U/Cb,
* 2 = V/Cr)
*
* @param width width (in pixels) of the YUV image. NOTE: this is the width
* of the whole image, not the plane width.
*
* @param stride bytes per row in the image plane.
*
* @param height height (in pixels) of the YUV image. NOTE: this is the
* height of the whole image, not the plane height.
*
* @param subsamp the level of chrominance subsampling used in the YUV
* image (one of {@link #SAMP_444 TJ.SAMP_*})
*
* @return the size of the buffer (in bytes) required to hold a YUV image
* plane with the given parameters.
*/
public static native int planeSizeYUV(int componentID, int width, int stride,
int height, int subsamp);
/**
* Returns the plane width of a YUV image plane with the given parameters.
* Refer to {@link YUVImage YUVImage} for a description of plane width.
*
* @param componentID ID number of the image plane (0 = Y, 1 = U/Cb,
* 2 = V/Cr)
*
* @param width width (in pixels) of the YUV image
*
* @param subsamp the level of chrominance subsampling used in the YUV image
* (one of {@link #SAMP_444 TJ.SAMP_*})
*
* @return the plane width of a YUV image plane with the given parameters.
*/
public static native int planeWidth(int componentID, int width, int subsamp);
/**
* Returns the plane height of a YUV image plane with the given parameters.
* Refer to {@link YUVImage YUVImage} for a description of plane height.
*
* @param componentID ID number of the image plane (0 = Y, 1 = U/Cb,
* 2 = V/Cr)
*
* @param height height (in pixels) of the YUV image
*
* @param subsamp the level of chrominance subsampling used in the YUV image
* (one of {@link #SAMP_444 TJ.SAMP_*})
*
* @return the plane height of a YUV image plane with the given parameters.
*/
public static native int planeHeight(int componentID, int height,
int subsamp);
/**
* Returns a list of fractional scaling factors that the JPEG decompressor
* supports.
*
* @return a list of fractional scaling factors that the JPEG decompressor
* supports.
*/
public static native TJScalingFactor[] getScalingFactors();
static {
TJLoader.load();
}
private static void checkPixelFormat(int pixelFormat) {
if (pixelFormat < 0 || pixelFormat >= NUMPF)
throw new IllegalArgumentException("Invalid pixel format");
}
private static void checkSubsampling(int subsamp) {
if (subsamp < 0 || subsamp >= NUMSAMP)
throw new IllegalArgumentException("Invalid subsampling type");
}
}

View File

@@ -0,0 +1,689 @@
/*
* Copyright (C)2011-2015, 2018, 2020, 2023 D. R. Commander.
* All Rights Reserved.
* Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the libjpeg-turbo Project nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.libjpegturbo.turbojpeg;
import java.awt.image.*;
import java.nio.*;
import java.io.*;
/**
* TurboJPEG compressor
*/
public class TJCompressor implements Closeable {
private static final String NO_ASSOC_ERROR =
"No source image is associated with this instance";
/**
* Create a TurboJPEG compressor instance.
*/
public TJCompressor() throws TJException {
init();
}
/**
* Create a TurboJPEG compressor instance and associate the packed-pixel
* 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#PF_RGB TJ.PF_*})
*/
public TJCompressor(byte[] srcImage, int x, int y, int width, int pitch,
int height, int pixelFormat) throws TJException {
setSourceImage(srcImage, x, y, width, pitch, height, pixelFormat);
}
/**
* @deprecated Use
* {@link #TJCompressor(byte[], int, int, int, int, int, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public TJCompressor(byte[] srcImage, int width, int pitch, int height,
int pixelFormat) throws TJException {
setSourceImage(srcImage, width, pitch, height, pixelFormat);
}
/**
* Create a TurboJPEG compressor instance and associate the packed-pixel
* source image stored in <code>srcImage</code> with the newly created
* instance.
*
* @param srcImage see
* {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
*
* @param x see
* {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
*
* @param y see
* {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
*
* @param width see
* {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
*
* @param height see
* {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
*/
public TJCompressor(BufferedImage srcImage, int x, int y, int width,
int height) throws TJException {
setSourceImage(srcImage, x, y, width, height);
}
/**
* Associate a packed-pixel RGB, grayscale, or CMYK source image with this
* compressor instance.
*
* @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK
* source image to be compressed or encoded. This buffer is not modified.
*
* @param x x offset (in pixels) of the region in the source image from which
* the JPEG or YUV image should be compressed/encoded
*
* @param y y offset (in pixels) of the region in the source image from which
* the JPEG or YUV image should be compressed/encoded
*
* @param width width (in pixels) of the region in the source image from
* which the JPEG or YUV image should be compressed/encoded
*
* @param pitch bytes per row in the source image. Normally this should be
* <code>width *
* </code>{@link TJ#getPixelSize TJ.getPixelSize}<code>(pixelFormat)</code>,
* if the source image is unpadded. However, you can use this parameter to,
* for instance, specify that the rows in the source image are padded to the
* nearest multiple of 4 bytes or to compress/encode a JPEG or YUV image from
* a region of a larger source image. You can also be clever and use this
* parameter to skip rows, etc. Setting this parameter to 0 is the
* equivalent of setting it to <code>width *
* </code>{@link TJ#getPixelSize TJ.getPixelSize}<code>(pixelFormat)</code>.
*
* @param height height (in pixels) of the region in the source image from
* which the JPEG or YUV image should be compressed/encoded
*
* @param pixelFormat pixel format of the source image (one of
* {@link TJ#PF_RGB TJ.PF_*})
*/
public void setSourceImage(byte[] srcImage, int x, int y, int width,
int pitch, int height, int pixelFormat)
throws TJException {
if (handle == 0) init();
if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 ||
pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
throw new IllegalArgumentException("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;
srcBufInt = null;
srcYUVImage = null;
}
/**
* @deprecated Use
* {@link #setSourceImage(byte[], int, int, int, int, int, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public void setSourceImage(byte[] srcImage, int width, int pitch,
int height, int pixelFormat) throws TJException {
setSourceImage(srcImage, 0, 0, width, pitch, height, pixelFormat);
srcX = srcY = -1;
}
/**
* Associate a packed-pixel RGB or grayscale source image with this
* compressor instance.
*
* @param srcImage a <code>BufferedImage</code> instance containing a
* packed-pixel RGB or grayscale source image to be compressed or encoded.
* This image is not modified.
*
* @param x x offset (in pixels) of the region in the source image from which
* the JPEG or YUV image should be compressed/encoded
*
* @param y y offset (in pixels) of the region in the source image from which
* the JPEG or YUV image should be compressed/encoded
*
* @param width width (in pixels) of the region in the source image from
* which the JPEG or YUV image should be compressed/encoded (0 = use the
* width of the source image)
*
* @param height height (in pixels) of the region in the source image from
* which the JPEG or YUV image should be compressed/encoded (0 = use the
* height of the source image)
*/
public void setSourceImage(BufferedImage srcImage, int x, int y, int width,
int height) throws TJException {
if (handle == 0) init();
if (srcImage == null || x < 0 || y < 0 || width < 0 || height < 0)
throw new IllegalArgumentException("Invalid argument in setSourceImage()");
srcX = x;
srcY = y;
srcWidth = (width == 0) ? srcImage.getWidth() : width;
srcHeight = (height == 0) ? srcImage.getHeight() : height;
if (x + width > srcImage.getWidth() || y + height > srcImage.getHeight())
throw new IllegalArgumentException("Compression region exceeds the bounds of the source image");
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 IllegalArgumentException("Unsupported BufferedImage format");
}
srcPixelFormat = pixelFormat;
WritableRaster wr = srcImage.getRaster();
if (intPixels) {
SinglePixelPackedSampleModel sm =
(SinglePixelPackedSampleModel)srcImage.getSampleModel();
srcStride = sm.getScanlineStride();
DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
srcBufInt = db.getData();
srcBuf = null;
} else {
ComponentSampleModel sm =
(ComponentSampleModel)srcImage.getSampleModel();
int pixelSize = sm.getPixelStride();
if (pixelSize != TJ.getPixelSize(pixelFormat))
throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage");
srcPitch = sm.getScanlineStride();
DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
srcBuf = db.getData();
srcBufInt = null;
}
srcYUVImage = null;
}
/**
* Associate a planar YUV source image with this compressor instance.
*
* @param srcImage planar YUV source image to be compressed. This image is
* not modified.
*/
public void setSourceImage(YUVImage srcImage) throws TJException {
if (handle == 0) init();
if (srcImage == null)
throw new IllegalArgumentException("Invalid argument in setSourceImage()");
srcYUVImage = srcImage;
srcBuf = null;
srcBufInt = null;
}
/**
* Set the level of chrominance subsampling for subsequent compress/encode
* operations. When pixels are converted from RGB to YCbCr (see
* {@link TJ#CS_YCbCr}) or from CMYK to YCCK (see {@link TJ#CS_YCCK}) as part
* of the JPEG compression process, some of the Cb and Cr (chrominance)
* components can be discarded or averaged together to produce a smaller
* image with little perceptible loss of image clarity. (The human eye is
* more sensitive to small changes in brightness than to small changes in
* color.) This is called "chrominance subsampling".
* <p>
* NOTE: This method has no effect when compressing a JPEG image from a
* planar YUV source image. In that case, the level of chrominance
* subsampling in the JPEG image is determined by the source image.
* Furthermore, this method has no effect when encoding to a pre-allocated
* {@link YUVImage} instance. In that case, the level of chrominance
* subsampling is determined by the destination image.
*
* @param newSubsamp the level of chrominance subsampling to use in
* subsequent compress/encode oeprations (one of
* {@link TJ#SAMP_444 TJ.SAMP_*})
*/
public void setSubsamp(int newSubsamp) {
if (newSubsamp < 0 || newSubsamp >= TJ.NUMSAMP)
throw new IllegalArgumentException("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) {
if (quality < 1 || quality > 100)
throw new IllegalArgumentException("Invalid argument in setJPEGQuality()");
jpegQuality = quality;
}
/**
* Compress the packed-pixel or planar YUV 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 source image's width and height and the desired level of chrominance
* subsampling.
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*/
public void compress(byte[] dstBuf, int flags) throws TJException {
if (dstBuf == null || flags < 0)
throw new IllegalArgumentException("Invalid argument in compress()");
if (srcBuf == null && srcBufInt == null && srcYUVImage == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (jpegQuality < 0)
throw new IllegalStateException("JPEG Quality not set");
if (subsamp < 0 && srcYUVImage == null)
throw new IllegalStateException("Subsampling level not set");
if (srcYUVImage != null)
compressedSize = compressFromYUV(srcYUVImage.getPlanes(),
srcYUVImage.getOffsets(),
srcYUVImage.getWidth(),
srcYUVImage.getStrides(),
srcYUVImage.getHeight(),
srcYUVImage.getSubsamp(),
dstBuf, jpegQuality, flags);
else if (srcBuf != null) {
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);
} else if (srcBufInt != null) {
if (srcX >= 0 && srcY >= 0)
compressedSize = compress(srcBufInt, srcX, srcY, srcWidth, srcStride,
srcHeight, srcPixelFormat, dstBuf, subsamp,
jpegQuality, flags);
else
compressedSize = compress(srcBufInt, srcWidth, srcStride, srcHeight,
srcPixelFormat, dstBuf, subsamp, jpegQuality,
flags);
}
}
/**
* Compress the packed-pixel or planar YUV 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#FLAG_BOTTOMUP 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 TJException {
byte[] buf;
if (srcYUVImage != null) {
buf = new byte[TJ.bufSize(srcYUVImage.getWidth(),
srcYUVImage.getHeight(),
srcYUVImage.getSubsamp())];
} else {
checkSourceImage();
buf = new byte[TJ.bufSize(srcWidth, srcHeight, subsamp)];
}
compress(buf, flags);
return buf;
}
/**
* @deprecated Use
* {@link #setSourceImage(BufferedImage, int, int, int, int)} and
* {@link #compress(byte[], int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public void compress(BufferedImage srcImage, byte[] dstBuf, int flags)
throws TJException {
setSourceImage(srcImage, 0, 0, 0, 0);
compress(dstBuf, flags);
}
/**
* @deprecated Use
* {@link #setSourceImage(BufferedImage, int, int, int, int)} and
* {@link #compress(int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public byte[] compress(BufferedImage srcImage, int flags)
throws TJException {
setSourceImage(srcImage, 0, 0, 0, 0);
return compress(flags);
}
/**
* Encode the packed-pixel source image associated with this compressor
* instance into a planar YUV image and store it in the given
* {@link YUVImage} instance. This method performs color conversion (which
* is accelerated in the libjpeg-turbo implementation) but does not execute
* any of the other steps in the JPEG compression process. Encoding CMYK
* source images into YUV images is not supported.
*
* @param dstImage {@link YUVImage} instance that will receive the planar YUV
* image
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*/
public void encodeYUV(YUVImage dstImage, int flags) throws TJException {
if (dstImage == null || flags < 0)
throw new IllegalArgumentException("Invalid argument in encodeYUV()");
if (srcBuf == null && srcBufInt == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (srcYUVImage != null)
throw new IllegalStateException("Source image is not correct type");
checkSubsampling();
if (srcWidth != dstImage.getWidth() || srcHeight != dstImage.getHeight())
throw new IllegalStateException("Destination image is the wrong size");
if (srcBufInt != null) {
encodeYUV(srcBufInt, srcX, srcY, srcWidth, srcStride, srcHeight,
srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(),
dstImage.getStrides(), dstImage.getSubsamp(), flags);
} else {
encodeYUV(srcBuf, srcX, srcY, srcWidth, srcPitch, srcHeight,
srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(),
dstImage.getStrides(), dstImage.getSubsamp(), flags);
}
compressedSize = 0;
}
/**
* @deprecated Use {@link #encodeYUV(YUVImage, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public void encodeYUV(byte[] dstBuf, int flags) throws TJException {
if (dstBuf == null)
throw new IllegalArgumentException("Invalid argument in encodeYUV()");
checkSourceImage();
checkSubsampling();
YUVImage dstYUVImage = new YUVImage(dstBuf, srcWidth, 4, srcHeight,
subsamp);
encodeYUV(dstYUVImage, flags);
}
/**
* Encode the packed-pixel source image associated with this compressor
* instance into a unified planar YUV image and return a {@link YUVImage}
* instance containing the encoded image. This method performs color
* conversion (which is accelerated in the libjpeg-turbo implementation) but
* does not execute any of the other steps in the JPEG compression process.
* Encoding CMYK source images into YUV images is not supported.
*
* @param align row alignment (in bytes) of the YUV image (must be a power of
* 2.) Setting this parameter to n will cause each row in each plane of the
* YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*
* @return a {@link YUVImage} instance containing the unified planar YUV
* encoded image
*/
public YUVImage encodeYUV(int align, int flags) throws TJException {
checkSourceImage();
checkSubsampling();
if (align < 1 || ((align & (align - 1)) != 0))
throw new IllegalStateException("Invalid argument in encodeYUV()");
YUVImage dstYUVImage = new YUVImage(srcWidth, align, srcHeight, subsamp);
encodeYUV(dstYUVImage, flags);
return dstYUVImage;
}
/**
* Encode the packed-pixel source image associated with this compressor
* instance into separate Y, U (Cb), and V (Cr) image planes and return a
* {@link YUVImage} instance containing the encoded image planes. This
* method performs color conversion (which is accelerated in the
* libjpeg-turbo implementation) but does not execute any of the other steps
* in the JPEG compression process. Encoding CMYK source images into YUV
* images is not supported.
*
* @param strides an array of integers, each specifying the number of bytes
* per row in the corresponding plane of the YUV source image. Setting the
* stride for any plane to 0 is the same as setting it to the plane width
* (see {@link YUVImage}.) If <code>strides</code> is null, then the strides
* for all planes will be set to their respective plane widths. You can
* adjust the strides in order to add an arbitrary amount of row padding to
* each plane.
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*
* @return a {@link YUVImage} instance containing the encoded image planes
*/
public YUVImage encodeYUV(int[] strides, int flags) throws TJException {
checkSourceImage();
checkSubsampling();
YUVImage dstYUVImage = new YUVImage(srcWidth, strides, srcHeight, subsamp);
encodeYUV(dstYUVImage, flags);
return dstYUVImage;
}
/**
* @deprecated Use {@link #encodeYUV(int, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public byte[] encodeYUV(int flags) throws TJException {
checkSourceImage();
checkSubsampling();
YUVImage dstYUVImage = new YUVImage(srcWidth, 4, srcHeight, subsamp);
encodeYUV(dstYUVImage, flags);
return dstYUVImage.getBuf();
}
/**
* @deprecated Use
* {@link #setSourceImage(BufferedImage, int, int, int, int)} and
* {@link #encodeYUV(byte[], int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public void encodeYUV(BufferedImage srcImage, byte[] dstBuf, int flags)
throws TJException {
setSourceImage(srcImage, 0, 0, 0, 0);
encodeYUV(dstBuf, flags);
}
/**
* @deprecated Use
* {@link #setSourceImage(BufferedImage, int, int, int, int)} and
* {@link #encodeYUV(int, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public byte[] encodeYUV(BufferedImage srcImage, int flags)
throws TJException {
setSourceImage(srcImage, 0, 0, 0, 0);
return encodeYUV(flags);
}
/**
* Returns the size of the image (in bytes) generated by the most recent
* compress operation.
*
* @return the size of the image (in bytes) generated by the most recent
* compress operation.
*/
public int getCompressedSize() {
return compressedSize;
}
/**
* Free the native structures associated with this compressor instance.
*/
@Override
public void close() throws TJException {
if (handle != 0)
destroy();
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
protected void finalize() throws Throwable {
try {
close();
} catch (TJException e) {
} finally {
super.finalize();
}
};
private native void init() throws TJException;
private native void destroy() throws TJException;
// JPEG size in bytes is returned
@SuppressWarnings("checkstyle:HiddenField")
@Deprecated
private native int compress(byte[] srcBuf, int width, int pitch,
int height, int pixelFormat, byte[] jpegBuf, int jpegSubsamp, int jpegQual,
int flags) throws TJException;
@SuppressWarnings("checkstyle:HiddenField")
private native int compress(byte[] srcBuf, int x, int y, int width,
int pitch, int height, int pixelFormat, byte[] jpegBuf, int jpegSubsamp,
int jpegQual, int flags) throws TJException;
@SuppressWarnings("checkstyle:HiddenField")
@Deprecated
private native int compress(int[] srcBuf, int width, int stride,
int height, int pixelFormat, byte[] jpegBuf, int jpegSubsamp, int jpegQual,
int flags) throws TJException;
@SuppressWarnings("checkstyle:HiddenField")
private native int compress(int[] srcBuf, int x, int y, int width,
int stride, int height, int pixelFormat, byte[] jpegBuf, int jpegSubsamp,
int jpegQual, int flags) throws TJException;
@SuppressWarnings("checkstyle:HiddenField")
private native int compressFromYUV(byte[][] srcPlanes, int[] srcOffsets,
int width, int[] srcStrides, int height, int subsamp, byte[] jpegBuf,
int jpegQual, int flags)
throws TJException;
@SuppressWarnings("checkstyle:HiddenField")
@Deprecated
private native void encodeYUV(byte[] srcBuf, int width, int pitch,
int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags)
throws TJException;
@SuppressWarnings("checkstyle:HiddenField")
private native void encodeYUV(byte[] srcBuf, int x, int y, int width,
int pitch, int height, int pixelFormat, byte[][] dstPlanes,
int[] dstOffsets, int[] dstStrides, int subsamp, int flags)
throws TJException;
@SuppressWarnings("checkstyle:HiddenField")
@Deprecated
private native void encodeYUV(int[] srcBuf, int width, int stride,
int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags)
throws TJException;
@SuppressWarnings("checkstyle:HiddenField")
private native void encodeYUV(int[] srcBuf, int x, int y, int width,
int srcStride, int height, int pixelFormat, byte[][] dstPlanes,
int[] dstOffsets, int[] dstStrides, int subsamp, int flags)
throws TJException;
static {
TJLoader.load();
}
private void checkSourceImage() {
if (srcWidth < 1 || srcHeight < 1)
throw new IllegalStateException(NO_ASSOC_ERROR);
}
private void checkSubsampling() {
if (subsamp < 0)
throw new IllegalStateException("Subsampling level not set");
}
private long handle = 0;
private byte[] srcBuf = null;
private int[] srcBufInt = null;
private int srcWidth = 0;
private int srcHeight = 0;
private int srcX = -1;
private int srcY = -1;
private int srcPitch = 0;
private int srcStride = 0;
private int srcPixelFormat = -1;
private YUVImage srcYUVImage = null;
private int subsamp = -1;
private int jpegQuality = -1;
private int compressedSize = 0;
private ByteOrder byteOrder = null;
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C)2011, 2013, 2023 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 image. 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, Cb, and Cr 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 TJException;
}

View File

@@ -0,0 +1,953 @@
/*
* Copyright (C)2011-2015, 2018, 2022-2023 D. R. Commander.
* All Rights Reserved.
* Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the libjpeg-turbo Project nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.libjpegturbo.turbojpeg;
import java.awt.image.*;
import java.nio.*;
import java.io.*;
/**
* TurboJPEG decompressor
*/
public class TJDecompressor implements Closeable {
private static final String NO_ASSOC_ERROR =
"No JPEG image is associated with this instance";
/**
* Create a TurboJPEG decompresssor instance.
*/
public TJDecompressor() throws TJException {
init();
}
/**
* Create a TurboJPEG decompressor instance and associate the JPEG source
* image or "abbreviated table specification" (AKA "tables-only") datastream
* stored in <code>jpegImage</code> with the newly created instance.
*
* @param jpegImage buffer containing a JPEG source image or tables-only
* datastream. (The size of the JPEG image or datastream is assumed to be
* the length of the array.) This buffer is not modified.
*/
public TJDecompressor(byte[] jpegImage) throws TJException {
init();
setSourceImage(jpegImage, jpegImage.length);
}
/**
* Create a TurboJPEG decompressor instance and associate the JPEG source
* image or "abbreviated table specification" (AKA "tables-only") datastream
* of length <code>imageSize</code> bytes stored in <code>jpegImage</code>
* with the newly created instance.
*
* @param jpegImage buffer containing a JPEG source image or tables-only
* datastream. This buffer is not modified.
*
* @param imageSize size of the JPEG source image or tables-only datastream
* (in bytes)
*/
public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException {
init();
setSourceImage(jpegImage, imageSize);
}
/**
* Create a TurboJPEG decompressor instance and associate the planar YUV
* source image stored in <code>yuvImage</code> with the newly created
* instance.
*
* @param yuvImage {@link YUVImage} instance containing a planar YUV source
* image to be decoded. This image is not modified.
*/
@SuppressWarnings("checkstyle:HiddenField")
public TJDecompressor(YUVImage yuvImage) throws TJException {
init();
setSourceImage(yuvImage);
}
/**
* Associate the JPEG image or "abbreviated table specification" (AKA
* "tables-only") datastream of length <code>imageSize</code> bytes stored in
* <code>jpegImage</code> with this decompressor instance. If
* <code>jpegImage</code> contains a JPEG image, then this image will be used
* as the source image for subsequent decompression operations. Passing a
* tables-only datastream to this method primes the decompressor with
* quantization and Huffman tables that can be used when decompressing
* subsequent "abbreviated image" datastreams. This is useful, for instance,
* when decompressing video streams in which all frames share the same
* quantization and Huffman tables.
*
* @param jpegImage buffer containing a JPEG source image or tables-only
* datastream. This buffer is not modified.
*
* @param imageSize size of the JPEG source image or tables-only datastream
* (in bytes)
*/
public void setSourceImage(byte[] jpegImage, int imageSize)
throws TJException {
if (jpegImage == null || imageSize < 1)
throw new IllegalArgumentException("Invalid argument in setSourceImage()");
jpegBuf = jpegImage;
jpegBufSize = imageSize;
decompressHeader(jpegBuf, jpegBufSize);
yuvImage = null;
}
/**
* @deprecated Use {@link #setSourceImage(byte[], int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public void setJPEGImage(byte[] jpegImage, int imageSize)
throws TJException {
setSourceImage(jpegImage, imageSize);
}
/**
* Associate the specified planar YUV source image with this decompressor
* instance. Subsequent decompression operations will decode this image into
* a packed-pixel RGB or grayscale destination image.
*
* @param srcImage {@link YUVImage} instance containing a planar YUV source
* image to be decoded. This image is not modified.
*/
public void setSourceImage(YUVImage srcImage) {
if (srcImage == null)
throw new IllegalArgumentException("Invalid argument in setSourceImage()");
yuvImage = srcImage;
jpegBuf = null;
jpegBufSize = 0;
}
/**
* Returns the width of the source image (JPEG or YUV) associated with this
* decompressor instance.
*
* @return the width of the source image (JPEG or YUV) associated with this
* decompressor instance.
*/
public int getWidth() {
if (yuvImage != null)
return yuvImage.getWidth();
if (jpegWidth < 1)
throw new IllegalStateException(NO_ASSOC_ERROR);
return jpegWidth;
}
/**
* Returns the height of the source image (JPEG or YUV) associated with this
* decompressor instance.
*
* @return the height of the source image (JPEG or YUV) associated with this
* decompressor instance.
*/
public int getHeight() {
if (yuvImage != null)
return yuvImage.getHeight();
if (jpegHeight < 1)
throw new IllegalStateException(NO_ASSOC_ERROR);
return jpegHeight;
}
/**
* Returns the level of chrominance subsampling used in the source image
* (JPEG or YUV) associated with this decompressor instance. See
* {@link TJ#SAMP_444 TJ.SAMP_*}.
*
* @return the level of chrominance subsampling used in the source image
* (JPEG or YUV) associated with this decompressor instance.
*/
public int getSubsamp() {
if (yuvImage != null)
return yuvImage.getSubsamp();
if (jpegSubsamp < 0)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (jpegSubsamp >= TJ.NUMSAMP)
throw new IllegalStateException("JPEG header information is invalid");
return jpegSubsamp;
}
/**
* Returns the colorspace used in the source image (JPEG or YUV) associated
* with this decompressor instance. See {@link TJ#CS_RGB TJ.CS_*}. If the
* source image is YUV, then this always returns {@link TJ#CS_YCbCr}.
*
* @return the colorspace used in the source image (JPEG or YUV) associated
* with this decompressor instance.
*/
public int getColorspace() {
if (yuvImage != null)
return TJ.CS_YCbCr;
if (jpegColorspace < 0)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (jpegColorspace >= TJ.NUMCS)
throw new IllegalStateException("JPEG header information is invalid");
return jpegColorspace;
}
/**
* Returns the JPEG buffer associated with this decompressor instance.
*
* @return the JPEG buffer associated with this decompressor instance.
*/
public byte[] getJPEGBuf() {
if (jpegBuf == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
return jpegBuf;
}
/**
* Returns the size of the JPEG image (in bytes) associated with this
* decompressor instance.
*
* @return the size of the JPEG image (in bytes) associated with this
* decompressor instance.
*/
public int getJPEGSize() {
if (jpegBufSize < 1)
throw new IllegalStateException(NO_ASSOC_ERROR);
return jpegBufSize;
}
/**
* Returns the width of the largest scaled-down image that the TurboJPEG
* decompressor can generate without exceeding the desired image width and
* height.
*
* @param desiredWidth desired width (in pixels) of the decompressed image.
* Setting this to 0 is the same as setting it to the width of the JPEG
* image. (In other words, the width will not be considered when determining
* the scaled image size.)
*
* @param desiredHeight desired height (in pixels) of the decompressed image.
* Setting this to 0 is the same as setting it to the height of the JPEG
* image. (In other words, the height will not be considered when
* determining the scaled image size.)
*
* @return the width of the largest scaled-down image that the TurboJPEG
* decompressor can generate without exceeding the desired image width and
* height.
*/
public int getScaledWidth(int desiredWidth, int desiredHeight) {
if (jpegWidth < 1 || jpegHeight < 1)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (desiredWidth < 0 || desiredHeight < 0)
throw new IllegalArgumentException("Invalid argument in getScaledWidth()");
TJScalingFactor[] sf = TJ.getScalingFactors();
if (desiredWidth == 0)
desiredWidth = jpegWidth;
if (desiredHeight == 0)
desiredHeight = jpegHeight;
int scaledWidth = jpegWidth, scaledHeight = jpegHeight;
for (int i = 0; i < sf.length; i++) {
scaledWidth = sf[i].getScaled(jpegWidth);
scaledHeight = sf[i].getScaled(jpegHeight);
if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight)
break;
}
if (scaledWidth > desiredWidth || scaledHeight > desiredHeight)
throw new IllegalArgumentException("Could not scale down to desired image dimensions");
return scaledWidth;
}
/**
* Returns the height of the largest scaled-down image that the TurboJPEG
* decompressor can generate without exceeding the desired image width and
* height.
*
* @param desiredWidth desired width (in pixels) of the decompressed image.
* Setting this to 0 is the same as setting it to the width of the JPEG
* image. (In other words, the width will not be considered when determining
* the scaled image size.)
*
* @param desiredHeight desired height (in pixels) of the decompressed image.
* Setting this to 0 is the same as setting it to the height of the JPEG
* image. (In other words, the height will not be considered when
* determining the scaled image size.)
*
* @return the height of the largest scaled-down image that the TurboJPEG
* decompressor can generate without exceeding the desired image width and
* height.
*/
public int getScaledHeight(int desiredWidth, int desiredHeight) {
if (jpegWidth < 1 || jpegHeight < 1)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (desiredWidth < 0 || desiredHeight < 0)
throw new IllegalArgumentException("Invalid argument in getScaledHeight()");
TJScalingFactor[] sf = TJ.getScalingFactors();
if (desiredWidth == 0)
desiredWidth = jpegWidth;
if (desiredHeight == 0)
desiredHeight = jpegHeight;
int scaledWidth = jpegWidth, scaledHeight = jpegHeight;
for (int i = 0; i < sf.length; i++) {
scaledWidth = sf[i].getScaled(jpegWidth);
scaledHeight = sf[i].getScaled(jpegHeight);
if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight)
break;
}
if (scaledWidth > desiredWidth || scaledHeight > desiredHeight)
throw new IllegalArgumentException("Could not scale down to desired image dimensions");
return scaledHeight;
}
/**
* Decompress the JPEG source image or decode the planar YUV source image
* associated with this decompressor instance and output a packed-pixel
* grayscale, RGB, or CMYK image to the given destination buffer.
* <p>
* NOTE: The destination image is fully recoverable if this method throws a
* non-fatal {@link TJException} (unless
* {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
*
* @param dstBuf buffer that will receive the packed-pixel
* decompressed/decoded image. If the source image is a JPEG image, then
* this buffer should normally be <code>pitch * scaledHeight</code> bytes in
* size, where <code>scaledHeight</code> can be determined by calling
* <code>scalingFactor.</code>{@link TJScalingFactor#getScaled getScaled}<code>(jpegHeight)</code>
* with one of the scaling factors returned from {@link TJ#getScalingFactors}
* or by calling {@link #getScaledHeight}. If the source image is a YUV
* image, then this buffer should normally be <code>pitch * height</code>
* bytes in size, where <code>height</code> is the height of the YUV image.
* However, the buffer may also be larger than the dimensions of the source
* image, in which case the <code>x</code>, <code>y</code>, and
* <code>pitch</code> parameters can be used to specify the region into which
* the source image should be decompressed/decoded.
*
* @param x x offset (in pixels) of the region in the destination image into
* which the source image should be decompressed/decoded
*
* @param y y offset (in pixels) of the region in the destination image into
* which the source image should be decompressed/decoded
*
* @param desiredWidth If the source image is a JPEG image, then this
* specifies the desired width (in pixels) of the decompressed image (or
* image region.) If the desired destination image dimensions are different
* than the source image dimensions, then TurboJPEG will use scaling in the
* JPEG decompressor to generate the largest possible image that will fit
* within the desired dimensions. Setting this to 0 is the same as setting
* it to the width of the JPEG image. (In other words, the width will not be
* considered when determining the scaled image size.) This parameter is
* ignored if the source image is a YUV image.
*
* @param pitch bytes per row in the destination image. Normally this should
* be set to <code>scaledWidth *
* </code>{@link TJ#getPixelSize TJ.getPixelSize}<code>(pixelFormat)</code>,
* if the destination image will be unpadded. However, you can use this to,
* for instance, pad each row of the destination image to the nearest
* multiple of 4 bytes or to decompress/decode the source image into a region
* of a larger image. NOTE: if the source image is a JPEG image, then
* <code>scaledWidth</code> can be determined by calling
* <code>scalingFactor.</code>{@link TJScalingFactor#getScaled getScaled}<code>(jpegWidth)</code>
* or by calling {@link #getScaledWidth}. If the source image is a YUV
* image, then <code>scaledWidth</code> is the width of the YUV image.
* Setting this parameter to 0 is the equivalent of setting it to
* <code>scaledWidth *
* </code>{@link TJ#getPixelSize TJ.getPixelSize}<code>(pixelFormat)</code>.
*
* @param desiredHeight If the source image is a JPEG image, then this
* specifies the desired height (in pixels) of the decompressed image (or
* image region.) If the desired destination image dimensions are different
* than the source image dimensions, then TurboJPEG will use scaling in the
* JPEG decompressor to generate the largest possible image that will fit
* within the desired dimensions. Setting this to 0 is the same as setting
* it to the height of the JPEG image. (In other words, the height will not
* be considered when determining the scaled image size.) This parameter is
* ignored if the source image is a YUV image.
*
* @param pixelFormat pixel format of the decompressed/decoded image (one of
* {@link TJ#PF_RGB TJ.PF_*})
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*/
public void decompress(byte[] dstBuf, int x, int y, int desiredWidth,
int pitch, int desiredHeight, int pixelFormat,
int flags) throws TJException {
if (jpegBuf == null && yuvImage == null)
throw new IllegalStateException("No source image is associated with this instance");
if (dstBuf == null || x < 0 || y < 0 || pitch < 0 ||
(yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
throw new IllegalArgumentException("Invalid argument in decompress()");
if (yuvImage != null)
decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y,
yuvImage.getWidth(), pitch, yuvImage.getHeight(), pixelFormat,
flags);
else {
if (x > 0 || y > 0)
decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, pitch,
desiredHeight, pixelFormat, flags);
else
decompress(jpegBuf, jpegBufSize, dstBuf, desiredWidth, pitch,
desiredHeight, pixelFormat, flags);
}
}
/**
* @deprecated Use
* {@link #decompress(byte[], int, int, int, int, int, int, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public void decompress(byte[] dstBuf, int desiredWidth, int pitch,
int desiredHeight, int pixelFormat, int flags)
throws TJException {
decompress(dstBuf, 0, 0, desiredWidth, pitch, desiredHeight, pixelFormat,
flags);
}
/**
* Decompress the JPEG source image or decode the planar YUV source image
* associated with this decompressor instance and return a buffer containing
* the packed-pixel decompressed image.
*
* @param desiredWidth see
* {@link #decompress(byte[], int, int, int, int, int, int, int)}
* for description
*
* @param pitch see
* {@link #decompress(byte[], int, int, int, int, int, int, int)}
* for description
*
* @param desiredHeight see
* {@link #decompress(byte[], int, int, int, int, int, int, int)}
* for description
*
* @param pixelFormat pixel format of the decompressed image (one of
* {@link TJ#PF_RGB TJ.PF_*})
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*
* @return a buffer containing the packed-pixel decompressed image.
*/
public byte[] decompress(int desiredWidth, int pitch, int desiredHeight,
int pixelFormat, int flags) throws TJException {
if (pitch < 0 ||
(yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
throw new IllegalArgumentException("Invalid argument in decompress()");
int pixelSize = TJ.getPixelSize(pixelFormat);
int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
if (pitch == 0)
pitch = scaledWidth * pixelSize;
byte[] buf = new byte[pitch * scaledHeight];
decompress(buf, desiredWidth, pitch, desiredHeight, pixelFormat, flags);
return buf;
}
/**
* Decompress the JPEG source image associated with this decompressor
* instance into a planar YUV image and store it in the given
* {@link YUVImage} instance. This method performs JPEG decompression but
* leaves out the color conversion step, so a planar YUV image is generated
* instead of a packed-pixel image. This method cannot be used to decompress
* JPEG source images with the CMYK or YCCK colorspace.
* <p>
* NOTE: The planar YUV destination image is fully recoverable if this method
* throws a non-fatal {@link TJException} (unless
* {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
*
* @param dstImage {@link YUVImage} instance that will receive the planar YUV
* decompressed image. The level of subsampling specified in this
* {@link YUVImage} instance must match that of the JPEG image, and the width
* and height specified in the {@link YUVImage} instance must match one of
* the scaled image sizes that the decompressor is capable of generating from
* the JPEG source image.
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*/
public void decompressToYUV(YUVImage dstImage, int flags)
throws TJException {
if (jpegBuf == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (dstImage == null || flags < 0)
throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
int scaledWidth = getScaledWidth(dstImage.getWidth(),
dstImage.getHeight());
int scaledHeight = getScaledHeight(dstImage.getWidth(),
dstImage.getHeight());
if (scaledWidth != dstImage.getWidth() ||
scaledHeight != dstImage.getHeight())
throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that the decompressor is capable of generating.");
if (jpegSubsamp != dstImage.getSubsamp())
throw new IllegalArgumentException("YUVImage subsampling level does not match that of the JPEG image");
decompressToYUV(jpegBuf, jpegBufSize, dstImage.getPlanes(),
dstImage.getOffsets(), dstImage.getWidth(),
dstImage.getStrides(), dstImage.getHeight(), flags);
}
/**
* @deprecated Use {@link #decompressToYUV(YUVImage, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public void decompressToYUV(byte[] dstBuf, int flags) throws TJException {
YUVImage dstYUVImage = new YUVImage(dstBuf, jpegWidth, 4, jpegHeight,
jpegSubsamp);
decompressToYUV(dstYUVImage, flags);
}
/**
* Decompress the JPEG source image associated with this decompressor
* instance into a set of Y, U (Cb), and V (Cr) image planes and return a
* {@link YUVImage} instance containing the decompressed image planes. This
* method performs JPEG decompression but leaves out the color conversion
* step, so a planar YUV image is generated instead of a packed-pixel image.
* This method cannot be used to decompress JPEG source images with the CMYK
* or YCCK colorspace.
*
* @param desiredWidth desired width (in pixels) of the YUV image. If the
* desired image dimensions are different than the dimensions of the JPEG
* image being decompressed, then TurboJPEG will use scaling in the JPEG
* decompressor to generate the largest possible image that will fit within
* the desired dimensions. Setting this to 0 is the same as setting it to
* the width of the JPEG image. (In other words, the width will not be
* considered when determining the scaled image size.)
*
* @param strides an array of integers, each specifying the number of bytes
* per row in the corresponding plane of the YUV image. Setting the stride
* for any plane to 0 is the same as setting it to the scaled plane width
* (see {@link YUVImage}.) If <code>strides</code> is null, then the strides
* for all planes will be set to their respective scaled plane widths. You
* can adjust the strides in order to add an arbitrary amount of row padding
* to each plane.
*
* @param desiredHeight desired height (in pixels) of the YUV image. If the
* desired image dimensions are different than the dimensions of the JPEG
* image being decompressed, then TurboJPEG will use scaling in the JPEG
* decompressor to generate the largest possible image that will fit within
* the desired dimensions. Setting this to 0 is the same as setting it to
* the height of the JPEG image. (In other words, the height will not be
* considered when determining the scaled image size.)
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*
* @return a {@link YUVImage} instance containing the decompressed image
* planes
*/
public YUVImage decompressToYUV(int desiredWidth, int[] strides,
int desiredHeight,
int flags) throws TJException {
if (flags < 0)
throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (jpegSubsamp >= TJ.NUMSAMP)
throw new IllegalStateException("JPEG header information is invalid");
if (yuvImage != null)
throw new IllegalStateException("Source image is the wrong type");
int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
YUVImage dstYUVImage = new YUVImage(scaledWidth, null, scaledHeight,
jpegSubsamp);
decompressToYUV(dstYUVImage, flags);
return dstYUVImage;
}
/**
* Decompress the JPEG source image associated with this decompressor
* instance into a unified planar YUV image and return a {@link YUVImage}
* instance containing the decompressed image. This method performs JPEG
* decompression but leaves out the color conversion step, so a planar YUV
* image is generated instead of a packed-pixel image. This method cannot be
* used to decompress JPEG source images with the CMYK or YCCK colorspace.
*
* @param desiredWidth desired width (in pixels) of the YUV image. If the
* desired image dimensions are different than the dimensions of the JPEG
* image being decompressed, then TurboJPEG will use scaling in the JPEG
* decompressor to generate the largest possible image that will fit within
* the desired dimensions. Setting this to 0 is the same as setting it to
* the width of the JPEG image. (In other words, the width will not be
* considered when determining the scaled image size.)
*
* @param align row alignment (in bytes) of the YUV image (must be a power of
* 2.) Setting this parameter to n will cause each row in each plane of the
* YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
*
* @param desiredHeight desired height (in pixels) of the YUV image. If the
* desired image dimensions are different than the dimensions of the JPEG
* image being decompressed, then TurboJPEG will use scaling in the JPEG
* decompressor to generate the largest possible image that will fit within
* the desired dimensions. Setting this to 0 is the same as setting it to
* the height of the JPEG image. (In other words, the height will not be
* considered when determining the scaled image size.)
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*
* @return a {@link YUVImage} instance containing the unified planar YUV
* decompressed image
*/
public YUVImage decompressToYUV(int desiredWidth, int align,
int desiredHeight, int flags)
throws TJException {
if (flags < 0)
throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (jpegSubsamp >= TJ.NUMSAMP)
throw new IllegalStateException("JPEG header information is invalid");
if (yuvImage != null)
throw new IllegalStateException("Source image is the wrong type");
int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
YUVImage dstYUVImage = new YUVImage(scaledWidth, align, scaledHeight,
jpegSubsamp);
decompressToYUV(dstYUVImage, flags);
return dstYUVImage;
}
/**
* @deprecated Use {@link #decompressToYUV(int, int, int, int)} instead.
*/
@SuppressWarnings("checkstyle:JavadocMethod")
@Deprecated
public byte[] decompressToYUV(int flags) throws TJException {
YUVImage dstYUVImage = new YUVImage(jpegWidth, 4, jpegHeight, jpegSubsamp);
decompressToYUV(dstYUVImage, flags);
return dstYUVImage.getBuf();
}
/**
* Decompress the JPEG source image or decode the planar YUV source image
* associated with this decompressor instance and output a packed-pixel
* grayscale, RGB, or CMYK image to the given destination buffer.
* <p>
* NOTE: The destination image is fully recoverable if this method throws a
* non-fatal {@link TJException} (unless
* {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
*
* @param dstBuf buffer that will receive the packed-pixel
* decompressed/decoded image. If the source image is a JPEG image, then
* this buffer should normally be <code>stride * scaledHeight</code> pixels
* in size, where <code>scaledHeight</code> can be determined by calling
* <code>scalingFactor.</code>{@link TJScalingFactor#getScaled getScaled}<code>(jpegHeight)</code>
* with one of the scaling factors returned from {@link TJ#getScalingFactors}
* or by calling {@link #getScaledHeight}. If the source image is a YUV
* image, then this buffer should normally be <code>stride * height</code>
* pixels in size, where <code>height</code> is the height of the YUV image.
* However, the buffer may also be larger than the dimensions of the JPEG
* image, in which case the <code>x</code>, <code>y</code>, and
* <code>stride</code> parameters can be used to specify the region into
* which the source image should be decompressed.
*
* @param x x offset (in pixels) of the region in the destination image into
* which the source image should be decompressed/decoded
*
* @param y y offset (in pixels) of the region in the destination image into
* which the source image should be decompressed/decoded
*
* @param desiredWidth If the source image is a JPEG image, then this
* specifies the desired width (in pixels) of the decompressed image (or
* image region.) If the desired destination image dimensions are different
* than the source image dimensions, then TurboJPEG will use scaling in the
* JPEG decompressor to generate the largest possible image that will fit
* within the desired dimensions. Setting this to 0 is the same as setting
* it to the width of the JPEG image. (In other words, the width will not be
* considered when determining the scaled image size.) This parameter is
* ignored if the source image is a YUV image.
*
* @param stride pixels per row in the destination image. Normally this
* should be set to <code>scaledWidth</code>, but you can use this to, for
* instance, decompress the JPEG image into a region of a larger image.
* NOTE: if the source image is a JPEG image, then <code>scaledWidth</code>
* can be determined by calling
* <code>scalingFactor.</code>{@link TJScalingFactor#getScaled getScaled}<code>(jpegWidth)</code>
* or by calling {@link #getScaledWidth}. If the source image is a YUV
* image, then <code>scaledWidth</code> is the width of the YUV image.
* Setting this parameter to 0 is the equivalent of setting it to
* <code>scaledWidth</code>.
*
* @param desiredHeight If the source image is a JPEG image, then this
* specifies the desired height (in pixels) of the decompressed image (or
* image region.) If the desired destination image dimensions are different
* than the source image dimensions, then TurboJPEG will use scaling in the
* JPEG decompressor to generate the largest possible image that will fit
* within the desired dimensions. Setting this to 0 is the same as setting
* it to the height of the JPEG image. (In other words, the height will not
* be considered when determining the scaled image size.) This parameter is
* ignored if the source image is a YUV image.
*
* @param pixelFormat pixel format of the decompressed image (one of
* {@link TJ#PF_RGB TJ.PF_*})
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*/
public void decompress(int[] dstBuf, int x, int y, int desiredWidth,
int stride, int desiredHeight, int pixelFormat,
int flags) throws TJException {
if (jpegBuf == null && yuvImage == null)
throw new IllegalStateException("No source image is associated with this instance");
if (dstBuf == null || x < 0 || y < 0 || stride < 0 ||
(yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
throw new IllegalArgumentException("Invalid argument in decompress()");
if (yuvImage != null)
decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y,
yuvImage.getWidth(), stride, yuvImage.getHeight(), pixelFormat,
flags);
else
decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, stride,
desiredHeight, pixelFormat, flags);
}
/**
* Decompress the JPEG source image or decode the planar YUV source image
* associated with this decompressor instance and output a packed-pixel
* decompressed/decoded image to the given <code>BufferedImage</code>
* instance.
* <p>
* NOTE: The destination image is fully recoverable if this method throws a
* non-fatal {@link TJException} (unless
* {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
*
* @param dstImage a <code>BufferedImage</code> instance that will receive
* the packed-pixel decompressed/decoded image. If the source image is a
* JPEG image, then the width and height of the <code>BufferedImage</code>
* instance must match one of the scaled image sizes that the decompressor is
* capable of generating from the JPEG image. If the source image is a YUV
* image, then the width and height of the <code>BufferedImage</code>
* instance must match the width and height of the YUV image.
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*/
public void decompress(BufferedImage dstImage, int flags)
throws TJException {
if (dstImage == null || flags < 0)
throw new IllegalArgumentException("Invalid argument in decompress()");
int desiredWidth = dstImage.getWidth();
int desiredHeight = dstImage.getHeight();
int scaledWidth, scaledHeight;
if (yuvImage != null) {
if (desiredWidth != yuvImage.getWidth() ||
desiredHeight != yuvImage.getHeight())
throw new IllegalArgumentException("BufferedImage dimensions do not match the dimensions of the source image.");
scaledWidth = yuvImage.getWidth();
scaledHeight = yuvImage.getHeight();
} else {
scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
if (scaledWidth != desiredWidth || scaledHeight != desiredHeight)
throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that the decompressor is capable of generating.");
}
int pixelFormat; boolean intPixels = false;
if (byteOrder == null)
byteOrder = ByteOrder.nativeOrder();
switch (dstImage.getType()) {
case BufferedImage.TYPE_3BYTE_BGR:
pixelFormat = TJ.PF_BGR; break;
case BufferedImage.TYPE_4BYTE_ABGR:
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
pixelFormat = TJ.PF_XBGR; break;
case BufferedImage.TYPE_BYTE_GRAY:
pixelFormat = TJ.PF_GRAY; break;
case BufferedImage.TYPE_INT_BGR:
if (byteOrder == ByteOrder.BIG_ENDIAN)
pixelFormat = TJ.PF_XBGR;
else
pixelFormat = TJ.PF_RGBX;
intPixels = true; break;
case BufferedImage.TYPE_INT_RGB:
if (byteOrder == ByteOrder.BIG_ENDIAN)
pixelFormat = TJ.PF_XRGB;
else
pixelFormat = TJ.PF_BGRX;
intPixels = true; break;
case BufferedImage.TYPE_INT_ARGB:
case BufferedImage.TYPE_INT_ARGB_PRE:
if (byteOrder == ByteOrder.BIG_ENDIAN)
pixelFormat = TJ.PF_ARGB;
else
pixelFormat = TJ.PF_BGRA;
intPixels = true; break;
default:
throw new IllegalArgumentException("Unsupported BufferedImage format");
}
WritableRaster wr = dstImage.getRaster();
if (intPixels) {
SinglePixelPackedSampleModel sm =
(SinglePixelPackedSampleModel)dstImage.getSampleModel();
int stride = sm.getScanlineStride();
DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
int[] buf = db.getData();
if (yuvImage != null)
decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
yuvImage.getStrides(), yuvImage.getSubsamp(), buf, 0, 0,
yuvImage.getWidth(), stride, yuvImage.getHeight(),
pixelFormat, flags);
else {
if (jpegBuf == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
decompress(jpegBuf, jpegBufSize, buf, 0, 0, scaledWidth, stride,
scaledHeight, pixelFormat, flags);
}
} else {
ComponentSampleModel sm =
(ComponentSampleModel)dstImage.getSampleModel();
int pixelSize = sm.getPixelStride();
if (pixelSize != TJ.getPixelSize(pixelFormat))
throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage");
int pitch = sm.getScanlineStride();
DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
byte[] buf = db.getData();
decompress(buf, 0, 0, scaledWidth, pitch, scaledHeight, pixelFormat,
flags);
}
}
/**
* Decompress the JPEG source image or decode the planar YUV source image
* associated with this decompressor instance and return a
* <code>BufferedImage</code> instance containing the packed-pixel
* decompressed/decoded image.
*
* @param desiredWidth see
* {@link #decompress(byte[], int, int, int, int, int, int, int)} for
* description
*
* @param desiredHeight see
* {@link #decompress(byte[], int, int, int, int, int, int, int)} for
* description
*
* @param bufferedImageType the image type of the <code>BufferedImage</code>
* instance that will be created (for instance,
* <code>BufferedImage.TYPE_INT_RGB</code>)
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*
* @return a <code>BufferedImage</code> instance containing the packed-pixel
* decompressed/decoded image.
*/
public BufferedImage decompress(int desiredWidth, int desiredHeight,
int bufferedImageType, int flags)
throws TJException {
if ((yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
flags < 0)
throw new IllegalArgumentException("Invalid argument in decompress()");
int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
BufferedImage img = new BufferedImage(scaledWidth, scaledHeight,
bufferedImageType);
decompress(img, flags);
return img;
}
/**
* Free the native structures associated with this decompressor instance.
*/
@Override
public void close() throws TJException {
if (handle != 0)
destroy();
}
@SuppressWarnings("checkstyle:DesignForExtension")
@Override
protected void finalize() throws Throwable {
try {
close();
} catch (TJException e) {
} finally {
super.finalize();
}
};
private native void init() throws TJException;
private native void destroy() throws TJException;
private native void decompressHeader(byte[] srcBuf, int size)
throws TJException;
@Deprecated
private native void decompress(byte[] srcBuf, int size, byte[] dstBuf,
int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)
throws TJException;
private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, int x,
int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat,
int flags) throws TJException;
@Deprecated
private native void decompress(byte[] srcBuf, int size, int[] dstBuf,
int desiredWidth, int stride, int desiredHeight, int pixelFormat,
int flags) throws TJException;
private native void decompress(byte[] srcBuf, int size, int[] dstBuf, int x,
int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat,
int flags) throws TJException;
@Deprecated
private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf,
int flags) throws TJException;
private native void decompressToYUV(byte[] srcBuf, int size,
byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides,
int desiredheight, int flags) throws TJException;
private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets,
int[] srcStrides, int subsamp, byte[] dstBuf, int x, int y, int width,
int pitch, int height, int pixelFormat, int flags) throws TJException;
private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets,
int[] srcStrides, int subsamp, int[] dstBuf, int x, int y, int width,
int stride, int height, int pixelFormat, int flags) throws TJException;
static {
TJLoader.load();
}
protected long handle = 0;
protected byte[] jpegBuf = null;
protected int jpegBufSize = 0;
protected YUVImage yuvImage = null;
protected int jpegWidth = 0;
protected int jpegHeight = 0;
protected int jpegSubsamp = -1;
protected int jpegColorspace = -1;
private ByteOrder byteOrder = null;
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
* Copyright (C)2017-2018 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.io.IOException;
@SuppressWarnings("checkstyle:JavadocType")
public class TJException extends IOException {
private static final long serialVersionUID = 1L;
@SuppressWarnings("checkstyle:JavadocMethod")
public TJException() {
super();
}
@SuppressWarnings("checkstyle:JavadocMethod")
public TJException(String message, Throwable cause) {
super(message, cause);
}
@SuppressWarnings("checkstyle:JavadocMethod")
public TJException(String message) {
super(message);
}
@SuppressWarnings("checkstyle:JavadocMethod")
public TJException(String message, int code) {
super(message);
if (errorCode >= 0 && errorCode < TJ.NUMERR)
errorCode = code;
}
@SuppressWarnings("checkstyle:JavadocMethod")
public TJException(Throwable cause) {
super(cause);
}
/**
* Returns a code (one of {@link TJ TJ.ERR_*}) indicating the severity of the
* last error.
*
* @return a code (one of {@link TJ TJ.ERR_*}) indicating the severity of the
* last error.
*/
public int getErrorCode() {
return errorCode;
}
private int errorCode = TJ.ERR_FATAL;
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C)2011-2013, 2016, 2020 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("@CMAKE_INSTALL_FULL_LIBDIR@/libturbojpeg.dylib");
} catch (java.lang.UnsatisfiedLinkError e2) {
System.load("/usr/lib/libturbojpeg.dylib");
}
} else {
try {
System.load("@CMAKE_INSTALL_FULL_LIBDIR@/libturbojpeg.so");
} catch (java.lang.UnsatisfiedLinkError e3) {
String libdir = "@CMAKE_INSTALL_FULL_LIBDIR@";
if (libdir.equals("@CMAKE_INSTALL_DEFAULT_PREFIX@/lib64")) {
System.load("@CMAKE_INSTALL_DEFAULT_PREFIX@/lib32/libturbojpeg.so");
} else if (libdir.equals("@CMAKE_INSTALL_DEFAULT_PREFIX@/lib32")) {
System.load("@CMAKE_INSTALL_DEFAULT_PREFIX@/lib64/libturbojpeg.so");
} else {
throw e3;
}
}
}
}
}
}

View File

@@ -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@");
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C)2011, 2018 D. R. Commander. All Rights Reserved.
* Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the libjpeg-turbo Project nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.libjpegturbo.turbojpeg;
/**
* Fractional scaling factor
*/
public class TJScalingFactor {
/**
* Create a TurboJPEG scaling factor instance.
*
* @param num numerator
* @param denom denominator
*/
@SuppressWarnings("checkstyle:HiddenField")
public TJScalingFactor(int num, int denom) {
if (num < 1 || denom < 1)
throw new IllegalArgumentException("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>.
*
* @param dimension width or height to multiply by this scaling factor
*
* @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.
*
* @param other the scaling factor against which to compare this one
*
* @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;
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright (C)2011, 2013, 2018, 2023 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 source image and produce a
* grayscale destination 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;
/**
* This option will enable progressive entropy coding in the JPEG image
* generated by this particular transform. Progressive entropy coding will
* generally improve compression relative to baseline entropy coding (the
* default), but it will reduce decompression performance considerably.
*/
public static final int OPT_PROGRESSIVE = 32;
/**
* This option will prevent {@link TJTransformer#transform
* TJTransformer.transform()} from copying any extra markers (including EXIF
* and ICC profile data) from the source image to the destination image.
*/
public static final int OPT_COPYNONE = 64;
/**
* 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 ({@link #OP_NONE OP_*})
*
* @param options the bitwise OR of one or more of the transform options
* ({@link #OPT_PERFECT OPT_*})
*
* @param cf an instance of an object that implements the {@link
* TJCustomFilter} interface, or null if no custom filter is needed
*/
@SuppressWarnings("checkstyle:HiddenField")
public TJTransform(int x, int y, int w, int h, int op, int options,
TJCustomFilter cf) {
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 ({@link #OP_NONE OP_*})
*
* @param options the bitwise OR of one or more of the transform options
* ({@link #OPT_PERFECT OPT_*})
*
* @param cf an instance of an object that implements the {@link
* TJCustomFilter} interface, or null if no custom filter is needed
*/
@SuppressWarnings("checkstyle:HiddenField")
public TJTransform(Rectangle r, int op, int options,
TJCustomFilter cf) {
super(r);
this.op = op;
this.options = options;
this.cf = cf;
}
/**
* Transform operation (one of {@link #OP_NONE OP_*})
*/
@SuppressWarnings("checkstyle:VisibilityModifier")
public int op = 0;
/**
* Transform options (bitwise OR of one or more of
* {@link #OPT_PERFECT OPT_*})
*/
@SuppressWarnings("checkstyle:VisibilityModifier")
public int options = 0;
/**
* Custom filter instance
*/
@SuppressWarnings("checkstyle:VisibilityModifier")
public TJCustomFilter cf = null;
}

View File

@@ -0,0 +1,167 @@
/*
* Copyright (C)2011, 2013-2015, 2023 D. R. Commander. All Rights Reserved.
* Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the libjpeg-turbo Project nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.libjpegturbo.turbojpeg;
/**
* TurboJPEG lossless transformer
*/
public class TJTransformer extends TJDecompressor {
/**
* Create a TurboJPEG lossless transformer instance.
*/
public TJTransformer() throws TJException {
init();
}
/**
* Create a TurboJPEG lossless transformer instance and associate the JPEG
* source image stored in <code>jpegImage</code> with the newly created
* instance.
*
* @param jpegImage buffer containing the JPEG source image to transform.
* (The size of the JPEG image is assumed to be the length of the array.)
* This buffer is not modified.
*/
public TJTransformer(byte[] jpegImage) throws TJException {
init();
setSourceImage(jpegImage, jpegImage.length);
}
/**
* Create a TurboJPEG lossless transformer instance and associate the JPEG
* source image of length <code>imageSize</code> bytes stored in
* <code>jpegImage</code> with the newly created instance.
*
* @param jpegImage buffer containing the JPEG source image to transform.
* This buffer is not modified.
*
* @param imageSize size of the JPEG source image (in bytes)
*/
public TJTransformer(byte[] jpegImage, int imageSize) throws TJException {
init();
setSourceImage(jpegImage, imageSize);
}
/**
* Losslessly transform the JPEG source 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 JPEG destination 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 and the level of subsampling
* used in the source image.
*
* @param transforms an array of {@link TJTransform} instances, each of
* which specifies the transform parameters and/or cropping region for the
* corresponding transformed JPEG image
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*/
public void transform(byte[][] dstBufs, TJTransform[] transforms,
int flags) throws TJException {
if (jpegBuf == null)
throw new IllegalStateException("JPEG buffer not initialized");
transformedSizes = transform(jpegBuf, jpegBufSize, dstBufs, transforms,
flags);
}
/**
* Losslessly transform the JPEG source 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 JPEG image
*
* @param flags the bitwise OR of one or more of
* {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
*
* @return an array of {@link TJDecompressor} instances, each of
* which has a transformed JPEG image associated with it.
*/
public TJDecompressor[] transform(TJTransform[] transforms, int flags)
throws TJException {
byte[][] dstBufs = new byte[transforms.length][];
if (jpegWidth < 1 || jpegHeight < 1)
throw new IllegalStateException("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
* (in bytes) generated by the most recent transform operation.
*
* @return an array containing the sizes of the transformed JPEG images
* (in bytes) generated by the most recent transform operation.
*/
public int[] getTransformedSizes() {
if (transformedSizes == null)
throw new IllegalStateException("No image has been transformed yet");
return transformedSizes;
}
private native void init() throws TJException;
private native int[] transform(byte[] srcBuf, int srcSize, byte[][] dstBufs,
TJTransform[] transforms, int flags) throws TJException;
static {
TJLoader.load();
}
private int[] transformedSizes = null;
}

View File

@@ -0,0 +1,452 @@
/*
* Copyright (C)2014, 2017, 2023 D. R. Commander. All Rights Reserved.
* Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the libjpeg-turbo Project nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.libjpegturbo.turbojpeg;
/**
* This class encapsulates a planar YUV image and the metadata
* associated with it. The TurboJPEG API allows both the JPEG compression and
* decompression pipelines to be split into stages: YUV encode, compress from
* YUV, decompress to YUV, and YUV decode. A <code>YUVImage</code> instance
* serves as the destination image for YUV encode and decompress-to-YUV
* operations and as the source image for compress-from-YUV and YUV decode
* operations.
* <p>
* Technically, the JPEG format uses the YCbCr colorspace (which is technically
* not a colorspace but a color transform), but per the convention of the
* digital video community, the TurboJPEG API uses "YUV" to refer to an image
* format consisting of Y, Cb, and Cr image planes.
* <p>
* Each plane is simply a 2D array of bytes, each byte representing the value
* of one of the components (Y, Cb, or Cr) at a particular location in the
* image. The width and height of each plane are determined by the image
* width, height, and level of chrominance subsampling. The luminance plane
* width is the image width padded to the nearest multiple of the horizontal
* subsampling factor (1 in the case of 4:4:4, grayscale, or 4:4:0; 2 in the
* case of 4:2:2 or 4:2:0; 4 in the case of 4:1:1.) Similarly, the luminance
* plane height is the image height padded to the nearest multiple of the
* vertical subsampling factor (1 in the case of 4:4:4, 4:2:2, grayscale, or
* 4:1:1; 2 in the case of 4:2:0 or 4:4:0.) This is irrespective of any
* additional padding that may be specified as an argument to the various
* YUVImage methods. The chrominance plane width is equal to the luminance
* plane width divided by the horizontal subsampling factor, and the
* chrominance plane height is equal to the luminance plane height divided by
* the vertical subsampling factor.
* <p>
* For example, if the source image is 35 x 35 pixels and 4:2:2 subsampling is
* used, then the luminance plane would be 36 x 35 bytes, and each of the
* chrominance planes would be 18 x 35 bytes. If you specify a row alignment
* of 4 bytes on top of this, then the luminance plane would be 36 x 35 bytes,
* and each of the chrominance planes would be 20 x 35 bytes.
*/
public class YUVImage {
private static final String NO_ASSOC_ERROR =
"No image data is associated with this instance";
/**
* Create a new <code>YUVImage</code> instance backed by separate image
* planes, and allocate memory for the image planes.
*
* @param width width (in pixels) of the YUV image
*
* @param strides an array of integers, each specifying the number of bytes
* per row in the corresponding plane of the YUV image. Setting the stride
* for any plane to 0 is the same as setting it to the plane width (see
* {@link YUVImage above}.) If <code>strides</code> is null, then the
* strides for all planes will be set to their respective plane widths. When
* using this constructor, the stride for each plane must be equal to or
* greater than the plane width.
*
* @param height height (in pixels) of the YUV image
*
* @param subsamp the level of chrominance subsampling to be used in the YUV
* image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
*/
public YUVImage(int width, int[] strides, int height, int subsamp) {
setBuf(null, null, width, strides, height, subsamp, true);
}
/**
* Create a new <code>YUVImage</code> instance backed by a unified buffer,
* and allocate memory for the buffer.
*
* @param width width (in pixels) of the YUV image
*
* @param align row alignment (in bytes) of the YUV image (must be a power of
* 2.) Setting this parameter to n specifies that each row in each plane of
* the YUV image will be padded to the nearest multiple of n bytes
* (1 = unpadded.)
*
* @param height height (in pixels) of the YUV image
*
* @param subsamp the level of chrominance subsampling to be used in the YUV
* image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
*/
public YUVImage(int width, int align, int height, int subsamp) {
setBuf(new byte[TJ.bufSizeYUV(width, align, height, subsamp)], width,
align, height, subsamp);
}
/**
* Create a new <code>YUVImage</code> instance from a set of existing image
* planes.
*
* @param planes an array of buffers representing the Y, U (Cb), and V (Cr)
* image planes (or just the Y plane, if the image is grayscale.) These
* planes can be contiguous or non-contiguous in memory. Plane
* <code>i</code> should be at least <code>offsets[i] + </code>
* {@link TJ#planeSizeYUV TJ.planeSizeYUV}<code>(i, width, strides[i], height, subsamp)</code>
* bytes in size.
*
* @param offsets If this <code>YUVImage</code> instance represents a
* subregion of a larger image, then <code>offsets[i]</code> specifies the
* offset (in bytes) of the subregion within plane <code>i</code> of the
* larger image. Setting this to null is the same as setting the offsets for
* all planes to 0.
*
* @param width width (in pixels) of the new YUV image (or subregion)
*
* @param strides an array of integers, each specifying the number of bytes
* per row in the corresponding plane of the YUV image. Setting the stride
* for any plane to 0 is the same as setting it to the plane width (see
* {@link YUVImage above}.) If <code>strides</code> is null, then the
* strides for all planes will be set to their respective plane widths. You
* can adjust the strides in order to add an arbitrary amount of row padding
* to each plane or to specify that this <code>YUVImage</code> instance is a
* subregion of a larger image (in which case, <code>strides[i]</code> should
* be set to the plane width of plane <code>i</code> in the larger image.)
*
* @param height height (in pixels) of the new YUV image (or subregion)
*
* @param subsamp the level of chrominance subsampling used in the YUV
* image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
*/
public YUVImage(byte[][] planes, int[] offsets, int width, int[] strides,
int height, int subsamp) {
setBuf(planes, offsets, width, strides, height, subsamp, false);
}
/**
* Create a new <code>YUVImage</code> instance from an existing unified
* buffer.
*
* @param yuvImage buffer that contains or will receive a unified planar YUV
* image. Use {@link TJ#bufSizeYUV} to determine the minimum size for this
* buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in
* the buffer. (See {@link YUVImage above} for a description of the image
* format.)
*
* @param width width (in pixels) of the YUV image
*
* @param align row alignment (in bytes) of the YUV image (must be a power of
* 2.) Setting this parameter to n specifies that each row in each plane of
* the YUV image will be padded to the nearest multiple of n bytes
* (1 = unpadded.)
*
* @param height height (in pixels) of the YUV image
*
* @param subsamp the level of chrominance subsampling used in the YUV
* image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
*/
public YUVImage(byte[] yuvImage, int width, int align, int height,
int subsamp) {
setBuf(yuvImage, width, align, height, subsamp);
}
/**
* Assign a set of image planes to this <code>YUVImage</code> instance.
*
* @param planes an array of buffers representing the Y, U (Cb), and V (Cr)
* image planes (or just the Y plane, if the image is grayscale.) These
* planes can be contiguous or non-contiguous in memory. Plane
* <code>i</code> should be at least <code>offsets[i] + </code>
* {@link TJ#planeSizeYUV TJ.planeSizeYUV}<code>(i, width, strides[i], height, subsamp)</code>
* bytes in size.
*
* @param offsets If this <code>YUVImage</code> instance represents a
* subregion of a larger image, then <code>offsets[i]</code> specifies the
* offset (in bytes) of the subregion within plane <code>i</code> of the
* larger image. Setting this to null is the same as setting the offsets for
* all planes to 0.
*
* @param width width (in pixels) of the YUV image (or subregion)
*
* @param strides an array of integers, each specifying the number of bytes
* per row in the corresponding plane of the YUV image. Setting the stride
* for any plane to 0 is the same as setting it to the plane width (see
* {@link YUVImage above}.) If <code>strides</code> is null, then the
* strides for all planes will be set to their respective plane widths. You
* can adjust the strides in order to add an arbitrary amount of row padding
* to each plane or to specify that this <code>YUVImage</code> instance is a
* subregion of a larger image (in which case, <code>strides[i]</code> should
* be set to the plane width of plane <code>i</code> in the larger image.)
*
* @param height height (in pixels) of the YUV image (or subregion)
*
* @param subsamp the level of chrominance subsampling used in the YUV
* image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
*/
public void setBuf(byte[][] planes, int[] offsets, int width, int[] strides,
int height, int subsamp) {
setBuf(planes, offsets, width, strides, height, subsamp, false);
}
private void setBuf(byte[][] planes, int[] offsets, int width, int[] strides,
int height, int subsamp, boolean alloc) {
if ((planes == null && !alloc) || width < 1 || height < 1 || subsamp < 0 ||
subsamp >= TJ.NUMSAMP)
throw new IllegalArgumentException("Invalid argument in YUVImage::setBuf()");
int nc = (subsamp == TJ.SAMP_GRAY ? 1 : 3);
if ((planes != null && planes.length != nc) ||
(offsets != null && offsets.length != nc) ||
(strides != null && strides.length != nc))
throw new IllegalArgumentException("YUVImage::setBuf(): planes, offsets, or strides array is the wrong size");
if (planes == null)
planes = new byte[nc][];
if (offsets == null)
offsets = new int[nc];
if (strides == null)
strides = new int[nc];
for (int i = 0; i < nc; i++) {
int pw = TJ.planeWidth(i, width, subsamp);
int ph = TJ.planeHeight(i, height, subsamp);
int planeSize = TJ.planeSizeYUV(i, width, strides[i], height, subsamp);
if (strides[i] == 0)
strides[i] = pw;
if (alloc) {
if (strides[i] < pw)
throw new IllegalArgumentException("Stride must be >= plane width when allocating a new YUV image");
planes[i] = new byte[strides[i] * ph];
}
if (planes[i] == null || offsets[i] < 0)
throw new IllegalArgumentException("Invalid argument in YUVImage::setBuf()");
if (strides[i] < 0 && offsets[i] - planeSize + pw < 0)
throw new IllegalArgumentException("Stride for plane " + i +
" would cause memory to be accessed below plane boundary");
if (planes[i].length < offsets[i] + planeSize)
throw new IllegalArgumentException("Image plane " + i +
" is not large enough");
}
yuvPlanes = planes;
yuvOffsets = offsets;
yuvWidth = width;
yuvStrides = strides;
yuvHeight = height;
yuvSubsamp = subsamp;
}
/**
* Assign a unified buffer to this <code>YUVImage</code> instance.
*
* @param yuvImage buffer that contains or will receive a unified planar YUV
* image. Use {@link TJ#bufSizeYUV} to determine the minimum size for this
* buffer. The Y, U (Cb), and V (Cr) image planes are stored sequentially in
* the buffer. (See {@link YUVImage above} for a description of the image
* format.)
*
* @param width width (in pixels) of the YUV image
*
* @param align row alignment (in bytes) of the YUV image (must be a power of
* 2.) Setting this parameter to n specifies that each row in each plane of
* the YUV image will be padded to the nearest multiple of n bytes
* (1 = unpadded.)
*
* @param height height (in pixels) of the YUV image
*
* @param subsamp the level of chrominance subsampling used in the YUV
* image (one of {@link TJ#SAMP_444 TJ.SAMP_*})
*/
public void setBuf(byte[] yuvImage, int width, int align, int height,
int subsamp) {
if (yuvImage == null || width < 1 || align < 1 ||
((align & (align - 1)) != 0) || height < 1 || subsamp < 0 ||
subsamp >= TJ.NUMSAMP)
throw new IllegalArgumentException("Invalid argument in YUVImage::setBuf()");
if (yuvImage.length < TJ.bufSizeYUV(width, align, height, subsamp))
throw new IllegalArgumentException("YUV buffer is not large enough");
int nc = (subsamp == TJ.SAMP_GRAY ? 1 : 3);
byte[][] planes = new byte[nc][];
int[] strides = new int[nc];
int[] offsets = new int[nc];
planes[0] = yuvImage;
strides[0] = pad(TJ.planeWidth(0, width, subsamp), align);
if (subsamp != TJ.SAMP_GRAY) {
strides[1] = strides[2] = pad(TJ.planeWidth(1, width, subsamp), align);
planes[1] = planes[2] = yuvImage;
offsets[1] = offsets[0] +
strides[0] * TJ.planeHeight(0, height, subsamp);
offsets[2] = offsets[1] +
strides[1] * TJ.planeHeight(1, height, subsamp);
}
yuvAlign = align;
setBuf(planes, offsets, width, strides, height, subsamp);
}
/**
* Returns the width of the YUV image (or subregion.)
*
* @return the width of the YUV image (or subregion)
*/
public int getWidth() {
if (yuvWidth < 1)
throw new IllegalStateException(NO_ASSOC_ERROR);
return yuvWidth;
}
/**
* Returns the height of the YUV image (or subregion.)
*
* @return the height of the YUV image (or subregion)
*/
public int getHeight() {
if (yuvHeight < 1)
throw new IllegalStateException(NO_ASSOC_ERROR);
return yuvHeight;
}
/**
* Returns the row alignment (in bytes) of the YUV buffer (if this image is
* stored in a unified buffer rather than separate image planes.)
*
* @return the row alignment of the YUV buffer
*/
public int getPad() {
if (yuvPlanes == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
if (yuvAlign < 1 || ((yuvAlign & (yuvAlign - 1)) != 0))
throw new IllegalStateException("Image is not stored in a unified buffer");
return yuvAlign;
}
/**
* Returns the number of bytes per row of each plane in the YUV image.
*
* @return the number of bytes per row of each plane in the YUV image
*/
public int[] getStrides() {
if (yuvStrides == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
return yuvStrides;
}
/**
* Returns the offsets (in bytes) of each plane within the planes of a larger
* YUV image.
*
* @return the offsets (in bytes) of each plane within the planes of a larger
* YUV image
*/
public int[] getOffsets() {
if (yuvOffsets == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
return yuvOffsets;
}
/**
* Returns the level of chrominance subsampling used in the YUV image. See
* {@link TJ#SAMP_444 TJ.SAMP_*}.
*
* @return the level of chrominance subsampling used in the YUV image
*/
public int getSubsamp() {
if (yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP)
throw new IllegalStateException(NO_ASSOC_ERROR);
return yuvSubsamp;
}
/**
* Returns the YUV image planes. If the image is stored in a unified buffer,
* then all image planes will point to that buffer.
*
* @return the YUV image planes
*/
public byte[][] getPlanes() {
if (yuvPlanes == null)
throw new IllegalStateException(NO_ASSOC_ERROR);
return yuvPlanes;
}
/**
* Returns the YUV buffer (if this image is stored in a unified buffer rather
* than separate image planes.)
*
* @return the YUV buffer
*/
public byte[] getBuf() {
if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP)
throw new IllegalStateException(NO_ASSOC_ERROR);
int nc = (yuvSubsamp == TJ.SAMP_GRAY ? 1 : 3);
for (int i = 1; i < nc; i++) {
if (yuvPlanes[i] != yuvPlanes[0])
throw new IllegalStateException("Image is not stored in a unified buffer");
}
return yuvPlanes[0];
}
/**
* Returns the size (in bytes) of the YUV buffer (if this image is stored in
* a unified buffer rather than separate image planes.)
*
* @return the size (in bytes) of the YUV buffer
*/
public int getSize() {
if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP)
throw new IllegalStateException(NO_ASSOC_ERROR);
int nc = (yuvSubsamp == TJ.SAMP_GRAY ? 1 : 3);
if (yuvAlign < 1)
throw new IllegalStateException("Image is not stored in a unified buffer");
for (int i = 1; i < nc; i++) {
if (yuvPlanes[i] != yuvPlanes[0])
throw new IllegalStateException("Image is not stored in a unified buffer");
}
return TJ.bufSizeYUV(yuvWidth, yuvAlign, yuvHeight, yuvSubsamp);
}
private static int pad(int v, int p) {
return (v + p - 1) & (~(p - 1));
}
protected long handle = 0;
protected byte[][] yuvPlanes = null;
protected int[] yuvOffsets = null;
protected int[] yuvStrides = null;
protected int yuvAlign = 1;
protected int yuvWidth = 0;
protected int yuvHeight = 0;
protected int yuvSubsamp = -1;
}