TJDecompressor.java revision 67ce3b2352fe1f7511edbfed74ec6960e41e97dc
1/*
2 * Copyright (C)2011 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
29package org.libjpegturbo.turbojpeg;
30
31import java.awt.image.*;
32import java.nio.*;
33
34/**
35 * TurboJPEG decompressor
36 */
37public class TJDecompressor {
38
39  private final static String NO_ASSOC_ERROR =
40    "No JPEG image is associated with this instance";
41
42  /**
43   * Create a TurboJPEG decompresssor instance.
44   */
45  public TJDecompressor() throws Exception {
46    init();
47  }
48
49  /**
50   * Create a TurboJPEG decompressor instance and associate the JPEG image
51   * 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)
55   */
56  public TJDecompressor(byte[] jpegImage) throws Exception {
57    init();
58    setJPEGImage(jpegImage, jpegImage.length);
59  }
60
61  /**
62   * Create a TurboJPEG decompressor instance and associate the JPEG image
63   * of length <code>imageSize</code> bytes stored in <code>jpegImage</code>
64   * with the newly-created instance.
65   *
66   * @param jpegImage JPEG image buffer
67   *
68   * @param imageSize size of the JPEG image (in bytes)
69   */
70  public TJDecompressor(byte[] jpegImage, int imageSize) throws Exception {
71    init();
72    setJPEGImage(jpegImage, imageSize);
73  }
74
75  /**
76   * Associate the JPEG image of length <code>imageSize</code> bytes stored in
77   * <code>jpegImage</code> with this decompressor instance.  This image will
78   * be used as the source image for subsequent decompress operations.
79   *
80   * @param jpegImage JPEG image buffer
81   *
82   * @param imageSize size of the JPEG image (in bytes)
83   */
84  public void setJPEGImage(byte[] jpegImage, int imageSize) throws Exception {
85    if(jpegImage == null || imageSize < 1)
86      throw new Exception("Invalid argument in setJPEGImage()");
87    jpegBuf = jpegImage;
88    jpegBufSize = imageSize;
89    decompressHeader(jpegBuf, jpegBufSize);
90  }
91
92  /**
93   * Returns the width of the JPEG image associated with this decompressor
94   * instance.
95   *
96   * @return the width of the JPEG image associated with this decompressor
97   * instance
98   */
99  public int getWidth() throws Exception {
100    if(jpegWidth < 1) throw new Exception(NO_ASSOC_ERROR);
101    return jpegWidth;
102  }
103
104  /**
105   * Returns the height of the JPEG image associated with this decompressor
106   * instance.
107   *
108   * @return the height of the JPEG image associated with this decompressor
109   * instance
110   */
111  public int getHeight() throws Exception {
112    if(jpegHeight < 1) throw new Exception(NO_ASSOC_ERROR);
113    return jpegHeight;
114  }
115
116  /**
117   * Returns the level of chrominance subsampling used in the JPEG image
118   * associated with this decompressor instance.
119   *
120   * @return the level of chrominance subsampling used in the JPEG image
121   * associated with this decompressor instance
122   */
123  public int getSubsamp() throws Exception {
124    if(jpegSubsamp < 0) throw new Exception(NO_ASSOC_ERROR);
125    if(jpegSubsamp >= TJ.NUMSAMP)
126      throw new Exception("JPEG header information is invalid");
127    return jpegSubsamp;
128  }
129
130  /**
131   * Returns the JPEG image buffer associated with this decompressor instance.
132   *
133   * @return the JPEG image buffer associated with this decompressor instance
134   */
135  public byte[] getJPEGBuf() throws Exception {
136    if(jpegBuf == null) throw new Exception(NO_ASSOC_ERROR);
137    return jpegBuf;
138  }
139
140  /**
141   * Returns the size of the JPEG image (in bytes) associated with this
142   * decompressor instance.
143   *
144   * @return the size of the JPEG image (in bytes) associated with this
145   * decompressor instance
146   */
147  public int getJPEGSize() throws Exception {
148    if(jpegBufSize < 1) throw new Exception(NO_ASSOC_ERROR);
149    return jpegBufSize;
150  }
151
152
153  /**
154   * Returns the width of the largest scaled down image that the TurboJPEG
155   * decompressor can generate without exceeding the desired image width and
156   * height.
157   *
158   * @param desiredWidth desired width (in pixels) of the decompressed image.
159   * Setting this to 0 is the same as setting it to the width of the JPEG image
160   * (in other words, the width will not be considered when determining the
161   * scaled image size.)
162   *
163   * @param desiredHeight desired height (in pixels) of the decompressed image.
164   * Setting this to 0 is the same as setting it to the height of the JPEG
165   * image (in other words, the height will not be considered when determining
166   * the scaled image size.)
167   *
168   * @return the width of the largest scaled down image that the TurboJPEG
169   * decompressor can generate without exceeding the desired image width and
170   * height
171   */
172  public int getScaledWidth(int desiredWidth, int desiredHeight)
173    throws Exception {
174    if(jpegWidth < 1 || jpegHeight < 1)
175      throw new Exception(NO_ASSOC_ERROR);
176    if(desiredWidth < 0 || desiredHeight < 0)
177      throw new Exception("Invalid argument in getScaledWidth()");
178    TJScalingFactor sf[] = TJ.getScalingFactors();
179    if(desiredWidth == 0) desiredWidth = jpegWidth;
180    if(desiredHeight == 0) desiredHeight = jpegHeight;
181    int scaledWidth = jpegWidth, scaledHeight = jpegHeight;
182    for(int i = 0; i < sf.length; i++) {
183      scaledWidth = sf[i].getScaled(jpegWidth);
184      scaledHeight = sf[i].getScaled(jpegHeight);
185      if(scaledWidth <= desiredWidth && scaledHeight <= desiredHeight)
186        break;
187    }
188    if(scaledWidth > desiredWidth || scaledHeight > desiredHeight)
189      throw new Exception("Could not scale down to desired image dimensions");
190    return scaledWidth;
191  }
192
193  /**
194   * Returns the height of the largest scaled down image that the TurboJPEG
195   * decompressor can generate without exceeding the desired image width and
196   * height.
197   *
198   * @param desiredWidth desired width (in pixels) of the decompressed image.
199   * Setting this to 0 is the same as setting it to the width of the JPEG image
200   * (in other words, the width will not be considered when determining the
201   * scaled image size.)
202   *
203   * @param desiredHeight desired height (in pixels) of the decompressed image.
204   * Setting this to 0 is the same as setting it to the height of the JPEG
205   * image (in other words, the height will not be considered when determining
206   * the scaled image size.)
207   *
208   * @return the height of the largest scaled down image that the TurboJPEG
209   * decompressor can generate without exceeding the desired image width and
210   * height
211   */
212  public int getScaledHeight(int desiredWidth, int desiredHeight)
213    throws Exception {
214    if(jpegWidth < 1 || jpegHeight < 1)
215      throw new Exception(NO_ASSOC_ERROR);
216    if(desiredWidth < 0 || desiredHeight < 0)
217      throw new Exception("Invalid argument in getScaledHeight()");
218    TJScalingFactor sf[] = TJ.getScalingFactors();
219    if(desiredWidth == 0) desiredWidth = jpegWidth;
220    if(desiredHeight == 0) desiredHeight = jpegHeight;
221    int scaledWidth = jpegWidth, scaledHeight = jpegHeight;
222    for(int i = 0; i < sf.length; i++) {
223      scaledWidth = sf[i].getScaled(jpegWidth);
224      scaledHeight = sf[i].getScaled(jpegHeight);
225      if(scaledWidth <= desiredWidth && scaledHeight <= desiredHeight)
226        break;
227    }
228    if(scaledWidth > desiredWidth || scaledHeight > desiredHeight)
229      throw new Exception("Could not scale down to desired image dimensions");
230    return scaledHeight;
231  }
232
233  /**
234   * Decompress the JPEG source image associated with this decompressor
235   * instance and output a decompressed image to the given destination buffer.
236   *
237   * @param dstBuf buffer that will receive the decompressed image.  This
238   * buffer should normally be <code>pitch * scaledHeight</code> bytes in size,
239   * where <code>scaledHeight</code> can be determined by calling <code>
240   * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegHeight)
241   * </code> with one of the scaling factors returned from {@link
242   * TJ#getScalingFactors} or by calling {@link #getScaledHeight}.
243   *
244   * @param desiredWidth desired width (in pixels) of the decompressed image.
245   * If the desired image dimensions are smaller than the dimensions of the
246   * JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG
247   * decompressor to generate the largest possible image that will fit within
248   * the desired dimensions.  Setting this to 0 is the same as setting it to
249   * the width of the JPEG image (in other words, the width will not be
250   * considered when determining the scaled image size.)
251   *
252   * @param pitch bytes per line of the destination image.  Normally, this
253   * should be set to <code>scaledWidth * TJ.pixelSize(pixelFormat)</code> if
254   * the decompressed image is unpadded, but you can use this to, for instance,
255   * pad each line of the decompressed image to a 4-byte boundary.  NOTE:
256   * <code>scaledWidth</code> can be determined by calling <code>
257   * scalingFactor.{@link TJScalingFactor#getScaled getScaled}(jpegWidth)
258   * </code> or by calling {@link #getScaledWidth}.  Setting this parameter to
259   * 0 is the equivalent of setting it to <code>scaledWidth *
260   * TJ.pixelSize(pixelFormat)</code>.
261   *
262   * @param desiredHeight desired height (in pixels) of the decompressed image.
263   * If the desired image dimensions are smaller than the dimensions of the
264   * JPEG image being decompressed, then TurboJPEG will use scaling in the JPEG
265   * decompressor to generate the largest possible image that will fit within
266   * the desired dimensions.  Setting this to 0 is the same as setting it to
267   * the height of the JPEG image (in other words, the height will not be
268   * considered when determining the scaled image size.)
269   *
270   * @param pixelFormat pixel format of the decompressed image (one of
271   * {@link TJ TJ.PF_*})
272   *
273   * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
274   */
275  public void decompress(byte[] dstBuf, int desiredWidth, int pitch,
276    int desiredHeight, int pixelFormat, int flags) throws Exception {
277    if(jpegBuf == null) throw new Exception(NO_ASSOC_ERROR);
278    if(dstBuf == null || desiredWidth < 0 || pitch < 0 || desiredHeight < 0
279      || pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
280      throw new Exception("Invalid argument in decompress()");
281    decompress(jpegBuf, jpegBufSize, dstBuf, desiredWidth, pitch,
282      desiredHeight, pixelFormat, flags);
283  }
284
285  /**
286   * Decompress the JPEG source image associated with this decompressor
287   * instance and return a buffer containing the decompressed image.
288   *
289   * @param desiredWidth see
290   * {@link #decompress(byte[], int, int, int, int, int)} for description
291   *
292   * @param pitch see
293   * {@link #decompress(byte[], int, int, int, int, int)} for description
294   *
295   * @param desiredHeight see
296   * {@link #decompress(byte[], int, int, int, int, int)} for description
297   *
298   * @param pixelFormat pixel format of the decompressed image (one of
299   * {@link TJ TJ.PF_*})
300   *
301   * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
302   *
303   * @return a buffer containing the decompressed image
304   */
305  public byte[] decompress(int desiredWidth, int pitch, int desiredHeight,
306    int pixelFormat, int flags) throws Exception {
307    if(desiredWidth < 0 || pitch < 0 || desiredHeight < 0
308      || pixelFormat < 0 || pixelFormat >= TJ.NUMPF || flags < 0)
309      throw new Exception("Invalid argument in decompress()");
310    int pixelSize = TJ.getPixelSize(pixelFormat);
311    int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
312    int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
313    if(pitch == 0) pitch = scaledWidth * pixelSize;
314    byte[] buf = new byte[pitch * scaledHeight];
315    decompress(buf, desiredWidth, pitch, desiredHeight, pixelFormat, flags);
316    return buf;
317  }
318
319  /**
320   * Decompress the JPEG source image associated with this decompressor
321   * instance and output a YUV planar image to the given destination buffer.
322   * This method performs JPEG decompression but leaves out the color
323   * conversion step, so a planar YUV image is generated instead of an RGB
324   * image.  The padding of the planes in this image is the same as the images
325   * generated by {@link TJCompressor#encodeYUV(byte[], int)}.  Note that, if
326   * the width or height of the image is not an even multiple of the MCU block
327   * size (see {@link TJ#getMCUWidth} and {@link TJ#getMCUHeight}), then an
328   * intermediate buffer copy will be performed within TurboJPEG.
329   *
330   * @param dstBuf buffer that will receive the YUV planar image.  Use
331   * {@link TJ#bufSizeYUV} to determine the appropriate size for this buffer
332   * based on the image width, height, and level of chrominance subsampling.
333   *
334   * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
335   */
336  public void decompressToYUV(byte[] dstBuf, int flags) throws Exception {
337    if(jpegBuf == null) throw new Exception(NO_ASSOC_ERROR);
338    if(dstBuf == null || flags < 0)
339      throw new Exception("Invalid argument in decompressToYUV()");
340    decompressToYUV(jpegBuf, jpegBufSize, dstBuf, flags);
341  }
342
343
344  /**
345   * Decompress the JPEG source image associated with this decompressor
346   * instance and return a buffer containing a YUV planar image.  See {@link
347   * #decompressToYUV(byte[], int)} for more detail.
348   *
349   * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
350   *
351   * @return a buffer containing a YUV planar image
352   */
353  public byte[] decompressToYUV(int flags) throws Exception {
354    if(flags < 0)
355      throw new Exception("Invalid argument in decompressToYUV()");
356    if(jpegWidth < 1 || jpegHeight < 1 || jpegSubsamp < 0)
357      throw new Exception(NO_ASSOC_ERROR);
358    if(jpegSubsamp >= TJ.NUMSAMP)
359      throw new Exception("JPEG header information is invalid");
360    byte[] buf = new byte[TJ.bufSizeYUV(jpegWidth, jpegHeight, jpegSubsamp)];
361    decompressToYUV(buf, flags);
362    return buf;
363  }
364
365  /**
366   * Decompress the JPEG source image associated with this decompressor
367   * instance and output a decompressed image to the given
368   * <code>BufferedImage</code> instance.
369   *
370   * @param dstImage a <code>BufferedImage</code> instance that will receive
371   * the decompressed image
372   *
373   * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
374   */
375  public void decompress(BufferedImage dstImage, int flags) throws Exception {
376    if(dstImage == null || flags < 0)
377      throw new Exception("Invalid argument in decompress()");
378    int desiredWidth = dstImage.getWidth();
379    int desiredHeight = dstImage.getHeight();
380    int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
381    int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
382    if(scaledWidth != desiredWidth || scaledHeight != desiredHeight)
383      throw new Exception("BufferedImage dimensions do not match a scaled image size that TurboJPEG is capable of generating.");
384    int pixelFormat;  boolean intPixels = false;
385    if(byteOrder == null)
386      byteOrder = ByteOrder.nativeOrder();
387    switch(dstImage.getType()) {
388      case BufferedImage.TYPE_3BYTE_BGR:
389        pixelFormat = TJ.PF_BGR;  break;
390      case BufferedImage.TYPE_4BYTE_ABGR:
391      case BufferedImage.TYPE_4BYTE_ABGR_PRE:
392        pixelFormat = TJ.PF_XBGR;  break;
393      case BufferedImage.TYPE_BYTE_GRAY:
394        pixelFormat = TJ.PF_GRAY;  break;
395      case BufferedImage.TYPE_INT_BGR:
396        if(byteOrder == ByteOrder.BIG_ENDIAN)
397          pixelFormat = TJ.PF_XBGR;
398        else
399          pixelFormat = TJ.PF_RGBX;
400        intPixels = true;  break;
401      case BufferedImage.TYPE_INT_RGB:
402        if(byteOrder == ByteOrder.BIG_ENDIAN)
403          pixelFormat = TJ.PF_XRGB;
404        else
405          pixelFormat = TJ.PF_BGRX;
406        intPixels = true;  break;
407      case BufferedImage.TYPE_INT_ARGB:
408      case BufferedImage.TYPE_INT_ARGB_PRE:
409        if(byteOrder == ByteOrder.BIG_ENDIAN)
410          pixelFormat = TJ.PF_ARGB;
411        else
412          pixelFormat = TJ.PF_BGRA;
413        intPixels = true;  break;
414      default:
415        throw new Exception("Unsupported BufferedImage format");
416    }
417    WritableRaster wr = dstImage.getRaster();
418    if(intPixels) {
419      SinglePixelPackedSampleModel sm =
420        (SinglePixelPackedSampleModel)dstImage.getSampleModel();
421      int pitch = sm.getScanlineStride();
422      DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
423      int[] buf = db.getData();
424      if(jpegBuf == null) throw new Exception(NO_ASSOC_ERROR);
425      decompress(jpegBuf, jpegBufSize, buf, scaledWidth, pitch, scaledHeight,
426        pixelFormat, flags);
427    }
428    else {
429      ComponentSampleModel sm =
430        (ComponentSampleModel)dstImage.getSampleModel();
431      int pixelSize = sm.getPixelStride();
432      if(pixelSize != TJ.getPixelSize(pixelFormat))
433        throw new Exception("Inconsistency between pixel format and pixel size in BufferedImage");
434      int pitch = sm.getScanlineStride();
435      DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
436      byte[] buf = db.getData();
437      decompress(buf, scaledWidth, pitch, scaledHeight, pixelFormat, flags);
438    }
439  }
440
441  /**
442   * Decompress the JPEG source image associated with this decompressor
443   * instance and return a <code>BufferedImage</code> instance containing the
444   * decompressed image.
445   *
446   * @param desiredWidth see
447   * {@link #decompress(byte[], int, int, int, int, int)} for description
448   *
449   * @param desiredHeight see
450   * {@link #decompress(byte[], int, int, int, int, int)} for description
451   *
452   * @param bufferedImageType the image type of the newly-created
453   * <code>BufferedImage</code> instance (for instance,
454   * <code>BufferedImage.TYPE_INT_RGB</code>)
455   *
456   * @param flags the bitwise OR of one or more of {@link TJ TJ.FLAG_*}
457   *
458   * @return a <code>BufferedImage</code> instance containing the
459   * decompressed image
460   */
461  public BufferedImage decompress(int desiredWidth, int desiredHeight,
462    int bufferedImageType, int flags) throws Exception {
463    if(desiredWidth < 0 || desiredHeight < 0 || flags < 0)
464      throw new Exception("Invalid argument in decompress()");
465    int scaledWidth = getScaledWidth(desiredWidth, desiredHeight);
466    int scaledHeight = getScaledHeight(desiredWidth, desiredHeight);
467    BufferedImage img = new BufferedImage(scaledWidth, scaledHeight,
468      bufferedImageType);
469    decompress(img, flags);
470    return img;
471  }
472
473  /**
474   * Free the native structures associated with this decompressor instance.
475   */
476  public void close() throws Exception {
477    destroy();
478  }
479
480  protected void finalize() throws Throwable {
481    try {
482      close();
483    }
484    catch(Exception e) {}
485    finally {
486      super.finalize();
487    }
488  };
489
490  private native void init() throws Exception;
491
492  private native void destroy() throws Exception;
493
494  private native void decompressHeader(byte[] srcBuf, int size)
495    throws Exception;
496
497  private native void decompress(byte[] srcBuf, int size, byte[] dstBuf,
498    int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)
499    throws Exception;
500
501  private native void decompress(byte[] srcBuf, int size, int[] dstBuf,
502    int desiredWidth, int pitch, int desiredHeight, int pixelFormat, int flags)
503    throws Exception;
504
505  private native void decompressToYUV(byte[] srcBuf, int size, byte[] dstBuf,
506    int flags)
507    throws Exception;
508
509  static {
510    TJLoader.load();
511  }
512
513  protected long handle = 0;
514  protected byte[] jpegBuf = null;
515  protected int jpegBufSize = 0;
516  protected int jpegWidth = 0;
517  protected int jpegHeight = 0;
518  protected int jpegSubsamp = -1;
519  private ByteOrder byteOrder = null;
520};
521