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