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