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