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