1 /*
2  * Copyright (C)2011-2015, 2018 D. R. Commander.  All Rights Reserved.
3  * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright notice,
11  *   this list of conditions and the following disclaimer in the documentation
12  *   and/or other materials provided with the distribution.
13  * - Neither the name of the libjpeg-turbo Project nor the names of its
14  *   contributors may be used to endorse or promote products derived from this
15  *   software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 package org.libjpegturbo.turbojpeg;
31 
32 import java.awt.image.*;
33 import java.nio.*;
34 import java.io.*;
35 
36 /**
37  * TurboJPEG decompressor
38  */
39 public class TJDecompressor implements Closeable {
40 
41   private static final String NO_ASSOC_ERROR =
42     "No JPEG image is associated with this instance";
43 
44   /**
45    * Create a TurboJPEG decompresssor instance.
46    */
TJDecompressor()47   public TJDecompressor() throws TJException {
48     init();
49   }
50 
51   /**
52    * Create a TurboJPEG decompressor instance and associate the JPEG source
53    * image stored in <code>jpegImage</code> with the newly created instance.
54    *
55    * @param jpegImage JPEG image buffer (size of the JPEG image is assumed to
56    * be the length of the array.)  This buffer is not modified.
57    */
TJDecompressor(byte[] jpegImage)58   public TJDecompressor(byte[] jpegImage) throws TJException {
59     init();
60     setSourceImage(jpegImage, jpegImage.length);
61   }
62 
63   /**
64    * Create a TurboJPEG decompressor instance and associate the JPEG source
65    * image of length <code>imageSize</code> bytes stored in
66    * <code>jpegImage</code> with the newly created instance.
67    *
68    * @param jpegImage JPEG image buffer.  This buffer is not modified.
69    *
70    * @param imageSize size of the JPEG image (in bytes)
71    */
TJDecompressor(byte[] jpegImage, int imageSize)72   public TJDecompressor(byte[] jpegImage, int imageSize) throws TJException {
73     init();
74     setSourceImage(jpegImage, imageSize);
75   }
76 
77   /**
78    * Create a TurboJPEG decompressor instance and associate the YUV planar
79    * source image stored in <code>yuvImage</code> with the newly created
80    * instance.
81    *
82    * @param yuvImage {@link YUVImage} instance containing a YUV planar
83    * image to be decoded.  This image is not modified.
84    */
85   @SuppressWarnings("checkstyle:HiddenField")
TJDecompressor(YUVImage yuvImage)86   public TJDecompressor(YUVImage yuvImage) throws TJException {
87     init();
88     setSourceImage(yuvImage);
89   }
90 
91   /**
92    * Associate the JPEG image of length <code>imageSize</code> bytes stored in
93    * <code>jpegImage</code> with this decompressor instance.  This image will
94    * be used as the source image for subsequent decompress operations.
95    *
96    * @param jpegImage JPEG image buffer.  This buffer is not modified.
97    *
98    * @param imageSize size of the JPEG image (in bytes)
99    */
setSourceImage(byte[] jpegImage, int imageSize)100   public void setSourceImage(byte[] jpegImage, int imageSize)
101                              throws TJException {
102     if (jpegImage == null || imageSize < 1)
103       throw new IllegalArgumentException("Invalid argument in setSourceImage()");
104     jpegBuf = jpegImage;
105     jpegBufSize = imageSize;
106     decompressHeader(jpegBuf, jpegBufSize);
107     yuvImage = null;
108   }
109 
110   /**
111    * @deprecated Use {@link #setSourceImage(byte[], int)} instead.
112    */
113   @SuppressWarnings("checkstyle:JavadocMethod")
114   @Deprecated
setJPEGImage(byte[] jpegImage, int imageSize)115   public void setJPEGImage(byte[] jpegImage, int imageSize)
116                            throws TJException {
117     setSourceImage(jpegImage, imageSize);
118   }
119 
120   /**
121    * Associate the specified YUV planar source image with this decompressor
122    * instance.  Subsequent decompress operations will decode this image into an
123    * RGB or grayscale destination image.
124    *
125    * @param srcImage {@link YUVImage} instance containing a YUV planar image to
126    * be decoded.  This image is not modified.
127    */
setSourceImage(YUVImage srcImage)128   public void setSourceImage(YUVImage srcImage) {
129     if (srcImage == null)
130       throw new IllegalArgumentException("Invalid argument in setSourceImage()");
131     yuvImage = srcImage;
132     jpegBuf = null;
133     jpegBufSize = 0;
134   }
135 
136 
137   /**
138    * Returns the width of the source image (JPEG or YUV) associated with this
139    * decompressor instance.
140    *
141    * @return the width of the source image (JPEG or YUV) associated with this
142    * decompressor instance.
143    */
getWidth()144   public int getWidth() {
145     if (yuvImage != null)
146       return yuvImage.getWidth();
147     if (jpegWidth < 1)
148       throw new IllegalStateException(NO_ASSOC_ERROR);
149     return jpegWidth;
150   }
151 
152   /**
153    * Returns the height of the source image (JPEG or YUV) associated with this
154    * decompressor instance.
155    *
156    * @return the height of the source image (JPEG or YUV) associated with this
157    * decompressor instance.
158    */
getHeight()159   public int getHeight() {
160     if (yuvImage != null)
161       return yuvImage.getHeight();
162     if (jpegHeight < 1)
163       throw new IllegalStateException(NO_ASSOC_ERROR);
164     return jpegHeight;
165   }
166 
167   /**
168    * Returns the level of chrominance subsampling used in the source image
169    * (JPEG or YUV) associated with this decompressor instance.  See
170    * {@link TJ#SAMP_444 TJ.SAMP_*}.
171    *
172    * @return the level of chrominance subsampling used in the source image
173    * (JPEG or YUV) associated with this decompressor instance.
174    */
getSubsamp()175   public int getSubsamp() {
176     if (yuvImage != null)
177       return yuvImage.getSubsamp();
178     if (jpegSubsamp < 0)
179       throw new IllegalStateException(NO_ASSOC_ERROR);
180     if (jpegSubsamp >= TJ.NUMSAMP)
181       throw new IllegalStateException("JPEG header information is invalid");
182     return jpegSubsamp;
183   }
184 
185   /**
186    * Returns the colorspace used in the source image (JPEG or YUV) associated
187    * with this decompressor instance.  See {@link TJ#CS_RGB TJ.CS_*}.  If the
188    * source image is YUV, then this always returns {@link TJ#CS_YCbCr}.
189    *
190    * @return the colorspace used in the source image (JPEG or YUV) associated
191    * with this decompressor instance.
192    */
getColorspace()193   public int getColorspace() {
194     if (yuvImage != null)
195       return TJ.CS_YCbCr;
196     if (jpegColorspace < 0)
197       throw new IllegalStateException(NO_ASSOC_ERROR);
198     if (jpegColorspace >= TJ.NUMCS)
199       throw new IllegalStateException("JPEG header information is invalid");
200     return jpegColorspace;
201   }
202 
203   /**
204    * Returns the JPEG image buffer associated with this decompressor instance.
205    *
206    * @return the JPEG image buffer associated with this decompressor instance.
207    */
getJPEGBuf()208   public byte[] getJPEGBuf() {
209     if (jpegBuf == null)
210       throw new IllegalStateException(NO_ASSOC_ERROR);
211     return jpegBuf;
212   }
213 
214   /**
215    * Returns the size of the JPEG image (in bytes) associated with this
216    * decompressor instance.
217    *
218    * @return the size of the JPEG image (in bytes) associated with this
219    * decompressor instance.
220    */
getJPEGSize()221   public int getJPEGSize() {
222     if (jpegBufSize < 1)
223       throw new IllegalStateException(NO_ASSOC_ERROR);
224     return jpegBufSize;
225   }
226 
227   /**
228    * Returns the width of the largest scaled-down image that the TurboJPEG
229    * decompressor can generate without exceeding the desired image width and
230    * height.
231    *
232    * @param desiredWidth desired width (in pixels) of the decompressed image.
233    * Setting this to 0 is the same as setting it to the width of the JPEG image
234    * (in other words, the width will not be considered when determining the
235    * scaled image size.)
236    *
237    * @param desiredHeight desired height (in pixels) of the decompressed image.
238    * Setting this to 0 is the same as setting it to the height of the JPEG
239    * image (in other words, the height will not be considered when determining
240    * the scaled image size.)
241    *
242    * @return the width of the largest scaled-down image that the TurboJPEG
243    * decompressor can generate without exceeding the desired image width and
244    * height.
245    */
getScaledWidth(int desiredWidth, int desiredHeight)246   public int getScaledWidth(int desiredWidth, int desiredHeight) {
247     if (jpegWidth < 1 || jpegHeight < 1)
248       throw new IllegalStateException(NO_ASSOC_ERROR);
249     if (desiredWidth < 0 || desiredHeight < 0)
250       throw new IllegalArgumentException("Invalid argument in getScaledWidth()");
251     TJScalingFactor[] sf = TJ.getScalingFactors();
252     if (desiredWidth == 0)
253       desiredWidth = jpegWidth;
254     if (desiredHeight == 0)
255       desiredHeight = jpegHeight;
256     int scaledWidth = jpegWidth, scaledHeight = jpegHeight;
257     for (int i = 0; i < sf.length; i++) {
258       scaledWidth = sf[i].getScaled(jpegWidth);
259       scaledHeight = sf[i].getScaled(jpegHeight);
260       if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight)
261         break;
262     }
263     if (scaledWidth > desiredWidth || scaledHeight > desiredHeight)
264       throw new IllegalArgumentException("Could not scale down to desired image dimensions");
265     return scaledWidth;
266   }
267 
268   /**
269    * Returns the height of the largest scaled-down image that the TurboJPEG
270    * decompressor can generate without exceeding the desired image width and
271    * height.
272    *
273    * @param desiredWidth desired width (in pixels) of the decompressed image.
274    * Setting this to 0 is the same as setting it to the width of the JPEG image
275    * (in other words, the width will not be considered when determining the
276    * scaled image size.)
277    *
278    * @param desiredHeight desired height (in pixels) of the decompressed image.
279    * Setting this to 0 is the same as setting it to the height of the JPEG
280    * image (in other words, the height will not be considered when determining
281    * the scaled image size.)
282    *
283    * @return the height of the largest scaled-down image that the TurboJPEG
284    * decompressor can generate without exceeding the desired image width and
285    * height.
286    */
getScaledHeight(int desiredWidth, int desiredHeight)287   public int getScaledHeight(int desiredWidth, int desiredHeight) {
288     if (jpegWidth < 1 || jpegHeight < 1)
289       throw new IllegalStateException(NO_ASSOC_ERROR);
290     if (desiredWidth < 0 || desiredHeight < 0)
291       throw new IllegalArgumentException("Invalid argument in getScaledHeight()");
292     TJScalingFactor[] sf = TJ.getScalingFactors();
293     if (desiredWidth == 0)
294       desiredWidth = jpegWidth;
295     if (desiredHeight == 0)
296       desiredHeight = jpegHeight;
297     int scaledWidth = jpegWidth, scaledHeight = jpegHeight;
298     for (int i = 0; i < sf.length; i++) {
299       scaledWidth = sf[i].getScaled(jpegWidth);
300       scaledHeight = sf[i].getScaled(jpegHeight);
301       if (scaledWidth <= desiredWidth && scaledHeight <= desiredHeight)
302         break;
303     }
304     if (scaledWidth > desiredWidth || scaledHeight > desiredHeight)
305       throw new IllegalArgumentException("Could not scale down to desired image dimensions");
306     return scaledHeight;
307   }
308 
309   /**
310    * Decompress the JPEG source image or decode the YUV source image associated
311    * with this decompressor instance and output a grayscale, RGB, or CMYK image
312    * to the given destination buffer.
313    * <p>
314    * NOTE: The output image is fully recoverable if this method throws a
315    * non-fatal {@link TJException} (unless
316    * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
317    *
318    * @param dstBuf buffer that will receive the decompressed/decoded image.
319    * If the source image is a JPEG image, then this buffer should normally be
320    * <code>pitch * scaledHeight</code> bytes in size, where
321    * <code>scaledHeight</code> can be determined by calling <code>
322    * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight)
323    * </code> with one of the scaling factors returned from {@link
324    * TJ#getScalingFactors} or by calling {@link #getScaledHeight}.  If the
325    * source image is a YUV image, then this buffer should normally be
326    * <code>pitch * height</code> bytes in size, where <code>height</code> is
327    * the height of the YUV image.  However, the buffer may also be larger than
328    * the dimensions of the source image, in which case the <code>x</code>,
329    * <code>y</code>, and <code>pitch</code> parameters can be used to specify
330    * the region into which the source image should be decompressed/decoded.
331    *
332    * @param x x offset (in pixels) of the region in the destination image into
333    * which the source image should be decompressed/decoded
334    *
335    * @param y y offset (in pixels) of the region in the destination image into
336    * which the source image should be decompressed/decoded
337    *
338    * @param desiredWidth If the source image is a JPEG image, then this
339    * specifies the desired width (in pixels) of the decompressed image (or
340    * image region.)  If the desired destination image dimensions are different
341    * than the source image dimensions, then TurboJPEG will use scaling in the
342    * JPEG decompressor to generate the largest possible image that will fit
343    * within the desired dimensions.  Setting this to 0 is the same as setting
344    * it to the width of the JPEG image (in other words, the width will not be
345    * considered when determining the scaled image size.)  This parameter is
346    * ignored if the source image is a YUV image.
347    *
348    * @param pitch bytes per line of the destination image.  Normally, this
349    * should be set to <code>scaledWidth * TJ.pixelSize(pixelFormat)</code> if
350    * the destination image is unpadded, but you can use this to, for instance,
351    * pad each line of the destination image to a 4-byte boundary or to
352    * decompress/decode the source image into a region of a larger image.  NOTE:
353    * if the source image is a JPEG image, then <code>scaledWidth</code> can be
354    * determined by calling <code>
355    * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth)
356    * </code> or by calling {@link #getScaledWidth}.  If the source image is a
357    * YUV image, then <code>scaledWidth</code> is the width of the YUV image.
358    * Setting this parameter to 0 is the equivalent of setting it to
359    * <code>scaledWidth * TJ.pixelSize(pixelFormat)</code>.
360    *
361    * @param desiredHeight If the source image is a JPEG image, then this
362    * specifies the desired height (in pixels) of the decompressed image (or
363    * image region.)  If the desired destination image dimensions are different
364    * than the source image dimensions, then TurboJPEG will use scaling in the
365    * JPEG decompressor to generate the largest possible image that will fit
366    * within the desired dimensions.  Setting this to 0 is the same as setting
367    * it to the height of the JPEG image (in other words, the height will not be
368    * considered when determining the scaled image size.)  This parameter is
369    * ignored if the source image is a YUV image.
370    *
371    * @param pixelFormat pixel format of the decompressed/decoded image (one of
372    * {@link TJ#PF_RGB TJ.PF_*})
373    *
374    * @param flags the bitwise OR of one or more of
375    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
376    */
decompress(byte[] dstBuf, int x, int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)377   public void decompress(byte[] dstBuf, int x, int y, int desiredWidth,
378                          int pitch, int desiredHeight, int pixelFormat,
379                          int flags) throws TJException {
380     if (jpegBuf == null && yuvImage == null)
381       throw new IllegalStateException(NO_ASSOC_ERROR);
382     if (dstBuf == null || x < 0 || y < 0 || pitch < 0 ||
383         (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
384         pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
385       throw new IllegalArgumentException("Invalid argument in decompress()");
386     if (yuvImage != null)
387       decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
388                 yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y,
389                 yuvImage.getWidth(), pitch, yuvImage.getHeight(), pixelFormat,
390                 flags);
391     else {
392       if (x > 0 || y > 0)
393         decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, pitch,
394                    desiredHeight, pixelFormat, flags);
395       else
396         decompress(jpegBuf, jpegBufSize, dstBuf, desiredWidth, pitch,
397                    desiredHeight, pixelFormat, flags);
398     }
399   }
400 
401   /**
402    * @deprecated Use
403    * {@link #decompress(byte[], int, int, int, int, int, int, int)} instead.
404    */
405   @SuppressWarnings("checkstyle:JavadocMethod")
406   @Deprecated
decompress(byte[] dstBuf, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)407   public void decompress(byte[] dstBuf, int desiredWidth, int pitch,
408                          int desiredHeight, int pixelFormat, int flags)
409                          throws TJException {
410     decompress(dstBuf, 0, 0, desiredWidth, pitch, desiredHeight, pixelFormat,
411                flags);
412   }
413 
414   /**
415    * Decompress the JPEG source image associated with this decompressor
416    * instance and return a buffer containing the decompressed image.
417    *
418    * @param desiredWidth see
419    * {@link #decompress(byte[], int, int, int, int, int, int, int)}
420    * for description
421    *
422    * @param pitch see
423    * {@link #decompress(byte[], int, int, int, int, int, int, int)}
424    * for description
425    *
426    * @param desiredHeight see
427    * {@link #decompress(byte[], int, int, int, int, int, int, int)}
428    * for description
429    *
430    * @param pixelFormat pixel format of the decompressed image (one of
431    * {@link TJ#PF_RGB TJ.PF_*})
432    *
433    * @param flags the bitwise OR of one or more of
434    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
435    *
436    * @return a buffer containing the decompressed image.
437    */
decompress(int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)438   public byte[] decompress(int desiredWidth, int pitch, int desiredHeight,
439                            int pixelFormat, int flags) throws TJException {
440     if (pitch < 0 ||
441         (yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
442         pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
443       throw new IllegalArgumentException("Invalid argument in decompress()");
444     int pixelSize = TJ.getPixelSize(pixelFormat);
445     int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
446     int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
447     if (pitch == 0)
448       pitch = scaledWidth * pixelSize;
449     byte[] buf = new byte[pitch * scaledHeight];
450     decompress(buf, desiredWidth, pitch, desiredHeight, pixelFormat, flags);
451     return buf;
452   }
453 
454   /**
455    * Decompress the JPEG source image associated with this decompressor
456    * instance into a YUV planar image and store it in the given
457    * <code>YUVImage</code> instance.  This method performs JPEG decompression
458    * but leaves out the color conversion step, so a planar YUV image is
459    * generated instead of an RGB or grayscale image.  This method cannot be
460    * used to decompress JPEG source images with the CMYK or YCCK colorspace.
461    * <p>
462    * NOTE: The YUV planar output image is fully recoverable if this method
463    * throws a non-fatal {@link TJException} (unless
464    * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
465    *
466    * @param dstImage {@link YUVImage} instance that will receive the YUV planar
467    * image.  The level of subsampling specified in this <code>YUVImage</code>
468    * instance must match that of the JPEG image, and the width and height
469    * specified in the <code>YUVImage</code> instance must match one of the
470    * scaled image sizes that TurboJPEG is capable of generating from the JPEG
471    * source image.
472    *
473    * @param flags the bitwise OR of one or more of
474    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
475    */
decompressToYUV(YUVImage dstImage, int flags)476   public void decompressToYUV(YUVImage dstImage, int flags)
477                               throws TJException {
478     if (jpegBuf == null)
479       throw new IllegalStateException(NO_ASSOC_ERROR);
480     if (dstImage == null || flags < 0)
481       throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
482     int scaledWidth = getScaledWidth(dstImage.getWidth(),
483                                      dstImage.getHeight());
484     int scaledHeight = getScaledHeight(dstImage.getWidth(),
485                                        dstImage.getHeight());
486     if (scaledWidth != dstImage.getWidth() ||
487         scaledHeight != dstImage.getHeight())
488       throw new IllegalArgumentException("YUVImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
489     if (jpegSubsamp != dstImage.getSubsamp())
490       throw new IllegalArgumentException("YUVImage subsampling level does not match that of the JPEG image");
491 
492     decompressToYUV(jpegBuf, jpegBufSize, dstImage.getPlanes(),
493                     dstImage.getOffsets(), dstImage.getWidth(),
494                     dstImage.getStrides(), dstImage.getHeight(), flags);
495   }
496 
497   /**
498    * @deprecated Use {@link #decompressToYUV(YUVImage, int)} instead.
499    */
500   @SuppressWarnings("checkstyle:JavadocMethod")
501   @Deprecated
decompressToYUV(byte[] dstBuf, int flags)502   public void decompressToYUV(byte[] dstBuf, int flags) throws TJException {
503     YUVImage dstYUVImage = new YUVImage(dstBuf, jpegWidth, 4, jpegHeight,
504                                         jpegSubsamp);
505     decompressToYUV(dstYUVImage, flags);
506   }
507 
508   /**
509    * Decompress the JPEG source image associated with this decompressor
510    * instance into a set of Y, U (Cb), and V (Cr) image planes and return a
511    * <code>YUVImage</code> instance containing the decompressed image planes.
512    * This method performs JPEG decompression but leaves out the color
513    * conversion step, so a planar YUV image is generated instead of an RGB or
514    * grayscale image.  This method cannot be used to decompress JPEG source
515    * images with the CMYK or YCCK colorspace.
516    *
517    * @param desiredWidth desired width (in pixels) of the YUV image.  If the
518    * desired image dimensions are different than the dimensions of the JPEG
519    * image being decompressed, then TurboJPEG will use scaling in the JPEG
520    * decompressor to generate the largest possible image that will fit within
521    * the desired dimensions.  Setting this to 0 is the same as setting it to
522    * the width of the JPEG image (in other words, the width will not be
523    * considered when determining the scaled image size.)
524    *
525    * @param strides an array of integers, each specifying the number of bytes
526    * per line in the corresponding plane of the output image.  Setting the
527    * stride for any plane to 0 is the same as setting it to the scaled
528    * component width of the plane.  If <tt>strides</tt> is NULL, then the
529    * strides for all planes will be set to their respective scaled component
530    * widths.  You can adjust the strides in order to add an arbitrary amount of
531    * line padding to each plane.
532    *
533    * @param desiredHeight desired height (in pixels) of the YUV image.  If the
534    * desired image dimensions are different than the dimensions of the JPEG
535    * image being decompressed, then TurboJPEG will use scaling in the JPEG
536    * decompressor to generate the largest possible image that will fit within
537    * the desired dimensions.  Setting this to 0 is the same as setting it to
538    * the height of the JPEG image (in other words, the height will not be
539    * considered when determining the scaled image size.)
540    *
541    * @param flags the bitwise OR of one or more of
542    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
543    *
544    * @return a YUV planar image.
545    */
decompressToYUV(int desiredWidth, int[] strides, int desiredHeight, int flags)546   public YUVImage decompressToYUV(int desiredWidth, int[] strides,
547                                   int desiredHeight,
548                                   int flags) throws TJException {
549     if (flags < 0)
550       throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
551     if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
552       throw new IllegalStateException(NO_ASSOC_ERROR);
553     if (jpegSubsamp >= TJ.NUMSAMP)
554       throw new IllegalStateException("JPEG header information is invalid");
555     if (yuvImage != null)
556       throw new IllegalStateException("Source image is the wrong type");
557 
558     int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
559     int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
560     YUVImage dstYUVImage = new YUVImage(scaledWidth, null, scaledHeight,
561                                         jpegSubsamp);
562     decompressToYUV(dstYUVImage, flags);
563     return dstYUVImage;
564   }
565 
566   /**
567    * Decompress the JPEG source image associated with this decompressor
568    * instance into a unified YUV planar image buffer and return a
569    * <code>YUVImage</code> instance containing the decompressed image.  This
570    * method performs JPEG decompression but leaves out the color conversion
571    * step, so a planar YUV image is generated instead of an RGB or grayscale
572    * image.  This method cannot be used to decompress JPEG source images with
573    * the CMYK or YCCK colorspace.
574    *
575    * @param desiredWidth desired width (in pixels) of the YUV image.  If the
576    * desired image dimensions are different than the dimensions of the JPEG
577    * image being decompressed, then TurboJPEG will use scaling in the JPEG
578    * decompressor to generate the largest possible image that will fit within
579    * the desired dimensions.  Setting this to 0 is the same as setting it to
580    * the width of the JPEG image (in other words, the width will not be
581    * considered when determining the scaled image size.)
582    *
583    * @param pad the width of each line in each plane of the YUV image will be
584    * padded to the nearest multiple of this number of bytes (must be a power of
585    * 2.)
586    *
587    * @param desiredHeight desired height (in pixels) of the YUV image.  If the
588    * desired image dimensions are different than the dimensions of the JPEG
589    * image being decompressed, then TurboJPEG will use scaling in the JPEG
590    * decompressor to generate the largest possible image that will fit within
591    * the desired dimensions.  Setting this to 0 is the same as setting it to
592    * the height of the JPEG image (in other words, the height will not be
593    * considered when determining the scaled image size.)
594    *
595    * @param flags the bitwise OR of one or more of
596    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
597    *
598    * @return a YUV planar image.
599    */
decompressToYUV(int desiredWidth, int pad, int desiredHeight, int flags)600   public YUVImage decompressToYUV(int desiredWidth, int pad, int desiredHeight,
601                                   int flags) throws TJException {
602     if (flags < 0)
603       throw new IllegalArgumentException("Invalid argument in decompressToYUV()");
604     if (jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
605       throw new IllegalStateException(NO_ASSOC_ERROR);
606     if (jpegSubsamp >= TJ.NUMSAMP)
607       throw new IllegalStateException("JPEG header information is invalid");
608     if (yuvImage != null)
609       throw new IllegalStateException("Source image is the wrong type");
610 
611     int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
612     int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
613     YUVImage dstYUVImage = new YUVImage(scaledWidth, pad, scaledHeight,
614                                         jpegSubsamp);
615     decompressToYUV(dstYUVImage, flags);
616     return dstYUVImage;
617   }
618 
619   /**
620    * @deprecated Use {@link #decompressToYUV(int, int, int, int)} instead.
621    */
622   @SuppressWarnings("checkstyle:JavadocMethod")
623   @Deprecated
decompressToYUV(int flags)624   public byte[] decompressToYUV(int flags) throws TJException {
625     YUVImage dstYUVImage = new YUVImage(jpegWidth, 4, jpegHeight, jpegSubsamp);
626     decompressToYUV(dstYUVImage, flags);
627     return dstYUVImage.getBuf();
628   }
629 
630   /**
631    * Decompress the JPEG source image or decode the YUV source image associated
632    * with this decompressor instance and output a grayscale, RGB, or CMYK image
633    * to the given destination buffer.
634    * <p>
635    * NOTE: The output image is fully recoverable if this method throws a
636    * non-fatal {@link TJException} (unless
637    * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
638    *
639    * @param dstBuf buffer that will receive the decompressed/decoded image.
640    * If the source image is a JPEG image, then this buffer should normally be
641    * <code>stride * scaledHeight</code> pixels in size, where
642    * <code>scaledHeight</code> can be determined by calling <code>
643    * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight)
644    * </code> with one of the scaling factors returned from {@link
645    * TJ#getScalingFactors} or by calling {@link #getScaledHeight}.  If the
646    * source image is a YUV image, then this buffer should normally be
647    * <code>stride * height</code> pixels in size, where <code>height</code> is
648    * the height of the YUV image.  However, the buffer may also be larger than
649    * the dimensions of the JPEG image, in which case the <code>x</code>,
650    * <code>y</code>, and <code>stride</code> parameters can be used to specify
651    * the region into which the source image should be decompressed.
652    *
653    * @param x x offset (in pixels) of the region in the destination image into
654    * which the source image should be decompressed/decoded
655    *
656    * @param y y offset (in pixels) of the region in the destination image into
657    * which the source image should be decompressed/decoded
658    *
659    * @param desiredWidth If the source image is a JPEG image, then this
660    * specifies the desired width (in pixels) of the decompressed image (or
661    * image region.)  If the desired destination image dimensions are different
662    * than the source image dimensions, then TurboJPEG will use scaling in the
663    * JPEG decompressor to generate the largest possible image that will fit
664    * within the desired dimensions.  Setting this to 0 is the same as setting
665    * it to the width of the JPEG image (in other words, the width will not be
666    * considered when determining the scaled image size.)  This parameter is
667    * ignored if the source image is a YUV image.
668    *
669    * @param stride pixels per line of the destination image.  Normally, this
670    * should be set to <code>scaledWidth</code>, but you can use this to, for
671    * instance, decompress the JPEG image into a region of a larger image.
672    * NOTE: if the source image is a JPEG image, then <code>scaledWidth</code>
673    * can be determined by calling <code>
674    * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth)
675    * </code> or by calling {@link #getScaledWidth}.  If the source image is a
676    * YUV image, then <code>scaledWidth</code> is the width of the YUV image.
677    * Setting this parameter to 0 is the equivalent of setting it to
678    * <code>scaledWidth</code>.
679    *
680    * @param desiredHeight If the source image is a JPEG image, then this
681    * specifies the desired height (in pixels) of the decompressed image (or
682    * image region.)  If the desired destination image dimensions are different
683    * than the source image dimensions, then TurboJPEG will use scaling in the
684    * JPEG decompressor to generate the largest possible image that will fit
685    * within the desired dimensions.  Setting this to 0 is the same as setting
686    * it to the height of the JPEG image (in other words, the height will not be
687    * considered when determining the scaled image size.)  This parameter is
688    * ignored if the source image is a YUV image.
689    *
690    * @param pixelFormat pixel format of the decompressed image (one of
691    * {@link TJ#PF_RGB TJ.PF_*})
692    *
693    * @param flags the bitwise OR of one or more of
694    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
695    */
decompress(int[] dstBuf, int x, int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat, int flags)696   public void decompress(int[] dstBuf, int x, int y, int desiredWidth,
697                          int stride, int desiredHeight, int pixelFormat,
698                          int flags) throws TJException {
699     if (jpegBuf == null && yuvImage == null)
700       throw new IllegalStateException(NO_ASSOC_ERROR);
701     if (dstBuf == null || x < 0 || y < 0 || stride < 0 ||
702         (yuvImage != null && (desiredWidth < 0 || desiredHeight < 0)) ||
703         pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
704       throw new IllegalArgumentException("Invalid argument in decompress()");
705     if (yuvImage != null)
706       decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
707                 yuvImage.getStrides(), yuvImage.getSubsamp(), dstBuf, x, y,
708                 yuvImage.getWidth(), stride, yuvImage.getHeight(), pixelFormat,
709                 flags);
710     else
711       decompress(jpegBuf, jpegBufSize, dstBuf, x, y, desiredWidth, stride,
712                  desiredHeight, pixelFormat, flags);
713   }
714 
715   /**
716    * Decompress the JPEG source image or decode the YUV source image associated
717    * with this decompressor instance and output a decompressed/decoded image to
718    * the given <code>BufferedImage</code> instance.
719    * <p>
720    * NOTE: The output image is fully recoverable if this method throws a
721    * non-fatal {@link TJException} (unless
722    * {@link TJ#FLAG_STOPONWARNING TJ.FLAG_STOPONWARNING} is specified.)
723    *
724    * @param dstImage a <code>BufferedImage</code> instance that will receive
725    * the decompressed/decoded image.  If the source image is a JPEG image, then
726    * the width and height of the <code>BufferedImage</code> instance must match
727    * one of the scaled image sizes that TurboJPEG is capable of generating from
728    * the JPEG image.  If the source image is a YUV image, then the width and
729    * height of the <code>BufferedImage</code> instance must match the width and
730    * height of the YUV image.
731    *
732    * @param flags the bitwise OR of one or more of
733    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
734    */
decompress(BufferedImage dstImage, int flags)735   public void decompress(BufferedImage dstImage, int flags)
736                          throws TJException {
737     if (dstImage == null || flags < 0)
738       throw new IllegalArgumentException("Invalid argument in decompress()");
739     int desiredWidth = dstImage.getWidth();
740     int desiredHeight = dstImage.getHeight();
741     int scaledWidth, scaledHeight;
742 
743     if (yuvImage != null) {
744       if (desiredWidth != yuvImage.getWidth() ||
745           desiredHeight != yuvImage.getHeight())
746         throw new IllegalArgumentException("BufferedImage dimensions do not match the dimensions of the source image.");
747       scaledWidth = yuvImage.getWidth();
748       scaledHeight = yuvImage.getHeight();
749     } else {
750       scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
751       scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
752       if (scaledWidth != desiredWidth || scaledHeight != desiredHeight)
753         throw new IllegalArgumentException("BufferedImage dimensions do not match one of the scaled image sizes that TurboJPEG is capable of generating.");
754     }
755     int pixelFormat;  boolean intPixels = false;
756     if (byteOrder == null)
757       byteOrder = ByteOrder.nativeOrder();
758     switch (dstImage.getType()) {
759     case BufferedImage.TYPE_3BYTE_BGR:
760       pixelFormat = TJ.PF_BGR;  break;
761     case BufferedImage.TYPE_4BYTE_ABGR:
762     case BufferedImage.TYPE_4BYTE_ABGR_PRE:
763       pixelFormat = TJ.PF_XBGR;  break;
764     case BufferedImage.TYPE_BYTE_GRAY:
765       pixelFormat = TJ.PF_GRAY;  break;
766     case BufferedImage.TYPE_INT_BGR:
767       if (byteOrder == ByteOrder.BIG_ENDIAN)
768         pixelFormat = TJ.PF_XBGR;
769       else
770         pixelFormat = TJ.PF_RGBX;
771       intPixels = true;  break;
772     case BufferedImage.TYPE_INT_RGB:
773       if (byteOrder == ByteOrder.BIG_ENDIAN)
774         pixelFormat = TJ.PF_XRGB;
775       else
776         pixelFormat = TJ.PF_BGRX;
777       intPixels = true;  break;
778     case BufferedImage.TYPE_INT_ARGB:
779     case BufferedImage.TYPE_INT_ARGB_PRE:
780       if (byteOrder == ByteOrder.BIG_ENDIAN)
781         pixelFormat = TJ.PF_ARGB;
782       else
783         pixelFormat = TJ.PF_BGRA;
784       intPixels = true;  break;
785     default:
786       throw new IllegalArgumentException("Unsupported BufferedImage format");
787     }
788     WritableRaster wr = dstImage.getRaster();
789     if (intPixels) {
790       SinglePixelPackedSampleModel sm =
791         (SinglePixelPackedSampleModel)dstImage.getSampleModel();
792       int stride = sm.getScanlineStride();
793       DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
794       int[] buf = db.getData();
795       if (yuvImage != null)
796         decodeYUV(yuvImage.getPlanes(), yuvImage.getOffsets(),
797                   yuvImage.getStrides(), yuvImage.getSubsamp(), buf, 0, 0,
798                   yuvImage.getWidth(), stride, yuvImage.getHeight(),
799                   pixelFormat, flags);
800       else {
801         if (jpegBuf == null)
802           throw new IllegalStateException(NO_ASSOC_ERROR);
803         decompress(jpegBuf, jpegBufSize, buf, 0, 0, scaledWidth, stride,
804                    scaledHeight, pixelFormat, flags);
805       }
806     } else {
807       ComponentSampleModel sm =
808         (ComponentSampleModel)dstImage.getSampleModel();
809       int pixelSize = sm.getPixelStride();
810       if (pixelSize != TJ.getPixelSize(pixelFormat))
811         throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage");
812       int pitch = sm.getScanlineStride();
813       DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
814       byte[] buf = db.getData();
815       decompress(buf, 0, 0, scaledWidth, pitch, scaledHeight, pixelFormat,
816                  flags);
817     }
818   }
819 
820   /**
821    * Decompress the JPEG source image or decode the YUV source image associated
822    * with this decompressor instance and return a <code>BufferedImage</code>
823    * instance containing the decompressed/decoded image.
824    *
825    * @param desiredWidth see
826    * {@link #decompress(byte[], int, int, int, int, int, int, int)} for
827    * description
828    *
829    * @param desiredHeight see
830    * {@link #decompress(byte[], int, int, int, int, int, int, int)} for
831    * description
832    *
833    * @param bufferedImageType the image type of the <code>BufferedImage</code>
834    * instance that will be created (for instance,
835    * <code>BufferedImage.TYPE_INT_RGB</code>)
836    *
837    * @param flags the bitwise OR of one or more of
838    * {@link TJ#FLAG_BOTTOMUP TJ.FLAG_*}
839    *
840    * @return a <code>BufferedImage</code> instance containing the
841    * decompressed/decoded image.
842    */
decompress(int desiredWidth, int desiredHeight, int bufferedImageType, int flags)843   public BufferedImage decompress(int desiredWidth, int desiredHeight,
844                                   int bufferedImageType, int flags)
845                                   throws TJException {
846     if ((yuvImage == null && (desiredWidth < 0 || desiredHeight < 0)) ||
847         flags < 0)
848       throw new IllegalArgumentException("Invalid argument in decompress()");
849     int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
850     int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
851     BufferedImage img = new BufferedImage(scaledWidth, scaledHeight,
852                                           bufferedImageType);
853     decompress(img, flags);
854     return img;
855   }
856 
857   /**
858    * Free the native structures associated with this decompressor instance.
859    */
860   @Override
close()861   public void close() throws TJException {
862     if (handle != 0)
863       destroy();
864   }
865 
866   @SuppressWarnings("checkstyle:DesignForExtension")
867   @Override
finalize()868   protected void finalize() throws Throwable {
869     try {
870       close();
871     } catch (TJException e) {
872     } finally {
873       super.finalize();
874     }
875   };
876 
init()877   private native void init() throws TJException;
878 
destroy()879   private native void destroy() throws TJException;
880 
decompressHeader(byte[] srcBuf, int size)881   private native void decompressHeader(byte[] srcBuf, int size)
882     throws TJException;
883 
884   @Deprecated
decompress(byte[] srcBuf, int size, byte[] dstBuf, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)885   private native void decompress(byte[] srcBuf, int size, byte[] dstBuf,
886     int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)
887     throws TJException;
888 
decompress(byte[] srcBuf, int size, byte[] dstBuf, int x, int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)889   private native void decompress(byte[] srcBuf, int size, byte[] dstBuf, int x,
890     int y, int desiredWidth, int pitch, int desiredHeight, int pixelFormat,
891     int flags) throws TJException;
892 
893   @Deprecated
decompress(byte[] srcBuf, int size, int[] dstBuf, int desiredWidth, int stride, int desiredHeight, int pixelFormat, int flags)894   private native void decompress(byte[] srcBuf, int size, int[] dstBuf,
895     int desiredWidth, int stride, int desiredHeight, int pixelFormat,
896     int flags) throws TJException;
897 
decompress(byte[] srcBuf, int size, int[] dstBuf, int x, int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat, int flags)898   private native void decompress(byte[] srcBuf, int size, int[] dstBuf, int x,
899     int y, int desiredWidth, int stride, int desiredHeight, int pixelFormat,
900     int flags) throws TJException;
901 
902   @Deprecated
decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf, int flags)903   private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf,
904     int flags) throws TJException;
905 
decompressToYUV(byte[] srcBuf, int size, byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides, int desiredheight, int flags)906   private native void decompressToYUV(byte[] srcBuf, int size,
907     byte[][] dstPlanes, int[] dstOffsets, int desiredWidth, int[] dstStrides,
908     int desiredheight, int flags) throws TJException;
909 
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)910   private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets,
911     int[] srcStrides, int subsamp, byte[] dstBuf, int x, int y, int width,
912     int pitch, int height, int pixelFormat, int flags) throws TJException;
913 
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)914   private native void decodeYUV(byte[][] srcPlanes, int[] srcOffsets,
915     int[] srcStrides, int subsamp, int[] dstBuf, int x, int y, int width,
916     int stride, int height, int pixelFormat, int flags) throws TJException;
917 
918   static {
TJLoader.load()919     TJLoader.load();
920   }
921 
922   protected long handle = 0;
923   protected byte[] jpegBuf = null;
924   protected int jpegBufSize = 0;
925   protected YUVImage yuvImage = null;
926   protected int jpegWidth = 0;
927   protected int jpegHeight = 0;
928   protected int jpegSubsamp = -1;
929   protected int jpegColorspace = -1;
930   private ByteOrder byteOrder = null;
931 }
932