2 * Copyright (C)2009-2014, 2017-2019, 2022-2023 D. R. Commander.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * - Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * - Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * - Neither the name of the libjpeg-turbo Project nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS",
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
31 * This program tests the various code paths in the TurboJPEG C Wrapper
35 #define _CRT_SECURE_NO_DEPRECATE
44 #include "turbojpeg.h"
46 #include "jconfigint.h"
50 #define random() rand()
51 #define getpid() _getpid()
57 static void usage(char *progName)
59 printf("\nUSAGE: %s [options]\n\n", progName);
61 printf("-yuv = test YUV encoding/compression/decompression/decoding\n");
62 printf(" (8-bit data precision only)\n");
63 printf("-noyuvpad = do not pad each row in each Y, U, and V plane to the nearest\n");
64 printf(" multiple of 4 bytes\n");
65 printf("-precision N = test N-bit data precision (N is 8, 12, or 16; default is 8; if N\n");
66 printf(" is 16, then -lossless is implied)\n");
67 printf("-lossless = test lossless JPEG compression/decompression\n");
68 printf("-alloc = test automatic JPEG buffer allocation\n");
69 printf("-bmp = test packed-pixel image I/O\n");
74 #define THROW_TJ(handle) { \
75 printf("TurboJPEG ERROR:\n%s\n", tj3GetErrorStr(handle)); \
78 #define TRY_TJ(handle, f) { if ((f) == -1) THROW_TJ(handle); }
79 #define THROW(m) { printf("ERROR: %s\n", m); BAILOUT() }
80 #define THROW_MD5(filename, md5sum, ref) { \
81 printf("\n%s has an MD5 sum of %s.\n Should be %s.\n", filename, md5sum, \
86 const char *subNameLong[TJ_NUMSAMP] = {
87 "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0", "4:1:1", "4:4:1"
89 const char *subName[TJ_NUMSAMP] = {
90 "444", "422", "420", "GRAY", "440", "411", "441"
93 const char *pixFormatStr[TJ_NUMPF] = {
94 "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale",
95 "RGBA", "BGRA", "ABGR", "ARGB", "CMYK"
98 const int _3sampleFormats[] = { TJPF_RGB, TJPF_BGR };
99 const int _4sampleFormats[] = {
100 TJPF_RGBX, TJPF_BGRX, TJPF_XBGR, TJPF_XRGB, TJPF_CMYK
102 const int _onlyGray[] = { TJPF_GRAY };
103 const int _onlyRGB[] = { TJPF_RGB };
105 int doYUV = 0, lossless = 0, psv = 1, alloc = 0, yuvAlign = 4;
106 int precision = 8, sampleSize, maxSample, tolerance, redToY, yellowToY;
109 #define BAILOUT() { exitStatus = -1; goto bailout; }
112 static void setVal(void *buf, int index, int value)
115 ((unsigned char *)buf)[index] = (unsigned char)value;
116 else if (precision == 12)
117 ((short *)buf)[index] = (short)value;
119 ((unsigned short *)buf)[index] = (unsigned short)value;
122 static void initBuf(void *buf, int w, int h, int pf, int bottomUp)
124 int roffset = tjRedOffset[pf];
125 int goffset = tjGreenOffset[pf];
126 int boffset = tjBlueOffset[pf];
127 int ps = tjPixelSize[pf];
128 int i, index, row, col, halfway = 16;
130 if (pf == TJPF_GRAY) {
131 memset(buf, 0, w * h * ps * sampleSize);
132 for (row = 0; row < h; row++) {
133 for (col = 0; col < w; col++) {
134 if (bottomUp) index = (h - row - 1) * w + col;
135 else index = row * w + col;
136 if (((row / 8) + (col / 8)) % 2 == 0)
137 setVal(buf, index, (row < halfway) ? maxSample : 0);
138 else setVal(buf, index, (row < halfway) ? redToY : yellowToY);
141 } else if (pf == TJPF_CMYK) {
142 for (i = 0; i < w * h * ps; i++)
143 setVal(buf, i, maxSample);
144 for (row = 0; row < h; row++) {
145 for (col = 0; col < w; col++) {
146 if (bottomUp) index = (h - row - 1) * w + col;
147 else index = row * w + col;
148 if (((row / 8) + (col / 8)) % 2 == 0) {
149 if (row >= halfway) setVal(buf, index * ps + 3, 0);
151 setVal(buf, index * ps + 2, 0);
152 if (row < halfway) setVal(buf, index * ps + 1, 0);
157 memset(buf, 0, w * h * ps * sampleSize);
158 for (row = 0; row < h; row++) {
159 for (col = 0; col < w; col++) {
160 if (bottomUp) index = (h - row - 1) * w + col;
161 else index = row * w + col;
162 if (((row / 8) + (col / 8)) % 2 == 0) {
164 setVal(buf, index * ps + roffset, maxSample);
165 setVal(buf, index * ps + goffset, maxSample);
166 setVal(buf, index * ps + boffset, maxSample);
169 setVal(buf, index * ps + roffset, maxSample);
170 if (row >= halfway) setVal(buf, index * ps + goffset, maxSample);
178 #define CHECKVAL(v, cv) { \
179 if (v < cv - tolerance || v > cv + tolerance) { \
180 printf("\nComp. %s at %d,%d should be %d, not %d\n", #v, row, col, cv, \
182 retval = 0; exitStatus = -1; goto bailout; \
186 #define CHECKVAL0(v) { \
187 if (v > tolerance) { \
188 printf("\nComp. %s at %d,%d should be 0, not %d\n", #v, row, col, v); \
189 retval = 0; exitStatus = -1; goto bailout; \
193 #define CHECKVALMAX(v) { \
194 if (v < maxSample - tolerance) { \
195 printf("\nComp. %s at %d,%d should be %d, not %d\n", #v, row, col, \
197 retval = 0; exitStatus = -1; goto bailout; \
202 static int getVal(void *buf, int index)
205 return ((unsigned char *)buf)[index];
206 else if (precision == 12)
207 return ((short *)buf)[index];
209 return ((unsigned short *)buf)[index];
212 static int checkBuf(void *buf, int w, int h, int pf, int subsamp,
213 tjscalingfactor sf, int bottomUp)
215 int roffset = tjRedOffset[pf];
216 int goffset = tjGreenOffset[pf];
217 int boffset = tjBlueOffset[pf];
218 int aoffset = tjAlphaOffset[pf];
219 int ps = tjPixelSize[pf];
220 int index, row, col, retval = 1;
221 int halfway = 16 * sf.num / sf.denom;
222 int blocksize = 8 * sf.num / sf.denom;
224 if (pf == TJPF_GRAY) roffset = goffset = boffset = 0;
226 if (pf == TJPF_CMYK) {
227 for (row = 0; row < h; row++) {
228 for (col = 0; col < w; col++) {
231 if (bottomUp) index = (h - row - 1) * w + col;
232 else index = row * w + col;
233 c = getVal(buf, index * ps);
234 m = getVal(buf, index * ps + 1);
235 y = getVal(buf, index * ps + 2);
236 k = getVal(buf, index * ps + 3);
237 if (((row / blocksize) + (col / blocksize)) % 2 == 0) {
238 CHECKVALMAX(c); CHECKVALMAX(m); CHECKVALMAX(y);
239 if (row < halfway) CHECKVALMAX(k)
242 CHECKVALMAX(c); CHECKVAL0(y); CHECKVALMAX(k);
243 if (row < halfway) CHECKVAL0(m)
251 for (row = 0; row < h; row++) {
252 for (col = 0; col < w; col++) {
255 if (bottomUp) index = (h - row - 1) * w + col;
256 else index = row * w + col;
257 r = getVal(buf, index * ps + roffset);
258 g = getVal(buf, index * ps + goffset);
259 b = getVal(buf, index * ps + boffset);
260 a = aoffset >= 0 ? getVal(buf, index * ps + aoffset) : maxSample;
261 if (((row / blocksize) + (col / blocksize)) % 2 == 0) {
263 CHECKVALMAX(r); CHECKVALMAX(g); CHECKVALMAX(b);
265 CHECKVAL0(r); CHECKVAL0(g); CHECKVAL0(b);
268 if (subsamp == TJSAMP_GRAY) {
270 CHECKVAL(r, redToY); CHECKVAL(g, redToY); CHECKVAL(b, redToY);
272 CHECKVAL(r, yellowToY); CHECKVAL(g, yellowToY);
273 CHECKVAL(b, yellowToY);
277 CHECKVALMAX(r); CHECKVAL0(g); CHECKVAL0(b);
279 CHECKVALMAX(r); CHECKVALMAX(g); CHECKVAL0(b);
289 for (row = 0; row < h; row++) {
290 for (col = 0; col < w; col++) {
292 printf("%.3d/%.3d/%.3d/%.3d ", getVal(buf, (row * w + col) * ps),
293 getVal(buf, (row * w + col) * ps + 1),
294 getVal(buf, (row * w + col) * ps + 2),
295 getVal(buf, (row * w + col) * ps + 3));
297 printf("%.3d/%.3d/%.3d ",
298 getVal(buf, (row * w + col) * ps + roffset),
299 getVal(buf, (row * w + col) * ps + goffset),
300 getVal(buf, (row * w + col) * ps + boffset));
309 #define PAD(v, p) ((v + (p) - 1) & (~((p) - 1)))
311 static int checkBufYUV(unsigned char *buf, int w, int h, int subsamp,
315 int hsf = tjMCUWidth[subsamp] / 8, vsf = tjMCUHeight[subsamp] / 8;
316 int pw = PAD(w, hsf), ph = PAD(h, vsf);
317 int cw = pw / hsf, ch = ph / vsf;
318 int ypitch = PAD(pw, yuvAlign), uvpitch = PAD(cw, yuvAlign);
320 int halfway = 16 * sf.num / sf.denom;
321 int blocksize = 8 * sf.num / sf.denom;
323 for (row = 0; row < ph; row++) {
324 for (col = 0; col < pw; col++) {
325 unsigned char y = buf[ypitch * row + col];
327 if (((row / blocksize) + (col / blocksize)) % 2 == 0) {
328 if (row < halfway) CHECKVALMAX(y)
331 if (row < halfway) CHECKVAL(y, 76)
332 else CHECKVAL(y, 225);
336 if (subsamp != TJSAMP_GRAY) {
337 halfway = 16 / vsf * sf.num / sf.denom;
339 for (row = 0; row < ch; row++) {
340 for (col = 0; col < cw; col++) {
341 unsigned char u = buf[ypitch * ph + (uvpitch * row + col)],
342 v = buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)];
344 if (((row * vsf / blocksize) + (col * hsf / blocksize)) % 2 == 0) {
345 CHECKVAL(u, 128); CHECKVAL(v, 128);
348 CHECKVAL(u, 85); CHECKVALMAX(v);
350 CHECKVAL0(u); CHECKVAL(v, 149);
359 for (row = 0; row < ph; row++) {
360 for (col = 0; col < pw; col++)
361 printf("%.3d ", buf[ypitch * row + col]);
365 for (row = 0; row < ch; row++) {
366 for (col = 0; col < cw; col++)
367 printf("%.3d ", buf[ypitch * ph + (uvpitch * row + col)]);
371 for (row = 0; row < ch; row++) {
372 for (col = 0; col < cw; col++)
374 buf[ypitch * ph + uvpitch * ch + (uvpitch * row + col)]);
383 static void writeJPEG(unsigned char *jpegBuf, size_t jpegSize, char *filename)
385 FILE *file = fopen(filename, "wb");
387 if (!file || fwrite(jpegBuf, jpegSize, 1, file) != 1) {
390 strerror_r(errno, err_str, 256);
391 printf("ERROR: Could not write to %s.\n%s\n", filename, err_str);
393 printf("ERROR: Could not write to %s.\n%s\n", filename, strerror(errno));
399 if (file) fclose(file);
403 static void compTest(tjhandle handle, unsigned char **dstBuf, size_t *dstSize,
404 int w, int h, int pf, char *basename)
408 unsigned char *yuvBuf = NULL;
409 const char *pfStr = pixFormatStr[pf];
410 int bottomUp = tj3Get(handle, TJPARAM_BOTTOMUP);
411 int subsamp = tj3Get(handle, TJPARAM_SUBSAMP);
412 int jpegPSV = tj3Get(handle, TJPARAM_LOSSLESSPSV);
413 int jpegQual = tj3Get(handle, TJPARAM_QUALITY);
414 const char *buStrLong = bottomUp ? "Bottom-Up" : "Top-Down ";
415 const char *buStr = bottomUp ? "BU" : "TD";
417 if ((srcBuf = malloc(w * h * tjPixelSize[pf] * sampleSize)) == NULL)
418 THROW("Memory allocation failure");
419 initBuf(srcBuf, w, h, pf, bottomUp);
421 if (*dstBuf && *dstSize > 0) memset(*dstBuf, 0, *dstSize);
424 size_t yuvSize = tj3YUVBufSize(w, yuvAlign, h, subsamp);
425 tjscalingfactor sf = { 1, 1 };
426 tjhandle handle2 = NULL;
428 if ((handle2 = tj3Init(TJINIT_COMPRESS)) == NULL)
430 TRY_TJ(handle2, tj3Set(handle2, TJPARAM_BOTTOMUP, bottomUp));
431 TRY_TJ(handle2, tj3Set(handle2, TJPARAM_SUBSAMP, subsamp));
433 if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
434 THROW("Memory allocation failure");
435 memset(yuvBuf, 0, yuvSize);
437 printf("%s %s -> YUV %s ... ", pfStr, buStrLong, subNameLong[subsamp]);
438 TRY_TJ(handle2, tj3EncodeYUV8(handle2, (unsigned char *)srcBuf, w, 0, h,
439 pf, yuvBuf, yuvAlign));
441 if (checkBufYUV(yuvBuf, w, h, subsamp, sf)) printf("Passed.\n");
442 else printf("FAILED!\n");
444 printf("YUV %s %s -> JPEG Q%d ... ", subNameLong[subsamp], buStrLong,
446 TRY_TJ(handle, tj3CompressFromYUV8(handle, yuvBuf, w, yuvAlign, h, dstBuf,
450 printf("%s %s -> LOSSLESS PSV%d ... ", pfStr, buStrLong, jpegPSV);
452 printf("%s %s -> %s Q%d ... ", pfStr, buStrLong, subNameLong[subsamp],
454 if (precision == 8) {
455 TRY_TJ(handle, tj3Compress8(handle, (unsigned char *)srcBuf, w, 0, h, pf,
457 } else if (precision == 12) {
458 TRY_TJ(handle, tj3Compress12(handle, (short *)srcBuf, w, 0, h, pf,
461 TRY_TJ(handle, tj3Compress16(handle, (unsigned short *)srcBuf, w, 0, h,
462 pf, dstBuf, dstSize));
467 SNPRINTF(tempStr, 1024, "%s_enc%d_%s_%s_LOSSLESS_PSV%d.jpg", basename,
468 precision, pfStr, buStr, jpegPSV);
470 SNPRINTF(tempStr, 1024, "%s_enc%d_%s_%s_%s_Q%d.jpg", basename, precision,
471 pfStr, buStr, subName[subsamp], jpegQual);
472 writeJPEG(*dstBuf, *dstSize, tempStr);
473 printf("Done.\n Result in %s\n", tempStr);
481 static void _decompTest(tjhandle handle, unsigned char *jpegBuf,
482 size_t jpegSize, int w, int h, int pf, char *basename,
483 int subsamp, tjscalingfactor sf)
486 unsigned char *yuvBuf = NULL;
487 int _hdrw = 0, _hdrh = 0, _hdrsubsamp;
488 int scaledWidth = TJSCALED(w, sf);
489 int scaledHeight = TJSCALED(h, sf);
491 int bottomUp = tj3Get(handle, TJPARAM_BOTTOMUP);
493 TRY_TJ(handle, tj3SetScalingFactor(handle, sf));
495 TRY_TJ(handle, tj3DecompressHeader(handle, jpegBuf, jpegSize));
496 _hdrw = tj3Get(handle, TJPARAM_JPEGWIDTH);
497 _hdrh = tj3Get(handle, TJPARAM_JPEGHEIGHT);
498 _hdrsubsamp = tj3Get(handle, TJPARAM_SUBSAMP);
499 if (lossless && subsamp != TJSAMP_444 && subsamp != TJSAMP_GRAY)
500 subsamp = TJSAMP_444;
501 if (_hdrw != w || _hdrh != h || _hdrsubsamp != subsamp)
502 THROW("Incorrect JPEG header");
504 dstSize = scaledWidth * scaledHeight * tjPixelSize[pf];
505 if ((dstBuf = malloc(dstSize * sampleSize)) == NULL)
506 THROW("Memory allocation failure");
507 memset(dstBuf, 0, dstSize * sampleSize);
510 size_t yuvSize = tj3YUVBufSize(scaledWidth, yuvAlign, scaledHeight,
512 tjhandle handle2 = NULL;
514 if ((handle2 = tj3Init(TJINIT_DECOMPRESS)) == NULL)
516 TRY_TJ(handle2, tj3Set(handle2, TJPARAM_BOTTOMUP, bottomUp));
517 TRY_TJ(handle2, tj3Set(handle2, TJPARAM_SUBSAMP, subsamp));
519 if ((yuvBuf = (unsigned char *)malloc(yuvSize)) == NULL)
520 THROW("Memory allocation failure");
521 memset(yuvBuf, 0, yuvSize);
523 printf("JPEG -> YUV %s ", subNameLong[subsamp]);
524 if (sf.num != 1 || sf.denom != 1)
525 printf("%d/%d ... ", sf.num, sf.denom);
527 TRY_TJ(handle, tj3DecompressToYUV8(handle, jpegBuf, jpegSize, yuvBuf,
529 if (checkBufYUV(yuvBuf, scaledWidth, scaledHeight, subsamp, sf))
531 else printf("FAILED!\n");
533 printf("YUV %s -> %s %s ... ", subNameLong[subsamp], pixFormatStr[pf],
534 bottomUp ? "Bottom-Up" : "Top-Down ");
535 TRY_TJ(handle2, tj3DecodeYUV8(handle2, yuvBuf, yuvAlign,
536 (unsigned char *)dstBuf, scaledWidth, 0,
540 printf("JPEG -> %s %s ", pixFormatStr[pf],
541 bottomUp ? "Bottom-Up" : "Top-Down ");
542 if (sf.num != 1 || sf.denom != 1)
543 printf("%d/%d ... ", sf.num, sf.denom);
545 if (precision == 8) {
546 TRY_TJ(handle, tj3Decompress8(handle, jpegBuf, jpegSize,
547 (unsigned char *)dstBuf, 0, pf));
548 } else if (precision == 12) {
549 TRY_TJ(handle, tj3Decompress12(handle, jpegBuf, jpegSize,
550 (short *)dstBuf, 0, pf));
552 TRY_TJ(handle, tj3Decompress16(handle, jpegBuf, jpegSize,
553 (unsigned short *)dstBuf, 0, pf));
557 if (checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, bottomUp))
559 else printf("FAILED!");
568 static void decompTest(tjhandle handle, unsigned char *jpegBuf,
569 size_t jpegSize, int w, int h, int pf, char *basename,
573 tjscalingfactor *sf = NULL;
576 _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp,
581 sf = tj3GetScalingFactors(&n);
582 if (!sf || !n) THROW_TJ(NULL);
584 for (i = 0; i < n; i++) {
585 if (subsamp == TJSAMP_444 || subsamp == TJSAMP_GRAY ||
586 ((subsamp == TJSAMP_411 || subsamp == TJSAMP_441) && sf[i].num == 1 &&
587 (sf[i].denom == 2 || sf[i].denom == 1)) ||
588 (subsamp != TJSAMP_411 && subsamp != TJSAMP_441 && sf[i].num == 1 &&
589 (sf[i].denom == 4 || sf[i].denom == 2 || sf[i].denom == 1)))
590 _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp,
599 static void doTest(int w, int h, const int *formats, int nformats, int subsamp,
602 tjhandle chandle = NULL, dhandle = NULL;
603 unsigned char *dstBuf = NULL;
607 if (lossless && subsamp != TJSAMP_GRAY)
608 subsamp = TJSAMP_444;
611 size = tj3JPEGBufSize(w, h, subsamp);
613 if ((dstBuf = (unsigned char *)tj3Alloc(size)) == NULL)
614 THROW("Memory allocation failure.");
616 if ((chandle = tj3Init(TJINIT_COMPRESS)) == NULL ||
617 (dhandle = tj3Init(TJINIT_DECOMPRESS)) == NULL)
620 TRY_TJ(chandle, tj3Set(chandle, TJPARAM_NOREALLOC, !alloc));
622 TRY_TJ(chandle, tj3Set(chandle, TJPARAM_LOSSLESS, lossless));
623 TRY_TJ(chandle, tj3Set(chandle, TJPARAM_LOSSLESSPSV,
624 ((psv++ - 1) % 7) + 1));
626 TRY_TJ(chandle, tj3Set(chandle, TJPARAM_QUALITY, 100));
627 if (subsamp == TJSAMP_422 || subsamp == TJSAMP_420 ||
628 subsamp == TJSAMP_440 || subsamp == TJSAMP_411 ||
629 subsamp == TJSAMP_441)
630 TRY_TJ(dhandle, tj3Set(dhandle, TJPARAM_FASTUPSAMPLE, 1));
632 TRY_TJ(chandle, tj3Set(chandle, TJPARAM_SUBSAMP, subsamp));
634 for (pfi = 0; pfi < nformats; pfi++) {
635 for (i = 0; i < 2; i++) {
636 TRY_TJ(chandle, tj3Set(chandle, TJPARAM_BOTTOMUP, i == 1));
637 TRY_TJ(dhandle, tj3Set(dhandle, TJPARAM_BOTTOMUP, i == 1));
639 compTest(chandle, &dstBuf, &size, w, h, pf, basename);
640 decompTest(dhandle, dstBuf, size, w, h, pf, basename, subsamp);
641 if (pf >= TJPF_RGBX && pf <= TJPF_XRGB) {
643 decompTest(dhandle, dstBuf, size, w, h, pf + (TJPF_RGBA - TJPF_RGBX),
649 printf("--------------------\n\n");
658 #if SIZEOF_SIZE_T == 8
659 #define CHECKSIZE(function) { \
660 if (size && size < (size_t)0xFFFFFFFF) \
661 THROW(#function " overflow"); \
663 #define CHECKSIZEUL(function) { \
664 if ((unsigned long long)ulsize < (unsigned long long)0xFFFFFFFF) \
665 THROW(#function " overflow"); \
668 #define CHECKSIZE(function) { \
669 if (size != 0 || !strcmp(tj3GetErrorStr(NULL), "No error")) \
670 THROW(#function " overflow"); \
672 #define CHECKSIZEUL(function) { \
673 if (ulsize != (unsigned long)(-1) || \
674 !strcmp(tj3GetErrorStr(NULL), "No error")) \
675 THROW(#function " overflow"); \
678 #define CHECKSIZEINT(function) { \
679 if (intsize != 0 || !strcmp(tj3GetErrorStr(NULL), "No error")) \
680 THROW(#function " overflow"); \
683 static void overflowTest(void)
685 /* Ensure that the various buffer size functions don't overflow */
687 unsigned long ulsize;
690 size = tj3JPEGBufSize(26755, 26755, TJSAMP_444);
691 CHECKSIZE(tj3JPEGBufSize());
692 ulsize = tjBufSize(26755, 26755, TJSAMP_444);
693 CHECKSIZEUL(tjBufSize());
694 ulsize = TJBUFSIZE(26755, 26755);
695 CHECKSIZEUL(TJBUFSIZE());
696 size = tj3YUVBufSize(37838, 1, 37838, TJSAMP_444);
697 CHECKSIZE(tj3YUVBufSize());
698 size = tj3YUVBufSize(37837, 3, 37837, TJSAMP_444);
699 CHECKSIZE(tj3YUVBufSize());
700 size = tj3YUVBufSize(37837, -1, 37837, TJSAMP_444);
701 CHECKSIZE(tj3YUVBufSize());
702 ulsize = tjBufSizeYUV2(37838, 1, 37838, TJSAMP_444);
703 CHECKSIZEUL(tjBufSizeYUV2());
704 ulsize = tjBufSizeYUV2(37837, 3, 37837, TJSAMP_444);
705 CHECKSIZEUL(tjBufSizeYUV2());
706 ulsize = tjBufSizeYUV2(37837, -1, 37837, TJSAMP_444);
707 CHECKSIZEUL(tjBufSizeYUV2());
708 ulsize = TJBUFSIZEYUV(37838, 37838, TJSAMP_444);
709 CHECKSIZEUL(TJBUFSIZEYUV());
710 ulsize = tjBufSizeYUV(37838, 37838, TJSAMP_444);
711 CHECKSIZEUL(tjBufSizeYUV());
712 size = tj3YUVPlaneSize(0, 65536, 0, 65536, TJSAMP_444);
713 CHECKSIZE(tj3YUVPlaneSize());
714 ulsize = tjPlaneSizeYUV(0, 65536, 0, 65536, TJSAMP_444);
715 CHECKSIZEUL(tjPlaneSizeYUV());
716 intsize = tj3YUVPlaneWidth(0, INT_MAX, TJSAMP_420);
717 CHECKSIZEINT(tj3YUVPlaneWidth());
718 intsize = tj3YUVPlaneHeight(0, INT_MAX, TJSAMP_420);
719 CHECKSIZEINT(tj3YUVPlaneHeight());
726 static void bufSizeTest(void)
728 int w, h, i, subsamp;
730 unsigned char *dstBuf = NULL;
731 tjhandle handle = NULL;
733 int numSamp = TJ_NUMSAMP;
735 if ((handle = tj3Init(TJINIT_COMPRESS)) == NULL)
738 TRY_TJ(handle, tj3Set(handle, TJPARAM_NOREALLOC, !alloc));
740 TRY_TJ(handle, tj3Set(handle, TJPARAM_LOSSLESS, lossless));
741 TRY_TJ(handle, tj3Set(handle, TJPARAM_LOSSLESSPSV,
742 ((psv++ - 1) % 7) + 1));
745 TRY_TJ(handle, tj3Set(handle, TJPARAM_QUALITY, 100));
747 printf("Buffer size regression test\n");
748 for (subsamp = 0; subsamp < numSamp; subsamp++) {
749 TRY_TJ(handle, tj3Set(handle, TJPARAM_SUBSAMP, subsamp));
750 for (w = 1; w < 48; w++) {
751 int maxh = (w == 1) ? 2048 : 48;
753 for (h = 1; h < maxh; h++) {
754 if (h % 100 == 0) printf("%.4d x %.4d\b\b\b\b\b\b\b\b\b\b\b", w, h);
755 if ((srcBuf = malloc(w * h * 4 * sampleSize)) == NULL)
756 THROW("Memory allocation failure");
757 if (!alloc || doYUV) {
758 if (doYUV) dstSize = tj3YUVBufSize(w, yuvAlign, h, subsamp);
759 else dstSize = tj3JPEGBufSize(w, h, subsamp);
760 if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL)
761 THROW("Memory allocation failure");
764 for (i = 0; i < w * h * 4; i++) {
765 if (random() < RAND_MAX / 2) setVal(srcBuf, i, 0);
766 else setVal(srcBuf, i, maxSample);
770 TRY_TJ(handle, tj3EncodeYUV8(handle, (unsigned char *)srcBuf, w, 0,
771 h, TJPF_BGRX, dstBuf, yuvAlign));
773 if (precision == 8) {
774 TRY_TJ(handle, tj3Compress8(handle, (unsigned char *)srcBuf, w, 0,
775 h, TJPF_BGRX, &dstBuf, &dstSize));
776 } else if (precision == 12) {
777 TRY_TJ(handle, tj3Compress12(handle, (short *)srcBuf, w, 0, h,
778 TJPF_BGRX, &dstBuf, &dstSize));
780 TRY_TJ(handle, tj3Compress16(handle, (unsigned short *)srcBuf, w,
781 0, h, TJPF_BGRX, &dstBuf, &dstSize));
784 free(srcBuf); srcBuf = NULL;
785 if (!alloc || doYUV) {
786 tj3Free(dstBuf); dstBuf = NULL;
789 if ((srcBuf = malloc(h * w * 4 * sampleSize)) == NULL)
790 THROW("Memory allocation failure");
791 if (!alloc || doYUV) {
792 if (doYUV) dstSize = tj3YUVBufSize(h, yuvAlign, w, subsamp);
793 else dstSize = tj3JPEGBufSize(h, w, subsamp);
794 if ((dstBuf = (unsigned char *)tj3Alloc(dstSize)) == NULL)
795 THROW("Memory allocation failure");
798 for (i = 0; i < h * w * 4; i++) {
799 if (random() < RAND_MAX / 2) setVal(srcBuf, i, 0);
800 else setVal(srcBuf, i, maxSample);
804 TRY_TJ(handle, tj3EncodeYUV8(handle, (unsigned char *)srcBuf, h, 0,
805 w, TJPF_BGRX, dstBuf, yuvAlign));
807 if (precision == 8) {
808 TRY_TJ(handle, tj3Compress8(handle, (unsigned char *)srcBuf, h, 0,
809 w, TJPF_BGRX, &dstBuf, &dstSize));
810 } else if (precision == 12) {
811 TRY_TJ(handle, tj3Compress12(handle, (short *)srcBuf, h, 0, w,
812 TJPF_BGRX, &dstBuf, &dstSize));
814 TRY_TJ(handle, tj3Compress16(handle, (unsigned short *)srcBuf, h,
815 0, w, TJPF_BGRX, &dstBuf, &dstSize));
818 free(srcBuf); srcBuf = NULL;
819 if (!alloc || doYUV) {
820 tj3Free(dstBuf); dstBuf = NULL;
834 static void rgb_to_cmyk(int r, int g, int b, int *c, int *m, int *y, int *k)
836 double ctmp = 1.0 - ((double)r / (double)maxSample);
837 double mtmp = 1.0 - ((double)g / (double)maxSample);
838 double ytmp = 1.0 - ((double)b / (double)maxSample);
839 double ktmp = min(min(ctmp, mtmp), ytmp);
841 if (ktmp == 1.0) ctmp = mtmp = ytmp = 0.0;
843 ctmp = (ctmp - ktmp) / (1.0 - ktmp);
844 mtmp = (mtmp - ktmp) / (1.0 - ktmp);
845 ytmp = (ytmp - ktmp) / (1.0 - ktmp);
847 *c = (int)((double)maxSample - ctmp * (double)maxSample + 0.5);
848 *m = (int)((double)maxSample - mtmp * (double)maxSample + 0.5);
849 *y = (int)((double)maxSample - ytmp * (double)maxSample + 0.5);
850 *k = (int)((double)maxSample - ktmp * (double)maxSample + 0.5);
853 static void initBitmap(void *buf, int width, int pitch, int height, int pf,
856 int roffset = tjRedOffset[pf];
857 int goffset = tjGreenOffset[pf];
858 int boffset = tjBlueOffset[pf];
859 int ps = tjPixelSize[pf];
862 for (j = 0; j < height; j++) {
863 int row = bottomUp ? height - j - 1 : j;
865 for (i = 0; i < width; i++) {
866 int r = (i * (maxSample + 1) / width) % (maxSample + 1);
867 int g = (j * (maxSample + 1) / height) % (maxSample + 1);
868 int b = (j * (maxSample + 1) / height +
869 i * (maxSample + 1) / width) % (maxSample + 1);
871 for (ci = 0; ci < ps; ci++)
872 setVal(buf, row * pitch + i * ps + ci, 0);
873 if (pf == TJPF_GRAY) setVal(buf, row * pitch + i * ps, b);
874 else if (pf == TJPF_CMYK) {
877 rgb_to_cmyk(r, g, b, &c, &m, &y, &k);
878 setVal(buf, row * pitch + i * ps + 0, c);
879 setVal(buf, row * pitch + i * ps + 1, m);
880 setVal(buf, row * pitch + i * ps + 2, y);
881 setVal(buf, row * pitch + i * ps + 3, k);
883 setVal(buf, row * pitch + i * ps + roffset, r);
884 setVal(buf, row * pitch + i * ps + goffset, g);
885 setVal(buf, row * pitch + i * ps + boffset, b);
892 static void cmyk_to_rgb(int c, int m, int y, int k, int *r, int *g, int *b)
894 *r = (int)((double)c * (double)k / (double)maxSample + 0.5);
895 *g = (int)((double)m * (double)k / (double)maxSample + 0.5);
896 *b = (int)((double)y * (double)k / (double)maxSample + 0.5);
899 static int cmpBitmap(void *buf, int width, int pitch, int height, int pf,
900 int bottomUp, int gray2rgb)
902 int roffset = tjRedOffset[pf];
903 int goffset = tjGreenOffset[pf];
904 int boffset = tjBlueOffset[pf];
905 int aoffset = tjAlphaOffset[pf];
906 int ps = tjPixelSize[pf];
909 for (j = 0; j < height; j++) {
910 int row = bottomUp ? height - j - 1 : j;
912 for (i = 0; i < width; i++) {
913 int r = (i * (maxSample + 1) / width) % (maxSample + 1);
914 int g = (j * (maxSample + 1) / height) % (maxSample + 1);
915 int b = (j * (maxSample + 1) / height +
916 i * (maxSample + 1) / width) % (maxSample + 1);
918 if (pf == TJPF_GRAY) {
919 if (getVal(buf, row * pitch + i * ps) != b)
921 } else if (pf == TJPF_CMYK) {
924 cmyk_to_rgb(getVal(buf, row * pitch + i * ps + 0),
925 getVal(buf, row * pitch + i * ps + 1),
926 getVal(buf, row * pitch + i * ps + 2),
927 getVal(buf, row * pitch + i * ps + 3), &rf, &gf, &bf);
929 if (rf != b || gf != b || bf != b)
931 } else if (rf != r || gf != g || bf != b) return 0;
934 if (getVal(buf, row * pitch + i * ps + roffset) != b ||
935 getVal(buf, row * pitch + i * ps + goffset) != b ||
936 getVal(buf, row * pitch + i * ps + boffset) != b)
938 } else if (getVal(buf, row * pitch + i * ps + roffset) != r ||
939 getVal(buf, row * pitch + i * ps + goffset) != g ||
940 getVal(buf, row * pitch + i * ps + boffset) != b)
943 getVal(buf, row * pitch + i * ps + aoffset) != maxSample)
952 static int doBmpTest(const char *ext, int width, int align, int height, int pf,
955 tjhandle handle = NULL;
956 char filename[80], *md5sum, md5buf[65];
957 int ps = tjPixelSize[pf], pitch = PAD(width * ps, align), loadWidth = 0,
958 loadHeight = 0, retval = 0, pixelFormat = pf;
962 if ((handle = tj3Init(TJINIT_TRANSFORM)) == NULL)
964 TRY_TJ(handle, tj3Set(handle, TJPARAM_BOTTOMUP, bottomUp));
966 if (pf == TJPF_GRAY) {
968 md5ref = !strcasecmp(ext, "ppm") ? "112c682e82ce5de1cca089e20d60000b" :
969 "51976530acf75f02beddf5d21149101d";
970 else if (precision == 12)
971 md5ref = "0d1895c7e6f2b2c9af6e821a655c239c";
973 md5ref = "64f3320b226ea37fb58080713b4df1b2";
976 md5ref = !strcasecmp(ext, "ppm") ? "c0c9f772b464d1896326883a5c79c545" :
977 "6d659071b9bfcdee2def22cb58ddadca";
978 else if (precision == 12)
979 md5ref = "2ff5299287017502832c99718450c90a";
981 md5ref = "623f54661b928d170bd2324bc3620565";
984 if ((buf = tj3Alloc(pitch * height * sampleSize)) == NULL)
985 THROW("Could not allocate memory");
986 initBitmap(buf, width, pitch, height, pf, bottomUp);
988 SNPRINTF(filename, 80, "test_bmp%d_%s_%d_%s_%d.%s", precision, pixFormatStr[pf],
989 align, bottomUp ? "bu" : "td", getpid(), ext);
990 if (precision == 8) {
991 TRY_TJ(handle, tj3SaveImage8(handle, filename, (unsigned char *)buf, width,
993 } else if (precision == 12) {
994 TRY_TJ(handle, tj3SaveImage12(handle, filename, (short *)buf, width, pitch,
997 TRY_TJ(handle, tj3SaveImage16(handle, filename, (unsigned short *)buf,
998 width, pitch, height, pf));
1000 md5sum = MD5File(filename, md5buf);
1002 printf("\n Could not determine MD5 sum of %s\n", filename);
1003 retval = -1; goto bailout;
1005 if (strcasecmp(md5sum, md5ref))
1006 THROW_MD5(filename, md5sum, md5ref);
1008 tj3Free(buf); buf = NULL;
1009 if (precision == 8) {
1010 if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align, &loadHeight,
1013 } else if (precision == 12) {
1014 if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, &loadHeight,
1018 if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align, &loadHeight,
1022 if (width != loadWidth || height != loadHeight) {
1023 printf("\n Image dimensions of %s are bogus\n", filename);
1024 retval = -1; goto bailout;
1026 if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 0)) {
1027 printf("\n Pixel data in %s is bogus\n", filename);
1028 retval = -1; goto bailout;
1030 if (pf == TJPF_GRAY) {
1031 tj3Free(buf); buf = NULL;
1033 if (precision == 8) {
1034 if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align,
1035 &loadHeight, &pf)) == NULL)
1037 } else if (precision == 12) {
1038 if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align,
1039 &loadHeight, &pf)) == NULL)
1042 if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align,
1043 &loadHeight, &pf)) == NULL)
1046 pitch = PAD(width * tjPixelSize[pf], align);
1047 if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1)) {
1048 printf("\n Converting %s to RGB failed\n", filename);
1049 retval = -1; goto bailout;
1052 tj3Free(buf); buf = NULL;
1054 if (precision == 8) {
1055 if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align,
1056 &loadHeight, &pf)) == NULL)
1058 } else if (precision == 12) {
1059 if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align,
1060 &loadHeight, &pf)) == NULL)
1063 if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align,
1064 &loadHeight, &pf)) == NULL)
1067 pitch = PAD(width * tjPixelSize[pf], align);
1068 if (!cmpBitmap(buf, width, pitch, height, pf, bottomUp, 1)) {
1069 printf("\n Converting %s to CMYK failed\n", filename);
1070 retval = -1; goto bailout;
1073 /* Verify that tj3LoadImage*() returns the proper "preferred" pixel format
1074 for the file type. */
1075 tj3Free(buf); buf = NULL;
1077 pixelFormat = TJPF_UNKNOWN;
1078 if (precision == 8) {
1079 if ((buf = tj3LoadImage8(handle, filename, &loadWidth, align, &loadHeight,
1080 &pixelFormat)) == NULL)
1082 } else if (precision == 12) {
1083 if ((buf = tj3LoadImage12(handle, filename, &loadWidth, align, &loadHeight,
1084 &pixelFormat)) == NULL)
1087 if ((buf = tj3LoadImage16(handle, filename, &loadWidth, align, &loadHeight,
1088 &pixelFormat)) == NULL)
1091 if ((pf == TJPF_GRAY && pixelFormat != TJPF_GRAY) ||
1092 (pf != TJPF_GRAY && !strcasecmp(ext, "bmp") &&
1093 pixelFormat != TJPF_BGR) ||
1094 (pf != TJPF_GRAY && !strcasecmp(ext, "ppm") &&
1095 pixelFormat != TJPF_RGB)) {
1096 printf("\n tj3LoadImage8() returned unexpected pixel format: %s\n",
1097 pixFormatStr[pixelFormat]);
1105 if (exitStatus < 0) return exitStatus;
1110 static int bmpTest(void)
1112 int align, width = 35, height = 39, format;
1114 for (align = 1; align <= 8; align *= 2) {
1115 for (format = 0; format < TJ_NUMPF; format++) {
1116 if (precision == 8) {
1117 printf("%s Top-Down BMP (row alignment = %d samples) ... ",
1118 pixFormatStr[format], align);
1119 if (doBmpTest("bmp", width, align, height, format, 0) == -1)
1124 printf("%s Top-Down PPM (row alignment = %d samples) ... ",
1125 pixFormatStr[format], align);
1126 if (doBmpTest("ppm", width, align, height, format, 1) == -1)
1130 if (precision == 8) {
1131 printf("%s Bottom-Up BMP (row alignment = %d samples) ... ",
1132 pixFormatStr[format], align);
1133 if (doBmpTest("bmp", width, align, height, format, 0) == -1)
1138 printf("%s Bottom-Up PPM (row alignment = %d samples) ... ",
1139 pixFormatStr[format], align);
1140 if (doBmpTest("ppm", width, align, height, format, 1) == -1)
1150 int main(int argc, char *argv[])
1152 int i, bmp = 0, num4bf = 5;
1155 srand((unsigned int)time(NULL));
1158 for (i = 1; i < argc; i++) {
1159 if (!strcasecmp(argv[i], "-yuv")) doYUV = 1;
1160 else if (!strcasecmp(argv[i], "-noyuvpad")) yuvAlign = 1;
1161 else if (!strcasecmp(argv[i], "-lossless")) lossless = 1;
1162 else if (!strcasecmp(argv[i], "-alloc")) alloc = 1;
1163 else if (!strcasecmp(argv[i], "-bmp")) bmp = 1;
1164 else if (!strcasecmp(argv[i], "-precision") && i < argc - 1) {
1165 int tempi = atoi(argv[++i]);
1167 if (tempi != 8 && tempi != 12 && tempi != 16)
1170 if (precision == 16) lossless = 1;
1175 if (lossless && doYUV)
1176 THROW("Lossless JPEG and YUV encoding/decoding are incompatible.");
1177 if (precision != 8 && doYUV)
1178 THROW("YUV encoding/decoding requires 8-bit data precision.");
1180 printf("Testing %d-bit precision\n", precision);
1181 sampleSize = (precision == 8 ? sizeof(unsigned char) : sizeof(short));
1182 maxSample = (1 << precision) - 1;
1183 tolerance = (lossless ? 0 : (precision > 8 ? 2 : 1));
1184 redToY = (19595U * maxSample) >> 16;
1185 yellowToY = (58065U * maxSample) >> 16;
1187 if (bmp) return bmpTest();
1188 if (alloc) printf("Testing automatic buffer allocation\n");
1189 if (doYUV) num4bf = 4;
1191 doTest(35, 39, _3sampleFormats, 2, TJSAMP_444, "test");
1192 doTest(39, 41, _4sampleFormats, num4bf, TJSAMP_444, "test");
1193 doTest(41, 35, _3sampleFormats, 2, TJSAMP_422, "test");
1195 doTest(35, 39, _4sampleFormats, num4bf, TJSAMP_422, "test");
1196 doTest(39, 41, _3sampleFormats, 2, TJSAMP_420, "test");
1197 doTest(41, 35, _4sampleFormats, num4bf, TJSAMP_420, "test");
1198 doTest(35, 39, _3sampleFormats, 2, TJSAMP_440, "test");
1199 doTest(39, 41, _4sampleFormats, num4bf, TJSAMP_440, "test");
1200 doTest(41, 35, _3sampleFormats, 2, TJSAMP_411, "test");
1201 doTest(35, 39, _4sampleFormats, num4bf, TJSAMP_411, "test");
1202 doTest(39, 41, _3sampleFormats, 2, TJSAMP_441, "test");
1203 doTest(41, 35, _4sampleFormats, num4bf, TJSAMP_441, "test");
1205 doTest(39, 41, _onlyGray, 1, TJSAMP_GRAY, "test");
1207 doTest(41, 35, _3sampleFormats, 2, TJSAMP_GRAY, "test");
1208 doTest(35, 39, _4sampleFormats, 4, TJSAMP_GRAY, "test");
1212 printf("\n--------------------\n\n");
1213 doTest(48, 48, _onlyRGB, 1, TJSAMP_444, "test_yuv0");
1214 doTest(48, 48, _onlyRGB, 1, TJSAMP_422, "test_yuv0");
1215 doTest(48, 48, _onlyRGB, 1, TJSAMP_420, "test_yuv0");
1216 doTest(48, 48, _onlyRGB, 1, TJSAMP_440, "test_yuv0");
1217 doTest(48, 48, _onlyRGB, 1, TJSAMP_411, "test_yuv0");
1218 doTest(48, 48, _onlyRGB, 1, TJSAMP_441, "test_yuv0");
1219 doTest(48, 48, _onlyRGB, 1, TJSAMP_GRAY, "test_yuv0");
1220 doTest(48, 48, _onlyGray, 1, TJSAMP_GRAY, "test_yuv0");