1 /* 2 * Copyright (C)2009-2014, 2016-2019 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 import java.io.*; 30 import java.awt.image.*; 31 import javax.imageio.*; 32 import java.util.*; 33 import org.libjpegturbo.turbojpeg.*; 34 35 final class TJBench { 36 TJBench()37 private TJBench() {} 38 39 private static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvPad = 1; 40 private static boolean compOnly, decompOnly, doTile, doYUV, write = true; 41 42 static final String[] PIXFORMATSTR = { 43 "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY" 44 }; 45 46 static final String[] SUBNAME_LONG = { 47 "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" 48 }; 49 50 static final String[] SUBNAME = { 51 "444", "422", "420", "GRAY", "440", "411" 52 }; 53 54 static final String[] CSNAME = { 55 "RGB", "YCbCr", "GRAY", "CMYK", "YCCK" 56 }; 57 58 private static TJScalingFactor sf; 59 private static int xformOp = TJTransform.OP_NONE, xformOpt = 0; 60 private static double benchTime = 5.0, warmup = 1.0; 61 62 getTime()63 static double getTime() { 64 return (double)System.nanoTime() / 1.0e9; 65 } 66 67 68 private static String tjErrorMsg; 69 private static int tjErrorCode = -1; 70 handleTJException(TJException e)71 static void handleTJException(TJException e) throws TJException { 72 String errorMsg = e.getMessage(); 73 int errorCode = e.getErrorCode(); 74 75 if ((flags & TJ.FLAG_STOPONWARNING) == 0 && 76 errorCode == TJ.ERR_WARNING) { 77 if (tjErrorMsg == null || !tjErrorMsg.equals(errorMsg) || 78 tjErrorCode != errorCode) { 79 tjErrorMsg = errorMsg; 80 tjErrorCode = errorCode; 81 System.out.println("WARNING: " + errorMsg); 82 } 83 } else 84 throw e; 85 } 86 87 formatName(int subsamp, int cs)88 static String formatName(int subsamp, int cs) { 89 if (cs == TJ.CS_YCbCr) 90 return SUBNAME_LONG[subsamp]; 91 else if (cs == TJ.CS_YCCK) 92 return CSNAME[cs] + " " + SUBNAME_LONG[subsamp]; 93 else 94 return CSNAME[cs]; 95 } 96 97 sigFig(double val, int figs)98 static String sigFig(double val, int figs) { 99 String format; 100 int digitsAfterDecimal = figs - (int)Math.ceil(Math.log10(Math.abs(val))); 101 102 if (digitsAfterDecimal < 1) 103 format = new String("%.0f"); 104 else 105 format = new String("%." + digitsAfterDecimal + "f"); 106 return String.format(format, val); 107 } 108 109 loadImage(String fileName, int[] w, int[] h, int pixelFormat)110 static byte[] loadImage(String fileName, int[] w, int[] h, int pixelFormat) 111 throws Exception { 112 BufferedImage img = ImageIO.read(new File(fileName)); 113 114 if (img == null) 115 throw new Exception("Could not read " + fileName); 116 w[0] = img.getWidth(); 117 h[0] = img.getHeight(); 118 119 int[] rgb = img.getRGB(0, 0, w[0], h[0], null, 0, w[0]); 120 int ps = TJ.getPixelSize(pixelFormat); 121 int rindex = TJ.getRedOffset(pixelFormat); 122 int gindex = TJ.getGreenOffset(pixelFormat); 123 int bindex = TJ.getBlueOffset(pixelFormat); 124 byte[] dstBuf = new byte[w[0] * h[0] * ps]; 125 int pixels = w[0] * h[0], dstPtr = 0, rgbPtr = 0; 126 127 while (pixels-- > 0) { 128 dstBuf[dstPtr + rindex] = (byte)((rgb[rgbPtr] >> 16) & 0xff); 129 dstBuf[dstPtr + gindex] = (byte)((rgb[rgbPtr] >> 8) & 0xff); 130 dstBuf[dstPtr + bindex] = (byte)(rgb[rgbPtr] & 0xff); 131 dstPtr += ps; 132 rgbPtr++; 133 } 134 return dstBuf; 135 } 136 137 saveImage(String fileName, byte[] srcBuf, int w, int h, int pixelFormat)138 static void saveImage(String fileName, byte[] srcBuf, int w, int h, 139 int pixelFormat) throws Exception { 140 BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); 141 int pixels = w * h, srcPtr = 0; 142 int ps = TJ.getPixelSize(pixelFormat); 143 int rindex = TJ.getRedOffset(pixelFormat); 144 int gindex = TJ.getGreenOffset(pixelFormat); 145 int bindex = TJ.getBlueOffset(pixelFormat); 146 147 for (int y = 0; y < h; y++) { 148 for (int x = 0; x < w; x++, srcPtr += ps) { 149 int pixel = (srcBuf[srcPtr + rindex] & 0xff) << 16 | 150 (srcBuf[srcPtr + gindex] & 0xff) << 8 | 151 (srcBuf[srcPtr + bindex] & 0xff); 152 153 img.setRGB(x, y, pixel); 154 } 155 } 156 ImageIO.write(img, "bmp", new File(fileName)); 157 } 158 159 160 /* Decompression test */ decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, byte[] dstBuf, int w, int h, int subsamp, int jpegQual, String fileName, int tilew, int tileh)161 static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, 162 byte[] dstBuf, int w, int h, int subsamp, int jpegQual, 163 String fileName, int tilew, int tileh) throws Exception { 164 String qualStr = new String(""), sizeStr, tempStr; 165 TJDecompressor tjd; 166 double elapsed, elapsedDecode; 167 int ps = TJ.getPixelSize(pf), i, iter = 0; 168 int scaledw = sf.getScaled(w); 169 int scaledh = sf.getScaled(h); 170 int pitch = scaledw * ps; 171 YUVImage yuvImage = null; 172 173 if (jpegQual > 0) 174 qualStr = new String("_Q" + jpegQual); 175 176 tjd = new TJDecompressor(); 177 178 if (dstBuf == null) 179 dstBuf = new byte[pitch * scaledh]; 180 181 /* Set the destination buffer to gray so we know whether the decompressor 182 attempted to write to it */ 183 Arrays.fill(dstBuf, (byte)127); 184 185 if (doYUV) { 186 int width = doTile ? tilew : scaledw; 187 int height = doTile ? tileh : scaledh; 188 189 yuvImage = new YUVImage(width, yuvPad, height, subsamp); 190 Arrays.fill(yuvImage.getBuf(), (byte)127); 191 } 192 193 /* Benchmark */ 194 iter = -1; 195 elapsed = elapsedDecode = 0.0; 196 while (true) { 197 int tile = 0; 198 double start = getTime(); 199 200 for (int y = 0; y < h; y += tileh) { 201 for (int x = 0; x < w; x += tilew, tile++) { 202 int width = doTile ? Math.min(tilew, w - x) : scaledw; 203 int height = doTile ? Math.min(tileh, h - y) : scaledh; 204 205 try { 206 tjd.setSourceImage(jpegBuf[tile], jpegSize[tile]); 207 } catch (TJException e) { handleTJException(e); } 208 if (doYUV) { 209 yuvImage.setBuf(yuvImage.getBuf(), width, yuvPad, height, subsamp); 210 try { 211 tjd.decompressToYUV(yuvImage, flags); 212 } catch (TJException e) { handleTJException(e); } 213 double startDecode = getTime(); 214 tjd.setSourceImage(yuvImage); 215 try { 216 tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags); 217 } catch (TJException e) { handleTJException(e); } 218 if (iter >= 0) 219 elapsedDecode += getTime() - startDecode; 220 } else { 221 try { 222 tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags); 223 } catch (TJException e) { handleTJException(e); } 224 } 225 } 226 } 227 elapsed += getTime() - start; 228 if (iter >= 0) { 229 iter++; 230 if (elapsed >= benchTime) 231 break; 232 } else if (elapsed >= warmup) { 233 iter = 0; 234 elapsed = elapsedDecode = 0.0; 235 } 236 } 237 if (doYUV) 238 elapsed -= elapsedDecode; 239 240 tjd = null; 241 for (i = 0; i < jpegBuf.length; i++) 242 jpegBuf[i] = null; 243 jpegBuf = null; jpegSize = null; 244 System.gc(); 245 246 if (quiet != 0) { 247 System.out.format("%-6s%s", 248 sigFig((double)(w * h) / 1000000. * 249 (double)iter / elapsed, 4), 250 quiet == 2 ? "\n" : " "); 251 if (doYUV) 252 System.out.format("%s\n", 253 sigFig((double)(w * h) / 1000000. * 254 (double)iter / elapsedDecode, 4)); 255 else if (quiet != 2) 256 System.out.print("\n"); 257 } else { 258 System.out.format("%s --> Frame rate: %f fps\n", 259 (doYUV ? "Decomp to YUV" : "Decompress "), 260 (double)iter / elapsed); 261 System.out.format(" Throughput: %f Megapixels/sec\n", 262 (double)(w * h) / 1000000. * (double)iter / elapsed); 263 if (doYUV) { 264 System.out.format("YUV Decode --> Frame rate: %f fps\n", 265 (double)iter / elapsedDecode); 266 System.out.format(" Throughput: %f Megapixels/sec\n", 267 (double)(w * h) / 1000000. * 268 (double)iter / elapsedDecode); 269 } 270 } 271 272 if (!write) return; 273 274 if (sf.getNum() != 1 || sf.getDenom() != 1) 275 sizeStr = new String(sf.getNum() + "_" + sf.getDenom()); 276 else if (tilew != w || tileh != h) 277 sizeStr = new String(tilew + "x" + tileh); 278 else 279 sizeStr = new String("full"); 280 if (decompOnly) 281 tempStr = new String(fileName + "_" + sizeStr + ".bmp"); 282 else 283 tempStr = new String(fileName + "_" + SUBNAME[subsamp] + qualStr + 284 "_" + sizeStr + ".bmp"); 285 286 saveImage(tempStr, dstBuf, scaledw, scaledh, pf); 287 int ndx = tempStr.lastIndexOf('.'); 288 tempStr = new String(tempStr.substring(0, ndx) + "-err.bmp"); 289 if (srcBuf != null && sf.getNum() == 1 && sf.getDenom() == 1) { 290 if (quiet == 0) 291 System.out.println("Compression error written to " + tempStr + "."); 292 if (subsamp == TJ.SAMP_GRAY) { 293 for (int y = 0, index = 0; y < h; y++, index += pitch) { 294 for (int x = 0, index2 = index; x < w; x++, index2 += ps) { 295 int rindex = index2 + TJ.getRedOffset(pf); 296 int gindex = index2 + TJ.getGreenOffset(pf); 297 int bindex = index2 + TJ.getBlueOffset(pf); 298 int lum = (int)((double)(srcBuf[rindex] & 0xff) * 0.299 + 299 (double)(srcBuf[gindex] & 0xff) * 0.587 + 300 (double)(srcBuf[bindex] & 0xff) * 0.114 + 0.5); 301 302 if (lum > 255) lum = 255; 303 if (lum < 0) lum = 0; 304 dstBuf[rindex] = (byte)Math.abs((dstBuf[rindex] & 0xff) - lum); 305 dstBuf[gindex] = (byte)Math.abs((dstBuf[gindex] & 0xff) - lum); 306 dstBuf[bindex] = (byte)Math.abs((dstBuf[bindex] & 0xff) - lum); 307 } 308 } 309 } else { 310 for (int y = 0; y < h; y++) 311 for (int x = 0; x < w * ps; x++) 312 dstBuf[pitch * y + x] = 313 (byte)Math.abs((dstBuf[pitch * y + x] & 0xff) - 314 (srcBuf[pitch * y + x] & 0xff)); 315 } 316 saveImage(tempStr, dstBuf, w, h, pf); 317 } 318 } 319 320 fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, String fileName)321 static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, 322 String fileName) throws Exception { 323 TJCompressor tjc; 324 byte[] tmpBuf; 325 byte[][] jpegBuf; 326 int[] jpegSize; 327 double start, elapsed, elapsedEncode; 328 int totalJpegSize = 0, tilew, tileh, i, iter; 329 int ps = TJ.getPixelSize(pf); 330 int ntilesw = 1, ntilesh = 1, pitch = w * ps; 331 String pfStr = PIXFORMATSTR[pf]; 332 YUVImage yuvImage = null; 333 334 tmpBuf = new byte[pitch * h]; 335 336 if (quiet == 0) 337 System.out.format(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pfStr, 338 (flags & TJ.FLAG_BOTTOMUP) != 0 ? 339 "Bottom-up" : "Top-down", 340 SUBNAME_LONG[subsamp], jpegQual); 341 342 tjc = new TJCompressor(); 343 344 for (tilew = doTile ? 8 : w, tileh = doTile ? 8 : h; ; 345 tilew *= 2, tileh *= 2) { 346 if (tilew > w) 347 tilew = w; 348 if (tileh > h) 349 tileh = h; 350 ntilesw = (w + tilew - 1) / tilew; 351 ntilesh = (h + tileh - 1) / tileh; 352 353 jpegBuf = new byte[ntilesw * ntilesh][TJ.bufSize(tilew, tileh, subsamp)]; 354 jpegSize = new int[ntilesw * ntilesh]; 355 356 /* Compression test */ 357 if (quiet == 1) 358 System.out.format("%-4s (%s) %-5s %-3d ", pfStr, 359 (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD", 360 SUBNAME_LONG[subsamp], jpegQual); 361 for (i = 0; i < h; i++) 362 System.arraycopy(srcBuf, w * ps * i, tmpBuf, pitch * i, w * ps); 363 tjc.setJPEGQuality(jpegQual); 364 tjc.setSubsamp(subsamp); 365 366 if (doYUV) { 367 yuvImage = new YUVImage(tilew, yuvPad, tileh, subsamp); 368 Arrays.fill(yuvImage.getBuf(), (byte)127); 369 } 370 371 /* Benchmark */ 372 iter = -1; 373 elapsed = elapsedEncode = 0.0; 374 while (true) { 375 int tile = 0; 376 377 totalJpegSize = 0; 378 start = getTime(); 379 for (int y = 0; y < h; y += tileh) { 380 for (int x = 0; x < w; x += tilew, tile++) { 381 int width = Math.min(tilew, w - x); 382 int height = Math.min(tileh, h - y); 383 384 tjc.setSourceImage(srcBuf, x, y, width, pitch, height, pf); 385 if (doYUV) { 386 double startEncode = getTime(); 387 388 yuvImage.setBuf(yuvImage.getBuf(), width, yuvPad, height, 389 subsamp); 390 tjc.encodeYUV(yuvImage, flags); 391 if (iter >= 0) 392 elapsedEncode += getTime() - startEncode; 393 tjc.setSourceImage(yuvImage); 394 } 395 tjc.compress(jpegBuf[tile], flags); 396 jpegSize[tile] = tjc.getCompressedSize(); 397 totalJpegSize += jpegSize[tile]; 398 } 399 } 400 elapsed += getTime() - start; 401 if (iter >= 0) { 402 iter++; 403 if (elapsed >= benchTime) 404 break; 405 } else if (elapsed >= warmup) { 406 iter = 0; 407 elapsed = elapsedEncode = 0.0; 408 } 409 } 410 if (doYUV) 411 elapsed -= elapsedEncode; 412 413 if (quiet == 1) 414 System.out.format("%-5d %-5d ", tilew, tileh); 415 if (quiet != 0) { 416 if (doYUV) 417 System.out.format("%-6s%s", 418 sigFig((double)(w * h) / 1000000. * 419 (double)iter / elapsedEncode, 4), 420 quiet == 2 ? "\n" : " "); 421 System.out.format("%-6s%s", 422 sigFig((double)(w * h) / 1000000. * 423 (double)iter / elapsed, 4), 424 quiet == 2 ? "\n" : " "); 425 System.out.format("%-6s%s", 426 sigFig((double)(w * h * ps) / (double)totalJpegSize, 427 4), 428 quiet == 2 ? "\n" : " "); 429 } else { 430 System.out.format("\n%s size: %d x %d\n", doTile ? "Tile" : "Image", 431 tilew, tileh); 432 if (doYUV) { 433 System.out.format("Encode YUV --> Frame rate: %f fps\n", 434 (double)iter / elapsedEncode); 435 System.out.format(" Output image size: %d bytes\n", 436 yuvImage.getSize()); 437 System.out.format(" Compression ratio: %f:1\n", 438 (double)(w * h * ps) / (double)yuvImage.getSize()); 439 System.out.format(" Throughput: %f Megapixels/sec\n", 440 (double)(w * h) / 1000000. * 441 (double)iter / elapsedEncode); 442 System.out.format(" Output bit stream: %f Megabits/sec\n", 443 (double)yuvImage.getSize() * 8. / 1000000. * 444 (double)iter / elapsedEncode); 445 } 446 System.out.format("%s --> Frame rate: %f fps\n", 447 doYUV ? "Comp from YUV" : "Compress ", 448 (double)iter / elapsed); 449 System.out.format(" Output image size: %d bytes\n", 450 totalJpegSize); 451 System.out.format(" Compression ratio: %f:1\n", 452 (double)(w * h * ps) / (double)totalJpegSize); 453 System.out.format(" Throughput: %f Megapixels/sec\n", 454 (double)(w * h) / 1000000. * (double)iter / elapsed); 455 System.out.format(" Output bit stream: %f Megabits/sec\n", 456 (double)totalJpegSize * 8. / 1000000. * 457 (double)iter / elapsed); 458 } 459 if (tilew == w && tileh == h && write) { 460 String tempStr = fileName + "_" + SUBNAME[subsamp] + "_" + "Q" + 461 jpegQual + ".jpg"; 462 FileOutputStream fos = new FileOutputStream(tempStr); 463 464 fos.write(jpegBuf[0], 0, jpegSize[0]); 465 fos.close(); 466 if (quiet == 0) 467 System.out.println("Reference image written to " + tempStr); 468 } 469 470 /* Decompression test */ 471 if (!compOnly) 472 decomp(srcBuf, jpegBuf, jpegSize, tmpBuf, w, h, subsamp, jpegQual, 473 fileName, tilew, tileh); 474 475 if (tilew == w && tileh == h) break; 476 } 477 } 478 479 decompTest(String fileName)480 static void decompTest(String fileName) throws Exception { 481 TJTransformer tjt; 482 byte[][] jpegBuf = null; 483 byte[] srcBuf; 484 int[] jpegSize = null; 485 int totalJpegSize; 486 double start, elapsed; 487 int ps = TJ.getPixelSize(pf), tile, x, y, iter; 488 // Original image 489 int w = 0, h = 0, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1; 490 // Transformed image 491 int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp; 492 493 FileInputStream fis = new FileInputStream(fileName); 494 int srcSize = (int)fis.getChannel().size(); 495 srcBuf = new byte[srcSize]; 496 fis.read(srcBuf, 0, srcSize); 497 fis.close(); 498 499 int index = fileName.lastIndexOf('.'); 500 if (index >= 0) 501 fileName = new String(fileName.substring(0, index)); 502 503 tjt = new TJTransformer(); 504 505 try { 506 tjt.setSourceImage(srcBuf, srcSize); 507 } catch (TJException e) { handleTJException(e); } 508 w = tjt.getWidth(); 509 h = tjt.getHeight(); 510 subsamp = tjt.getSubsamp(); 511 cs = tjt.getColorspace(); 512 513 if (quiet == 1) { 514 System.out.println("All performance values in Mpixels/sec\n"); 515 System.out.format("Bitmap JPEG JPEG %s %s Xform Comp Decomp ", 516 (doTile ? "Tile " : "Image"), 517 (doTile ? "Tile " : "Image")); 518 if (doYUV) 519 System.out.print("Decode"); 520 System.out.print("\n"); 521 System.out.print("Format CS Subsamp Width Height Perf Ratio Perf "); 522 if (doYUV) 523 System.out.print("Perf"); 524 System.out.println("\n"); 525 } else if (quiet == 0) 526 System.out.format(">>>>> JPEG %s --> %s (%s) <<<<<\n", 527 formatName(subsamp, cs), PIXFORMATSTR[pf], 528 (flags & TJ.FLAG_BOTTOMUP) != 0 ? 529 "Bottom-up" : "Top-down"); 530 531 for (int tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ; 532 tilew *= 2, tileh *= 2) { 533 if (tilew > w) 534 tilew = w; 535 if (tileh > h) 536 tileh = h; 537 ntilesw = (w + tilew - 1) / tilew; 538 ntilesh = (h + tileh - 1) / tileh; 539 540 tw = w; th = h; ttilew = tilew; ttileh = tileh; 541 if (quiet == 0) { 542 System.out.format("\n%s size: %d x %d", (doTile ? "Tile" : "Image"), 543 ttilew, ttileh); 544 if (sf.getNum() != 1 || sf.getDenom() != 1) 545 System.out.format(" --> %d x %d", sf.getScaled(tw), 546 sf.getScaled(th)); 547 System.out.println(""); 548 } else if (quiet == 1) { 549 System.out.format("%-4s (%s) %-5s %-5s ", PIXFORMATSTR[pf], 550 (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD", 551 CSNAME[cs], SUBNAME_LONG[subsamp]); 552 System.out.format("%-5d %-5d ", tilew, tileh); 553 } 554 555 tsubsamp = subsamp; 556 if (doTile || xformOp != TJTransform.OP_NONE || xformOpt != 0) { 557 if (xformOp == TJTransform.OP_TRANSPOSE || 558 xformOp == TJTransform.OP_TRANSVERSE || 559 xformOp == TJTransform.OP_ROT90 || 560 xformOp == TJTransform.OP_ROT270) { 561 tw = h; th = w; ttilew = tileh; ttileh = tilew; 562 } 563 564 if ((xformOpt & TJTransform.OPT_GRAY) != 0) 565 tsubsamp = TJ.SAMP_GRAY; 566 if (xformOp == TJTransform.OP_HFLIP || 567 xformOp == TJTransform.OP_ROT180) 568 tw = tw - (tw % TJ.getMCUWidth(tsubsamp)); 569 if (xformOp == TJTransform.OP_VFLIP || 570 xformOp == TJTransform.OP_ROT180) 571 th = th - (th % TJ.getMCUHeight(tsubsamp)); 572 if (xformOp == TJTransform.OP_TRANSVERSE || 573 xformOp == TJTransform.OP_ROT90) 574 tw = tw - (tw % TJ.getMCUHeight(tsubsamp)); 575 if (xformOp == TJTransform.OP_TRANSVERSE || 576 xformOp == TJTransform.OP_ROT270) 577 th = th - (th % TJ.getMCUWidth(tsubsamp)); 578 tntilesw = (tw + ttilew - 1) / ttilew; 579 tntilesh = (th + ttileh - 1) / ttileh; 580 581 if (xformOp == TJTransform.OP_TRANSPOSE || 582 xformOp == TJTransform.OP_TRANSVERSE || 583 xformOp == TJTransform.OP_ROT90 || 584 xformOp == TJTransform.OP_ROT270) { 585 if (tsubsamp == TJ.SAMP_422) 586 tsubsamp = TJ.SAMP_440; 587 else if (tsubsamp == TJ.SAMP_440) 588 tsubsamp = TJ.SAMP_422; 589 } 590 591 TJTransform[] t = new TJTransform[tntilesw * tntilesh]; 592 jpegBuf = 593 new byte[tntilesw * tntilesh][TJ.bufSize(ttilew, ttileh, subsamp)]; 594 595 for (y = 0, tile = 0; y < th; y += ttileh) { 596 for (x = 0; x < tw; x += ttilew, tile++) { 597 t[tile] = new TJTransform(); 598 t[tile].width = Math.min(ttilew, tw - x); 599 t[tile].height = Math.min(ttileh, th - y); 600 t[tile].x = x; 601 t[tile].y = y; 602 t[tile].op = xformOp; 603 t[tile].options = xformOpt | TJTransform.OPT_TRIM; 604 if ((t[tile].options & TJTransform.OPT_NOOUTPUT) != 0 && 605 jpegBuf[tile] != null) 606 jpegBuf[tile] = null; 607 } 608 } 609 610 iter = -1; 611 elapsed = 0.; 612 while (true) { 613 start = getTime(); 614 try { 615 tjt.transform(jpegBuf, t, flags); 616 } catch (TJException e) { handleTJException(e); } 617 jpegSize = tjt.getTransformedSizes(); 618 elapsed += getTime() - start; 619 if (iter >= 0) { 620 iter++; 621 if (elapsed >= benchTime) 622 break; 623 } else if (elapsed >= warmup) { 624 iter = 0; 625 elapsed = 0.0; 626 } 627 } 628 t = null; 629 630 for (tile = 0, totalJpegSize = 0; tile < tntilesw * tntilesh; tile++) 631 totalJpegSize += jpegSize[tile]; 632 633 if (quiet != 0) { 634 System.out.format("%-6s%s%-6s%s", 635 sigFig((double)(w * h) / 1000000. / elapsed, 4), 636 quiet == 2 ? "\n" : " ", 637 sigFig((double)(w * h * ps) / 638 (double)totalJpegSize, 4), 639 quiet == 2 ? "\n" : " "); 640 } else if (quiet == 0) { 641 System.out.format("Transform --> Frame rate: %f fps\n", 642 1.0 / elapsed); 643 System.out.format(" Output image size: %d bytes\n", 644 totalJpegSize); 645 System.out.format(" Compression ratio: %f:1\n", 646 (double)(w * h * ps) / (double)totalJpegSize); 647 System.out.format(" Throughput: %f Megapixels/sec\n", 648 (double)(w * h) / 1000000. / elapsed); 649 System.out.format(" Output bit stream: %f Megabits/sec\n", 650 (double)totalJpegSize * 8. / 1000000. / elapsed); 651 } 652 } else { 653 if (quiet == 1) 654 System.out.print("N/A N/A "); 655 jpegBuf = new byte[1][TJ.bufSize(ttilew, ttileh, subsamp)]; 656 jpegSize = new int[1]; 657 jpegBuf[0] = srcBuf; 658 jpegSize[0] = srcSize; 659 } 660 661 if (w == tilew) 662 ttilew = tw; 663 if (h == tileh) 664 ttileh = th; 665 if ((xformOpt & TJTransform.OPT_NOOUTPUT) == 0) 666 decomp(null, jpegBuf, jpegSize, null, tw, th, tsubsamp, 0, 667 fileName, ttilew, ttileh); 668 else if (quiet == 1) 669 System.out.println("N/A"); 670 671 jpegBuf = null; 672 jpegSize = null; 673 674 if (tilew == w && tileh == h) break; 675 } 676 } 677 678 usage()679 static void usage() throws Exception { 680 int i; 681 TJScalingFactor[] scalingFactors = TJ.getScalingFactors(); 682 int nsf = scalingFactors.length; 683 String className = new TJBench().getClass().getName(); 684 685 System.out.println("\nUSAGE: java " + className); 686 System.out.println(" <Inputfile (BMP)> <Quality> [options]\n"); 687 System.out.println(" java " + className); 688 System.out.println(" <Inputfile (JPG)> [options]\n"); 689 System.out.println("Options:\n"); 690 System.out.println("-alloc = Dynamically allocate JPEG image buffers"); 691 System.out.println("-bottomup = Test bottom-up compression/decompression"); 692 System.out.println("-tile = Test performance of the codec when the image is encoded as separate"); 693 System.out.println(" tiles of varying sizes."); 694 System.out.println("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb ="); 695 System.out.println(" Test the specified color conversion path in the codec (default = BGR)"); 696 System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in"); 697 System.out.println(" the underlying codec"); 698 System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying"); 699 System.out.println(" codec"); 700 System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the"); 701 System.out.println(" underlying codec"); 702 System.out.println("-progressive = Use progressive entropy coding in JPEG images generated by"); 703 System.out.println(" compression and transform operations."); 704 System.out.println("-subsamp <s> = When testing JPEG compression, this option specifies the level"); 705 System.out.println(" of chrominance subsampling to use (<s> = 444, 422, 440, 420, 411, or"); 706 System.out.println(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in"); 707 System.out.println(" sequence."); 708 System.out.println("-quiet = Output results in tabular rather than verbose format"); 709 System.out.println("-yuv = Test YUV encoding/decoding functions"); 710 System.out.println("-yuvpad <p> = If testing YUV encoding/decoding, this specifies the number of"); 711 System.out.println(" bytes to which each row of each plane in the intermediate YUV image is"); 712 System.out.println(" padded (default = 1)"); 713 System.out.println("-scale M/N = Scale down the width/height of the decompressed JPEG image by a"); 714 System.out.print (" factor of M/N (M/N = "); 715 for (i = 0; i < nsf; i++) { 716 System.out.format("%d/%d", scalingFactors[i].getNum(), 717 scalingFactors[i].getDenom()); 718 if (nsf == 2 && i != nsf - 1) 719 System.out.print(" or "); 720 else if (nsf > 2) { 721 if (i != nsf - 1) 722 System.out.print(", "); 723 if (i == nsf - 2) 724 System.out.print("or "); 725 } 726 if (i % 8 == 0 && i != 0) 727 System.out.print("\n "); 728 } 729 System.out.println(")"); 730 System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 ="); 731 System.out.println(" Perform the corresponding lossless transform prior to"); 732 System.out.println(" decompression (these options are mutually exclusive)"); 733 System.out.println("-grayscale = Perform lossless grayscale conversion prior to decompression"); 734 System.out.println(" test (can be combined with the other transforms above)"); 735 System.out.println("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)"); 736 System.out.println(" when transforming the image."); 737 System.out.println("-benchtime <t> = Run each benchmark for at least <t> seconds (default = 5.0)"); 738 System.out.println("-warmup <t> = Run each benchmark for <t> seconds (default = 1.0) prior to"); 739 System.out.println(" starting the timer, in order to prime the caches and thus improve the"); 740 System.out.println(" consistency of the results."); 741 System.out.println("-componly = Stop after running compression tests. Do not test decompression."); 742 System.out.println("-nowrite = Do not write reference or output images (improves consistency"); 743 System.out.println(" of performance measurements.)"); 744 System.out.println("-stoponwarning = Immediately discontinue the current"); 745 System.out.println(" compression/decompression/transform operation if the underlying codec"); 746 System.out.println(" throws a warning (non-fatal error)\n"); 747 System.out.println("NOTE: If the quality is specified as a range (e.g. 90-100), a separate"); 748 System.out.println("test will be performed for all quality values in the range.\n"); 749 System.exit(1); 750 } 751 752 main(String[] argv)753 public static void main(String[] argv) { 754 byte[] srcBuf = null; 755 int w = 0, h = 0, minQual = -1, maxQual = -1; 756 int minArg = 1, retval = 0; 757 int subsamp = -1; 758 759 try { 760 761 if (argv.length < minArg) 762 usage(); 763 764 String tempStr = argv[0].toLowerCase(); 765 if (tempStr.endsWith(".jpg") || tempStr.endsWith(".jpeg")) 766 decompOnly = true; 767 768 System.out.println(""); 769 770 if (!decompOnly) { 771 minArg = 2; 772 if (argv.length < minArg) 773 usage(); 774 try { 775 minQual = Integer.parseInt(argv[1]); 776 } catch (NumberFormatException e) {} 777 if (minQual < 1 || minQual > 100) 778 throw new Exception("Quality must be between 1 and 100."); 779 int dashIndex = argv[1].indexOf('-'); 780 if (dashIndex > 0 && argv[1].length() > dashIndex + 1) { 781 try { 782 maxQual = Integer.parseInt(argv[1].substring(dashIndex + 1)); 783 } catch (NumberFormatException e) {} 784 } 785 if (maxQual < 1 || maxQual > 100) 786 maxQual = minQual; 787 } 788 789 if (argv.length > minArg) { 790 for (int i = minArg; i < argv.length; i++) { 791 if (argv[i].equalsIgnoreCase("-tile")) { 792 doTile = true; xformOpt |= TJTransform.OPT_CROP; 793 } else if (argv[i].equalsIgnoreCase("-fastupsample")) { 794 System.out.println("Using fast upsampling code\n"); 795 flags |= TJ.FLAG_FASTUPSAMPLE; 796 } else if (argv[i].equalsIgnoreCase("-fastdct")) { 797 System.out.println("Using fastest DCT/IDCT algorithm\n"); 798 flags |= TJ.FLAG_FASTDCT; 799 } else if (argv[i].equalsIgnoreCase("-accuratedct")) { 800 System.out.println("Using most accurate DCT/IDCT algorithm\n"); 801 flags |= TJ.FLAG_ACCURATEDCT; 802 } else if (argv[i].equalsIgnoreCase("-progressive")) { 803 System.out.println("Using progressive entropy coding\n"); 804 flags |= TJ.FLAG_PROGRESSIVE; 805 } else if (argv[i].equalsIgnoreCase("-rgb")) 806 pf = TJ.PF_RGB; 807 else if (argv[i].equalsIgnoreCase("-rgbx")) 808 pf = TJ.PF_RGBX; 809 else if (argv[i].equalsIgnoreCase("-bgr")) 810 pf = TJ.PF_BGR; 811 else if (argv[i].equalsIgnoreCase("-bgrx")) 812 pf = TJ.PF_BGRX; 813 else if (argv[i].equalsIgnoreCase("-xbgr")) 814 pf = TJ.PF_XBGR; 815 else if (argv[i].equalsIgnoreCase("-xrgb")) 816 pf = TJ.PF_XRGB; 817 else if (argv[i].equalsIgnoreCase("-bottomup")) 818 flags |= TJ.FLAG_BOTTOMUP; 819 else if (argv[i].equalsIgnoreCase("-quiet")) 820 quiet = 1; 821 else if (argv[i].equalsIgnoreCase("-qq")) 822 quiet = 2; 823 else if (argv[i].equalsIgnoreCase("-scale") && i < argv.length - 1) { 824 int temp1 = 0, temp2 = 0; 825 boolean match = false, scanned = true; 826 Scanner scanner = new Scanner(argv[++i]).useDelimiter("/"); 827 828 try { 829 temp1 = scanner.nextInt(); 830 temp2 = scanner.nextInt(); 831 } catch (Exception e) {} 832 if (temp2 <= 0) temp2 = 1; 833 if (temp1 > 0) { 834 TJScalingFactor[] scalingFactors = TJ.getScalingFactors(); 835 836 for (int j = 0; j < scalingFactors.length; j++) { 837 if ((double)temp1 / (double)temp2 == 838 (double)scalingFactors[j].getNum() / 839 (double)scalingFactors[j].getDenom()) { 840 sf = scalingFactors[j]; 841 match = true; break; 842 } 843 } 844 if (!match) usage(); 845 } else 846 usage(); 847 } else if (argv[i].equalsIgnoreCase("-hflip")) 848 xformOp = TJTransform.OP_HFLIP; 849 else if (argv[i].equalsIgnoreCase("-vflip")) 850 xformOp = TJTransform.OP_VFLIP; 851 else if (argv[i].equalsIgnoreCase("-transpose")) 852 xformOp = TJTransform.OP_TRANSPOSE; 853 else if (argv[i].equalsIgnoreCase("-transverse")) 854 xformOp = TJTransform.OP_TRANSVERSE; 855 else if (argv[i].equalsIgnoreCase("-rot90")) 856 xformOp = TJTransform.OP_ROT90; 857 else if (argv[i].equalsIgnoreCase("-rot180")) 858 xformOp = TJTransform.OP_ROT180; 859 else if (argv[i].equalsIgnoreCase("-rot270")) 860 xformOp = TJTransform.OP_ROT270; 861 else if (argv[i].equalsIgnoreCase("-grayscale")) 862 xformOpt |= TJTransform.OPT_GRAY; 863 else if (argv[i].equalsIgnoreCase("-nooutput")) 864 xformOpt |= TJTransform.OPT_NOOUTPUT; 865 else if (argv[i].equalsIgnoreCase("-copynone")) 866 xformOpt |= TJTransform.OPT_COPYNONE; 867 else if (argv[i].equalsIgnoreCase("-benchtime") && 868 i < argv.length - 1) { 869 double temp = -1; 870 871 try { 872 temp = Double.parseDouble(argv[++i]); 873 } catch (NumberFormatException e) {} 874 if (temp > 0.0) 875 benchTime = temp; 876 else 877 usage(); 878 } else if (argv[i].equalsIgnoreCase("-warmup") && 879 i < argv.length - 1) { 880 double temp = -1; 881 882 try { 883 temp = Double.parseDouble(argv[++i]); 884 } catch (NumberFormatException e) {} 885 if (temp >= 0.0) { 886 warmup = temp; 887 System.out.format("Warmup time = %.1f seconds\n\n", warmup); 888 } else 889 usage(); 890 } else if (argv[i].equalsIgnoreCase("-yuv")) { 891 System.out.println("Testing YUV planar encoding/decoding\n"); 892 doYUV = true; 893 } else if (argv[i].equalsIgnoreCase("-yuvpad") && 894 i < argv.length - 1) { 895 int temp = 0; 896 897 try { 898 temp = Integer.parseInt(argv[++i]); 899 } catch (NumberFormatException e) {} 900 if (temp >= 1) 901 yuvPad = temp; 902 } else if (argv[i].equalsIgnoreCase("-subsamp") && 903 i < argv.length - 1) { 904 i++; 905 if (argv[i].toUpperCase().startsWith("G")) 906 subsamp = TJ.SAMP_GRAY; 907 else if (argv[i].equals("444")) 908 subsamp = TJ.SAMP_444; 909 else if (argv[i].equals("422")) 910 subsamp = TJ.SAMP_422; 911 else if (argv[i].equals("440")) 912 subsamp = TJ.SAMP_440; 913 else if (argv[i].equals("420")) 914 subsamp = TJ.SAMP_420; 915 else if (argv[i].equals("411")) 916 subsamp = TJ.SAMP_411; 917 } else if (argv[i].equalsIgnoreCase("-componly")) 918 compOnly = true; 919 else if (argv[i].equalsIgnoreCase("-nowrite")) 920 write = false; 921 else if (argv[i].equalsIgnoreCase("-stoponwarning")) 922 flags |= TJ.FLAG_STOPONWARNING; 923 else usage(); 924 } 925 } 926 927 if (sf == null) 928 sf = new TJScalingFactor(1, 1); 929 930 if ((sf.getNum() != 1 || sf.getDenom() != 1) && doTile) { 931 System.out.println("Disabling tiled compression/decompression tests, because those tests do not"); 932 System.out.println("work when scaled decompression is enabled."); 933 doTile = false; 934 } 935 936 if (!decompOnly) { 937 int[] width = new int[1], height = new int[1]; 938 939 srcBuf = loadImage(argv[0], width, height, pf); 940 w = width[0]; h = height[0]; 941 int index = -1; 942 if ((index = argv[0].lastIndexOf('.')) >= 0) 943 argv[0] = argv[0].substring(0, index); 944 } 945 946 if (quiet == 1 && !decompOnly) { 947 System.out.println("All performance values in Mpixels/sec\n"); 948 System.out.format("Bitmap JPEG JPEG %s %s ", 949 (doTile ? "Tile " : "Image"), 950 (doTile ? "Tile " : "Image")); 951 if (doYUV) 952 System.out.print("Encode "); 953 System.out.print("Comp Comp Decomp "); 954 if (doYUV) 955 System.out.print("Decode"); 956 System.out.print("\n"); 957 System.out.print("Format Subsamp Qual Width Height "); 958 if (doYUV) 959 System.out.print("Perf "); 960 System.out.print("Perf Ratio Perf "); 961 if (doYUV) 962 System.out.print("Perf"); 963 System.out.println("\n"); 964 } 965 966 if (decompOnly) { 967 decompTest(argv[0]); 968 System.out.println(""); 969 System.exit(retval); 970 } 971 972 System.gc(); 973 if (subsamp >= 0 && subsamp < TJ.NUMSAMP) { 974 for (int i = maxQual; i >= minQual; i--) 975 fullTest(srcBuf, w, h, subsamp, i, argv[0]); 976 System.out.println(""); 977 } else { 978 for (int i = maxQual; i >= minQual; i--) 979 fullTest(srcBuf, w, h, TJ.SAMP_GRAY, i, argv[0]); 980 System.out.println(""); 981 System.gc(); 982 for (int i = maxQual; i >= minQual; i--) 983 fullTest(srcBuf, w, h, TJ.SAMP_420, i, argv[0]); 984 System.out.println(""); 985 System.gc(); 986 for (int i = maxQual; i >= minQual; i--) 987 fullTest(srcBuf, w, h, TJ.SAMP_422, i, argv[0]); 988 System.out.println(""); 989 System.gc(); 990 for (int i = maxQual; i >= minQual; i--) 991 fullTest(srcBuf, w, h, TJ.SAMP_444, i, argv[0]); 992 System.out.println(""); 993 } 994 995 } catch (Exception e) { 996 if (e instanceof TJException) { 997 TJException tje = (TJException)e; 998 999 System.out.println((tje.getErrorCode() == TJ.ERR_WARNING ? 1000 "WARNING: " : "ERROR: ") + tje.getMessage()); 1001 } else 1002 System.out.println("ERROR: " + e.getMessage()); 1003 e.printStackTrace(); 1004 retval = -1; 1005 } 1006 1007 System.exit(retval); 1008 } 1009 1010 } 1011