Imported Upstream version 3.0.1
[platform/upstream/libjpeg-turbo.git] / java / org / libjpegturbo / turbojpeg / TJCompressor.java
1 /*
2  * Copyright (C)2011-2015, 2018, 2020, 2022-2023 D. R. Commander.
3  *                                               All Rights Reserved.
4  * Copyright (C)2015 Viktor Szathmáry.  All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * - Redistributions of source code must retain the above copyright notice,
10  *   this list of conditions and the following disclaimer.
11  * - Redistributions in binary form must reproduce the above copyright notice,
12  *   this list of conditions and the following disclaimer in the documentation
13  *   and/or other materials provided with the distribution.
14  * - Neither the name of the libjpeg-turbo Project nor the names of its
15  *   contributors may be used to endorse or promote products derived from this
16  *   software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 package org.libjpegturbo.turbojpeg;
32
33 import java.awt.image.*;
34 import java.nio.*;
35 import java.io.*;
36
37 /**
38  * TurboJPEG compressor
39  */
40 public class TJCompressor implements Closeable {
41
42   /**
43    * Create a TurboJPEG compressor instance.
44    */
45   public TJCompressor() throws TJException {
46     init();
47   }
48
49   /**
50    * Create a TurboJPEG compressor instance and associate the 8-bit-per-sample
51    * packed-pixel source image stored in <code>srcImage</code> with the newly
52    * created instance.
53    *
54    * @param srcImage see {@link #setSourceImage} for description
55    *
56    * @param x see {@link #setSourceImage} for description
57    *
58    * @param y see {@link #setSourceImage} for description
59    *
60    * @param width see {@link #setSourceImage} for description
61    *
62    * @param pitch see {@link #setSourceImage} for description
63    *
64    * @param height see {@link #setSourceImage} for description
65    *
66    * @param pixelFormat pixel format of the source image (one of
67    * {@link TJ#PF_RGB TJ.PF_*})
68    */
69   public TJCompressor(byte[] srcImage, int x, int y, int width, int pitch,
70                       int height, int pixelFormat) throws TJException {
71     setSourceImage(srcImage, x, y, width, pitch, height, pixelFormat);
72   }
73
74   /**
75    * Create a TurboJPEG compressor instance and associate the 8-bit-per-sample
76    * packed-pixel source image stored in <code>srcImage</code> with the newly
77    * created instance.
78    *
79    * @param srcImage see
80    * {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
81    *
82    * @param x see
83    * {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
84    *
85    * @param y see
86    * {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
87    *
88    * @param width see
89    * {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
90    *
91    * @param height see
92    * {@link #setSourceImage(BufferedImage, int, int, int, int)} for description
93    */
94   public TJCompressor(BufferedImage srcImage, int x, int y, int width,
95                       int height) throws TJException {
96     setSourceImage(srcImage, x, y, width, height);
97   }
98
99   /**
100    * Associate an 8-bit-per-sample packed-pixel RGB, grayscale, or CMYK source
101    * image with this compressor instance.
102    *
103    * @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK
104    * source image to be compressed or encoded.  This buffer is not modified.
105    *
106    * @param x x offset (in pixels) of the region in the source image from which
107    * the JPEG or YUV image should be compressed/encoded
108    *
109    * @param y y offset (in pixels) of the region in the source image from which
110    * the JPEG or YUV image should be compressed/encoded
111    *
112    * @param width width (in pixels) of the region in the source image from
113    * which the JPEG or YUV image should be compressed/encoded
114    *
115    * @param pitch bytes per row in the source image.  Normally this should be
116    * <code>width * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>,
117    * if the source image is unpadded.  (Setting this parameter to 0 is the
118    * equivalent of setting it to <code>width *
119    * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>.)  However,
120    * you can also use this parameter to specify the row alignment/padding of
121    * the source image, to skip rows, or to compress/encode a JPEG or YUV image
122    * from a specific region of a larger source image.
123    *
124    * @param height height (in pixels) of the region in the source image from
125    * which the JPEG or YUV image should be compressed/encoded
126    *
127    * @param pixelFormat pixel format of the source image (one of
128    * {@link TJ#PF_RGB TJ.PF_*})
129    */
130   public void setSourceImage(byte[] srcImage, int x, int y, int width,
131                              int pitch, int height, int pixelFormat)
132                              throws TJException {
133     if (handle == 0) init();
134     if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 ||
135         pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
136       throw new IllegalArgumentException("Invalid argument in setSourceImage()");
137     srcBuf8 = srcImage;
138     srcWidth = width;
139     if (pitch == 0)
140       srcPitch = width * TJ.getPixelSize(pixelFormat);
141     else
142       srcPitch = pitch;
143     srcHeight = height;
144     srcPixelFormat = pixelFormat;
145     srcX = x;
146     srcY = y;
147     srcBuf12 = null;
148     srcBuf16 = null;
149     srcBufInt = null;
150     srcYUVImage = null;
151   }
152
153   /**
154    * Associate a 12-bit-per-sample packed-pixel RGB, grayscale, or CMYK source
155    * image with this compressor instance.  Note that 12-bit-per-sample
156    * packed-pixel source images can only be compressed into 12-bit-per-sample
157    * JPEG images.
158    *
159    * @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK
160    * source image to be compressed.  This buffer is not modified.
161    *
162    * @param x x offset (in pixels) of the region in the source image from which
163    * the JPEG image should be compressed
164    *
165    * @param y y offset (in pixels) of the region in the source image from which
166    * the JPEG image should be compressed
167    *
168    * @param width width (in pixels) of the region in the source image from
169    * which the JPEG image should be compressed
170    *
171    * @param pitch samples per row in the source image.  Normally this should be
172    * <code>width * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>,
173    * if the source image is unpadded.  (Setting this parameter to 0 is the
174    * equivalent of setting it to <code>width *
175    * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>.)  However,
176    * you can also use this parameter to specify the row alignment/padding of
177    * the source image, to skip rows, or to compress a JPEG image from a
178    * specific region of a larger source image.
179    *
180    * @param height height (in pixels) of the region in the source image from
181    * which the JPEG image should be compressed
182    *
183    * @param pixelFormat pixel format of the source image (one of
184    * {@link TJ#PF_RGB TJ.PF_*})
185    */
186   public void setSourceImage12(short[] srcImage, int x, int y, int width,
187                                int pitch, int height, int pixelFormat)
188                                throws TJException {
189     if (handle == 0) init();
190     if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 ||
191         pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
192       throw new IllegalArgumentException("Invalid argument in setSourceImage()");
193     srcBuf12 = srcImage;
194     srcWidth = width;
195     if (pitch == 0)
196       srcPitch = width * TJ.getPixelSize(pixelFormat);
197     else
198       srcPitch = pitch;
199     srcHeight = height;
200     srcPixelFormat = pixelFormat;
201     srcX = x;
202     srcY = y;
203     srcBuf8 = null;
204     srcBuf16 = null;
205     srcBufInt = null;
206     srcYUVImage = null;
207   }
208
209   /**
210    * Associate a 16-bit-per-sample packed-pixel RGB, grayscale, or CMYK source
211    * image with this compressor instance.  Note that 16-bit-per-sample
212    * packed-pixel source images can only be compressed into 16-bit-per-sample
213    * lossless JPEG images.
214    *
215    * @param srcImage buffer containing a packed-pixel RGB, grayscale, or CMYK
216    * source image to be compressed.  This buffer is not modified.
217    *
218    * @param x x offset (in pixels) of the region in the source image from which
219    * the JPEG image should be compressed
220    *
221    * @param y y offset (in pixels) of the region in the source image from which
222    * the JPEG image should be compressed
223    *
224    * @param width width (in pixels) of the region in the source image from
225    * which the JPEG image should be compressed
226    *
227    * @param pitch samples per row in the source image.  Normally this should be
228    * <code>width * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>,
229    * if the source image is unpadded.  (Setting this parameter to 0 is the
230    * equivalent of setting it to <code>width *
231    * {@link TJ#getPixelSize TJ.getPixelSize}(pixelFormat)</code>.)  However,
232    * you can also use this parameter to specify the row alignment/padding of
233    * the source image, to skip rows, or to compress a JPEG image from a
234    * specific region of a larger source image.
235    *
236    * @param height height (in pixels) of the region in the source image from
237    * which the JPEG image should be compressed
238    *
239    * @param pixelFormat pixel format of the source image (one of
240    * {@link TJ#PF_RGB TJ.PF_*})
241    */
242   public void setSourceImage16(short[] srcImage, int x, int y, int width,
243                                int pitch, int height, int pixelFormat)
244                                throws TJException {
245     if (handle == 0) init();
246     if (srcImage == null || x < 0 || y < 0 || width < 1 || height < 1 ||
247         pitch < 0 || pixelFormat < 0 || pixelFormat >= TJ.NUMPF)
248       throw new IllegalArgumentException("Invalid argument in setSourceImage()");
249     srcBuf16 = srcImage;
250     srcWidth = width;
251     if (pitch == 0)
252       srcPitch = width * TJ.getPixelSize(pixelFormat);
253     else
254       srcPitch = pitch;
255     srcHeight = height;
256     srcPixelFormat = pixelFormat;
257     srcX = x;
258     srcY = y;
259     srcBuf8 = null;
260     srcBuf12 = null;
261     srcBufInt = null;
262     srcYUVImage = null;
263   }
264
265   /**
266    * Associate an 8-bit-per-pixel packed-pixel RGB or grayscale source image
267    * with this compressor instance.
268    *
269    * @param srcImage a <code>BufferedImage</code> instance containing a
270    * packed-pixel RGB or grayscale source image to be compressed or encoded.
271    * This image is not modified.
272    *
273    * @param x x offset (in pixels) of the region in the source image from which
274    * the JPEG or YUV image should be compressed/encoded
275    *
276    * @param y y offset (in pixels) of the region in the source image from which
277    * the JPEG or YUV image should be compressed/encoded
278    *
279    * @param width width (in pixels) of the region in the source image from
280    * which the JPEG or YUV image should be compressed/encoded (0 = use the
281    * width of the source image)
282    *
283    * @param height height (in pixels) of the region in the source image from
284    * which the JPEG or YUV image should be compressed/encoded (0 = use the
285    * height of the source image)
286    */
287   public void setSourceImage(BufferedImage srcImage, int x, int y, int width,
288                              int height) throws TJException {
289     if (handle == 0) init();
290     if (srcImage == null || x < 0 || y < 0 || width < 0 || height < 0)
291       throw new IllegalArgumentException("Invalid argument in setSourceImage()");
292     srcX = x;
293     srcY = y;
294     srcWidth = (width == 0) ? srcImage.getWidth() : width;
295     srcHeight = (height == 0) ? srcImage.getHeight() : height;
296     if (x + width > srcImage.getWidth() || y + height > srcImage.getHeight())
297       throw new IllegalArgumentException("Compression region exceeds the bounds of the source image");
298
299     int pixelFormat;
300     boolean intPixels = false;
301     if (byteOrder == null)
302       byteOrder = ByteOrder.nativeOrder();
303     switch (srcImage.getType()) {
304     case BufferedImage.TYPE_3BYTE_BGR:
305       pixelFormat = TJ.PF_BGR;  break;
306     case BufferedImage.TYPE_4BYTE_ABGR:
307     case BufferedImage.TYPE_4BYTE_ABGR_PRE:
308       pixelFormat = TJ.PF_XBGR;  break;
309     case BufferedImage.TYPE_BYTE_GRAY:
310       pixelFormat = TJ.PF_GRAY;  break;
311     case BufferedImage.TYPE_INT_BGR:
312       if (byteOrder == ByteOrder.BIG_ENDIAN)
313         pixelFormat = TJ.PF_XBGR;
314       else
315         pixelFormat = TJ.PF_RGBX;
316       intPixels = true;  break;
317     case BufferedImage.TYPE_INT_RGB:
318     case BufferedImage.TYPE_INT_ARGB:
319     case BufferedImage.TYPE_INT_ARGB_PRE:
320       if (byteOrder == ByteOrder.BIG_ENDIAN)
321         pixelFormat = TJ.PF_XRGB;
322       else
323         pixelFormat = TJ.PF_BGRX;
324       intPixels = true;  break;
325     default:
326       throw new IllegalArgumentException("Unsupported BufferedImage format");
327     }
328     srcPixelFormat = pixelFormat;
329
330     WritableRaster wr = srcImage.getRaster();
331     if (intPixels) {
332       SinglePixelPackedSampleModel sm =
333         (SinglePixelPackedSampleModel)srcImage.getSampleModel();
334       srcStride = sm.getScanlineStride();
335       DataBufferInt db = (DataBufferInt)wr.getDataBuffer();
336       srcBufInt = db.getData();
337       srcBuf8 = null;
338     } else {
339       ComponentSampleModel sm =
340         (ComponentSampleModel)srcImage.getSampleModel();
341       int pixelSize = sm.getPixelStride();
342       if (pixelSize != TJ.getPixelSize(pixelFormat))
343         throw new IllegalArgumentException("Inconsistency between pixel format and pixel size in BufferedImage");
344       srcPitch = sm.getScanlineStride();
345       DataBufferByte db = (DataBufferByte)wr.getDataBuffer();
346       srcBuf8 = db.getData();
347       srcBufInt = null;
348     }
349     srcYUVImage = null;
350   }
351
352   /**
353    * Associate an 8-bit-per-sample planar YUV source image with this compressor
354    * instance.  This method sets {@link TJ#PARAM_SUBSAMP} to the chrominance
355    * subsampling level of the source image.
356    *
357    * @param srcImage planar YUV source image to be compressed.  This image is
358    * not modified.
359    */
360   public void setSourceImage(YUVImage srcImage) throws TJException {
361     if (handle == 0) init();
362     if (srcImage == null)
363       throw new IllegalArgumentException("Invalid argument in setSourceImage()");
364     srcYUVImage = srcImage;
365     set(TJ.PARAM_SUBSAMP, srcImage.getSubsamp());
366     srcBuf8 = null;
367     srcBufInt = null;
368   }
369
370   /**
371    * Set the value of a compression parameter.
372    *
373    * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*}
374    *
375    * @param value value of the compression parameter (refer to
376    * {@link TJ#PARAM_STOPONWARNING parameter documentation})
377    */
378   public native void set(int param, int value);
379
380   /**
381    * Get the value of a compression parameter.
382    *
383    * @param param one of {@link TJ#PARAM_STOPONWARNING TJ.PARAM_*}
384    *
385    * @return the value of the specified compression parameter, or -1 if the
386    * value is unknown.
387    */
388   public native int get(int param);
389
390   /**
391    * @deprecated Use
392    * <code>{@link #set set}({@link TJ#PARAM_SUBSAMP}, ...)</code> instead.
393    */
394   @SuppressWarnings("checkstyle:JavadocMethod")
395   @Deprecated
396   public void setSubsamp(int subsamp) {
397     if (subsamp < 0 || subsamp >= TJ.NUMSAMP)
398       throw new IllegalArgumentException("Invalid argument in setSubsamp()");
399     set(TJ.PARAM_SUBSAMP, subsamp);
400   }
401
402   /**
403    * @deprecated Use
404    * <code>{@link #set set}({@link TJ#PARAM_QUALITY}, ...)</code> instead.
405    */
406   @SuppressWarnings("checkstyle:JavadocMethod")
407   @Deprecated
408   public void setJPEGQuality(int quality) {
409     if (quality < 1 || quality > 100)
410       throw new IllegalArgumentException("Invalid argument in setJPEGQuality()");
411     set(TJ.PARAM_QUALITY, quality);
412   }
413
414   /**
415    * Compress the packed-pixel or planar YUV source image associated with this
416    * compressor instance and output a JPEG image to the given destination
417    * buffer.
418    *
419    * @param dstBuf buffer that will receive the JPEG image.  Use
420    * {@link TJ#bufSize TJ.bufSize()} to determine the maximum size for this
421    * buffer based on the source image's width and height and the desired level
422    * of chrominance subsampling (see {@link TJ#PARAM_SUBSAMP}.)
423    */
424   public void compress(byte[] dstBuf) throws TJException {
425     if (dstBuf == null)
426       throw new IllegalArgumentException("Invalid argument in compress()");
427
428     if (srcYUVImage != null) {
429       checkSubsampling();
430       if (get(TJ.PARAM_SUBSAMP) != srcYUVImage.getSubsamp())
431         throw new IllegalStateException("TJ.PARAM_SUBSAMP must match subsampling level of YUV image");
432       compressedSize = compressFromYUV8(srcYUVImage.getPlanes(),
433                                         srcYUVImage.getOffsets(),
434                                         srcYUVImage.getWidth(),
435                                         srcYUVImage.getStrides(),
436                                         srcYUVImage.getHeight(), dstBuf);
437     } else if (srcBuf8 != null)
438       compressedSize = compress8(srcBuf8, srcX, srcY, srcWidth, srcPitch,
439                                  srcHeight, srcPixelFormat, dstBuf);
440     else if (srcBuf12 != null)
441       compressedSize = compress12(srcBuf12, srcX, srcY, srcWidth, srcPitch,
442                                   srcHeight, srcPixelFormat, dstBuf);
443     else if (srcBuf16 != null)
444       compressedSize = compress16(srcBuf16, srcX, srcY, srcWidth, srcPitch,
445                                   srcHeight, srcPixelFormat, dstBuf);
446     else if (srcBufInt != null)
447       compressedSize = compress8(srcBufInt, srcX, srcY, srcWidth, srcStride,
448                                  srcHeight, srcPixelFormat, dstBuf);
449     else
450       throw new IllegalStateException("No source image is associated with this instance");
451   }
452
453   /**
454    * @deprecated Use {@link #set set()} and {@link #compress(byte[])} instead.
455    */
456   @SuppressWarnings("checkstyle:JavadocMethod")
457   @Deprecated
458   public void compress(byte[] dstBuf, int flags) throws TJException {
459     if (flags < 0)
460       throw new IllegalArgumentException("Invalid argument in compress()");
461     processFlags(flags);
462     compress(dstBuf);
463   }
464
465   /**
466    * Compress the packed-pixel or planar YUV source image associated with this
467    * compressor instance and return a buffer containing a JPEG image.
468    *
469    * @return a buffer containing a JPEG image.  The length of this buffer will
470    * not be equal to the size of the JPEG image.  Use
471    * {@link #getCompressedSize} to obtain the size of the JPEG image.
472    */
473   public byte[] compress() throws TJException {
474     byte[] buf;
475     if (srcYUVImage != null) {
476       buf = new byte[TJ.bufSize(srcYUVImage.getWidth(),
477                                 srcYUVImage.getHeight(),
478                                 srcYUVImage.getSubsamp())];
479     } else {
480       checkSubsampling();
481       int subsamp = get(TJ.PARAM_SUBSAMP);
482       buf = new byte[TJ.bufSize(srcWidth, srcHeight, subsamp)];
483     }
484     compress(buf);
485     return buf;
486   }
487
488   /**
489    * @deprecated Use {@link #set set()} and {@link #compress()} instead.
490    */
491   @SuppressWarnings("checkstyle:JavadocMethod")
492   @Deprecated
493   public byte[] compress(int flags) throws TJException {
494     processFlags(flags);
495     return compress();
496   }
497
498   /**
499    * Encode the 8-bit-per-sample packed-pixel source image associated with this
500    * compressor instance into an 8-bit-per-sample planar YUV image and store it
501    * in the given {@link YUVImage} instance.  This method performs color
502    * conversion (which is accelerated in the libjpeg-turbo implementation) but
503    * does not execute any of the other steps in the JPEG compression process.
504    * Encoding CMYK source images into YUV images is not supported.  This method
505    * sets {@link TJ#PARAM_SUBSAMP} to the chrominance subsampling level of the
506    * destination image.
507    *
508    * @param dstImage {@link YUVImage} instance that will receive the planar YUV
509    * image
510    */
511   public void encodeYUV(YUVImage dstImage) throws TJException {
512     if (dstImage == null)
513       throw new IllegalArgumentException("Invalid argument in encodeYUV()");
514     if (srcBuf8 == null && srcBufInt == null)
515       throw new IllegalStateException("No 8-bit-per-sample source image is associated with this instance");
516     if (srcYUVImage != null)
517       throw new IllegalStateException("Source image is not correct type");
518     if (srcWidth != dstImage.getWidth() || srcHeight != dstImage.getHeight())
519       throw new IllegalStateException("Destination image is the wrong size");
520     set(TJ.PARAM_SUBSAMP, dstImage.getSubsamp());
521
522     if (srcBufInt != null) {
523       encodeYUV8(srcBufInt, srcX, srcY, srcWidth, srcStride, srcHeight,
524                  srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(),
525                  dstImage.getStrides());
526     } else {
527       encodeYUV8(srcBuf8, srcX, srcY, srcWidth, srcPitch, srcHeight,
528                  srcPixelFormat, dstImage.getPlanes(), dstImage.getOffsets(),
529                  dstImage.getStrides());
530     }
531     compressedSize = 0;
532   }
533
534   /**
535    * @deprecated Use {@link #set set()} and {@link #encodeYUV(YUVImage)}
536    * instead.
537    */
538   @SuppressWarnings("checkstyle:JavadocMethod")
539   @Deprecated
540   public void encodeYUV(YUVImage dstImage, int flags) throws TJException {
541     if (flags < 0)
542       throw new IllegalArgumentException("Invalid argument in encodeYUV()");
543
544     processFlags(flags);
545     encodeYUV(dstImage);
546   }
547
548   /**
549    * Encode the 8-bit-per-sample packed-pixel source image associated with this
550    * compressor instance into an 8-bit-per-sample unified planar YUV image and
551    * return a {@link YUVImage} instance containing the encoded image.  This
552    * method performs color conversion (which is accelerated in the
553    * libjpeg-turbo implementation) but does not execute any of the other steps
554    * in the JPEG compression process.  Encoding CMYK source images into YUV
555    * images is not supported.
556    *
557    * @param align row alignment (in bytes) of the YUV image (must be a power of
558    * 2.)  Setting this parameter to n will cause each row in each plane of the
559    * YUV image to be padded to the nearest multiple of n bytes (1 = unpadded.)
560    *
561    * @return a {@link YUVImage} instance containing the unified planar YUV
562    * encoded image
563    */
564   public YUVImage encodeYUV(int align) throws TJException {
565     if (srcBuf8 == null && srcBufInt == null)
566       throw new IllegalStateException("No 8-bit-per-sample source image is associated with this instance");
567     checkSubsampling();
568     if (align < 1 || ((align & (align - 1)) != 0))
569       throw new IllegalStateException("Invalid argument in encodeYUV()");
570     YUVImage dstYUVImage = new YUVImage(srcWidth, align, srcHeight,
571                                         get(TJ.PARAM_SUBSAMP));
572     encodeYUV(dstYUVImage);
573     return dstYUVImage;
574   }
575
576   /**
577    * @deprecated Use {@link #set set()} and {@link #encodeYUV(int)} instead.
578    */
579   @SuppressWarnings("checkstyle:JavadocMethod")
580   @Deprecated
581   public YUVImage encodeYUV(int align, int flags) throws TJException {
582     processFlags(flags);
583     return encodeYUV(align);
584   }
585
586   /**
587    * Encode the 8-bit-per-sample packed-pixel source image associated with this
588    * compressor instance into separate 8-bit-per-sample Y, U (Cb), and V (Cr)
589    * image planes and return a {@link YUVImage} instance containing the encoded
590    * image planes.  This method performs color conversion (which is accelerated
591    * in the libjpeg-turbo implementation) but does not execute any of the other
592    * steps in the JPEG compression process.  Encoding CMYK source images into
593    * YUV images is not supported.
594    *
595    * @param strides an array of integers, each specifying the number of bytes
596    * per row in the corresponding plane of the YUV source image.  Setting the
597    * stride for any plane to 0 is the same as setting it to the plane width
598    * (see {@link YUVImage}.)  If <code>strides</code> is null, then the strides
599    * for all planes will be set to their respective plane widths.  You can
600    * adjust the strides in order to add an arbitrary amount of row padding to
601    * each plane.
602    *
603    * @return a {@link YUVImage} instance containing the encoded image planes
604    */
605   public YUVImage encodeYUV(int[] strides) throws TJException {
606     if (srcBuf8 == null && srcBufInt == null)
607       throw new IllegalStateException("No 8-bit-per-sample source image is associated with this instance");
608     checkSubsampling();
609     YUVImage dstYUVImage = new YUVImage(srcWidth, strides, srcHeight,
610                                         get(TJ.PARAM_SUBSAMP));
611     encodeYUV(dstYUVImage);
612     return dstYUVImage;
613   }
614
615   /**
616    * @deprecated Use {@link #set set()} and {@link #encodeYUV(int[])} instead.
617    */
618   @SuppressWarnings("checkstyle:JavadocMethod")
619   @Deprecated
620   public YUVImage encodeYUV(int[] strides, int flags) throws TJException {
621     processFlags(flags);
622     return encodeYUV(strides);
623   }
624
625   /**
626    * Returns the size of the image (in bytes) generated by the most recent
627    * compress operation.
628    *
629    * @return the size of the image (in bytes) generated by the most recent
630    * compress operation.
631    */
632   public int getCompressedSize() {
633     return compressedSize;
634   }
635
636   /**
637    * Free the native structures associated with this compressor instance.
638    */
639   @Override
640   public void close() throws TJException {
641     if (handle != 0)
642       destroy();
643   }
644
645   @SuppressWarnings("checkstyle:DesignForExtension")
646   @Override
647   protected void finalize() throws Throwable {
648     try {
649       close();
650     } catch (TJException e) {
651     } finally {
652       super.finalize();
653     }
654   };
655
656   @SuppressWarnings("deprecation")
657   private void processFlags(int flags) {
658     set(TJ.PARAM_BOTTOMUP, (flags & TJ.FLAG_BOTTOMUP) != 0 ? 1 : 0);
659
660     if (get(TJ.PARAM_QUALITY) >= 96 || (flags & TJ.FLAG_ACCURATEDCT) != 0)
661       set(TJ.PARAM_FASTDCT, 0);
662     else
663       set(TJ.PARAM_FASTDCT, 1);
664
665     set(TJ.PARAM_STOPONWARNING, (flags & TJ.FLAG_STOPONWARNING) != 0 ? 1 : 0);
666     set(TJ.PARAM_PROGRESSIVE, (flags & TJ.FLAG_PROGRESSIVE) != 0 ? 1 : 0);
667   }
668
669   private void checkSubsampling() {
670     if (get(TJ.PARAM_SUBSAMP) == TJ.SAMP_UNKNOWN)
671       throw new IllegalStateException("TJ.PARAM_SUBSAMP must be specified");
672   }
673
674   private native void init() throws TJException;
675
676   private native void destroy() throws TJException;
677
678   // JPEG size in bytes is returned
679   @SuppressWarnings("checkstyle:HiddenField")
680   private native int compress8(byte[] srcBuf, int x, int y, int width,
681     int pitch, int height, int pixelFormat, byte[] jpegBuf) throws TJException;
682
683   @SuppressWarnings("checkstyle:HiddenField")
684   private native int compress12(short[] srcBuf, int x, int y, int width,
685     int pitch, int height, int pixelFormat, byte[] jpegBuf) throws TJException;
686
687   @SuppressWarnings("checkstyle:HiddenField")
688   private native int compress16(short[] srcBuf, int x, int y, int width,
689     int pitch, int height, int pixelFormat, byte[] jpegBuf) throws TJException;
690
691   @SuppressWarnings("checkstyle:HiddenField")
692   private native int compress8(int[] srcBuf, int x, int y, int width,
693     int stride, int height, int pixelFormat, byte[] jpegBuf)
694     throws TJException;
695
696   @SuppressWarnings("checkstyle:HiddenField")
697   private native int compressFromYUV8(byte[][] srcPlanes, int[] srcOffsets,
698     int width, int[] srcStrides, int height, byte[] jpegBuf)
699     throws TJException;
700
701   @SuppressWarnings("checkstyle:HiddenField")
702   private native void encodeYUV8(byte[] srcBuf, int x, int y, int width,
703     int pitch, int height, int pixelFormat, byte[][] dstPlanes,
704     int[] dstOffsets, int[] dstStrides) throws TJException;
705
706   @SuppressWarnings("checkstyle:HiddenField")
707   private native void encodeYUV8(int[] srcBuf, int x, int y, int width,
708     int srcStride, int height, int pixelFormat, byte[][] dstPlanes,
709     int[] dstOffsets, int[] dstStrides) throws TJException;
710
711   /**
712    * @hidden
713    * Ugly hack alert.  It isn't straightforward to load 12-bit-per-sample and
714    * 16-bit-per-sample images using the ImageIO and BufferedImage classes, and
715    * ImageIO doesn't support PBMPLUS files anyhow.  This method accesses
716    * tj3LoadImage() through JNI and copies the pixel data between the C and
717    * Java heaps.  Currently it is undocumented and used only by TJBench.
718    */
719   @SuppressWarnings("checkstyle:JavadocMethod")
720   public native Object loadImage(int precision, String fileName, int[] width,
721                                  int align, int[] height, int[] pixelFormat)
722                                  throws TJException;
723
724   static {
725     TJLoader.load();
726   }
727
728   private long handle = 0;
729   private byte[] srcBuf8 = null;
730   private short[] srcBuf12 = null;
731   private short[] srcBuf16 = null;
732   private int[] srcBufInt = null;
733   private int srcWidth = 0;
734   private int srcHeight = 0;
735   private int srcX = -1;
736   private int srcY = -1;
737   private int srcPitch = 0;
738   private int srcStride = 0;
739   private int srcPixelFormat = -1;
740   private YUVImage srcYUVImage = null;
741   private int compressedSize = 0;
742   private ByteOrder byteOrder = null;
743 }