From e34390b8f76c5b5ab6d16df2e3283f89c9a77b96 Mon Sep 17 00:00:00 2001 From: DRC Date: Fri, 3 Apr 2009 12:04:24 +0000 Subject: [PATCH] Include low-level unit tests borrowed from VirtualGL git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@3745 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- common/jpeg/Makefile.am | 13 +- common/jpeg/bmp.c | 370 +++++++++++++++++++++++++++++++++++++ common/jpeg/bmp.h | 48 +++++ common/jpeg/jpegut.c | 384 +++++++++++++++++++++++++++++++++++++++ common/jpeg/jpgtest.cxx | 382 ++++++++++++++++++++++++++++++++++++++ common/jpeg/rrtimer.h | 114 ++++++++++++ common/jpeg/rrutil.h | 81 +++++++++ common/jpeg/turbojpeg.h | 229 +++++++++++++++++++++++ common/jpeg/turbojpegl.c | 352 +++++++++++++++++++++++++++++++++++ 9 files changed, 1972 insertions(+), 1 deletion(-) create mode 100644 common/jpeg/bmp.c create mode 100644 common/jpeg/bmp.h create mode 100644 common/jpeg/jpegut.c create mode 100644 common/jpeg/jpgtest.cxx create mode 100644 common/jpeg/rrtimer.h create mode 100644 common/jpeg/rrutil.h create mode 100644 common/jpeg/turbojpeg.h create mode 100644 common/jpeg/turbojpegl.c diff --git a/common/jpeg/Makefile.am b/common/jpeg/Makefile.am index 1d868b7b..93120789 100644 --- a/common/jpeg/Makefile.am +++ b/common/jpeg/Makefile.am @@ -1,7 +1,7 @@ noinst_LTLIBRARIES = libjpeg.la HDRS = jchuff.h jdct.h jdhuff.h jerror.h jinclude.h jmemsys.h jmorecfg.h \ - jpegint.h jpeglib.h jversion.h jsimd.h jsimddct.h + jpegint.h jpeglib.h jversion.h jsimd.h jsimddct.h turbojpeg.h libjpeg_la_SOURCES = $(HDRS) jcapimin.c jcapistd.c jccoefct.c jccolor.c \ jcdctmgr.c jchuff.c jcinit.c jcmainct.c jcmarker.c jcmaster.c \ @@ -20,3 +20,14 @@ libjpeg_la_LIBADD = simd/libsimd.la endif +TSTHDRS = turbojpeg.h rrutil.h rrtimer.h + +bin_PROGRAMS = jpgtest jpegut + +jpgtest_SOURCES = $(TSTHDRS) jpgtest.cxx bmp.c turbojpegl.c + +jpgtest_LDADD = $(top_srcdir)/libjpeg.la + +jpegut_SOURCES = $(TSTHDRS) jpegut.c bmp.c turbojpegl.c + +jpegut_LDADD = $(top_srcdir)/libjpeg.la diff --git a/common/jpeg/bmp.c b/common/jpeg/bmp.c new file mode 100644 index 00000000..3ccc8772 --- /dev/null +++ b/common/jpeg/bmp.c @@ -0,0 +1,370 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 + #include +#else + #include +#endif +#include "./rrutil.h" +#include "./bmp.h" + +#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; jBMPPIXELFORMATS-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.bfOffBitsBMPPIXELFORMATS-1 || srcpitch<0) + _throw("bad argument to savebmp()"); + + if(srcpitch==0) srcpitch=w*ps[f]; + + if((temp=strrchr(filename, '.'))!=NULL) + { + if(!stricmp(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/common/jpeg/bmp.h b/common/jpeg/bmp.h new file mode 100644 index 00000000..437d3275 --- /dev/null +++ b/common/jpeg/bmp.h @@ -0,0 +1,48 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * + * 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_RGBA, BMP_BGR, BMP_BGRA, BMP_ABGR, BMP_ARGB}; + +#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/common/jpeg/jpegut.c b/common/jpeg/jpegut.c new file mode 100644 index 00000000..777cd3dd --- /dev/null +++ b/common/jpeg/jpegut.c @@ -0,0 +1,384 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * Copyright (C)2009 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 +#include +#include +#include "./rrtimer.h" +#include "./turbojpeg.h" + +#define _catch(f) {if((f)==-1) {printf("TJPEG: %s\n", tjGetErrorStr()); goto finally;}} + +const char *_subnamel[NUMSUBOPT]={"4:4:4", "4:2:2", "4:1:1", "GRAY"}; +const char *_subnames[NUMSUBOPT]={"444", "422", "411", "GRAY"}; + +int pixels[9][3]= +{ + {0, 255, 0}, + {255, 0, 255}, + {255, 255, 0}, + {0, 0, 255}, + {0, 255, 255}, + {255, 0, 0}, + {255, 255, 255}, + {0, 0, 0}, + {255, 0, 0} +}; + +void initbuf(unsigned char *buf, int w, int h, int ps, int flags) +{ + int roffset=(flags&TJ_BGR)?2:0, goffset=1, boffset=(flags&TJ_BGR)?0:2, i, + _i, j; + if(flags&TJ_ALPHAFIRST) {roffset++; goffset++; boffset++;} + memset(buf, 0, w*h*ps); + for(_i=0; _i<16; _i++) + { + if(flags&TJ_BOTTOMUP) i=h-_i-1; else i=_i; + for(j=0; j78 || g<74 || g>78 || b<74 || b>78) return 0; + } + } + } + for(_i=16; _i2 || g>2 || b>2) return 0; + } + else + { + if(r<224 || r>228 || g<224 || g>228 || b<224 || b>228) return 0; + } + } + } + } + else + { + for(_i=0; _i<16; _i++) + { + if(flags&TJ_BOTTOMUP) i=h-_i-1; else i=_i; + for(j=0; j2) return 0; + if(buf[(w*i+j)*ps+boffset]>2) return 0; + } + } + } + for(_i=16; _i2) return 0; + if(((_i/8)+(j/8))%2==0) + { + if(buf[(w*i+j)*ps+roffset]>2) return 0; + if(buf[(w*i+j)*ps+goffset]>2) return 0; + } + else + { + if(buf[(w*i+j)*ps+roffset]<253) return 0; + if(buf[(w*i+j)*ps+goffset]<253) return 0; + } + } + } + } + return 1; +} + +void writejpeg(unsigned char *jpegbuf, unsigned long jpgbufsize, char *filename) +{ + FILE *outfile=NULL; + if((outfile=fopen(filename, "wb"))==NULL) + { + printf("ERROR: Could not open %s for writing.\n", filename); + goto finally; + } + if(fwrite(jpegbuf, jpgbufsize, 1, outfile)!=1) + { + printf("ERROR: Could not write to %s.\n", filename); + goto finally; + } + + finally: + if(outfile) fclose(outfile); +} + +void gentestjpeg(tjhandle hnd, unsigned char *jpegbuf, unsigned long *size, + int w, int h, int ps, char *basefilename, int subsamp, int qual, int flags) +{ + char tempstr[1024]; unsigned char *bmpbuf=NULL; + const char *pixformat; double t; + + if(flags&TJ_BGR) + { + if(ps==3) pixformat="BGR"; + else {if(flags&TJ_ALPHAFIRST) pixformat="ABGR"; else pixformat="BGRA";} + } + else + { + if(ps==3) pixformat="RGB"; + else {if(flags&TJ_ALPHAFIRST) pixformat="ARGB"; else pixformat="RGBA";} + } + printf("%s %s -> %s Q%d ... ", pixformat, + (flags&TJ_BOTTOMUP)?"Bottom-Up":"Top-Down ", _subnamel[subsamp], qual); + + if((bmpbuf=(unsigned char *)malloc(w*h*ps+1))==NULL) + { + printf("ERROR: Could not allocate buffer\n"); goto finally; + } + initbuf(bmpbuf, w, h, ps, flags); + memset(jpegbuf, 0, TJBUFSIZE(w, h)); + + t=rrtime(); + _catch(tjCompress(hnd, bmpbuf, w, 0, h, ps, jpegbuf, size, subsamp, qual, flags)); + t=rrtime()-t; + + sprintf(tempstr, "%s_enc_%s_%s_%sQ%d.jpg", basefilename, pixformat, + (flags&TJ_BOTTOMUP)? "BU":"TD", _subnames[subsamp], qual); + writejpeg(jpegbuf, *size, tempstr); + printf("Done. %f ms\n Result in %s\n", t*1000., tempstr); + + finally: + if(bmpbuf) free(bmpbuf); +} + +void gentestbmp(tjhandle hnd, unsigned char *jpegbuf, unsigned long jpegsize, + int w, int h, int ps, char *basefilename, int subsamp, int qual, int flags) +{ + unsigned char *bmpbuf=NULL; + const char *pixformat; int _w=0, _h=0; double t; + + if(flags&TJ_BGR) + { + if(ps==3) pixformat="BGR"; + else {if(flags&TJ_ALPHAFIRST) pixformat="ABGR"; else pixformat="BGRA";} + } + else + { + if(ps==3) pixformat="RGB"; + else {if(flags&TJ_ALPHAFIRST) pixformat="ARGB"; else pixformat="RGBA";} + } + printf("JPEG -> %s %s ... ", pixformat, (flags&TJ_BOTTOMUP)?"Bottom-Up":"Top-Down "); + + _catch(tjDecompressHeader(hnd, jpegbuf, jpegsize, &_w, &_h)); + if(_w!=w || _h!=h) + { + printf("Incorrect JPEG header\n"); goto finally; + } + + if((bmpbuf=(unsigned char *)malloc(w*h*ps+1))==NULL) + { + printf("ERROR: Could not allocate buffer\n"); goto finally; + } + memset(bmpbuf, 0, w*ps*h); + + t=rrtime(); + _catch(tjDecompress(hnd, jpegbuf, jpegsize, bmpbuf, w, w*ps, h, ps, flags)); + t=rrtime()-t; + + if(checkbuf(bmpbuf, w, h, ps, subsamp, flags)) printf("Passed."); + else {printf("FAILED!"); dumpbuf(bmpbuf, w, h, ps, flags);} + + printf(" %f ms\n\n", t*1000.); + + finally: + if(bmpbuf) free(bmpbuf); +} + +void dotest(int w, int h, int ps, int subsamp, char *basefilename) +{ + tjhandle hnd=NULL, dhnd=NULL; unsigned char *jpegbuf=NULL; + unsigned long size; + + if((jpegbuf=(unsigned char *)malloc(TJBUFSIZE(w, h))) == NULL) + { + puts("ERROR: Could not allocate buffer."); goto finally; + } + + if((hnd=tjInitCompress())==NULL) + {printf("Error in tjInitCompress():\n%s\n", tjGetErrorStr()); goto finally;} + if((dhnd=tjInitDecompress())==NULL) + {printf("Error in tjInitDecompress():\n%s\n", tjGetErrorStr()); goto finally;} + + gentestjpeg(hnd, jpegbuf, &size, w, h, ps, basefilename, subsamp, 100, 0); + gentestbmp(dhnd, jpegbuf, size, w, h, ps, basefilename, subsamp, 100, 0); + + gentestjpeg(hnd, jpegbuf, &size, w, h, ps, basefilename, subsamp, 100, TJ_BGR); + gentestbmp(dhnd, jpegbuf, size, w, h, ps, basefilename, subsamp, 100, TJ_BGR); + + gentestjpeg(hnd, jpegbuf, &size, w, h, ps, basefilename, subsamp, 100, TJ_BOTTOMUP); + gentestbmp(dhnd, jpegbuf, size, w, h, ps, basefilename, subsamp, 100, TJ_BOTTOMUP); + + gentestjpeg(hnd, jpegbuf, &size, w, h, ps, basefilename, subsamp, 100, TJ_BGR|TJ_BOTTOMUP); + gentestbmp(dhnd, jpegbuf, size, w, h, ps, basefilename, subsamp, 100, TJ_BGR|TJ_BOTTOMUP); + + if(ps==4) + { + gentestjpeg(hnd, jpegbuf, &size, w, h, ps, basefilename, subsamp, 100, TJ_ALPHAFIRST); + gentestbmp(dhnd, jpegbuf, size, w, h, ps, basefilename, subsamp, 100, TJ_ALPHAFIRST); + + gentestjpeg(hnd, jpegbuf, &size, w, h, ps, basefilename, subsamp, 100, TJ_ALPHAFIRST|TJ_BGR); + gentestbmp(dhnd, jpegbuf, size, w, h, ps, basefilename, subsamp, 100, TJ_ALPHAFIRST|TJ_BGR); + + gentestjpeg(hnd, jpegbuf, &size, w, h, ps, basefilename, subsamp, 100, TJ_ALPHAFIRST|TJ_BOTTOMUP); + gentestbmp(dhnd, jpegbuf, size, w, h, ps, basefilename, subsamp, 100, TJ_ALPHAFIRST|TJ_BOTTOMUP); + + gentestjpeg(hnd, jpegbuf, &size, w, h, ps, basefilename, subsamp, 100, TJ_ALPHAFIRST|TJ_BGR|TJ_BOTTOMUP); + gentestbmp(dhnd, jpegbuf, size, w, h, ps, basefilename, subsamp, 100, TJ_ALPHAFIRST|TJ_BGR|TJ_BOTTOMUP); + } + + finally: + if(hnd) tjDestroy(hnd); + if(dhnd) tjDestroy(dhnd); + + if(jpegbuf) free(jpegbuf); +} + +#define MAXLENGTH 2048 + +void dotest1(void) +{ + int i, j, i2; unsigned char *bmpbuf=NULL, *jpgbuf=NULL; + tjhandle hnd=NULL; unsigned long size; + if((hnd=tjInitCompress())==NULL) + {printf("Error in tjInitCompress():\n%s\n", tjGetErrorStr()); goto finally;} + printf("Buffer size regression test\n"); + for(j=1; j<48; j++) + { + for(i=1; i<(j==1?MAXLENGTH:48); i++) + { + if(i%100==0) printf("%.4d x %.4d\b\b\b\b\b\b\b\b\b\b\b", i, j); + if((bmpbuf=(unsigned char *)malloc(i*j*4))==NULL + || (jpgbuf=(unsigned char *)malloc(TJBUFSIZE(i, j)))==NULL) + { + printf("Memory allocation failure\n"); goto finally; + } + memset(bmpbuf, 0, i*j*4); + for(i2=0; i2 +#include +#include +#include +#include "./bmp.h" +#include "./rrutil.h" +#include "./rrtimer.h" +#include "./turbojpeg.h" + +#define _catch(f) {if((f)==-1) {printf("Error in %s:\n%s\n", #f, tjGetErrorStr()); goto bailout;}} + +int forcemmx=0, forcesse=0, forcesse2=0, forcesse3=0; +const int _ps[BMPPIXELFORMATS]={3, 4, 3, 4, 4, 4}; +const int _flags[BMPPIXELFORMATS]={0, 0, TJ_BGR, TJ_BGR, + TJ_BGR|TJ_ALPHAFIRST, TJ_ALPHAFIRST}; +const int _rindex[BMPPIXELFORMATS]={0, 0, 2, 2, 3, 1}; +const int _gindex[BMPPIXELFORMATS]={1, 1, 1, 1, 2, 2}; +const int _bindex[BMPPIXELFORMATS]={2, 2, 0, 0, 1, 3}; +const char *_pfname[]={"RGB", "RGBA", "BGR", "BGRA", "ABGR", "ARGB"}; +const char *_subnamel[NUMSUBOPT]={"4:4:4", "4:2:2", "4:1:1", "GRAY"}; +const char *_subnames[NUMSUBOPT]={"444", "422", "411", "GRAY"}; + +void printsigfig(double val, int figs) +{ + char format[80]; + double _l=log10(val); int l; + if(_l<0.) + { + l=(int)fabs(_l); + sprintf(format, "%%%d.%df", figs+l+2, figs+l); + } + else + { + l=(int)_l+1; + if(figs<=l) sprintf(format, "%%.0f"); + else sprintf(format, "%%%d.%df", figs+1, figs-l); + } + printf(format, val); +} + +void dotest(unsigned char *srcbuf, int w, int h, BMPPIXELFORMAT pf, int bu, + int jpegsub, int qual, char *filename, int dotile, int useppm, int quiet) +{ + char tempstr[1024]; + FILE *outfile; tjhandle hnd; + unsigned char **jpegbuf=NULL, *rgbbuf=NULL; + rrtimer timer; double elapsed; + int jpgbufsize=0, i, j, tilesizex, tilesizey, numtilesx, numtilesy, ITER; + unsigned long *comptilesize=NULL; + int flags=(forcemmx?TJ_FORCEMMX:0)|(forcesse?TJ_FORCESSE:0) + |(forcesse2?TJ_FORCESSE2:0)|(forcesse3?TJ_FORCESSE3:0); + int ps=_ps[pf]; + int pitch=w*ps; + + flags |= _flags[pf]; + if(bu) flags |= TJ_BOTTOMUP; + + if((rgbbuf=(unsigned char *)malloc(pitch*h)) == NULL) + { + puts("ERROR: Could not allocate image buffer."); + exit(1); + } + + if(!quiet) printf("\n>>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", _pfname[pf], + bu?"Bottom-up":"Top-down", _subnamel[jpegsub], qual); + if(dotile) {tilesizex=tilesizey=4;} else {tilesizex=w; tilesizey=h;} + + do + { + tilesizex*=2; if(tilesizex>w) tilesizex=w; + tilesizey*=2; if(tilesizey>h) tilesizey=h; + numtilesx=(w+tilesizex-1)/tilesizex; + numtilesy=(h+tilesizey-1)/tilesizey; + if((comptilesize=(unsigned long *)malloc(sizeof(unsigned long)*numtilesx*numtilesy)) == NULL + || (jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *)*numtilesx*numtilesy)) == NULL) + { + puts("ERROR: Could not allocate image buffers."); + goto bailout; + } + memset(jpegbuf, 0, sizeof(unsigned char *)*numtilesx*numtilesy); + for(i=0; i Frame rate: %f fps\n", (double)ITER/elapsed); + printf(" Output image size: %d bytes\n", jpgbufsize); + printf(" Compression ratio: %f:1\n", + (double)(w*h*ps)/(double)jpgbufsize); + printf(" Source throughput: %f Megapixels/sec\n", + (double)(w*h)/1000000.*(double)ITER/elapsed); + printf(" Output bit stream: %f Megabits/sec\n", + (double)jpgbufsize*8./1000000.*(double)ITER/elapsed); + } + if(tilesizex==w && tilesizey==h) + { + sprintf(tempstr, "%s_%sQ%d.jpg", filename, _subnames[jpegsub], qual); + if((outfile=fopen(tempstr, "wb"))==NULL) + { + puts("ERROR: Could not open reference image"); + exit(1); + } + if(fwrite(jpegbuf[0], jpgbufsize, 1, outfile)!=1) + { + puts("ERROR: Could not write reference image"); + exit(1); + } + fclose(outfile); + if(!quiet) printf("Reference image written to %s\n", tempstr); + } + + // Decompression test + memset(rgbbuf, 127, pitch*h); // Grey image means decompressor did nothing + if((hnd=tjInitDecompress())==NULL) + { + printf("Error in tjInitDecompress():\n%s\n", tjGetErrorStr()); + goto bailout; + } + _catch(tjDecompress(hnd, jpegbuf[0], jpgbufsize, rgbbuf, tilesizex, pitch, + tilesizey, ps, flags)); + ITER=0; + timer.start(); + do + { + int tilen=0; + for(i=0; i Frame rate: %f fps\n", (double)ITER/elapsed); + printf(" Dest. throughput: %f Megapixels/sec\n", + (double)(w*h)/1000000.*(double)ITER/elapsed); + } + if(tilesizex==w && tilesizey==h) + sprintf(tempstr, "%s_%sQ%d_full.%s", filename, _subnames[jpegsub], qual, + useppm?"ppm":"bmp"); + else sprintf(tempstr, "%s_%sQ%d_%dx%d.%s", filename, _subnames[jpegsub], + qual, tilesizex, tilesizey, useppm?"ppm":"bmp"); + if(savebmp(tempstr, rgbbuf, w, h, pf, pitch, bu)==-1) + { + printf("ERROR saving bitmap: %s\n", bmpgeterr()); + goto bailout; + } + sprintf(strrchr(tempstr, '.'), "-err.%s", useppm?"ppm":"bmp"); + if(!quiet) + printf("Computing compression error and saving to %s.\n", tempstr); + if(jpegsub==TJ_GRAYSCALE) + { + for(j=0; j255) y=255; if(y<0) y=0; + rgbbuf[pitch*j+i+_rindex[pf]]=abs(rgbbuf[pitch*j+i+_rindex[pf]]-y); + rgbbuf[pitch*j+i+_gindex[pf]]=abs(rgbbuf[pitch*j+i+_gindex[pf]]-y); + rgbbuf[pitch*j+i+_bindex[pf]]=abs(rgbbuf[pitch*j+i+_bindex[pf]]-y); + } + } + } + else + { + for(j=0; j <%% Quality>\n\n", argv[0]); + printf(" [-tile]\n"); + printf(" Test performance of the codec when the image is encoded\n"); + printf(" as separate tiles of varying sizes.\n\n"); + printf(" [-forcemmx] [-forcesse] [-forcesse2] [-forcesse3]\n"); + printf(" Force MMX, SSE, or SSE2 code paths in Intel codec\n\n"); + printf(" [-rgb | -bgr | -rgba | -bgra | -abgr | -argb]\n"); + printf(" Test the specified color conversion path in the codec (default: BGR)\n\n"); + printf(" [-quiet]\n"); + printf(" Output in tabular rather than verbose format\n\n"); + printf(" NOTE: If the quality is specified as a range, i.e. 90-100, a separate\n"); + printf(" test will be performed for all quality values in the range.\n"); + exit(1); + } + if((qual=atoi(argv[2]))<1 || qual>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", &hiqual)==1 && hiqual>qual && hiqual>=1 + && hiqual<=100) {} + else hiqual=qual; + + if(argc>3) + { + for(i=3; i=qual; i--) + dotest(bmpbuf, w, h, pf, bu, TJ_GRAYSCALE, i, argv[1], dotile, useppm, quiet); + if(quiet) printf("\n"); + for(i=hiqual; i>=qual; i--) + dotest(bmpbuf, w, h, pf, bu, TJ_411, i, argv[1], dotile, useppm, quiet); + if(quiet) printf("\n"); + for(i=hiqual; i>=qual; i--) + dotest(bmpbuf, w, h, pf, bu, TJ_422, i, argv[1], dotile, useppm, quiet); + if(quiet) printf("\n"); + for(i=hiqual; i>=qual; i--) + dotest(bmpbuf, w, h, pf, bu, TJ_444, i, argv[1], dotile, useppm, quiet); + + if(bmpbuf) free(bmpbuf); + return 0; +} diff --git a/common/jpeg/rrtimer.h b/common/jpeg/rrtimer.h new file mode 100644 index 00000000..4db5e371 --- /dev/null +++ b/common/jpeg/rrtimer.h @@ -0,0 +1,114 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * + * 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. + */ + +#ifndef __RRTIMER_H__ +#define __RRTIMER_H__ + +#ifdef __cplusplus + +#ifdef _WIN32 +#include +#else +#include +#endif + +class rrtimer +{ + public: + + rrtimer(void) : t1(0.0) + { + #ifdef _WIN32 + highres=false; tick=0.001; + LARGE_INTEGER Frequency; + if(QueryPerformanceFrequency(&Frequency)!=0) + { + tick=(double)1.0/(double)(Frequency.QuadPart); + highres=true; + } + #endif + } + + void start(void) + { + t1=time(); + } + + double time(void) + { + #ifdef _WIN32 + if(highres) + { + LARGE_INTEGER Time; + QueryPerformanceCounter(&Time); + return((double)(Time.QuadPart)*tick); + } + else + return((double)GetTickCount()*tick); + #else + struct timeval __tv; + gettimeofday(&__tv, (struct timezone *)NULL); + return((double)(__tv.tv_sec)+(double)(__tv.tv_usec)*0.000001); + #endif + } + + double elapsed(void) + { + return time()-t1; + } + + private: + + #ifdef _WIN32 + bool highres; double tick; + #endif + double t1; +}; + +#endif // __cplusplus + +#ifdef _WIN32 + +#include + +__inline double rrtime(void) +{ + LARGE_INTEGER Frequency, Time; + if(QueryPerformanceFrequency(&Frequency)!=0) + { + QueryPerformanceCounter(&Time); + return (double)Time.QuadPart/(double)Frequency.QuadPart; + } + else return (double)GetTickCount()*0.001; +} + +#else + +#include + +#ifdef sun +#define __inline inline +#endif + +static __inline double rrtime(void) +{ + struct timeval __tv; + gettimeofday(&__tv, (struct timezone *)NULL); + return((double)__tv.tv_sec+(double)__tv.tv_usec*0.000001); +} + +#endif + +#endif + diff --git a/common/jpeg/rrutil.h b/common/jpeg/rrutil.h new file mode 100644 index 00000000..49181204 --- /dev/null +++ b/common/jpeg/rrutil.h @@ -0,0 +1,81 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * + * 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. + */ + +#ifndef __RRUTIL_H__ +#define __RRUTIL_H__ + +#ifdef _WIN32 + #include + #define sleep(t) Sleep((t)*1000) + #define usleep(t) Sleep((t)/1000) +#else + #include + #define stricmp strcasecmp + #define strnicmp strncasecmp +#endif + +#ifndef min + #define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef max + #define max(a,b) ((a)>(b)?(a):(b)) +#endif + +#define pow2(i) (1<<(i)) +#define isPow2(x) (((x)&(x-1))==0) + +#ifdef sgi +#define _SC_NPROCESSORS_CONF _SC_NPROC_CONF +#endif + +#ifdef sun +#define __inline inline +#endif + +static __inline int numprocs(void) +{ + #ifdef _WIN32 + DWORD ProcAff, SysAff, i; int count=0; + if(!GetProcessAffinityMask(GetCurrentProcess(), &ProcAff, &SysAff)) return(1); + for(i=0; i<32; i++) if(ProcAff&(1<> 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; +} + +#endif diff --git a/common/jpeg/turbojpeg.h b/common/jpeg/turbojpeg.h new file mode 100644 index 00000000..90f12789 --- /dev/null +++ b/common/jpeg/turbojpeg.h @@ -0,0 +1,229 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005, 2006 Sun Microsystems, Inc. + * + * 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. + */ + +#if (defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__)) && defined(_WIN32) && defined(DLLDEFINE) +#define DLLEXPORT __declspec(dllexport) +#else +#define DLLEXPORT +#endif + +#define DLLCALL + +/* Subsampling */ +#define NUMSUBOPT 4 + +enum {TJ_444=0, TJ_422, TJ_411, TJ_GRAYSCALE}; + +/* Flags */ +#define TJ_BGR 1 +#define TJ_BOTTOMUP 2 +#define TJ_FORCEMMX 8 /* Force IPP to use MMX code even if SSE available */ +#define TJ_FORCESSE 16 /* Force IPP to use SSE1 code even if SSE2 available */ +#define TJ_FORCESSE2 32 /* Force IPP to use SSE2 code (useful if auto-detect is not working properly) */ +#define TJ_ALPHAFIRST 64 /* BGR buffer is ABGR and RGB buffer is ARGB */ +#define TJ_FORCESSE3 128 /* Force IPP to use SSE3 code (useful if auto-detect is not working properly) */ + +typedef void* tjhandle; + +#define TJPAD(p) (((p)+3)&(~3)) +#ifndef max + #define max(a,b) ((a)>(b)?(a):(b)) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* API follows */ + + +/* + tjhandle tjInitCompress(void) + + Creates a new JPEG compressor instance, allocates memory for the structures, + and returns a handle to the instance. Most applications will only + need to call this once at the beginning of the program or once for each + concurrent thread. Don't try to create a new instance every time you + compress an image, because this will cause performance to suffer. + + RETURNS: NULL on error +*/ +DLLEXPORT tjhandle DLLCALL tjInitCompress(void); + + +/* + int tjCompress(tjhandle j, + unsigned char *srcbuf, int width, int pitch, int height, int pixelsize, + unsigned char *dstbuf, unsigned long *size, + int jpegsubsamp, int jpegqual, int flags) + + [INPUT] j = instance handle previously returned from a call to + tjInitCompress() + [INPUT] srcbuf = pointer to user-allocated image buffer containing pixels in + RGB(A) or BGR(A) form + [INPUT] width = width (in pixels) of the source image + [INPUT] pitch = bytes per line of the source image (width*pixelsize if the + bitmap is unpadded, else TJPAD(width*pixelsize) if each line of the bitmap + is padded to the nearest 32-bit boundary, such as is the case for Windows + bitmaps. You can also be clever and use this parameter to skip lines, etc., + as long as the pitch is greater than 0.) + [INPUT] height = height (in pixels) of the source image + [INPUT] pixelsize = size (in bytes) of each pixel in the source image + RGBA and BGRA: 4, RGB and BGR: 3 + [INPUT] dstbuf = pointer to user-allocated image buffer which will receive + the JPEG image. Use the macro TJBUFSIZE(width, height) to determine + the appropriate size for this buffer based on the image width and height. + [OUTPUT] size = pointer to unsigned long which receives the size (in bytes) + of the compressed image + [INPUT] jpegsubsamp = Specifies either 4:1:1, 4:2:2, or 4:4:4 subsampling. + When the image is converted from the RGB to YCbCr colorspace as part of the + JPEG compression process, every other Cb and Cr (chrominance) pixel can be + discarded to produce a smaller image with little perceptible loss of + image clarity (the human eye is more sensitive to small changes in + brightness than small changes in color.) + + TJ_411: 4:1:1 subsampling. Discards every other Cb, Cr pixel in both + horizontal and vertical directions. + TJ_422: 4:2:2 subsampling. Discards every other Cb, Cr pixel only in + the horizontal direction. + TJ_444: no subsampling. + TJ_GRAYSCALE: Generate grayscale JPEG image + + [INPUT] jpegqual = JPEG quality (an integer between 0 and 100 inclusive.) + [INPUT] flags = the bitwise OR of one or more of the following + + TJ_BGR: The components of each pixel in the source image are stored in + B,G,R order, not R,G,B + TJ_BOTTOMUP: The source image is stored in bottom-up (Windows) order, + not top-down + TJ_FORCEMMX: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use MMX code (bypass CPU auto-detection) + TJ_FORCESSE: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE code (bypass CPU auto-detection) + TJ_FORCESSE2: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE2 code (bypass CPU auto-detection) + TJ_FORCESSE3: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE3 code (bypass CPU auto-detection) + + RETURNS: 0 on success, -1 on error +*/ +DLLEXPORT int DLLCALL tjCompress(tjhandle j, + unsigned char *srcbuf, int width, int pitch, int height, int pixelsize, + unsigned char *dstbuf, unsigned long *size, + int jpegsubsamp, int jpegqual, int flags); + +DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height); + +/* + tjhandle tjInitDecompress(void) + + Creates a new JPEG decompressor instance, allocates memory for the + structures, and returns a handle to the instance. Most applications will + only need to call this once at the beginning of the program or once for each + concurrent thread. Don't try to create a new instance every time you + decompress an image, because this will cause performance to suffer. + + RETURNS: NULL on error +*/ +DLLEXPORT tjhandle DLLCALL tjInitDecompress(void); + + +/* + int tjDecompressHeader(tjhandle j, + unsigned char *srcbuf, unsigned long size, + int *width, int *height) + + [INPUT] j = instance handle previously returned from a call to + tjInitDecompress() + [INPUT] srcbuf = pointer to a user-allocated buffer containing the JPEG image + to decompress + [INPUT] size = size of the JPEG image buffer (in bytes) + [OUTPUT] width = width (in pixels) of the JPEG image + [OUTPUT] height = height (in pixels) of the JPEG image + + RETURNS: 0 on success, -1 on error +*/ +DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle j, + unsigned char *srcbuf, unsigned long size, + int *width, int *height); + + +/* + int tjDecompress(tjhandle j, + unsigned char *srcbuf, unsigned long size, + unsigned char *dstbuf, int width, int pitch, int height, int pixelsize, + int flags) + + [INPUT] j = instance handle previously returned from a call to + tjInitDecompress() + [INPUT] srcbuf = pointer to a user-allocated buffer containing the JPEG image + to decompress + [INPUT] size = size of the JPEG image buffer (in bytes) + [INPUT] dstbuf = pointer to user-allocated image buffer which will receive + the bitmap image. This buffer should normally be pitch*height + bytes in size, although this pointer may also be used to decompress into + a specific region of a larger buffer. + [INPUT] width = width (in pixels) of the destination image + [INPUT] pitch = bytes per line of the destination image (width*pixelsize if the + bitmap is unpadded, else TJPAD(width*pixelsize) if each line of the bitmap + is padded to the nearest 32-bit boundary, such as is the case for Windows + bitmaps. You can also be clever and use this parameter to skip lines, etc., + as long as the pitch is greater than 0.) + [INPUT] height = height (in pixels) of the destination image + [INPUT] pixelsize = size (in bytes) of each pixel in the destination image + RGBA/RGBx and BGRA/BGRx: 4, RGB and BGR: 3 + [INPUT] flags = the bitwise OR of one or more of the following + + TJ_BGR: The components of each pixel in the destination image should be + written in B,G,R order, not R,G,B + TJ_BOTTOMUP: The destination image should be stored in bottom-up + (Windows) order, not top-down + TJ_FORCEMMX: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use MMX code (bypass CPU auto-detection) + TJ_FORCESSE: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE code (bypass CPU auto-detection) + TJ_FORCESSE2: Valid only for the Intel Performance Primitives implementation + of this codec-- force IPP to use SSE2 code (bypass CPU auto-detection) + + RETURNS: 0 on success, -1 on error +*/ +DLLEXPORT int DLLCALL tjDecompress(tjhandle j, + unsigned char *srcbuf, unsigned long size, + unsigned char *dstbuf, int width, int pitch, int height, int pixelsize, + int flags); + + +/* + int tjDestroy(tjhandle h) + + Frees structures associated with a compression or decompression instance + + [INPUT] h = instance handle (returned from a previous call to + tjInitCompress() or tjInitDecompress() + + RETURNS: 0 on success, -1 on error +*/ +DLLEXPORT int DLLCALL tjDestroy(tjhandle h); + + +/* + char *tjGetErrorStr(void) + + Returns a descriptive error message explaining why the last command failed +*/ +DLLEXPORT char* DLLCALL tjGetErrorStr(void); + +#ifdef __cplusplus +} +#endif diff --git a/common/jpeg/turbojpegl.c b/common/jpeg/turbojpegl.c new file mode 100644 index 00000000..3f03aed9 --- /dev/null +++ b/common/jpeg/turbojpegl.c @@ -0,0 +1,352 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * + * 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 implements a JPEG compressor/decompressor using the libjpeg API + +#include +#include +#include +#include +#include +#include +#include "./turbojpeg.h" + + +// Error handling + +static char lasterror[JMSG_LENGTH_MAX]="No error"; + +typedef struct _error_mgr +{ + struct jpeg_error_mgr pub; + jmp_buf jb; +} error_mgr; + +static void my_error_exit(j_common_ptr cinfo) +{ + error_mgr *myerr = (error_mgr *)cinfo->err; + (*cinfo->err->output_message)(cinfo); + longjmp(myerr->jb, 1); +} + +static void my_output_message(j_common_ptr cinfo) +{ + (*cinfo->err->format_message)(cinfo, lasterror); +} + + +// Global structures, macros, etc. + +typedef struct _jpgstruct +{ + struct jpeg_compress_struct cinfo; + struct jpeg_decompress_struct dinfo; + struct jpeg_destination_mgr jdms; + struct jpeg_source_mgr jsms; + error_mgr jerr; + int initc, initd; +} jpgstruct; + +static const int hsampfactor[NUMSUBOPT]={1, 2, 2, 1}; +static const int vsampfactor[NUMSUBOPT]={1, 1, 2, 1}; + +#define _throw(c) {sprintf(lasterror, "%s", c); return -1;} +#define _catch(f) {if((f)==-1) return -1;} +#define checkhandle(h) jpgstruct *j=(jpgstruct *)h; \ + if(!j) _throw("Invalid handle"); + + +// CO + +static boolean empty_output_buffer(struct jpeg_compress_struct *cinfo) +{ + ERREXIT(cinfo, JERR_BUFFER_SIZE); + return TRUE; +} + +static void destination_noop(struct jpeg_compress_struct *cinfo) +{ +} + +DLLEXPORT tjhandle DLLCALL tjInitCompress(void) +{ + jpgstruct *j=NULL; + if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL) + {sprintf(lasterror, "Memory allocation failure"); return NULL;} + memset(j, 0, sizeof(jpgstruct)); + j->cinfo.err=jpeg_std_error(&j->jerr.pub); + j->jerr.pub.error_exit=my_error_exit; + j->jerr.pub.output_message=my_output_message; + + if(setjmp(j->jerr.jb)) + { // this will execute if LIBJPEG has an error + if(j) free(j); return NULL; + } + + jpeg_create_compress(&j->cinfo); + j->cinfo.dest=&j->jdms; + j->jdms.init_destination=destination_noop; + j->jdms.empty_output_buffer=empty_output_buffer; + j->jdms.term_destination=destination_noop; + + j->initc=1; + return (tjhandle)j; +} + +DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height) +{ + // This allows enough room in case the image doesn't compress + return ((width+15)&(~15)) * ((height+15)&(~15)) * 6 + 2048; +} + +DLLEXPORT int DLLCALL tjCompress(tjhandle h, + unsigned char *srcbuf, int width, int pitch, int height, int ps, + unsigned char *dstbuf, unsigned long *size, + int jpegsub, int qual, int flags) +{ + int i; JSAMPROW *row_pointer=NULL; + + checkhandle(h); + + if(srcbuf==NULL || width<=0 || pitch<0 || height<=0 + || dstbuf==NULL || size==NULL + || jpegsub<0 || jpegsub>=NUMSUBOPT || qual<0 || qual>100) + _throw("Invalid argument in tjCompress()"); + if(ps!=3 && ps!=4) _throw("This compressor can only take 24-bit or 32-bit RGB input"); + if(!j->initc) _throw("Instance has not been initialized for compression"); + + if(pitch==0) pitch=width*ps; + + j->cinfo.image_width = width; + j->cinfo.image_height = height; + j->cinfo.input_components = ps; + + #if JCS_EXTENSIONS==1 + j->cinfo.in_color_space = JCS_EXT_RGB; + if(ps==3 && (flags&TJ_BGR)) + j->cinfo.in_color_space = JCS_EXT_BGR; + else if(ps==4 && !(flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST)) + j->cinfo.in_color_space = JCS_EXT_RGBX; + else if(ps==4 && (flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST)) + j->cinfo.in_color_space = JCS_EXT_BGRX; + else if(ps==4 && (flags&TJ_BGR) && (flags&TJ_ALPHAFIRST)) + j->cinfo.in_color_space = JCS_EXT_XBGR; + else if(ps==4 && !(flags&TJ_BGR) && (flags&TJ_ALPHAFIRST)) + j->cinfo.in_color_space = JCS_EXT_XRGB; + #else + #error "TurboJPEG requires JPEG colorspace extensions" + #endif + + if(setjmp(j->jerr.jb)) + { // this will execute if LIBJPEG has an error + if(row_pointer) free(row_pointer); + return -1; + } + + jpeg_set_defaults(&j->cinfo); + + jpeg_set_quality(&j->cinfo, qual, TRUE); + if(jpegsub==TJ_GRAYSCALE) + jpeg_set_colorspace(&j->cinfo, JCS_GRAYSCALE); + else + jpeg_set_colorspace(&j->cinfo, JCS_YCbCr); + j->cinfo.dct_method = JDCT_FASTEST; + + j->cinfo.comp_info[0].h_samp_factor=hsampfactor[jpegsub]; + j->cinfo.comp_info[1].h_samp_factor=1; + j->cinfo.comp_info[2].h_samp_factor=1; + j->cinfo.comp_info[0].v_samp_factor=vsampfactor[jpegsub]; + j->cinfo.comp_info[1].v_samp_factor=1; + j->cinfo.comp_info[2].v_samp_factor=1; + + j->jdms.next_output_byte = dstbuf; + j->jdms.free_in_buffer = TJBUFSIZE(j->cinfo.image_width, j->cinfo.image_height); + + if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL) + _throw("Memory allocation failed in tjInitCompress()"); + for(i=0; icinfo, TRUE); + while(j->cinfo.next_scanlinecinfo.image_height) + { + jpeg_write_scanlines(&j->cinfo, &row_pointer[j->cinfo.next_scanline], + j->cinfo.image_height-j->cinfo.next_scanline); + } + jpeg_finish_compress(&j->cinfo); + *size=TJBUFSIZE(j->cinfo.image_width, j->cinfo.image_height)-(j->jdms.free_in_buffer); + + if(row_pointer) free(row_pointer); + return 0; +} + + +// DEC + +static boolean fill_input_buffer (struct jpeg_decompress_struct *dinfo) +{ + ERREXIT(dinfo, JERR_BUFFER_SIZE); + return TRUE; +} + +static void skip_input_data (struct jpeg_decompress_struct *dinfo, long num_bytes) +{ + dinfo->src->next_input_byte += (size_t) num_bytes; + dinfo->src->bytes_in_buffer -= (size_t) num_bytes; +} + +static void source_noop (struct jpeg_decompress_struct *dinfo) +{ +} + +DLLEXPORT tjhandle DLLCALL tjInitDecompress(void) +{ + jpgstruct *j; + if((j=(jpgstruct *)malloc(sizeof(jpgstruct)))==NULL) + {sprintf(lasterror, "Memory allocation failure"); return NULL;} + memset(j, 0, sizeof(jpgstruct)); + j->dinfo.err=jpeg_std_error(&j->jerr.pub); + j->jerr.pub.error_exit=my_error_exit; + j->jerr.pub.output_message=my_output_message; + + if(setjmp(j->jerr.jb)) + { // this will execute if LIBJPEG has an error + free(j); return NULL; + } + + jpeg_create_decompress(&j->dinfo); + j->dinfo.src=&j->jsms; + j->jsms.init_source=source_noop; + j->jsms.fill_input_buffer = fill_input_buffer; + j->jsms.skip_input_data = skip_input_data; + j->jsms.resync_to_restart = jpeg_resync_to_restart; + j->jsms.term_source = source_noop; + + j->initd=1; + return (tjhandle)j; +} + + +DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle h, + unsigned char *srcbuf, unsigned long size, + int *width, int *height) +{ + checkhandle(h); + + if(srcbuf==NULL || size<=0 || width==NULL || height==NULL) + _throw("Invalid argument in tjDecompressHeader()"); + if(!j->initd) _throw("Instance has not been initialized for decompression"); + + if(setjmp(j->jerr.jb)) + { // this will execute if LIBJPEG has an error + return -1; + } + + j->jsms.bytes_in_buffer = size; + j->jsms.next_input_byte = srcbuf; + + jpeg_read_header(&j->dinfo, TRUE); + + *width=j->dinfo.image_width; *height=j->dinfo.image_height; + + jpeg_abort_decompress(&j->dinfo); + + if(*width<1 || *height<1) _throw("Invalid data returned in header"); + return 0; +} + + +DLLEXPORT int DLLCALL tjDecompress(tjhandle h, + unsigned char *srcbuf, unsigned long size, + unsigned char *dstbuf, int width, int pitch, int height, int ps, + int flags) +{ + int i; JSAMPROW *row_pointer=NULL; + + checkhandle(h); + + if(srcbuf==NULL || size<=0 + || dstbuf==NULL || width<=0 || pitch<0 || height<=0) + _throw("Invalid argument in tjDecompress()"); + if(ps!=3 && ps!=4) _throw("This compressor can only take 24-bit or 32-bit RGB input"); + if(!j->initd) _throw("Instance has not been initialized for decompression"); + + if(pitch==0) pitch=width*ps; + + if(setjmp(j->jerr.jb)) + { // this will execute if LIBJPEG has an error + if(row_pointer) free(row_pointer); + return -1; + } + + j->jsms.bytes_in_buffer = size; + j->jsms.next_input_byte = srcbuf; + + jpeg_read_header(&j->dinfo, TRUE); + + if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL) + _throw("Memory allocation failed in tjInitDecompress()"); + for(i=0; idinfo.out_color_space = JCS_EXT_RGB; + if(ps==3 && (flags&TJ_BGR)) + j->dinfo.out_color_space = JCS_EXT_BGR; + else if(ps==4 && !(flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST)) + j->dinfo.out_color_space = JCS_EXT_RGBX; + else if(ps==4 && (flags&TJ_BGR) && !(flags&TJ_ALPHAFIRST)) + j->dinfo.out_color_space = JCS_EXT_BGRX; + else if(ps==4 && (flags&TJ_BGR) && (flags&TJ_ALPHAFIRST)) + j->dinfo.out_color_space = JCS_EXT_XBGR; + else if(ps==4 && !(flags&TJ_BGR) && (flags&TJ_ALPHAFIRST)) + j->dinfo.out_color_space = JCS_EXT_XRGB; + #else + #error "TurboJPEG requires JPEG colorspace extensions" + #endif + + jpeg_start_decompress(&j->dinfo); + while(j->dinfo.output_scanlinedinfo.output_height) + { + jpeg_read_scanlines(&j->dinfo, &row_pointer[j->dinfo.output_scanline], + j->dinfo.output_height-j->dinfo.output_scanline); + } + jpeg_finish_decompress(&j->dinfo); + + if(row_pointer) free(row_pointer); + return 0; +} + + +// General + +DLLEXPORT char* DLLCALL tjGetErrorStr(void) +{ + return lasterror; +} + +DLLEXPORT int DLLCALL tjDestroy(tjhandle h) +{ + checkhandle(h); + if(setjmp(j->jerr.jb)) return -1; + if(j->initc) jpeg_destroy_compress(&j->cinfo); + if(j->initd) jpeg_destroy_decompress(&j->dinfo); + free(j); + return 0; +} -- 2.39.5