/* * Copyright (C)2009-2014, 2016-2017 D. R. Commander. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the libjpeg-turbo Project nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ import java.io.*; import java.awt.image.*; import javax.imageio.*; import java.util.*; import org.libjpegturbo.turbojpeg.*; class TJBench { static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvpad = 1; static boolean compOnly, decompOnly, doTile, doYUV, write = true; static final String[] pixFormatStr = { "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY" }; static final String[] subNameLong = { "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1" }; static final String[] subName = { "444", "422", "420", "GRAY", "440", "411" }; static final String[] csName = { "RGB", "YCbCr", "GRAY", "CMYK", "YCCK" }; static TJScalingFactor sf; static int xformOp = TJTransform.OP_NONE, xformOpt = 0; static double benchTime = 5.0, warmup = 1.0; static final double getTime() { return (double)System.nanoTime() / 1.0e9; } static String formatName(int subsamp, int cs) { if (cs == TJ.CS_YCbCr) return subNameLong[subsamp]; else if (cs == TJ.CS_YCCK) return csName[cs] + " " + subNameLong[subsamp]; else return csName[cs]; } static String sigFig(double val, int figs) { String format; int digitsAfterDecimal = figs - (int)Math.ceil(Math.log10(Math.abs(val))); if (digitsAfterDecimal < 1) format = new String("%.0f"); else format = new String("%." + digitsAfterDecimal + "f"); return String.format(format, val); } static byte[] loadImage(String fileName, int[] w, int[] h, int pixelFormat) throws Exception { BufferedImage img = ImageIO.read(new File(fileName)); if (img == null) throw new Exception("Could not read " + fileName); w[0] = img.getWidth(); h[0] = img.getHeight(); int[] rgb = img.getRGB(0, 0, w[0], h[0], null, 0, w[0]); int ps = TJ.getPixelSize(pixelFormat); int rindex = TJ.getRedOffset(pixelFormat); int gindex = TJ.getGreenOffset(pixelFormat); int bindex = TJ.getBlueOffset(pixelFormat); if ((long)w[0] * (long)h[0] * (long)ps > (long)Integer.MAX_VALUE) throw new Exception("Image is too lange"); byte[] dstBuf = new byte[w[0] * h[0] * ps]; int pixels = w[0] * h[0], dstPtr = 0, rgbPtr = 0; while (pixels-- > 0) { dstBuf[dstPtr + rindex] = (byte)((rgb[rgbPtr] >> 16) & 0xff); dstBuf[dstPtr + gindex] = (byte)((rgb[rgbPtr] >> 8) & 0xff); dstBuf[dstPtr + bindex] = (byte)(rgb[rgbPtr] & 0xff); dstPtr += ps; rgbPtr++; } return dstBuf; } static void saveImage(String fileName, byte[] srcBuf, int w, int h, int pixelFormat) throws Exception { BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB); int pixels = w * h, srcPtr = 0; int ps = TJ.getPixelSize(pixelFormat); int rindex = TJ.getRedOffset(pixelFormat); int gindex = TJ.getGreenOffset(pixelFormat); int bindex = TJ.getBlueOffset(pixelFormat); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++, srcPtr += ps) { int pixel = (srcBuf[srcPtr + rindex] & 0xff) << 16 | (srcBuf[srcPtr + gindex] & 0xff) << 8 | (srcBuf[srcPtr + bindex] & 0xff); img.setRGB(x, y, pixel); } } ImageIO.write(img, "bmp", new File(fileName)); } /* Decompression test */ static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, byte[] dstBuf, int w, int h, int subsamp, int jpegQual, String fileName, int tilew, int tileh) throws Exception { String qualStr = new String(""), sizeStr, tempStr; TJDecompressor tjd; double elapsed, elapsedDecode; int ps = TJ.getPixelSize(pf), i, iter = 0; int scaledw = sf.getScaled(w); int scaledh = sf.getScaled(h); int pitch = scaledw * ps; YUVImage yuvImage = null; if (jpegQual > 0) qualStr = new String("_Q" + jpegQual); tjd = new TJDecompressor(); if (dstBuf == null) { if ((long)pitch * (long)scaledh > (long)Integer.MAX_VALUE) throw new Exception("Image is too large"); dstBuf = new byte[pitch * scaledh]; } /* Set the destination buffer to gray so we know whether the decompressor attempted to write to it */ Arrays.fill(dstBuf, (byte)127); if (doYUV) { int width = doTile ? tilew : scaledw; int height = doTile ? tileh : scaledh; yuvImage = new YUVImage(width, yuvpad, height, subsamp); Arrays.fill(yuvImage.getBuf(), (byte)127); } /* Benchmark */ iter = -1; elapsed = elapsedDecode = 0.0; while (true) { int tile = 0; double start = getTime(); for (int y = 0; y < h; y += tileh) { for (int x = 0; x < w; x += tilew, tile++) { int width = doTile ? Math.min(tilew, w - x) : scaledw; int height = doTile ? Math.min(tileh, h - y) : scaledh; tjd.setSourceImage(jpegBuf[tile], jpegSize[tile]); if (doYUV) { yuvImage.setBuf(yuvImage.getBuf(), width, yuvpad, height, subsamp); tjd.decompressToYUV(yuvImage, flags); double startDecode = getTime(); tjd.setSourceImage(yuvImage); tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags); if (iter >= 0) elapsedDecode += getTime() - startDecode; } else tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags); } } elapsed += getTime() - start; if (iter >= 0) { iter++; if (elapsed >= benchTime) break; } else if (elapsed >= warmup) { iter = 0; elapsed = elapsedDecode = 0.0; } } if(doYUV) elapsed -= elapsedDecode; tjd = null; for (i = 0; i < jpegBuf.length; i++) jpegBuf[i] = null; jpegBuf = null; jpegSize = null; System.gc(); if (quiet != 0) { System.out.format("%-6s%s", sigFig((double)(w * h) / 1000000. * (double)iter / elapsed, 4), quiet == 2 ? "\n" : " "); if (doYUV) System.out.format("%s\n", sigFig((double)(w * h) / 1000000. * (double)iter / elapsedDecode, 4)); else if (quiet != 2) System.out.print("\n"); } else { System.out.format("%s --> Frame rate: %f fps\n", (doYUV ? "Decomp to YUV":"Decompress "), (double)iter / elapsed); System.out.format(" Throughput: %f Megapixels/sec\n", (double)(w * h) / 1000000. * (double)iter / elapsed); if (doYUV) { System.out.format("YUV Decode --> Frame rate: %f fps\n", (double)iter / elapsedDecode); System.out.format(" Throughput: %f Megapixels/sec\n", (double)(w * h) / 1000000. * (double)iter / elapsedDecode); } } if (!write) return; if (sf.getNum() != 1 || sf.getDenom() != 1) sizeStr = new String(sf.getNum() + "_" + sf.getDenom()); else if (tilew != w || tileh != h) sizeStr = new String(tilew + "x" + tileh); else sizeStr = new String("full"); if (decompOnly) tempStr = new String(fileName + "_" + sizeStr + ".bmp"); else tempStr = new String(fileName + "_" + subName[subsamp] + qualStr + "_" + sizeStr + ".bmp"); saveImage(tempStr, dstBuf, scaledw, scaledh, pf); int ndx = tempStr.lastIndexOf('.'); tempStr = new String(tempStr.substring(0, ndx) + "-err.bmp"); if (srcBuf != null && sf.getNum() == 1 && sf.getDenom() == 1) { if (quiet == 0) System.out.println("Compression error written to " + tempStr + "."); if (subsamp == TJ.SAMP_GRAY) { for (int y = 0, index = 0; y < h; y++, index += pitch) { for (int x = 0, index2 = index; x < w; x++, index2 += ps) { int rindex = index2 + TJ.getRedOffset(pf); int gindex = index2 + TJ.getGreenOffset(pf); int bindex = index2 + TJ.getBlueOffset(pf); int lum = (int)((double)(srcBuf[rindex] & 0xff) * 0.299 + (double)(srcBuf[gindex] & 0xff) * 0.587 + (double)(srcBuf[bindex] & 0xff) * 0.114 + 0.5); if (lum > 255) lum = 255; if (lum < 0) lum = 0; dstBuf[rindex] = (byte)Math.abs((dstBuf[rindex] & 0xff) - lum); dstBuf[gindex] = (byte)Math.abs((dstBuf[gindex] & 0xff) - lum); dstBuf[bindex] = (byte)Math.abs((dstBuf[bindex] & 0xff) - lum); } } } else { for (int y = 0; y < h; y++) for (int x = 0; x < w * ps; x++) dstBuf[pitch * y + x] = (byte)Math.abs((dstBuf[pitch * y + x] & 0xff) - (srcBuf[pitch * y + x] & 0xff)); } saveImage(tempStr, dstBuf, w, h, pf); } } static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, String fileName) throws Exception { TJCompressor tjc; byte[] tmpBuf; byte[][] jpegBuf; int[] jpegSize; double start, elapsed, elapsedEncode; int totalJpegSize = 0, tilew, tileh, i, iter; int ps = TJ.getPixelSize(pf); int ntilesw = 1, ntilesh = 1, pitch = w * ps; String pfStr = pixFormatStr[pf]; YUVImage yuvImage = null; if ((long)pitch * (long)h > (long)Integer.MAX_VALUE) throw new Exception("Image is too lange"); tmpBuf = new byte[pitch * h]; if (quiet == 0) System.out.format(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pfStr, (flags & TJ.FLAG_BOTTOMUP) != 0 ? "Bottom-up" : "Top-down", subNameLong[subsamp], jpegQual); tjc = new TJCompressor(); for (tilew = doTile ? 8 : w, tileh = doTile ? 8 : h; ; tilew *= 2, tileh *= 2) { if (tilew > w) tilew = w; if (tileh > h) tileh = h; ntilesw = (w + tilew - 1) / tilew; ntilesh = (h + tileh - 1) / tileh; jpegBuf = new byte[ntilesw * ntilesh][TJ.bufSize(tilew, tileh, subsamp)]; jpegSize = new int[ntilesw * ntilesh]; /* Compression test */ if (quiet == 1) System.out.format("%-4s (%s) %-5s %-3d ", pfStr, (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD", subNameLong[subsamp], jpegQual); for (i = 0; i < h; i++) System.arraycopy(srcBuf, w * ps * i, tmpBuf, pitch * i, w * ps); tjc.setJPEGQuality(jpegQual); tjc.setSubsamp(subsamp); if (doYUV) { yuvImage = new YUVImage(tilew, yuvpad, tileh, subsamp); Arrays.fill(yuvImage.getBuf(), (byte)127); } /* Benchmark */ iter = -1; elapsed = elapsedEncode = 0.0; while (true) { int tile = 0; totalJpegSize = 0; start = getTime(); for (int y = 0; y < h; y += tileh) { for (int x = 0; x < w; x += tilew, tile++) { int width = Math.min(tilew, w - x); int height = Math.min(tileh, h - y); tjc.setSourceImage(srcBuf, x, y, width, pitch, height, pf); if (doYUV) { double startEncode = getTime(); yuvImage.setBuf(yuvImage.getBuf(), width, yuvpad, height, subsamp); tjc.encodeYUV(yuvImage, flags); if (iter >= 0) elapsedEncode += getTime() - startEncode; tjc.setSourceImage(yuvImage); } tjc.compress(jpegBuf[tile], flags); jpegSize[tile] = tjc.getCompressedSize(); totalJpegSize += jpegSize[tile]; } } elapsed += getTime() - start; if (iter >= 0) { iter++; if (elapsed >= benchTime) break; } else if (elapsed >= warmup) { iter = 0; elapsed = elapsedEncode = 0.0; } } if (doYUV) elapsed -= elapsedEncode; if (quiet == 1) System.out.format("%-5d %-5d ", tilew, tileh); if (quiet != 0) { if (doYUV) System.out.format("%-6s%s", sigFig((double)(w * h) / 1000000. * (double)iter / elapsedEncode, 4), quiet == 2 ? "\n" : " "); System.out.format("%-6s%s", sigFig((double)(w * h) / 1000000. * (double)iter / elapsed, 4), quiet == 2 ? "\n" : " "); System.out.format("%-6s%s", sigFig((double)(w * h * ps) / (double)totalJpegSize, 4), quiet == 2 ? "\n" : " "); } else { System.out.format("\n%s size: %d x %d\n", doTile ? "Tile" : "Image", tilew, tileh); if (doYUV) { System.out.format("Encode YUV --> Frame rate: %f fps\n", (double)iter / elapsedEncode); System.out.format(" Output image size: %d bytes\n", yuvImage.getSize()); System.out.format(" Compression ratio: %f:1\n", (double)(w * h * ps) / (double)yuvImage.getSize()); System.out.format(" Throughput: %f Megapixels/sec\n", (double)(w * h) / 1000000. * (double)iter / elapsedEncode); System.out.format(" Output bit stream: %f Megabits/sec\n", (double)yuvImage.getSize() * 8. / 1000000. * (double)iter / elapsedEncode); } System.out.format("%s --> Frame rate: %f fps\n", doYUV ? "Comp from YUV" : "Compress ", (double)iter / elapsed); System.out.format(" Output image size: %d bytes\n", totalJpegSize); System.out.format(" Compression ratio: %f:1\n", (double)(w * h * ps) / (double)totalJpegSize); System.out.format(" Throughput: %f Megapixels/sec\n", (double)(w * h) / 1000000. * (double)iter / elapsed); System.out.format(" Output bit stream: %f Megabits/sec\n", (double)totalJpegSize * 8. / 1000000. * (double)iter / elapsed); } if (tilew == w && tileh == h && write) { String tempStr = fileName + "_" + subName[subsamp] + "_" + "Q" + jpegQual + ".jpg"; FileOutputStream fos = new FileOutputStream(tempStr); fos.write(jpegBuf[0], 0, jpegSize[0]); fos.close(); if (quiet == 0) System.out.println("Reference image written to " + tempStr); } /* Decompression test */ if (!compOnly) decomp(srcBuf, jpegBuf, jpegSize, tmpBuf, w, h, subsamp, jpegQual, fileName, tilew, tileh); if (tilew == w && tileh == h) break; } } static void decompTest(String fileName) throws Exception { TJTransformer tjt; byte[][] jpegBuf = null; byte[] srcBuf; int[] jpegSize = null; int totalJpegSize; int w = 0, h = 0, subsamp = -1, cs = -1, _w, _h, _tilew, _tileh, _ntilesw, _ntilesh, _subsamp, x, y, iter; int ntilesw = 1, ntilesh = 1; double start, elapsed; int ps = TJ.getPixelSize(pf), tile; FileInputStream fis = new FileInputStream(fileName); if (fis.getChannel().size() > (long)Integer.MAX_VALUE) throw new Exception("Image is too large"); int srcSize = (int)fis.getChannel().size(); srcBuf = new byte[srcSize]; fis.read(srcBuf, 0, srcSize); fis.close(); int index = fileName.lastIndexOf('.'); if (index >= 0) fileName = new String(fileName.substring(0, index)); tjt = new TJTransformer(); tjt.setSourceImage(srcBuf, srcSize); w = tjt.getWidth(); h = tjt.getHeight(); subsamp = tjt.getSubsamp(); cs = tjt.getColorspace(); if (quiet == 1) { System.out.println("All performance values in Mpixels/sec\n"); System.out.format("Bitmap JPEG JPEG %s %s Xform Comp Decomp ", (doTile ? "Tile " : "Image"), (doTile ? "Tile " : "Image")); if (doYUV) System.out.print("Decode"); System.out.print("\n"); System.out.print("Format CS Subsamp Width Height Perf Ratio Perf "); if (doYUV) System.out.print("Perf"); System.out.println("\n"); } else if (quiet == 0) System.out.format(">>>>> JPEG %s --> %s (%s) <<<<<\n", formatName(subsamp, cs), pixFormatStr[pf], (flags & TJ.FLAG_BOTTOMUP) != 0 ? "Bottom-up" : "Top-down"); for (int tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ; tilew *= 2, tileh *= 2) { if (tilew > w) tilew = w; if (tileh > h) tileh = h; ntilesw = (w + tilew - 1) / tilew; ntilesh = (h + tileh - 1) / tileh; _w = w; _h = h; _tilew = tilew; _tileh = tileh; if (quiet == 0) { System.out.format("\n%s size: %d x %d", (doTile ? "Tile" : "Image"), _tilew, _tileh); if (sf.getNum() != 1 || sf.getDenom() != 1) System.out.format(" --> %d x %d", sf.getScaled(_w), sf.getScaled(_h)); System.out.println(""); } else if (quiet == 1) { System.out.format("%-4s (%s) %-5s %-5s ", pixFormatStr[pf], (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD", csName[cs], subNameLong[subsamp]); System.out.format("%-5d %-5d ", tilew, tileh); } _subsamp = subsamp; if (doTile || xformOp != TJTransform.OP_NONE || xformOpt != 0) { if (xformOp == TJTransform.OP_TRANSPOSE || xformOp == TJTransform.OP_TRANSVERSE || xformOp == TJTransform.OP_ROT90 || xformOp == TJTransform.OP_ROT270) { _w = h; _h = w; _tilew = tileh; _tileh = tilew; } if ((xformOpt & TJTransform.OPT_GRAY) != 0) _subsamp = TJ.SAMP_GRAY; if (xformOp == TJTransform.OP_HFLIP || xformOp == TJTransform.OP_ROT180) _w = _w - (_w % TJ.getMCUWidth(_subsamp)); if (xformOp == TJTransform.OP_VFLIP || xformOp == TJTransform.OP_ROT180) _h = _h - (_h % TJ.getMCUHeight(_subsamp)); if (xformOp == TJTransform.OP_TRANSVERSE || xformOp == TJTransform.OP_ROT90) _w = _w - (_w % TJ.getMCUHeight(_subsamp)); if (xformOp == TJTransform.OP_TRANSVERSE || xformOp == TJTransform.OP_ROT270) _h = _h - (_h % TJ.getMCUWidth(_subsamp)); _ntilesw = (_w + _tilew - 1) / _tilew; _ntilesh = (_h + _tileh - 1) / _tileh; if (xformOp == TJTransform.OP_TRANSPOSE || xformOp == TJTransform.OP_TRANSVERSE || xformOp == TJTransform.OP_ROT90 || xformOp == TJTransform.OP_ROT270) { if (_subsamp == TJ.SAMP_422) _subsamp = TJ.SAMP_440; else if (_subsamp == TJ.SAMP_440) _subsamp = TJ.SAMP_422; } TJTransform[] t = new TJTransform[_ntilesw * _ntilesh]; jpegBuf = new byte[_ntilesw * _ntilesh][TJ.bufSize(_tilew, _tileh, subsamp)]; for (y = 0, tile = 0; y < _h; y += _tileh) { for (x = 0; x < _w; x += _tilew, tile++) { t[tile] = new TJTransform(); t[tile].width = Math.min(_tilew, _w - x); t[tile].height = Math.min(_tileh, _h - y); t[tile].x = x; t[tile].y = y; t[tile].op = xformOp; t[tile].options = xformOpt | TJTransform.OPT_TRIM; if ((t[tile].options & TJTransform.OPT_NOOUTPUT) != 0 && jpegBuf[tile] != null) jpegBuf[tile] = null; } } iter = -1; elapsed = 0.; while (true) { start = getTime(); tjt.transform(jpegBuf, t, flags); jpegSize = tjt.getTransformedSizes(); elapsed += getTime() - start; if (iter >= 0) { iter++; if (elapsed >= benchTime) break; } else if (elapsed >= warmup) { iter = 0; elapsed = 0.0; } } t = null; for (tile = 0, totalJpegSize = 0; tile < _ntilesw * _ntilesh; tile++) totalJpegSize += jpegSize[tile]; if (quiet != 0) { System.out.format("%-6s%s%-6s%s", sigFig((double)(w * h) / 1000000. / elapsed, 4), quiet == 2 ? "\n" : " ", sigFig((double)(w * h * ps) / (double)totalJpegSize, 4), quiet == 2 ? "\n" : " "); } else if (quiet == 0) { System.out.format("Transform --> Frame rate: %f fps\n", 1.0 / elapsed); System.out.format(" Output image size: %d bytes\n", totalJpegSize); System.out.format(" Compression ratio: %f:1\n", (double)(w * h * ps) / (double)totalJpegSize); System.out.format(" Throughput: %f Megapixels/sec\n", (double)(w * h) / 1000000. / elapsed); System.out.format(" Output bit stream: %f Megabits/sec\n", (double)totalJpegSize * 8. / 1000000. / elapsed); } } else { if (quiet == 1) System.out.print("N/A N/A "); jpegBuf = new byte[1][TJ.bufSize(_tilew, _tileh, subsamp)]; jpegSize = new int[1]; jpegBuf[0] = srcBuf; jpegSize[0] = srcSize; } if (w == tilew) _tilew = _w; if (h == tileh) _tileh = _h; if ((xformOpt & TJTransform.OPT_NOOUTPUT) == 0) decomp(null, jpegBuf, jpegSize, null, _w, _h, _subsamp, 0, fileName, _tilew, _tileh); else if (quiet == 1) System.out.println("N/A"); jpegBuf = null; jpegSize = null; if (tilew == w && tileh == h) break; } } static void usage() throws Exception { int i; TJScalingFactor[] scalingFactors = TJ.getScalingFactors(); int nsf = scalingFactors.length; String className = new TJBench().getClass().getName(); System.out.println("\nUSAGE: java " + className); System.out.println(" [options]\n"); System.out.println(" java " + className); System.out.println(" [options]\n"); System.out.println("Options:\n"); System.out.println("-alloc = Dynamically allocate JPEG image buffers"); System.out.println("-bottomup = Test bottom-up compression/decompression"); System.out.println("-tile = Test performance of the codec when the image is encoded as separate"); System.out.println(" tiles of varying sizes."); System.out.println("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb ="); System.out.println(" Test the specified color conversion path in the codec (default = BGR)"); System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in"); System.out.println(" the underlying codec"); System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying"); System.out.println(" codec"); System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the"); System.out.println(" underlying codec"); System.out.println("-subsamp = When testing JPEG compression, this option specifies the level"); System.out.println(" of chrominance subsampling to use ( = 444, 422, 440, 420, 411, or"); System.out.println(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in"); System.out.println(" sequence."); System.out.println("-quiet = Output results in tabular rather than verbose format"); System.out.println("-yuv = Test YUV encoding/decoding functions"); System.out.println("-yuvpad

= If testing YUV encoding/decoding, this specifies the number of"); System.out.println(" bytes to which each row of each plane in the intermediate YUV image is"); System.out.println(" padded (default = 1)"); System.out.println("-scale M/N = Scale down the width/height of the decompressed JPEG image by a"); System.out.print (" factor of M/N (M/N = "); for (i = 0; i < nsf; i++) { System.out.format("%d/%d", scalingFactors[i].getNum(), scalingFactors[i].getDenom()); if (nsf == 2 && i != nsf - 1) System.out.print(" or "); else if (nsf > 2) { if (i != nsf - 1) System.out.print(", "); if (i == nsf - 2) System.out.print("or "); } if (i % 8 == 0 && i != 0) System.out.print("\n "); } System.out.println(")"); System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 ="); System.out.println(" Perform the corresponding lossless transform prior to"); System.out.println(" decompression (these options are mutually exclusive)"); System.out.println("-grayscale = Perform lossless grayscale conversion prior to decompression"); System.out.println(" test (can be combined with the other transforms above)"); System.out.println("-benchtime = Run each benchmark for at least seconds (default = 5.0)"); System.out.println("-warmup = Run each benchmark for seconds (default = 1.0) prior to"); System.out.println(" starting the timer, in order to prime the caches and thus improve the"); System.out.println(" consistency of the results."); System.out.println("-componly = Stop after running compression tests. Do not test decompression."); System.out.println("-nowrite = Do not write reference or output images (improves consistency"); System.out.println(" of performance measurements.)\n"); System.out.println("NOTE: If the quality is specified as a range (e.g. 90-100), a separate"); System.out.println("test will be performed for all quality values in the range.\n"); System.exit(1); } public static void main(String[] argv) { byte[] srcBuf = null; int w = 0, h = 0; int minQual = -1, maxQual = -1; int minArg = 1; int retval = 0; int subsamp = -1; try { if (argv.length < minArg) usage(); String tempStr = argv[0].toLowerCase(); if (tempStr.endsWith(".jpg") || tempStr.endsWith(".jpeg")) decompOnly = true; System.out.println(""); if (!decompOnly) { minArg = 2; if (argv.length < minArg) usage(); try { minQual = Integer.parseInt(argv[1]); } catch (NumberFormatException e) {} if (minQual < 1 || minQual > 100) throw new Exception("Quality must be between 1 and 100."); int dashIndex = argv[1].indexOf('-'); if (dashIndex > 0 && argv[1].length() > dashIndex + 1) { try { maxQual = Integer.parseInt(argv[1].substring(dashIndex + 1)); } catch (NumberFormatException e) {} } if (maxQual < 1 || maxQual > 100) maxQual = minQual; } if (argv.length > minArg) { for (int i = minArg; i < argv.length; i++) { if (argv[i].equalsIgnoreCase("-tile")) { doTile = true; xformOpt |= TJTransform.OPT_CROP; } else if (argv[i].equalsIgnoreCase("-fastupsample")) { System.out.println("Using fast upsampling code\n"); flags |= TJ.FLAG_FASTUPSAMPLE; } else if (argv[i].equalsIgnoreCase("-fastdct")) { System.out.println("Using fastest DCT/IDCT algorithm\n"); flags |= TJ.FLAG_FASTDCT; } else if (argv[i].equalsIgnoreCase("-accuratedct")) { System.out.println("Using most accurate DCT/IDCT algorithm\n"); flags |= TJ.FLAG_ACCURATEDCT; } else if (argv[i].equalsIgnoreCase("-rgb")) pf = TJ.PF_RGB; else if (argv[i].equalsIgnoreCase("-rgbx")) pf = TJ.PF_RGBX; else if (argv[i].equalsIgnoreCase("-bgr")) pf = TJ.PF_BGR; else if (argv[i].equalsIgnoreCase("-bgrx")) pf = TJ.PF_BGRX; else if (argv[i].equalsIgnoreCase("-xbgr")) pf = TJ.PF_XBGR; else if (argv[i].equalsIgnoreCase("-xrgb")) pf = TJ.PF_XRGB; else if (argv[i].equalsIgnoreCase("-bottomup")) flags |= TJ.FLAG_BOTTOMUP; else if (argv[i].equalsIgnoreCase("-quiet")) quiet = 1; else if (argv[i].equalsIgnoreCase("-qq")) quiet = 2; else if (argv[i].equalsIgnoreCase("-scale") && i < argv.length - 1) { int temp1 = 0, temp2 = 0; boolean match = false, scanned = true; Scanner scanner = new Scanner(argv[++i]).useDelimiter("/"); try { temp1 = scanner.nextInt(); temp2 = scanner.nextInt(); } catch(Exception e) {} if (temp2 <= 0) temp2 = 1; if (temp1 > 0) { TJScalingFactor[] scalingFactors = TJ.getScalingFactors(); for (int j = 0; j < scalingFactors.length; j++) { if ((double)temp1 / (double)temp2 == (double)scalingFactors[j].getNum() / (double)scalingFactors[j].getDenom()) { sf = scalingFactors[j]; match = true; break; } } if (!match) usage(); } else usage(); } else if (argv[i].equalsIgnoreCase("-hflip")) xformOp = TJTransform.OP_HFLIP; else if (argv[i].equalsIgnoreCase("-vflip")) xformOp = TJTransform.OP_VFLIP; else if (argv[i].equalsIgnoreCase("-transpose")) xformOp = TJTransform.OP_TRANSPOSE; else if (argv[i].equalsIgnoreCase("-transverse")) xformOp = TJTransform.OP_TRANSVERSE; else if (argv[i].equalsIgnoreCase("-rot90")) xformOp = TJTransform.OP_ROT90; else if (argv[i].equalsIgnoreCase("-rot180")) xformOp = TJTransform.OP_ROT180; else if (argv[i].equalsIgnoreCase("-rot270")) xformOp = TJTransform.OP_ROT270; else if (argv[i].equalsIgnoreCase("-grayscale")) xformOpt |= TJTransform.OPT_GRAY; else if (argv[i].equalsIgnoreCase("-nooutput")) xformOpt |= TJTransform.OPT_NOOUTPUT; else if (argv[i].equalsIgnoreCase("-benchtime") && i < argv.length - 1) { double temp = -1; try { temp = Double.parseDouble(argv[++i]); } catch (NumberFormatException e) {} if (temp > 0.0) benchTime = temp; else usage(); } else if (argv[i].equalsIgnoreCase("-yuv")) { System.out.println("Testing YUV planar encoding/decoding\n"); doYUV = true; } else if (argv[i].equalsIgnoreCase("-yuvpad") && i < argv.length - 1) { int temp = 0; try { temp = Integer.parseInt(argv[++i]); } catch (NumberFormatException e) {} if (temp >= 1) yuvpad = temp; } else if (argv[i].equalsIgnoreCase("-subsamp") && i < argv.length - 1) { i++; if (argv[i].toUpperCase().startsWith("G")) subsamp = TJ.SAMP_GRAY; else if (argv[i].equals("444")) subsamp = TJ.SAMP_444; else if (argv[i].equals("422")) subsamp = TJ.SAMP_422; else if (argv[i].equals("440")) subsamp = TJ.SAMP_440; else if (argv[i].equals("420")) subsamp = TJ.SAMP_420; else if (argv[i].equals("411")) subsamp = TJ.SAMP_411; } else if (argv[i].equalsIgnoreCase("-componly")) compOnly = true; else if (argv[i].equalsIgnoreCase("-nowrite")) write = false; else if (argv[i].equalsIgnoreCase("-warmup") && i < argv.length - 1) { double temp = -1; try { temp = Double.parseDouble(argv[++i]); } catch (NumberFormatException e) {} if (temp >= 0.0) { warmup = temp; System.out.format("Warmup time = %.1f seconds\n\n", warmup); } else usage(); } else usage(); } } if (sf == null) sf = new TJScalingFactor(1, 1); if ((sf.getNum() != 1 || sf.getDenom() != 1) && doTile) { System.out.println("Disabling tiled compression/decompression tests, because those tests do not"); System.out.println("work when scaled decompression is enabled."); doTile = false; } if (!decompOnly) { int[] width = new int[1], height = new int[1]; srcBuf = loadImage(argv[0], width, height, pf); w = width[0]; h = height[0]; int index = -1; if ((index = argv[0].lastIndexOf('.')) >= 0) argv[0] = argv[0].substring(0, index); } if (quiet == 1 && !decompOnly) { System.out.println("All performance values in Mpixels/sec\n"); System.out.format("Bitmap JPEG JPEG %s %s ", (doTile ? "Tile " : "Image"), (doTile ? "Tile " : "Image")); if (doYUV) System.out.print("Encode "); System.out.print("Comp Comp Decomp "); if (doYUV) System.out.print("Decode"); System.out.print("\n"); System.out.print("Format Subsamp Qual Width Height "); if (doYUV) System.out.print("Perf "); System.out.print("Perf Ratio Perf "); if (doYUV) System.out.print("Perf"); System.out.println("\n"); } if (decompOnly) { decompTest(argv[0]); System.out.println(""); System.exit(retval); } System.gc(); if (subsamp >= 0 && subsamp < TJ.NUMSAMP) { for (int i = maxQual; i >= minQual; i--) fullTest(srcBuf, w, h, subsamp, i, argv[0]); System.out.println(""); } else { for (int i = maxQual; i >= minQual; i--) fullTest(srcBuf, w, h, TJ.SAMP_GRAY, i, argv[0]); System.out.println(""); System.gc(); for (int i = maxQual; i >= minQual; i--) fullTest(srcBuf, w, h, TJ.SAMP_420, i, argv[0]); System.out.println(""); System.gc(); for (int i = maxQual; i >= minQual; i--) fullTest(srcBuf, w, h, TJ.SAMP_422, i, argv[0]); System.out.println(""); System.gc(); for (int i = maxQual; i >= minQual; i--) fullTest(srcBuf, w, h, TJ.SAMP_444, i, argv[0]); System.out.println(""); } } catch (Exception e) { System.out.println("ERROR: " + e.getMessage()); e.printStackTrace(); retval = -1; } System.exit(retval); } }