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
30package org.libjpegturbo.turbojpeg;
31
32import java.awt.image.*;
33import java.nio.*;
34import java.io.*;
35
36/**
37 * TurboJPEG decompressor
38 */
39public 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   */
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   */
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   */
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  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   */
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
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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   */
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
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   */
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   */
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
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   */
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   */
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
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   */
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   */
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   */
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
840  public void close() throws TJException {
841    if (handle != 0)
842      destroy();
843  }
844
845  @Override
846  protected void finalize() throws Throwable {
847    try {
848      close();
849    } catch(TJException e) {
850    } finally {
851      super.finalize();
852    }
853  };
854
855  private native void init() throws TJException;
856
857  private native void destroy() throws TJException;
858
859  private native void decompressHeader(byte[] srcBuf, int size)
860    throws TJException;
861
862  @Deprecated
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
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
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
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
881  private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf,
882    int flags) throws TJException;
883
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
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
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 {
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