- When transforming, the worst-case JPEG buffer size depends on the subsampling level used in the destination image, since a grayscale transform might have been applied. - Parentheses Police
1181 lines
50 KiB
Java
1181 lines
50 KiB
Java
/*
|
|
* Copyright (C)2011-2015, 2018, 2022-2024 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.Rectangle;
|
|
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. Refer
|
|
* to {@link #setSourceImage(byte[], int)} for more details.
|
|
*
|
|
* @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. Refer to
|
|
* {@link #setSourceImage(byte[], int)} for more details.
|
|
*
|
|
* @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
|
|
* 8-bit-per-sample planar YUV source image stored in <code>yuvImage</code>
|
|
* with the newly created instance. Refer to
|
|
* {@link #setSourceImage(YUVImage)} for more details.
|
|
*
|
|
* @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. If a JPEG image is passed to this
|
|
* method, then the {@link TJ#PARAM_STOPONWARNING parameters} that describe
|
|
* the JPEG image will be set when the method returns.
|
|
*
|
|
* @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;
|
|
}
|
|
|
|
/**
|
|
* 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. This method sets
|
|
* {@link TJ#PARAM_SUBSAMP} to the chrominance subsampling level of the
|
|
* source 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;
|
|
set(TJ.PARAM_SUBSAMP, srcImage.getSubsamp());
|
|
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();
|
|
return getJPEGWidth();
|
|
}
|
|
|
|
private int getJPEGWidth() {
|
|
int jpegWidth = get(TJ.PARAM_JPEGWIDTH);
|
|
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();
|
|
return getJPEGHeight();
|
|
}
|
|
|
|
private int getJPEGHeight() {
|
|
int jpegHeight = get(TJ.PARAM_JPEGHEIGHT);
|
|
if (jpegHeight < 1)
|
|
throw new IllegalStateException(NO_ASSOC_ERROR);
|
|
return jpegHeight;
|
|
}
|
|
|
|
/**
|
|
* Set the value of a decompression parameter.
|
|
*
|
|
* @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*}
|
|
*
|
|
* @param value value of the decompression parameter (refer to
|
|
* {@link TJ#PARAM_STOPONWARNING parameter documentation})
|
|
*/
|
|
public native void set(int param, int value);
|
|
|
|
/**
|
|
* Get the value of a decompression parameter.
|
|
*
|
|
* @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*}
|
|
*
|
|
* @return the value of the specified decompression parameter, or -1 if the
|
|
* value is unknown.
|
|
*/
|
|
public native int get(int param);
|
|
|
|
/**
|
|
* Set the scaling factor for subsequent lossy decompression operations.
|
|
*
|
|
* @param scalingFactor {@link TJScalingFactor} instance that specifies a
|
|
* fractional scaling factor that the decompressor supports (see
|
|
* {@link TJ#getScalingFactors}), or {@link TJ#UNSCALED} for no scaling.
|
|
* Decompression scaling is a function of the IDCT algorithm, so scaling
|
|
* factors are generally limited to multiples of 1/8. If the entire JPEG
|
|
* image will be decompressed, then the width and height of the scaled
|
|
* destination image can be determined by calling
|
|
* <code>scalingFactor.</code>{@link TJScalingFactor#getScaled getScaled()}
|
|
* with the JPEG image width and height (see {@link #getWidth} and
|
|
* {@link #getHeight}.) When decompressing into a planar YUV image, an
|
|
* intermediate buffer copy will be performed if the width or height of the
|
|
* scaled destination image is not an even multiple of the iMCU size (see
|
|
* {@link TJ#getMCUWidth TJ.getMCUWidth()} and {@link TJ#getMCUHeight
|
|
* TJ.getMCUHeight()}.) Note that decompression scaling is not available
|
|
* (and the specified scaling factor is ignored) when decompressing lossless
|
|
* JPEG images (see {@link TJ#PARAM_LOSSLESS}), since the IDCT algorithm is
|
|
* not used with those images. Note also that {@link TJ#PARAM_FASTDCT} is
|
|
* ignored when decompression scaling is enabled.
|
|
*/
|
|
@SuppressWarnings("checkstyle:HiddenField")
|
|
public void setScalingFactor(TJScalingFactor scalingFactor) {
|
|
if (scalingFactor == null)
|
|
throw new IllegalArgumentException("Invalid argument in setScalingFactor()");
|
|
|
|
TJScalingFactor[] sf = TJ.getScalingFactors();
|
|
int i;
|
|
for (i = 0; i < sf.length; i++) {
|
|
if (scalingFactor.getNum() == sf[i].getNum() &&
|
|
scalingFactor.getDenom() == sf[i].getDenom())
|
|
break;
|
|
}
|
|
if (i >= sf.length)
|
|
throw new IllegalArgumentException("Unsupported scaling factor");
|
|
|
|
this.scalingFactor = scalingFactor;
|
|
}
|
|
|
|
/**
|
|
* Set the cropping region for partially decompressing a lossy JPEG image
|
|
* into a packed-pixel image.
|
|
*
|
|
* @param croppingRegion <code>java.awt.Rectangle</code> instance that
|
|
* specifies a subregion of the JPEG image to decompress, or
|
|
* {@link TJ#UNCROPPED} for no cropping. The left boundary of the cropping
|
|
* region must be evenly divisible by the scaled iMCU width, which can be
|
|
* determined by calling {@link TJScalingFactor#getScaled
|
|
* TJScalingFactor.getScaled()} with the specified scaling factor (see
|
|
* {@link #setScalingFactor setScalingFactor()}) and the iMCU width (see
|
|
* {@link TJ#getMCUWidth TJ.getMCUWidth()}) for the level of chrominance
|
|
* subsampling in the JPEG image (see {@link TJ#PARAM_SUBSAMP}.) The
|
|
* cropping region should be specified relative to the scaled image
|
|
* dimensions. Unless <code>croppingRegion</code> is {@link TJ#UNCROPPED},
|
|
* the JPEG header must be read (see {@link #setSourceImage(byte[], int)})
|
|
* prior to calling this method.
|
|
*/
|
|
@SuppressWarnings("checkstyle:HiddenField")
|
|
public void setCroppingRegion(Rectangle croppingRegion) throws TJException {
|
|
this.croppingRegion = croppingRegion;
|
|
setCroppingRegion();
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use <code>{@link #get get}({@link TJ#PARAM_SUBSAMP})</code>
|
|
* instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public int getSubsamp() {
|
|
int subsamp = get(TJ.PARAM_SUBSAMP);
|
|
if (subsamp == TJ.SAMP_UNKNOWN)
|
|
throw new IllegalStateException(NO_ASSOC_ERROR);
|
|
if (subsamp >= TJ.NUMSAMP)
|
|
throw new IllegalStateException("JPEG header information is invalid");
|
|
return subsamp;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use <code>{@link #get get}({@link TJ#PARAM_COLORSPACE})</code>
|
|
* instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public int getColorspace() {
|
|
if (yuvImage != null)
|
|
return TJ.CS_YCbCr;
|
|
int jpegColorspace = get(TJ.PARAM_COLORSPACE);
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #setScalingFactor setScalingFactor()} and
|
|
* {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public int getScaledWidth(int desiredWidth, int desiredHeight) {
|
|
TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight);
|
|
return sf.getScaled(getJPEGWidth());
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #setScalingFactor setScalingFactor()} and
|
|
* {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public int getScaledHeight(int desiredWidth, int desiredHeight) {
|
|
TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight);
|
|
return sf.getScaled(getJPEGHeight());
|
|
}
|
|
|
|
private TJScalingFactor getScalingFactor(int desiredWidth,
|
|
int desiredHeight) {
|
|
int jpegWidth = getJPEGWidth();
|
|
int jpegHeight = getJPEGHeight();
|
|
if (desiredWidth < 0 || desiredHeight < 0)
|
|
throw new IllegalArgumentException("Invalid argument");
|
|
|
|
TJScalingFactor[] sf = TJ.getScalingFactors();
|
|
|
|
if (desiredWidth == 0)
|
|
desiredWidth = jpegWidth;
|
|
if (desiredHeight == 0)
|
|
desiredHeight = jpegHeight;
|
|
int i;
|
|
for (i = 0; i < sf.length; i++) {
|
|
if (sf[i].getScaled(jpegWidth) <= desiredWidth &&
|
|
sf[i].getScaled(jpegHeight) <= desiredHeight)
|
|
break;
|
|
}
|
|
if (i >= sf.length)
|
|
throw new IllegalArgumentException("Could not scale down to desired image dimensions");
|
|
|
|
return sf[i];
|
|
}
|
|
|
|
/**
|
|
* Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV
|
|
* source image associated with this decompressor instance and output an
|
|
* 8-bit-per-sample 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#PARAM_STOPONWARNING} is
|
|
* set.)
|
|
*
|
|
* @param dstBuf buffer that will receive the packed-pixel
|
|
* decompressed/decoded image. This buffer should normally be
|
|
* <code>pitch * destinationHeight</code> bytes in size. However, the buffer
|
|
* may also be larger, 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. NOTE: If the source
|
|
* image is a lossy JPEG image, then <code>destinationHeight</code> is either
|
|
* the scaled JPEG height (see {@link #setScalingFactor setScalingFactor()},
|
|
* {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and
|
|
* {@link #getHeight}) or the height of the cropping region (see
|
|
* {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a
|
|
* YUV image or a lossless JPEG image, then <code>destinationHeight</code> is
|
|
* the height of the source image.
|
|
*
|
|
* @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 pitch bytes per row in the destination image. Normally this should
|
|
* be set to <code>destinationWidth *
|
|
* {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>, if the
|
|
* destination image will be unpadded. (Setting this parameter to 0 is the
|
|
* equivalent of setting it to <code>destinationWidth *
|
|
* {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>.) However,
|
|
* you can also use this parameter to specify the row alignment/padding of
|
|
* the destination image, to skip rows, or to decompress/decode into a
|
|
* specific region of a larger image. NOTE: If the source image is a lossy
|
|
* JPEG image, then <code>destinationWidth</code> is either the scaled JPEG
|
|
* width (see {@link #setScalingFactor setScalingFactor()},
|
|
* {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and
|
|
* {@link #getWidth}) or the width of the cropping region (see
|
|
* {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a
|
|
* YUV image or a lossless JPEG image, then <code>destinationWidth</code> is
|
|
* the width of the source image.
|
|
*
|
|
* @param pixelFormat pixel format of the decompressed/decoded image (one of
|
|
* {@link TJ#PF_RGB TJ.PF_*})
|
|
*/
|
|
public void decompress8(byte[] dstBuf, int x, int y, int pitch,
|
|
int pixelFormat) 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 || pixelFormat < 0 ||
|
|
pixelFormat >= TJ.NUMPF)
|
|
throw new IllegalArgumentException("Invalid argument in decompress8()");
|
|
if (yuvImage != null) {
|
|
checkSubsampling();
|
|
decodeYUV8(yuvImage.getPlanes(), yuvImage.getOffsets(),
|
|
yuvImage.getStrides(), dstBuf, x, y, yuvImage.getWidth(),
|
|
pitch, yuvImage.getHeight(), pixelFormat);
|
|
} else
|
|
decompress8(jpegBuf, jpegBufSize, dstBuf, x, y, pitch, pixelFormat);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #set set()},
|
|
* {@link #setScalingFactor setScalingFactor()}, and
|
|
* {@link #decompress8(byte[], int, int, int, int)} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public void decompress(byte[] dstBuf, int x, int y, int desiredWidth,
|
|
int pitch, int desiredHeight, int pixelFormat,
|
|
int flags) throws TJException {
|
|
if ((yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
|
|
flags < 0)
|
|
throw new IllegalArgumentException("Invalid argument in decompress()");
|
|
|
|
if (yuvImage == null) {
|
|
TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight);
|
|
setScalingFactor(sf);
|
|
}
|
|
processFlags(flags);
|
|
decompress8(dstBuf, x, y, pitch, pixelFormat);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV
|
|
* source image associated with this decompressor instance and return a
|
|
* buffer containing an 8-bit-per-sample packed-pixel decompressed image.
|
|
*
|
|
* @param pitch see
|
|
* {@link #decompress8(byte[], int, int, int, int)} for description
|
|
*
|
|
* @param pixelFormat pixel format of the decompressed image (one of
|
|
* {@link TJ#PF_RGB TJ.PF_*})
|
|
*
|
|
* @return a buffer containing an 8-bit-per-sample packed-pixel decompressed
|
|
* image.
|
|
*/
|
|
public byte[] decompress8(int pitch, int pixelFormat) throws TJException {
|
|
if (pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
|
|
throw new IllegalArgumentException("Invalid argument in decompress8()");
|
|
int pixelSize = TJ.getPixelSize(pixelFormat);
|
|
int scaledWidth = scalingFactor.getScaled(getJPEGWidth());
|
|
int scaledHeight = scalingFactor.getScaled(getJPEGHeight());
|
|
if (pitch == 0)
|
|
pitch = scaledWidth * pixelSize;
|
|
byte[] buf = new byte[pitch * scaledHeight];
|
|
decompress8(buf, 0, 0, pitch, pixelFormat);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #set set()},
|
|
* {@link #setScalingFactor setScalingFactor()}, and
|
|
* {@link #decompress8(int, int)} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public byte[] decompress(int desiredWidth, int pitch, int desiredHeight,
|
|
int pixelFormat, int flags) throws TJException {
|
|
if ((yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
|
|
flags < 0)
|
|
throw new IllegalArgumentException("Invalid argument in decompress()");
|
|
|
|
if (yuvImage == null) {
|
|
TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight);
|
|
setScalingFactor(sf);
|
|
}
|
|
processFlags(flags);
|
|
return decompress8(pitch, pixelFormat);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 12-bit-per-sample JPEG source image associated with this
|
|
* decompressor instance and output a 12-bit-per-sample 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#PARAM_STOPONWARNING} is
|
|
* set.)
|
|
*
|
|
* @param dstBuf buffer that will receive the packed-pixel
|
|
* decompressed image. This buffer should normally be
|
|
* <code>pitch * destinationHeight</code> samples in size. However, the
|
|
* buffer may also be larger, 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. NOTE: If
|
|
* the source image is a lossy JPEG image, then
|
|
* <code>destinationHeight</code> is either the scaled JPEG height (see
|
|
* {@link #setScalingFactor setScalingFactor()},
|
|
* {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and
|
|
* {@link #getHeight}) or the height of the cropping region (see
|
|
* {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a
|
|
* lossless JPEG image, then <code>destinationHeight</code> is the height of
|
|
* the source image.
|
|
*
|
|
* @param x x offset (in pixels) of the region in the destination image into
|
|
* which the source image should be decompressed
|
|
*
|
|
* @param y y offset (in pixels) of the region in the destination image into
|
|
* which the source image should be decompressed
|
|
*
|
|
* @param pitch samples per row in the destination image. Normally this
|
|
* should be set to <code>destinationWidth *
|
|
* {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>, if the
|
|
* destination image will be unpadded. (Setting this parameter to 0 is the
|
|
* equivalent of setting it to <code>destinationWidth *
|
|
* {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>.) However,
|
|
* you can also use this parameter to specify the row alignment/padding of
|
|
* the destination image, to skip rows, or to decompress into a specific
|
|
* region of a larger image. NOTE: If the source image is a lossy JPEG
|
|
* image, then <code>destinationWidth</code> is either the scaled JPEG width
|
|
* (see {@link #setScalingFactor setScalingFactor()},
|
|
* {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and
|
|
* {@link #getWidth}) or the width of the cropping region (see
|
|
* {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a
|
|
* YUV image or a lossless JPEG image, then <code>destinationWidth</code> is
|
|
* the width of the source image.
|
|
*
|
|
* @param pixelFormat pixel format of the decompressed image (one of
|
|
* {@link TJ#PF_RGB TJ.PF_*})
|
|
*/
|
|
public void decompress12(short[] dstBuf, int x, int y, int pitch,
|
|
int pixelFormat) throws TJException {
|
|
if (jpegBuf == null)
|
|
throw new IllegalStateException(NO_ASSOC_ERROR);
|
|
if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || pixelFormat < 0 ||
|
|
pixelFormat >= TJ.NUMPF)
|
|
throw new IllegalArgumentException("Invalid argument in decompress12()");
|
|
decompress12(jpegBuf, jpegBufSize, dstBuf, x, y, pitch, pixelFormat);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 12-bit-per-sample JPEG source image associated with this
|
|
* decompressor instance and return a buffer containing a 12-bit-per-sample
|
|
* packed-pixel decompressed image.
|
|
*
|
|
* @param pitch see
|
|
* {@link #decompress12(short[], int, int, int, int)} for description
|
|
*
|
|
* @param pixelFormat pixel format of the decompressed image (one of
|
|
* {@link TJ#PF_RGB TJ.PF_*})
|
|
*
|
|
* @return a buffer containing a 12-bit-per-sample packed-pixel decompressed
|
|
* image.
|
|
*/
|
|
public short[] decompress12(int pitch, int pixelFormat) throws TJException {
|
|
if (pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
|
|
throw new IllegalArgumentException("Invalid argument in decompress12()");
|
|
int pixelSize = TJ.getPixelSize(pixelFormat);
|
|
int scaledWidth = scalingFactor.getScaled(getJPEGWidth());
|
|
int scaledHeight = scalingFactor.getScaled(getJPEGHeight());
|
|
if (pitch == 0)
|
|
pitch = scaledWidth * pixelSize;
|
|
short[] buf = new short[pitch * scaledHeight];
|
|
decompress12(buf, 0, 0, pitch, pixelFormat);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Decompress the 16-bit-per-sample lossless JPEG source image associated
|
|
* with this decompressor instance and output a 16-bit-per-sample
|
|
* 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#PARAM_STOPONWARNING} is
|
|
* set.)
|
|
*
|
|
* @param dstBuf buffer that will receive the packed-pixel
|
|
* decompressed image. This buffer should normally be
|
|
* <code>pitch * jpegHeight</code> samples in size. However, the buffer may
|
|
* also be larger, 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.
|
|
*
|
|
* @param x x offset (in pixels) of the region in the destination image into
|
|
* which the source image should be decompressed
|
|
*
|
|
* @param y y offset (in pixels) of the region in the destination image into
|
|
* which the source image should be decompressed
|
|
*
|
|
* @param pitch samples per row in the destination image. Normally this
|
|
* should be set to <code>jpegWidth *
|
|
* {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>, if the
|
|
* destination image will be unpadded. (Setting this parameter to 0 is the
|
|
* equivalent of setting it to <code>jpegWidth *
|
|
* {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>.) However,
|
|
* you can also use this parameter to specify the row alignment/padding of
|
|
* the destination image, to skip rows, or to decompress into a specific
|
|
* region of a larger image.
|
|
*
|
|
* @param pixelFormat pixel format of the decompressed image (one of
|
|
* {@link TJ#PF_RGB TJ.PF_*})
|
|
*/
|
|
public void decompress16(short[] dstBuf, int x, int y, int pitch,
|
|
int pixelFormat) throws TJException {
|
|
if (jpegBuf == null)
|
|
throw new IllegalStateException(NO_ASSOC_ERROR);
|
|
if (dstBuf == null || x < 0 || y < 0 || pitch < 0 || pixelFormat < 0 ||
|
|
pixelFormat >= TJ.NUMPF)
|
|
throw new IllegalArgumentException("Invalid argument in decompress16()");
|
|
decompress16(jpegBuf, jpegBufSize, dstBuf, x, y, pitch, pixelFormat);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 16-bit-per-sample JPEG source image associated with this
|
|
* decompressor instance and return a buffer containing a 16-bit-per-sample
|
|
* packed-pixel decompressed image.
|
|
*
|
|
* @param pitch see
|
|
* {@link #decompress16(short[], int, int, int, int)} for description
|
|
*
|
|
* @param pixelFormat pixel format of the decompressed image (one of
|
|
* {@link TJ#PF_RGB TJ.PF_*})
|
|
*
|
|
* @return a buffer containing a 16-bit-per-sample packed-pixel decompressed
|
|
* image.
|
|
*/
|
|
public short[] decompress16(int pitch, int pixelFormat) throws TJException {
|
|
if (pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
|
|
throw new IllegalArgumentException("Invalid argument in decompress16()");
|
|
int pixelSize = TJ.getPixelSize(pixelFormat);
|
|
int scaledWidth = scalingFactor.getScaled(getJPEGWidth());
|
|
int scaledHeight = scalingFactor.getScaled(getJPEGHeight());
|
|
if (pitch == 0)
|
|
pitch = scaledWidth * pixelSize;
|
|
short[] buf = new short[pitch * scaledHeight];
|
|
decompress16(buf, 0, 0, pitch, pixelFormat);
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Decompress the 8-bit-per-sample JPEG source image associated with this
|
|
* decompressor instance into an 8-bit-per-sample 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#PARAM_STOPONWARNING} is set.)
|
|
*
|
|
* @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 the
|
|
* scaled JPEG width and height (see {@link #setScalingFactor
|
|
* setScalingFactor()}, {@link TJScalingFactor#getScaled
|
|
* TJScalingFactor.getScaled()}, {@link #getWidth}, and {@link #getHeight}.)
|
|
*/
|
|
public void decompressToYUV(YUVImage dstImage) throws TJException {
|
|
if (jpegBuf == null)
|
|
throw new IllegalStateException(NO_ASSOC_ERROR);
|
|
if (dstImage == null)
|
|
throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
|
|
checkSubsampling();
|
|
if (get(TJ.PARAM_SUBSAMP) != dstImage.getSubsamp())
|
|
throw new IllegalArgumentException("YUVImage subsampling level does not match that of the JPEG image");
|
|
if (scalingFactor.getScaled(getJPEGWidth()) != dstImage.getWidth() ||
|
|
scalingFactor.getScaled(getJPEGHeight()) != dstImage.getHeight())
|
|
throw new IllegalArgumentException("YUVImage dimensions do not match the scaled JPEG dimensions");
|
|
|
|
decompressToYUV8(jpegBuf, jpegBufSize, dstImage.getPlanes(),
|
|
dstImage.getOffsets(), dstImage.getStrides());
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #set set()}, {@link #setScalingFactor
|
|
* setScalingFactor()}, and {@link #decompressToYUV(YUVImage)} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public void decompressToYUV(YUVImage dstImage, int flags)
|
|
throws TJException {
|
|
if (flags < 0)
|
|
throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
|
|
|
|
TJScalingFactor sf = getScalingFactor(dstImage.getWidth(),
|
|
dstImage.getHeight());
|
|
if (sf.getScaled(getJPEGWidth()) != dstImage.getWidth() ||
|
|
sf.getScaled(getJPEGHeight()) != dstImage.getHeight())
|
|
throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that the decompressor is capable of generating.");
|
|
|
|
setScalingFactor(sf);
|
|
processFlags(flags);
|
|
decompressToYUV(dstImage);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 8-bit-per-sample JPEG source image associated with this
|
|
* decompressor instance into a set of 8-bit-per-sample 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 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.
|
|
*
|
|
* @return a {@link YUVImage} instance containing the decompressed image
|
|
* planes
|
|
*/
|
|
public YUVImage decompressToYUV(int[] strides) throws TJException {
|
|
int jpegWidth = getJPEGWidth();
|
|
int jpegHeight = getJPEGHeight();
|
|
checkSubsampling();
|
|
if (yuvImage != null)
|
|
throw new IllegalStateException("Source image is the wrong type");
|
|
|
|
YUVImage dstYUVImage = new YUVImage(scalingFactor.getScaled(jpegWidth),
|
|
null,
|
|
scalingFactor.getScaled(jpegHeight),
|
|
get(TJ.PARAM_SUBSAMP));
|
|
decompressToYUV(dstYUVImage);
|
|
return dstYUVImage;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #set set()}, {@link #setScalingFactor
|
|
* setScalingFactor()}, and {@link #decompressToYUV(int[])} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public YUVImage decompressToYUV(int desiredWidth, int[] strides,
|
|
int desiredHeight,
|
|
int flags) throws TJException {
|
|
if (flags < 0)
|
|
throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
|
|
|
|
TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight);
|
|
setScalingFactor(sf);
|
|
processFlags(flags);
|
|
return decompressToYUV(strides);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 8-bit-per-sample JPEG source image associated with this
|
|
* decompressor instance into an 8-bit-per-sample 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 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.)
|
|
*
|
|
* @return a {@link YUVImage} instance containing the unified planar YUV
|
|
* decompressed image
|
|
*/
|
|
public YUVImage decompressToYUV(int align) throws TJException {
|
|
int jpegWidth = getJPEGWidth();
|
|
int jpegHeight = getJPEGHeight();
|
|
checkSubsampling();
|
|
if (yuvImage != null)
|
|
throw new IllegalStateException("Source image is the wrong type");
|
|
|
|
YUVImage dstYUVImage = new YUVImage(scalingFactor.getScaled(jpegWidth),
|
|
align,
|
|
scalingFactor.getScaled(jpegHeight),
|
|
get(TJ.PARAM_SUBSAMP));
|
|
decompressToYUV(dstYUVImage);
|
|
return dstYUVImage;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #set set()}, {@link #setScalingFactor
|
|
* setScalingFactor()}, and {@link #decompressToYUV(int)} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public YUVImage decompressToYUV(int desiredWidth, int align,
|
|
int desiredHeight, int flags)
|
|
throws TJException {
|
|
if (flags < 0)
|
|
throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
|
|
|
|
TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight);
|
|
setScalingFactor(sf);
|
|
processFlags(flags);
|
|
return decompressToYUV(align);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV
|
|
* source image associated with this decompressor instance and output an
|
|
* 8-bit-per-sample 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#PARAM_STOPONWARNING}
|
|
* is set.)
|
|
*
|
|
* @param dstBuf buffer that will receive the packed-pixel
|
|
* decompressed/decoded image. This buffer should normally be
|
|
* <code>stride * destinationHeight</code> pixels in size. However, the
|
|
* buffer may also be larger, 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.
|
|
* NOTE: If the source image is a lossy JPEG image, then
|
|
* <code>destinationHeight</code> is either the scaled JPEG height (see
|
|
* {@link #setScalingFactor setScalingFactor()},
|
|
* {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()}, and
|
|
* {@link #getHeight}) or the height of the cropping region (see
|
|
* {@link #setCroppingRegion setCroppingRegion()}.) If the source image is a
|
|
* YUV image or a lossless JPEG image, then <code>destinationHeight</code> is
|
|
* the height of the source image.
|
|
*
|
|
* @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 stride pixels per row in the destination image. Normally this
|
|
* should be set to <code>destinationWidth</code>. (Setting this parameter
|
|
* to 0 is the equivalent of setting it to <code>destinationWidth</code>.)
|
|
* However, you can also use this parameter to skip rows or to
|
|
* decompress/decode into a specific region of a larger image. NOTE: If the
|
|
* source image is a lossy JPEG image, then <code>destinationWidth</code> is
|
|
* either the scaled JPEG width (see {@link #setScalingFactor
|
|
* setScalingFactor()}, {@link TJScalingFactor#getScaled
|
|
* TJScalingFactor.getScaled()}, and {@link #getWidth}) or the width of the
|
|
* cropping region (see {@link #setCroppingRegion setCroppingRegion()}.) If
|
|
* the source image is a YUV image or a lossless JPEG image, then
|
|
* <code>destinationWidth</code> is the width of the source image.
|
|
*
|
|
* @param pixelFormat pixel format of the decompressed/decoded image (one of
|
|
* {@link TJ#PF_RGB TJ.PF_*})
|
|
*/
|
|
public void decompress8(int[] dstBuf, int x, int y, int stride,
|
|
int pixelFormat) 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 ||
|
|
pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
|
|
throw new IllegalArgumentException("Invalid argument in decompress8()");
|
|
if (yuvImage != null) {
|
|
checkSubsampling();
|
|
decodeYUV8(yuvImage.getPlanes(), yuvImage.getOffsets(),
|
|
yuvImage.getStrides(), dstBuf, x, y, yuvImage.getWidth(),
|
|
stride, yuvImage.getHeight(), pixelFormat);
|
|
} else
|
|
decompress8(jpegBuf, jpegBufSize, dstBuf, x, y, stride, pixelFormat);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #set set()}, {@link #setScalingFactor
|
|
* setScalingFactor()}, and {@link #decompress8(int[], int, int, int, int)}
|
|
* instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public void decompress(int[] dstBuf, int x, int y, int desiredWidth,
|
|
int stride, int desiredHeight, int pixelFormat,
|
|
int flags) throws TJException {
|
|
if ((yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
|
|
flags < 0)
|
|
throw new IllegalArgumentException("Invalid argument in decompress()");
|
|
|
|
if (yuvImage == null) {
|
|
TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight);
|
|
setScalingFactor(sf);
|
|
}
|
|
processFlags(flags);
|
|
decompress8(dstBuf, x, y, stride, pixelFormat);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV
|
|
* source image associated with this decompressor instance and output an
|
|
* 8-bit-per-sample 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#PARAM_STOPONWARNING}
|
|
* is set.)
|
|
*
|
|
* @param dstImage a <code>BufferedImage</code> instance that will receive
|
|
* the packed-pixel decompressed/decoded image. If the source image is a
|
|
* lossy JPEG image, then the width and height of the
|
|
* <code>BufferedImage</code> instance must match the scaled JPEG width and
|
|
* height (see {@link #setScalingFactor setScalingFactor()},
|
|
* {@link TJScalingFactor#getScaled TJScalingFactor.getScaled()},
|
|
* {@link #getWidth}, and {@link #getHeight}) or the width and height of the
|
|
* cropping region (see {@link #setCroppingRegion setCroppingRegion()}.) If
|
|
* the source image is a YUV image or a lossless JPEG image, then the width
|
|
* and height of the <code>BufferedImage</code> instance must match the width
|
|
* and height of the source image.
|
|
*/
|
|
public void decompress8(BufferedImage dstImage) throws TJException {
|
|
if (dstImage == null)
|
|
throw new IllegalArgumentException("Invalid argument in decompress8()");
|
|
|
|
if (yuvImage != null) {
|
|
if (dstImage.getWidth() != yuvImage.getWidth() ||
|
|
dstImage.getHeight() != yuvImage.getHeight())
|
|
throw new IllegalArgumentException("BufferedImage dimensions do not match the dimensions of the source image.");
|
|
} else {
|
|
if (scalingFactor.getScaled(getJPEGWidth()) != dstImage.getWidth() ||
|
|
scalingFactor.getScaled(getJPEGHeight()) != dstImage.getHeight())
|
|
throw new IllegalArgumentException("BufferedImage dimensions do not match the scaled JPEG dimensions.");
|
|
}
|
|
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) {
|
|
checkSubsampling();
|
|
decodeYUV8(yuvImage.getPlanes(), yuvImage.getOffsets(),
|
|
yuvImage.getStrides(), buf, 0, 0, yuvImage.getWidth(),
|
|
stride, yuvImage.getHeight(), pixelFormat);
|
|
} else {
|
|
if (jpegBuf == null)
|
|
throw new IllegalStateException(NO_ASSOC_ERROR);
|
|
decompress8(jpegBuf, jpegBufSize, buf, 0, 0, stride, pixelFormat);
|
|
}
|
|
} 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();
|
|
decompress8(buf, 0, 0, pitch, pixelFormat);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #set set()}, {@link #setScalingFactor
|
|
* setScalingFactor()}, and {@link #decompress8(BufferedImage)} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
public void decompress(BufferedImage dstImage, int flags)
|
|
throws TJException {
|
|
if (flags < 0)
|
|
throw new IllegalArgumentException("Invalid argument in decompress()");
|
|
|
|
if (yuvImage == null) {
|
|
TJScalingFactor sf = getScalingFactor(dstImage.getWidth(),
|
|
dstImage.getHeight());
|
|
if (sf.getScaled(getJPEGWidth()) != dstImage.getWidth() ||
|
|
sf.getScaled(getJPEGHeight()) != dstImage.getHeight())
|
|
throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
|
|
|
|
setScalingFactor(sf);
|
|
}
|
|
|
|
processFlags(flags);
|
|
decompress8(dstImage);
|
|
}
|
|
|
|
/**
|
|
* Decompress the 8-bit-per-sample JPEG source image or decode the planar YUV
|
|
* source image associated with this decompressor instance and return a
|
|
* <code>BufferedImage</code> instance containing the 8-bit-per-sample
|
|
* packed-pixel decompressed/decoded image.
|
|
*
|
|
* @param bufferedImageType the image type of the <code>BufferedImage</code>
|
|
* instance that will be created (for instance,
|
|
* <code>BufferedImage.TYPE_INT_RGB</code>)
|
|
*
|
|
* @return a <code>BufferedImage</code> instance containing the
|
|
* 8-bit-per-sample packed-pixel decompressed/decoded image.
|
|
*/
|
|
public BufferedImage decompress8(int bufferedImageType) throws TJException {
|
|
BufferedImage img =
|
|
new BufferedImage(scalingFactor.getScaled(getJPEGWidth()),
|
|
scalingFactor.getScaled(getJPEGHeight()),
|
|
bufferedImageType);
|
|
decompress8(img);
|
|
return img;
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use {@link #set set()}, {@link #setScalingFactor
|
|
* setScalingFactor()}, and {@link #decompress8(int)} instead.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
@Deprecated
|
|
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()");
|
|
|
|
if (yuvImage == null) {
|
|
TJScalingFactor sf = getScalingFactor(desiredWidth, desiredHeight);
|
|
setScalingFactor(sf);
|
|
}
|
|
processFlags(flags);
|
|
return decompress8(bufferedImageType);
|
|
}
|
|
|
|
/**
|
|
* 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();
|
|
}
|
|
};
|
|
|
|
@SuppressWarnings("deprecation")
|
|
final void processFlags(int flags) {
|
|
set(TJ.PARAM_BOTTOMUP, (flags & TJ.FLAG_BOTTOMUP) != 0 ? 1 : 0);
|
|
set(TJ.PARAM_FASTUPSAMPLE, (flags & TJ.FLAG_FASTUPSAMPLE) != 0 ? 1 : 0);
|
|
set(TJ.PARAM_FASTDCT, (flags & TJ.FLAG_FASTDCT) != 0 ? 1 : 0);
|
|
set(TJ.PARAM_STOPONWARNING, (flags & TJ.FLAG_STOPONWARNING) != 0 ? 1 : 0);
|
|
set(TJ.PARAM_SCANLIMIT, (flags & TJ.FLAG_LIMITSCANS) != 0 ? 500 : 0);
|
|
}
|
|
|
|
final void checkSubsampling() {
|
|
if (get(TJ.PARAM_SUBSAMP) == TJ.SAMP_UNKNOWN)
|
|
throw new IllegalStateException("Unknown or unspecified subsampling level");
|
|
}
|
|
|
|
private native void init() throws TJException;
|
|
|
|
private native void destroy() throws TJException;
|
|
|
|
private native void decompressHeader(byte[] srcBuf, int size)
|
|
throws TJException;
|
|
|
|
private native void setCroppingRegion() throws TJException;
|
|
|
|
@SuppressWarnings("checkstyle:HiddenField")
|
|
private native void decompress8(byte[] srcBuf, int size, byte[] dstBuf,
|
|
int x, int y, int pitch, int pixelFormat) throws TJException;
|
|
|
|
@SuppressWarnings("checkstyle:HiddenField")
|
|
private native void decompress12(byte[] srcBuf, int size, short[] dstBuf,
|
|
int x, int y, int pitch, int pixelFormat) throws TJException;
|
|
|
|
@SuppressWarnings("checkstyle:HiddenField")
|
|
private native void decompress16(byte[] srcBuf, int size, short[] dstBuf,
|
|
int x, int y, int pitch, int pixelFormat) throws TJException;
|
|
|
|
@SuppressWarnings("checkstyle:HiddenField")
|
|
private native void decompress8(byte[] srcBuf, int size, int[] dstBuf, int x,
|
|
int y, int stride, int pixelFormat) throws TJException;
|
|
|
|
@SuppressWarnings("checkstyle:HiddenField")
|
|
private native void decompressToYUV8(byte[] srcBuf, int size,
|
|
byte[][] dstPlanes, int[] dstOffsets, int[] dstStrides) throws TJException;
|
|
|
|
private native void decodeYUV8(byte[][] srcPlanes, int[] srcOffsets,
|
|
int[] srcStrides, byte[] dstBuf, int x, int y, int width, int pitch,
|
|
int height, int pixelFormat) throws TJException;
|
|
|
|
private native void decodeYUV8(byte[][] srcPlanes, int[] srcOffsets,
|
|
int[] srcStrides, int[] dstBuf, int x, int y, int width, int stride,
|
|
int height, int pixelFormat) throws TJException;
|
|
|
|
/**
|
|
* @hidden
|
|
* Ugly hack alert. It isn't straightforward to save 12-bit-per-sample and
|
|
* 16-bit-per-sample images using the ImageIO and BufferedImage classes, and
|
|
* ImageIO doesn't support PBMPLUS files anyhow. This method accesses
|
|
* tj3SaveImage() through JNI and copies the pixel data between the C and
|
|
* Java heaps. Currently it is undocumented and used only by TJBench.
|
|
*/
|
|
@SuppressWarnings("checkstyle:JavadocMethod")
|
|
public native void saveImage(int precision, String fileName, Object srcBuf,
|
|
int width, int pitch, int height,
|
|
int pixelFormat) throws TJException;
|
|
|
|
static {
|
|
TJLoader.load();
|
|
}
|
|
|
|
private long handle = 0;
|
|
private byte[] jpegBuf = null;
|
|
private int jpegBufSize = 0;
|
|
private YUVImage yuvImage = null;
|
|
private TJScalingFactor scalingFactor = TJ.UNSCALED;
|
|
private Rectangle croppingRegion = TJ.UNCROPPED;
|
|
private ByteOrder byteOrder = null;
|
|
}
|