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