diff options
Diffstat (limited to 'java/TJExample.java')
-rw-r--r-- | java/TJExample.java | 403 |
1 files changed, 223 insertions, 180 deletions
diff --git a/java/TJExample.java b/java/TJExample.java index 835a5b9..7859886 100644 --- a/java/TJExample.java +++ b/java/TJExample.java @@ -1,6 +1,6 @@ /* - * Copyright (C)2011-2012, 2014-2015, 2017 D. R. Commander. - * All Rights Reserved. + * Copyright (C)2011-2012, 2014-2015, 2017-2018 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: @@ -28,8 +28,8 @@ */ /* - * This program demonstrates how to compress and decompress JPEG files using - * the TurboJPEG JNI wrapper + * This program demonstrates how to compress, decompress, and transform JPEG + * images using the TurboJPEG Java API */ import java.io.*; @@ -40,138 +40,178 @@ import javax.imageio.*; import javax.swing.*; import org.libjpegturbo.turbojpeg.*; -public class TJExample implements TJCustomFilter { - public static final String classname = new TJExample().getClass().getName(); +@SuppressWarnings("checkstyle:JavadocType") +class TJExample implements TJCustomFilter { - 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"); + static final String CLASS_NAME = + new TJExample().getClass().getName(); + + static final int DEFAULT_SUBSAMP = TJ.SAMP_444; + static final int DEFAULT_QUALITY = 95; + + + static final String[] SUBSAMP_NAME = { + "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1" + }; + + static final String[] COLORSPACE_NAME = { + "RGB", "YCbCr", "GRAY", "CMYK", "YCCK" + }; + + + /* DCT filter example. This produces a negative of the image. */ + + @SuppressWarnings("checkstyle:JavadocMethod") + 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))); + } + } + + + static void usage() throws Exception { + System.out.println("\nUSAGE: java [Java options] " + CLASS_NAME + + " <Input image> <Output image> [options]\n"); + + System.out.println("Input and output images can be in 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.println("the TurboJPEG API will be used to compress or decompress the image.\n"); + + System.out.println("Compression Options (used if the output image is a JPEG image)"); + System.out.println("--------------------------------------------------------------\n"); + + System.out.println("-subsamp <444|422|420|gray> = Apply this level of chrominance subsampling when"); + System.out.println(" compressing the output image. The default is to use the same level of"); + System.out.println(" subsampling as in the input image, if the input image is also a JPEG"); + System.out.println(" image, or to use grayscale if the input image is a grayscale non-JPEG"); + System.out.println(" image, or to use " + + SUBSAMP_NAME[DEFAULT_SUBSAMP] + + " subsampling otherwise.\n"); + + System.out.println("-q <1-100> = Compress the output image with this JPEG quality level"); + System.out.println(" (default = " + DEFAULT_QUALITY + ").\n"); + + System.out.println("Decompression Options (used if the input image is a JPEG image)"); + System.out.println("---------------------------------------------------------------\n"); + + System.out.println("-scale M/N = Scale the input image by a factor of M/N when decompressing it."); + System.out.print("(M/N = "); + for (int i = 0; i < SCALING_FACTORS.length; i++) { + System.out.print(SCALING_FACTORS[i].getNum() + "/" + + SCALING_FACTORS[i].getDenom()); + if (SCALING_FACTORS.length == 2 && i != SCALING_FACTORS.length - 1) System.out.print(" or "); - else if (sf.length > 2) { - if (i != sf.length - 1) + else if (SCALING_FACTORS.length > 2) { + if (i != SCALING_FACTORS.length - 1) System.out.print(", "); - if (i == sf.length - 2) + if (i == SCALING_FACTORS.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(" Perform one of these lossless transform operations on the input image"); + System.out.println(" prior to decompressing it (these options are mutually exclusive.)\n"); + + System.out.println("-grayscale = Perform lossless grayscale conversion on the input image prior"); + System.out.println(" to decompressing it (can be combined with the other transform operations"); + System.out.println(" above.)\n"); + + System.out.println("-crop WxH+X+Y = Perform lossless cropping on the input image prior to"); + System.out.println(" decompressing it. X and Y specify the upper left corner of the cropping"); + System.out.println(" region, and W and H specify the width and height of the cropping region."); + System.out.println(" X and Y must be evenly divible by the MCU block size (8x8 if the input"); + System.out.println(" image was compressed using no subsampling or grayscale, 16x8 if it was"); + System.out.println(" compressed using 4:2:2 subsampling, or 16x16 if it was compressed using"); + System.out.println(" 4:2:0 subsampling.)\n"); + + System.out.println("General Options"); + System.out.println("---------------\n"); + + System.out.println("-display = Display output image (Output filename 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(" 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(" codec.\n"); + System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the"); - System.out.println(" underlying codec\n"); + System.out.println(" underlying codec.\n"); + System.exit(1); } - private static final String[] sampName = { - "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1" - }; public static void main(String[] argv) { - BufferedImage img = null; - byte[] bmpBuf = null; - TJTransform xform = new TJTransform(); - int flags = 0; - try { - sf = TJ.getScalingFactors(); + TJScalingFactor scalingFactor = new TJScalingFactor(1, 1); + int outSubsamp = -1, outQual = -1; + TJTransform xform = new TJTransform(); + boolean display = false; + int flags = 0; + int width, height; + String inFormat = "jpg", outFormat = "jpg"; + BufferedImage img = null; + byte[] imgBuf = null; - if (argv.length < 2) { + 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[1].substring(0, 2).equalsIgnoreCase("-d")) display = true; + /* Parse arguments. */ for (int i = 2; i < argv.length; i++) { if (argv[i].length() < 2) continue; else if (argv[i].length() > 2 && - argv[i].substring(0, 3).equalsIgnoreCase("-sc")) { + argv[i].substring(0, 3).equalsIgnoreCase("-sc") && + i < argv.length - 1) { 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; - } + 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 < SCALING_FACTORS.length; j++) { + if (tempsf.equals(SCALING_FACTORS[j])) { + scalingFactor = SCALING_FACTORS[j]; + match = 1; + break; } } } - if (match != 1) usage(); - } - else 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 + if (match != 1) usage(); - } - else 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 + } else if (argv[i].length() > 2 && + argv[i].substring(0, 3).equalsIgnoreCase("-su") && + 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 if (argv[i].substring(0, 2).equalsIgnoreCase("-g")) + } else if (argv[i].substring(0, 2).equalsIgnoreCase("-q") && + i < argv.length - 1) { + outQual = Integer.parseInt(argv[++i]); + if (outQual < 1 || outQual > 100) + usage(); + } else if (argv[i].substring(0, 2).equalsIgnoreCase("-g")) xform.options |= TJTransform.OPT_GRAY; else if (argv[i].equalsIgnoreCase("-hflip")) xform.op = TJTransform.OP_HFLIP; @@ -190,43 +230,34 @@ public class TJExample implements TJCustomFilter { else 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) + argv[i].substring(0, 2).equalsIgnoreCase("-c") && + i < argv.length - 1) { + String[] cropArg = argv[++i].split("[x\\+]"); + if (cropArg.length != 4) 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) + xform.width = Integer.parseInt(cropArg[0]); + xform.height = Integer.parseInt(cropArg[1]); + xform.x = Integer.parseInt(cropArg[2]); + xform.y = Integer.parseInt(cropArg[3]); + if (xform.x < 0 || xform.y < 0 || xform.width < 1 || + xform.height < 1) usage(); - xform.x = tempx; - xform.y = tempy; - xform.width = tempw; - xform.height = temph; xform.options |= TJTransform.OPT_CROP; - } - else if (argv[i].substring(0, 2).equalsIgnoreCase("-d")) + } else if (argv[i].substring(0, 2).equalsIgnoreCase("-d")) display = true; else if (argv[i].equalsIgnoreCase("-fastupsample")) { System.out.println("Using fast upsampling code"); flags |= TJ.FLAG_FASTUPSAMPLE; - } - else if (argv[i].equalsIgnoreCase("-fastdct")) { + } else if (argv[i].equalsIgnoreCase("-fastdct")) { System.out.println("Using fastest DCT/IDCT algorithm"); flags |= TJ.FLAG_FASTDCT; - } - else if (argv[i].equalsIgnoreCase("-accuratedct")) { + } else if (argv[i].equalsIgnoreCase("-accuratedct")) { System.out.println("Using most accurate DCT/IDCT algorithm"); flags |= TJ.FLAG_ACCURATEDCT; - } - else usage(); + } else usage(); } + + /* Determine input and output image formats based on file extensions. */ String[] inFileTokens = argv[0].split("\\."); if (inFileTokens.length > 1) inFormat = inFileTokens[inFileTokens.length - 1]; @@ -239,61 +270,75 @@ public class TJExample implements TJCustomFilter { 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) { + /* Input image is a JPEG image. Decompress and/or transform it. */ + boolean doTransform = (xform.op != TJTransform.OP_NONE || + xform.options != 0 || xform.cf != null); + + /* Read the JPEG file into memory. */ + File jpegFile = new File(argv[0]); + FileInputStream fis = new FileInputStream(jpegFile); + int jpegSize = fis.available(); + if (jpegSize < 1) { System.out.println("Input file contains no data"); System.exit(1); } - byte[] inputBuf = new byte[inputSize]; - fis.read(inputBuf); + byte[] jpegBuf = new byte[jpegSize]; + fis.read(jpegBuf); 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]; + if (doTransform) { + /* Transform it. */ + TJTransformer tjt = new TJTransformer(jpegBuf); + TJTransform[] xforms = new TJTransform[1]; + xforms[0] = xform; + xforms[0].options |= TJTransform.OPT_TRIM; + TJDecompressor[] tjds = tjt.transform(xforms, 0); + tjd = tjds[0]; + tjt.close(); } else - tjd = new TJDecompressor(inputBuf); + tjd = new TJDecompressor(jpegBuf); 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; + int inColorspace = tjd.getColorspace(); - if (outFormat.equalsIgnoreCase("jpg") && - (xform.op != TJTransform.OP_NONE || xform.options != 0) && - scaleFactor.isOne()) { - file = new File(argv[1]); - FileOutputStream fos = new FileOutputStream(file); + System.out.println((doTransform ? "Transformed" : "Input") + + " Image (jpg): " + width + " x " + height + + " pixels, " + SUBSAMP_NAME[inSubsamp] + + " subsampling, " + COLORSPACE_NAME[inColorspace]); + + if (outFormat.equalsIgnoreCase("jpg") && doTransform && + scalingFactor.isOne() && outSubsamp < 0 && outQual < 0) { + /* Input image has been transformed, and no re-compression options + have been selected. Write the transformed image to disk and + exit. */ + File outFile = new File(argv[1]); + FileOutputStream fos = new FileOutputStream(outFile); fos.write(tjd.getJPEGBuf(), 0, tjd.getJPEGSize()); fos.close(); System.exit(0); } - width = scaleFactor.getScaled(width); - height = scaleFactor.getScaled(height); + /* Scaling and/or a non-JPEG output image format and/or compression + options have been selected, so we need to decompress the + input/transformed image. */ + width = scalingFactor.getScaled(width); + height = scalingFactor.getScaled(height); + if (outSubsamp < 0) + outSubsamp = inSubsamp; 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); + imgBuf = tjd.decompress(width, 0, height, TJ.PF_BGRX, flags); tjd.close(); } else { - img = ImageIO.read(file); + /* Input image is not a JPEG image. Load it into memory. */ + img = ImageIO.read(new File(argv[0])); if (img == null) throw new Exception("Input image type not supported."); width = img.getWidth(); @@ -302,61 +347,59 @@ public class TJExample implements TJCustomFilter { if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) outSubsamp = TJ.SAMP_GRAY; else - outSubsamp = TJ.SAMP_444; + outSubsamp = DEFAULT_SUBSAMP; } + System.out.println("Input Image: " + width + " x " + height + + " pixels"); } System.gc(); if (!display) - System.out.print("Dest. Image (" + outFormat + "): " + width + " x " + - height + " pixels"); + System.out.print("Output Image (" + outFormat + "): " + width + + " x " + height + " pixels"); if (display) { + /* Display the uncompressed image */ 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] + + /* Output image format is JPEG. Compress the uncompressed image. */ + if (outQual < 0) + outQual = DEFAULT_QUALITY; + System.out.println(", " + SUBSAMP_NAME[outSubsamp] + " subsampling, quality = " + outQual); - TJCompressor tjc = new TJCompressor(); - int jpegSize; - byte[] jpegBuf; + TJCompressor tjc = new TJCompressor(); 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(); + else + tjc.setSourceImage(imgBuf, 0, 0, width, 0, height, TJ.PF_BGRX); + byte[] jpegBuf = tjc.compress(flags); + int jpegSize = tjc.getCompressedSize(); tjc.close(); - file = new File(argv[1]); - FileOutputStream fos = new FileOutputStream(file); + /* Write the JPEG image to disk. */ + File outFile = new File(argv[1]); + FileOutputStream fos = new FileOutputStream(outFile); fos.write(jpegBuf, 0, jpegSize); fos.close(); } else { + /* Output image format is not JPEG. Save the uncompressed image + directly to disk. */ System.out.print("\n"); - file = new File(argv[1]); - ImageIO.write(img, outFormat, file); + File outFile = new File(argv[1]); + ImageIO.write(img, outFormat, outFile); } - } catch(Exception e) { + } 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))); - } - } - - static TJScalingFactor[] sf = null; + static final TJScalingFactor[] SCALING_FACTORS = + TJ.getScalingFactors(); }; |