xref: /aosp_15_r20/external/libjpeg-turbo/tjbench.c (revision dfc6aa5c1cfd4bc4e2018dc74aa96e29ee49c6da)
1 /*
2  * Copyright (C)2009-2019, 2021-2023 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 #ifdef _MSC_VER
30 #define _CRT_SECURE_NO_DEPRECATE
31 #endif
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <math.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include <cdjpeg.h>
41 #include "./tjutil.h"
42 #include "./turbojpeg.h"
43 
44 
45 #define THROW(op, err) { \
46   fprintf(stderr, "ERROR in line %d while %s:\n%s\n", __LINE__, op, err); \
47   retval = -1;  goto bailout; \
48 }
49 #define THROW_UNIX(m)  THROW(m, strerror(errno))
50 
51 static char tjErrorStr[JMSG_LENGTH_MAX] = "\0",
52             tjErrorMsg[JMSG_LENGTH_MAX] = "\0";
53 static int tjErrorLine = -1, tjErrorCode = -1;
54 
55 #define THROW_TJG(m) { \
56   fprintf(stderr, "ERROR in line %d while %s:\n%s\n", __LINE__, m, \
57           tjGetErrorStr2(NULL)); \
58   retval = -1;  goto bailout; \
59 }
60 
61 #define THROW_TJ(m) { \
62   int _tjErrorCode = tjGetErrorCode(handle); \
63   char *_tjErrorStr = tjGetErrorStr2(handle); \
64   \
65   if (!(flags & TJFLAG_STOPONWARNING) && _tjErrorCode == TJERR_WARNING) { \
66     if (strncmp(tjErrorStr, _tjErrorStr, JMSG_LENGTH_MAX) || \
67         strncmp(tjErrorMsg, m, JMSG_LENGTH_MAX) || \
68         tjErrorCode != _tjErrorCode || tjErrorLine != __LINE__) { \
69       strncpy(tjErrorStr, _tjErrorStr, JMSG_LENGTH_MAX); \
70       tjErrorStr[JMSG_LENGTH_MAX - 1] = '\0'; \
71       strncpy(tjErrorMsg, m, JMSG_LENGTH_MAX); \
72       tjErrorMsg[JMSG_LENGTH_MAX - 1] = '\0'; \
73       tjErrorCode = _tjErrorCode; \
74       tjErrorLine = __LINE__; \
75       fprintf(stderr, "WARNING in line %d while %s:\n%s\n", __LINE__, m, \
76               _tjErrorStr); \
77     } \
78   } else { \
79     fprintf(stderr, "%s in line %d while %s:\n%s\n", \
80             _tjErrorCode == TJERR_WARNING ? "WARNING" : "ERROR", __LINE__, m, \
81             _tjErrorStr); \
82     retval = -1;  goto bailout; \
83   } \
84 }
85 
86 static int flags = TJFLAG_NOREALLOC, compOnly = 0, decompOnly = 0, doYUV = 0,
87   quiet = 0, doTile = 0, pf = TJPF_BGR, yuvAlign = 1, doWrite = 1;
88 static char *ext = "ppm";
89 static const char *pixFormatStr[TJ_NUMPF] = {
90   "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY", "", "", "", "", "CMYK"
91 };
92 static const char *subNameLong[TJ_NUMSAMP] = {
93   "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1"
94 };
95 static const char *csName[TJ_NUMCS] = {
96   "RGB", "YCbCr", "GRAY", "CMYK", "YCCK"
97 };
98 static const char *subName[TJ_NUMSAMP] = {
99   "444", "422", "420", "GRAY", "440", "411"
100 };
101 static tjscalingfactor *scalingFactors = NULL, sf = { 1, 1 };
102 static int nsf = 0, xformOp = TJXOP_NONE, xformOpt = 0;
103 static int (*customFilter) (short *, tjregion, tjregion, int, int,
104                             tjtransform *);
105 static double benchTime = 5.0, warmup = 1.0;
106 
107 
formatName(int subsamp,int cs,char * buf)108 static char *formatName(int subsamp, int cs, char *buf)
109 {
110   if (cs == TJCS_YCbCr)
111     return (char *)subNameLong[subsamp];
112   else if (cs == TJCS_YCCK || cs == TJCS_CMYK) {
113     SNPRINTF(buf, 80, "%s %s", csName[cs], subNameLong[subsamp]);
114     return buf;
115   } else
116     return (char *)csName[cs];
117 }
118 
119 
sigfig(double val,int figs,char * buf,int len)120 static char *sigfig(double val, int figs, char *buf, int len)
121 {
122   char format[80];
123   int digitsAfterDecimal = figs - (int)ceil(log10(fabs(val)));
124 
125   if (digitsAfterDecimal < 1)
126     SNPRINTF(format, 80, "%%.0f");
127   else
128     SNPRINTF(format, 80, "%%.%df", digitsAfterDecimal);
129   SNPRINTF(buf, len, format, val);
130   return buf;
131 }
132 
133 
134 /* Custom DCT filter which produces a negative of the image */
dummyDCTFilter(short * coeffs,tjregion arrayRegion,tjregion planeRegion,int componentIndex,int transformIndex,tjtransform * transform)135 static int dummyDCTFilter(short *coeffs, tjregion arrayRegion,
136                           tjregion planeRegion, int componentIndex,
137                           int transformIndex, tjtransform *transform)
138 {
139   int i;
140 
141   for (i = 0; i < arrayRegion.w * arrayRegion.h; i++)
142     coeffs[i] = -coeffs[i];
143   return 0;
144 }
145 
146 
147 /* Decompression test */
decomp(unsigned char * srcBuf,unsigned char ** jpegBuf,unsigned long * jpegSize,unsigned char * dstBuf,int w,int h,int subsamp,int jpegQual,char * fileName,int tilew,int tileh)148 static int decomp(unsigned char *srcBuf, unsigned char **jpegBuf,
149                   unsigned long *jpegSize, unsigned char *dstBuf, int w, int h,
150                   int subsamp, int jpegQual, char *fileName, int tilew,
151                   int tileh)
152 {
153   char tempStr[1024], sizeStr[24] = "\0", qualStr[13] = "\0", *ptr;
154   FILE *file = NULL;
155   tjhandle handle = NULL;
156   int row, col, iter = 0, dstBufAlloc = 0, retval = 0;
157   double elapsed, elapsedDecode;
158   int ps = tjPixelSize[pf];
159   int scaledw = TJSCALED(w, sf);
160   int scaledh = TJSCALED(h, sf);
161   int pitch = scaledw * ps;
162   int ntilesw = (w + tilew - 1) / tilew, ntilesh = (h + tileh - 1) / tileh;
163   unsigned char *dstPtr, *dstPtr2, *yuvBuf = NULL;
164 
165   if (jpegQual > 0) {
166     SNPRINTF(qualStr, 13, "_Q%d", jpegQual);
167     qualStr[12] = 0;
168   }
169 
170   if ((handle = tjInitDecompress()) == NULL)
171     THROW_TJ("executing tjInitDecompress()");
172 
173   if (dstBuf == NULL) {
174     if ((unsigned long long)pitch * (unsigned long long)scaledh >
175         (unsigned long long)((size_t)-1))
176       THROW("allocating destination buffer", "Image is too large");
177     if ((dstBuf = (unsigned char *)malloc((size_t)pitch * scaledh)) == NULL)
178       THROW_UNIX("allocating destination buffer");
179     dstBufAlloc = 1;
180   }
181   /* Set the destination buffer to gray so we know whether the decompressor
182      attempted to write to it */
183   memset(dstBuf, 127, (size_t)pitch * scaledh);
184 
185   if (doYUV) {
186     int width = doTile ? tilew : scaledw;
187     int height = doTile ? tileh : scaledh;
188     unsigned long yuvSize = tjBufSizeYUV2(width, yuvAlign, height, subsamp);
189 
190     if (yuvSize == (unsigned long)-1)
191       THROW_TJ("allocating YUV buffer");
192     if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
193       THROW_UNIX("allocating YUV buffer");
194     memset(yuvBuf, 127, yuvSize);
195   }
196 
197   /* Benchmark */
198   iter = -1;
199   elapsed = elapsedDecode = 0.;
200   while (1) {
201     int tile = 0;
202     double start = getTime();
203 
204     for (row = 0, dstPtr = dstBuf; row < ntilesh;
205          row++, dstPtr += (size_t)pitch * tileh) {
206       for (col = 0, dstPtr2 = dstPtr; col < ntilesw;
207            col++, tile++, dstPtr2 += ps * tilew) {
208         int width = doTile ? min(tilew, w - col * tilew) : scaledw;
209         int height = doTile ? min(tileh, h - row * tileh) : scaledh;
210 
211         if (doYUV) {
212           double startDecode;
213 
214           if (tjDecompressToYUV2(handle, jpegBuf[tile], jpegSize[tile], yuvBuf,
215                                  width, yuvAlign, height, flags) == -1)
216             THROW_TJ("executing tjDecompressToYUV2()");
217           startDecode = getTime();
218           if (tjDecodeYUV(handle, yuvBuf, yuvAlign, subsamp, dstPtr2, width,
219                           pitch, height, pf, flags) == -1)
220             THROW_TJ("executing tjDecodeYUV()");
221           if (iter >= 0) elapsedDecode += getTime() - startDecode;
222         } else if (tjDecompress2(handle, jpegBuf[tile], jpegSize[tile],
223                                  dstPtr2, width, pitch, height, pf,
224                                  flags) == -1)
225           THROW_TJ("executing tjDecompress2()");
226       }
227     }
228     elapsed += getTime() - start;
229     if (iter >= 0) {
230       iter++;
231       if (elapsed >= benchTime) break;
232     } else if (elapsed >= warmup) {
233       iter = 0;
234       elapsed = elapsedDecode = 0.;
235     }
236   }
237   if (doYUV) elapsed -= elapsedDecode;
238 
239   if (tjDestroy(handle) == -1) THROW_TJ("executing tjDestroy()");
240   handle = NULL;
241 
242   if (quiet) {
243     fprintf(stderr, "%-6s%s",
244             sigfig((double)(w * h) / 1000000. * (double)iter / elapsed, 4,
245                    tempStr, 1024),
246            quiet == 2 ? "\n" : "  ");
247     if (doYUV)
248       fprintf(stderr, "%s\n",
249               sigfig((double)(w * h) / 1000000. * (double)iter / elapsedDecode,
250                      4, tempStr, 1024));
251     else if (quiet != 2) fprintf(stderr, "\n");
252   } else {
253     fprintf(stderr, "%s --> Frame rate:         %f fps\n",
254             doYUV ? "Decomp to YUV" : "Decompress   ", (double)iter / elapsed);
255     fprintf(stderr,
256             "                  Throughput:         %f Megapixels/sec\n",
257             (double)(w * h) / 1000000. * (double)iter / elapsed);
258     if (doYUV) {
259       fprintf(stderr, "YUV Decode    --> Frame rate:         %f fps\n",
260               (double)iter / elapsedDecode);
261       fprintf(stderr,
262               "                  Throughput:         %f Megapixels/sec\n",
263               (double)(w * h) / 1000000. * (double)iter / elapsedDecode);
264     }
265   }
266 
267   if (!doWrite) goto bailout;
268 
269   if (sf.num != 1 || sf.denom != 1)
270     SNPRINTF(sizeStr, 24, "%d_%d", sf.num, sf.denom);
271   else if (tilew != w || tileh != h)
272     SNPRINTF(sizeStr, 24, "%dx%d", tilew, tileh);
273   else SNPRINTF(sizeStr, 24, "full");
274   if (decompOnly)
275     SNPRINTF(tempStr, 1024, "%s_%s.%s", fileName, sizeStr, ext);
276   else
277     SNPRINTF(tempStr, 1024, "%s_%s%s_%s.%s", fileName, subName[subsamp],
278              qualStr, sizeStr, ext);
279 
280   if (tjSaveImage(tempStr, dstBuf, scaledw, 0, scaledh, pf, flags) == -1)
281     THROW_TJG("saving output image");
282   ptr = strrchr(tempStr, '.');
283   SNPRINTF(ptr, 1024 - (ptr - tempStr), "-err.%s", ext);
284   if (srcBuf && sf.num == 1 && sf.denom == 1) {
285     if (!quiet) fprintf(stderr, "Compression error written to %s.\n", tempStr);
286     if (subsamp == TJSAMP_GRAY) {
287       unsigned long index, index2;
288 
289       for (row = 0, index = 0; row < h; row++, index += pitch) {
290         for (col = 0, index2 = index; col < w; col++, index2 += ps) {
291           unsigned long rindex = index2 + tjRedOffset[pf];
292           unsigned long gindex = index2 + tjGreenOffset[pf];
293           unsigned long bindex = index2 + tjBlueOffset[pf];
294           int y = (int)((double)srcBuf[rindex] * 0.299 +
295                         (double)srcBuf[gindex] * 0.587 +
296                         (double)srcBuf[bindex] * 0.114 + 0.5);
297 
298           if (y > 255) y = 255;
299           if (y < 0) y = 0;
300           dstBuf[rindex] = (unsigned char)abs(dstBuf[rindex] - y);
301           dstBuf[gindex] = (unsigned char)abs(dstBuf[gindex] - y);
302           dstBuf[bindex] = (unsigned char)abs(dstBuf[bindex] - y);
303         }
304       }
305     } else {
306       for (row = 0; row < h; row++)
307         for (col = 0; col < w * ps; col++)
308           dstBuf[pitch * row + col] =
309             (unsigned char)abs(dstBuf[pitch * row + col] -
310                                srcBuf[pitch * row + col]);
311     }
312     if (tjSaveImage(tempStr, dstBuf, w, 0, h, pf, flags) == -1)
313       THROW_TJG("saving output image");
314   }
315 
316 bailout:
317   if (file) fclose(file);
318   if (handle) tjDestroy(handle);
319   if (dstBufAlloc) free(dstBuf);
320   free(yuvBuf);
321   return retval;
322 }
323 
324 
fullTest(unsigned char * srcBuf,int w,int h,int subsamp,int jpegQual,char * fileName)325 static int fullTest(unsigned char *srcBuf, int w, int h, int subsamp,
326                     int jpegQual, char *fileName)
327 {
328   char tempStr[1024], tempStr2[80];
329   FILE *file = NULL;
330   tjhandle handle = NULL;
331   unsigned char **jpegBuf = NULL, *yuvBuf = NULL, *tmpBuf = NULL, *srcPtr,
332     *srcPtr2;
333   double start, elapsed, elapsedEncode;
334   int totalJpegSize = 0, row, col, i, tilew = w, tileh = h, retval = 0;
335   int iter;
336   unsigned long *jpegSize = NULL, yuvSize = 0;
337   int ps = tjPixelSize[pf];
338   int ntilesw = 1, ntilesh = 1, pitch = w * ps;
339   const char *pfStr = pixFormatStr[pf];
340 
341   if ((unsigned long long)pitch * (unsigned long long)h >
342       (unsigned long long)((size_t)-1))
343     THROW("allocating temporary image buffer", "Image is too large");
344   if ((tmpBuf = (unsigned char *)malloc((size_t)pitch * h)) == NULL)
345     THROW_UNIX("allocating temporary image buffer");
346 
347   if (!quiet)
348     fprintf(stderr, ">>>>>  %s (%s) <--> JPEG %s Q%d  <<<<<\n", pfStr,
349             (flags & TJFLAG_BOTTOMUP) ? "Bottom-up" : "Top-down",
350             subNameLong[subsamp], jpegQual);
351 
352   for (tilew = doTile ? 8 : w, tileh = doTile ? 8 : h; ;
353        tilew *= 2, tileh *= 2) {
354     if (tilew > w) tilew = w;
355     if (tileh > h) tileh = h;
356     ntilesw = (w + tilew - 1) / tilew;
357     ntilesh = (h + tileh - 1) / tileh;
358 
359     if ((jpegBuf = (unsigned char **)malloc(sizeof(unsigned char *) *
360                                             ntilesw * ntilesh)) == NULL)
361       THROW_UNIX("allocating JPEG tile array");
362     memset(jpegBuf, 0, sizeof(unsigned char *) * ntilesw * ntilesh);
363     if ((jpegSize = (unsigned long *)malloc(sizeof(unsigned long) *
364                                             ntilesw * ntilesh)) == NULL)
365       THROW_UNIX("allocating JPEG size array");
366     memset(jpegSize, 0, sizeof(unsigned long) * ntilesw * ntilesh);
367 
368     if ((flags & TJFLAG_NOREALLOC) != 0)
369       for (i = 0; i < ntilesw * ntilesh; i++) {
370         if (tjBufSize(tilew, tileh, subsamp) > (unsigned long)INT_MAX)
371           THROW("getting buffer size", "Image is too large");
372         if ((jpegBuf[i] = (unsigned char *)
373                           tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL)
374           THROW_UNIX("allocating JPEG tiles");
375       }
376 
377     /* Compression test */
378     if (quiet == 1)
379       fprintf(stderr, "%-4s (%s)  %-5s    %-3d   ", pfStr,
380               (flags & TJFLAG_BOTTOMUP) ? "BU" : "TD", subNameLong[subsamp],
381               jpegQual);
382     for (i = 0; i < h; i++)
383       memcpy(&tmpBuf[pitch * i], &srcBuf[w * ps * i], w * ps);
384     if ((handle = tjInitCompress()) == NULL)
385       THROW_TJ("executing tjInitCompress()");
386 
387     if (doYUV) {
388       yuvSize = tjBufSizeYUV2(tilew, yuvAlign, tileh, subsamp);
389       if (yuvSize == (unsigned long)-1)
390         THROW_TJ("allocating YUV buffer");
391       if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
392         THROW_UNIX("allocating YUV buffer");
393       memset(yuvBuf, 127, yuvSize);
394     }
395 
396     /* Benchmark */
397     iter = -1;
398     elapsed = elapsedEncode = 0.;
399     while (1) {
400       int tile = 0;
401 
402       totalJpegSize = 0;
403       start = getTime();
404       for (row = 0, srcPtr = srcBuf; row < ntilesh;
405            row++, srcPtr += pitch * tileh) {
406         for (col = 0, srcPtr2 = srcPtr; col < ntilesw;
407              col++, tile++, srcPtr2 += ps * tilew) {
408           int width = min(tilew, w - col * tilew);
409           int height = min(tileh, h - row * tileh);
410 
411           if (doYUV) {
412             double startEncode = getTime();
413 
414             if (tjEncodeYUV3(handle, srcPtr2, width, pitch, height, pf, yuvBuf,
415                              yuvAlign, subsamp, flags) == -1)
416               THROW_TJ("executing tjEncodeYUV3()");
417             if (iter >= 0) elapsedEncode += getTime() - startEncode;
418             if (tjCompressFromYUV(handle, yuvBuf, width, yuvAlign, height,
419                                   subsamp, &jpegBuf[tile], &jpegSize[tile],
420                                   jpegQual, flags) == -1)
421               THROW_TJ("executing tjCompressFromYUV()");
422           } else {
423             if (tjCompress2(handle, srcPtr2, width, pitch, height, pf,
424                             &jpegBuf[tile], &jpegSize[tile], subsamp, jpegQual,
425                             flags) == -1)
426               THROW_TJ("executing tjCompress2()");
427           }
428           totalJpegSize += jpegSize[tile];
429         }
430       }
431       elapsed += getTime() - start;
432       if (iter >= 0) {
433         iter++;
434         if (elapsed >= benchTime) break;
435       } else if (elapsed >= warmup) {
436         iter = 0;
437         elapsed = elapsedEncode = 0.;
438       }
439     }
440     if (doYUV) elapsed -= elapsedEncode;
441 
442     if (tjDestroy(handle) == -1) THROW_TJ("executing tjDestroy()");
443     handle = NULL;
444 
445     if (quiet == 1) fprintf(stderr, "%-5d  %-5d   ", tilew, tileh);
446     if (quiet) {
447       if (doYUV)
448         fprintf(stderr, "%-6s%s",
449                 sigfig((double)(w * h) / 1000000. *
450                        (double)iter / elapsedEncode, 4, tempStr, 1024),
451                 quiet == 2 ? "\n" : "  ");
452       fprintf(stderr, "%-6s%s",
453               sigfig((double)(w * h) / 1000000. * (double)iter / elapsed, 4,
454                      tempStr, 1024),
455               quiet == 2 ? "\n" : "  ");
456       fprintf(stderr, "%-6s%s",
457               sigfig((double)(w * h * ps) / (double)totalJpegSize, 4, tempStr2,
458                      80),
459               quiet == 2 ? "\n" : "  ");
460     } else {
461       fprintf(stderr, "\n%s size: %d x %d\n", doTile ? "Tile" : "Image", tilew,
462               tileh);
463       if (doYUV) {
464         fprintf(stderr, "Encode YUV    --> Frame rate:         %f fps\n",
465                 (double)iter / elapsedEncode);
466         fprintf(stderr, "                  Output image size:  %lu bytes\n",
467                 yuvSize);
468         fprintf(stderr, "                  Compression ratio:  %f:1\n",
469                 (double)(w * h * ps) / (double)yuvSize);
470         fprintf(stderr,
471                 "                  Throughput:         %f Megapixels/sec\n",
472                 (double)(w * h) / 1000000. * (double)iter / elapsedEncode);
473         fprintf(stderr,
474                 "                  Output bit stream:  %f Megabits/sec\n",
475                 (double)yuvSize * 8. / 1000000. * (double)iter / elapsedEncode);
476       }
477       fprintf(stderr, "%s --> Frame rate:         %f fps\n",
478               doYUV ? "Comp from YUV" : "Compress     ",
479               (double)iter / elapsed);
480       fprintf(stderr, "                  Output image size:  %d bytes\n",
481               totalJpegSize);
482       fprintf(stderr, "                  Compression ratio:  %f:1\n",
483               (double)(w * h * ps) / (double)totalJpegSize);
484       fprintf(stderr,
485               "                  Throughput:         %f Megapixels/sec\n",
486               (double)(w * h) / 1000000. * (double)iter / elapsed);
487       fprintf(stderr,
488               "                  Output bit stream:  %f Megabits/sec\n",
489               (double)totalJpegSize * 8. / 1000000. * (double)iter / elapsed);
490     }
491     if (tilew == w && tileh == h && doWrite) {
492       SNPRINTF(tempStr, 1024, "%s_%s_Q%d.jpg", fileName, subName[subsamp],
493                jpegQual);
494       if ((file = fopen(tempStr, "wb")) == NULL)
495         THROW_UNIX("opening reference image");
496       if (fwrite(jpegBuf[0], jpegSize[0], 1, file) != 1)
497         THROW_UNIX("writing reference image");
498       fclose(file);  file = NULL;
499       if (!quiet) fprintf(stderr, "Reference image written to %s\n", tempStr);
500     }
501 
502     /* Decompression test */
503     if (!compOnly) {
504       if (decomp(srcBuf, jpegBuf, jpegSize, tmpBuf, w, h, subsamp, jpegQual,
505                  fileName, tilew, tileh) == -1)
506         goto bailout;
507     } else if (quiet == 1) fprintf(stderr, "N/A\n");
508 
509     for (i = 0; i < ntilesw * ntilesh; i++) {
510       tjFree(jpegBuf[i]);
511       jpegBuf[i] = NULL;
512     }
513     free(jpegBuf);  jpegBuf = NULL;
514     free(jpegSize);  jpegSize = NULL;
515     if (doYUV) {
516       free(yuvBuf);  yuvBuf = NULL;
517     }
518 
519     if (tilew == w && tileh == h) break;
520   }
521 
522 bailout:
523   if (file) fclose(file);
524   if (jpegBuf) {
525     for (i = 0; i < ntilesw * ntilesh; i++)
526       tjFree(jpegBuf[i]);
527   }
528   free(jpegBuf);
529   free(yuvBuf);
530   free(jpegSize);
531   free(tmpBuf);
532   if (handle) tjDestroy(handle);
533   return retval;
534 }
535 
536 
decompTest(char * fileName)537 static int decompTest(char *fileName)
538 {
539   FILE *file = NULL;
540   tjhandle handle = NULL;
541   unsigned char **jpegBuf = NULL, *srcBuf = NULL;
542   unsigned long *jpegSize = NULL, srcSize, totalJpegSize;
543   tjtransform *t = NULL;
544   double start, elapsed;
545   int ps = tjPixelSize[pf], tile, row, col, i, iter, retval = 0, decompsrc = 0;
546   char *temp = NULL, tempStr[80], tempStr2[80];
547   /* Original image */
548   int w = 0, h = 0, tilew, tileh, ntilesw = 1, ntilesh = 1, subsamp = -1,
549     cs = -1;
550   /* Transformed image */
551   int tw, th, ttilew, ttileh, tntilesw, tntilesh, tsubsamp;
552 
553   if ((file = fopen(fileName, "rb")) == NULL)
554     THROW_UNIX("opening file");
555   if (fseek(file, 0, SEEK_END) < 0 ||
556       (srcSize = ftell(file)) == (unsigned long)-1)
557     THROW_UNIX("determining file size");
558   if ((srcBuf = (unsigned char *)malloc(srcSize)) == NULL)
559     THROW_UNIX("allocating memory");
560   if (fseek(file, 0, SEEK_SET) < 0)
561     THROW_UNIX("setting file position");
562   if (fread(srcBuf, srcSize, 1, file) < 1)
563     THROW_UNIX("reading JPEG data");
564   fclose(file);  file = NULL;
565 
566   temp = strrchr(fileName, '.');
567   if (temp != NULL) *temp = '\0';
568 
569   if ((handle = tjInitTransform()) == NULL)
570     THROW_TJ("executing tjInitTransform()");
571   if (tjDecompressHeader3(handle, srcBuf, srcSize, &w, &h, &subsamp,
572                           &cs) == -1)
573     THROW_TJ("executing tjDecompressHeader3()");
574   if (w < 1 || h < 1)
575     THROW("reading JPEG header", "Invalid image dimensions");
576   if (cs == TJCS_YCCK || cs == TJCS_CMYK) {
577     pf = TJPF_CMYK;  ps = tjPixelSize[pf];
578   }
579 
580   if (quiet == 1) {
581     fprintf(stderr, "All performance values in Mpixels/sec\n\n");
582     fprintf(stderr,
583             "Pixel     JPEG   JPEG     %s  %s   Xform   Comp    Decomp  ",
584             doTile ? "Tile " : "Image", doTile ? "Tile " : "Image");
585     if (doYUV) fprintf(stderr, "Decode");
586     fprintf(stderr, "\n");
587     fprintf(stderr,
588         "Format     CS     Subsamp  Width  Height  Perf    Ratio   Perf    ");
589     if (doYUV) fprintf(stderr, "Perf");
590     fprintf(stderr, "\n\n");
591   } else if (!quiet)
592     fprintf(stderr, ">>>>>  JPEG %s --> %s (%s)  <<<<<\n",
593             formatName(subsamp, cs, tempStr), pixFormatStr[pf],
594             (flags & TJFLAG_BOTTOMUP) ? "Bottom-up" : "Top-down");
595 
596   for (tilew = doTile ? 16 : w, tileh = doTile ? 16 : h; ;
597        tilew *= 2, tileh *= 2) {
598     if (tilew > w) tilew = w;
599     if (tileh > h) tileh = h;
600     ntilesw = (w + tilew - 1) / tilew;
601     ntilesh = (h + tileh - 1) / tileh;
602 
603     if ((jpegBuf = (unsigned char **)malloc(sizeof(unsigned char *) *
604                                             ntilesw * ntilesh)) == NULL)
605       THROW_UNIX("allocating JPEG tile array");
606     memset(jpegBuf, 0, sizeof(unsigned char *) * ntilesw * ntilesh);
607     if ((jpegSize = (unsigned long *)malloc(sizeof(unsigned long) *
608                                             ntilesw * ntilesh)) == NULL)
609       THROW_UNIX("allocating JPEG size array");
610     memset(jpegSize, 0, sizeof(unsigned long) * ntilesw * ntilesh);
611 
612     if ((flags & TJFLAG_NOREALLOC) != 0 &&
613         (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter))
614       for (i = 0; i < ntilesw * ntilesh; i++) {
615         if (tjBufSize(tilew, tileh, subsamp) > (unsigned long)INT_MAX)
616           THROW("getting buffer size", "Image is too large");
617         if ((jpegBuf[i] = (unsigned char *)
618                           tjAlloc(tjBufSize(tilew, tileh, subsamp))) == NULL)
619           THROW_UNIX("allocating JPEG tiles");
620       }
621 
622     tw = w;  th = h;  ttilew = tilew;  ttileh = tileh;
623     if (!quiet) {
624       fprintf(stderr, "\n%s size: %d x %d", doTile ? "Tile" : "Image", ttilew,
625               ttileh);
626       if (sf.num != 1 || sf.denom != 1)
627         fprintf(stderr, " --> %d x %d", TJSCALED(tw, sf), TJSCALED(th, sf));
628       fprintf(stderr, "\n");
629     } else if (quiet == 1) {
630       fprintf(stderr, "%-4s (%s)  %-5s  %-5s    ", pixFormatStr[pf],
631               (flags & TJFLAG_BOTTOMUP) ? "BU" : "TD", csName[cs],
632               subNameLong[subsamp]);
633       fprintf(stderr, "%-5d  %-5d   ", tilew, tileh);
634     }
635 
636     tsubsamp = subsamp;
637     if (doTile || xformOp != TJXOP_NONE || xformOpt != 0 || customFilter) {
638       if ((t = (tjtransform *)malloc(sizeof(tjtransform) * ntilesw *
639                                      ntilesh)) == NULL)
640         THROW_UNIX("allocating image transform array");
641 
642       if (xformOp == TJXOP_TRANSPOSE || xformOp == TJXOP_TRANSVERSE ||
643           xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) {
644         tw = h;  th = w;  ttilew = tileh;  ttileh = tilew;
645       }
646 
647       if (xformOpt & TJXOPT_GRAY) tsubsamp = TJSAMP_GRAY;
648       if (xformOp == TJXOP_HFLIP || xformOp == TJXOP_ROT180)
649         tw = tw - (tw % tjMCUWidth[tsubsamp]);
650       if (xformOp == TJXOP_VFLIP || xformOp == TJXOP_ROT180)
651         th = th - (th % tjMCUHeight[tsubsamp]);
652       if (xformOp == TJXOP_TRANSVERSE || xformOp == TJXOP_ROT90)
653         tw = tw - (tw % tjMCUHeight[tsubsamp]);
654       if (xformOp == TJXOP_TRANSVERSE || xformOp == TJXOP_ROT270)
655         th = th - (th % tjMCUWidth[tsubsamp]);
656       tntilesw = (tw + ttilew - 1) / ttilew;
657       tntilesh = (th + ttileh - 1) / ttileh;
658 
659       if (xformOp == TJXOP_TRANSPOSE || xformOp == TJXOP_TRANSVERSE ||
660           xformOp == TJXOP_ROT90 || xformOp == TJXOP_ROT270) {
661         if (tsubsamp == TJSAMP_422) tsubsamp = TJSAMP_440;
662         else if (tsubsamp == TJSAMP_440) tsubsamp = TJSAMP_422;
663       }
664 
665       for (row = 0, tile = 0; row < tntilesh; row++) {
666         for (col = 0; col < tntilesw; col++, tile++) {
667           t[tile].r.w = min(ttilew, tw - col * ttilew);
668           t[tile].r.h = min(ttileh, th - row * ttileh);
669           t[tile].r.x = col * ttilew;
670           t[tile].r.y = row * ttileh;
671           t[tile].op = xformOp;
672           t[tile].options = xformOpt | TJXOPT_TRIM;
673           t[tile].customFilter = customFilter;
674           if (t[tile].options & TJXOPT_NOOUTPUT && jpegBuf[tile]) {
675             tjFree(jpegBuf[tile]);  jpegBuf[tile] = NULL;
676           }
677         }
678       }
679 
680       iter = -1;
681       elapsed = 0.;
682       while (1) {
683         start = getTime();
684         if (tjTransform(handle, srcBuf, srcSize, tntilesw * tntilesh, jpegBuf,
685                         jpegSize, t, flags) == -1)
686           THROW_TJ("executing tjTransform()");
687         elapsed += getTime() - start;
688         if (iter >= 0) {
689           iter++;
690           if (elapsed >= benchTime) break;
691         } else if (elapsed >= warmup) {
692           iter = 0;
693           elapsed = 0.;
694         }
695       }
696 
697       free(t);  t = NULL;
698 
699       for (tile = 0, totalJpegSize = 0; tile < tntilesw * tntilesh; tile++)
700         totalJpegSize += jpegSize[tile];
701 
702       if (quiet) {
703         fprintf(stderr, "%-6s%s%-6s%s",
704                 sigfig((double)(w * h) / 1000000. / elapsed, 4, tempStr, 80),
705                 quiet == 2 ? "\n" : "  ",
706                 sigfig((double)(w * h * ps) / (double)totalJpegSize, 4,
707                        tempStr2, 80),
708                 quiet == 2 ? "\n" : "  ");
709       } else {
710         fprintf(stderr, "Transform     --> Frame rate:         %f fps\n",
711                 1.0 / elapsed);
712         fprintf(stderr, "                  Output image size:  %lu bytes\n",
713                 totalJpegSize);
714         fprintf(stderr, "                  Compression ratio:  %f:1\n",
715                 (double)(w * h * ps) / (double)totalJpegSize);
716         fprintf(stderr,
717                 "                  Throughput:         %f Megapixels/sec\n",
718                 (double)(w * h) / 1000000. / elapsed);
719         fprintf(stderr,
720                 "                  Output bit stream:  %f Megabits/sec\n",
721                 (double)totalJpegSize * 8. / 1000000. / elapsed);
722       }
723     } else {
724       if (quiet == 1) fprintf(stderr, "N/A     N/A     ");
725       tjFree(jpegBuf[0]);
726       jpegBuf[0] = NULL;
727       decompsrc = 1;
728     }
729 
730     if (w == tilew) ttilew = tw;
731     if (h == tileh) ttileh = th;
732     if (!(xformOpt & TJXOPT_NOOUTPUT)) {
733       if (decomp(NULL, decompsrc ? &srcBuf : jpegBuf,
734                  decompsrc ? &srcSize : jpegSize, NULL, tw, th, tsubsamp, 0,
735                  fileName, ttilew, ttileh) == -1)
736         goto bailout;
737     } else if (quiet == 1) fprintf(stderr, "N/A\n");
738 
739     for (i = 0; i < ntilesw * ntilesh; i++) {
740       tjFree(jpegBuf[i]);
741       jpegBuf[i] = NULL;
742     }
743     free(jpegBuf);  jpegBuf = NULL;
744     free(jpegSize);  jpegSize = NULL;
745 
746     if (tilew == w && tileh == h) break;
747   }
748 
749 bailout:
750   if (file) fclose(file);
751   if (jpegBuf) {
752     for (i = 0; i < ntilesw * ntilesh; i++)
753       tjFree(jpegBuf[i]);
754   }
755   free(jpegBuf);
756   free(jpegSize);
757   free(srcBuf);
758   free(t);
759   if (handle) { tjDestroy(handle);  handle = NULL; }
760   return retval;
761 }
762 
763 
usage(char * progName)764 static void usage(char *progName)
765 {
766   int i;
767 
768   printf("USAGE: %s\n", progName);
769   printf("       <Inputimage (BMP|PPM)> <Quality> [options]\n\n");
770   printf("       %s\n", progName);
771   printf("       <Inputimage (JPG)> [options]\n\n");
772   printf("Options:\n\n");
773   printf("-alloc = Dynamically allocate JPEG buffers\n");
774   printf("-bmp = Use Windows Bitmap format for output images [default = PPM]\n");
775   printf("-bottomup = Use bottom-up row order for packed-pixel source/destination buffers\n");
776   printf("-tile = Compress/transform the input image into separate JPEG tiles of varying\n");
777   printf("     sizes (useful for measuring JPEG overhead)\n");
778   printf("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =\n");
779   printf("     Use the specified pixel format for packed-pixel source/destination buffers\n");
780   printf("     [default = BGR]\n");
781   printf("-cmyk = Indirectly test YCCK JPEG compression/decompression\n");
782   printf("     (use the CMYK pixel format for packed-pixel source/destination buffers)\n");
783   printf("-fastupsample = Use the fastest chrominance upsampling algorithm available\n");
784   printf("-fastdct = Use the fastest DCT/IDCT algorithm available\n");
785   printf("-accuratedct = Use the most accurate DCT/IDCT algorithm available\n");
786   printf("-progressive = Use progressive entropy coding in JPEG images generated by\n");
787   printf("     compression and transform operations\n");
788   printf("-subsamp <s> = When compressing, use the specified level of chrominance\n");
789   printf("     subsampling (<s> = 444, 422, 440, 420, 411, or GRAY) [default = test\n");
790   printf("     Grayscale, 4:2:0, 4:2:2, and 4:4:4 in sequence]\n");
791   printf("-quiet = Output results in tabular rather than verbose format\n");
792   printf("-yuv = Compress from/decompress to intermediate planar YUV images\n");
793   printf("-yuvpad <p> = The number of bytes by which each row in each plane of an\n");
794   printf("     intermediate YUV image is evenly divisible (must be a power of 2)\n");
795   printf("     [default = 1]\n");
796   printf("-scale M/N = When decompressing, scale the width/height of the JPEG image by a\n");
797   printf("     factor of M/N (M/N = ");
798   for (i = 0; i < nsf; i++) {
799     printf("%d/%d", scalingFactors[i].num, scalingFactors[i].denom);
800     if (nsf == 2 && i != nsf - 1) printf(" or ");
801     else if (nsf > 2) {
802       if (i != nsf - 1) printf(", ");
803       if (i == nsf - 2) printf("or ");
804     }
805     if (i % 8 == 0 && i != 0) printf("\n     ");
806   }
807   printf(")\n");
808   printf("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =\n");
809   printf("     Perform the specified lossless transform operation on the input image\n");
810   printf("     prior to decompression (these operations are mutually exclusive)\n");
811   printf("-grayscale = Transform the input image into a grayscale JPEG image prior to\n");
812   printf("     decompression (can be combined with the other transform operations above)\n");
813   printf("-copynone = Do not copy any extra markers (including EXIF and ICC profile data)\n");
814   printf("     when transforming the input image\n");
815   printf("-benchtime <t> = Run each benchmark for at least <t> seconds [default = 5.0]\n");
816   printf("-warmup <t> = Run each benchmark for <t> seconds [default = 1.0] prior to\n");
817   printf("     starting the timer, in order to prime the caches and thus improve the\n");
818   printf("     consistency of the benchmark results\n");
819   printf("-componly = Stop after running compression tests.  Do not test decompression.\n");
820   printf("-nowrite = Do not write reference or output images (improves consistency of\n");
821   printf("     benchmark results)\n");
822   printf("-limitscans = Refuse to decompress or transform progressive JPEG images that\n");
823   printf("     have an unreasonably large number of scans\n");
824   printf("-stoponwarning = Immediately discontinue the current\n");
825   printf("     compression/decompression/transform operation if a warning (non-fatal\n");
826   printf("     error) occurs\n\n");
827   printf("NOTE:  If the quality is specified as a range (e.g. 90-100), a separate\n");
828   printf("test will be performed for all quality values in the range.\n\n");
829   exit(1);
830 }
831 
832 #ifndef GTEST
main(int argc,char * argv[])833 int main(int argc, char *argv[])
834 #else
835 int tjbench(int argc, char *argv[])
836 #endif
837 {
838   unsigned char *srcBuf = NULL;
839   int w = 0, h = 0, i, j, minQual = -1, maxQual = -1;
840   char *temp;
841   int minArg = 2, retval = 0, subsamp = -1;
842 
843   if ((scalingFactors = tjGetScalingFactors(&nsf)) == NULL || nsf == 0)
844     THROW("executing tjGetScalingFactors()", tjGetErrorStr());
845 
846   if (argc < minArg) usage(argv[0]);
847 
848   temp = strrchr(argv[1], '.');
849   if (temp != NULL) {
850     if (!strcasecmp(temp, ".bmp")) ext = "bmp";
851     if (!strcasecmp(temp, ".jpg") || !strcasecmp(temp, ".jpeg"))
852       decompOnly = 1;
853   }
854 
855   fprintf(stderr, "\n");
856 
857   if (!decompOnly) {
858     minArg = 3;
859     if (argc < minArg) usage(argv[0]);
860     if ((minQual = atoi(argv[2])) < 1 || minQual > 100) {
861       puts("ERROR: Quality must be between 1 and 100.");
862       exit(1);
863     }
864     if ((temp = strchr(argv[2], '-')) != NULL && strlen(temp) > 1 &&
865         sscanf(&temp[1], "%d", &maxQual) == 1 && maxQual > minQual &&
866         maxQual >= 1 && maxQual <= 100) {}
867     else maxQual = minQual;
868   }
869 
870   if (argc > minArg) {
871     for (i = minArg; i < argc; i++) {
872       if (!strcasecmp(argv[i], "-tile")) {
873         doTile = 1;  xformOpt |= TJXOPT_CROP;
874       } else if (!strcasecmp(argv[i], "-fastupsample")) {
875         fprintf(stderr, "Using fastest upsampling algorithm\n\n");
876         flags |= TJFLAG_FASTUPSAMPLE;
877       } else if (!strcasecmp(argv[i], "-fastdct")) {
878         fprintf(stderr, "Using fastest DCT/IDCT algorithm\n\n");
879         flags |= TJFLAG_FASTDCT;
880       } else if (!strcasecmp(argv[i], "-accuratedct")) {
881         fprintf(stderr, "Using most accurate DCT/IDCT algorithm\n\n");
882         flags |= TJFLAG_ACCURATEDCT;
883       } else if (!strcasecmp(argv[i], "-progressive")) {
884         fprintf(stderr, "Using progressive entropy coding\n\n");
885         flags |= TJFLAG_PROGRESSIVE;
886         xformOpt |= TJXOPT_PROGRESSIVE;
887       } else if (!strcasecmp(argv[i], "-rgb"))
888         pf = TJPF_RGB;
889       else if (!strcasecmp(argv[i], "-rgbx"))
890         pf = TJPF_RGBX;
891       else if (!strcasecmp(argv[i], "-bgr"))
892         pf = TJPF_BGR;
893       else if (!strcasecmp(argv[i], "-bgrx"))
894         pf = TJPF_BGRX;
895       else if (!strcasecmp(argv[i], "-xbgr"))
896         pf = TJPF_XBGR;
897       else if (!strcasecmp(argv[i], "-xrgb"))
898         pf = TJPF_XRGB;
899       else if (!strcasecmp(argv[i], "-cmyk"))
900         pf = TJPF_CMYK;
901       else if (!strcasecmp(argv[i], "-bottomup"))
902         flags |= TJFLAG_BOTTOMUP;
903       else if (!strcasecmp(argv[i], "-quiet"))
904         quiet = 1;
905       else if (!strcasecmp(argv[i], "-qq"))
906         quiet = 2;
907       else if (!strcasecmp(argv[i], "-scale") && i < argc - 1) {
908         int temp1 = 0, temp2 = 0, match = 0;
909 
910         if (sscanf(argv[++i], "%d/%d", &temp1, &temp2) == 2) {
911           for (j = 0; j < nsf; j++) {
912             if ((double)temp1 / (double)temp2 ==
913                 (double)scalingFactors[j].num /
914                 (double)scalingFactors[j].denom) {
915               sf = scalingFactors[j];
916               match = 1;  break;
917             }
918           }
919           if (!match) usage(argv[0]);
920         } else usage(argv[0]);
921       } else if (!strcasecmp(argv[i], "-hflip"))
922         xformOp = TJXOP_HFLIP;
923       else if (!strcasecmp(argv[i], "-vflip"))
924         xformOp = TJXOP_VFLIP;
925       else if (!strcasecmp(argv[i], "-transpose"))
926         xformOp = TJXOP_TRANSPOSE;
927       else if (!strcasecmp(argv[i], "-transverse"))
928         xformOp = TJXOP_TRANSVERSE;
929       else if (!strcasecmp(argv[i], "-rot90"))
930         xformOp = TJXOP_ROT90;
931       else if (!strcasecmp(argv[i], "-rot180"))
932         xformOp = TJXOP_ROT180;
933       else if (!strcasecmp(argv[i], "-rot270"))
934         xformOp = TJXOP_ROT270;
935       else if (!strcasecmp(argv[i], "-grayscale"))
936         xformOpt |= TJXOPT_GRAY;
937       else if (!strcasecmp(argv[i], "-custom"))
938         customFilter = dummyDCTFilter;
939       else if (!strcasecmp(argv[i], "-nooutput"))
940         xformOpt |= TJXOPT_NOOUTPUT;
941       else if (!strcasecmp(argv[i], "-copynone"))
942         xformOpt |= TJXOPT_COPYNONE;
943       else if (!strcasecmp(argv[i], "-benchtime") && i < argc - 1) {
944         double tempd = atof(argv[++i]);
945 
946         if (tempd > 0.0) benchTime = tempd;
947         else usage(argv[0]);
948       } else if (!strcasecmp(argv[i], "-warmup") && i < argc - 1) {
949         double tempd = atof(argv[++i]);
950 
951         if (tempd >= 0.0) warmup = tempd;
952         else usage(argv[0]);
953         fprintf(stderr, "Warmup time = %.1f seconds\n\n", warmup);
954       } else if (!strcasecmp(argv[i], "-alloc"))
955         flags &= (~TJFLAG_NOREALLOC);
956       else if (!strcasecmp(argv[i], "-bmp"))
957         ext = "bmp";
958       else if (!strcasecmp(argv[i], "-yuv")) {
959         fprintf(stderr, "Testing planar YUV encoding/decoding\n\n");
960         doYUV = 1;
961       } else if (!strcasecmp(argv[i], "-yuvpad") && i < argc - 1) {
962         int tempi = atoi(argv[++i]);
963 
964         if (tempi >= 1 && (tempi & (tempi - 1)) == 0) yuvAlign = tempi;
965         else usage(argv[0]);
966       } else if (!strcasecmp(argv[i], "-subsamp") && i < argc - 1) {
967         i++;
968         if (toupper(argv[i][0]) == 'G') subsamp = TJSAMP_GRAY;
969         else {
970           int tempi = atoi(argv[i]);
971 
972           switch (tempi) {
973           case 444:  subsamp = TJSAMP_444;  break;
974           case 422:  subsamp = TJSAMP_422;  break;
975           case 440:  subsamp = TJSAMP_440;  break;
976           case 420:  subsamp = TJSAMP_420;  break;
977           case 411:  subsamp = TJSAMP_411;  break;
978           default:  usage(argv[0]);
979           }
980         }
981       } else if (!strcasecmp(argv[i], "-componly"))
982         compOnly = 1;
983       else if (!strcasecmp(argv[i], "-nowrite"))
984         doWrite = 0;
985       else if (!strcasecmp(argv[i], "-limitscans"))
986         flags |= TJFLAG_LIMITSCANS;
987       else if (!strcasecmp(argv[i], "-stoponwarning"))
988         flags |= TJFLAG_STOPONWARNING;
989       else usage(argv[0]);
990     }
991   }
992 
993   if ((sf.num != 1 || sf.denom != 1) && doTile) {
994     fprintf(stderr, "Disabling tiled compression/decompression tests, because those tests do not\n");
995     fprintf(stderr, "work when scaled decompression is enabled.\n\n");
996     doTile = 0;  xformOpt &= (~TJXOPT_CROP);
997   }
998 
999   if ((flags & TJFLAG_NOREALLOC) == 0 && doTile) {
1000     fprintf(stderr, "Disabling tiled compression/decompression tests, because those tests do not\n");
1001     fprintf(stderr, "work when dynamic JPEG buffer allocation is enabled.\n\n");
1002     doTile = 0;  xformOpt &= (~TJXOPT_CROP);
1003   }
1004 
1005   if (!decompOnly) {
1006     if ((srcBuf = tjLoadImage(argv[1], &w, 1, &h, &pf, flags)) == NULL)
1007       THROW_TJG("loading input image");
1008     temp = strrchr(argv[1], '.');
1009     if (temp != NULL) *temp = '\0';
1010   }
1011 
1012   if (quiet == 1 && !decompOnly) {
1013     fprintf(stderr, "All performance values in Mpixels/sec\n\n");
1014     fprintf(stderr, "Pixel      JPEG     JPEG  %s  %s   ",
1015             doTile ? "Tile " : "Image", doTile ? "Tile " : "Image");
1016     if (doYUV) fprintf(stderr, "Encode  ");
1017     fprintf(stderr, "Comp    Comp    Decomp  ");
1018     if (doYUV) fprintf(stderr, "Decode");
1019     fprintf(stderr, "\n");
1020     fprintf(stderr, "Format     Subsamp  Qual  Width  Height  ");
1021     if (doYUV) fprintf(stderr, "Perf    ");
1022     fprintf(stderr, "Perf    Ratio   Perf    ");
1023     if (doYUV) fprintf(stderr, "Perf");
1024     fprintf(stderr, "\n\n");
1025   }
1026 
1027   if (decompOnly) {
1028     decompTest(argv[1]);
1029     fprintf(stderr, "\n");
1030     goto bailout;
1031   }
1032   if (subsamp >= 0 && subsamp < TJ_NUMSAMP) {
1033     for (i = maxQual; i >= minQual; i--)
1034       fullTest(srcBuf, w, h, subsamp, i, argv[1]);
1035     fprintf(stderr, "\n");
1036   } else {
1037     if (pf != TJPF_CMYK) {
1038       for (i = maxQual; i >= minQual; i--)
1039         fullTest(srcBuf, w, h, TJSAMP_GRAY, i, argv[1]);
1040       fprintf(stderr, "\n");
1041     }
1042     for (i = maxQual; i >= minQual; i--)
1043       fullTest(srcBuf, w, h, TJSAMP_420, i, argv[1]);
1044     fprintf(stderr, "\n");
1045     for (i = maxQual; i >= minQual; i--)
1046       fullTest(srcBuf, w, h, TJSAMP_422, i, argv[1]);
1047     fprintf(stderr, "\n");
1048     for (i = maxQual; i >= minQual; i--)
1049       fullTest(srcBuf, w, h, TJSAMP_444, i, argv[1]);
1050     fprintf(stderr, "\n");
1051   }
1052 
1053 bailout:
1054   tjFree(srcBuf);
1055   return retval;
1056 }
1057