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