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