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 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 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 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