1 /* 2 * Copyright (C)2011-2012, 2014-2015, 2017 D. R. Commander. 3 * All Rights Reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * - Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * - Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * - Neither the name of the libjpeg-turbo Project nor the names of its 14 * contributors may be used to endorse or promote products derived from this 15 * software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 /* 31 * This program demonstrates how to compress and decompress JPEG files using 32 * the TurboJPEG JNI wrapper 33 */ 34 35 import java.io.*; 36 import java.awt.*; 37 import java.awt.image.*; 38 import java.nio.*; 39 import javax.imageio.*; 40 import javax.swing.*; 41 import org.libjpegturbo.turbojpeg.*; 42 43 public class TJExample implements TJCustomFilter { 44 45 public static final String classname = new TJExample().getClass().getName(); 46 usage()47 private static void usage() throws Exception { 48 System.out.println("\nUSAGE: java " + classname + " <Input file> <Output file> [options]\n"); 49 System.out.println("Input and output files can be any image format that the Java Image I/O"); 50 System.out.println("extensions understand. If either filename ends in a .jpg extension, then"); 51 System.out.println("TurboJPEG will be used to compress or decompress the file.\n"); 52 System.out.println("Options:\n"); 53 System.out.println("-scale M/N = if the input image is a JPEG file, scale the width/height of the"); 54 System.out.print(" output image by a factor of M/N (M/N = "); 55 for (int i = 0; i < sf.length; i++) { 56 System.out.print(sf[i].getNum() + "/" + sf[i].getDenom()); 57 if (sf.length == 2 && i != sf.length - 1) 58 System.out.print(" or "); 59 else if (sf.length > 2) { 60 if (i != sf.length - 1) 61 System.out.print(", "); 62 if (i == sf.length - 2) 63 System.out.print("or "); 64 } 65 } 66 System.out.println(")\n"); 67 System.out.println("-samp <444|422|420|gray> = If the output image is a JPEG file, this specifies"); 68 System.out.println(" the level of chrominance subsampling to use when"); 69 System.out.println(" recompressing it. Default is to use the same level"); 70 System.out.println(" of subsampling as the input, if the input is a JPEG"); 71 System.out.println(" file, or 4:4:4 otherwise.\n"); 72 System.out.println("-q <1-100> = If the output image is a JPEG file, this specifies the JPEG"); 73 System.out.println(" quality to use when recompressing it (default = 95).\n"); 74 System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 ="); 75 System.out.println(" If the input image is a JPEG file, perform the corresponding lossless"); 76 System.out.println(" transform prior to decompression (these options are mutually exclusive)\n"); 77 System.out.println("-grayscale = If the input image is a JPEG file, perform lossless grayscale"); 78 System.out.println(" conversion prior to decompression (can be combined with the other"); 79 System.out.println(" transforms above)\n"); 80 System.out.println("-crop X,Y,WxH = If the input image is a JPEG file, perform lossless cropping"); 81 System.out.println(" prior to decompression. X,Y specifies the upper left corner of the"); 82 System.out.println(" cropping region, and WxH specifies its width and height. X,Y must be"); 83 System.out.println(" evenly divible by the MCU block size (8x8 if the source image was"); 84 System.out.println(" compressed using no subsampling or grayscale, or 16x8 for 4:2:2 or 16x16"); 85 System.out.println(" for 4:2:0.)\n"); 86 System.out.println("-display = Display output image (Output file need not be specified in this"); 87 System.out.println(" case.)\n"); 88 System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in"); 89 System.out.println(" the underlying codec\n"); 90 System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying"); 91 System.out.println(" codec\n"); 92 System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the"); 93 System.out.println(" underlying codec\n"); 94 System.exit(1); 95 } 96 97 private static final String[] sampName = { 98 "4:4:4", "4:2:2", "4:2:0", "Grayscale", "4:4:0", "4:1:1" 99 }; 100 main(String[] argv)101 public static void main(String[] argv) { 102 103 BufferedImage img = null; 104 byte[] bmpBuf = null; 105 TJTransform xform = new TJTransform(); 106 int flags = 0; 107 108 try { 109 110 sf = TJ.getScalingFactors(); 111 112 if (argv.length < 2) { 113 usage(); 114 } 115 116 TJScalingFactor scaleFactor = new TJScalingFactor(1, 1); 117 String inFormat = "jpg", outFormat = "jpg"; 118 int outSubsamp = -1, outQual = 95; 119 boolean display = false; 120 121 if (argv[1].substring(0, 2).equalsIgnoreCase("-d")) 122 display = true; 123 124 for (int i = 2; i < argv.length; i++) { 125 if (argv[i].length() < 2) 126 continue; 127 else if (argv[i].length() > 2 && 128 argv[i].substring(0, 3).equalsIgnoreCase("-sc")) { 129 int match = 0; 130 if (i < argv.length - 1) { 131 String[] scaleArg = argv[++i].split("/"); 132 if (scaleArg.length == 2) { 133 TJScalingFactor tempsf = 134 new TJScalingFactor(Integer.parseInt(scaleArg[0]), 135 Integer.parseInt(scaleArg[1])); 136 for (int j = 0; j < sf.length; j++) { 137 if (tempsf.equals(sf[j])) { 138 scaleFactor = sf[j]; 139 match = 1; 140 break; 141 } 142 } 143 } 144 } 145 if (match != 1) usage(); 146 } 147 else if (argv[i].length() > 2 && 148 argv[i].substring(0, 3).equalsIgnoreCase("-sa")) { 149 if (i < argv.length - 1) { 150 i++; 151 if (argv[i].substring(0, 1).equalsIgnoreCase("g")) 152 outSubsamp = TJ.SAMP_GRAY; 153 else if (argv[i].equals("444")) 154 outSubsamp = TJ.SAMP_444; 155 else if (argv[i].equals("422")) 156 outSubsamp = TJ.SAMP_422; 157 else if (argv[i].equals("420")) 158 outSubsamp = TJ.SAMP_420; 159 else 160 usage(); 161 } else 162 usage(); 163 } 164 else if (argv[i].substring(0, 2).equalsIgnoreCase("-q")) { 165 if (i < argv.length - 1) { 166 int qual = Integer.parseInt(argv[++i]); 167 if (qual >= 1 && qual <= 100) 168 outQual = qual; 169 else 170 usage(); 171 } else 172 usage(); 173 } 174 else if (argv[i].substring(0, 2).equalsIgnoreCase("-g")) 175 xform.options |= TJTransform.OPT_GRAY; 176 else if (argv[i].equalsIgnoreCase("-hflip")) 177 xform.op = TJTransform.OP_HFLIP; 178 else if (argv[i].equalsIgnoreCase("-vflip")) 179 xform.op = TJTransform.OP_VFLIP; 180 else if (argv[i].equalsIgnoreCase("-transpose")) 181 xform.op = TJTransform.OP_TRANSPOSE; 182 else if (argv[i].equalsIgnoreCase("-transverse")) 183 xform.op = TJTransform.OP_TRANSVERSE; 184 else if (argv[i].equalsIgnoreCase("-rot90")) 185 xform.op = TJTransform.OP_ROT90; 186 else if (argv[i].equalsIgnoreCase("-rot180")) 187 xform.op = TJTransform.OP_ROT180; 188 else if (argv[i].equalsIgnoreCase("-rot270")) 189 xform.op = TJTransform.OP_ROT270; 190 else if (argv[i].equalsIgnoreCase("-custom")) 191 xform.cf = new TJExample(); 192 else if (argv[i].length() > 2 && 193 argv[i].substring(0, 2).equalsIgnoreCase("-c")) { 194 if (i >= argv.length - 1) 195 usage(); 196 String[] cropArg = argv[++i].split(","); 197 if (cropArg.length != 3) 198 usage(); 199 String[] dimArg = cropArg[2].split("[xX]"); 200 if (dimArg.length != 2) 201 usage(); 202 int tempx = Integer.parseInt(cropArg[0]); 203 int tempy = Integer.parseInt(cropArg[1]); 204 int tempw = Integer.parseInt(dimArg[0]); 205 int temph = Integer.parseInt(dimArg[1]); 206 if (tempx < 0 || tempy < 0 || tempw < 0 || temph < 0) 207 usage(); 208 xform.x = tempx; 209 xform.y = tempy; 210 xform.width = tempw; 211 xform.height = temph; 212 xform.options |= TJTransform.OPT_CROP; 213 } 214 else if (argv[i].substring(0, 2).equalsIgnoreCase("-d")) 215 display = true; 216 else if (argv[i].equalsIgnoreCase("-fastupsample")) { 217 System.out.println("Using fast upsampling code"); 218 flags |= TJ.FLAG_FASTUPSAMPLE; 219 } 220 else if (argv[i].equalsIgnoreCase("-fastdct")) { 221 System.out.println("Using fastest DCT/IDCT algorithm"); 222 flags |= TJ.FLAG_FASTDCT; 223 } 224 else if (argv[i].equalsIgnoreCase("-accuratedct")) { 225 System.out.println("Using most accurate DCT/IDCT algorithm"); 226 flags |= TJ.FLAG_ACCURATEDCT; 227 } 228 else usage(); 229 } 230 String[] inFileTokens = argv[0].split("\\."); 231 if (inFileTokens.length > 1) 232 inFormat = inFileTokens[inFileTokens.length - 1]; 233 String[] outFileTokens; 234 if (display) 235 outFormat = "bmp"; 236 else { 237 outFileTokens = argv[1].split("\\."); 238 if (outFileTokens.length > 1) 239 outFormat = outFileTokens[outFileTokens.length - 1]; 240 } 241 242 File file = new File(argv[0]); 243 int width, height; 244 245 if (inFormat.equalsIgnoreCase("jpg")) { 246 FileInputStream fis = new FileInputStream(file); 247 int inputSize = fis.available(); 248 if (inputSize < 1) { 249 System.out.println("Input file contains no data"); 250 System.exit(1); 251 } 252 byte[] inputBuf = new byte[inputSize]; 253 fis.read(inputBuf); 254 fis.close(); 255 256 TJDecompressor tjd; 257 if (xform.op != TJTransform.OP_NONE || xform.options != 0 || 258 xform.cf != null) { 259 TJTransformer tjt = new TJTransformer(inputBuf); 260 TJTransform[] t = new TJTransform[1]; 261 t[0] = xform; 262 t[0].options |= TJTransform.OPT_TRIM; 263 TJDecompressor[] tjdx = tjt.transform(t, 0); 264 tjd = tjdx[0]; 265 } else 266 tjd = new TJDecompressor(inputBuf); 267 268 width = tjd.getWidth(); 269 height = tjd.getHeight(); 270 int inSubsamp = tjd.getSubsamp(); 271 System.out.println("Source Image: " + width + " x " + height + 272 " pixels, " + sampName[inSubsamp] + " subsampling"); 273 if (outSubsamp < 0) 274 outSubsamp = inSubsamp; 275 276 if (outFormat.equalsIgnoreCase("jpg") && 277 (xform.op != TJTransform.OP_NONE || xform.options != 0) && 278 scaleFactor.isOne()) { 279 file = new File(argv[1]); 280 FileOutputStream fos = new FileOutputStream(file); 281 fos.write(tjd.getJPEGBuf(), 0, tjd.getJPEGSize()); 282 fos.close(); 283 System.exit(0); 284 } 285 286 width = scaleFactor.getScaled(width); 287 height = scaleFactor.getScaled(height); 288 289 if (!outFormat.equalsIgnoreCase("jpg")) 290 img = tjd.decompress(width, height, BufferedImage.TYPE_INT_RGB, 291 flags); 292 else 293 bmpBuf = tjd.decompress(width, 0, height, TJ.PF_BGRX, flags); 294 tjd.close(); 295 } else { 296 img = ImageIO.read(file); 297 if (img == null) 298 throw new Exception("Input image type not supported."); 299 width = img.getWidth(); 300 height = img.getHeight(); 301 if (outSubsamp < 0) { 302 if (img.getType() == BufferedImage.TYPE_BYTE_GRAY) 303 outSubsamp = TJ.SAMP_GRAY; 304 else 305 outSubsamp = TJ.SAMP_444; 306 } 307 } 308 System.gc(); 309 if (!display) 310 System.out.print("Dest. Image (" + outFormat + "): " + width + " x " + 311 height + " pixels"); 312 313 if (display) { 314 ImageIcon icon = new ImageIcon(img); 315 JLabel label = new JLabel(icon, JLabel.CENTER); 316 JOptionPane.showMessageDialog(null, label, "Output Image", 317 JOptionPane.PLAIN_MESSAGE); 318 } else if (outFormat.equalsIgnoreCase("jpg")) { 319 System.out.println(", " + sampName[outSubsamp] + 320 " subsampling, quality = " + outQual); 321 TJCompressor tjc = new TJCompressor(); 322 int jpegSize; 323 byte[] jpegBuf; 324 325 tjc.setSubsamp(outSubsamp); 326 tjc.setJPEGQuality(outQual); 327 if (img != null) 328 tjc.setSourceImage(img, 0, 0, 0, 0); 329 else { 330 tjc.setSourceImage(bmpBuf, 0, 0, width, 0, height, TJ.PF_BGRX); 331 } 332 jpegBuf = tjc.compress(flags); 333 jpegSize = tjc.getCompressedSize(); 334 tjc.close(); 335 336 file = new File(argv[1]); 337 FileOutputStream fos = new FileOutputStream(file); 338 fos.write(jpegBuf, 0, jpegSize); 339 fos.close(); 340 } else { 341 System.out.print("\n"); 342 file = new File(argv[1]); 343 ImageIO.write(img, outFormat, file); 344 } 345 346 } catch(Exception e) { 347 e.printStackTrace(); 348 System.exit(-1); 349 } 350 } 351 customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion, Rectangle planeRegion, int componentIndex, int transformIndex, TJTransform transform)352 public void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion, 353 Rectangle planeRegion, int componentIndex, 354 int transformIndex, TJTransform transform) 355 throws TJException { 356 for (int i = 0; i < bufferRegion.width * bufferRegion.height; i++) { 357 coeffBuffer.put(i, (short)(-coeffBuffer.get(i))); 358 } 359 } 360 361 static TJScalingFactor[] sf = null; 362 }; 363