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