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