content: /*
   * Copyright (C)2011-2012, 2014-2015 D. R. Commander.  All Rights Reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are met:
   *
   * - Redistributions of source code must retain the above copyright notice,
   *   this list of conditions and the following disclaimer.
   * - Redistributions in binary form must reproduce the above copyright notice,
   *   this list of conditions and the following disclaimer in the documentation
   *   and/or other materials provided with the distribution.
   * - Neither the name of the libjpeg-turbo Project nor the names of its
   *   contributors may be used to endorse or promote products derived from this
   *   software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   */
  
  /*
   * This program demonstrates how to compress and decompress JPEG files using
   * the TurboJPEG JNI wrapper
   */
  
  import org.libjpegturbo.turbojpeg.*;
  
  public class TJExample implements TJCustomFilter {
  
  	public static final String classname = new TJExample().getClass().getName();
  	private static final String[] sampName = {"4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0"};
  	static TJScalingFactor[] sf = null;
  
  	private static void usage() throws Exception {
  		System.out.println("\nUSAGE: java " + classname + " <Input file> <Output file> [options]\n");
  		System.out.println("Input and output files can be any image format that the Java Image I/O");
  		System.out.println("extensions understand.  If either filename ends in a .jpg extension, then");
  		System.out.println("TurboJPEG will be used to compress or decompress the file.\n");
  		System.out.println("Options:\n");
  		System.out.println("-scale M/N = if the input image is a JPEG file, scale the width/height of the");
  		System.out.print("             output image by a factor of M/N (M/N = ");
  		for (int i = 0; i < sf.length; i++) {
  			System.out.print(sf[i].getNum() + "/" + sf[i].getDenom());
  			if (sf.length == 2 && i != sf.length - 1) {
  				System.out.print(" or ");
  			} else if (sf.length > 2) {
  				if (i != sf.length - 1) {
  					System.out.print(", ");
  				}
  				if (i == sf.length - 2) {
  					System.out.print("or ");
  				}
  			}
  		}
  		System.out.println(")\n");
  		System.out.println("-samp <444|422|420|gray> = If the output image is a JPEG file, this specifies");
  		System.out.println("                           the level of chrominance subsampling to use when");
  		System.out.println("                           recompressing it.  Default is to use the same level");
  		System.out.println("                           of subsampling as the input, if the input is a JPEG");
  		System.out.println("                           file, or 4:4:4 otherwise.\n");
  		System.out.println("-q <1-100> = If the output image is a JPEG file, this specifies the JPEG");
  		System.out.println("             quality to use when recompressing it (default = 95).\n");
  		System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =");
  		System.out.println("     If the input image is a JPEG file, perform the corresponding lossless");
  		System.out.println("     transform prior to decompression (these options are mutually exclusive)\n");
  		System.out.println("-grayscale = If the input image is a JPEG file, perform lossless grayscale");
  		System.out.println("     conversion prior to decompression (can be combined with the other");
  		System.out.println("     transforms above)\n");
  		System.out.println("-crop X,Y,WxH = If the input image is a JPEG file, perform lossless cropping");
  		System.out.println("     prior to decompression.  X,Y specifies the upper left corner of the");
  		System.out.println("     cropping region, and WxH specifies its width and height.  X,Y must be");
  		System.out.println("     evenly divible by the MCU block size (8x8 if the source image was");
  		System.out.println("     compressed using no subsampling or grayscale, or 16x8 for 4:2:2 or 16x16");
  		System.out.println("     for 4:2:0.)\n");
  		System.out.println("-display = Display output image (Output file need not be specified in this");
  		System.out.println("     case.)\n");
  		System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in");
  		System.out.println("     the underlying codec\n");
  		System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying");
  		System.out.println("     codec\n");
  		System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the");
  		System.out.println("     underlying codec\n");
  		System.exit(1);
  	}
  
  	public static void main(String[] argv) {
  
  		BufferedImage img = null;
  		byte[] bmpBuf = null;
  		TJTransform xform = new TJTransform();
  		int flags = 0;
  
  		try {
  
  			sf = TJ.getScalingFactors();
  
  			if (argv.length < 2) {
  				usage();
  			}
  
  			TJScalingFactor scaleFactor = new TJScalingFactor(1, 1);
  			String inFormat = "jpg", outFormat = "jpg";
  			int outSubsamp = -1, outQual = 95;
  			boolean display = false;
  
  			if (argv.length > 1) {
  				for (int i = 1; i < argv.length; i++) {
  					if (argv[i].length() < 2) {
  						continue;
  					}
  					if (argv[i].length() > 2 && argv[i].substring(0, 3).equalsIgnoreCase("-sc")) {
  						int match = 0;
  						if (i < argv.length - 1) {
  							String[] scaleArg = argv[++i].split("/");
  							if (scaleArg.length == 2) {
  								TJScalingFactor tempsf = new TJScalingFactor(Integer.parseInt(scaleArg[0]), Integer.parseInt(scaleArg[1]));
  								for (int j = 0; j < sf.length; j++) {
  									if (tempsf.equals(sf[j])) {
  										scaleFactor = sf[j];
  										match = 1;
  										break;
  									}
  								}
  							}
  						}
  						if (match != 1) {
  							usage();
  						}
  					}
  					if (argv[i].equalsIgnoreCase("-h") || argv[i].equalsIgnoreCase("-?")) {
  						usage();
  					}
  					if (argv[i].length() > 2 && argv[i].substring(0, 3).equalsIgnoreCase("-sa")) {
  						if (i < argv.length - 1) {
  							i++;
  							if (argv[i].substring(0, 1).equalsIgnoreCase("g")) {
  								outSubsamp = TJ.SAMP_GRAY;
  							} else if (argv[i].equals("444")) {
  								outSubsamp = TJ.SAMP_444;
  							} else if (argv[i].equals("422")) {
  								outSubsamp = TJ.SAMP_422;
  							} else if (argv[i].equals("420")) {
  								outSubsamp = TJ.SAMP_420;
  							} else {
  								usage();
  							}
  						} else {
  							usage();
  						}
  					}
  					if (argv[i].substring(0, 2).equalsIgnoreCase("-q")) {
  						if (i < argv.length - 1) {
  							int qual = Integer.parseInt(argv[++i]);
  							if (qual >= 1 && qual <= 100) {
  								outQual = qual;
  							} else {
  								usage();
  							}
  						} else {
  							usage();
  						}
  					}
  					if (argv[i].substring(0, 2).equalsIgnoreCase("-g")) {
  						xform.options |= TJTransform.OPT_GRAY;
  					}
  					if (argv[i].equalsIgnoreCase("-hflip")) {
  						xform.op = TJTransform.OP_HFLIP;
  					}
  					if (argv[i].equalsIgnoreCase("-vflip")) {
  						xform.op = TJTransform.OP_VFLIP;
  					}
  					if (argv[i].equalsIgnoreCase("-transpose")) {
  						xform.op = TJTransform.OP_TRANSPOSE;
  					}
  					if (argv[i].equalsIgnoreCase("-transverse")) {
  						xform.op = TJTransform.OP_TRANSVERSE;
  					}
  					if (argv[i].equalsIgnoreCase("-rot90")) {
  						xform.op = TJTransform.OP_ROT90;
  					}
  					if (argv[i].equalsIgnoreCase("-rot180")) {
  						xform.op = TJTransform.OP_ROT180;
  					}
  					if (argv[i].equalsIgnoreCase("-rot270")) {
  						xform.op = TJTransform.OP_ROT270;
  					}
  					if (argv[i].equalsIgnoreCase("-custom")) {
  						xform.cf = new TJExample();
  					} else if (argv[i].length() > 2 && argv[i].substring(0, 2).equalsIgnoreCase("-c")) {
  						if (i >= argv.length - 1) {
  							usage();
  						}
  						String[] cropArg = argv[++i].split(",");
  						if (cropArg.length != 3) {
  							usage();
  						}
  						String[] dimArg = cropArg[2].split("[xX]");
  						if (dimArg.length != 2) {
  							usage();
  						}
  						int tempx = Integer.parseInt(cropArg[0]);
  						int tempy = Integer.parseInt(cropArg[1]);
  						int tempw = Integer.parseInt(dimArg[0]);
  						int temph = Integer.parseInt(dimArg[1]);
  						if (tempx < 0 || tempy < 0 || tempw < 0 || temph < 0) {
  							usage();
  						}
  						xform.x = tempx;
  						xform.y = tempy;
  						xform.width = tempw;
  						xform.height = temph;
  						xform.options |= TJTransform.OPT_CROP;
  					}
  					if (argv[i].substring(0, 2).equalsIgnoreCase("-d")) {
  						display = true;
  					}
  					if (argv[i].equalsIgnoreCase("-fastupsample")) {
  						System.out.println("Using fast upsampling code");
  						flags |= TJ.FLAG_FASTUPSAMPLE;
  					}
  					if (argv[i].equalsIgnoreCase("-fastdct")) {
  						System.out.println("Using fastest DCT/IDCT algorithm");
  						flags |= TJ.FLAG_FASTDCT;
  					}
  					if (argv[i].equalsIgnoreCase("-accuratedct")) {
  						System.out.println("Using most accurate DCT/IDCT algorithm");
  						flags |= TJ.FLAG_ACCURATEDCT;
  					}
  				}
  			}
  			String[] inFileTokens = argv[0].split("\\.");
  			if (inFileTokens.length > 1) {
  				inFormat = inFileTokens[inFileTokens.length - 1];
  			}
  			String[] outFileTokens;
  			if (display) {
  				outFormat = "bmp";
  			} else {
  				outFileTokens = argv[1].split("\\.");
  				if (outFileTokens.length > 1) {
  					outFormat = outFileTokens[outFileTokens.length - 1];
  				}
  			}
  
  			File file = new File(argv[0]);
  			int width, height;
  
  			if (inFormat.equalsIgnoreCase("jpg")) {
  				FileInputStream fis = new FileInputStream(file);
  				int inputSize = fis.available();
  				if (inputSize < 1) {
  					System.out.println("Input file contains no data");
  					System.exit(1);
  				}
  				byte[] inputBuf = new byte[inputSize];
  				fis.read(inputBuf);
  				fis.close();
  
  				TJDecompressor tjd;
  				if (xform.op != TJTransform.OP_NONE || xform.options != 0 || xform.cf != null) {
  					TJTransformer tjt = new TJTransformer(inputBuf);
  					TJTransform[] t = new TJTransform[1];
  					t[0] = xform;
  					t[0].options |= TJTransform.OPT_TRIM;
  					TJDecompressor[] tjdx = tjt.transform(t, 0);
  					tjd = tjdx[0];
  				} else {
  					tjd = new TJDecompressor(inputBuf);
  				}
  
  				width = tjd.getWidth();
  				height = tjd.getHeight();
  				int inSubsamp = tjd.getSubsamp();
  				System.out.println("Source Image: " + width + " x " + height + " pixels, " + sampName[inSubsamp] + " subsampling");
  				if (outSubsamp < 0) {
  					outSubsamp = inSubsamp;
  				}
  
  				if (outFormat.equalsIgnoreCase("jpg") && (xform.op != TJTransform.OP_NONE || xform.options != 0) && scaleFactor.isOne()) {
  					file = new File(argv[1]);
  					FileOutputStream fos = new FileOutputStream(file);
  					fos.write(tjd.getJPEGBuf(), 0, tjd.getJPEGSize());
  					fos.close();
  					System.exit(0);
  				}
  
  				width = scaleFactor.getScaled(width);
  				height = scaleFactor.getScaled(height);
  
  				if (!outFormat.equalsIgnoreCase("jpg")) {
  					img = tjd.decompress(width, height, BufferedImage.TYPE_INT_RGB, flags);
  				} else {
  					bmpBuf = tjd.decompress(width, 0, height, TJ.PF_BGRX, flags);
  				}
  				tjd.close();
  			} else {
  				img = ImageIO.read(file);
  				if (img == null) {
  					throw new Exception("Input image type not supported.");
  				}
  				width = img.getWidth();
  				height = img.getHeight();
  				if (outSubsamp < 0) {
  					if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) {
  						outSubsamp = TJ.SAMP_GRAY;
  					} else {
  						outSubsamp = TJ.SAMP_444;
  					}
  				}
  			}
  			System.gc();
  			if (!display) {
  				System.out.print("Dest. Image (" + outFormat + "):  " + width + " x " + height + " pixels");
  			}
  
  			if (display) {
  				ImageIcon icon = new ImageIcon(img);
  				JLabel label = new JLabel(icon, JLabel.CENTER);
  				JOptionPane.showMessageDialog(null, label, "Output Image", JOptionPane.PLAIN_MESSAGE);
  			} else if (outFormat.equalsIgnoreCase("jpg")) {
  				System.out.println(", " + sampName[outSubsamp] + " subsampling, quality = " + outQual);
  				TJCompressor tjc = new TJCompressor();
  				int jpegSize;
  				byte[] jpegBuf;
  
  				tjc.setSubsamp(outSubsamp);
  				tjc.setJPEGQuality(outQual);
  				if (img != null) {
  					tjc.setSourceImage(img, 0, 0, 0, 0);
  				} else {
  					tjc.setSourceImage(bmpBuf, 0, 0, width, 0, height, TJ.PF_BGRX);
  				}
  				jpegBuf = tjc.compress(flags);
  				jpegSize = tjc.getCompressedSize();
  				tjc.close();
  
  				file = new File(argv[1]);
  				FileOutputStream fos = new FileOutputStream(file);
  				fos.write(jpegBuf, 0, jpegSize);
  				fos.close();
  			} else {
  				System.out.print("\n");
  				file = new File(argv[1]);
  				ImageIO.write(img, outFormat, file);
  			}
  		} catch (Exception e) {
  			e.printStackTrace();
  			System.exit(-1);
  		}
  	}
  
  	public void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion, Rectangle planeRegion, int componentIndex, int transformIndex, TJTransform transform) throws TJException {
  		for (int i = 0; i < bufferRegion.width * bufferRegion.height; i++) {
  			coeffBuffer.put(i, (short) (-coeffBuffer.get(i)));
  		}
  	}
  };
  