1 /* 2 * Copyright (C)2011-2012, 2014-2015 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 usage()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 main(String[] argv)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 customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion, Rectangle planeRegion, int componentIndex, int transformIndex, TJTransform transform)351 public void customFilter(ShortBuffer coeffBuffer, Rectangle bufferRegion, 352 Rectangle planeRegion, int componentIndex, 353 int transformIndex, TJTransform transform) 354 throws TJException { 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