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