7859886988c4e0a8c3b1d95cb0451fe653ffa2f7
[platform/upstream/libjpeg-turbo.git] / java / TJExample.java
1 /*
2  * Copyright (C)2011-2012, 2014-2015, 2017-2018 D. R. Commander.
3  *                                              All Rights Reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * - Redistributions of source code must retain the above copyright notice,
9  *   this list of conditions and the following disclaimer.
10  * - Redistributions in binary form must reproduce the above copyright notice,
11  *   this list of conditions and the following disclaimer in the documentation
12  *   and/or other materials provided with the distribution.
13  * - Neither the name of the libjpeg-turbo Project nor the names of its
14  *   contributors may be used to endorse or promote products derived from this
15  *   software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 /*
31  * This program demonstrates how to compress, decompress, and transform JPEG
32  * images using the TurboJPEG Java API
33  */
34
35 import java.io.*;
36 import java.awt.*;
37 import java.awt.image.*;
38 import java.nio.*;
39 import javax.imageio.*;
40 import javax.swing.*;
41 import org.libjpegturbo.turbojpeg.*;
42
43
44 @SuppressWarnings("checkstyle:JavadocType")
45 class TJExample implements TJCustomFilter {
46
47   static final String CLASS_NAME =
48     new TJExample().getClass().getName();
49
50   static final int DEFAULT_SUBSAMP = TJ.SAMP_444;
51   static final int DEFAULT_QUALITY = 95;
52
53
54   static final String[] SUBSAMP_NAME = {
55     "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1"
56   };
57
58   static final String[] COLORSPACE_NAME = {
59     "RGB", "YCbCr", "GRAY", "CMYK", "YCCK"
60   };
61
62
63   /* DCT filter example.  This produces a negative of the image. */
64
65   @SuppressWarnings("checkstyle:JavadocMethod")
66   public void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion,
67                            Rectangle planeRegion, int componentIndex,
68                            int transformIndex, TJTransform transform)
69                            throws TJException {
70     for (int i = 0; i < bufferRegion.width * bufferRegion.height; i++) {
71       coeffBuffer.put(i, (short)(-coeffBuffer.get(i)));
72     }
73   }
74
75
76   static void usage() throws Exception {
77     System.out.println("\nUSAGE: java [Java options] " + CLASS_NAME +
78                        " <Input image> <Output image> [options]\n");
79
80     System.out.println("Input and output images can be in any image format that the Java Image I/O");
81     System.out.println("extensions understand.  If either filename ends in a .jpg extension, then");
82     System.out.println("the TurboJPEG API will be used to compress or decompress the image.\n");
83
84     System.out.println("Compression Options (used if the output image is a JPEG image)");
85     System.out.println("--------------------------------------------------------------\n");
86
87     System.out.println("-subsamp <444|422|420|gray> = Apply this level of chrominance subsampling when");
88     System.out.println("     compressing the output image.  The default is to use the same level of");
89     System.out.println("     subsampling as in the input image, if the input image is also a JPEG");
90     System.out.println("     image, or to use grayscale if the input image is a grayscale non-JPEG");
91     System.out.println("     image, or to use " +
92                        SUBSAMP_NAME[DEFAULT_SUBSAMP] +
93                        " subsampling otherwise.\n");
94
95     System.out.println("-q <1-100> = Compress the output image with this JPEG quality level");
96     System.out.println("     (default = " + DEFAULT_QUALITY + ").\n");
97
98     System.out.println("Decompression Options (used if the input image is a JPEG image)");
99     System.out.println("---------------------------------------------------------------\n");
100
101     System.out.println("-scale M/N = Scale the input image by a factor of M/N when decompressing it.");
102     System.out.print("(M/N = ");
103     for (int i = 0; i < SCALING_FACTORS.length; i++) {
104       System.out.print(SCALING_FACTORS[i].getNum() + "/" +
105                        SCALING_FACTORS[i].getDenom());
106       if (SCALING_FACTORS.length == 2 && i != SCALING_FACTORS.length - 1)
107         System.out.print(" or ");
108       else if (SCALING_FACTORS.length > 2) {
109         if (i != SCALING_FACTORS.length - 1)
110           System.out.print(", ");
111         if (i == SCALING_FACTORS.length - 2)
112           System.out.print("or ");
113       }
114     }
115     System.out.println(")\n");
116
117     System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =");
118     System.out.println("     Perform one of these lossless transform operations on the input image");
119     System.out.println("     prior to decompressing it (these options are mutually exclusive.)\n");
120
121     System.out.println("-grayscale = Perform lossless grayscale conversion on the input image prior");
122     System.out.println("     to decompressing it (can be combined with the other transform operations");
123     System.out.println("     above.)\n");
124
125     System.out.println("-crop WxH+X+Y = Perform lossless cropping on the input image prior to");
126     System.out.println("     decompressing it.  X and Y specify the upper left corner of the cropping");
127     System.out.println("     region, and W and H specify the width and height of the cropping region.");
128     System.out.println("     X and Y must be evenly divible by the MCU block size (8x8 if the input");
129     System.out.println("     image was compressed using no subsampling or grayscale, 16x8 if it was");
130     System.out.println("     compressed using 4:2:2 subsampling, or 16x16 if it was compressed using");
131     System.out.println("     4:2:0 subsampling.)\n");
132
133     System.out.println("General Options");
134     System.out.println("---------------\n");
135
136     System.out.println("-display = Display output image (Output filename need not be specified in this");
137     System.out.println("     case.)\n");
138
139     System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in");
140     System.out.println("     the underlying codec.\n");
141
142     System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying");
143     System.out.println("     codec.\n");
144
145     System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the");
146     System.out.println("     underlying codec.\n");
147
148     System.exit(1);
149   }
150
151
152   public static void main(String[] argv) {
153
154     try {
155
156       TJScalingFactor scalingFactor = new TJScalingFactor(1, 1);
157       int outSubsamp = -1, outQual = -1;
158       TJTransform xform = new TJTransform();
159       boolean display = false;
160       int flags = 0;
161       int width, height;
162       String inFormat = "jpg", outFormat = "jpg";
163       BufferedImage img = null;
164       byte[] imgBuf = null;
165
166       if (argv.length < 2)
167         usage();
168
169       if (argv[1].substring(0, 2).equalsIgnoreCase("-d"))
170         display = true;
171
172       /* Parse arguments. */
173       for (int i = 2; i < argv.length; i++) {
174         if (argv[i].length() < 2)
175           continue;
176         else if (argv[i].length() > 2 &&
177                  argv[i].substring(0, 3).equalsIgnoreCase("-sc") &&
178                  i < argv.length - 1) {
179           int match = 0;
180           String[] scaleArg = argv[++i].split("/");
181           if (scaleArg.length == 2) {
182             TJScalingFactor tempsf =
183               new TJScalingFactor(Integer.parseInt(scaleArg[0]),
184                                   Integer.parseInt(scaleArg[1]));
185             for (int j = 0; j < SCALING_FACTORS.length; j++) {
186               if (tempsf.equals(SCALING_FACTORS[j])) {
187                 scalingFactor = SCALING_FACTORS[j];
188                 match = 1;
189                 break;
190               }
191             }
192           }
193           if (match != 1)
194             usage();
195         } else if (argv[i].length() > 2 &&
196                    argv[i].substring(0, 3).equalsIgnoreCase("-su") &&
197                    i < argv.length - 1) {
198           i++;
199           if (argv[i].substring(0, 1).equalsIgnoreCase("g"))
200             outSubsamp = TJ.SAMP_GRAY;
201           else if (argv[i].equals("444"))
202             outSubsamp = TJ.SAMP_444;
203           else if (argv[i].equals("422"))
204             outSubsamp = TJ.SAMP_422;
205           else if (argv[i].equals("420"))
206             outSubsamp = TJ.SAMP_420;
207           else
208             usage();
209         } else if (argv[i].substring(0, 2).equalsIgnoreCase("-q") &&
210                    i < argv.length - 1) {
211           outQual = Integer.parseInt(argv[++i]);
212           if (outQual < 1 || outQual > 100)
213             usage();
214         } else if (argv[i].substring(0, 2).equalsIgnoreCase("-g"))
215           xform.options |= TJTransform.OPT_GRAY;
216         else if (argv[i].equalsIgnoreCase("-hflip"))
217           xform.op = TJTransform.OP_HFLIP;
218         else if (argv[i].equalsIgnoreCase("-vflip"))
219           xform.op = TJTransform.OP_VFLIP;
220         else if (argv[i].equalsIgnoreCase("-transpose"))
221           xform.op = TJTransform.OP_TRANSPOSE;
222         else if (argv[i].equalsIgnoreCase("-transverse"))
223           xform.op = TJTransform.OP_TRANSVERSE;
224         else if (argv[i].equalsIgnoreCase("-rot90"))
225           xform.op = TJTransform.OP_ROT90;
226         else if (argv[i].equalsIgnoreCase("-rot180"))
227           xform.op = TJTransform.OP_ROT180;
228         else if (argv[i].equalsIgnoreCase("-rot270"))
229           xform.op = TJTransform.OP_ROT270;
230         else if (argv[i].equalsIgnoreCase("-custom"))
231           xform.cf = new TJExample();
232         else if (argv[i].length() > 2 &&
233                  argv[i].substring(0, 2).equalsIgnoreCase("-c") &&
234                  i < argv.length - 1) {
235           String[] cropArg = argv[++i].split("[x\\+]");
236           if (cropArg.length != 4)
237             usage();
238           xform.width = Integer.parseInt(cropArg[0]);
239           xform.height = Integer.parseInt(cropArg[1]);
240           xform.x = Integer.parseInt(cropArg[2]);
241           xform.y = Integer.parseInt(cropArg[3]);
242           if (xform.x < 0 || xform.y < 0 || xform.width < 1 ||
243               xform.height < 1)
244             usage();
245           xform.options |= TJTransform.OPT_CROP;
246         } else if (argv[i].substring(0, 2).equalsIgnoreCase("-d"))
247           display = true;
248         else if (argv[i].equalsIgnoreCase("-fastupsample")) {
249           System.out.println("Using fast upsampling code");
250           flags |= TJ.FLAG_FASTUPSAMPLE;
251         } else if (argv[i].equalsIgnoreCase("-fastdct")) {
252           System.out.println("Using fastest DCT/IDCT algorithm");
253           flags |= TJ.FLAG_FASTDCT;
254         } else if (argv[i].equalsIgnoreCase("-accuratedct")) {
255           System.out.println("Using most accurate DCT/IDCT algorithm");
256           flags |= TJ.FLAG_ACCURATEDCT;
257         } else usage();
258       }
259
260       /* Determine input and output image formats based on file extensions. */
261       String[] inFileTokens = argv[0].split("\\.");
262       if (inFileTokens.length > 1)
263         inFormat = inFileTokens[inFileTokens.length - 1];
264       String[] outFileTokens;
265       if (display)
266         outFormat = "bmp";
267       else {
268         outFileTokens = argv[1].split("\\.");
269         if (outFileTokens.length > 1)
270           outFormat = outFileTokens[outFileTokens.length - 1];
271       }
272
273       if (inFormat.equalsIgnoreCase("jpg")) {
274         /* Input image is a JPEG image.  Decompress and/or transform it. */
275         boolean doTransform = (xform.op != TJTransform.OP_NONE ||
276                                xform.options != 0 || xform.cf != null);
277
278         /* Read the JPEG file into memory. */
279         File jpegFile = new File(argv[0]);
280         FileInputStream fis = new FileInputStream(jpegFile);
281         int jpegSize = fis.available();
282         if (jpegSize < 1) {
283           System.out.println("Input file contains no data");
284           System.exit(1);
285         }
286         byte[] jpegBuf = new byte[jpegSize];
287         fis.read(jpegBuf);
288         fis.close();
289
290         TJDecompressor tjd;
291         if (doTransform) {
292           /* Transform it. */
293           TJTransformer tjt = new TJTransformer(jpegBuf);
294           TJTransform[] xforms = new TJTransform[1];
295           xforms[0] = xform;
296           xforms[0].options |= TJTransform.OPT_TRIM;
297           TJDecompressor[] tjds = tjt.transform(xforms, 0);
298           tjd = tjds[0];
299           tjt.close();
300         } else
301           tjd = new TJDecompressor(jpegBuf);
302
303         width = tjd.getWidth();
304         height = tjd.getHeight();
305         int inSubsamp = tjd.getSubsamp();
306         int inColorspace = tjd.getColorspace();
307
308         System.out.println((doTransform ? "Transformed" : "Input") +
309                            " Image (jpg):  " + width + " x " + height +
310                            " pixels, " + SUBSAMP_NAME[inSubsamp] +
311                            " subsampling, " + COLORSPACE_NAME[inColorspace]);
312
313         if (outFormat.equalsIgnoreCase("jpg") && doTransform &&
314             scalingFactor.isOne() && outSubsamp < 0 && outQual < 0) {
315           /* Input image has been transformed, and no re-compression options
316              have been selected.  Write the transformed image to disk and
317              exit. */
318           File outFile = new File(argv[1]);
319           FileOutputStream fos = new FileOutputStream(outFile);
320           fos.write(tjd.getJPEGBuf(), 0, tjd.getJPEGSize());
321           fos.close();
322           System.exit(0);
323         }
324
325         /* Scaling and/or a non-JPEG output image format and/or compression
326            options have been selected, so we need to decompress the
327            input/transformed image. */
328         width = scalingFactor.getScaled(width);
329         height = scalingFactor.getScaled(height);
330         if (outSubsamp < 0)
331           outSubsamp = inSubsamp;
332
333         if (!outFormat.equalsIgnoreCase("jpg"))
334           img = tjd.decompress(width, height, BufferedImage.TYPE_INT_RGB,
335                                flags);
336         else
337           imgBuf = tjd.decompress(width, 0, height, TJ.PF_BGRX, flags);
338         tjd.close();
339       } else {
340         /* Input image is not a JPEG image.  Load it into memory. */
341         img = ImageIO.read(new File(argv[0]));
342         if (img == null)
343           throw new Exception("Input image type not supported.");
344         width = img.getWidth();
345         height = img.getHeight();
346         if (outSubsamp < 0) {
347           if (img.getType() == BufferedImage.TYPE_BYTE_GRAY)
348             outSubsamp = TJ.SAMP_GRAY;
349           else
350             outSubsamp = DEFAULT_SUBSAMP;
351         }
352         System.out.println("Input Image:  " + width + " x " + height +
353                            " pixels");
354       }
355       System.gc();
356       if (!display)
357         System.out.print("Output Image (" + outFormat + "):  " + width +
358                          " x " + height + " pixels");
359
360       if (display) {
361         /* Display the uncompressed image */
362         ImageIcon icon = new ImageIcon(img);
363         JLabel label = new JLabel(icon, JLabel.CENTER);
364         JOptionPane.showMessageDialog(null, label, "Output Image",
365                                       JOptionPane.PLAIN_MESSAGE);
366       } else if (outFormat.equalsIgnoreCase("jpg")) {
367         /* Output image format is JPEG.  Compress the uncompressed image. */
368         if (outQual < 0)
369           outQual = DEFAULT_QUALITY;
370         System.out.println(", " + SUBSAMP_NAME[outSubsamp] +
371                            " subsampling, quality = " + outQual);
372
373         TJCompressor tjc = new TJCompressor();
374         tjc.setSubsamp(outSubsamp);
375         tjc.setJPEGQuality(outQual);
376         if (img != null)
377           tjc.setSourceImage(img, 0, 0, 0, 0);
378         else
379           tjc.setSourceImage(imgBuf, 0, 0, width, 0, height, TJ.PF_BGRX);
380         byte[] jpegBuf = tjc.compress(flags);
381         int jpegSize = tjc.getCompressedSize();
382         tjc.close();
383
384         /* Write the JPEG image to disk. */
385         File outFile = new File(argv[1]);
386         FileOutputStream fos = new FileOutputStream(outFile);
387         fos.write(jpegBuf, 0, jpegSize);
388         fos.close();
389       } else {
390         /* Output image format is not JPEG.  Save the uncompressed image
391            directly to disk. */
392         System.out.print("\n");
393         File outFile = new File(argv[1]);
394         ImageIO.write(img, outFormat, outFile);
395       }
396
397     } catch (Exception e) {
398       e.printStackTrace();
399       System.exit(-1);
400     }
401   }
402
403   static final TJScalingFactor[] SCALING_FACTORS =
404     TJ.getScalingFactors();
405 };