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