summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordcommander <dcommander@3789f03b-4d11-0410-bbf8-ca57d06f2519>2009-04-03 12:04:24 +0000
committerdcommander <dcommander@3789f03b-4d11-0410-bbf8-ca57d06f2519>2009-04-03 12:04:24 +0000
commita8a291e2d702c9456bf542b114bc430f5d29520c (patch)
treefa2d79d55d5d9d6d27ec8961968412fd105803ca
parent4d25e604cf293f3dfa32ac796d58de719631ac50 (diff)
Include low-level unit tests borrowed from VirtualGL
git-svn-id: https://libjpeg-turbo.svn.sourceforge.net/svnroot/libjpeg-turbo@36 3789f03b-4d11-0410-bbf8-ca57d06f2519
-rw-r--r--trunk/Makefile.am13
-rw-r--r--trunk/bmp.c370
-rw-r--r--trunk/bmp.h48
-rw-r--r--trunk/jpegut.c384
-rw-r--r--trunk/jpgtest.cxx382
-rw-r--r--trunk/rrtimer.h114
-rw-r--r--trunk/rrutil.h81
-rw-r--r--trunk/turbojpeg.h229
-rw-r--r--trunk/turbojpegl.c352
9 files changed, 1972 insertions, 1 deletions
diff --git a/trunk/Makefile.am b/trunk/Makefile.am
index 1d868b7..9312078 100644
--- a/trunk/Makefile.am
+++ b/trunk/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/trunk/bmp.c b/trunk/bmp.c
new file mode 100644
index 0000000..3ccc877
--- /dev/null
+++ b/trunk/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 <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 "./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; 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(!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/trunk/bmp.h b/trunk/bmp.h
new file mode 100644
index 0000000..437d327
--- /dev/null
+++ b/trunk/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/trunk/jpegut.c b/trunk/jpegut.c
new file mode 100644
index 0000000..777cd3d
--- /dev/null
+++ b/trunk/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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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; j<w; j++)
+ {
+ buf[(w*i+j)*ps+roffset]=255;
+ if(((_i/8)+(j/8))%2==0)
+ {
+ buf[(w*i+j)*ps+goffset]=255;
+ buf[(w*i+j)*ps+boffset]=255;
+ }
+ }
+ }
+ for(_i=16; _i<h; _i++)
+ {
+ if(flags&TJ_BOTTOMUP) i=h-_i-1; else i=_i;
+ for(j=0; j<w; j++)
+ {
+ if(((_i/8)+(j/8))%2!=0)
+ {
+ buf[(w*i+j)*ps+roffset]=255;
+ buf[(w*i+j)*ps+goffset]=255;
+ }
+ }
+ }
+}
+
+int dumpbuf(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,
+ j;
+ for(i=0; i<h; i++)
+ {
+ for(j=0; j<w; j++)
+ {
+ printf("%.3d/%.3d/%.3d ", buf[(w*i+j)*ps+roffset],
+ buf[(w*i+j)*ps+roffset], buf[(w*i+j)*ps+roffset]);
+ }
+ printf("\n");
+ }
+}
+
+int checkbuf(unsigned char *buf, int w, int h, int ps, int subsamp, 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++;}
+ if(subsamp==TJ_GRAYSCALE)
+ {
+ for(_i=0; _i<16; _i++)
+ {
+ if(flags&TJ_BOTTOMUP) i=h-_i-1; else i=_i;
+ for(j=0; j<w; j++)
+ {
+ unsigned char r=buf[(w*i+j)*ps+roffset],
+ g=buf[(w*i+j)*ps+goffset],
+ b=buf[(w*i+j)*ps+boffset];
+ if(((_i/8)+(j/8))%2==0)
+ {
+ if(r<253 || g<253 || b<253) return 0;
+ }
+ else
+ {
+ if(r<74 || r>78 || g<74 || g>78 || b<74 || b>78) return 0;
+ }
+ }
+ }
+ for(_i=16; _i<h; _i++)
+ {
+ if(flags&TJ_BOTTOMUP) i=h-_i-1; else i=_i;
+ for(j=0; j<w; j++)
+ {
+ unsigned char r=buf[(w*i+j)*ps+roffset],
+ g=buf[(w*i+j)*ps+goffset],
+ b=buf[(w*i+j)*ps+boffset];
+ if(((_i/8)+(j/8))%2==0)
+ {
+ if(r>2 || 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; j<w; j++)
+ {
+ if(buf[(w*i+j)*ps+roffset]<253) return 0;
+ if(((_i/8)+(j/8))%2==0)
+ {
+ if(buf[(w*i+j)*ps+goffset]<253) return 0;
+ if(buf[(w*i+j)*ps+boffset]<253) return 0;
+ }
+ else
+ {
+ if(buf[(w*i+j)*ps+goffset]>2) return 0;
+ if(buf[(w*i+j)*ps+boffset]>2) return 0;
+ }
+ }
+ }
+ for(_i=16; _i<h; _i++)
+ {
+ if(flags&TJ_BOTTOMUP) i=h-_i-1; else i=_i;
+ for(j=0; j<w; j++)
+ {
+ if(buf[(w*i+j)*ps+boffset]>2) 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<i*j; i2++)
+ {
+ bmpbuf[i2*4]=pixels[i2%9][2];
+ bmpbuf[i2*4+1]=pixels[i2%9][1];
+ bmpbuf[i2*2+2]=pixels[i2%9][0];
+ }
+ _catch(tjCompress(hnd, bmpbuf, i, i*4, j, 4,
+ jpgbuf, &size, TJ_444, 100, TJ_BGR));
+ free(bmpbuf); bmpbuf=NULL; free(jpgbuf); jpgbuf=NULL;
+
+ if((bmpbuf=(unsigned char *)malloc(j*i*4))==NULL
+ || (jpgbuf=(unsigned char *)malloc(TJBUFSIZE(j, i)))==NULL)
+ {
+ printf("Memory allocation failure\n"); goto finally;
+ }
+ for(i2=0; i2<j*i*4; i2++)
+ {
+ if(i2%2==0) bmpbuf[i2]=0xFF;
+ else bmpbuf[i2]=0;
+ }
+ _catch(tjCompress(hnd, bmpbuf, j, j*4, i, 4,
+ jpgbuf, &size, TJ_444, 100, TJ_BGR));
+ free(bmpbuf); bmpbuf=NULL; free(jpgbuf); jpgbuf=NULL;
+ }
+ }
+ printf("Done. \n");
+
+ finally:
+ if(bmpbuf) free(bmpbuf); if(jpgbuf) free(jpgbuf);
+ if(hnd) tjDestroy(hnd);
+}
+
+int main(int argc, char *argv[])
+{
+ dotest(35, 41, 3, TJ_444, "test");
+ dotest(35, 41, 4, TJ_444, "test");
+ dotest(35, 41, 3, TJ_GRAYSCALE, "test");
+ dotest(35, 41, 4, TJ_GRAYSCALE, "test");
+ dotest1();
+
+ return 0;
+}
diff --git a/trunk/jpgtest.cxx b/trunk/jpgtest.cxx
new file mode 100644
index 0000000..d0baea5
--- /dev/null
+++ b/trunk/jpgtest.cxx
@@ -0,0 +1,382 @@
+/* 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#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<numtilesx*numtilesy; i++)
+ {
+ if((jpegbuf[i]=(unsigned char *)malloc(TJBUFSIZE(tilesizex, tilesizey))) == NULL)
+ {
+ puts("ERROR: Could not allocate image buffers.");
+ goto bailout;
+ }
+ }
+
+ // Compression test
+ if(quiet) printf("%s\t%s\t%s\t%d\t", _pfname[pf], bu?"BU":"TD",
+ _subnamel[jpegsub], qual);
+ for(i=0; i<h; i++) memcpy(&rgbbuf[pitch*i], &srcbuf[w*ps*i], w*ps);
+ if((hnd=tjInitCompress())==NULL)
+ {
+ printf("Error in tjInitCompress():\n%s\n", tjGetErrorStr());
+ goto bailout;
+ }
+ _catch(tjCompress(hnd, rgbbuf, tilesizex, pitch, tilesizey, ps,
+ jpegbuf[0], &comptilesize[0], jpegsub, qual, flags));
+ ITER=0;
+ timer.start();
+ do
+ {
+ jpgbufsize=0; int tilen=0;
+ for(i=0; i<h; i+=tilesizey)
+ {
+ for(j=0; j<w; j+=tilesizex)
+ {
+ int tempw=min(tilesizex, w-j), temph=min(tilesizey, h-i);
+ _catch(tjCompress(hnd, &rgbbuf[pitch*i+j*ps], tempw, pitch,
+ temph, ps, jpegbuf[tilen], &comptilesize[tilen], jpegsub, qual,
+ flags));
+ jpgbufsize+=comptilesize[tilen];
+ tilen++;
+ }
+ }
+ ITER++;
+ } while((elapsed=timer.elapsed())<5.);
+ _catch(tjDestroy(hnd));
+ if(quiet)
+ {
+ if(tilesizex==w && tilesizey==h) printf("Full \t");
+ else printf("%-4d %-4d\t", tilesizex, tilesizey);
+ printsigfig((double)(w*h)/1000000.*(double)ITER/elapsed, 4);
+ printf("\t");
+ printsigfig((double)(w*h*ps)/(double)jpgbufsize, 4);
+ printf("\t");
+ }
+ else
+ {
+ if(tilesizex==w && tilesizey==h) printf("\nFull image\n");
+ else printf("\nTile size: %d x %d\n", tilesizex, tilesizey);
+ printf("C--> 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<h; i+=tilesizey)
+ {
+ for(j=0; j<w; j+=tilesizex)
+ {
+ int tempw=min(tilesizex, w-j), temph=min(tilesizey, h-i);
+ _catch(tjDecompress(hnd, jpegbuf[tilen], comptilesize[tilen],
+ &rgbbuf[pitch*i+ps*j], tempw, pitch, temph, ps, flags));
+ tilen++;
+ }
+ }
+ ITER++;
+ } while((elapsed=timer.elapsed())<5.);
+ _catch(tjDestroy(hnd));
+ if(quiet)
+ {
+ printsigfig((double)(w*h)/1000000.*(double)ITER/elapsed, 4);
+ printf("\n");
+ }
+ else
+ {
+ printf("D--> 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; j<h; j++)
+ {
+ for(i=0; i<w*ps; i+=ps)
+ {
+ int y=(int)((double)srcbuf[w*ps*j+i+_rindex[pf]]*0.299
+ + (double)srcbuf[w*ps*j+i+_gindex[pf]]*0.587
+ + (double)srcbuf[w*ps*j+i+_bindex[pf]]*0.114 + 0.5);
+ if(y>255) 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<h; j++) for(i=0; i<w*ps; i++)
+ rgbbuf[pitch*j+i]=abs(rgbbuf[pitch*j+i]-srcbuf[w*ps*j+i]);
+ }
+ if(savebmp(tempstr, rgbbuf, w, h, pf, pitch, bu)==-1)
+ {
+ printf("ERROR saving bitmap: %s\n", bmpgeterr());
+ goto bailout;
+ }
+
+ // Cleanup
+ if(jpegbuf)
+ {
+ for(i=0; i<numtilesx*numtilesy; i++)
+ {if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL;}
+ free(jpegbuf); jpegbuf=NULL;
+ }
+ if(comptilesize) {free(comptilesize); comptilesize=NULL;}
+ } while(tilesizex<w || tilesizey<h);
+
+ if(rgbbuf) {free(rgbbuf); rgbbuf=NULL;}
+ return;
+
+ bailout:
+ if(jpegbuf)
+ {
+ for(i=0; i<numtilesx*numtilesy; i++)
+ {if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL;}
+ free(jpegbuf); jpegbuf=NULL;
+ }
+ if(comptilesize) {free(comptilesize); comptilesize=NULL;}
+ if(rgbbuf) {free(rgbbuf); rgbbuf=NULL;}
+ return;
+}
+
+
+int main(int argc, char *argv[])
+{
+ unsigned char *bmpbuf=NULL; int w, h, i, useppm=0;
+ int qual, dotile=0, quiet=0, hiqual=-1; char *temp;
+ BMPPIXELFORMAT pf=BMP_BGR;
+ int bu=0;
+
+ printf("\n");
+
+ if(argc<3)
+ {
+ printf("USAGE: %s <Inputfile (BMP|PPM)> <%% 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<argc; i++)
+ {
+ if(!stricmp(argv[i], "-tile")) dotile=1;
+ if(!stricmp(argv[i], "-forcesse3"))
+ {
+ printf("Using SSE3 code in Intel compressor\n");
+ forcesse3=1;
+ }
+ if(!stricmp(argv[i], "-forcesse2"))
+ {
+ printf("Using SSE2 code in Intel compressor\n");
+ forcesse2=1;
+ }
+ if(!stricmp(argv[i], "-forcesse"))
+ {
+ printf("Using SSE code in Intel compressor\n");
+ forcesse=1;
+ }
+ if(!stricmp(argv[i], "-forcemmx"))
+ {
+ printf("Using MMX code in Intel compressor\n");
+ forcemmx=1;
+ }
+ if(!stricmp(argv[i], "-rgb")) pf=BMP_RGB;
+ if(!stricmp(argv[i], "-rgba")) pf=BMP_RGBA;
+ if(!stricmp(argv[i], "-bgr")) pf=BMP_BGR;
+ if(!stricmp(argv[i], "-bgra")) pf=BMP_BGRA;
+ if(!stricmp(argv[i], "-abgr")) pf=BMP_ABGR;
+ if(!stricmp(argv[i], "-argb")) pf=BMP_ARGB;
+ if(!stricmp(argv[i], "-bottomup")) bu=1;
+ if(!stricmp(argv[i], "-quiet")) quiet=1;
+ }
+ }
+
+ if(loadbmp(argv[1], &bmpbuf, &w, &h, pf, 1, bu)==-1)
+ {
+ printf("ERROR loading bitmap: %s\n", bmpgeterr()); exit(1);
+ }
+
+ temp=strrchr(argv[1], '.');
+ if(temp!=NULL)
+ {
+ if(!stricmp(temp, ".ppm")) useppm=1;
+ *temp='\0';
+ }
+
+ if(quiet)
+ {
+ printf("All performance values in Mpixels/sec\n\n");
+ printf("Bitmap\tBitmap\tJPEG\tJPEG\tTile Size\tCompr\tCompr\tDecomp\n");
+ printf("Format\tOrder\tFormat\tQual\t X Y \tPerf \tRatio\tPerf\n\n");
+ }
+
+ for(i=hiqual; 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/trunk/rrtimer.h b/trunk/rrtimer.h
new file mode 100644
index 0000000..4db5e37
--- /dev/null
+++ b/trunk/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 <windows.h>
+#else
+#include <sys/time.h>
+#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 <windows.h>
+
+__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 <sys/time.h>
+
+#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/trunk/rrutil.h b/trunk/rrutil.h
new file mode 100644
index 0000000..4918120
--- /dev/null
+++ b/trunk/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 <windows.h>
+ #define sleep(t) Sleep((t)*1000)
+ #define usleep(t) Sleep((t)/1000)
+#else
+ #include <unistd.h>
+ #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<<i)) count++;
+ return(count);
+ #elif defined (__APPLE__)
+ return(1);
+ #else
+ long count=1;
+ if((count=sysconf(_SC_NPROCESSORS_CONF))!=-1) return((int)count);
+ else return(1);
+ #endif
+}
+
+#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;
+}
+
+#endif
diff --git a/trunk/turbojpeg.h b/trunk/turbojpeg.h
new file mode 100644
index 0000000..90f1278
--- /dev/null
+++ b/trunk/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/trunk/turbojpegl.c b/trunk/turbojpegl.c
new file mode 100644
index 0000000..3f03aed
--- /dev/null
+++ b/trunk/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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <jpeglib.h>
+#include <jerror.h>
+#include <setjmp.h>
+#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; i<height; i++)
+ {
+ if(flags&TJ_BOTTOMUP) row_pointer[i]= &srcbuf[(height-i-1)*pitch];
+ else row_pointer[i]= &srcbuf[i*pitch];
+ }
+ jpeg_start_compress(&j->cinfo, TRUE);
+ while(j->cinfo.next_scanline<j->cinfo.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; i<height; i++)
+ {
+ if(flags&TJ_BOTTOMUP) row_pointer[i]= &dstbuf[(height-i-1)*pitch];
+ else row_pointer[i]= &dstbuf[i*pitch];
+ }
+
+ #if JCS_EXTENSIONS==1
+ j->dinfo.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_scanline<j->dinfo.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;
+}