diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/Makefile.am | 13 | ||||
-rw-r--r-- | test/bmp.c | 389 | ||||
-rw-r--r-- | test/bmp.h | 49 | ||||
-rw-r--r-- | test/tjbench.c | 658 | ||||
-rw-r--r-- | test/tjunittest.c | 461 | ||||
-rw-r--r-- | test/tjutil.c | 66 | ||||
-rw-r--r-- | test/tjutil.h | 47 |
7 files changed, 1682 insertions, 1 deletions
diff --git a/test/Makefile.am b/test/Makefile.am index 30498c2..ff6e8f2 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,4 +1,15 @@ -INCLUDES = -I$(top_srcdir) +# TurboJPEG wrapper tests + +noinst_PROGRAMS=tjunittest tjbench + +tjunittest_SOURCES=tjunittest.c ../common/turbojpeg.c ../common/turbojpeg.h \ + tjutil.c tjutil.h + +tjbench_SOURCES=tjbench.c ../common/turbojpeg.c ../common/turbojpeg.h \ + tjutil.c tjutil.h bmp.c bmp.h +tjbench_LDADD=$(LDADD) -lm + +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/common LDADD = ../libvncserver/libvncserver.la ../libvncclient/libvncclient.la @WSOCKLIB@ if HAVE_LIBPTHREAD diff --git a/test/bmp.c b/test/bmp.c new file mode 100644 index 0000000..7dcbf13 --- /dev/null +++ b/test/bmp.c @@ -0,0 +1,389 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * Copyright (C)2010, 2012 D. R. Commander + * + * This library is free software and may be redistributed and/or modified under + * the terms of the wxWindows Library License, Version 3.1 or (at your option) + * any later version. The full license is in the LICENSE.txt file included + * with this distribution. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * wxWindows Library License for more details. +*/ + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifdef _WIN32 + #include <io.h> +#else + #include <unistd.h> +#endif +#include "./tjutil.h" +#include "./bmp.h" + +#define byteswap(i) ( \ + (((i) & 0xff000000) >> 24) | \ + (((i) & 0x00ff0000) >> 8) | \ + (((i) & 0x0000ff00) << 8) | \ + (((i) & 0x000000ff) << 24) ) + +#define byteswap16(i) ( \ + (((i) & 0xff00) >> 8) | \ + (((i) & 0x00ff) << 8) ) + +static __inline int littleendian(void) +{ + unsigned int value=1; + unsigned char *ptr=(unsigned char *)(&value); + if(ptr[0]==1 && ptr[3]==0) return 1; + else return 0; +} + +#ifndef BI_BITFIELDS +#define BI_BITFIELDS 3L +#endif +#ifndef BI_RGB +#define BI_RGB 0L +#endif + +#define BMPHDRSIZE 54 +typedef struct _bmphdr +{ + unsigned short bfType; + unsigned int bfSize; + unsigned short bfReserved1, bfReserved2; + unsigned int bfOffBits; + + unsigned int biSize; + int biWidth, biHeight; + unsigned short biPlanes, biBitCount; + unsigned int biCompression, biSizeImage; + int biXPelsPerMeter, biYPelsPerMeter; + unsigned int biClrUsed, biClrImportant; +} bmphdr; + +static const char *__bmperr="No error"; + +static const int ps[BMPPIXELFORMATS]={3, 4, 3, 4, 4, 4}; +static const int roffset[BMPPIXELFORMATS]={0, 0, 2, 2, 3, 1}; +static const int goffset[BMPPIXELFORMATS]={1, 1, 1, 1, 2, 2}; +static const int boffset[BMPPIXELFORMATS]={2, 2, 0, 0, 1, 3}; + +#define _throw(m) {__bmperr=m; retcode=-1; goto finally;} +#define _unix(f) {if((f)==-1) _throw(strerror(errno));} +#define _catch(f) {if((f)==-1) {retcode=-1; goto finally;}} + +#define readme(fd, addr, size) \ + if((bytesread=read(fd, addr, (size)))==-1) _throw(strerror(errno)); \ + if(bytesread!=(size)) _throw("Read error"); + +void pixelconvert(unsigned char *srcbuf, enum BMPPIXELFORMAT srcformat, + int srcpitch, unsigned char *dstbuf, enum BMPPIXELFORMAT dstformat, int dstpitch, + int w, int h, int flip) +{ + unsigned char *srcptr, *srcptr0, *dstptr, *dstptr0; + int i, j; + + srcptr=flip? &srcbuf[srcpitch*(h-1)]:srcbuf; + for(j=0, dstptr=dstbuf; j<h; j++, + srcptr+=flip? -srcpitch:srcpitch, dstptr+=dstpitch) + { + for(i=0, srcptr0=srcptr, dstptr0=dstptr; i<w; i++, + srcptr0+=ps[srcformat], dstptr0+=ps[dstformat]) + { + dstptr0[roffset[dstformat]]=srcptr0[roffset[srcformat]]; + dstptr0[goffset[dstformat]]=srcptr0[goffset[srcformat]]; + dstptr0[boffset[dstformat]]=srcptr0[boffset[srcformat]]; + } + } +} + +int loadppm(int *fd, unsigned char **buf, int *w, int *h, + enum BMPPIXELFORMAT f, int align, int dstbottomup, int ascii) +{ + FILE *fs=NULL; int retcode=0, scalefactor, dstpitch; + unsigned char *tempbuf=NULL; char temps[255], temps2[255]; + int numread=0, totalread=0, pixel[3], i, j; + + if((fs=fdopen(*fd, "r"))==NULL) _throw(strerror(errno)); + + do + { + if(!fgets(temps, 255, fs)) _throw("Read error"); + if(strlen(temps)==0 || temps[0]=='\n') continue; + if(sscanf(temps, "%s", temps2)==1 && temps2[1]=='#') continue; + switch(totalread) + { + case 0: + if((numread=sscanf(temps, "%d %d %d", w, h, &scalefactor))==EOF) + _throw("Read error"); + break; + case 1: + if((numread=sscanf(temps, "%d %d", h, &scalefactor))==EOF) + _throw("Read error"); + break; + case 2: + if((numread=sscanf(temps, "%d", &scalefactor))==EOF) + _throw("Read error"); + break; + } + totalread+=numread; + } while(totalread<3); + if((*w)<1 || (*h)<1 || scalefactor<1) _throw("Corrupt PPM header"); + + dstpitch=(((*w)*ps[f])+(align-1))&(~(align-1)); + if((*buf=(unsigned char *)malloc(dstpitch*(*h)))==NULL) + _throw("Memory allocation error"); + if(ascii) + { + for(j=0; j<*h; j++) + { + for(i=0; i<*w; i++) + { + if(fscanf(fs, "%d%d%d", &pixel[0], &pixel[1], &pixel[2])!=3) + _throw("Read error"); + (*buf)[j*dstpitch+i*ps[f]+roffset[f]]=(unsigned char)(pixel[0]*255/scalefactor); + (*buf)[j*dstpitch+i*ps[f]+goffset[f]]=(unsigned char)(pixel[1]*255/scalefactor); + (*buf)[j*dstpitch+i*ps[f]+boffset[f]]=(unsigned char)(pixel[2]*255/scalefactor); + } + } + } + else + { + if(scalefactor!=255) + _throw("Binary PPMs must have 8-bit components"); + if((tempbuf=(unsigned char *)malloc((*w)*(*h)*3))==NULL) + _throw("Memory allocation error"); + if(fread(tempbuf, (*w)*(*h)*3, 1, fs)!=1) _throw("Read error"); + pixelconvert(tempbuf, BMP_RGB, (*w)*3, *buf, f, dstpitch, *w, *h, dstbottomup); + } + + finally: + if(fs) {fclose(fs); *fd=-1;} + if(tempbuf) free(tempbuf); + return retcode; +} + + +int loadbmp(char *filename, unsigned char **buf, int *w, int *h, + enum BMPPIXELFORMAT f, int align, int dstbottomup) +{ + int fd=-1, bytesread, srcpitch, srcbottomup=1, srcps, dstpitch, + retcode=0; + unsigned char *tempbuf=NULL; + bmphdr bh; int flags=O_RDONLY; + + dstbottomup=dstbottomup? 1:0; + #ifdef _WIN32 + flags|=O_BINARY; + #endif + if(!filename || !buf || !w || !h || f<0 || f>BMPPIXELFORMATS-1 || align<1) + _throw("invalid argument to loadbmp()"); + if((align&(align-1))!=0) + _throw("Alignment must be a power of 2"); + _unix(fd=open(filename, flags)); + + readme(fd, &bh.bfType, sizeof(unsigned short)); + if(!littleendian()) bh.bfType=byteswap16(bh.bfType); + + if(bh.bfType==0x3650) + { + _catch(loadppm(&fd, buf, w, h, f, align, dstbottomup, 0)); + goto finally; + } + if(bh.bfType==0x3350) + { + _catch(loadppm(&fd, buf, w, h, f, align, dstbottomup, 1)); + goto finally; + } + + readme(fd, &bh.bfSize, sizeof(unsigned int)); + readme(fd, &bh.bfReserved1, sizeof(unsigned short)); + readme(fd, &bh.bfReserved2, sizeof(unsigned short)); + readme(fd, &bh.bfOffBits, sizeof(unsigned int)); + readme(fd, &bh.biSize, sizeof(unsigned int)); + readme(fd, &bh.biWidth, sizeof(int)); + readme(fd, &bh.biHeight, sizeof(int)); + readme(fd, &bh.biPlanes, sizeof(unsigned short)); + readme(fd, &bh.biBitCount, sizeof(unsigned short)); + readme(fd, &bh.biCompression, sizeof(unsigned int)); + readme(fd, &bh.biSizeImage, sizeof(unsigned int)); + readme(fd, &bh.biXPelsPerMeter, sizeof(int)); + readme(fd, &bh.biYPelsPerMeter, sizeof(int)); + readme(fd, &bh.biClrUsed, sizeof(unsigned int)); + readme(fd, &bh.biClrImportant, sizeof(unsigned int)); + + if(!littleendian()) + { + bh.bfSize=byteswap(bh.bfSize); + bh.bfOffBits=byteswap(bh.bfOffBits); + bh.biSize=byteswap(bh.biSize); + bh.biWidth=byteswap(bh.biWidth); + bh.biHeight=byteswap(bh.biHeight); + bh.biPlanes=byteswap16(bh.biPlanes); + bh.biBitCount=byteswap16(bh.biBitCount); + bh.biCompression=byteswap(bh.biCompression); + bh.biSizeImage=byteswap(bh.biSizeImage); + bh.biXPelsPerMeter=byteswap(bh.biXPelsPerMeter); + bh.biYPelsPerMeter=byteswap(bh.biYPelsPerMeter); + bh.biClrUsed=byteswap(bh.biClrUsed); + bh.biClrImportant=byteswap(bh.biClrImportant); + } + + if(bh.bfType!=0x4d42 || bh.bfOffBits<BMPHDRSIZE + || bh.biWidth<1 || bh.biHeight==0) + _throw("Corrupt bitmap header"); + if((bh.biBitCount!=24 && bh.biBitCount!=32) || bh.biCompression!=BI_RGB) + _throw("Only uncompessed RGB bitmaps are supported"); + + *w=bh.biWidth; *h=bh.biHeight; srcps=bh.biBitCount/8; + if(*h<0) {*h=-(*h); srcbottomup=0;} + srcpitch=(((*w)*srcps)+3)&(~3); + dstpitch=(((*w)*ps[f])+(align-1))&(~(align-1)); + + if(srcpitch*(*h)+bh.bfOffBits!=bh.bfSize) _throw("Corrupt bitmap header"); + if((tempbuf=(unsigned char *)malloc(srcpitch*(*h)))==NULL + || (*buf=(unsigned char *)malloc(dstpitch*(*h)))==NULL) + _throw("Memory allocation error"); + if(lseek(fd, (long)bh.bfOffBits, SEEK_SET)!=(long)bh.bfOffBits) + _throw(strerror(errno)); + _unix(bytesread=read(fd, tempbuf, srcpitch*(*h))); + if(bytesread!=srcpitch*(*h)) _throw("Read error"); + + pixelconvert(tempbuf, BMP_BGR, srcpitch, *buf, f, dstpitch, *w, *h, + srcbottomup!=dstbottomup); + + finally: + if(tempbuf) free(tempbuf); + if(fd!=-1) close(fd); + return retcode; +} + +#define writeme(fd, addr, size) \ + if((byteswritten=write(fd, addr, (size)))==-1) _throw(strerror(errno)); \ + if(byteswritten!=(size)) _throw("Write error"); + +int saveppm(char *filename, unsigned char *buf, int w, int h, + enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup) +{ + FILE *fs=NULL; int retcode=0; + unsigned char *tempbuf=NULL; + + if((fs=fopen(filename, "wb"))==NULL) _throw(strerror(errno)); + if(fprintf(fs, "P6\n")<1) _throw("Write error"); + if(fprintf(fs, "%d %d\n", w, h)<1) _throw("Write error"); + if(fprintf(fs, "255\n")<1) _throw("Write error"); + + if((tempbuf=(unsigned char *)malloc(w*h*3))==NULL) + _throw("Memory allocation error"); + + pixelconvert(buf, f, srcpitch, tempbuf, BMP_RGB, w*3, w, h, + srcbottomup); + + if((fwrite(tempbuf, w*h*3, 1, fs))!=1) _throw("Write error"); + + finally: + if(tempbuf) free(tempbuf); + if(fs) fclose(fs); + return retcode; +} + +int savebmp(char *filename, unsigned char *buf, int w, int h, + enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup) +{ + int fd=-1, byteswritten, dstpitch, retcode=0; + int flags=O_RDWR|O_CREAT|O_TRUNC; + unsigned char *tempbuf=NULL; char *temp; + bmphdr bh; int mode; + + #ifdef _WIN32 + flags|=O_BINARY; mode=_S_IREAD|_S_IWRITE; + #else + mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + #endif + if(!filename || !buf || w<1 || h<1 || f<0 || f>BMPPIXELFORMATS-1 || srcpitch<0) + _throw("bad argument to savebmp()"); + + if(srcpitch==0) srcpitch=w*ps[f]; + + if((temp=strrchr(filename, '.'))!=NULL) + { + if(!strcasecmp(temp, ".ppm")) + return saveppm(filename, buf, w, h, f, srcpitch, srcbottomup); + } + + _unix(fd=open(filename, flags, mode)); + dstpitch=((w*3)+3)&(~3); + + bh.bfType=0x4d42; + bh.bfSize=BMPHDRSIZE+dstpitch*h; + bh.bfReserved1=0; bh.bfReserved2=0; + bh.bfOffBits=BMPHDRSIZE; + bh.biSize=40; + bh.biWidth=w; bh.biHeight=h; + bh.biPlanes=0; bh.biBitCount=24; + bh.biCompression=BI_RGB; bh.biSizeImage=0; + bh.biXPelsPerMeter=0; bh.biYPelsPerMeter=0; + bh.biClrUsed=0; bh.biClrImportant=0; + + if(!littleendian()) + { + bh.bfType=byteswap16(bh.bfType); + bh.bfSize=byteswap(bh.bfSize); + bh.bfOffBits=byteswap(bh.bfOffBits); + bh.biSize=byteswap(bh.biSize); + bh.biWidth=byteswap(bh.biWidth); + bh.biHeight=byteswap(bh.biHeight); + bh.biPlanes=byteswap16(bh.biPlanes); + bh.biBitCount=byteswap16(bh.biBitCount); + bh.biCompression=byteswap(bh.biCompression); + bh.biSizeImage=byteswap(bh.biSizeImage); + bh.biXPelsPerMeter=byteswap(bh.biXPelsPerMeter); + bh.biYPelsPerMeter=byteswap(bh.biYPelsPerMeter); + bh.biClrUsed=byteswap(bh.biClrUsed); + bh.biClrImportant=byteswap(bh.biClrImportant); + } + + writeme(fd, &bh.bfType, sizeof(unsigned short)); + writeme(fd, &bh.bfSize, sizeof(unsigned int)); + writeme(fd, &bh.bfReserved1, sizeof(unsigned short)); + writeme(fd, &bh.bfReserved2, sizeof(unsigned short)); + writeme(fd, &bh.bfOffBits, sizeof(unsigned int)); + writeme(fd, &bh.biSize, sizeof(unsigned int)); + writeme(fd, &bh.biWidth, sizeof(int)); + writeme(fd, &bh.biHeight, sizeof(int)); + writeme(fd, &bh.biPlanes, sizeof(unsigned short)); + writeme(fd, &bh.biBitCount, sizeof(unsigned short)); + writeme(fd, &bh.biCompression, sizeof(unsigned int)); + writeme(fd, &bh.biSizeImage, sizeof(unsigned int)); + writeme(fd, &bh.biXPelsPerMeter, sizeof(int)); + writeme(fd, &bh.biYPelsPerMeter, sizeof(int)); + writeme(fd, &bh.biClrUsed, sizeof(unsigned int)); + writeme(fd, &bh.biClrImportant, sizeof(unsigned int)); + + if((tempbuf=(unsigned char *)malloc(dstpitch*h))==NULL) + _throw("Memory allocation error"); + + pixelconvert(buf, f, srcpitch, tempbuf, BMP_BGR, dstpitch, w, h, + !srcbottomup); + + if((byteswritten=write(fd, tempbuf, dstpitch*h))!=dstpitch*h) + _throw(strerror(errno)); + + finally: + if(tempbuf) free(tempbuf); + if(fd!=-1) close(fd); + return retcode; +} + +const char *bmpgeterr(void) +{ + return __bmperr; +} diff --git a/test/bmp.h b/test/bmp.h new file mode 100644 index 0000000..055b1ee --- /dev/null +++ b/test/bmp.h @@ -0,0 +1,49 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * Copyright (C)2011 D. R. Commander + * + * This library is free software and may be redistributed and/or modified under + * the terms of the wxWindows Library License, Version 3.1 or (at your option) + * any later version. The full license is in the LICENSE.txt file included + * with this distribution. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * wxWindows Library License for more details. +*/ + +// This provides rudimentary facilities for loading and saving true color +// BMP and PPM files + +#ifndef __BMP_H__ +#define __BMP_H__ + +#define BMPPIXELFORMATS 6 +enum BMPPIXELFORMAT {BMP_RGB=0, BMP_RGBX, BMP_BGR, BMP_BGRX, BMP_XBGR, BMP_XRGB}; + +#ifdef __cplusplus +extern "C" { +#endif + +// This will load a Windows bitmap from a file and return a buffer with the +// specified pixel format, scanline alignment, and orientation. The width and +// height are returned in w and h. + +int loadbmp(char *filename, unsigned char **buf, int *w, int *h, + enum BMPPIXELFORMAT f, int align, int dstbottomup); + +// This will save a buffer with the specified pixel format, pitch, orientation, +// width, and height as a 24-bit Windows bitmap or PPM (the filename determines +// which format to use) + +int savebmp(char *filename, unsigned char *buf, int w, int h, + enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup); + +const char *bmpgeterr(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/tjbench.c b/test/tjbench.c new file mode 100644 index 0000000..b1f343f --- /dev/null +++ b/test/tjbench.c @@ -0,0 +1,658 @@ +/* + * Copyright (C)2009-2012 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <errno.h> +#include "./bmp.h" +#include "./tjutil.h" +#include "./turbojpeg.h" + + +#define _throw(op, err) { \ + printf("ERROR in line %d while %s:\n%s\n", __LINE__, op, err); \ + retval=-1; goto bailout;} +#define _throwunix(m) _throw(m, strerror(errno)) +#define _throwtj(m) _throw(m, tjGetErrorStr()) +#define _throwbmp(m) _throw(m, bmpgeterr()) + +int flags=0, decomponly=0, quiet=0, dotile=0, pf=TJPF_BGR; +char *ext="ppm"; +const char *pixFormatStr[TJ_NUMPF]= +{ + "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY" +}; +const int bmpPF[TJ_NUMPF]= +{ + BMP_RGB, BMP_BGR, BMP_RGBX, BMP_BGRX, BMP_XBGR, BMP_XRGB, -1 +}; +const char *subNameLong[TJ_NUMSAMP]= +{ + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0" +}; +const char *subName[NUMSUBOPT]={"444", "422", "420", "GRAY", "440"}; +tjscalingfactor *scalingfactors=NULL, sf={1, 1}; int nsf=0; +double benchtime=5.0; + + +char *sigfig(double val, int figs, char *buf, int len) +{ + char format[80]; + int digitsafterdecimal=figs-(int)ceil(log10(fabs(val))); + if(digitsafterdecimal<1) snprintf(format, 80, "%%.0f"); + else snprintf(format, 80, "%%.%df", digitsafterdecimal); + snprintf(buf, len, format, val); + return buf; +} + + +/* 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 bufsize; + 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; + + if(jpegqual>0) + { + snprintf(qualstr, 6, "_Q%d", jpegqual); + qualstr[5]=0; + } + + if((handle=tjInitDecompress())==NULL) + _throwtj("executing tjInitDecompress()"); + + bufsize=pitch*scaledh; + 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(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; + 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(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, bmpPF[pf], pitch, + (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, bmpPF[pf], pitch, + (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; +} + + +void dotest(unsigned char *srcbuf, int w, int h, int subsamp, int jpegqual, + char *filename) +{ + char tempstr[1024], tempstr2[80]; + FILE *file=NULL; tjhandle handle=NULL; + unsigned char **jpegbuf=NULL, *tmpbuf=NULL, *srcptr, *srcptr2; + double start, elapsed; + int totaljpegsize=0, row, col, i, tilew=w, tileh=h, retval=0; + unsigned long *jpegsize=NULL; + int ps=tjPixelSize[pf], ntilesw=1, ntilesh=1, pitch=w*ps; + + if((tmpbuf=(unsigned char *)malloc(pitch*h)) == NULL) + _throwunix("allocating temporary image buffer"); + + if(!quiet) + printf(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "Bottom-up":"Top-down", subNameLong[subsamp], + jpegqual); + + for(tilew=dotile? 8:w, tileh=dotile? 8:h; ; tilew*=2, tileh*=2) + { + if(tilew>w) tilew=w; if(tileh>h) tileh=h; + ntilesw=(w+tilew-1)/tilew; ntilesh=(h+tileh-1)/tileh; + + if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *) + *ntilesw*ntilesh))==NULL) + _throwunix("allocating JPEG tile array"); + memset(jpegbuf, 0, sizeof(unsigned char *)*ntilesw*ntilesh); + if((jpegsize=(unsigned long *)malloc(sizeof(unsigned long) + *ntilesw*ntilesh))==NULL) + _throwunix("allocating JPEG size array"); + memset(jpegsize, 0, sizeof(unsigned long)*ntilesw*ntilesh); + + for(i=0; i<ntilesw*ntilesh; i++) + { + if((jpegbuf[i]=(unsigned char *)malloc(tjBufSize(tilew, tileh, + subsamp)))==NULL) + _throwunix("allocating JPEG tiles"); + } + + /* Compression test */ + if(quiet==1) + printf("%s\t%s\t%s\t%d\t", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "BU":"TD", subNameLong[subsamp], jpegqual); + for(i=0; i<h; i++) + memcpy(&tmpbuf[pitch*i], &srcbuf[w*ps*i], w*ps); + if((handle=tjInitCompress())==NULL) + _throwtj("executing tjInitCompress()"); + + /* Execute once to preload cache */ + if(tjCompress2(handle, srcbuf, tilew, pitch, tileh, pf, &jpegbuf[0], + &jpegsize[0], subsamp, jpegqual, flags)==-1) + _throwtj("executing tjCompress2()"); + + /* Benchmark */ + for(i=0, start=gettime(); (elapsed=gettime()-start)<benchtime; i++) + { + int tile=0; + totaljpegsize=0; + for(row=0, srcptr=srcbuf; row<ntilesh; row++, srcptr+=pitch*tileh) + { + for(col=0, srcptr2=srcptr; col<ntilesw; col++, tile++, + srcptr2+=ps*tilew) + { + int width=min(tilew, w-col*tilew); + int height=min(tileh, h-row*tileh); + if(tjCompress2(handle, srcptr2, width, pitch, height, pf, + &jpegbuf[tile], &jpegsize[tile], subsamp, jpegqual, flags)==-1) + _throwtj("executing tjCompress()2"); + totaljpegsize+=jpegsize[tile]; + } + } + } + + if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()"); + handle=NULL; + + if(quiet==1) printf("%-4d %-4d\t", tilew, tileh); + if(quiet) + { + printf("%s%c%s%c", + sigfig((double)(w*h)/1000000.*(double)i/elapsed, 4, tempstr, 1024), + quiet==2? '\n':'\t', + sigfig((double)(w*h*ps)/(double)totaljpegsize, 4, tempstr2, 80), + quiet==2? '\n':'\t'); + } + else + { + printf("\n%s size: %d x %d\n", dotile? "Tile":"Image", tilew, + tileh); + printf("C--> Frame rate: %f fps\n", (double)i/elapsed); + printf(" Output image size: %d bytes\n", totaljpegsize); + printf(" Compression ratio: %f:1\n", + (double)(w*h*ps)/(double)totaljpegsize); + printf(" Source throughput: %f Megapixels/sec\n", + (double)(w*h)/1000000.*(double)i/elapsed); + printf(" Output bit stream: %f Megabits/sec\n", + (double)totaljpegsize*8./1000000.*(double)i/elapsed); + } + if(tilew==w && tileh==h) + { + snprintf(tempstr, 1024, "%s_%s_Q%d.jpg", filename, subName[subsamp], + jpegqual); + if((file=fopen(tempstr, "wb"))==NULL) + _throwunix("opening reference image"); + if(fwrite(jpegbuf[0], jpegsize[0], 1, file)!=1) + _throwunix("writing reference image"); + fclose(file); file=NULL; + if(!quiet) printf("Reference image written to %s\n", tempstr); + } + + /* Decompression test */ + if(decomptest(srcbuf, jpegbuf, jpegsize, tmpbuf, w, h, subsamp, jpegqual, + filename, tilew, tileh)==-1) + goto bailout; + + for(i=0; i<ntilesw*ntilesh; i++) + { + if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL; + } + free(jpegbuf); jpegbuf=NULL; + free(jpegsize); jpegsize=NULL; + + if(tilew==w && tileh==h) break; + } + + bailout: + if(file) {fclose(file); file=NULL;} + if(jpegbuf) + { + for(i=0; i<ntilesw*ntilesh; i++) + { + if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL; + } + free(jpegbuf); jpegbuf=NULL; + } + if(jpegsize) {free(jpegsize); jpegsize=NULL;} + if(tmpbuf) {free(tmpbuf); tmpbuf=NULL;} + if(handle) {tjDestroy(handle); handle=NULL;} + return; +} + + +void dodecomptest(char *filename) +{ + FILE *file=NULL; tjhandle handle=NULL; + unsigned char **jpegbuf=NULL, *srcbuf=NULL; + unsigned long *jpegsize=NULL, srcsize; + int w=0, h=0, subsamp=-1, _w, _h, _tilew, _tileh, _subsamp; + char *temp=NULL; + int i, tilew, tileh, ntilesw=1, ntilesh=1, retval=0; + + if((file=fopen(filename, "rb"))==NULL) + _throwunix("opening file"); + if(fseek(file, 0, SEEK_END)<0 || (srcsize=ftell(file))<0) + _throwunix("determining file size"); + if((srcbuf=(unsigned char *)malloc(srcsize))==NULL) + _throwunix("allocating memory"); + if(fseek(file, 0, SEEK_SET)<0) + _throwunix("setting file position"); + if(fread(srcbuf, srcsize, 1, file)<1) + _throwunix("reading JPEG data"); + fclose(file); file=NULL; + + temp=strrchr(filename, '.'); + if(temp!=NULL) *temp='\0'; + + if((handle=tjInitDecompress())==NULL) + _throwtj("executing tjInitDecompress()"); + if(tjDecompressHeader2(handle, srcbuf, srcsize, &w, &h, &subsamp)==-1) + _throwtj("executing tjDecompressHeader2()"); + + if(quiet==1) + { + printf("All performance values in Mpixels/sec\n\n"); + printf("Bitmap\tBitmap\tJPEG\t%s %s \tXform\tComp\tDecomp\n", + dotile? "Tile ":"Image", dotile? "Tile ":"Image"); + printf("Format\tOrder\tSubsamp\tWidth Height\tPerf \tRatio\tPerf\n\n"); + } + else if(!quiet) + { + printf(">>>>> JPEG %s --> %s (%s) <<<<<\n", subNameLong[subsamp], + pixFormatStr[pf], (flags&TJFLAG_BOTTOMUP)? "Bottom-up":"Top-down"); + } + + for(tilew=dotile? 16:w, tileh=dotile? 16:h; ; tilew*=2, tileh*=2) + { + if(tilew>w) tilew=w; if(tileh>h) tileh=h; + ntilesw=(w+tilew-1)/tilew; ntilesh=(h+tileh-1)/tileh; + + if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *) + *ntilesw*ntilesh))==NULL) + _throwunix("allocating JPEG tile array"); + memset(jpegbuf, 0, sizeof(unsigned char *)*ntilesw*ntilesh); + if((jpegsize=(unsigned long *)malloc(sizeof(unsigned long) + *ntilesw*ntilesh))==NULL) + _throwunix("allocating JPEG size array"); + memset(jpegsize, 0, sizeof(unsigned long)*ntilesw*ntilesh); + + for(i=0; i<ntilesw*ntilesh; i++) + { + if((jpegbuf[i]=(unsigned char *)malloc(tjBufSize(tilew, tileh, + subsamp)))==NULL) + _throwunix("allocating JPEG tiles"); + } + + _w=w; _h=h; _tilew=tilew; _tileh=tileh; + if(!quiet) + { + printf("\n%s size: %d x %d", dotile? "Tile":"Image", _tilew, + _tileh); + if(sf.num!=1 || sf.denom!=1) + printf(" --> %d x %d", TJSCALED(_w, sf), TJSCALED(_h, sf)); + printf("\n"); + } + else if(quiet==1) + { + printf("%s\t%s\t%s\t", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "BU":"TD", subNameLong[subsamp]); + printf("%-4d %-4d\t", tilew, tileh); + } + + _subsamp=subsamp; + if(quiet==1) printf("N/A\tN/A\t"); + jpegsize[0]=srcsize; + memcpy(jpegbuf[0], srcbuf, srcsize); + + if(w==tilew) _tilew=_w; + if(h==tileh) _tileh=_h; + if(decomptest(NULL, jpegbuf, jpegsize, NULL, _w, _h, _subsamp, 0, + filename, _tilew, _tileh)==-1) + goto bailout; + else if(quiet==1) printf("N/A\n"); + + for(i=0; i<ntilesw*ntilesh; i++) + { + free(jpegbuf[i]); jpegbuf[i]=NULL; + } + free(jpegbuf); jpegbuf=NULL; + if(jpegsize) {free(jpegsize); jpegsize=NULL;} + + if(tilew==w && tileh==h) break; + } + + bailout: + if(file) {fclose(file); file=NULL;} + if(jpegbuf) + { + for(i=0; i<ntilesw*ntilesh; i++) + { + if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL; + } + free(jpegbuf); jpegbuf=NULL; + } + if(jpegsize) {free(jpegsize); jpegsize=NULL;} + if(srcbuf) {free(srcbuf); srcbuf=NULL;} + if(handle) {tjDestroy(handle); handle=NULL;} + return; +} + + +void usage(char *progname) +{ + int i; + printf("USAGE: %s\n", progname); + printf(" <Inputfile (BMP|PPM)> <%% Quality> [options]\n\n"); + printf(" %s\n", progname); + printf(" <Inputfile (JPG)> [options]\n\n"); + printf("Options:\n\n"); + printf("-bmp = Generate output images in Windows Bitmap format (default=PPM)\n"); + printf("-bottomup = Test bottom-up compression/decompression\n"); + printf("-tile = Test performance of the codec when the image is encoded as separate\n"); + printf(" tiles of varying sizes.\n"); + printf("-forcemmx, -forcesse, -forcesse2, -forcesse3 =\n"); + printf(" Force MMX, SSE, SSE2, or SSE3 code paths in the underlying codec\n"); + printf("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =\n"); + printf(" Test the specified color conversion path in the codec (default: BGR)\n"); + printf("-fastupsample = Use fast, inaccurate upsampling code to perform 4:2:2 and 4:2:0\n"); + printf(" YUV decoding\n"); + printf("-quiet = Output results in tabular rather than verbose format\n"); + printf("-scale M/N = scale down the width/height of the decompressed JPEG image by a\n"); + printf(" factor of M/N (M/N = "); + for(i=0; i<nsf; i++) + { + printf("%d/%d", scalingfactors[i].num, scalingfactors[i].denom); + if(nsf==2 && i!=nsf-1) printf(" or "); + else if(nsf>2) + { + if(i!=nsf-1) printf(", "); + if(i==nsf-2) printf("or "); + } + } + printf(")\n"); + printf("-benchtime <t> = Run each benchmark for at least <t> seconds (default = 5.0)\n\n"); + printf("NOTE: If the quality is specified as a range (e.g. 90-100), a separate\n"); + printf("test will be performed for all quality values in the range.\n\n"); + exit(1); +} + + +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(!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; + } + 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], "-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], "-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(!decomponly) + { + if(loadbmp(argv[1], &srcbuf, &w, &h, bmpPF[pf], 1, + (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; +} diff --git a/test/tjunittest.c b/test/tjunittest.c new file mode 100644 index 0000000..c9960eb --- /dev/null +++ b/test/tjunittest.c @@ -0,0 +1,461 @@ +/* + * Copyright (C)2009-2012 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This program tests the various code paths in the TurboJPEG C Wrapper + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "./tjutil.h" +#include "./turbojpeg.h" +#ifdef _WIN32 + #include <time.h> + #define random() rand() +#endif + + +#define _throwtj() {printf("TurboJPEG ERROR:\n%s\n", tjGetErrorStr()); \ + bailout();} +#define _tj(f) {if((f)==-1) _throwtj();} +#define _throw(m) {printf("ERROR: %s\n", m); bailout();} + +const char *subNameLong[TJ_NUMSAMP]= +{ + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0" +}; +const char *subName[TJ_NUMSAMP]={"444", "422", "420", "GRAY", "440"}; + +const char *pixFormatStr[TJ_NUMPF]= +{ + "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale", + "RGBA", "BGRA", "ABGR", "ARGB" +}; + +const int alphaOffset[TJ_NUMPF] = {-1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0}; + +const int _3byteFormats[]={TJPF_RGB, TJPF_BGR}; +const int _4byteFormats[]={TJPF_RGBX, TJPF_BGRX, TJPF_XBGR, TJPF_XRGB}; +const int _onlyGray[]={TJPF_GRAY}; +const int _onlyRGB[]={TJPF_RGB}; + +int exitStatus=0; +#define bailout() {exitStatus=-1; goto bailout;} + + +void initBuf(unsigned char *buf, int w, int h, int pf, int flags) +{ + int roffset=tjRedOffset[pf]; + int goffset=tjGreenOffset[pf]; + int boffset=tjBlueOffset[pf]; + int ps=tjPixelSize[pf]; + int index, row, col, halfway=16; + + memset(buf, 0, w*h*ps); + if(pf==TJPF_GRAY) + { + for(row=0; row<h; row++) + { + for(col=0; col<w; col++) + { + if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col; + else index=row*w+col; + if(((row/8)+(col/8))%2==0) buf[index]=(row<halfway)? 255:0; + else buf[index]=(row<halfway)? 76:226; + } + } + } + else + { + for(row=0; row<h; row++) + { + for(col=0; col<w; col++) + { + if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col; + else index=row*w+col; + if(((row/8)+(col/8))%2==0) + { + if(row<halfway) + { + buf[index*ps+roffset]=255; + buf[index*ps+goffset]=255; + buf[index*ps+boffset]=255; + } + } + else + { + buf[index*ps+roffset]=255; + if(row>=halfway) buf[index*ps+goffset]=255; + } + } + } + } +} + + +#define checkval(v, cv) { \ + if(v<cv-1 || v>cv+1) { \ + printf("\nComp. %s at %d,%d should be %d, not %d\n", \ + #v, row, col, cv, v); \ + retval=0; exitStatus=-1; goto bailout; \ + }} + +#define checkval0(v) { \ + if(v>1) { \ + printf("\nComp. %s at %d,%d should be 0, not %d\n", #v, row, col, v); \ + retval=0; exitStatus=-1; goto bailout; \ + }} + +#define checkval255(v) { \ + if(v<254) { \ + printf("\nComp. %s at %d,%d should be 255, not %d\n", #v, row, col, v); \ + retval=0; exitStatus=-1; goto bailout; \ + }} + + +int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp, + tjscalingfactor sf, int flags) +{ + int roffset=tjRedOffset[pf]; + int goffset=tjGreenOffset[pf]; + int boffset=tjBlueOffset[pf]; + int aoffset=alphaOffset[pf]; + int ps=tjPixelSize[pf]; + int index, row, col, retval=1; + int halfway=16*sf.num/sf.denom; + int blocksize=8*sf.num/sf.denom; + + for(row=0; row<h; row++) + { + for(col=0; col<w; col++) + { + unsigned char r, g, b, a; + if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col; + else index=row*w+col; + r=buf[index*ps+roffset]; + g=buf[index*ps+goffset]; + b=buf[index*ps+boffset]; + a=aoffset>=0? buf[index*ps+aoffset]:0xFF; + if(((row/blocksize)+(col/blocksize))%2==0) + { + if(row<halfway) + { + checkval255(r); checkval255(g); checkval255(b); + } + else + { + checkval0(r); checkval0(g); checkval0(b); + } + } + else + { + if(subsamp==TJSAMP_GRAY) + { + if(row<halfway) + { + checkval(r, 76); checkval(g, 76); checkval(b, 76); + } + else + { + checkval(r, 226); checkval(g, 226); checkval(b, 226); + } + } + else + { + if(row<halfway) + { + checkval255(r); checkval0(g); checkval0(b); + } + else + { + checkval255(r); checkval255(g); checkval0(b); + } + } + } + checkval255(a); + } + } + + bailout: + if(retval==0) + { + printf("\n"); + for(row=0; row<h; row++) + { + for(col=0; col<w; col++) + { + printf("%.3d/%.3d/%.3d ", buf[(row*w+col)*ps+roffset], + buf[(row*w+col)*ps+goffset], buf[(row*w+col)*ps+boffset]); + } + printf("\n"); + } + } + return retval; +} + + +void writeJPEG(unsigned char *jpegBuf, unsigned long jpegSize, char *filename) +{ + FILE *file=fopen(filename, "wb"); + if(!file || fwrite(jpegBuf, jpegSize, 1, file)!=1) + { + printf("ERROR: Could not write to %s.\n%s\n", filename, strerror(errno)); + bailout(); + } + + bailout: + if(file) fclose(file); +} + + +void compTest(tjhandle handle, unsigned char **dstBuf, + unsigned long *dstSize, int w, int h, int pf, char *basename, + int subsamp, int jpegQual, int flags) +{ + char tempStr[1024]; unsigned char *srcBuf=NULL; + double t; + + printf("%s %s -> %s Q%d ... ", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ", subNameLong[subsamp], + jpegQual); + + if((srcBuf=(unsigned char *)malloc(w*h*tjPixelSize[pf]))==NULL) + _throw("Memory allocation failure"); + initBuf(srcBuf, w, h, pf, flags); + if(*dstBuf && *dstSize>0) memset(*dstBuf, 0, *dstSize); + + t=gettime(); + *dstSize=tjBufSize(w, h, subsamp); + _tj(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp, + jpegQual, flags)); + t=gettime()-t; + + snprintf(tempStr, 1024, "%s_enc_%s_%s_%s_Q%d.jpg", basename, + pixFormatStr[pf], (flags&TJFLAG_BOTTOMUP)? "BU":"TD", subName[subsamp], + jpegQual); + writeJPEG(*dstBuf, *dstSize, tempStr); + printf("Done."); + printf(" %f ms\n Result in %s\n", t*1000., tempStr); + + bailout: + if(srcBuf) free(srcBuf); +} + + +void _decompTest(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, int w, int h, int pf, char *basename, int subsamp, + int flags, tjscalingfactor sf) +{ + unsigned char *dstBuf=NULL; + int _hdrw=0, _hdrh=0, _hdrsubsamp=-1; double t; + int scaledWidth=TJSCALED(w, sf); + int scaledHeight=TJSCALED(h, sf); + unsigned long dstSize=0; + + printf("JPEG -> %s %s ", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down "); + if(sf.num!=1 || sf.denom!=1) + printf("%d/%d ... ", sf.num, sf.denom); + else printf("... "); + + _tj(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh, + &_hdrsubsamp)); + if(_hdrw!=w || _hdrh!=h || _hdrsubsamp!=subsamp) + _throw("Incorrect JPEG header"); + + dstSize=scaledWidth*scaledHeight*tjPixelSize[pf]; + if((dstBuf=(unsigned char *)malloc(dstSize))==NULL) + _throw("Memory allocation failure"); + memset(dstBuf, 0, dstSize); + + t=gettime(); + _tj(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth, 0, + scaledHeight, pf, flags)); + t=gettime()-t; + + if(checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, flags)) + printf("Passed."); + else printf("FAILED!"); + printf(" %f ms\n", t*1000.); + + bailout: + if(dstBuf) free(dstBuf); +} + + +void decompTest(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, int w, int h, int pf, char *basename, int subsamp, + int flags) +{ + int i, n=0; + tjscalingfactor *sf=tjGetScalingFactors(&n), sf1={1, 1}; + if(!sf || !n) _throwtj(); + + if((subsamp==TJSAMP_444 || subsamp==TJSAMP_GRAY)) + { + for(i=0; i<n; i++) + _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, + flags, sf[i]); + } + else + _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, flags, + sf1); + + bailout: + printf("\n"); +} + + +void doTest(int w, int h, const int *formats, int nformats, int subsamp, + char *basename) +{ + tjhandle chandle=NULL, dhandle=NULL; + unsigned char *dstBuf=NULL; + unsigned long size=0; int pfi, pf, i; + + size=tjBufSize(w, h, subsamp); + if((dstBuf=(unsigned char *)malloc(size))==NULL) + _throw("Memory allocation failure."); + + if((chandle=tjInitCompress())==NULL || (dhandle=tjInitDecompress())==NULL) + _throwtj(); + + for(pfi=0; pfi<nformats; pfi++) + { + for(i=0; i<2; i++) + { + int flags=0; + if(subsamp==TJSAMP_422 || subsamp==TJSAMP_420 || subsamp==TJSAMP_440) + flags|=TJFLAG_FASTUPSAMPLE; + if(i==1) flags|=TJFLAG_BOTTOMUP; + pf=formats[pfi]; + compTest(chandle, &dstBuf, &size, w, h, pf, basename, subsamp, 100, + flags); + decompTest(dhandle, dstBuf, size, w, h, pf, basename, subsamp, + flags); + if(pf>=TJPF_RGBX && pf<=TJPF_XRGB) + decompTest(dhandle, dstBuf, size, w, h, pf+(TJPF_RGBA-TJPF_RGBX), + basename, subsamp, flags); + } + } + + bailout: + if(chandle) tjDestroy(chandle); + if(dhandle) tjDestroy(dhandle); + + if(dstBuf) free(dstBuf); +} + + +void bufSizeTest(void) +{ + int w, h, i, subsamp; + unsigned char *srcBuf=NULL, *jpegBuf=NULL; + tjhandle handle=NULL; + unsigned long jpegSize=0; + + if((handle=tjInitCompress())==NULL) _throwtj(); + + printf("Buffer size regression test\n"); + for(subsamp=0; subsamp<TJ_NUMSAMP; subsamp++) + { + for(w=1; w<48; w++) + { + int maxh=(w==1)? 2048:48; + for(h=1; h<maxh; h++) + { + if(h%100==0) printf("%.4d x %.4d\b\b\b\b\b\b\b\b\b\b\b", w, h); + if((srcBuf=(unsigned char *)malloc(w*h*4))==NULL) + _throw("Memory allocation failure"); + if((jpegBuf=(unsigned char *)malloc(tjBufSize(w, h, subsamp))) + ==NULL) + _throw("Memory allocation failure"); + jpegSize=tjBufSize(w, h, subsamp); + + for(i=0; i<w*h*4; i++) + { + if(random()<RAND_MAX/2) srcBuf[i]=0; + else srcBuf[i]=255; + } + + _tj(tjCompress2(handle, srcBuf, w, 0, h, TJPF_BGRX, &jpegBuf, + &jpegSize, subsamp, 100, 0)); + free(srcBuf); srcBuf=NULL; + free(jpegBuf); jpegBuf=NULL; + + if((srcBuf=(unsigned char *)malloc(h*w*4))==NULL) + _throw("Memory allocation failure"); + if((jpegBuf=(unsigned char *)malloc(tjBufSize(h, w, subsamp))) + ==NULL) + _throw("Memory allocation failure"); + jpegSize=tjBufSize(h, w, subsamp); + + for(i=0; i<h*w*4; i++) + { + if(random()<RAND_MAX/2) srcBuf[i]=0; + else srcBuf[i]=255; + } + + _tj(tjCompress2(handle, srcBuf, h, 0, w, TJPF_BGRX, &jpegBuf, + &jpegSize, subsamp, 100, 0)); + free(srcBuf); srcBuf=NULL; + free(jpegBuf); jpegBuf=NULL; + } + } + } + printf("Done. \n"); + + bailout: + if(srcBuf) free(srcBuf); + if(jpegBuf) free(jpegBuf); + if(handle) tjDestroy(handle); +} + + +int main(int argc, char *argv[]) +{ + #ifdef _WIN32 + srand((unsigned int)time(NULL)); + #endif + doTest(35, 39, _3byteFormats, 2, TJSAMP_444, "test"); + doTest(39, 41, _4byteFormats, 4, TJSAMP_444, "test"); + doTest(41, 35, _3byteFormats, 2, TJSAMP_422, "test"); + doTest(35, 39, _4byteFormats, 4, TJSAMP_422, "test"); + doTest(39, 41, _3byteFormats, 2, TJSAMP_420, "test"); + doTest(41, 35, _4byteFormats, 4, TJSAMP_420, "test"); + doTest(35, 39, _3byteFormats, 2, TJSAMP_440, "test"); + doTest(39, 41, _4byteFormats, 4, TJSAMP_440, "test"); + doTest(35, 39, _onlyGray, 1, TJSAMP_GRAY, "test"); + doTest(39, 41, _3byteFormats, 2, TJSAMP_GRAY, "test"); + doTest(41, 35, _4byteFormats, 4, TJSAMP_GRAY, "test"); + bufSizeTest(); + + return exitStatus; +} diff --git a/test/tjutil.c b/test/tjutil.c new file mode 100644 index 0000000..6618d15 --- /dev/null +++ b/test/tjutil.c @@ -0,0 +1,66 @@ +/* + * Copyright (C)2011 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + +#include <windows.h> + +static double getfreq(void) +{ + LARGE_INTEGER freq; + if(!QueryPerformanceFrequency(&freq)) return 0.0; + return (double)freq.QuadPart; +} + +static double f=-1.0; + +double gettime(void) +{ + LARGE_INTEGER t; + if(f<0.0) f=getfreq(); + if(f==0.0) return (double)GetTickCount()/1000.; + else + { + QueryPerformanceCounter(&t); + return (double)t.QuadPart/f; + } +} + +#else + +#include <stdlib.h> +#include <sys/time.h> + +double gettime(void) +{ + struct timeval tv; + if(gettimeofday(&tv, NULL)<0) return 0.0; + else return (double)tv.tv_sec+((double)tv.tv_usec/1000000.); +} + +#endif diff --git a/test/tjutil.h b/test/tjutil.h new file mode 100644 index 0000000..bdad348 --- /dev/null +++ b/test/tjutil.h @@ -0,0 +1,47 @@ +/* + * Copyright (C)2011 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + #ifndef __MINGW32__ + #include <stdio.h> + #define snprintf(str, n, format, ...) \ + _snprintf_s(str, n, _TRUNCATE, format, __VA_ARGS__) + #endif + #define strcasecmp stricmp + #define strncasecmp strnicmp +#endif + +#ifndef min + #define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef max + #define max(a,b) ((a)>(b)?(a):(b)) +#endif + +extern double gettime(void); |