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 
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     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 
saveImage(String fileName, byte[] srcBuf, int w, int h, int pixelFormat)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 */
decomp(byte[] srcBuf, byte[][] jpegBuf, int[] jpegSize, byte[] dstBuf, int w, int h, int subsamp, int jpegQual, String fileName, int tilew, int tileh)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 
fullTest(byte[] srcBuf, int w, int h, int subsamp, int jpegQual, String fileName)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 
decompTest(String fileName)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 
usage()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 
main(String[] argv)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