| /* |
| * Copyright (C)2011-2012, 2014-2015, 2017-2018, 2022-2024 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 use the TurboJPEG C API to approximate the |
| * functionality of the IJG's djpeg program. djpeg features that are not |
| * covered: |
| * |
| * - OS/2 BMP, GIF, and Targa output file formats [legacy feature] |
| * - Color quantization and dithering [legacy feature] |
| * - The floating-point IDCT method [legacy feature] |
| * - Extracting an ICC color management profile |
| * - Progress reporting |
| * - Skipping rows (i.e. exclusive rather than inclusive partial decompression) |
| * - Debug output |
| */ |
| |
| import java.io.*; |
| import java.util.*; |
| import org.libjpegturbo.turbojpeg.*; |
| |
| |
| @SuppressWarnings("checkstyle:JavadocType") |
| final class TJDecomp { |
| |
| private TJDecomp() {} |
| |
| static final String CLASS_NAME = |
| new TJDecomp().getClass().getName(); |
| |
| |
| private static boolean isCropped(java.awt.Rectangle cr) { |
| return (cr.x != 0 || cr.y != 0 || cr.width != 0 || cr.height != 0); |
| } |
| |
| |
| private static String tjErrorMsg; |
| private static int tjErrorCode = -1; |
| |
| static void handleTJException(TJException e, int stopOnWarning) |
| throws TJException { |
| String errorMsg = e.getMessage(); |
| int errorCode = e.getErrorCode(); |
| |
| if (stopOnWarning != 1 && errorCode == TJ.ERR_WARNING) { |
| if (tjErrorMsg == null || !tjErrorMsg.equals(errorMsg) || |
| tjErrorCode != errorCode) { |
| tjErrorMsg = errorMsg; |
| tjErrorCode = errorCode; |
| System.out.println("WARNING: " + errorMsg); |
| } |
| } else |
| throw e; |
| } |
| |
| |
| static void usage() { |
| int i; |
| TJScalingFactor[] scalingFactors = TJ.getScalingFactors(); |
| int numScalingFactors = scalingFactors.length; |
| |
| System.out.println("\nUSAGE: java [Java options] " + CLASS_NAME + |
| " [options] <JPEG image> <Output image>\n"); |
| |
| System.out.println("The output image will be in Windows BMP or PBMPLUS (PPM/PGM) format, depending"); |
| System.out.println("on the file extension.\n"); |
| |
| System.out.println("GENERAL OPTIONS (CAN BE ABBREVBIATED)"); |
| System.out.println("-------------------------------------"); |
| System.out.println("-icc FILE"); |
| System.out.println(" Extract the ICC (International Color Consortium) color profile from the"); |
| System.out.println(" JPEG image to the specified file"); |
| System.out.println("-strict"); |
| System.out.println(" Treat all warnings as fatal; abort immediately if incomplete or corrupt"); |
| System.out.println(" data is encountered in the JPEG image, rather than trying to salvage the"); |
| System.out.println(" rest of the image\n"); |
| |
| System.out.println("LOSSY JPEG OPTIONS (CAN BE ABBREVIATED)"); |
| System.out.println("---------------------------------------"); |
| System.out.println("-crop WxH+X+Y"); |
| System.out.println(" Decompress only the specified region of the JPEG image. (W, H, X, and Y"); |
| System.out.println(" are the width, height, left boundary, and upper boundary of the region, all"); |
| System.out.println(" specified relative to the scaled image dimensions.) If necessary, X will"); |
| System.out.println(" be shifted left to the nearest iMCU boundary, and W will be increased"); |
| System.out.println(" accordingly."); |
| System.out.println("-dct fast"); |
| System.out.println(" Use less accurate IDCT algorithm [legacy feature]"); |
| System.out.println("-dct int"); |
| System.out.println(" Use more accurate IDCT algorithm [default]"); |
| System.out.println("-grayscale"); |
| System.out.println(" Decompress a full-color JPEG image into a grayscale output image"); |
| System.out.println("-maxmemory N"); |
| System.out.println(" Memory limit (in megabytes) for intermediate buffers used with progressive"); |
| System.out.println(" JPEG decompression [default = no limit]"); |
| System.out.println("-maxscans N"); |
| System.out.println(" Refuse to decompress progressive JPEG images that have more than N scans"); |
| System.out.println("-nosmooth"); |
| System.out.println(" Use the fastest chrominance upsampling algorithm available"); |
| System.out.println("-rgb"); |
| System.out.println(" Decompress a grayscale JPEG image into a full-color output image"); |
| System.out.println("-scale M/N"); |
| System.out.println(" Scale the width/height of the JPEG image by a factor of M/N when"); |
| System.out.print(" decompressing it (M/N = "); |
| for (i = 0; i < numScalingFactors; i++) { |
| System.out.format("%d/%d", scalingFactors[i].getNum(), |
| scalingFactors[i].getDenom()); |
| if (numScalingFactors == 2 && i != numScalingFactors - 1) |
| System.out.print(" or "); |
| else if (numScalingFactors > 2) { |
| if (i != numScalingFactors - 1) |
| System.out.print(", "); |
| if (i == numScalingFactors - 2) |
| System.out.print("or "); |
| } |
| if (i % 8 == 0 && i != 0) System.out.print("\n "); |
| } |
| System.out.println(")\n"); |
| |
| System.exit(1); |
| } |
| |
| |
| static boolean matchArg(String arg, String string, int minChars) { |
| if (arg.length() > string.length() || arg.length() < minChars) |
| return false; |
| |
| int cmpChars = Math.max(arg.length(), minChars); |
| string = string.substring(0, cmpChars); |
| |
| return arg.equalsIgnoreCase(string); |
| } |
| |
| |
| public static void main(String[] argv) { |
| int exitStatus = 0; |
| TJDecompressor tjd = null; |
| FileInputStream fis = null; |
| FileOutputStream fos = null; |
| |
| try { |
| |
| int i; |
| int colorspace, fastDCT = -1, fastUpsample = -1, maxMemory = -1, |
| maxScans = -1, pixelFormat = TJ.PF_UNKNOWN, precision, |
| stopOnWarning = -1, subsamp; |
| java.awt.Rectangle croppingRegion = TJ.UNCROPPED; |
| String iccFilename = null; |
| TJScalingFactor scalingFactor = TJ.UNSCALED; |
| int width, height; |
| byte[] jpegBuf, iccBuf = null; |
| Object dstBuf = null; |
| |
| for (i = 0; i < argv.length; i++) { |
| if (matchArg(argv[i], "-crop", 2) && i < argv.length - 1) { |
| int tempWidth = -1, tempHeight = -1, tempX = -1, tempY = -1; |
| Scanner scanner = new Scanner(argv[++i]).useDelimiter("x|X|\\+"); |
| |
| try { |
| tempWidth = scanner.nextInt(); |
| tempHeight = scanner.nextInt(); |
| tempX = scanner.nextInt(); |
| tempY = scanner.nextInt(); |
| } catch (Exception e) {} |
| |
| if (tempWidth < 1 || tempHeight < 1 || tempX < 0 || tempY < 0) |
| usage(); |
| croppingRegion.width = tempWidth; |
| croppingRegion.height = tempHeight; |
| croppingRegion.x = tempX; |
| croppingRegion.y = tempY; |
| } else if (matchArg(argv[i], "-dct", 2) && i < argv.length - 1) { |
| i++; |
| if (matchArg(argv[i], "fast", 1)) |
| fastDCT = 1; |
| else if (!matchArg(argv[i], "int", 1)) |
| usage(); |
| } else if (matchArg(argv[i], "-grayscale", 2) || |
| matchArg(argv[i], "-greyscale", 2)) |
| pixelFormat = TJ.PF_GRAY; |
| else if (matchArg(argv[i], "-icc", 2) && i < argv.length - 1) |
| iccFilename = argv[++i]; |
| else if (matchArg(argv[i], "-maxscans", 5) && i < argv.length - 1) { |
| int temp = -1; |
| |
| try { |
| temp = Integer.parseInt(argv[++i]); |
| } catch (NumberFormatException e) {} |
| if (temp < 0) |
| usage(); |
| maxScans = temp; |
| } else if (matchArg(argv[i], "-maxmemory", 2) && i < argv.length - 1) { |
| int temp = -1; |
| |
| try { |
| temp = Integer.parseInt(argv[++i]); |
| } catch (NumberFormatException e) {} |
| if (temp < 0) |
| usage(); |
| maxMemory = temp; |
| } else if (matchArg(argv[i], "-nosmooth", 2)) |
| fastUpsample = 1; |
| else if (matchArg(argv[i], "-rgb", 2)) |
| pixelFormat = TJ.PF_RGB; |
| else if (matchArg(argv[i], "-strict", 3)) |
| stopOnWarning = 1; |
| else if (matchArg(argv[i], "-scale", 2) && i < argv.length - 1) { |
| int tempNum = 0, tempDenom = 0; |
| boolean match = false, scanned = true; |
| Scanner scanner = new Scanner(argv[++i]).useDelimiter("/"); |
| |
| try { |
| tempNum = scanner.nextInt(); |
| tempDenom = scanner.nextInt(); |
| } catch (Exception e) {} |
| |
| if (tempNum < 1 || tempDenom < 1) |
| usage(); |
| |
| TJScalingFactor[] scalingFactors = TJ.getScalingFactors(); |
| for (int j = 0; j < scalingFactors.length; j++) { |
| if ((double)tempNum / (double)tempDenom == |
| (double)scalingFactors[j].getNum() / |
| (double)scalingFactors[j].getDenom()) { |
| scalingFactor = scalingFactors[j]; |
| match = true; break; |
| } |
| } |
| if (!match) usage(); |
| } else break; |
| } |
| |
| if (i != argv.length - 2) |
| usage(); |
| |
| tjd = new TJDecompressor(); |
| |
| if (stopOnWarning >= 0) |
| tjd.set(TJ.PARAM_STOPONWARNING, stopOnWarning); |
| if (fastUpsample >= 0) |
| tjd.set(TJ.PARAM_FASTUPSAMPLE, fastUpsample); |
| if (fastDCT >= 0) |
| tjd.set(TJ.PARAM_FASTDCT, fastDCT); |
| if (maxScans >= 0) |
| tjd.set(TJ.PARAM_SCANLIMIT, maxScans); |
| if (maxMemory >= 0) |
| tjd.set(TJ.PARAM_MAXMEMORY, maxMemory); |
| |
| File jpegFile = new File(argv[i++]); |
| fis = new FileInputStream(jpegFile); |
| int jpegSize = fis.available(); |
| if (jpegSize < 1) |
| throw new Exception("Input file contains no data"); |
| jpegBuf = new byte[jpegSize]; |
| fis.read(jpegBuf); |
| fis.close(); fis = null; |
| |
| try { |
| tjd.setSourceImage(jpegBuf, jpegSize); |
| } catch (TJException e) { handleTJException(e, stopOnWarning); } |
| subsamp = tjd.get(TJ.PARAM_SUBSAMP); |
| width = tjd.get(TJ.PARAM_JPEGWIDTH); |
| height = tjd.get(TJ.PARAM_JPEGHEIGHT); |
| precision = tjd.get(TJ.PARAM_PRECISION); |
| colorspace = tjd.get(TJ.PARAM_COLORSPACE); |
| |
| if (iccFilename != null) { |
| try { |
| iccBuf = tjd.getICCProfile(); |
| } catch (TJException e) { handleTJException(e, stopOnWarning); } |
| if (iccBuf != null) { |
| File iccFile = new File(iccFilename); |
| fos = new FileOutputStream(iccFile); |
| fos.write(iccBuf, 0, iccBuf.length); |
| } |
| } |
| |
| if (pixelFormat == TJ.PF_UNKNOWN) { |
| if (colorspace == TJ.CS_GRAY) |
| pixelFormat = TJ.PF_GRAY; |
| else if (colorspace == TJ.CS_CMYK || colorspace == TJ.CS_YCCK) |
| pixelFormat = TJ.PF_CMYK; |
| else |
| pixelFormat = TJ.PF_RGB; |
| } |
| |
| if (tjd.get(TJ.PARAM_LOSSLESS) == 0) { |
| tjd.setScalingFactor(scalingFactor); |
| width = scalingFactor.getScaled(width); |
| height = scalingFactor.getScaled(height); |
| |
| if (isCropped(croppingRegion)) { |
| int adjustment; |
| |
| if (subsamp == TJ.SAMP_UNKNOWN) |
| throw new Exception("Could not determine subsampling level of JPEG image"); |
| adjustment = croppingRegion.x % |
| scalingFactor.getScaled(TJ.getMCUWidth(subsamp)); |
| croppingRegion.x -= adjustment; |
| croppingRegion.width += adjustment; |
| tjd.setCroppingRegion(croppingRegion); |
| width = croppingRegion.width; |
| height = croppingRegion.height; |
| } |
| } |
| |
| if (precision <= 8) |
| dstBuf = new byte[width * height * TJ.getPixelSize(pixelFormat)]; |
| else |
| dstBuf = new short[width * height * TJ.getPixelSize(pixelFormat)]; |
| |
| try { |
| if (precision <= 8) |
| tjd.decompress8((byte[])dstBuf, 0, 0, 0, pixelFormat); |
| else if (precision <= 12) |
| tjd.decompress12((short[])dstBuf, 0, 0, 0, pixelFormat); |
| else |
| tjd.decompress16((short[])dstBuf, 0, 0, 0, pixelFormat); |
| } catch (TJException e) { handleTJException(e, stopOnWarning); } |
| |
| tjd.saveImage(argv[i], dstBuf, 0, 0, width, 0, height, pixelFormat); |
| } catch (Exception e) { |
| e.printStackTrace(); |
| exitStatus = -1; |
| } |
| |
| try { |
| if (tjd != null) tjd.close(); |
| if (fis != null) fis.close(); |
| if (fos != null) fos.close(); |
| } catch (Exception e) {} |
| System.exit(exitStatus); |
| } |
| }; |