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