Update libjpeg-turbo to 2.0.0
Bug: 78329453
Update to upstream at https://github.com/libjpeg-turbo/libjpeg-turbo/tree/2.0.0
This includes a fix for a bug that could result in an infinite loop.
ChangeLog.md contains detailed changes about the upstream library. Changes
I made are below:
- Remove files that are no longer in upstream, and include all current
files from upstream.
- Update various references to the version.
Android.bp:
- Update to build new files/files in new locations.
- Run bpfmt
README.android:
- Remove cherry-pick references, as they are no longer needed.
- Remove modification in jsimdext.inc, which no longer appears to be
necessary.
README.version:
- Use the github URL, as it is now the official upstream build
- Replace msarett as OWNER, as he no longer works on this project
- Update the version
Change-Id: Ie6cfee5a8f820f28656bbb305f500e75e7ce7915
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.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();
-
- if (argv.length < 2) {
- usage();
- }
-
- TJScalingFactor scaleFactor = new TJScalingFactor(1, 1);
- String inFormat = "jpg", outFormat = "jpg";
- int outSubsamp = -1, outQual = 95;
+ 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")) {
+ 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 @@
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)
+ argv[i].substring(0, 2).equalsIgnoreCase("-c") &&
+ i < argv.length - 1) {
+ String[] cropArg = argv[++i].split("[x\\+]");
+ if (cropArg.length != 4)
usage();
- String[] cropArg = argv[++i].split(",");
- if (cropArg.length != 3)
+ 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();
- 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;
- }
- 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 @@
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 @@
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();
};