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