1 /* 2 * Copyright (C)2009-2015 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, warmup=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; 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=-warmup; 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 iter++; 180 if(iter>=1) 181 { 182 elapsed+=gettime()-start; 183 if(elapsed>=benchtime) break; 184 } 185 } 186 if(doyuv) elapsed-=elapsedDecode; 187 188 if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()"); 189 handle=NULL; 190 191 if(quiet) 192 { 193 printf("%-6s%s", 194 sigfig((double)(w*h)/1000000.*(double)iter/elapsed, 4, tempstr, 1024), 195 quiet==2? "\n":" "); 196 if(doyuv) 197 printf("%s\n", 198 sigfig((double)(w*h)/1000000.*(double)iter/elapsedDecode, 4, tempstr, 199 1024)); 200 else if(quiet!=2) printf("\n"); 201 } 202 else 203 { 204 printf("%s --> Frame rate: %f fps\n", 205 doyuv? "Decomp to YUV":"Decompress ", (double)iter/elapsed); 206 printf(" Throughput: %f Megapixels/sec\n", 207 (double)(w*h)/1000000.*(double)iter/elapsed); 208 if(doyuv) 209 { 210 printf("YUV Decode --> Frame rate: %f fps\n", 211 (double)iter/elapsedDecode); 212 printf(" Throughput: %f Megapixels/sec\n", 213 (double)(w*h)/1000000.*(double)iter/elapsedDecode); 214 } 215 } 216 if(sf.num!=1 || sf.denom!=1) 217 snprintf(sizestr, 20, "%d_%d", sf.num, sf.denom); 218 else if(tilew!=w || tileh!=h) 219 snprintf(sizestr, 20, "%dx%d", tilew, tileh); 220 else snprintf(sizestr, 20, "full"); 221 if(decomponly) 222 snprintf(tempstr, 1024, "%s_%s.%s", filename, sizestr, ext); 223 else 224 snprintf(tempstr, 1024, "%s_%s%s_%s.%s", filename, subName[subsamp], 225 qualstr, sizestr, ext); 226 227 if(savebmp(tempstr, dstbuf, scaledw, scaledh, pf, 228 (flags&TJFLAG_BOTTOMUP)!=0)==-1) 229 _throwbmp("saving bitmap"); 230 ptr=strrchr(tempstr, '.'); 231 snprintf(ptr, 1024-(ptr-tempstr), "-err.%s", ext); 232 if(srcbuf && sf.num==1 && sf.denom==1) 233 { 234 if(!quiet) printf("Compression error written to %s.\n", tempstr); 235 if(subsamp==TJ_GRAYSCALE) 236 { 237 int index, index2; 238 for(row=0, index=0; row<h; row++, index+=pitch) 239 { 240 for(col=0, index2=index; col<w; col++, index2+=ps) 241 { 242 int rindex=index2+tjRedOffset[pf]; 243 int gindex=index2+tjGreenOffset[pf]; 244 int bindex=index2+tjBlueOffset[pf]; 245 int y=(int)((double)srcbuf[rindex]*0.299 246 + (double)srcbuf[gindex]*0.587 247 + (double)srcbuf[bindex]*0.114 + 0.5); 248 if(y>255) y=255; if(y<0) y=0; 249 dstbuf[rindex]=abs(dstbuf[rindex]-y); 250 dstbuf[gindex]=abs(dstbuf[gindex]-y); 251 dstbuf[bindex]=abs(dstbuf[bindex]-y); 252 } 253 } 254 } 255 else 256 { 257 for(row=0; row<h; row++) 258 for(col=0; col<w*ps; col++) 259 dstbuf[pitch*row+col] 260 =abs(dstbuf[pitch*row+col]-srcbuf[pitch*row+col]); 261 } 262 if(savebmp(tempstr, dstbuf, w, h, pf, 263 (flags&TJFLAG_BOTTOMUP)!=0)==-1) 264 _throwbmp("saving bitmap"); 265 } 266 267 bailout: 268 if(file) fclose(file); 269 if(handle) tjDestroy(handle); 270 if(dstbuf && dstbufalloc) free(dstbuf); 271 if(yuvbuf) free(yuvbuf); 272 return retval; 273 } 274 275 276 int fullTest(unsigned char *srcbuf, int w, int h, int subsamp, int jpegqual, 277 char *filename) 278 { 279 char tempstr[1024], tempstr2[80]; 280 FILE *file=NULL; tjhandle handle=NULL; 281 unsigned char **jpegbuf=NULL, *yuvbuf=NULL, *tmpbuf=NULL, *srcptr, *srcptr2; 282 double start, elapsed, elapsedEncode; 283 int totaljpegsize=0, row, col, i, tilew=w, tileh=h, retval=0; 284 int iter, yuvsize=0; 285 unsigned long *jpegsize=NULL; 286 int ps=tjPixelSize[pf]; 287 int ntilesw=1, ntilesh=1, pitch=w*ps; 288 const char *pfStr=pixFormatStr[pf]; 289 290 if((tmpbuf=(unsigned char *)malloc(pitch*h)) == NULL) 291 _throwunix("allocating temporary image buffer"); 292 293 if(!quiet) 294 printf(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pfStr, 295 (flags&TJFLAG_BOTTOMUP)? "Bottom-up":"Top-down", subNameLong[subsamp], 296 jpegqual); 297 298 for(tilew=dotile? 8:w, tileh=dotile? 8:h; ; tilew*=2, tileh*=2) 299 { 300 if(tilew>w) tilew=w; if(tileh>h) tileh=h; 301 ntilesw=(w+tilew-1)/tilew; ntilesh=(h+tileh-1)/tileh; 302 303 if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *) 304 *ntilesw*ntilesh))==NULL) 305 _throwunix("allocating JPEG tile array"); 306 memset(jpegbuf, 0, sizeof(unsigned char *)*ntilesw*ntilesh); 307 if((jpegsize=(unsigned long *)malloc(sizeof(unsigned long) 308 *ntilesw*ntilesh))==NULL) 309 _throwunix("allocating JPEG size array"); 310 memset(jpegsize, 0, sizeof(unsigned long)*ntilesw*ntilesh); 311 312 if((flags&TJFLAG_NOREALLOC)!=0) 313 for(i=0; i<ntilesw*ntilesh; i++) 314 { 315 if((jpegbuf[i]=(unsigned char *)tjAlloc(tjBufSize(tilew, tileh, 316 subsamp)))==NULL) 317 _throwunix("allocating JPEG tiles"); 318 } 319 320 /* Compression test */ 321 if(quiet==1) 322 printf("%-4s (%s) %-5s %-3d ", pfStr, 323 (flags&TJFLAG_BOTTOMUP)? "BU":"TD", subNameLong[subsamp], jpegqual); 324 for(i=0; i<h; i++) 325 memcpy(&tmpbuf[pitch*i], &srcbuf[w*ps*i], w*ps); 326 if((handle=tjInitCompress())==NULL) 327 _throwtj("executing tjInitCompress()"); 328 329 if(doyuv) 330 { 331 yuvsize=tjBufSizeYUV2(tilew, yuvpad, tileh, subsamp); 332 if((yuvbuf=(unsigned char *)malloc(yuvsize))==NULL) 333 _throwunix("allocating YUV buffer"); 334 memset(yuvbuf, 127, yuvsize); 335 } 336 337 /* Benchmark */ 338 iter=-warmup; 339 elapsed=elapsedEncode=0.; 340 while(1) 341 { 342 int tile=0; 343 totaljpegsize=0; 344 start=gettime(); 345 for(row=0, srcptr=srcbuf; row<ntilesh; row++, srcptr+=pitch*tileh) 346 { 347 for(col=0, srcptr2=srcptr; col<ntilesw; col++, tile++, 348 srcptr2+=ps*tilew) 349 { 350 int width=min(tilew, w-col*tilew); 351 int height=min(tileh, h-row*tileh); 352 if(doyuv) 353 { 354 double startEncode=gettime(); 355 if(tjEncodeYUV3(handle, srcptr2, width, pitch, height, pf, yuvbuf, 356 yuvpad, subsamp, flags)==-1) 357 _throwtj("executing tjEncodeYUV3()"); 358 if(iter>=0) elapsedEncode+=gettime()-startEncode; 359 if(tjCompressFromYUV(handle, yuvbuf, width, yuvpad, height, 360 subsamp, &jpegbuf[tile], &jpegsize[tile], jpegqual, flags)==-1) 361 _throwtj("executing tjCompressFromYUV()"); 362 } 363 else 364 { 365 if(tjCompress2(handle, srcptr2, width, pitch, height, pf, 366 &jpegbuf[tile], &jpegsize[tile], subsamp, jpegqual, flags)==-1) 367 _throwtj("executing tjCompress2()"); 368 } 369 totaljpegsize+=jpegsize[tile]; 370 } 371 } 372 iter++; 373 if(iter>=1) 374 { 375 elapsed+=gettime()-start; 376 if(elapsed>=benchtime) break; 377 } 378 } 379 if(doyuv) elapsed-=elapsedEncode; 380 381 if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()"); 382 handle=NULL; 383 384 if(quiet==1) printf("%-5d %-5d ", tilew, tileh); 385 if(quiet) 386 { 387 if(doyuv) 388 printf("%-6s%s", 389 sigfig((double)(w*h)/1000000.*(double)iter/elapsedEncode, 4, tempstr, 390 1024), quiet==2? "\n":" "); 391 printf("%-6s%s", 392 sigfig((double)(w*h)/1000000.*(double)iter/elapsed, 4, tempstr, 1024), 393 quiet==2? "\n":" "); 394 printf("%-6s%s", 395 sigfig((double)(w*h*ps)/(double)totaljpegsize, 4, tempstr2, 80), 396 quiet==2? "\n":" "); 397 } 398 else 399 { 400 printf("\n%s size: %d x %d\n", dotile? "Tile":"Image", tilew, 401 tileh); 402 if(doyuv) 403 { 404 printf("Encode YUV --> Frame rate: %f fps\n", 405 (double)iter/elapsedEncode); 406 printf(" Output image size: %d bytes\n", yuvsize); 407 printf(" Compression ratio: %f:1\n", 408 (double)(w*h*ps)/(double)yuvsize); 409 printf(" Throughput: %f Megapixels/sec\n", 410 (double)(w*h)/1000000.*(double)iter/elapsedEncode); 411 printf(" Output bit stream: %f Megabits/sec\n", 412 (double)yuvsize*8./1000000.*(double)iter/elapsedEncode); 413 } 414 printf("%s --> Frame rate: %f fps\n", 415 doyuv? "Comp from YUV":"Compress ", (double)iter/elapsed); 416 printf(" Output image size: %d bytes\n", 417 totaljpegsize); 418 printf(" Compression ratio: %f:1\n", 419 (double)(w*h*ps)/(double)totaljpegsize); 420 printf(" Throughput: %f Megapixels/sec\n", 421 (double)(w*h)/1000000.*(double)iter/elapsed); 422 printf(" Output bit stream: %f Megabits/sec\n", 423 (double)totaljpegsize*8./1000000.*(double)iter/elapsed); 424 } 425 if(tilew==w && tileh==h) 426 { 427 snprintf(tempstr, 1024, "%s_%s_Q%d.jpg", filename, subName[subsamp], 428 jpegqual); 429 if((file=fopen(tempstr, "wb"))==NULL) 430 _throwunix("opening reference image"); 431 if(fwrite(jpegbuf[0], jpegsize[0], 1, file)!=1) 432 _throwunix("writing reference image"); 433 fclose(file); file=NULL; 434 if(!quiet) printf("Reference image written to %s\n", tempstr); 435 } 436 437 /* Decompression test */ 438 if(!componly) 439 { 440 if(decomp(srcbuf, jpegbuf, jpegsize, tmpbuf, w, h, subsamp, jpegqual, 441 filename, tilew, tileh)==-1) 442 goto bailout; 443 } 444 445 for(i=0; i<ntilesw*ntilesh; i++) 446 { 447 if(jpegbuf[i]) tjFree(jpegbuf[i]); jpegbuf[i]=NULL; 448 } 449 free(jpegbuf); jpegbuf=NULL; 450 free(jpegsize); jpegsize=NULL; 451 if(doyuv) 452 { 453 free(yuvbuf); yuvbuf=NULL; 454 } 455 456 if(tilew==w && tileh==h) break; 457 } 458 459 bailout: 460 if(file) {fclose(file); file=NULL;} 461 if(jpegbuf) 462 { 463 for(i=0; i<ntilesw*ntilesh; i++) 464 { 465 if(jpegbuf[i]) tjFree(jpegbuf[i]); jpegbuf[i]=NULL; 466 } 467 free(jpegbuf); jpegbuf=NULL; 468 } 469 if(yuvbuf) {free(yuvbuf); yuvbuf=NULL;} 470 if(jpegsize) {free(jpegsize); jpegsize=NULL;} 471 if(tmpbuf) {free(tmpbuf); tmpbuf=NULL;} 472 if(handle) {tjDestroy(handle); handle=NULL;} 473 return retval; 474 } 475 476 477 int decompTest(char *filename) 478 { 479 FILE *file=NULL; tjhandle handle=NULL; 480 unsigned char **jpegbuf=NULL, *srcbuf=NULL; 481 unsigned long *jpegsize=NULL, srcsize, totaljpegsize; 482 tjtransform *t=NULL; 483 int w=0, h=0, subsamp=-1, cs=-1, _w, _h, _tilew, _tileh, 484 _ntilesw, _ntilesh, _subsamp; 485 char *temp=NULL, tempstr[80], tempstr2[80]; 486 int row, col, i, iter, tilew, tileh, ntilesw=1, ntilesh=1, retval=0; 487 double start, elapsed; 488 int ps=tjPixelSize[pf], tile; 489 490 if((file=fopen(filename, "rb"))==NULL) 491 _throwunix("opening file"); 492 if(fseek(file, 0, SEEK_END)<0 || (srcsize=ftell(file))==(unsigned long)-1) 493 _throwunix("determining file size"); 494 if((srcbuf=(unsigned char *)malloc(srcsize))==NULL) 495 _throwunix("allocating memory"); 496 if(fseek(file, 0, SEEK_SET)<0) 497 _throwunix("setting file position"); 498 if(fread(srcbuf, srcsize, 1, file)<1) 499 _throwunix("reading JPEG data"); 500 fclose(file); file=NULL; 501 502 temp=strrchr(filename, '.'); 503 if(temp!=NULL) *temp='\0'; 504 505 if((handle=tjInitTransform())==NULL) 506 _throwtj("executing tjInitTransform()"); 507 if(tjDecompressHeader3(handle, srcbuf, srcsize, &w, &h, &subsamp, &cs)==-1) 508 _throwtj("executing tjDecompressHeader3()"); 509 if(cs==TJCS_YCCK || cs==TJCS_CMYK) 510 { 511 pf=TJPF_CMYK; ps=tjPixelSize[pf]; 512 } 513 514 if(quiet==1) 515 { 516 printf("All performance values in Mpixels/sec\n\n"); 517 printf("Bitmap JPEG JPEG %s %s Xform Comp Decomp ", 518 dotile? "Tile ":"Image", dotile? "Tile ":"Image"); 519 if(doyuv) printf("Decode"); 520 printf("\n"); 521 printf("Format CS Subsamp Width Height Perf Ratio Perf "); 522 if(doyuv) printf("Perf"); 523 printf("\n\n"); 524 } 525 else if(!quiet) 526 printf(">>>>> JPEG %s --> %s (%s) <<<<<\n", 527 formatName(subsamp, cs, tempstr), pixFormatStr[pf], 528 (flags&TJFLAG_BOTTOMUP)? "Bottom-up":"Top-down"); 529 530 for(tilew=dotile? 16:w, tileh=dotile? 16:h; ; tilew*=2, tileh*=2) 531 { 532 if(tilew>w) tilew=w; if(tileh>h) tileh=h; 533 ntilesw=(w+tilew-1)/tilew; ntilesh=(h+tileh-1)/tileh; 534 535 if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *) 536 *ntilesw*ntilesh))==NULL) 537 _throwunix("allocating JPEG tile array"); 538 memset(jpegbuf, 0, sizeof(unsigned char *)*ntilesw*ntilesh); 539 if((jpegsize=(unsigned long *)malloc(sizeof(unsigned long) 540 *ntilesw*ntilesh))==NULL) 541 _throwunix("allocating JPEG size array"); 542 memset(jpegsize, 0, sizeof(unsigned long)*ntilesw*ntilesh); 543 544 if((flags&TJFLAG_NOREALLOC)!=0 || !dotile) 545 for(i=0; i<ntilesw*ntilesh; i++) 546 { 547 if((jpegbuf[i]=(unsigned char *)tjAlloc(tjBufSize(tilew, tileh, 548 subsamp)))==NULL) 549 _throwunix("allocating JPEG tiles"); 550 } 551 552 _w=w; _h=h; _tilew=tilew; _tileh=tileh; 553 if(!quiet) 554 { 555 printf("\n%s size: %d x %d", dotile? "Tile":"Image", _tilew, 556 _tileh); 557 if(sf.num!=1 || sf.denom!=1) 558 printf(" --> %d x %d", TJSCALED(_w, sf), TJSCALED(_h, sf)); 559 printf("\n"); 560 } 561 else if(quiet==1) 562 { 563 printf("%-4s (%s) %-5s %-5s ", pixFormatStr[pf], 564 (flags&TJFLAG_BOTTOMUP)? "BU":"TD", csName[cs], subNameLong[subsamp]); 565 printf("%-5d %-5d ", tilew, tileh); 566 } 567 568 _subsamp=subsamp; 569 if(dotile || xformop!=TJXOP_NONE || xformopt!=0 || customFilter) 570 { 571 if((t=(tjtransform *)malloc(sizeof(tjtransform)*ntilesw*ntilesh)) 572 ==NULL) 573 _throwunix("allocating image transform array"); 574 575 if(xformop==TJXOP_TRANSPOSE || xformop==TJXOP_TRANSVERSE 576 || xformop==TJXOP_ROT90 || xformop==TJXOP_ROT270) 577 { 578 _w=h; _h=w; _tilew=tileh; _tileh=tilew; 579 } 580 581 if(xformopt&TJXOPT_GRAY) _subsamp=TJ_GRAYSCALE; 582 if(xformop==TJXOP_HFLIP || xformop==TJXOP_ROT180) 583 _w=_w-(_w%tjMCUWidth[_subsamp]); 584 if(xformop==TJXOP_VFLIP || xformop==TJXOP_ROT180) 585 _h=_h-(_h%tjMCUHeight[_subsamp]); 586 if(xformop==TJXOP_TRANSVERSE || xformop==TJXOP_ROT90) 587 _w=_w-(_w%tjMCUHeight[_subsamp]); 588 if(xformop==TJXOP_TRANSVERSE || xformop==TJXOP_ROT270) 589 _h=_h-(_h%tjMCUWidth[_subsamp]); 590 _ntilesw=(_w+_tilew-1)/_tilew; 591 _ntilesh=(_h+_tileh-1)/_tileh; 592 593 if(xformop==TJXOP_TRANSPOSE || xformop==TJXOP_TRANSVERSE 594 || xformop==TJXOP_ROT90 || xformop==TJXOP_ROT270) 595 { 596 if(_subsamp==TJSAMP_422) _subsamp=TJSAMP_440; 597 else if(_subsamp==TJSAMP_440) _subsamp=TJSAMP_422; 598 } 599 600 for(row=0, tile=0; row<_ntilesh; row++) 601 { 602 for(col=0; col<_ntilesw; col++, tile++) 603 { 604 t[tile].r.w=min(_tilew, _w-col*_tilew); 605 t[tile].r.h=min(_tileh, _h-row*_tileh); 606 t[tile].r.x=col*_tilew; 607 t[tile].r.y=row*_tileh; 608 t[tile].op=xformop; 609 t[tile].options=xformopt|TJXOPT_TRIM; 610 t[tile].customFilter=customFilter; 611 if(t[tile].options&TJXOPT_NOOUTPUT && jpegbuf[tile]) 612 { 613 tjFree(jpegbuf[tile]); jpegbuf[tile]=NULL; 614 } 615 } 616 } 617 618 iter=-warmup; 619 elapsed=0.; 620 while(1) 621 { 622 start=gettime(); 623 if(tjTransform(handle, srcbuf, srcsize, _ntilesw*_ntilesh, jpegbuf, 624 jpegsize, t, flags)==-1) 625 _throwtj("executing tjTransform()"); 626 iter++; 627 if(iter>=1) 628 { 629 elapsed+=gettime()-start; 630 if(elapsed>=benchtime) break; 631 } 632 } 633 634 free(t); t=NULL; 635 636 for(tile=0, totaljpegsize=0; tile<_ntilesw*_ntilesh; tile++) 637 totaljpegsize+=jpegsize[tile]; 638 639 if(quiet) 640 { 641 printf("%-6s%s%-6s%s", 642 sigfig((double)(w*h)/1000000./elapsed, 4, tempstr, 80), 643 quiet==2? "\n":" ", 644 sigfig((double)(w*h*ps)/(double)totaljpegsize, 4, tempstr2, 80), 645 quiet==2? "\n":" "); 646 } 647 else if(!quiet) 648 { 649 printf("Transform --> Frame rate: %f fps\n", 1.0/elapsed); 650 printf(" Output image size: %lu bytes\n", totaljpegsize); 651 printf(" Compression ratio: %f:1\n", 652 (double)(w*h*ps)/(double)totaljpegsize); 653 printf(" Throughput: %f Megapixels/sec\n", 654 (double)(w*h)/1000000./elapsed); 655 printf(" Output bit stream: %f Megabits/sec\n", 656 (double)totaljpegsize*8./1000000./elapsed); 657 } 658 } 659 else 660 { 661 if(quiet==1) printf("N/A N/A "); 662 jpegsize[0]=srcsize; 663 memcpy(jpegbuf[0], srcbuf, srcsize); 664 } 665 666 if(w==tilew) _tilew=_w; 667 if(h==tileh) _tileh=_h; 668 if(!(xformopt&TJXOPT_NOOUTPUT)) 669 { 670 if(decomp(NULL, jpegbuf, jpegsize, NULL, _w, _h, _subsamp, 0, 671 filename, _tilew, _tileh)==-1) 672 goto bailout; 673 } 674 else if(quiet==1) printf("N/A\n"); 675 676 for(i=0; i<ntilesw*ntilesh; i++) 677 { 678 tjFree(jpegbuf[i]); jpegbuf[i]=NULL; 679 } 680 free(jpegbuf); jpegbuf=NULL; 681 if(jpegsize) {free(jpegsize); jpegsize=NULL;} 682 683 if(tilew==w && tileh==h) break; 684 } 685 686 bailout: 687 if(file) {fclose(file); file=NULL;} 688 if(jpegbuf) 689 { 690 for(i=0; i<ntilesw*ntilesh; i++) 691 { 692 if(jpegbuf[i]) tjFree(jpegbuf[i]); jpegbuf[i]=NULL; 693 } 694 free(jpegbuf); jpegbuf=NULL; 695 } 696 if(jpegsize) {free(jpegsize); jpegsize=NULL;} 697 if(srcbuf) {free(srcbuf); srcbuf=NULL;} 698 if(t) {free(t); t=NULL;} 699 if(handle) {tjDestroy(handle); handle=NULL;} 700 return retval; 701 } 702 703 704 void usage(char *progname) 705 { 706 int i; 707 printf("USAGE: %s\n", progname); 708 printf(" <Inputfile (BMP|PPM)> <Quality> [options]\n\n"); 709 printf(" %s\n", progname); 710 printf(" <Inputfile (JPG)> [options]\n\n"); 711 printf("Options:\n\n"); 712 printf("-alloc = Dynamically allocate JPEG image buffers\n"); 713 printf("-bmp = Generate output images in Windows Bitmap format (default = PPM)\n"); 714 printf("-bottomup = Test bottom-up compression/decompression\n"); 715 printf("-tile = Test performance of the codec when the image is encoded as separate\n"); 716 printf(" tiles of varying sizes.\n"); 717 printf("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =\n"); 718 printf(" Test the specified color conversion path in the codec (default = BGR)\n"); 719 printf("-cmyk = Indirectly test YCCK JPEG compression/decompression (the source\n"); 720 printf(" and destination bitmaps are still RGB. The conversion is done\n"); 721 printf(" internally prior to compression or after decompression.)\n"); 722 printf("-fastupsample = Use the fastest chrominance upsampling algorithm available in\n"); 723 printf(" the underlying codec\n"); 724 printf("-fastdct = Use the fastest DCT/IDCT algorithms available in the underlying\n"); 725 printf(" codec\n"); 726 printf("-accuratedct = Use the most accurate DCT/IDCT algorithms available in the\n"); 727 printf(" underlying codec\n"); 728 printf("-subsamp <s> = When testing JPEG compression, this option specifies the level\n"); 729 printf(" of chrominance subsampling to use (<s> = 444, 422, 440, 420, 411, or\n"); 730 printf(" GRAY). The default is to test Grayscale, 4:2:0, 4:2:2, and 4:4:4 in\n"); 731 printf(" sequence.\n"); 732 printf("-quiet = Output results in tabular rather than verbose format\n"); 733 printf("-yuv = Test YUV encoding/decoding functions\n"); 734 printf("-yuvpad <p> = If testing YUV encoding/decoding, this specifies the number of\n"); 735 printf(" bytes to which each row of each plane in the intermediate YUV image is\n"); 736 printf(" padded (default = 1)\n"); 737 printf("-scale M/N = Scale down the width/height of the decompressed JPEG image by a\n"); 738 printf(" factor of M/N (M/N = "); 739 for(i=0; i<nsf; i++) 740 { 741 printf("%d/%d", scalingfactors[i].num, scalingfactors[i].denom); 742 if(nsf==2 && i!=nsf-1) printf(" or "); 743 else if(nsf>2) 744 { 745 if(i!=nsf-1) printf(", "); 746 if(i==nsf-2) printf("or "); 747 } 748 if(i%8==0 && i!=0) printf("\n "); 749 } 750 printf(")\n"); 751 printf("-hflip, -vflip, -transpose, -transverse, -rot90, -rot180, -rot270 =\n"); 752 printf(" Perform the corresponding lossless transform prior to\n"); 753 printf(" decompression (these options are mutually exclusive)\n"); 754 printf("-grayscale = Perform lossless grayscale conversion prior to decompression\n"); 755 printf(" test (can be combined with the other transforms above)\n"); 756 printf("-benchtime <t> = Run each benchmark for at least <t> seconds (default = 5.0)\n"); 757 printf("-warmup <w> = Execute each benchmark <w> times to prime the cache before\n"); 758 printf(" taking performance measurements (default = 1)\n"); 759 printf("-componly = Stop after running compression tests. Do not test decompression.\n\n"); 760 printf("NOTE: If the quality is specified as a range (e.g. 90-100), a separate\n"); 761 printf("test will be performed for all quality values in the range.\n\n"); 762 exit(1); 763 } 764 765 766 int main(int argc, char *argv[]) 767 { 768 unsigned char *srcbuf=NULL; int w=0, h=0, i, j; 769 int minqual=-1, maxqual=-1; char *temp; 770 int minarg=2, retval=0, subsamp=-1; 771 772 if((scalingfactors=tjGetScalingFactors(&nsf))==NULL || nsf==0) 773 _throwtj("executing tjGetScalingFactors()"); 774 775 if(argc<minarg) usage(argv[0]); 776 777 temp=strrchr(argv[1], '.'); 778 if(temp!=NULL) 779 { 780 if(!strcasecmp(temp, ".bmp")) ext="bmp"; 781 if(!strcasecmp(temp, ".jpg") || !strcasecmp(temp, ".jpeg")) decomponly=1; 782 } 783 784 printf("\n"); 785 786 if(!decomponly) 787 { 788 minarg=3; 789 if(argc<minarg) usage(argv[0]); 790 if((minqual=atoi(argv[2]))<1 || minqual>100) 791 { 792 puts("ERROR: Quality must be between 1 and 100."); 793 exit(1); 794 } 795 if((temp=strchr(argv[2], '-'))!=NULL && strlen(temp)>1 796 && sscanf(&temp[1], "%d", &maxqual)==1 && maxqual>minqual && maxqual>=1 797 && maxqual<=100) {} 798 else maxqual=minqual; 799 } 800 801 if(argc>minarg) 802 { 803 for(i=minarg; i<argc; i++) 804 { 805 if(!strcasecmp(argv[i], "-tile")) 806 { 807 dotile=1; xformopt|=TJXOPT_CROP; 808 } 809 if(!strcasecmp(argv[i], "-fastupsample")) 810 { 811 printf("Using fast upsampling code\n\n"); 812 flags|=TJFLAG_FASTUPSAMPLE; 813 } 814 if(!strcasecmp(argv[i], "-fastdct")) 815 { 816 printf("Using fastest DCT/IDCT algorithm\n\n"); 817 flags|=TJFLAG_FASTDCT; 818 } 819 if(!strcasecmp(argv[i], "-accuratedct")) 820 { 821 printf("Using most accurate DCT/IDCT algorithm\n\n"); 822 flags|=TJFLAG_ACCURATEDCT; 823 } 824 if(!strcasecmp(argv[i], "-rgb")) pf=TJPF_RGB; 825 if(!strcasecmp(argv[i], "-rgbx")) pf=TJPF_RGBX; 826 if(!strcasecmp(argv[i], "-bgr")) pf=TJPF_BGR; 827 if(!strcasecmp(argv[i], "-bgrx")) pf=TJPF_BGRX; 828 if(!strcasecmp(argv[i], "-xbgr")) pf=TJPF_XBGR; 829 if(!strcasecmp(argv[i], "-xrgb")) pf=TJPF_XRGB; 830 if(!strcasecmp(argv[i], "-cmyk")) pf=TJPF_CMYK; 831 if(!strcasecmp(argv[i], "-bottomup")) flags|=TJFLAG_BOTTOMUP; 832 if(!strcasecmp(argv[i], "-quiet")) quiet=1; 833 if(!strcasecmp(argv[i], "-qq")) quiet=2; 834 if(!strcasecmp(argv[i], "-scale") && i<argc-1) 835 { 836 int temp1=0, temp2=0, match=0; 837 if(sscanf(argv[++i], "%d/%d", &temp1, &temp2)==2) 838 { 839 for(j=0; j<nsf; j++) 840 { 841 if((double)temp1/(double)temp2 842 == (double)scalingfactors[j].num/(double)scalingfactors[j].denom) 843 { 844 sf=scalingfactors[j]; 845 match=1; break; 846 } 847 } 848 if(!match) usage(argv[0]); 849 } 850 else usage(argv[0]); 851 } 852 if(!strcasecmp(argv[i], "-hflip")) xformop=TJXOP_HFLIP; 853 if(!strcasecmp(argv[i], "-vflip")) xformop=TJXOP_VFLIP; 854 if(!strcasecmp(argv[i], "-transpose")) xformop=TJXOP_TRANSPOSE; 855 if(!strcasecmp(argv[i], "-transverse")) xformop=TJXOP_TRANSVERSE; 856 if(!strcasecmp(argv[i], "-rot90")) xformop=TJXOP_ROT90; 857 if(!strcasecmp(argv[i], "-rot180")) xformop=TJXOP_ROT180; 858 if(!strcasecmp(argv[i], "-rot270")) xformop=TJXOP_ROT270; 859 if(!strcasecmp(argv[i], "-grayscale")) xformopt|=TJXOPT_GRAY; 860 if(!strcasecmp(argv[i], "-custom")) customFilter=dummyDCTFilter; 861 if(!strcasecmp(argv[i], "-nooutput")) xformopt|=TJXOPT_NOOUTPUT; 862 if(!strcasecmp(argv[i], "-benchtime") && i<argc-1) 863 { 864 double temp=atof(argv[++i]); 865 if(temp>0.0) benchtime=temp; 866 else usage(argv[0]); 867 } 868 if(!strcasecmp(argv[i], "-warmup") && i<argc-1) 869 { 870 int temp=atoi(argv[++i]); 871 if(temp>=0) 872 { 873 warmup=temp; 874 printf("Warmup runs = %d\n\n", warmup); 875 } 876 else usage(argv[0]); 877 } 878 if(!strcmp(argv[i], "-?")) usage(argv[0]); 879 if(!strcasecmp(argv[i], "-alloc")) flags&=(~TJFLAG_NOREALLOC); 880 if(!strcasecmp(argv[i], "-bmp")) ext="bmp"; 881 if(!strcasecmp(argv[i], "-yuv")) 882 { 883 printf("Testing YUV planar encoding/decoding\n\n"); 884 doyuv=1; 885 } 886 if(!strcasecmp(argv[i], "-yuvpad") && i<argc-1) 887 { 888 int temp=atoi(argv[++i]); 889 if(temp>=1) yuvpad=temp; 890 } 891 if(!strcasecmp(argv[i], "-subsamp") && i<argc-1) 892 { 893 i++; 894 if(toupper(argv[i][0])=='G') subsamp=TJSAMP_GRAY; 895 else 896 { 897 int temp=atoi(argv[i]); 898 switch(temp) 899 { 900 case 444: subsamp=TJSAMP_444; break; 901 case 422: subsamp=TJSAMP_422; break; 902 case 440: subsamp=TJSAMP_440; break; 903 case 420: subsamp=TJSAMP_420; break; 904 case 411: subsamp=TJSAMP_411; break; 905 } 906 } 907 } 908 if(!strcasecmp(argv[i], "-componly")) componly=1; 909 } 910 } 911 912 if((sf.num!=1 || sf.denom!=1) && dotile) 913 { 914 printf("Disabling tiled compression/decompression tests, because those tests do not\n"); 915 printf("work when scaled decompression is enabled.\n"); 916 dotile=0; 917 } 918 919 if((flags&TJFLAG_NOREALLOC)==0 && dotile) 920 { 921 printf("Disabling tiled compression/decompression tests, because those tests do not\n"); 922 printf("work when dynamic JPEG buffer allocation is enabled.\n\n"); 923 dotile=0; 924 } 925 926 if(!decomponly) 927 { 928 if(loadbmp(argv[1], &srcbuf, &w, &h, pf, (flags&TJFLAG_BOTTOMUP)!=0)==-1) 929 _throwbmp("loading bitmap"); 930 temp=strrchr(argv[1], '.'); 931 if(temp!=NULL) *temp='\0'; 932 } 933 934 if(quiet==1 && !decomponly) 935 { 936 printf("All performance values in Mpixels/sec\n\n"); 937 printf("Bitmap JPEG JPEG %s %s ", 938 dotile? "Tile ":"Image", dotile? "Tile ":"Image"); 939 if(doyuv) printf("Encode "); 940 printf("Comp Comp Decomp "); 941 if(doyuv) printf("Decode"); 942 printf("\n"); 943 printf("Format Subsamp Qual Width Height "); 944 if(doyuv) printf("Perf "); 945 printf("Perf Ratio Perf "); 946 if(doyuv) printf("Perf"); 947 printf("\n\n"); 948 } 949 950 if(decomponly) 951 { 952 decompTest(argv[1]); 953 printf("\n"); 954 goto bailout; 955 } 956 if(subsamp>=0 && subsamp<TJ_NUMSAMP) 957 { 958 for(i=maxqual; i>=minqual; i--) 959 fullTest(srcbuf, w, h, subsamp, i, argv[1]); 960 printf("\n"); 961 } 962 else 963 { 964 if(pf!=TJPF_CMYK) 965 { 966 for(i=maxqual; i>=minqual; i--) 967 fullTest(srcbuf, w, h, TJSAMP_GRAY, i, argv[1]); 968 printf("\n"); 969 } 970 for(i=maxqual; i>=minqual; i--) 971 fullTest(srcbuf, w, h, TJSAMP_420, i, argv[1]); 972 printf("\n"); 973 for(i=maxqual; i>=minqual; i--) 974 fullTest(srcbuf, w, h, TJSAMP_422, i, argv[1]); 975 printf("\n"); 976 for(i=maxqual; i>=minqual; i--) 977 fullTest(srcbuf, w, h, TJSAMP_444, i, argv[1]); 978 printf("\n"); 979 } 980 981 bailout: 982 if(srcbuf) free(srcbuf); 983 return retval; 984 } 985