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