示例#1
0
/* Decompression test */
int decomptest(unsigned char *srcbuf, unsigned char **jpegbuf,
	unsigned long *jpegsize, unsigned char *dstbuf, int w, int h,
	int subsamp, int jpegqual, char *filename, int tilew, int tileh)
{
	char tempstr[1024], sizestr[20]="\0", qualstr[6]="\0", *ptr;
	FILE *file=NULL;  tjhandle handle=NULL;
	int row, col, i, dstbufalloc=0, retval=0;
	double start, elapsed;
	int ps=tjPixelSize[pf];
	int yuvsize=tjBufSizeYUV(w, h, subsamp), bufsize;
	int scaledw=(yuv==YUVDECODE)? w : TJSCALED(w, sf);
	int scaledh=(yuv==YUVDECODE)? h : TJSCALED(h, sf);
	int pitch=scaledw*ps;
	int ntilesw=(w+tilew-1)/tilew, ntilesh=(h+tileh-1)/tileh;
	unsigned char *dstptr, *dstptr2;

	if(jpegqual>0)
	{
		snprintf(qualstr, 6, "_Q%d", jpegqual);
		qualstr[5]=0;
	}

	if((handle=tjInitDecompress())==NULL)
		_throwtj("executing tjInitDecompress()");

	bufsize=(yuv==YUVDECODE? yuvsize:pitch*h);
	if(dstbuf==NULL)
	{
		if((dstbuf=(unsigned char *)malloc(bufsize)) == NULL)
			_throwunix("allocating image buffer");
		dstbufalloc=1;
	}
	/* Set the destination buffer to gray so we know whether the decompressor
	   attempted to write to it */
	memset(dstbuf, 127, bufsize);

	/* Execute once to preload cache */
	if(yuv==YUVDECODE)
	{
		if(tjDecompressToYUV(handle, jpegbuf[0], jpegsize[0], dstbuf, flags)==-1)
			_throwtj("executing tjDecompressToYUV()");
	}
	else if(tjDecompress2(handle, jpegbuf[0], jpegsize[0], dstbuf, scaledw,
		pitch, scaledh, pf, flags)==-1)
		_throwtj("executing tjDecompress2()");

	/* Benchmark */
	for(i=0, start=gettime(); (elapsed=gettime()-start)<benchtime; i++)
	{
		int tile=0;
		if(yuv==YUVDECODE)
		{
			if(tjDecompressToYUV(handle, jpegbuf[0], jpegsize[0], dstbuf, flags)==-1)
			_throwtj("executing tjDecompressToYUV()");
		}
		else for(row=0, dstptr=dstbuf; row<ntilesh; row++, dstptr+=pitch*tileh)
		{
			for(col=0, dstptr2=dstptr; col<ntilesw; col++, tile++, dstptr2+=ps*tilew)
			{
				int width=dotile? min(tilew, w-col*tilew):scaledw;
				int height=dotile? min(tileh, h-row*tileh):scaledh;
				if(tjDecompress2(handle, jpegbuf[tile], jpegsize[tile], dstptr2, width,
					pitch, height, pf, flags)==-1)
					_throwtj("executing tjDecompress2()");
			}
		}
	}

	if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()");
	handle=NULL;

	if(quiet)
	{
		printf("%s\n",
			sigfig((double)(w*h)/1000000.*(double)i/elapsed, 4, tempstr, 1024));
	}
	else
	{
		printf("D--> Frame rate:           %f fps\n", (double)i/elapsed);
		printf("     Dest. throughput:     %f Megapixels/sec\n",
			(double)(w*h)/1000000.*(double)i/elapsed);
	}
	if(yuv==YUVDECODE)
	{
		snprintf(tempstr, 1024, "%s_%s%s.yuv", filename, subName[subsamp],
			qualstr);
		if((file=fopen(tempstr, "wb"))==NULL)
			_throwunix("opening YUV image for output");
		if(fwrite(dstbuf, yuvsize, 1, file)!=1)
			_throwunix("writing YUV image");
		fclose(file);  file=NULL;
	}
	else
	{
		if(sf.num!=1 || sf.denom!=1)
			snprintf(sizestr, 20, "%d_%d", sf.num, sf.denom);
		else if(tilew!=w || tileh!=h)
			snprintf(sizestr, 20, "%dx%d", tilew, tileh);
		else snprintf(sizestr, 20, "full");
		if(decomponly)
			snprintf(tempstr, 1024, "%s_%s.%s", filename, sizestr, ext);
		else
			snprintf(tempstr, 1024, "%s_%s%s_%s.%s", filename, subName[subsamp],
				qualstr, sizestr, ext);
		if(savebmp(tempstr, dstbuf, scaledw, scaledh, pf,
			(flags&TJFLAG_BOTTOMUP)!=0)==-1)
			_throwbmp("saving bitmap");
		ptr=strrchr(tempstr, '.');
		snprintf(ptr, 1024-(ptr-tempstr), "-err.%s", ext);
		if(srcbuf && sf.num==1 && sf.denom==1)
		{
			if(!quiet) printf("Compression error written to %s.\n", tempstr);
			if(subsamp==TJ_GRAYSCALE)
			{
				int index, index2;
				for(row=0, index=0; row<h; row++, index+=pitch)
				{
					for(col=0, index2=index; col<w; col++, index2+=ps)
					{
						int rindex=index2+tjRedOffset[pf];
						int gindex=index2+tjGreenOffset[pf];
						int bindex=index2+tjBlueOffset[pf];
						int y=(int)((double)srcbuf[rindex]*0.299
							+ (double)srcbuf[gindex]*0.587
							+ (double)srcbuf[bindex]*0.114 + 0.5);
						if(y>255) y=255;  if(y<0) y=0;
						dstbuf[rindex]=abs(dstbuf[rindex]-y);
						dstbuf[gindex]=abs(dstbuf[gindex]-y);
						dstbuf[bindex]=abs(dstbuf[bindex]-y);
					}
				}
			}		
			else
			{
				for(row=0; row<h; row++)
					for(col=0; col<w*ps; col++)
						dstbuf[pitch*row+col]
							=abs(dstbuf[pitch*row+col]-srcbuf[pitch*row+col]);
			}
			if(savebmp(tempstr, dstbuf, w, h, pf,
				(flags&TJFLAG_BOTTOMUP)!=0)==-1)
				_throwbmp("saving bitmap");
		}
	}

	bailout:
	if(file) {fclose(file);  file=NULL;}
	if(handle) {tjDestroy(handle);  handle=NULL;}
	if(dstbuf && dstbufalloc) {free(dstbuf);  dstbuf=NULL;}
	return retval;
}
示例#2
0
int main(int argc, char *argv[])
{
	unsigned char *srcbuf=NULL;  int w=0, h=0, i, j;
	int minqual=-1, maxqual=-1;  char *temp;
	int minarg=2, retval=0, subsamp=-1;

	if((scalingfactors=tjGetScalingFactors(&nsf))==NULL || nsf==0)
		_throwtj("executing tjGetScalingFactors()");

	if(argc<minarg) usage(argv[0]);

	temp=strrchr(argv[1], '.');
	if(temp!=NULL)
	{
		if(!strcasecmp(temp, ".bmp")) ext="bmp";
		if(!strcasecmp(temp, ".jpg") || !strcasecmp(temp, ".jpeg")) decomponly=1;
	}

	printf("\n");

	if(!decomponly)
	{
		minarg=3;
		if(argc<minarg) usage(argv[0]);
		if((minqual=atoi(argv[2]))<1 || minqual>100)
		{
			puts("ERROR: Quality must be between 1 and 100.");
			exit(1);
		}
		if((temp=strchr(argv[2], '-'))!=NULL && strlen(temp)>1
			&& sscanf(&temp[1], "%d", &maxqual)==1 && maxqual>minqual && maxqual>=1
			&& maxqual<=100) {}
		else maxqual=minqual;
	}

	if(argc>minarg)
	{
		for(i=minarg; i<argc; i++)
		{
			if(!strcasecmp(argv[i], "-tile"))
			{
				dotile=1;  xformopt|=TJXOPT_CROP;
			}
			if(!strcasecmp(argv[i], "-fastupsample"))
			{
				printf("Using fast upsampling code\n\n");
				flags|=TJFLAG_FASTUPSAMPLE;
			}
			if(!strcasecmp(argv[i], "-fastdct"))
			{
				printf("Using fastest DCT/IDCT algorithm\n\n");
				flags|=TJFLAG_FASTDCT;
			}
			if(!strcasecmp(argv[i], "-accuratedct"))
			{
				printf("Using most accurate DCT/IDCT algorithm\n\n");
				flags|=TJFLAG_ACCURATEDCT;
			}
			if(!strcasecmp(argv[i], "-rgb")) pf=TJPF_RGB;
			if(!strcasecmp(argv[i], "-rgbx")) pf=TJPF_RGBX;
			if(!strcasecmp(argv[i], "-bgr")) pf=TJPF_BGR;
			if(!strcasecmp(argv[i], "-bgrx")) pf=TJPF_BGRX;
			if(!strcasecmp(argv[i], "-xbgr")) pf=TJPF_XBGR;
			if(!strcasecmp(argv[i], "-xrgb")) pf=TJPF_XRGB;
			if(!strcasecmp(argv[i], "-cmyk")) pf=TJPF_CMYK;
			if(!strcasecmp(argv[i], "-bottomup")) flags|=TJFLAG_BOTTOMUP;
			if(!strcasecmp(argv[i], "-quiet")) quiet=1;
			if(!strcasecmp(argv[i], "-qq")) quiet=2;
			if(!strcasecmp(argv[i], "-scale") && i<argc-1)
			{
				int temp1=0, temp2=0, match=0;
				if(sscanf(argv[++i], "%d/%d", &temp1, &temp2)==2)
				{
					for(j=0; j<nsf; j++)
					{
						if((double)temp1/(double)temp2
							== (double)scalingfactors[j].num/(double)scalingfactors[j].denom)
						{
							sf=scalingfactors[j];
							match=1;  break;
						}
					}
					if(!match) usage(argv[0]);
				}
				else usage(argv[0]);
			}
			if(!strcasecmp(argv[i], "-hflip")) xformop=TJXOP_HFLIP;
			if(!strcasecmp(argv[i], "-vflip")) xformop=TJXOP_VFLIP;
			if(!strcasecmp(argv[i], "-transpose")) xformop=TJXOP_TRANSPOSE;
			if(!strcasecmp(argv[i], "-transverse")) xformop=TJXOP_TRANSVERSE;
			if(!strcasecmp(argv[i], "-rot90")) xformop=TJXOP_ROT90;
			if(!strcasecmp(argv[i], "-rot180")) xformop=TJXOP_ROT180;
			if(!strcasecmp(argv[i], "-rot270")) xformop=TJXOP_ROT270;
			if(!strcasecmp(argv[i], "-grayscale")) xformopt|=TJXOPT_GRAY;
			if(!strcasecmp(argv[i], "-custom")) customFilter=dummyDCTFilter;
			if(!strcasecmp(argv[i], "-nooutput")) xformopt|=TJXOPT_NOOUTPUT;
			if(!strcasecmp(argv[i], "-benchtime") && i<argc-1)
			{
				double temp=atof(argv[++i]);
				if(temp>0.0) benchtime=temp;
				else usage(argv[0]);
			}
			if(!strcasecmp(argv[i], "-warmup") && i<argc-1)
			{
				int temp=atoi(argv[++i]);
				if(temp>=0)
				{
					warmup=temp;
					printf("Warmup runs = %d\n\n", warmup);
				}
				else usage(argv[0]);
			}
			if(!strcmp(argv[i], "-?")) usage(argv[0]);
			if(!strcasecmp(argv[i], "-alloc")) flags&=(~TJFLAG_NOREALLOC);
			if(!strcasecmp(argv[i], "-bmp")) ext="bmp";
			if(!strcasecmp(argv[i], "-yuv"))
			{
				printf("Testing YUV planar encoding/decoding\n\n");
				doyuv=1;
			}
			if(!strcasecmp(argv[i], "-yuvpad") && i<argc-1)
			{
				int temp=atoi(argv[++i]);
				if(temp>=1) yuvpad=temp;
			}
			if(!strcasecmp(argv[i], "-subsamp") && i<argc-1)
			{
				i++;
				if(toupper(argv[i][0])=='G') subsamp=TJSAMP_GRAY;
				else
				{
					int temp=atoi(argv[i]);
					switch(temp)
					{
						case 444:  subsamp=TJSAMP_444;  break;
						case 422:  subsamp=TJSAMP_422;  break;
						case 440:  subsamp=TJSAMP_440;  break;
						case 420:  subsamp=TJSAMP_420;  break;
						case 411:  subsamp=TJSAMP_411;  break;
					}
				}
			}
			if(!strcasecmp(argv[i], "-componly")) componly=1;
		}
	}

	if((sf.num!=1 || sf.denom!=1) && dotile)
	{
		printf("Disabling tiled compression/decompression tests, because those tests do not\n");
		printf("work when scaled decompression is enabled.\n");
		dotile=0;
	}

	if((flags&TJFLAG_NOREALLOC)==0 && dotile)
	{
		printf("Disabling tiled compression/decompression tests, because those tests do not\n");
		printf("work when dynamic JPEG buffer allocation is enabled.\n\n");
		dotile=0;
	}

	if(!decomponly)
	{
		if(loadbmp(argv[1], &srcbuf, &w, &h, pf, (flags&TJFLAG_BOTTOMUP)!=0)==-1)
			_throwbmp("loading bitmap");
		temp=strrchr(argv[1], '.');
		if(temp!=NULL) *temp='\0';
	}

	if(quiet==1 && !decomponly)
	{
		printf("All performance values in Mpixels/sec\n\n");
		printf("Bitmap     JPEG     JPEG  %s  %s   ",
			dotile? "Tile ":"Image", dotile? "Tile ":"Image");
		if(doyuv) printf("Encode  ");
		printf("Comp    Comp    Decomp  ");
		if(doyuv) printf("Decode");
		printf("\n");
		printf("Format     Subsamp  Qual  Width  Height  ");
		if(doyuv) printf("Perf    ");
		printf("Perf    Ratio   Perf    ");
		if(doyuv) printf("Perf");
		printf("\n\n");
	}

	if(decomponly)
	{
		decompTest(argv[1]);
		printf("\n");
		goto bailout;
	}
	if(subsamp>=0 && subsamp<TJ_NUMSAMP)
	{
		for(i=maxqual; i>=minqual; i--)
			fullTest(srcbuf, w, h, subsamp, i, argv[1]);
		printf("\n");
	}
	else
	{
		if(pf!=TJPF_CMYK)
		{
			for(i=maxqual; i>=minqual; i--)
				fullTest(srcbuf, w, h, TJSAMP_GRAY, i, argv[1]);
			printf("\n");
		}
		for(i=maxqual; i>=minqual; i--)
			fullTest(srcbuf, w, h, TJSAMP_420, i, argv[1]);
		printf("\n");
		for(i=maxqual; i>=minqual; i--)
			fullTest(srcbuf, w, h, TJSAMP_422, i, argv[1]);
		printf("\n");
		for(i=maxqual; i>=minqual; i--)
			fullTest(srcbuf, w, h, TJSAMP_444, i, argv[1]);
		printf("\n");
	}

	bailout:
	if(srcbuf) free(srcbuf);
	return retval;
}
示例#3
0
int main(int argc, char *argv[])
{
	unsigned char *srcbuf=NULL;  int w, h, i, j;
	int minqual=-1, maxqual=-1;  char *temp;
	int minarg=2;  int retval=0;

	if((scalingfactors=tjGetScalingFactors(&nsf))==NULL || nsf==0)
		_throwtj("executing tjGetScalingFactors()");

	if(argc<minarg) usage(argv[0]);

	temp=strrchr(argv[1], '.');
	if(temp!=NULL)
	{
		if(!strcasecmp(temp, ".bmp")) ext="bmp";
		if(!strcasecmp(temp, ".jpg") || !strcasecmp(temp, ".jpeg")) decomponly=1;
	}

	printf("\n");

	if(argc>minarg)
	{
		for(i=minarg; i<argc; i++)
		{
			if(!strcasecmp(argv[i], "-yuvencode"))
			{
				printf("Testing YUV planar encoding\n\n");
				yuv=YUVENCODE;  maxqual=minqual=100;
			}
			if(!strcasecmp(argv[i], "-yuvdecode"))
			{
				printf("Testing YUV planar decoding\n\n");
				yuv=YUVDECODE;
			}
		}
	}

	if(!decomponly && yuv!=YUVENCODE)
	{
		minarg=3;
		if(argc<minarg) usage(argv[0]);
		if((minqual=atoi(argv[2]))<1 || minqual>100)
		{
			puts("ERROR: Quality must be between 1 and 100.");
			exit(1);
		}
		if((temp=strchr(argv[2], '-'))!=NULL && strlen(temp)>1
			&& sscanf(&temp[1], "%d", &maxqual)==1 && maxqual>minqual && maxqual>=1
			&& maxqual<=100) {}
		else maxqual=minqual;
	}

	if(argc>minarg)
	{
		for(i=minarg; i<argc; i++)
		{
			if(!strcasecmp(argv[i], "-tile"))
			{
				dotile=1;  xformopt|=TJXOPT_CROP;
			}
			if(!strcasecmp(argv[i], "-forcesse3"))
			{
				printf("Forcing SSE3 code\n\n");
				flags|=TJFLAG_FORCESSE3;
			}
			if(!strcasecmp(argv[i], "-forcesse2"))
			{
				printf("Forcing SSE2 code\n\n");
				flags|=TJFLAG_FORCESSE2;
			}
			if(!strcasecmp(argv[i], "-forcesse"))
			{
				printf("Forcing SSE code\n\n");
				flags|=TJFLAG_FORCESSE;
			}
			if(!strcasecmp(argv[i], "-forcemmx"))
			{
				printf("Forcing MMX code\n\n");
				flags|=TJFLAG_FORCEMMX;
			}
			if(!strcasecmp(argv[i], "-fastupsample"))
			{
				printf("Using fast upsampling code\n\n");
				flags|=TJFLAG_FASTUPSAMPLE;
			}
			if(!strcasecmp(argv[i], "-rgb")) pf=TJPF_RGB;
			if(!strcasecmp(argv[i], "-rgbx")) pf=TJPF_RGBX;
			if(!strcasecmp(argv[i], "-bgr")) pf=TJPF_BGR;
			if(!strcasecmp(argv[i], "-bgrx")) pf=TJPF_BGRX;
			if(!strcasecmp(argv[i], "-xbgr")) pf=TJPF_XBGR;
			if(!strcasecmp(argv[i], "-xrgb")) pf=TJPF_XRGB;
			if(!strcasecmp(argv[i], "-bottomup")) flags|=TJFLAG_BOTTOMUP;
			if(!strcasecmp(argv[i], "-quiet")) quiet=1;
			if(!strcasecmp(argv[i], "-qq")) quiet=2;
			if(!strcasecmp(argv[i], "-scale") && i<argc-1)
			{
				int temp1=0, temp2=0, match=0;
				if(sscanf(argv[++i], "%d/%d", &temp1, &temp2)==2)
				{
					for(j=0; j<nsf; j++)
					{
						if(temp1==scalingfactors[j].num && temp2==scalingfactors[j].denom)
						{
							sf=scalingfactors[j];
							match=1;  break;
						}
					}
					if(!match) usage(argv[0]);
				}
				else usage(argv[0]);
			}
			if(!strcasecmp(argv[i], "-hflip")) xformop=TJXOP_HFLIP;
			if(!strcasecmp(argv[i], "-vflip")) xformop=TJXOP_VFLIP;
			if(!strcasecmp(argv[i], "-transpose")) xformop=TJXOP_TRANSPOSE;
			if(!strcasecmp(argv[i], "-transverse")) xformop=TJXOP_TRANSVERSE;
			if(!strcasecmp(argv[i], "-rot90")) xformop=TJXOP_ROT90;
			if(!strcasecmp(argv[i], "-rot180")) xformop=TJXOP_ROT180;
			if(!strcasecmp(argv[i], "-rot270")) xformop=TJXOP_ROT270;
			if(!strcasecmp(argv[i], "-grayscale")) xformopt|=TJXOPT_GRAY;
			if(!strcasecmp(argv[i], "-benchtime") && i<argc-1)
			{
				double temp=atof(argv[++i]);
				if(temp>0.0) benchtime=temp;
				else usage(argv[0]);
			}
			if(!strcmp(argv[i], "-?")) usage(argv[0]);
			if(!strcasecmp(argv[i], "-alloc")) flags&=(~TJFLAG_NOREALLOC);
			if(!strcasecmp(argv[i], "-bmp")) ext="bmp";
		}
	}

	if((sf.num!=1 || sf.denom!=1) && dotile)
	{
		printf("Disabling tiled compression/decompression tests, because those tests do not\n");
		printf("work when scaled decompression is enabled.\n");
		dotile=0;
	}

	if(yuv && dotile)
	{
		printf("Disabling tiled compression/decompression tests, because those tests do not\n");
		printf("work when YUV encoding or decoding is enabled.\n\n");
		dotile=0;
	}

	if(!decomponly)
	{
		if(loadbmp(argv[1], &srcbuf, &w, &h, pf, (flags&TJFLAG_BOTTOMUP)!=0)==-1)
			_throwbmp("loading bitmap");
		temp=strrchr(argv[1], '.');
		if(temp!=NULL) *temp='\0';
	}

	if(quiet==1 && !decomponly)
	{
		printf("All performance values in Mpixels/sec\n\n");
		printf("Bitmap\tBitmap\tJPEG\tJPEG\t%s %s \tComp\tComp\tDecomp\n",
			dotile? "Tile ":"Image", dotile? "Tile ":"Image");
		printf("Format\tOrder\tSubsamp\tQual\tWidth Height\tPerf \tRatio\tPerf\n\n");
	}

	if(decomponly)
	{
		dodecomptest(argv[1]);
		printf("\n");
		goto bailout;
	}
	for(i=maxqual; i>=minqual; i--)
		dotest(srcbuf, w, h, TJ_GRAYSCALE, i, argv[1]);
	printf("\n");
	for(i=maxqual; i>=minqual; i--)
		dotest(srcbuf, w, h, TJ_420, i, argv[1]);
	printf("\n");
	for(i=maxqual; i>=minqual; i--)
		dotest(srcbuf, w, h, TJ_422, i, argv[1]);
	printf("\n");
	for(i=maxqual; i>=minqual; i--)
		dotest(srcbuf, w, h, TJ_444, i, argv[1]);
	printf("\n");

	bailout:
	if(srcbuf) free(srcbuf);
	return retval;
}
示例#4
0
/* Decompression test */
int decomp(unsigned char *srcbuf, unsigned char **jpegbuf,
	unsigned long *jpegsize, unsigned char *dstbuf, int w, int h,
	int subsamp, int jpegqual, char *filename, int tilew, int tileh)
{
	char tempstr[1024], sizestr[20]="\0", qualstr[6]="\0", *ptr;
	FILE *file=NULL;  tjhandle handle=NULL;
	int row, col, iter=0, dstbufalloc=0, retval=0;
	double elapsed, elapsedDecode;
	int ps=tjPixelSize[pf];
	int scaledw=TJSCALED(w, sf);
	int scaledh=TJSCALED(h, sf);
	int pitch=scaledw*ps;
	int ntilesw=(w+tilew-1)/tilew, ntilesh=(h+tileh-1)/tileh;
	unsigned char *dstptr, *dstptr2, *yuvbuf=NULL;

	if(jpegqual>0)
	{
		snprintf(qualstr, 6, "_Q%d", jpegqual);
		qualstr[5]=0;
	}

	if((handle=tjInitDecompress())==NULL)
		_throwtj("executing tjInitDecompress()");

	if(dstbuf==NULL)
	{
		if((dstbuf=(unsigned char *)malloc(pitch*scaledh))==NULL)
			_throwunix("allocating destination buffer");
		dstbufalloc=1;
	}
	/* Set the destination buffer to gray so we know whether the decompressor
	   attempted to write to it */
	memset(dstbuf, 127, pitch*scaledh);

	if(doyuv)
	{
		int width=dotile? tilew:scaledw;
		int height=dotile? tileh:scaledh;
		int yuvsize=tjBufSizeYUV2(width, yuvpad, height, subsamp);
		if((yuvbuf=(unsigned char *)malloc(yuvsize))==NULL)
			_throwunix("allocating YUV buffer");
		memset(yuvbuf, 127, yuvsize);
	}

	/* Benchmark */
	iter=-warmup;
	elapsed=elapsedDecode=0.;
	while(1)
	{
		int tile=0;
		double start=gettime();
		for(row=0, dstptr=dstbuf; row<ntilesh; row++, dstptr+=pitch*tileh)
		{
			for(col=0, dstptr2=dstptr; col<ntilesw; col++, tile++, dstptr2+=ps*tilew)
			{
				int width=dotile? min(tilew, w-col*tilew):scaledw;
				int height=dotile? min(tileh, h-row*tileh):scaledh;
				if(doyuv)
				{
					double startDecode;
					if(tjDecompressToYUV2(handle, jpegbuf[tile], jpegsize[tile], yuvbuf,
						width, yuvpad, height, flags)==-1)
						_throwtj("executing tjDecompressToYUV2()");
					startDecode=gettime();
					if(tjDecodeYUV(handle, yuvbuf, yuvpad, subsamp, dstptr2, width,
						pitch, height, pf, flags)==-1)
						_throwtj("executing tjDecodeYUV()");
					if(iter>=0) elapsedDecode+=gettime()-startDecode;
				}
				else
					if(tjDecompress2(handle, jpegbuf[tile], jpegsize[tile], dstptr2,
						width, pitch, height, pf, flags)==-1)
						_throwtj("executing tjDecompress2()");
			}
		}
		iter++;
		if(iter>=1)
		{
			elapsed+=gettime()-start;
			if(elapsed>=benchtime) break;
		}
	}
	if(doyuv) elapsed-=elapsedDecode;

	if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()");
	handle=NULL;

	if(quiet)
	{
		printf("%-6s%s",
			sigfig((double)(w*h)/1000000.*(double)iter/elapsed, 4, tempstr, 1024),
			quiet==2? "\n":"  ");
		if(doyuv)
			printf("%s\n",
				sigfig((double)(w*h)/1000000.*(double)iter/elapsedDecode, 4, tempstr,
					1024));
		else if(quiet!=2) printf("\n");
	}
	else
	{
		printf("%s --> Frame rate:         %f fps\n",
			doyuv? "Decomp to YUV":"Decompress   ", (double)iter/elapsed);
		printf("                  Throughput:         %f Megapixels/sec\n",
			(double)(w*h)/1000000.*(double)iter/elapsed);
		if(doyuv)
		{
			printf("YUV Decode    --> Frame rate:         %f fps\n",
				(double)iter/elapsedDecode);
			printf("                  Throughput:         %f Megapixels/sec\n",
				(double)(w*h)/1000000.*(double)iter/elapsedDecode);
		}
	}
	if(sf.num!=1 || sf.denom!=1)
		snprintf(sizestr, 20, "%d_%d", sf.num, sf.denom);
	else if(tilew!=w || tileh!=h)
		snprintf(sizestr, 20, "%dx%d", tilew, tileh);
	else snprintf(sizestr, 20, "full");
	if(decomponly)
		snprintf(tempstr, 1024, "%s_%s.%s", filename, sizestr, ext);
	else
		snprintf(tempstr, 1024, "%s_%s%s_%s.%s", filename, subName[subsamp],
			qualstr, sizestr, ext);

	if(savebmp(tempstr, dstbuf, scaledw, scaledh, pf,
		(flags&TJFLAG_BOTTOMUP)!=0)==-1)
		_throwbmp("saving bitmap");
	ptr=strrchr(tempstr, '.');
	snprintf(ptr, 1024-(ptr-tempstr), "-err.%s", ext);
	if(srcbuf && sf.num==1 && sf.denom==1)
	{
		if(!quiet) printf("Compression error written to %s.\n", tempstr);
		if(subsamp==TJ_GRAYSCALE)
		{
			int index, index2;
			for(row=0, index=0; row<h; row++, index+=pitch)
			{
				for(col=0, index2=index; col<w; col++, index2+=ps)
				{
					int rindex=index2+tjRedOffset[pf];
					int gindex=index2+tjGreenOffset[pf];
					int bindex=index2+tjBlueOffset[pf];
					int y=(int)((double)srcbuf[rindex]*0.299
						+ (double)srcbuf[gindex]*0.587
						+ (double)srcbuf[bindex]*0.114 + 0.5);
					if(y>255) y=255;  if(y<0) y=0;
					dstbuf[rindex]=abs(dstbuf[rindex]-y);
					dstbuf[gindex]=abs(dstbuf[gindex]-y);
					dstbuf[bindex]=abs(dstbuf[bindex]-y);
				}
			}
		}
		else
		{
			for(row=0; row<h; row++)
				for(col=0; col<w*ps; col++)
					dstbuf[pitch*row+col]
						=abs(dstbuf[pitch*row+col]-srcbuf[pitch*row+col]);
		}
		if(savebmp(tempstr, dstbuf, w, h, pf,
			(flags&TJFLAG_BOTTOMUP)!=0)==-1)
			_throwbmp("saving bitmap");
	}

	bailout:
	if(file) fclose(file);
	if(handle) tjDestroy(handle);
	if(dstbuf && dstbufalloc) free(dstbuf);
	if(yuvbuf) free(yuvbuf);
	return retval;
}