2 * Copyright (C)2009-2014, 2016-2019, 2021 D. R. Commander.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * - Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * - Neither the name of the libjpeg-turbo Project nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
31 import java.awt.image.*;
32 import javax.imageio.*;
34 import org.libjpegturbo.turbojpeg.*;
40 private static int flags = 0, quiet = 0, pf = TJ.PF_BGR, yuvPad = 1;
41 private static boolean compOnly, decompOnly, doTile, doYUV, write = true;
43 static final String[] PIXFORMATSTR = {
44 "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY"
47 static final String[] SUBNAME_LONG = {
48 "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1"
51 static final String[] SUBNAME = {
52 "444", "422", "420", "GRAY", "440", "411"
55 static final String[] CSNAME = {
56 "RGB", "YCbCr", "GRAY", "CMYK", "YCCK"
59 private static TJScalingFactor sf;
60 private static int xformOp = TJTransform.OP_NONE, xformOpt = 0;
61 private static double benchTime = 5.0, warmup = 1.0;
64 static double getTime() {
65 return (double)System.nanoTime() / 1.0e9;
69 private static String tjErrorMsg;
70 private static int tjErrorCode = -1;
72 static void handleTJException(TJException e) throws TJException {
73 String errorMsg = e.getMessage();
74 int errorCode = e.getErrorCode();
76 if ((flags & TJ.FLAG_STOPONWARNING) == 0 &&
77 errorCode == TJ.ERR_WARNING) {
78 if (tjErrorMsg == null || !tjErrorMsg.equals(errorMsg) ||
79 tjErrorCode != errorCode) {
80 tjErrorMsg = errorMsg;
81 tjErrorCode = errorCode;
82 System.out.println("WARNING: " + errorMsg);
89 static String formatName(int subsamp, int cs) {
90 if (cs == TJ.CS_YCbCr)
91 return SUBNAME_LONG[subsamp];
92 else if (cs == TJ.CS_YCCK)
93 return CSNAME[cs] + " " + SUBNAME_LONG[subsamp];
99 static String sigFig(double val, int figs) {
101 int digitsAfterDecimal = figs - (int)Math.ceil(Math.log10(Math.abs(val)));
103 if (digitsAfterDecimal < 1)
104 format = new String("%.0f");
106 format = new String("%." + digitsAfterDecimal + "f");
107 return String.format(format, val);
111 static byte[] loadImage(String fileName, int[] w, int[] h, int pixelFormat)
113 BufferedImage img = ImageIO.read(new File(fileName));
116 throw new Exception("Could not read " + fileName);
117 w[0] = img.getWidth();
118 h[0] = img.getHeight();
120 int[] rgb = img.getRGB(0, 0, w[0], h[0], null, 0, w[0]);
121 int ps = TJ.getPixelSize(pixelFormat);
122 int rindex = TJ.getRedOffset(pixelFormat);
123 int gindex = TJ.getGreenOffset(pixelFormat);
124 int bindex = TJ.getBlueOffset(pixelFormat);
125 if ((long)w[0] * (long)h[0] * (long)ps > (long)Integer.MAX_VALUE)
126 throw new Exception("Image is too large");
127 byte[] dstBuf = new byte[w[0] * h[0] * ps];
128 int pixels = w[0] * h[0], dstPtr = 0, rgbPtr = 0;
130 while (pixels-- > 0) {
131 dstBuf[dstPtr + rindex] = (byte)((rgb[rgbPtr] >> 16) & 0xff);
132 dstBuf[dstPtr + gindex] = (byte)((rgb[rgbPtr] >> 8) & 0xff);
133 dstBuf[dstPtr + bindex] = (byte)(rgb[rgbPtr] & 0xff);
141 static void saveImage(String fileName, byte[] srcBuf, int w, int h,
142 int pixelFormat) throws Exception {
143 BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
144 int pixels = w * h, srcPtr = 0;
145 int ps = TJ.getPixelSize(pixelFormat);
146 int rindex = TJ.getRedOffset(pixelFormat);
147 int gindex = TJ.getGreenOffset(pixelFormat);
148 int bindex = TJ.getBlueOffset(pixelFormat);
150 for (int y = 0; y < h; y++) {
151 for (int x = 0; x < w; x++, srcPtr += ps) {
152 int pixel = (srcBuf[srcPtr + rindex] & 0xff) << 16 |
153 (srcBuf[srcPtr + gindex] & 0xff) << 8 |
154 (srcBuf[srcPtr + bindex] & 0xff);
156 img.setRGB(x, y, pixel);
159 ImageIO.write(img, "bmp", new File(fileName));
163 /* Decompression test */
164 static void decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize,
165 byte[] dstBuf, int w, int h, int subsamp, int jpegQual,
166 String fileName, int tilew, int tileh) throws Exception {
167 String qualStr = new String(""), sizeStr, tempStr;
169 double elapsed, elapsedDecode;
170 int ps = TJ.getPixelSize(pf), i, iter = 0;
171 int scaledw = sf.getScaled(w);
172 int scaledh = sf.getScaled(h);
173 int pitch = scaledw * ps;
174 YUVImage yuvImage = null;
177 qualStr = new String("_Q" + jpegQual);
179 tjd = new TJDecompressor();
181 if (dstBuf == null) {
182 if ((long)pitch * (long)scaledh > (long)Integer.MAX_VALUE)
183 throw new Exception("Image is too large");
184 dstBuf = new byte[pitch * scaledh];
187 /* Set the destination buffer to gray so we know whether the decompressor
188 attempted to write to it */
189 Arrays.fill(dstBuf, (byte)127);
192 int width = doTile ? tilew : scaledw;
193 int height = doTile ? tileh : scaledh;
195 yuvImage = new YUVImage(width, yuvPad, height, subsamp);
196 Arrays.fill(yuvImage.getBuf(), (byte)127);
201 elapsed = elapsedDecode = 0.0;
204 double start = getTime();
206 for (int y = 0; y < h; y += tileh) {
207 for (int x = 0; x < w; x += tilew, tile++) {
208 int width = doTile ? Math.min(tilew, w - x) : scaledw;
209 int height = doTile ? Math.min(tileh, h - y) : scaledh;
212 tjd.setSourceImage(jpegBuf[tile], jpegSize[tile]);
213 } catch (TJException e) { handleTJException(e); }
215 yuvImage.setBuf(yuvImage.getBuf(), width, yuvPad, height, subsamp);
217 tjd.decompressToYUV(yuvImage, flags);
218 } catch (TJException e) { handleTJException(e); }
219 double startDecode = getTime();
220 tjd.setSourceImage(yuvImage);
222 tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags);
223 } catch (TJException e) { handleTJException(e); }
225 elapsedDecode += getTime() - startDecode;
228 tjd.decompress(dstBuf, x, y, width, pitch, height, pf, flags);
229 } catch (TJException e) { handleTJException(e); }
233 elapsed += getTime() - start;
236 if (elapsed >= benchTime)
238 } else if (elapsed >= warmup) {
240 elapsed = elapsedDecode = 0.0;
244 elapsed -= elapsedDecode;
247 for (i = 0; i < jpegBuf.length; i++)
249 jpegBuf = null; jpegSize = null;
253 System.out.format("%-6s%s",
254 sigFig((double)(w * h) / 1000000. *
255 (double)iter / elapsed, 4),
256 quiet == 2 ? "\n" : " ");
258 System.out.format("%s\n",
259 sigFig((double)(w * h) / 1000000. *
260 (double)iter / elapsedDecode, 4));
262 System.out.print("\n");
264 System.out.format("%s --> Frame rate: %f fps\n",
265 (doYUV ? "Decomp to YUV" : "Decompress "),
266 (double)iter / elapsed);
267 System.out.format(" Throughput: %f Megapixels/sec\n",
268 (double)(w * h) / 1000000. * (double)iter / elapsed);
270 System.out.format("YUV Decode --> Frame rate: %f fps\n",
271 (double)iter / elapsedDecode);
272 System.out.format(" Throughput: %f Megapixels/sec\n",
273 (double)(w * h) / 1000000. *
274 (double)iter / elapsedDecode);
280 if (sf.getNum() != 1 || sf.getDenom() != 1)
281 sizeStr = new String(sf.getNum() + "_" + sf.getDenom());
282 else if (tilew != w || tileh != h)
283 sizeStr = new String(tilew + "x" + tileh);
285 sizeStr = new String("full");
287 tempStr = new String(fileName + "_" + sizeStr + ".bmp");
289 tempStr = new String(fileName + "_" + SUBNAME[subsamp] + qualStr +
290 "_" + sizeStr + ".bmp");
292 saveImage(tempStr, dstBuf, scaledw, scaledh, pf);
293 int ndx = tempStr.lastIndexOf('.');
294 tempStr = new String(tempStr.substring(0, ndx) + "-err.bmp");
295 if (srcBuf != null && sf.getNum() == 1 && sf.getDenom() == 1) {
297 System.out.println("Compression error written to " + tempStr + ".");
298 if (subsamp == TJ.SAMP_GRAY) {
299 for (int y = 0, index = 0; y < h; y++, index += pitch) {
300 for (int x = 0, index2 = index; x < w; x++, index2 += ps) {
301 int rindex = index2 + TJ.getRedOffset(pf);
302 int gindex = index2 + TJ.getGreenOffset(pf);
303 int bindex = index2 + TJ.getBlueOffset(pf);
304 int lum = (int)((double)(srcBuf[rindex] & 0xff) * 0.299 +
305 (double)(srcBuf[gindex] & 0xff) * 0.587 +
306 (double)(srcBuf[bindex] & 0xff) * 0.114 + 0.5);
308 if (lum > 255) lum = 255;
309 if (lum < 0) lum = 0;
310 dstBuf[rindex] = (byte)Math.abs((dstBuf[rindex] & 0xff) - lum);
311 dstBuf[gindex] = (byte)Math.abs((dstBuf[gindex] & 0xff) - lum);
312 dstBuf[bindex] = (byte)Math.abs((dstBuf[bindex] & 0xff) - lum);
316 for (int y = 0; y < h; y++)
317 for (int x = 0; x < w * ps; x++)
318 dstBuf[pitch * y + x] =
319 (byte)Math.abs((dstBuf[pitch * y + x] & 0xff) -
320 (srcBuf[pitch * y + x] & 0xff));
322 saveImage(tempStr, dstBuf, w, h, pf);
327 static void fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual,
328 String fileName) throws Exception {
333 double start, elapsed, elapsedEncode;
334 int totalJpegSize = 0, tilew, tileh, i, iter;
335 int ps = TJ.getPixelSize(pf);
336 int ntilesw = 1, ntilesh = 1, pitch = w * ps;
337 String pfStr = PIXFORMATSTR[pf];
338 YUVImage yuvImage = null;
340 if ((long)pitch * (long)h > (long)Integer.MAX_VALUE)
341 throw new Exception("Image is too large");
342 tmpBuf = new byte[pitch * h];
345 System.out.format(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pfStr,
346 (flags & TJ.FLAG_BOTTOMUP) != 0 ?
347 "Bottom-up" : "Top-down",
348 SUBNAME_LONG[subsamp], jpegQual);
350 tjc = new TJCompressor();
352 for (tilew = doTile ? 8 : w, tileh = doTile ? 8 : h; ;
353 tilew *= 2, tileh *= 2) {
358 ntilesw = (w + tilew - 1) / tilew;
359 ntilesh = (h + tileh - 1) / tileh;
361 jpegBuf = new byte[ntilesw * ntilesh][TJ.bufSize(tilew, tileh, subsamp)];
362 jpegSize = new int[ntilesw * ntilesh];
364 /* Compression test */
366 System.out.format("%-4s (%s) %-5s %-3d ", pfStr,
367 (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD",
368 SUBNAME_LONG[subsamp], jpegQual);
369 for (i = 0; i < h; i++)
370 System.arraycopy(srcBuf, w * ps * i, tmpBuf, pitch * i, w * ps);
371 tjc.setJPEGQuality(jpegQual);
372 tjc.setSubsamp(subsamp);
375 yuvImage = new YUVImage(tilew, yuvPad, tileh, subsamp);
376 Arrays.fill(yuvImage.getBuf(), (byte)127);
381 elapsed = elapsedEncode = 0.0;
387 for (int y = 0; y < h; y += tileh) {
388 for (int x = 0; x < w; x += tilew, tile++) {
389 int width = Math.min(tilew, w - x);
390 int height = Math.min(tileh, h - y);
392 tjc.setSourceImage(srcBuf, x, y, width, pitch, height, pf);
394 double startEncode = getTime();
396 yuvImage.setBuf(yuvImage.getBuf(), width, yuvPad, height,
398 tjc.encodeYUV(yuvImage, flags);
400 elapsedEncode += getTime() - startEncode;
401 tjc.setSourceImage(yuvImage);
403 tjc.compress(jpegBuf[tile], flags);
404 jpegSize[tile] = tjc.getCompressedSize();
405 totalJpegSize += jpegSize[tile];
408 elapsed += getTime() - start;
411 if (elapsed >= benchTime)
413 } else if (elapsed >= warmup) {
415 elapsed = elapsedEncode = 0.0;
419 elapsed -= elapsedEncode;
422 System.out.format("%-5d %-5d ", tilew, tileh);
425 System.out.format("%-6s%s",
426 sigFig((double)(w * h) / 1000000. *
427 (double)iter / elapsedEncode, 4),
428 quiet == 2 ? "\n" : " ");
429 System.out.format("%-6s%s",
430 sigFig((double)(w * h) / 1000000. *
431 (double)iter / elapsed, 4),
432 quiet == 2 ? "\n" : " ");
433 System.out.format("%-6s%s",
434 sigFig((double)(w * h * ps) / (double)totalJpegSize,
436 quiet == 2 ? "\n" : " ");
438 System.out.format("\n%s size: %d x %d\n", doTile ? "Tile" : "Image",
441 System.out.format("Encode YUV --> Frame rate: %f fps\n",
442 (double)iter / elapsedEncode);
443 System.out.format(" Output image size: %d bytes\n",
445 System.out.format(" Compression ratio: %f:1\n",
446 (double)(w * h * ps) / (double)yuvImage.getSize());
447 System.out.format(" Throughput: %f Megapixels/sec\n",
448 (double)(w * h) / 1000000. *
449 (double)iter / elapsedEncode);
450 System.out.format(" Output bit stream: %f Megabits/sec\n",
451 (double)yuvImage.getSize() * 8. / 1000000. *
452 (double)iter / elapsedEncode);
454 System.out.format("%s --> Frame rate: %f fps\n",
455 doYUV ? "Comp from YUV" : "Compress ",
456 (double)iter / elapsed);
457 System.out.format(" Output image size: %d bytes\n",
459 System.out.format(" Compression ratio: %f:1\n",
460 (double)(w * h * ps) / (double)totalJpegSize);
461 System.out.format(" Throughput: %f Megapixels/sec\n",
462 (double)(w * h) / 1000000. * (double)iter / elapsed);
463 System.out.format(" Output bit stream: %f Megabits/sec\n",
464 (double)totalJpegSize * 8. / 1000000. *
465 (double)iter / elapsed);
467 if (tilew == w && tileh == h && write) {
468 String tempStr = fileName + "_" + SUBNAME[subsamp] + "_" + "Q" +
470 FileOutputStream fos = new FileOutputStream(tempStr);
472 fos.write(jpegBuf[0], 0, jpegSize[0]);
475 System.out.println("Reference image written to " + tempStr);
478 /* Decompression test */
480 decomp(srcBuf, jpegBuf, jpegSize, tmpBuf, w, h, subsamp, jpegQual,
481 fileName, tilew, tileh);
483 System.out.println("N/A");
485 if (tilew == w && tileh == h) break;
490 static void decompTest(String fileName) throws Exception {
492 byte[][] jpegBuf = null;
494 int[] jpegSize = null;
496 double start, elapsed;
497 int ps = TJ.getPixelSize(pf), tile, x, y, iter;
499 int w = 0, h = 0, ntilesw = 1, ntilesh = 1, subsamp = -1, cs = -1;
501 int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp;
503 FileInputStream fis = new FileInputStream(fileName);
504 if (fis.getChannel().size() > (long)Integer.MAX_VALUE)
505 throw new Exception("Image is too large");
506 int srcSize = (int)fis.getChannel().size();
507 srcBuf = new byte[srcSize];
508 fis.read(srcBuf, 0, srcSize);
511 int index = fileName.lastIndexOf('.');
513 fileName = new String(fileName.substring(0, index));
515 tjt = new TJTransformer();
518 tjt.setSourceImage(srcBuf, srcSize);
519 } catch (TJException e) { handleTJException(e); }
522 subsamp = tjt.getSubsamp();
523 cs = tjt.getColorspace();
526 System.out.println("All performance values in Mpixels/sec\n");
527 System.out.format("Bitmap JPEG JPEG %s %s Xform Comp Decomp ",
528 (doTile ? "Tile " : "Image"),
529 (doTile ? "Tile " : "Image"));
531 System.out.print("Decode");
532 System.out.print("\n");
533 System.out.print("Format CS Subsamp Width Height Perf Ratio Perf ");
535 System.out.print("Perf");
536 System.out.println("\n");
537 } else if (quiet == 0)
538 System.out.format(">>>>> JPEG %s --> %s (%s) <<<<<\n",
539 formatName(subsamp, cs), PIXFORMATSTR[pf],
540 (flags & TJ.FLAG_BOTTOMUP) != 0 ?
541 "Bottom-up" : "Top-down");
543 for (int tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ;
544 tilew *= 2, tileh *= 2) {
549 ntilesw = (w + tilew - 1) / tilew;
550 ntilesh = (h + tileh - 1) / tileh;
552 tw = w; th = h; ttilew = tilew; ttileh = tileh;
554 System.out.format("\n%s size: %d x %d", (doTile ? "Tile" : "Image"),
556 if (sf.getNum() != 1 || sf.getDenom() != 1)
557 System.out.format(" --> %d x %d", sf.getScaled(tw),
559 System.out.println("");
560 } else if (quiet == 1) {
561 System.out.format("%-4s (%s) %-5s %-5s ", PIXFORMATSTR[pf],
562 (flags & TJ.FLAG_BOTTOMUP) != 0 ? "BU" : "TD",
563 CSNAME[cs], SUBNAME_LONG[subsamp]);
564 System.out.format("%-5d %-5d ", tilew, tileh);
568 if (doTile || xformOp != TJTransform.OP_NONE || xformOpt != 0) {
569 if (xformOp == TJTransform.OP_TRANSPOSE ||
570 xformOp == TJTransform.OP_TRANSVERSE ||
571 xformOp == TJTransform.OP_ROT90 ||
572 xformOp == TJTransform.OP_ROT270) {
573 tw = h; th = w; ttilew = tileh; ttileh = tilew;
576 if ((xformOpt & TJTransform.OPT_GRAY) != 0)
577 tsubsamp = TJ.SAMP_GRAY;
578 if (xformOp == TJTransform.OP_HFLIP ||
579 xformOp == TJTransform.OP_ROT180)
580 tw = tw - (tw % TJ.getMCUWidth(tsubsamp));
581 if (xformOp == TJTransform.OP_VFLIP ||
582 xformOp == TJTransform.OP_ROT180)
583 th = th - (th % TJ.getMCUHeight(tsubsamp));
584 if (xformOp == TJTransform.OP_TRANSVERSE ||
585 xformOp == TJTransform.OP_ROT90)
586 tw = tw - (tw % TJ.getMCUHeight(tsubsamp));
587 if (xformOp == TJTransform.OP_TRANSVERSE ||
588 xformOp == TJTransform.OP_ROT270)
589 th = th - (th % TJ.getMCUWidth(tsubsamp));
590 tntilesw = (tw + ttilew - 1) / ttilew;
591 tntilesh = (th + ttileh - 1) / ttileh;
593 if (xformOp == TJTransform.OP_TRANSPOSE ||
594 xformOp == TJTransform.OP_TRANSVERSE ||
595 xformOp == TJTransform.OP_ROT90 ||
596 xformOp == TJTransform.OP_ROT270) {
597 if (tsubsamp == TJ.SAMP_422)
598 tsubsamp = TJ.SAMP_440;
599 else if (tsubsamp == TJ.SAMP_440)
600 tsubsamp = TJ.SAMP_422;
603 TJTransform[] t = new TJTransform[tntilesw * tntilesh];
605 new byte[tntilesw * tntilesh][TJ.bufSize(ttilew, ttileh, subsamp)];
607 for (y = 0, tile = 0; y < th; y += ttileh) {
608 for (x = 0; x < tw; x += ttilew, tile++) {
609 t[tile] = new TJTransform();
610 t[tile].width = Math.min(ttilew, tw - x);
611 t[tile].height = Math.min(ttileh, th - y);
614 t[tile].op = xformOp;
615 t[tile].options = xformOpt | TJTransform.OPT_TRIM;
616 if ((t[tile].options & TJTransform.OPT_NOOUTPUT) != 0 &&
617 jpegBuf[tile] != null)
618 jpegBuf[tile] = null;
627 tjt.transform(jpegBuf, t, flags);
628 } catch (TJException e) { handleTJException(e); }
629 jpegSize = tjt.getTransformedSizes();
630 elapsed += getTime() - start;
633 if (elapsed >= benchTime)
635 } else if (elapsed >= warmup) {
642 for (tile = 0, totalJpegSize = 0; tile < tntilesw * tntilesh; tile++)
643 totalJpegSize += jpegSize[tile];
646 System.out.format("%-6s%s%-6s%s",
647 sigFig((double)(w * h) / 1000000. / elapsed, 4),
648 quiet == 2 ? "\n" : " ",
649 sigFig((double)(w * h * ps) /
650 (double)totalJpegSize, 4),
651 quiet == 2 ? "\n" : " ");
653 System.out.format("Transform --> Frame rate: %f fps\n",
655 System.out.format(" Output image size: %d bytes\n",
657 System.out.format(" Compression ratio: %f:1\n",
658 (double)(w * h * ps) / (double)totalJpegSize);
659 System.out.format(" Throughput: %f Megapixels/sec\n",
660 (double)(w * h) / 1000000. / elapsed);
661 System.out.format(" Output bit stream: %f Megabits/sec\n",
662 (double)totalJpegSize * 8. / 1000000. / elapsed);
666 System.out.print("N/A N/A ");
667 jpegBuf = new byte[1][TJ.bufSize(ttilew, ttileh, subsamp)];
668 jpegSize = new int[1];
670 jpegSize[0] = srcSize;
677 if ((xformOpt & TJTransform.OPT_NOOUTPUT) == 0)
678 decomp(null, jpegBuf, jpegSize, null, tw, th, tsubsamp, 0,
679 fileName, ttilew, ttileh);
681 System.out.println("N/A");
686 if (tilew == w && tileh == h) break;
691 static void usage() throws Exception {
693 TJScalingFactor[] scalingFactors = TJ.getScalingFactors();
694 int nsf = scalingFactors.length;
695 String className = new TJBench().getClass().getName();
697 System.out.println("\nUSAGE: java " + className);
698 System.out.println(" <Inputfile (BMP)> <Quality> [options]\n");
699 System.out.println(" java " + className);
700 System.out.println(" <Inputfile (JPG)> [options]\n");
701 System.out.println("Options:\n");
702 System.out.println("-alloc = Dynamically allocate JPEG image buffers");
703 System.out.println("-bottomup = Test bottom-up compression/decompression");
704 System.out.println("-tile = Test performance of the codec when the image is encoded as separate");
705 System.out.println(" tiles of varying sizes.");
706 System.out.println("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =");
707 System.out.println(" Test the specified color conversion path in the codec (default = BGR)");
708 System.out.println("-fastupsample = Use the fastest chrominance upsampling algorithm available in");
709 System.out.println(" the underlying codec");
710 System.out.println("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying");
711 System.out.println(" codec");
712 System.out.println("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the");
713 System.out.println(" underlying codec");
714 System.out.println("-progressive = Use progressive entropy coding in JPEG images generated by");
715 System.out.println(" compression and transform operations.");
716 System.out.println("-subsamp <s> = When testing JPEG compression, this option specifies the level");
717 System.out.println(" of chrominance subsampling to use (<s> = 444, 422, 440, 420, 411, or");
718 System.out.println(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in");
719 System.out.println(" sequence.");
720 System.out.println("-quiet = Output results in tabular rather than verbose format");
721 System.out.println("-yuv = Test YUV encoding/decoding functions");
722 System.out.println("-yuvpad <p> = If testing YUV encoding/decoding, this specifies the number of");
723 System.out.println(" bytes to which each row of each plane in the intermediate YUV image is");
724 System.out.println(" padded (default = 1)");
725 System.out.println("-scale M/N = Scale down the width/height of the decompressed JPEG image by a");
726 System.out.print(" factor of M/N (M/N = ");
727 for (i = 0; i < nsf; i++) {
728 System.out.format("%d/%d", scalingFactors[i].getNum(),
729 scalingFactors[i].getDenom());
730 if (nsf == 2 && i != nsf - 1)
731 System.out.print(" or ");
734 System.out.print(", ");
736 System.out.print("or ");
738 if (i % 8 == 0 && i != 0)
739 System.out.print("\n ");
741 System.out.println(")");
742 System.out.println("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =");
743 System.out.println(" Perform the corresponding lossless transform prior to");
744 System.out.println(" decompression (these options are mutually exclusive)");
745 System.out.println("-grayscale = Perform lossless grayscale conversion prior to decompression");
746 System.out.println(" test (can be combined with the other transforms above)");
747 System.out.println("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)");
748 System.out.println(" when transforming the image.");
749 System.out.println("-benchtime <t> = Run each benchmark for at least <t> seconds (default = 5.0)");
750 System.out.println("-warmup <t> = Run each benchmark for <t> seconds (default = 1.0) prior to");
751 System.out.println(" starting the timer, in order to prime the caches and thus improve the");
752 System.out.println(" consistency of the results.");
753 System.out.println("-componly = Stop after running compression tests. Do not test decompression.");
754 System.out.println("-nowrite = Do not write reference or output images (improves consistency");
755 System.out.println(" of performance measurements.)");
756 System.out.println("-limitscans = Refuse to decompress or transform progressive JPEG images that");
757 System.out.println(" have an unreasonably large number of scans");
758 System.out.println("-stoponwarning = Immediately discontinue the current");
759 System.out.println(" compression/decompression/transform operation if the underlying codec");
760 System.out.println(" throws a warning (non-fatal error)\n");
761 System.out.println("NOTE: If the quality is specified as a range (e.g. 90-100), a separate");
762 System.out.println("test will be performed for all quality values in the range.\n");
767 public static void main(String[] argv) {
768 byte[] srcBuf = null;
769 int w = 0, h = 0, minQual = -1, maxQual = -1;
770 int minArg = 1, retval = 0;
775 if (argv.length < minArg)
778 String tempStr = argv[0].toLowerCase();
779 if (tempStr.endsWith(".jpg") || tempStr.endsWith(".jpeg"))
782 System.out.println("");
786 if (argv.length < minArg)
789 minQual = Integer.parseInt(argv[1]);
790 } catch (NumberFormatException e) {}
791 if (minQual < 1 || minQual > 100)
792 throw new Exception("Quality must be between 1 and 100.");
793 int dashIndex = argv[1].indexOf('-');
794 if (dashIndex > 0 && argv[1].length() > dashIndex + 1) {
796 maxQual = Integer.parseInt(argv[1].substring(dashIndex + 1));
797 } catch (NumberFormatException e) {}
799 if (maxQual < 1 || maxQual > 100)
803 if (argv.length > minArg) {
804 for (int i = minArg; i < argv.length; i++) {
805 if (argv[i].equalsIgnoreCase("-tile")) {
806 doTile = true; xformOpt |= TJTransform.OPT_CROP;
807 } else if (argv[i].equalsIgnoreCase("-fastupsample")) {
808 System.out.println("Using fast upsampling code\n");
809 flags |= TJ.FLAG_FASTUPSAMPLE;
810 } else if (argv[i].equalsIgnoreCase("-fastdct")) {
811 System.out.println("Using fastest DCT/IDCT algorithm\n");
812 flags |= TJ.FLAG_FASTDCT;
813 } else if (argv[i].equalsIgnoreCase("-accuratedct")) {
814 System.out.println("Using most accurate DCT/IDCT algorithm\n");
815 flags |= TJ.FLAG_ACCURATEDCT;
816 } else if (argv[i].equalsIgnoreCase("-progressive")) {
817 System.out.println("Using progressive entropy coding\n");
818 flags |= TJ.FLAG_PROGRESSIVE;
819 } else if (argv[i].equalsIgnoreCase("-rgb"))
821 else if (argv[i].equalsIgnoreCase("-rgbx"))
823 else if (argv[i].equalsIgnoreCase("-bgr"))
825 else if (argv[i].equalsIgnoreCase("-bgrx"))
827 else if (argv[i].equalsIgnoreCase("-xbgr"))
829 else if (argv[i].equalsIgnoreCase("-xrgb"))
831 else if (argv[i].equalsIgnoreCase("-bottomup"))
832 flags |= TJ.FLAG_BOTTOMUP;
833 else if (argv[i].equalsIgnoreCase("-quiet"))
835 else if (argv[i].equalsIgnoreCase("-qq"))
837 else if (argv[i].equalsIgnoreCase("-scale") && i < argv.length - 1) {
838 int temp1 = 0, temp2 = 0;
839 boolean match = false, scanned = true;
840 Scanner scanner = new Scanner(argv[++i]).useDelimiter("/");
843 temp1 = scanner.nextInt();
844 temp2 = scanner.nextInt();
845 } catch (Exception e) {}
846 if (temp2 <= 0) temp2 = 1;
848 TJScalingFactor[] scalingFactors = TJ.getScalingFactors();
850 for (int j = 0; j < scalingFactors.length; j++) {
851 if ((double)temp1 / (double)temp2 ==
852 (double)scalingFactors[j].getNum() /
853 (double)scalingFactors[j].getDenom()) {
854 sf = scalingFactors[j];
861 } else if (argv[i].equalsIgnoreCase("-hflip"))
862 xformOp = TJTransform.OP_HFLIP;
863 else if (argv[i].equalsIgnoreCase("-vflip"))
864 xformOp = TJTransform.OP_VFLIP;
865 else if (argv[i].equalsIgnoreCase("-transpose"))
866 xformOp = TJTransform.OP_TRANSPOSE;
867 else if (argv[i].equalsIgnoreCase("-transverse"))
868 xformOp = TJTransform.OP_TRANSVERSE;
869 else if (argv[i].equalsIgnoreCase("-rot90"))
870 xformOp = TJTransform.OP_ROT90;
871 else if (argv[i].equalsIgnoreCase("-rot180"))
872 xformOp = TJTransform.OP_ROT180;
873 else if (argv[i].equalsIgnoreCase("-rot270"))
874 xformOp = TJTransform.OP_ROT270;
875 else if (argv[i].equalsIgnoreCase("-grayscale"))
876 xformOpt |= TJTransform.OPT_GRAY;
877 else if (argv[i].equalsIgnoreCase("-nooutput"))
878 xformOpt |= TJTransform.OPT_NOOUTPUT;
879 else if (argv[i].equalsIgnoreCase("-copynone"))
880 xformOpt |= TJTransform.OPT_COPYNONE;
881 else if (argv[i].equalsIgnoreCase("-benchtime") &&
882 i < argv.length - 1) {
886 temp = Double.parseDouble(argv[++i]);
887 } catch (NumberFormatException e) {}
892 } else if (argv[i].equalsIgnoreCase("-warmup") &&
893 i < argv.length - 1) {
897 temp = Double.parseDouble(argv[++i]);
898 } catch (NumberFormatException e) {}
901 System.out.format("Warmup time = %.1f seconds\n\n", warmup);
904 } else if (argv[i].equalsIgnoreCase("-yuv")) {
905 System.out.println("Testing YUV planar encoding/decoding\n");
907 } else if (argv[i].equalsIgnoreCase("-yuvpad") &&
908 i < argv.length - 1) {
912 temp = Integer.parseInt(argv[++i]);
913 } catch (NumberFormatException e) {}
916 } else if (argv[i].equalsIgnoreCase("-subsamp") &&
917 i < argv.length - 1) {
919 if (argv[i].toUpperCase().startsWith("G"))
920 subsamp = TJ.SAMP_GRAY;
921 else if (argv[i].equals("444"))
922 subsamp = TJ.SAMP_444;
923 else if (argv[i].equals("422"))
924 subsamp = TJ.SAMP_422;
925 else if (argv[i].equals("440"))
926 subsamp = TJ.SAMP_440;
927 else if (argv[i].equals("420"))
928 subsamp = TJ.SAMP_420;
929 else if (argv[i].equals("411"))
930 subsamp = TJ.SAMP_411;
931 } else if (argv[i].equalsIgnoreCase("-componly"))
933 else if (argv[i].equalsIgnoreCase("-nowrite"))
935 else if (argv[i].equalsIgnoreCase("-limitscans"))
936 flags |= TJ.FLAG_LIMITSCANS;
937 else if (argv[i].equalsIgnoreCase("-stoponwarning"))
938 flags |= TJ.FLAG_STOPONWARNING;
944 sf = new TJScalingFactor(1, 1);
946 if ((sf.getNum() != 1 || sf.getDenom() != 1) && doTile) {
947 System.out.println("Disabling tiled compression/decompression tests, because those tests do not");
948 System.out.println("work when scaled decompression is enabled.");
953 int[] width = new int[1], height = new int[1];
955 srcBuf = loadImage(argv[0], width, height, pf);
956 w = width[0]; h = height[0];
958 if ((index = argv[0].lastIndexOf('.')) >= 0)
959 argv[0] = argv[0].substring(0, index);
962 if (quiet == 1 && !decompOnly) {
963 System.out.println("All performance values in Mpixels/sec\n");
964 System.out.format("Bitmap JPEG JPEG %s %s ",
965 (doTile ? "Tile " : "Image"),
966 (doTile ? "Tile " : "Image"));
968 System.out.print("Encode ");
969 System.out.print("Comp Comp Decomp ");
971 System.out.print("Decode");
972 System.out.print("\n");
973 System.out.print("Format Subsamp Qual Width Height ");
975 System.out.print("Perf ");
976 System.out.print("Perf Ratio Perf ");
978 System.out.print("Perf");
979 System.out.println("\n");
984 System.out.println("");
989 if (subsamp >= 0 && subsamp < TJ.NUMSAMP) {
990 for (int i = maxQual; i >= minQual; i--)
991 fullTest(srcBuf, w, h, subsamp, i, argv[0]);
992 System.out.println("");
994 for (int i = maxQual; i >= minQual; i--)
995 fullTest(srcBuf, w, h, TJ.SAMP_GRAY, i, argv[0]);
996 System.out.println("");
998 for (int i = maxQual; i >= minQual; i--)
999 fullTest(srcBuf, w, h, TJ.SAMP_420, i, argv[0]);
1000 System.out.println("");
1002 for (int i = maxQual; i >= minQual; i--)
1003 fullTest(srcBuf, w, h, TJ.SAMP_422, i, argv[0]);
1004 System.out.println("");
1006 for (int i = maxQual; i >= minQual; i--)
1007 fullTest(srcBuf, w, h, TJ.SAMP_444, i, argv[0]);
1008 System.out.println("");
1011 } catch (Exception e) {
1012 if (e instanceof TJException) {
1013 TJException tje = (TJException)e;
1015 System.out.println((tje.getErrorCode() == TJ.ERR_WARNING ?
1016 "WARNING: " : "ERROR: ") + tje.getMessage());
1018 System.out.println("ERROR: " + e.getMessage());
1019 e.printStackTrace();
1023 System.exit(retval);