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