You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
405 lines
17 KiB
405 lines
17 KiB
/* |
|
* 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: |
|
* |
|
* - 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, decompress, and transform JPEG |
|
* images using the TurboJPEG Java API |
|
*/ |
|
|
|
import java.io.*; |
|
import java.awt.*; |
|
import java.awt.image.*; |
|
import java.nio.*; |
|
import javax.imageio.*; |
|
import javax.swing.*; |
|
import org.libjpegturbo.turbojpeg.*; |
|
|
|
|
|
@SuppressWarnings("checkstyle:JavadocType") |
|
class TJExample implements TJCustomFilter { |
|
|
|
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("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 (SCALING_FACTORS.length > 2) { |
|
if (i != SCALING_FACTORS.length - 1) |
|
System.out.print(", "); |
|
if (i == SCALING_FACTORS.length - 2) |
|
System.out.print("or "); |
|
} |
|
} |
|
System.out.println(")\n"); |
|
|
|
System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 ="); |
|
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("-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) { |
|
|
|
try { |
|
|
|
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) |
|
usage(); |
|
|
|
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") && |
|
i < argv.length - 1) { |
|
int match = 0; |
|
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("-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("-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; |
|
else if (argv[i].equalsIgnoreCase("-vflip")) |
|
xform.op = TJTransform.OP_VFLIP; |
|
else if (argv[i].equalsIgnoreCase("-transpose")) |
|
xform.op = TJTransform.OP_TRANSPOSE; |
|
else if (argv[i].equalsIgnoreCase("-transverse")) |
|
xform.op = TJTransform.OP_TRANSVERSE; |
|
else if (argv[i].equalsIgnoreCase("-rot90")) |
|
xform.op = TJTransform.OP_ROT90; |
|
else if (argv[i].equalsIgnoreCase("-rot180")) |
|
xform.op = TJTransform.OP_ROT180; |
|
else if (argv[i].equalsIgnoreCase("-rot270")) |
|
xform.op = TJTransform.OP_ROT270; |
|
else if (argv[i].equalsIgnoreCase("-custom")) |
|
xform.cf = new TJExample(); |
|
else if (argv[i].length() > 2 && |
|
argv[i].substring(0, 2).equalsIgnoreCase("-c") && |
|
i < argv.length - 1) { |
|
String[] cropArg = argv[++i].split("[x\\+]"); |
|
if (cropArg.length != 4) |
|
usage(); |
|
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.options |= TJTransform.OPT_CROP; |
|
} 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")) { |
|
System.out.println("Using fastest DCT/IDCT algorithm"); |
|
flags |= TJ.FLAG_FASTDCT; |
|
} else if (argv[i].equalsIgnoreCase("-accuratedct")) { |
|
System.out.println("Using most accurate DCT/IDCT algorithm"); |
|
flags |= TJ.FLAG_ACCURATEDCT; |
|
} 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]; |
|
String[] outFileTokens; |
|
if (display) |
|
outFormat = "bmp"; |
|
else { |
|
outFileTokens = argv[1].split("\\."); |
|
if (outFileTokens.length > 1) |
|
outFormat = outFileTokens[outFileTokens.length - 1]; |
|
} |
|
|
|
if (inFormat.equalsIgnoreCase("jpg")) { |
|
/* 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[] jpegBuf = new byte[jpegSize]; |
|
fis.read(jpegBuf); |
|
fis.close(); |
|
|
|
TJDecompressor tjd; |
|
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(jpegBuf); |
|
|
|
width = tjd.getWidth(); |
|
height = tjd.getHeight(); |
|
int inSubsamp = tjd.getSubsamp(); |
|
int inColorspace = tjd.getColorspace(); |
|
|
|
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); |
|
} |
|
|
|
/* 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 |
|
imgBuf = tjd.decompress(width, 0, height, TJ.PF_BGRX, flags); |
|
tjd.close(); |
|
} else { |
|
/* 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(); |
|
height = img.getHeight(); |
|
if (outSubsamp < 0) { |
|
if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) |
|
outSubsamp = TJ.SAMP_GRAY; |
|
else |
|
outSubsamp = DEFAULT_SUBSAMP; |
|
} |
|
System.out.println("Input Image: " + width + " x " + height + |
|
" pixels"); |
|
} |
|
System.gc(); |
|
if (!display) |
|
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")) { |
|
/* 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(); |
|
tjc.setSubsamp(outSubsamp); |
|
tjc.setJPEGQuality(outQual); |
|
if (img != null) |
|
tjc.setSourceImage(img, 0, 0, 0, 0); |
|
else |
|
tjc.setSourceImage(imgBuf, 0, 0, width, 0, height, TJ.PF_BGRX); |
|
byte[] jpegBuf = tjc.compress(flags); |
|
int jpegSize = tjc.getCompressedSize(); |
|
tjc.close(); |
|
|
|
/* 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 outFile = new File(argv[1]); |
|
ImageIO.write(img, outFormat, outFile); |
|
} |
|
|
|
} catch (Exception e) { |
|
e.printStackTrace(); |
|
System.exit(-1); |
|
} |
|
} |
|
|
|
static final TJScalingFactor[] SCALING_FACTORS = |
|
TJ.getScalingFactors(); |
|
};
|
|
|