1/* Copyright (C)2004 Landmark Graphics Corporation
2 * Copyright (C)2005, 2006 Sun Microsystems, Inc.
3 * Copyright (C)2009 D. R. Commander
4 *
5 * This library is free software and may be redistributed and/or modified under
6 * the terms of the wxWindows Library License, Version 3.1 or (at your option)
7 * any later version.  The full license is in the LICENSE.txt file included
8 * with this distribution.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * wxWindows Library License for more details.
14 */
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <math.h>
20#include "./bmp.h"
21#include "./rrutil.h"
22#include "./rrtimer.h"
23#include "./turbojpeg.h"
24
25#define _catch(f) {if((f)==-1) {printf("Error in %s:\n%s\n", #f, tjGetErrorStr());  goto bailout;}}
26
27int forcemmx=0, forcesse=0, forcesse2=0, forcesse3=0, fastupsample=0;
28const int _ps[BMPPIXELFORMATS]={3, 4, 3, 4, 4, 4};
29const int _flags[BMPPIXELFORMATS]={0, 0, TJ_BGR, TJ_BGR,
30	TJ_BGR|TJ_ALPHAFIRST, TJ_ALPHAFIRST};
31const int _rindex[BMPPIXELFORMATS]={0, 0, 2, 2, 3, 1};
32const int _gindex[BMPPIXELFORMATS]={1, 1, 1, 1, 2, 2};
33const int _bindex[BMPPIXELFORMATS]={2, 2, 0, 0, 1, 3};
34const char *_pfname[]={"RGB", "RGBA", "BGR", "BGRA", "ABGR", "ARGB"};
35const char *_subnamel[NUMSUBOPT]={"4:4:4", "4:2:2", "4:2:0", "GRAY"};
36const char *_subnames[NUMSUBOPT]={"444", "422", "420", "GRAY"};
37
38void printsigfig(double val, int figs)
39{
40	char format[80];
41	double _l=log10(val);  int l;
42	if(_l<0.)
43	{
44		l=(int)fabs(_l);
45		sprintf(format, "%%%d.%df", figs+l+2, figs+l);
46	}
47	else
48	{
49		l=(int)_l+1;
50		if(figs<=l) sprintf(format, "%%.0f");
51		else sprintf(format, "%%%d.%df", figs+1, figs-l);
52	}
53	printf(format, val);
54}
55
56void dotest(unsigned char *srcbuf, int w, int h, BMPPIXELFORMAT pf, int bu,
57	int jpegsub, int qual, char *filename, int dotile, int useppm, int quiet)
58{
59	char tempstr[1024];
60	FILE *outfile;  tjhandle hnd;
61	unsigned char **jpegbuf=NULL, *rgbbuf=NULL;
62	rrtimer timer; double elapsed;
63	int jpgbufsize=0, i, j, tilesizex, tilesizey, numtilesx, numtilesy, ITER;
64	unsigned long *comptilesize=NULL;
65	int flags=(forcemmx?TJ_FORCEMMX:0)|(forcesse?TJ_FORCESSE:0)
66		|(forcesse2?TJ_FORCESSE2:0)|(forcesse3?TJ_FORCESSE3:0)
67		|(fastupsample?TJ_FASTUPSAMPLE:0);
68	int ps=_ps[pf];
69	int pitch=w*ps;
70
71	flags |= _flags[pf];
72	if(bu) flags |= TJ_BOTTOMUP;
73
74	if((rgbbuf=(unsigned char *)malloc(pitch*h)) == NULL)
75	{
76		puts("ERROR: Could not allocate image buffer.");
77		exit(1);
78	}
79
80	if(!quiet) printf("\n>>>>>  %s (%s) <--> JPEG %s Q%d  <<<<<\n", _pfname[pf],
81		bu?"Bottom-up":"Top-down", _subnamel[jpegsub], qual);
82	if(dotile) {tilesizex=tilesizey=4;}  else {tilesizex=w;  tilesizey=h;}
83
84	do
85	{
86		tilesizex*=2;  if(tilesizex>w) tilesizex=w;
87		tilesizey*=2;  if(tilesizey>h) tilesizey=h;
88		numtilesx=(w+tilesizex-1)/tilesizex;
89		numtilesy=(h+tilesizey-1)/tilesizey;
90		if((comptilesize=(unsigned long *)malloc(sizeof(unsigned long)*numtilesx*numtilesy)) == NULL
91		|| (jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *)*numtilesx*numtilesy)) == NULL)
92		{
93			puts("ERROR: Could not allocate image buffers.");
94			goto bailout;
95		}
96		memset(jpegbuf, 0, sizeof(unsigned char *)*numtilesx*numtilesy);
97		for(i=0; i<numtilesx*numtilesy; i++)
98		{
99			if((jpegbuf[i]=(unsigned char *)malloc(TJBUFSIZE(tilesizex, tilesizey))) == NULL)
100			{
101				puts("ERROR: Could not allocate image buffers.");
102				goto bailout;
103			}
104		}
105
106		// Compression test
107		if(quiet) printf("%s\t%s\t%s\t%d\t",  _pfname[pf], bu?"BU":"TD",
108			_subnamel[jpegsub], qual);
109		for(i=0; i<h; i++) memcpy(&rgbbuf[pitch*i], &srcbuf[w*ps*i], w*ps);
110		if((hnd=tjInitCompress())==NULL)
111		{
112			printf("Error in tjInitCompress():\n%s\n", tjGetErrorStr());
113			goto bailout;
114		}
115		_catch(tjCompress(hnd, rgbbuf, tilesizex, pitch, tilesizey, ps,
116			jpegbuf[0], &comptilesize[0], jpegsub, qual, flags));
117		ITER=0;
118		timer.start();
119		do
120		{
121			jpgbufsize=0;  int tilen=0;
122			for(i=0; i<h; i+=tilesizey)
123			{
124				for(j=0; j<w; j+=tilesizex)
125				{
126					int tempw=min(tilesizex, w-j), temph=min(tilesizey, h-i);
127					_catch(tjCompress(hnd, &rgbbuf[pitch*i+j*ps], tempw, pitch,
128						temph, ps, jpegbuf[tilen], &comptilesize[tilen], jpegsub, qual,
129						flags));
130					jpgbufsize+=comptilesize[tilen];
131					tilen++;
132				}
133			}
134			ITER++;
135		} while((elapsed=timer.elapsed())<5.);
136		_catch(tjDestroy(hnd));
137		if(quiet)
138		{
139			if(tilesizex==w && tilesizey==h) printf("Full     \t");
140			else printf("%-4d %-4d\t", tilesizex, tilesizey);
141			printsigfig((double)(w*h)/1000000.*(double)ITER/elapsed, 4);
142			printf("\t");
143			printsigfig((double)(w*h*ps)/(double)jpgbufsize, 4);
144			printf("\t");
145		}
146		else
147		{
148			if(tilesizex==w && tilesizey==h) printf("\nFull image\n");
149			else printf("\nTile size: %d x %d\n", tilesizex, tilesizey);
150			printf("C--> Frame rate:           %f fps\n", (double)ITER/elapsed);
151			printf("     Output image size:    %d bytes\n", jpgbufsize);
152			printf("     Compression ratio:    %f:1\n",
153				(double)(w*h*ps)/(double)jpgbufsize);
154			printf("     Source throughput:    %f Megapixels/sec\n",
155				(double)(w*h)/1000000.*(double)ITER/elapsed);
156			printf("     Output bit stream:    %f Megabits/sec\n",
157				(double)jpgbufsize*8./1000000.*(double)ITER/elapsed);
158		}
159		if(tilesizex==w && tilesizey==h)
160		{
161			sprintf(tempstr, "%s_%sQ%d.jpg", filename, _subnames[jpegsub], qual);
162			if((outfile=fopen(tempstr, "wb"))==NULL)
163			{
164				puts("ERROR: Could not open reference image");
165				exit(1);
166			}
167			if(fwrite(jpegbuf[0], jpgbufsize, 1, outfile)!=1)
168			{
169				puts("ERROR: Could not write reference image");
170				exit(1);
171			}
172			fclose(outfile);
173			if(!quiet) printf("Reference image written to %s\n", tempstr);
174		}
175
176		// Decompression test
177		memset(rgbbuf, 127, pitch*h);  // Grey image means decompressor did nothing
178		if((hnd=tjInitDecompress())==NULL)
179		{
180			printf("Error in tjInitDecompress():\n%s\n", tjGetErrorStr());
181			goto bailout;
182		}
183		_catch(tjDecompress(hnd, jpegbuf[0], jpgbufsize, rgbbuf, tilesizex, pitch,
184			tilesizey, ps, flags));
185		ITER=0;
186		timer.start();
187		do
188		{
189			int tilen=0;
190			for(i=0; i<h; i+=tilesizey)
191			{
192				for(j=0; j<w; j+=tilesizex)
193				{
194					int tempw=min(tilesizex, w-j), temph=min(tilesizey, h-i);
195					_catch(tjDecompress(hnd, jpegbuf[tilen], comptilesize[tilen],
196						&rgbbuf[pitch*i+ps*j], tempw, pitch, temph, ps, flags));
197					tilen++;
198				}
199			}
200			ITER++;
201		}	while((elapsed=timer.elapsed())<5.);
202		_catch(tjDestroy(hnd));
203		if(quiet)
204		{
205			printsigfig((double)(w*h)/1000000.*(double)ITER/elapsed, 4);
206			printf("\n");
207		}
208		else
209		{
210			printf("D--> Frame rate:           %f fps\n", (double)ITER/elapsed);
211			printf("     Dest. throughput:     %f Megapixels/sec\n",
212				(double)(w*h)/1000000.*(double)ITER/elapsed);
213		}
214		if(tilesizex==w && tilesizey==h)
215			sprintf(tempstr, "%s_%sQ%d_full.%s", filename, _subnames[jpegsub], qual,
216				useppm?"ppm":"bmp");
217		else sprintf(tempstr, "%s_%sQ%d_%dx%d.%s", filename, _subnames[jpegsub],
218			qual, tilesizex, tilesizey, useppm?"ppm":"bmp");
219		if(savebmp(tempstr, rgbbuf, w, h, pf, pitch, bu)==-1)
220		{
221			printf("ERROR saving bitmap: %s\n", bmpgeterr());
222			goto bailout;
223		}
224		sprintf(strrchr(tempstr, '.'), "-err.%s", useppm?"ppm":"bmp");
225		if(!quiet)
226			printf("Computing compression error and saving to %s.\n", tempstr);
227		if(jpegsub==TJ_GRAYSCALE)
228		{
229			for(j=0; j<h; j++)
230			{
231				for(i=0; i<w*ps; i+=ps)
232				{
233					int y=(int)((double)srcbuf[w*ps*j+i+_rindex[pf]]*0.299
234						+ (double)srcbuf[w*ps*j+i+_gindex[pf]]*0.587
235						+ (double)srcbuf[w*ps*j+i+_bindex[pf]]*0.114 + 0.5);
236					if(y>255) y=255;  if(y<0) y=0;
237					rgbbuf[pitch*j+i+_rindex[pf]]=abs(rgbbuf[pitch*j+i+_rindex[pf]]-y);
238					rgbbuf[pitch*j+i+_gindex[pf]]=abs(rgbbuf[pitch*j+i+_gindex[pf]]-y);
239					rgbbuf[pitch*j+i+_bindex[pf]]=abs(rgbbuf[pitch*j+i+_bindex[pf]]-y);
240				}
241			}
242		}
243		else
244		{
245			for(j=0; j<h; j++) for(i=0; i<w*ps; i++)
246				rgbbuf[pitch*j+i]=abs(rgbbuf[pitch*j+i]-srcbuf[w*ps*j+i]);
247		}
248		if(savebmp(tempstr, rgbbuf, w, h, pf, pitch, bu)==-1)
249		{
250			printf("ERROR saving bitmap: %s\n", bmpgeterr());
251			goto bailout;
252		}
253
254		// Cleanup
255		if(jpegbuf)
256		{
257			for(i=0; i<numtilesx*numtilesy; i++)
258				{if(jpegbuf[i]) free(jpegbuf[i]);  jpegbuf[i]=NULL;}
259			free(jpegbuf);  jpegbuf=NULL;
260		}
261		if(comptilesize) {free(comptilesize);  comptilesize=NULL;}
262	} while(tilesizex<w || tilesizey<h);
263
264	if(rgbbuf) {free(rgbbuf);  rgbbuf=NULL;}
265	return;
266
267	bailout:
268	if(jpegbuf)
269	{
270		for(i=0; i<numtilesx*numtilesy; i++)
271			{if(jpegbuf[i]) free(jpegbuf[i]);  jpegbuf[i]=NULL;}
272		free(jpegbuf);  jpegbuf=NULL;
273	}
274	if(comptilesize) {free(comptilesize);  comptilesize=NULL;}
275	if(rgbbuf) {free(rgbbuf);  rgbbuf=NULL;}
276	return;
277}
278
279
280int main(int argc, char *argv[])
281{
282	unsigned char *bmpbuf=NULL;  int w, h, i, useppm=0;
283	int qual, dotile=0, quiet=0, hiqual=-1;  char *temp;
284	BMPPIXELFORMAT pf=BMP_BGR;
285	int bu=0;
286
287	printf("\n");
288
289	if(argc<3)
290	{
291		printf("USAGE: %s <Inputfile (BMP|PPM)> <%% Quality>\n\n", argv[0]);
292		printf("       [-tile]\n");
293		printf("       Test performance of the codec when the image is encoded\n");
294		printf("       as separate tiles of varying sizes.\n\n");
295		printf("       [-forcemmx] [-forcesse] [-forcesse2] [-forcesse3]\n");
296		printf("       Force MMX, SSE, or SSE2 code paths in Intel codec\n\n");
297		printf("       [-rgb | -bgr | -rgba | -bgra | -abgr | -argb]\n");
298		printf("       Test the specified color conversion path in the codec (default: BGR)\n\n");
299		printf("       [-fastupsample]\n");
300		printf("       Use fast, inaccurate upsampling code to perform 4:2:2 and 4:2:0\n");
301		printf("       YUV decoding in libjpeg decompressor\n\n");
302		printf("       [-quiet]\n");
303		printf("       Output in tabular rather than verbose format\n\n");
304		printf("       NOTE: If the quality is specified as a range, i.e. 90-100, a separate\n");
305		printf("       test will be performed for all quality values in the range.\n");
306		exit(1);
307	}
308	if((qual=atoi(argv[2]))<1 || qual>100)
309	{
310		puts("ERROR: Quality must be between 1 and 100.");
311		exit(1);
312	}
313	if((temp=strchr(argv[2], '-'))!=NULL && strlen(temp)>1
314		&& sscanf(&temp[1], "%d", &hiqual)==1 && hiqual>qual && hiqual>=1
315		&& hiqual<=100) {}
316	else hiqual=qual;
317
318	if(argc>3)
319	{
320		for(i=3; i<argc; i++)
321		{
322			if(!stricmp(argv[i], "-tile")) dotile=1;
323			if(!stricmp(argv[i], "-forcesse3"))
324			{
325				printf("Using SSE3 code\n");
326				forcesse3=1;
327			}
328			if(!stricmp(argv[i], "-forcesse2"))
329			{
330				printf("Using SSE2 code\n");
331				forcesse2=1;
332			}
333			if(!stricmp(argv[i], "-forcesse"))
334			{
335				printf("Using SSE code\n");
336				forcesse=1;
337			}
338			if(!stricmp(argv[i], "-forcemmx"))
339			{
340				printf("Using MMX code\n");
341				forcemmx=1;
342			}
343			if(!stricmp(argv[i], "-fastupsample"))
344			{
345				printf("Using fast upsampling code\n");
346				fastupsample=1;
347			}
348			if(!stricmp(argv[i], "-rgb")) pf=BMP_RGB;
349			if(!stricmp(argv[i], "-rgba")) pf=BMP_RGBA;
350			if(!stricmp(argv[i], "-bgr")) pf=BMP_BGR;
351			if(!stricmp(argv[i], "-bgra")) pf=BMP_BGRA;
352			if(!stricmp(argv[i], "-abgr")) pf=BMP_ABGR;
353			if(!stricmp(argv[i], "-argb")) pf=BMP_ARGB;
354			if(!stricmp(argv[i], "-bottomup")) bu=1;
355			if(!stricmp(argv[i], "-quiet")) quiet=1;
356		}
357	}
358
359	if(loadbmp(argv[1], &bmpbuf, &w, &h, pf, 1, bu)==-1)
360	{
361		printf("ERROR loading bitmap: %s\n", bmpgeterr());  exit(1);
362	}
363
364	temp=strrchr(argv[1], '.');
365	if(temp!=NULL)
366	{
367		if(!stricmp(temp, ".ppm")) useppm=1;
368		*temp='\0';
369	}
370
371	if(quiet)
372	{
373		printf("All performance values in Mpixels/sec\n\n");
374		printf("Bitmap\tBitmap\tJPEG\tJPEG\tTile Size\tCompr\tCompr\tDecomp\n");
375		printf("Format\tOrder\tFormat\tQual\t X    Y  \tPerf \tRatio\tPerf\n\n");
376	}
377
378	for(i=hiqual; i>=qual; i--)
379		dotest(bmpbuf, w, h, pf, bu, TJ_GRAYSCALE, i, argv[1], dotile, useppm, quiet);
380	if(quiet) printf("\n");
381	for(i=hiqual; i>=qual; i--)
382		dotest(bmpbuf, w, h, pf, bu, TJ_420, i, argv[1], dotile, useppm, quiet);
383	if(quiet) printf("\n");
384	for(i=hiqual; i>=qual; i--)
385		dotest(bmpbuf, w, h, pf, bu, TJ_422, i, argv[1], dotile, useppm, quiet);
386	if(quiet) printf("\n");
387	for(i=hiqual; i>=qual; i--)
388		dotest(bmpbuf, w, h, pf, bu, TJ_444, i, argv[1], dotile, useppm, quiet);
389
390	if(bmpbuf) free(bmpbuf);
391	return 0;
392}
393