1/*
2 * Copyright (C)2011-2012, 2014-2015 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
29/*
30 * This program demonstrates how to compress and decompress JPEG files using
31 * the TurboJPEG JNI wrapper
32 */
33
34import java.io.*;
35import java.awt.*;
36import java.awt.image.*;
37import java.nio.*;
38import javax.imageio.*;
39import javax.swing.*;
40import org.libjpegturbo.turbojpeg.*;
41
42public class TJExample implements TJCustomFilter {
43
44  public static final String classname = new TJExample().getClass().getName();
45
46  private static void usage() throws Exception {
47    System.out.println("\nUSAGE: java " + classname + " <Input file> <Output file> [options]\n");
48    System.out.println("Input and output files can be any image format that the Java Image I/O");
49    System.out.println("extensions understand.  If either filename ends in a .jpg extension, then");
50    System.out.println("TurboJPEG will be used to compress or decompress the file.\n");
51    System.out.println("Options:\n");
52    System.out.println("-scale M/N = if the input image is a JPEG file, scale the width/height of the");
53    System.out.print("             output image by a factor of M/N (M/N = ");
54    for (int i = 0; i < sf.length; i++) {
55      System.out.print(sf[i].getNum() + "/" + sf[i].getDenom());
56      if (sf.length == 2 && i != sf.length - 1)
57        System.out.print(" or ");
58      else if (sf.length > 2) {
59        if (i != sf.length - 1)
60          System.out.print(", ");
61        if (i == sf.length - 2)
62          System.out.print("or ");
63      }
64    }
65    System.out.println(")\n");
66    System.out.println("-samp <444|422|420|gray> = If the output image is a JPEG file, this specifies");
67    System.out.println("                           the level of chrominance subsampling to use when");
68    System.out.println("                           recompressing it.  Default is to use the same level");
69    System.out.println("                           of subsampling as the input, if the input is a JPEG");
70    System.out.println("                           file, or 4:4:4 otherwise.\n");
71    System.out.println("-q <1-100> = If the output image is a JPEG file, this specifies the JPEG");
72    System.out.println("             quality to use when recompressing it (default = 95).\n");
73    System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =");
74    System.out.println("     If the input image is a JPEG file, perform the corresponding lossless");
75    System.out.println("     transform prior to decompression (these options are mutually exclusive)\n");
76    System.out.println("-grayscale = If the input image is a JPEG file, perform lossless grayscale");
77    System.out.println("     conversion prior to decompression (can be combined with the other");
78    System.out.println("     transforms above)\n");
79    System.out.println("-crop X,Y,WxH = If the input image is a JPEG file, perform lossless cropping");
80    System.out.println("     prior to decompression.  X,Y specifies the upper left corner of the");
81    System.out.println("     cropping region, and WxH specifies its width and height.  X,Y must be");
82    System.out.println("     evenly divible by the MCU block size (8x8 if the source image was");
83    System.out.println("     compressed using no subsampling or grayscale, or 16x8 for 4:2:2 or 16x16");
84    System.out.println("     for 4:2:0.)\n");
85    System.out.println("-display = Display output image (Output file need not be specified in this");
86    System.out.println("     case.)\n");
87    System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in");
88    System.out.println("     the underlying codec\n");
89    System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying");
90    System.out.println("     codec\n");
91    System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the");
92    System.out.println("     underlying codec\n");
93    System.exit(1);
94  }
95
96  private static final String[] sampName = {
97    "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0"
98  };
99
100  public static void main(String[] argv) {
101
102    BufferedImage img = null;
103    byte[] bmpBuf = null;
104    TJTransform xform = new TJTransform();
105    int flags = 0;
106
107    try {
108
109      sf = TJ.getScalingFactors();
110
111      if (argv.length < 2) {
112        usage();
113      }
114
115      TJScalingFactor scaleFactor = new TJScalingFactor(1, 1);
116      String inFormat = "jpg", outFormat = "jpg";
117      int outSubsamp = -1, outQual = 95;
118      boolean display = false;
119
120      if (argv.length > 1) {
121        for (int i = 1; i < argv.length; i++) {
122          if (argv[i].length() < 2)
123            continue;
124          if (argv[i].length() > 2 &&
125              argv[i].substring(0, 3).equalsIgnoreCase("-sc")) {
126            int match = 0;
127            if (i < argv.length - 1) {
128              String[] scaleArg = argv[++i].split("/");
129              if (scaleArg.length == 2) {
130                TJScalingFactor tempsf =
131                  new TJScalingFactor(Integer.parseInt(scaleArg[0]),
132                                      Integer.parseInt(scaleArg[1]));
133                for (int j = 0; j < sf.length; j++) {
134                  if (tempsf.equals(sf[j])) {
135                    scaleFactor = sf[j];
136                    match = 1;
137                    break;
138                  }
139                }
140              }
141            }
142            if (match != 1) usage();
143          }
144          if (argv[i].equalsIgnoreCase("-h") || argv[i].equalsIgnoreCase("-?"))
145            usage();
146          if (argv[i].length() > 2 &&
147              argv[i].substring(0, 3).equalsIgnoreCase("-sa")) {
148            if (i < argv.length - 1) {
149              i++;
150              if (argv[i].substring(0, 1).equalsIgnoreCase("g"))
151                outSubsamp = TJ.SAMP_GRAY;
152              else if (argv[i].equals("444"))
153                outSubsamp = TJ.SAMP_444;
154              else if (argv[i].equals("422"))
155                outSubsamp = TJ.SAMP_422;
156              else if (argv[i].equals("420"))
157                outSubsamp = TJ.SAMP_420;
158              else
159                usage();
160            } else
161              usage();
162          }
163          if (argv[i].substring(0, 2).equalsIgnoreCase("-q")) {
164            if (i < argv.length - 1) {
165              int qual = Integer.parseInt(argv[++i]);
166              if (qual >= 1 && qual <= 100)
167                outQual = qual;
168              else
169                usage();
170            } else
171              usage();
172          }
173          if (argv[i].substring(0, 2).equalsIgnoreCase("-g"))
174            xform.options |= TJTransform.OPT_GRAY;
175          if (argv[i].equalsIgnoreCase("-hflip"))
176            xform.op = TJTransform.OP_HFLIP;
177          if (argv[i].equalsIgnoreCase("-vflip"))
178            xform.op = TJTransform.OP_VFLIP;
179          if (argv[i].equalsIgnoreCase("-transpose"))
180            xform.op = TJTransform.OP_TRANSPOSE;
181          if (argv[i].equalsIgnoreCase("-transverse"))
182            xform.op = TJTransform.OP_TRANSVERSE;
183          if (argv[i].equalsIgnoreCase("-rot90"))
184            xform.op = TJTransform.OP_ROT90;
185          if (argv[i].equalsIgnoreCase("-rot180"))
186            xform.op = TJTransform.OP_ROT180;
187          if (argv[i].equalsIgnoreCase("-rot270"))
188            xform.op = TJTransform.OP_ROT270;
189          if (argv[i].equalsIgnoreCase("-custom"))
190            xform.cf = new TJExample();
191          else if (argv[i].length() > 2 &&
192                   argv[i].substring(0, 2).equalsIgnoreCase("-c")) {
193            if (i >= argv.length - 1)
194              usage();
195            String[] cropArg = argv[++i].split(",");
196            if (cropArg.length != 3)
197              usage();
198            String[] dimArg = cropArg[2].split("[xX]");
199            if (dimArg.length != 2)
200              usage();
201            int tempx = Integer.parseInt(cropArg[0]);
202            int tempy = Integer.parseInt(cropArg[1]);
203            int tempw = Integer.parseInt(dimArg[0]);
204            int temph = Integer.parseInt(dimArg[1]);
205            if (tempx < 0 || tempy < 0 || tempw < 0 || temph < 0)
206              usage();
207            xform.x = tempx;
208            xform.y = tempy;
209            xform.width = tempw;
210            xform.height = temph;
211            xform.options |= TJTransform.OPT_CROP;
212          }
213          if (argv[i].substring(0, 2).equalsIgnoreCase("-d"))
214            display = true;
215          if (argv[i].equalsIgnoreCase("-fastupsample")) {
216            System.out.println("Using fast upsampling code");
217            flags |= TJ.FLAG_FASTUPSAMPLE;
218          }
219          if (argv[i].equalsIgnoreCase("-fastdct")) {
220            System.out.println("Using fastest DCT/IDCT algorithm");
221            flags |= TJ.FLAG_FASTDCT;
222          }
223          if (argv[i].equalsIgnoreCase("-accuratedct")) {
224            System.out.println("Using most accurate DCT/IDCT algorithm");
225            flags |= TJ.FLAG_ACCURATEDCT;
226          }
227        }
228      }
229      String[] inFileTokens = argv[0].split("\\.");
230      if (inFileTokens.length > 1)
231        inFormat = inFileTokens[inFileTokens.length - 1];
232      String[] outFileTokens;
233      if (display)
234        outFormat = "bmp";
235      else {
236        outFileTokens = argv[1].split("\\.");
237        if (outFileTokens.length > 1)
238          outFormat = outFileTokens[outFileTokens.length - 1];
239      }
240
241      File file = new File(argv[0]);
242      int width, height;
243
244      if (inFormat.equalsIgnoreCase("jpg")) {
245        FileInputStream fis = new FileInputStream(file);
246        int inputSize = fis.available();
247        if (inputSize < 1) {
248          System.out.println("Input file contains no data");
249          System.exit(1);
250        }
251        byte[] inputBuf = new byte[inputSize];
252        fis.read(inputBuf);
253        fis.close();
254
255        TJDecompressor tjd;
256        if (xform.op != TJTransform.OP_NONE || xform.options != 0 ||
257            xform.cf != null) {
258          TJTransformer tjt = new TJTransformer(inputBuf);
259          TJTransform[] t = new TJTransform[1];
260          t[0] = xform;
261          t[0].options |= TJTransform.OPT_TRIM;
262          TJDecompressor[] tjdx = tjt.transform(t, 0);
263          tjd = tjdx[0];
264        } else
265          tjd = new TJDecompressor(inputBuf);
266
267        width = tjd.getWidth();
268        height = tjd.getHeight();
269        int inSubsamp = tjd.getSubsamp();
270        System.out.println("Source Image: " + width + " x " + height +
271                           " pixels, " + sampName[inSubsamp] + " subsampling");
272        if (outSubsamp < 0)
273          outSubsamp = inSubsamp;
274
275        if (outFormat.equalsIgnoreCase("jpg") &&
276            (xform.op != TJTransform.OP_NONE || xform.options != 0) &&
277            scaleFactor.isOne()) {
278          file = new File(argv[1]);
279          FileOutputStream fos = new FileOutputStream(file);
280          fos.write(tjd.getJPEGBuf(), 0, tjd.getJPEGSize());
281          fos.close();
282          System.exit(0);
283        }
284
285        width = scaleFactor.getScaled(width);
286        height = scaleFactor.getScaled(height);
287
288        if (!outFormat.equalsIgnoreCase("jpg"))
289          img = tjd.decompress(width, height, BufferedImage.TYPE_INT_RGB,
290                               flags);
291        else
292          bmpBuf = tjd.decompress(width, 0, height, TJ.PF_BGRX, flags);
293        tjd.close();
294      } else {
295        img = ImageIO.read(file);
296        if (img == null)
297          throw new Exception("Input image type not supported.");
298        width = img.getWidth();
299        height = img.getHeight();
300        if (outSubsamp < 0) {
301          if (img.getType() == BufferedImage.TYPE_BYTE_GRAY)
302            outSubsamp = TJ.SAMP_GRAY;
303          else
304            outSubsamp = TJ.SAMP_444;
305        }
306      }
307      System.gc();
308      if (!display)
309        System.out.print("Dest. Image (" + outFormat + "):  " + width + " x " +
310                         height + " pixels");
311
312      if (display) {
313        ImageIcon icon = new ImageIcon(img);
314        JLabel label = new JLabel(icon, JLabel.CENTER);
315        JOptionPane.showMessageDialog(null, label, "Output Image",
316                                      JOptionPane.PLAIN_MESSAGE);
317      } else if (outFormat.equalsIgnoreCase("jpg")) {
318        System.out.println(", " + sampName[outSubsamp] +
319                           " subsampling, quality = " + outQual);
320        TJCompressor tjc = new TJCompressor();
321        int jpegSize;
322        byte[] jpegBuf;
323
324        tjc.setSubsamp(outSubsamp);
325        tjc.setJPEGQuality(outQual);
326        if (img != null)
327          tjc.setSourceImage(img, 0, 0, 0, 0);
328        else {
329          tjc.setSourceImage(bmpBuf, 0, 0, width, 0, height, TJ.PF_BGRX);
330        }
331        jpegBuf = tjc.compress(flags);
332        jpegSize = tjc.getCompressedSize();
333        tjc.close();
334
335        file = new File(argv[1]);
336        FileOutputStream fos = new FileOutputStream(file);
337        fos.write(jpegBuf, 0, jpegSize);
338        fos.close();
339      } else {
340        System.out.print("\n");
341        file = new File(argv[1]);
342        ImageIO.write(img, outFormat, file);
343      }
344
345    } catch(Exception e) {
346      e.printStackTrace();
347      System.exit(-1);
348    }
349  }
350
351  public void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion,
352                           Rectangle planeRegion, int componentIndex,
353                           int transformIndex, TJTransform transform)
354                           throws TJException {
355    for (int i = 0; i < bufferRegion.width * bufferRegion.height; i++) {
356      coeffBuffer.put(i, (short)(-coeffBuffer.get(i)));
357    }
358  }
359
360  static TJScalingFactor[] sf = null;
361};
362