diff options
author | dcommander <dcommander@632fc199-4ca6-4c93-a231-07263d6284db> | 2014-08-17 12:23:49 +0000 |
---|---|---|
committer | dcommander <dcommander@632fc199-4ca6-4c93-a231-07263d6284db> | 2014-08-17 12:23:49 +0000 |
commit | 7cf01068d01ad27a42b682b81e1829073f00680a (patch) | |
tree | 2dbb715e1f2e148b178920da98ea63ee59272a59 /java/org/libjpegturbo/turbojpeg | |
parent | 13d2bd8952748c7d38282b8fbc6a5c6a33de43e5 (diff) |
Refactored YUVImage Java class so that it supports both unified YUV image buffers as well as separate YUV image planes; modified the JNI functions accordingly and added new helper functions to the TurboJPEG C API (tjPlaneWidth(), tjPlaneHeight(), tjPlaneSizeYUV()) to facilitate those modifications; changed potentially confusing "component width" and "component height" terms to "plane width" and "plane height" and modified variable names in turbojpeg.c to reflect this; numerous other documentation tweaks
git-svn-id: svn://svn.code.sf.net/p/libjpeg-turbo/code/trunk@1360 632fc199-4ca6-4c93-a231-07263d6284db
Diffstat (limited to 'java/org/libjpegturbo/turbojpeg')
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJ.java | 82 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJCompressor.java | 79 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJDecompressor.java | 175 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJScalingFactor.java | 11 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/TJTransformer.java | 4 | ||||
-rw-r--r-- | java/org/libjpegturbo/turbojpeg/YUVImage.java | 298 |
6 files changed, 501 insertions, 148 deletions
diff --git a/java/org/libjpegturbo/turbojpeg/TJ.java b/java/org/libjpegturbo/turbojpeg/TJ.java index ac4a4dd..644a197 100644 --- a/java/org/libjpegturbo/turbojpeg/TJ.java +++ b/java/org/libjpegturbo/turbojpeg/TJ.java @@ -84,7 +84,8 @@ public final class TJ { * @param subsamp the level of chrominance subsampling (one of * <code>SAMP_*</code>) * - * @return the MCU block width for the given level of chrominance subsampling + * @return the MCU block width for the given level of chrominance + * subsampling. */ public static int getMCUWidth(int subsamp) throws Exception { if (subsamp < 0 || subsamp >= NUMSAMP) @@ -105,7 +106,7 @@ public final class TJ { * <code>SAMP_*</code>) * * @return the MCU block height for the given level of chrominance - * subsampling + * subsampling. */ public static int getMCUHeight(int subsamp) throws Exception { if (subsamp < 0 || subsamp >= NUMSAMP) @@ -214,7 +215,7 @@ public final class TJ { * * @param pixelFormat the pixel format (one of <code>PF_*</code>) * - * @return the pixel size (in bytes) for the given pixel format + * @return the pixel size (in bytes) for the given pixel format. */ public static int getPixelSize(int pixelFormat) throws Exception { if (pixelFormat < 0 || pixelFormat >= NUMPF) @@ -236,7 +237,7 @@ public final class TJ { * * @param pixelFormat the pixel format (one of <code>PF_*</code>) * - * @return the red offset for the given pixel format + * @return the red offset for the given pixel format. */ public static int getRedOffset(int pixelFormat) throws Exception { if (pixelFormat < 0 || pixelFormat >= NUMPF) @@ -258,7 +259,7 @@ public final class TJ { * * @param pixelFormat the pixel format (one of <code>PF_*</code>) * - * @return the green offset for the given pixel format + * @return the green offset for the given pixel format. */ public static int getGreenOffset(int pixelFormat) throws Exception { if (pixelFormat < 0 || pixelFormat >= NUMPF) @@ -280,7 +281,7 @@ public final class TJ { * * @param pixelFormat the pixel format (one of <code>PF_*</code>) * - * @return the blue offset for the given pixel format + * @return the blue offset for the given pixel format. */ public static int getBlueOffset(int pixelFormat) throws Exception { if (pixelFormat < 0 || pixelFormat >= NUMPF) @@ -404,7 +405,7 @@ public final class TJ { * generating the JPEG image (one of {@link TJ TJ.SAMP_*}) * * @return the maximum size of the buffer (in bytes) required to hold a JPEG - * image with the given width, height, and level of chrominance subsampling + * image with the given width, height, and level of chrominance subsampling. */ public static native int bufSize(int width, int height, int jpegSubsamp) throws Exception; @@ -416,8 +417,7 @@ public final class TJ { * @param width the width (in pixels) of the YUV image * * @param pad the width of each line in each plane of the image is padded to - * the nearest multiple of this number of bytes (must be a power of - * 2.) + * the nearest multiple of this number of bytes (must be a power of 2.) * * @param height the height (in pixels) of the YUV image * @@ -425,7 +425,7 @@ public final class TJ { * image (one of {@link TJ TJ.SAMP_*}) * * @return the size of the buffer (in bytes) required to hold a YUV planar - * image with the given width, height, and level of chrominance subsampling + * image with the given width, height, and level of chrominance subsampling. */ public static native int bufSizeYUV(int width, int pad, int height, int subsamp) @@ -439,11 +439,71 @@ public final class TJ { throws Exception; /** + * 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 line 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 TJ TJ.SAMP_*}) + * + * @return the size of the buffer (in bytes) required to hold a YUV planar + * image with the given parameters. + */ + public static native int planeSizeYUV(int componentID, int width, int stride, + int height, int subsamp) + throws Exception; + + /** + * 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 TJ 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) + throws Exception; + + /** + * 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 TJ 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) + throws Exception; + + /** * Returns a list of fractional scaling factors that the JPEG decompressor in * this implementation of TurboJPEG supports. * * @return a list of fractional scaling factors that the JPEG decompressor in - * this implementation of TurboJPEG supports + * this implementation of TurboJPEG supports. */ public static native TJScalingFactor[] getScalingFactors() throws Exception; diff --git a/java/org/libjpegturbo/turbojpeg/TJCompressor.java b/java/org/libjpegturbo/turbojpeg/TJCompressor.java index 0debf53..c4a8cc5 100644 --- a/java/org/libjpegturbo/turbojpeg/TJCompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJCompressor.java @@ -332,9 +332,10 @@ public class TJCompressor { throw new Exception("Subsampling level not set"); if (srcYUVImage != null) - compressedSize = compressFromYUV(srcYUVImage.getBuf(), + compressedSize = compressFromYUV(srcYUVImage.getPlanes(), + srcYUVImage.getOffsets(), srcYUVImage.getWidth(), - srcYUVImage.getPad(), + srcYUVImage.getStrides(), srcYUVImage.getHeight(), srcYUVImage.getSubsamp(), dstBuf, jpegQuality, flags); @@ -429,14 +430,14 @@ public class TJCompressor { if (srcBufInt != null) { encodeYUV(srcBufInt, srcX, srcY, srcWidth, srcStride, srcHeight, - srcPixelFormat, dstImage.getBuf(), dstImage.getPad(), - dstImage.getSubsamp(), flags); + srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(), + dstImage.getStrides(), dstImage.getSubsamp(), flags); } else { encodeYUV(srcBuf, srcX, srcY, srcWidth, srcPitch, srcHeight, - srcPixelFormat, dstImage.getBuf(), dstImage.getPad(), - dstImage.getSubsamp(), flags); + srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(), + dstImage.getStrides(), dstImage.getSubsamp(), flags); } - compressedSize = dstImage.getSize(); + compressedSize = 0; } /** @@ -456,11 +457,11 @@ public class TJCompressor { /** * Encode the uncompressed source image associated with this compressor - * instance into a YUV planar image and return a <code>YUVImage</code> - * instance containing the encoded image. This method uses the accelerated - * color conversion routines in TurboJPEG's underlying codec but does not - * execute any of the other steps in the JPEG compression process. Encoding - * CMYK source images to YUV is not supported. + * instance into a unified YUV planar image buffer and return a + * <code>YUVImage</code> instance containing the encoded image. This method + * uses the accelerated color conversion routines in TurboJPEG's underlying + * codec but does not execute any of the other steps in the JPEG compression + * process. Encoding CMYK source images to YUV is not supported. * * @param pad the width of each line in each plane of the YUV image will be * padded to the nearest multiple of this number of bytes (must be a power of @@ -469,7 +470,7 @@ public class TJCompressor { * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a YUV planar image + * @return a YUV planar image. */ public YUVImage encodeYUV(int pad, int flags) throws Exception { if (srcWidth < 1 || srcHeight < 1) @@ -484,6 +485,37 @@ public class TJCompressor { } /** + * Encode the uncompressed source image associated with this compressor + * instance into separate Y, U (Cb), and V (Cr) image planes and return a + * <code>YUVImage</code> instance containing the encoded image planes. This + * method uses the accelerated color conversion routines in TurboJPEG's + * underlying codec but does not execute any of the other steps in the JPEG + * compression process. Encoding CMYK source images to YUV is not supported. + * + * @param strides an array of integers, each specifying the number of bytes + * per line in the corresponding plane of the output image. Setting the + * stride for any plane to 0 is the same as setting it to the component width + * of the plane. If <code>strides</code> is null, then the strides for all + * planes will be set to their respective component widths. You can adjust + * the strides in order to add an arbitrary amount of line padding to each + * plane. + * + * @param flags the bitwise OR of one or more of + * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * + * @return a YUV planar image. + */ + public YUVImage encodeYUV(int[] strides, int flags) throws Exception { + if (srcWidth < 1 || srcHeight < 1) + throw new Exception(NO_ASSOC_ERROR); + if (subsamp < 0) + throw new Exception("Subsampling level not set"); + YUVImage yuvImage = new YUVImage(srcWidth, strides, srcHeight, subsamp); + encodeYUV(yuvImage, flags); + return yuvImage; + } + + /** * @deprecated Use {@link #encodeYUV(int, int)} instead. */ @Deprecated @@ -512,7 +544,7 @@ public class TJCompressor { /** * @deprecated Use * {@link #setSourceImage(BufferedImage, int, int, int, int)} and - * {@link #encodeYUV(int)} instead. + * {@link #encodeYUV(int, int)} instead. */ @Deprecated public byte[] encodeYUV(BufferedImage srcImage, int flags) throws Exception { @@ -522,10 +554,10 @@ public class TJCompressor { /** * Returns the size of the image (in bytes) generated by the most recent - * compress/encode operation. + * compress operation. * * @return the size of the image (in bytes) generated by the most recent - * compress/encode operation + * compress operation. */ public int getCompressedSize() { return compressedSize; @@ -568,8 +600,9 @@ public class TJCompressor { int stride, int height, int pixelFormat, byte[] dstBuf, int jpegSubsamp, int jpegQual, int flags) throws Exception; - private native int compressFromYUV(byte[] srcBuf, int width, int pad, - int height, int subsamp, byte[] dstBuf, int jpegQual, int flags) + private native int compressFromYUV(byte[][] srcPlanes, int[] srcOffsets, + int width, int[] srcStrides, int height, int subsamp, byte[] dstBuf, + int jpegQual, int flags) throws Exception; private native void encodeYUV(byte[] srcBuf, int width, int pitch, @@ -577,16 +610,18 @@ public class TJCompressor { throws Exception; // deprecated private native void encodeYUV(byte[] srcBuf, int x, int y, int width, - int pitch, int height, int pixelFormat, byte[] dstBuf, int pad, - int subsamp, int flags) throws Exception; + int pitch, int height, int pixelFormat, byte[][] dstPlanes, + int[] dstOffsets, int[] dstStrides, int subsamp, int flags) + throws Exception; private native void encodeYUV(int[] srcBuf, int width, int stride, int height, int pixelFormat, byte[] dstBuf, int subsamp, int flags) throws Exception; // deprecated private native void encodeYUV(int[] srcBuf, int x, int y, int width, - int pitch, int height, int pixelFormat, byte[] dstBuf, int pad, - int subsamp, int flags) throws Exception; + int srcStride, int height, int pixelFormat, byte[][] dstPlanes, + int[] dstOffsets, int[] dstStrides, int subsamp, int flags) + throws Exception; static { TJLoader.load(); diff --git a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java index c71ce77..1a2774c 100644 --- a/java/org/libjpegturbo/turbojpeg/TJDecompressor.java +++ b/java/org/libjpegturbo/turbojpeg/TJDecompressor.java @@ -87,18 +87,18 @@ public class TJDecompressor { /** * Associate the JPEG image of length <code>imageSize</code> bytes stored in - * <code>srcImage</code> with this decompressor instance. This image will + * <code>jpegImage</code> with this decompressor instance. This image will * be used as the source image for subsequent decompress operations. * - * @param srcImage JPEG image buffer + * @param jpegImage JPEG image buffer * * @param imageSize size of the JPEG image (in bytes) */ - public void setSourceImage(byte[] srcImage, int imageSize) + public void setSourceImage(byte[] jpegImage, int imageSize) throws Exception { - if (srcImage == null || imageSize < 1) + if (jpegImage == null || imageSize < 1) throw new Exception("Invalid argument in setSourceImage()"); - jpegBuf = srcImage; + jpegBuf = jpegImage; jpegBufSize = imageSize; decompressHeader(jpegBuf, jpegBufSize); yuvImage = null; @@ -134,7 +134,7 @@ public class TJDecompressor { * decompressor instance. * * @return the width of the source image (JPEG or YUV) associated with this - * decompressor instance + * decompressor instance. */ public int getWidth() throws Exception { if (yuvImage != null) @@ -149,7 +149,7 @@ public class TJDecompressor { * decompressor instance. * * @return the height of the source image (JPEG or YUV) associated with this - * decompressor instance + * decompressor instance. */ public int getHeight() throws Exception { if (yuvImage != null) @@ -165,7 +165,7 @@ public class TJDecompressor { * {@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 + * (JPEG or YUV) associated with this decompressor instance. */ public int getSubsamp() throws Exception { if (yuvImage != null) @@ -183,7 +183,7 @@ public class TJDecompressor { * 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 + * with this decompressor instance. */ public int getColorspace() throws Exception { if (yuvImage != null) @@ -196,23 +196,10 @@ public class TJDecompressor { } /** - * Returns the source image buffer associated with this decompressor - * instance. + * Returns the JPEG image buffer associated with this decompressor instance. * - * @return the source image buffer associated with this decompressor instance + * @return the JPEG image buffer associated with this decompressor instance. */ - public byte[] getSourceBuf() throws Exception { - if (yuvImage != null) - return yuvImage.getBuf(); - if (jpegBuf == null) - throw new Exception(NO_ASSOC_ERROR); - return jpegBuf; - } - - /** - * @deprecated Use {@link #getSourceBuf} instead. - */ - @Deprecated public byte[] getJPEGBuf() throws Exception { if (jpegBuf == null) throw new Exception(NO_ASSOC_ERROR); @@ -220,24 +207,12 @@ public class TJDecompressor { } /** - * Returns the size of the source image (in bytes) associated with this + * Returns the size of the JPEG image (in bytes) associated with this * decompressor instance. * - * @return the size of the source image (in bytes) associated with this - * decompressor instance - */ - public int getSourceSize() throws Exception { - if (yuvImage != null) - return yuvImage.getSize(); - if (jpegBufSize < 1) - throw new Exception(NO_ASSOC_ERROR); - return jpegBufSize; - } - - /** - * @deprecated Use {@link #getSourceSize} instead. + * @return the size of the JPEG image (in bytes) associated with this + * decompressor instance. */ - @Deprecated public int getJPEGSize() throws Exception { if (jpegBufSize < 1) throw new Exception(NO_ASSOC_ERROR); @@ -261,7 +236,7 @@ public class TJDecompressor { * * @return the width of the largest scaled-down image that the TurboJPEG * decompressor can generate without exceeding the desired image width and - * height + * height. */ public int getScaledWidth(int desiredWidth, int desiredHeight) throws Exception { @@ -303,7 +278,7 @@ public class TJDecompressor { * * @return the height of the largest scaled-down image that the TurboJPEG * decompressor can generate without exceeding the desired image width and - * height + * height. */ public int getScaledHeight(int desiredWidth, int desiredHeight) throws Exception { @@ -402,9 +377,10 @@ public class TJDecompressor { pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) throw new Exception("Invalid argument in decompress()"); if (yuvImage != null) - decodeYUV(yuvImage.getBuf(), yuvImage.getPad(), yuvImage.getSubsamp(), - dstBuf, x, y, yuvImage.getWidth(), pitch, yuvImage.getHeight(), - pixelFormat, flags); + 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, @@ -449,7 +425,7 @@ public class TJDecompressor { * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a buffer containing the decompressed image + * @return a buffer containing the decompressed image. */ public byte[] decompress(int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags) throws Exception { @@ -500,9 +476,9 @@ public class TJDecompressor { if (jpegSubsamp != dstImage.getSubsamp()) throw new Exception("YUVImage subsampling level does not match that of the JPEG image"); - decompressToYUV(jpegBuf, jpegBufSize, dstImage.getBuf(), - dstImage.getWidth(), dstImage.getPad(), - dstImage.getHeight(), flags); + decompressToYUV(jpegBuf, jpegBufSize, dstImage.getPlanes(), + dstImage.getOffsets(), dstImage.getWidth(), + dstImage.getStrides(), dstImage.getHeight(), flags); } /** @@ -517,12 +493,70 @@ public class TJDecompressor { /** * Decompress the JPEG source image associated with this decompressor - * instance into a YUV planar image and return a <code>YUVImage</code> - * instance containing the decompressed image. This method performs JPEG - * decompression but leaves out the color conversion step, so a planar YUV - * image is generated instead of an RGB or grayscale image. This method - * cannot be used to decompress JPEG source images with the CMYK or YCCK - * colorspace. + * instance into a set of Y, U (Cb), and V (Cr) image planes and return a + * <code>YUVImage</code> instance containing the decompressed image planes. + * This method performs JPEG decompression but leaves out the color + * conversion step, so a planar YUV image is generated instead of an RGB or + * grayscale image. This method cannot be used to decompress JPEG source + * images with the CMYK or YCCK colorspace. + * + * @param desiredWidth desired width (in pixels) of the YUV image. If the + * desired image dimensions are different than the dimensions of the JPEG + * image being decompressed, then TurboJPEG will use scaling in the JPEG + * decompressor to generate the largest possible image that will fit within + * the desired dimensions. Setting this to 0 is the same as setting it to + * the width of the JPEG image (in other words, the width will not be + * considered when determining the scaled image size.) + * + * @param strides an array of integers, each specifying the number of bytes + * per line in the corresponding plane of the output image. Setting the + * stride for any plane to 0 is the same as setting it to the scaled + * component width of the plane. If <tt>strides</tt> is NULL, then the + * strides for all planes will be set to their respective scaled component + * widths. You can adjust the strides in order to add an arbitrary amount of + * line padding to each plane. + * + * @param desiredHeight desired height (in pixels) of the YUV image. If the + * desired image dimensions are different than the dimensions of the JPEG + * image being decompressed, then TurboJPEG will use scaling in the JPEG + * decompressor to generate the largest possible image that will fit within + * the desired dimensions. Setting this to 0 is the same as setting it to + * the height of the JPEG image (in other words, the height will not be + * considered when determining the scaled image size.) + * + * @param flags the bitwise OR of one or more of + * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} + * + * @return a YUV planar image. + */ + public YUVImage decompressToYUV(int desiredWidth, int[] strides, + int desiredHeight, + int flags) throws Exception { + if (flags < 0) + throw new Exception("Invalid argument in decompressToYUV()"); + if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0) + throw new Exception(NO_ASSOC_ERROR); + if (jpegSubsamp >= TJ.NUMSAMP) + throw new Exception("JPEG header information is invalid"); + if (yuvImage != null) + throw new Exception("Source image is the wrong type"); + + int scaledWidth = getScaledWidth(desiredWidth, desiredHeight); + int scaledHeight = getScaledHeight(desiredWidth, desiredHeight); + YUVImage yuvImage = new YUVImage(scaledWidth, null, scaledHeight, + jpegSubsamp); + decompressToYUV(yuvImage, flags); + return yuvImage; + } + + /** + * Decompress the JPEG source image associated with this decompressor + * instance into a unified YUV planar image buffer and return a + * <code>YUVImage</code> instance containing the decompressed image. This + * method performs JPEG decompression but leaves out the color conversion + * step, so a planar YUV image is generated instead of an RGB or grayscale + * image. This method cannot be used to decompress JPEG source images with + * the CMYK or YCCK colorspace. * * @param desiredWidth desired width (in pixels) of the YUV image. If the * desired image dimensions are different than the dimensions of the JPEG @@ -547,7 +581,7 @@ public class TJDecompressor { * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * - * @return a YUV planar image + * @return a YUV planar image. */ public YUVImage decompressToYUV(int desiredWidth, int pad, int desiredHeight, int flags) throws Exception { @@ -650,9 +684,10 @@ public class TJDecompressor { pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0) throw new Exception("Invalid argument in decompress()"); if (yuvImage != null) - decodeYUV(yuvImage.getBuf(), yuvImage.getPad(), yuvImage.getSubsamp(), - dstBuf, x, y, yuvImage.getWidth(), stride, - yuvImage.getHeight(), pixelFormat, flags); + 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); @@ -734,8 +769,9 @@ public class TJDecompressor { DataBufferInt db = (DataBufferInt)wr.getDataBuffer(); int[] buf = db.getData(); if (yuvImage != null) - decodeYUV(yuvImage.getBuf(), yuvImage.getPad(), yuvImage.getSubsamp(), - buf, 0, 0, yuvImage.getWidth(), stride, yuvImage.getHeight(), + decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(), + yuvImage.getStrides(), yuvImage.getSubsamp(), buf, 0, 0, + yuvImage.getWidth(), stride, yuvImage.getHeight(), pixelFormat, flags); else { if (jpegBuf == null) @@ -778,7 +814,7 @@ public class TJDecompressor { * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} * * @return a <code>BufferedImage</code> instance containing the - * decompressed/decoded image + * decompressed/decoded image. */ public BufferedImage decompress(int desiredWidth, int desiredHeight, int bufferedImageType, int flags) @@ -836,16 +872,17 @@ public class TJDecompressor { private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf, int flags) throws Exception; // deprecated - private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf, - int desiredWidth, int pad, int desiredheight, int flags) throws Exception; + private native void decompressToYUV(byte[] srcBuf, int size, + byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides, + int desiredheight, int flags) throws Exception; - private native void decodeYUV(byte[] srcBuf, int pad, int subsamp, - byte[] dstBuf, int x, int y, int width, int pitch, int height, - int pixelFormat, int flags) throws Exception; + 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 Exception; - private native void decodeYUV(byte[] srcBuf, int pad, int subsamp, - int[] dstBuf, int x, int y, int width, int stride, int height, - int pixelFormat, int flags) throws Exception; + 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 Exception; static { TJLoader.load(); diff --git a/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java b/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java index 4e7363f..e00fdf7 100644 --- a/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java +++ b/java/org/libjpegturbo/turbojpeg/TJScalingFactor.java @@ -42,6 +42,7 @@ public class TJScalingFactor { /** * Returns numerator + * * @return numerator */ public int getNum() { @@ -50,6 +51,7 @@ public class TJScalingFactor { /** * Returns denominator + * * @return denominator */ public int getDenom() { @@ -60,7 +62,8 @@ public class TJScalingFactor { * Returns the scaled value of <code>dimension</code>. This function * performs the integer equivalent of * <code>ceil(dimension * scalingFactor)</code>. - * @return the scaled value of <code>dimension</code> + * + * @return the scaled value of <code>dimension</code>. */ public int getScaled(int dimension) { return (dimension * num + denom - 1) / denom; @@ -69,8 +72,9 @@ public class TJScalingFactor { /** * Returns true or false, depending on whether this instance and * <code>other</code> have the same numerator and denominator. + * * @return true or false, depending on whether this instance and - * <code>other</code> have the same numerator and denominator + * <code>other</code> have the same numerator and denominator. */ public boolean equals(TJScalingFactor other) { return (this.num == other.num && this.denom == other.denom); @@ -79,8 +83,9 @@ public class TJScalingFactor { /** * 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 + * 1/1. */ public boolean isOne() { return (num == 1 && denom == 1); diff --git a/java/org/libjpegturbo/turbojpeg/TJTransformer.java b/java/org/libjpegturbo/turbojpeg/TJTransformer.java index a62f410..cf773fa 100644 --- a/java/org/libjpegturbo/turbojpeg/TJTransformer.java +++ b/java/org/libjpegturbo/turbojpeg/TJTransformer.java @@ -111,7 +111,7 @@ public class TJTransformer extends TJDecompressor { * corresponding transformed output image * * @return an array of {@link TJDecompressor} instances, each of - * which has a transformed JPEG image associated with it + * which has a transformed JPEG image associated with it. * * @param flags the bitwise OR of one or more of * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*} @@ -141,7 +141,7 @@ public class TJTransformer extends TJDecompressor { * generated by the most recent transform operation. * * @return an array containing the sizes of the transformed JPEG images - * generated by the most recent transform operation + * generated by the most recent transform operation. */ public int[] getTransformedSizes() throws Exception { if (transformedSizes == null) diff --git a/java/org/libjpegturbo/turbojpeg/YUVImage.java b/java/org/libjpegturbo/turbojpeg/YUVImage.java index 27b1b70..2d790e9 100644 --- a/java/org/libjpegturbo/turbojpeg/YUVImage.java +++ b/java/org/libjpegturbo/turbojpeg/YUVImage.java @@ -43,20 +43,18 @@ package org.libjpegturbo.turbojpeg; * 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 at a particular location in the image. The - * "component width" and "component height" of each plane are determined by the - * image width, height, and level of chrominance subsampling. For the - * luminance plane, the component width is the image width padded to the - * nearest multiple of the horizontal subsampling factor (2 in the case of - * 4:2:0 and 4:2:2, 4 in the case of 4:1:1, 1 in the case of 4:4:4 or - * grayscale.) Similarly, the component height of the luminance plane is the - * image height padded to the nearest multiple of the vertical subsampling - * factor (2 in the case of 4:2:0 or 4:4:0, 1 in the case of 4:4:4 or - * grayscale.) The component width of the chrominance planes is equal to the - * component width of the luminance plane divided by the horizontal subsampling - * factor, and the component height of the chrominance planes is equal to the - * component height of the luminance plane divided by the vertical subsampling - * factor. + * 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 (2 in the case of 4:2:0 and 4:2:2, 4 in the case of + * 4:1:1, 1 in the case of 4:4:4 or grayscale.) Similarly, the luminance plane + * height is the image height padded to the nearest multiple of the vertical + * subsampling factor (2 in the case of 4:2:0 or 4:4:0, 1 in the case of 4:4:4 + * or grayscale.) 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 @@ -67,10 +65,35 @@ package org.libjpegturbo.turbojpeg; public class YUVImage { private static final String NO_ASSOC_ERROR = - "No YUV buffer is associated with this instance"; + "No image data is associated with this instance"; /** - * Create a <code>YUVImage</code> instance with a new image buffer. + * 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 line 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) + throws Exception { + setBuf(null, null, width, strides, height, subsamp, true); + } + + /** + * Create a new <code>YUVImage</code> instance backed by a unified image + * buffer, and allocate memory for the image buffer. * * @param width width (in pixels) of the YUV image * @@ -83,13 +106,52 @@ public class YUVImage { * image (one of {@link TJ#SAMP_444 TJ.SAMP_*}) */ public YUVImage(int width, int pad, int height, int subsamp) - throws Exception { + throws Exception { setBuf(new byte[TJ.bufSizeYUV(width, pad, height, subsamp)], width, pad, height, subsamp); } /** - * Create a <code>YUVImage</code> instance from an existing YUV planar image + * 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] + + * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(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 line 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 line 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) throws Exception { + setBuf(planes, offsets, width, strides, height, subsamp, false); + } + + /** + * Create a new <code>YUVImage</code> instance from an existing unified image * buffer. * * @param yuvImage image buffer that contains or will contain YUV planar @@ -115,8 +177,89 @@ public class YUVImage { } /** - * Assign an existing YUV planar image buffer to this <code>YUVImage</code> - * instance. + * 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] + + * {@link TJ#planeSizeYUV TJ.planeSizeYUV}(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 line 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 line padding + * to each plane or to specify that this <code>YUVImage</code> image 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) throws Exception { + 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) throws Exception { + if ((planes == null && !alloc) || width < 1 || height < 1 || subsamp < 0 || + subsamp >= TJ.NUMSAMP) + throw new Exception("Invalid argument in YUVImage::setBuf()"); + + int nc = (subsamp == TJ.SAMP_GRAY ? 1 : 3); + if (planes.length != nc || (offsets != null && offsets.length != nc) || + (strides != null && strides.length != nc)) + throw new Exception("YUVImage::setBuf(): planes, offsets, or strides array is the wrong size"); + + 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 Exception("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 Exception("Invalid argument in YUVImage::setBuf()"); + if (strides[i] < 0 && offsets[i] - planeSize + pw < 0) + throw new Exception("Stride for plane " + i + " would cause memory to be accessed below plane boundary"); + if (planes[i].length < offsets[i] + planeSize) + throw new Exception("Image plane " + i + " is not large enough"); + } + + yuvPlanes = planes; + yuvOffsets = offsets; + yuvWidth = width; + yuvStrides = strides; + yuvHeight = height; + yuvSubsamp = subsamp; + } + + /** + * Assign a unified image buffer to this <code>YUVImage</code> instance. * * @param yuvImage image buffer that contains or will contain YUV planar * image data. Use {@link TJ#bufSizeYUV} to determine the minimum size for @@ -139,20 +282,34 @@ public class YUVImage { int subsamp) throws Exception { if (yuvImage == null || width < 1 || pad < 1 || ((pad & (pad - 1)) != 0) || height < 1 || subsamp < 0 || subsamp >= TJ.NUMSAMP) - throw new Exception("Invalid argument in YUVImage()"); + throw new Exception("Invalid argument in YUVImage::setBuf()"); if (yuvImage.length < TJ.bufSizeYUV(width, pad, height, subsamp)) throw new Exception("YUV image buffer is not large enough"); - yuvBuf = yuvImage; - yuvWidth = width; + + 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), pad); + if (subsamp != TJ.SAMP_GRAY) { + strides[1] = strides[2] = PAD(TJ.planeWidth(1, width, subsamp), pad); + 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); + } + yuvPad = pad; - yuvHeight = height; - yuvSubsamp = subsamp; + setBuf(planes, offsets, width, strides, height, subsamp); } /** - * Returns the width of the YUV image. + * Returns the width of the YUV image (or subregion.) * - * @return the width of the YUV image + * @return the width of the YUV image (or subregion) */ public int getWidth() throws Exception { if (yuvWidth < 1) @@ -161,9 +318,9 @@ public class YUVImage { } /** - * Returns the height of the YUV image. + * Returns the height of the YUV image (or subregion.) * - * @return the height of the YUV image + * @return the height of the YUV image (or subregion) */ public int getHeight() throws Exception { if (yuvHeight < 1) @@ -172,17 +329,44 @@ public class YUVImage { } /** - * Returns the line padding used in the YUV image buffer. + * Returns the line padding used in the YUV image buffer (if this image is + * stored in a unified buffer rather than separate image planes.) * * @return the line padding used in the YUV image buffer */ public int getPad() throws Exception { - if (yuvPad < 1 || ((yuvPad & (yuvPad - 1)) != 0)) + if (yuvPlanes == null) throw new Exception(NO_ASSOC_ERROR); + if (yuvPad < 1 || ((yuvPad & (yuvPad - 1)) != 0)) + throw new Exception("Image is not stored in a unified buffer"); return yuvPad; } /** + * Returns the number of bytes per line of each plane in the YUV image. + * + * @return the number of bytes per line of each plane in the YUV image + */ + public int[] getStrides() throws Exception { + if (yuvStrides == null) + throw new Exception(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() throws Exception { + if (yuvOffsets == null) + throw new Exception(NO_ASSOC_ERROR); + return yuvOffsets; + } + + /** * Returns the level of chrominance subsampling used in the YUV image. See * {@link TJ#SAMP_444 TJ.SAMP_*}. * @@ -195,29 +379,61 @@ public class YUVImage { } /** - * Returns the YUV image buffer + * 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() throws Exception { + if (yuvPlanes == null) + throw new Exception(NO_ASSOC_ERROR); + return yuvPlanes; + } + + /** + * Returns the YUV image buffer (if this image is stored in a unified + * buffer rather than separate image planes.) * * @return the YUV image buffer */ public byte[] getBuf() throws Exception { - if (yuvBuf == null) + if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP) throw new Exception(NO_ASSOC_ERROR); - return yuvBuf; + int nc = (yuvSubsamp == TJ.SAMP_GRAY ? 1 : 3); + for (int i = 1; i < nc; i++) { + if (yuvPlanes[i] != yuvPlanes[0]) + throw new Exception("Image is not stored in a unified buffer"); + } + return yuvPlanes[0]; } /** - * Returns the size (in bytes) of the YUV image buffer + * Returns the size (in bytes) of the YUV image buffer (if this image is + * stored in a unified buffer rather than separate image planes.) * * @return the size (in bytes) of the YUV image buffer */ - public int getSize() throws Exception { - if (yuvBuf == null) - throw new Exception(NO_ASSOC_ERROR); - return TJ.bufSizeYUV(yuvWidth, yuvPad, yuvHeight, yuvSubsamp); - } + public int getSize() throws Exception { + if (yuvPlanes == null || yuvSubsamp < 0 || yuvSubsamp >= TJ.NUMSAMP) + throw new Exception(NO_ASSOC_ERROR); + int nc = (yuvSubsamp == TJ.SAMP_GRAY ? 1 : 3); + if (yuvPad < 1) + throw new Exception("Image is not stored in a unified buffer"); + for (int i = 1; i < nc; i++) { + if (yuvPlanes[i] != yuvPlanes[0]) + throw new Exception("Image is not stored in a unified buffer"); + } + return TJ.bufSizeYUV(yuvWidth, yuvPad, yuvHeight, yuvSubsamp); + } + + private static final int PAD(int v, int p) { + return (v + p - 1) & (~(p - 1)); + } protected long handle = 0; - protected byte[] yuvBuf = null; + protected byte[][] yuvPlanes = null; + protected int[] yuvOffsets = null; + protected int[] yuvStrides = null; protected int yuvPad = 0; protected int yuvWidth = 0; protected int yuvHeight = 0; |