diff options
-rw-r--r-- | common/turbojpeg.c | 944 | ||||
-rw-r--r-- | common/turbojpeg.h | 680 | ||||
-rw-r--r-- | configure.ac | 27 | ||||
-rw-r--r-- | libvncserver/Makefile.am | 6 | ||||
-rw-r--r-- | libvncserver/rfbserver.c | 50 | ||||
-rw-r--r-- | libvncserver/tight.c | 999 | ||||
-rw-r--r-- | libvncserver/turbo.c | 1566 | ||||
-rw-r--r-- | rfb/rfb.h | 9 | ||||
-rw-r--r-- | rfb/rfbproto.h | 18 | ||||
-rw-r--r-- | test/Makefile.am | 11 | ||||
-rw-r--r-- | test/bmp.c | 389 | ||||
-rw-r--r-- | test/bmp.h | 49 | ||||
-rw-r--r-- | test/tjbench.c | 658 | ||||
-rw-r--r-- | test/tjunittest.c | 461 | ||||
-rw-r--r-- | test/tjutil.c | 66 | ||||
-rw-r--r-- | test/tjutil.h | 47 |
16 files changed, 3302 insertions, 2678 deletions
diff --git a/common/turbojpeg.c b/common/turbojpeg.c index 497ec59..c145338 100644 --- a/common/turbojpeg.c +++ b/common/turbojpeg.c @@ -1,112 +1,517 @@ -/* Copyright (C)2004 Landmark Graphics Corporation - * Copyright (C)2005 Sun Microsystems, Inc. - * Copyright (C)2009-2011 D. R. Commander +/* + * Copyright (C)2009-2012 D. R. Commander. All Rights Reserved. * - * 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * 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. + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ -// This implements a JPEG compressor/decompressor using the libjpeg API +/* TurboJPEG/OSS: this implements the TurboJPEG API using libjpeg-turbo */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <jpeglib.h> #include <jerror.h> +#ifndef JCS_EXTENSIONS +#define JPEG_INTERNALS +#include <jmorecfg.h> +#endif #include <setjmp.h> #include "./turbojpeg.h" +#define PAD(v, p) ((v+(p)-1)&(~((p)-1))) #define CSTATE_START 100 #define DSTATE_START 200 +#define MEMZERO(ptr, size) memset(ptr, 0, size) + +#ifndef min + #define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef max + #define max(a,b) ((a)>(b)?(a):(b)) +#endif -// Error handling +/* Error handling (based on example in example.c) */ -static char lasterror[JMSG_LENGTH_MAX]="No error"; +static char errStr[JMSG_LENGTH_MAX]="No error"; -typedef struct _error_mgr +struct my_error_mgr { struct jpeg_error_mgr pub; - jmp_buf jb; -} error_mgr; + jmp_buf setjmp_buffer; +}; +typedef struct my_error_mgr *my_error_ptr; static void my_error_exit(j_common_ptr cinfo) { - error_mgr *myerr = (error_mgr *)cinfo->err; + my_error_ptr myerr=(my_error_ptr)cinfo->err; (*cinfo->err->output_message)(cinfo); - longjmp(myerr->jb, 1); + longjmp(myerr->setjmp_buffer, 1); } +/* Based on output_message() in jerror.c */ + static void my_output_message(j_common_ptr cinfo) { - (*cinfo->err->format_message)(cinfo, lasterror); + (*cinfo->err->format_message)(cinfo, errStr); } -// Global structures, macros, etc. +/* Global structures, macros, etc. */ + +enum {COMPRESS=1, DECOMPRESS=2}; -typedef struct _jpgstruct +typedef struct _tjinstance { 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; + struct jpeg_destination_mgr jdst; + struct jpeg_source_mgr jsrc; + struct my_error_mgr jerr; + int init; +} tjinstance; + +static const int pixelsize[TJ_NUMSAMP]={3, 3, 3, 1, 3}; + +#define NUMSF 4 +static const tjscalingfactor sf[NUMSF]={ + {1, 1}, + {1, 2}, + {1, 4}, + {1, 8} +}; + +#define _throw(m) {snprintf(errStr, JMSG_LENGTH_MAX, "%s", m); \ + retval=-1; goto bailout;} +#define getinstance(handle) tjinstance *this=(tjinstance *)handle; \ + j_compress_ptr cinfo=NULL; j_decompress_ptr dinfo=NULL; \ + if(!this) {snprintf(errStr, JMSG_LENGTH_MAX, "Invalid handle"); \ + return -1;} \ + cinfo=&this->cinfo; dinfo=&this->dinfo; + +static int getPixelFormat(int pixelSize, int flags) +{ + if(pixelSize==1) return TJPF_GRAY; + if(pixelSize==3) + { + if(flags&TJ_BGR) return TJPF_BGR; + else return TJPF_RGB; + } + if(pixelSize==4) + { + if(flags&TJ_ALPHAFIRST) + { + if(flags&TJ_BGR) return TJPF_XBGR; + else return TJPF_XRGB; + } + else + { + if(flags&TJ_BGR) return TJPF_BGRX; + else return TJPF_RGBX; + } + } + return -1; +} + +static int setCompDefaults(struct jpeg_compress_struct *cinfo, + int pixelFormat, int subsamp, int jpegQual) +{ + int retval=0; + + switch(pixelFormat) + { + case TJPF_GRAY: + cinfo->in_color_space=JCS_GRAYSCALE; break; + #if JCS_EXTENSIONS==1 + case TJPF_RGB: + cinfo->in_color_space=JCS_EXT_RGB; break; + case TJPF_BGR: + cinfo->in_color_space=JCS_EXT_BGR; break; + case TJPF_RGBX: + case TJPF_RGBA: + cinfo->in_color_space=JCS_EXT_RGBX; break; + case TJPF_BGRX: + case TJPF_BGRA: + cinfo->in_color_space=JCS_EXT_BGRX; break; + case TJPF_XRGB: + case TJPF_ARGB: + cinfo->in_color_space=JCS_EXT_XRGB; break; + case TJPF_XBGR: + case TJPF_ABGR: + cinfo->in_color_space=JCS_EXT_XBGR; break; + #else + case TJPF_RGB: + case TJPF_BGR: + case TJPF_RGBX: + case TJPF_BGRX: + case TJPF_XRGB: + case TJPF_XBGR: + case TJPF_RGBA: + case TJPF_BGRA: + case TJPF_ARGB: + case TJPF_ABGR: + cinfo->in_color_space=JCS_RGB; pixelFormat=TJPF_RGB; + break; + #endif + } + + cinfo->input_components=tjPixelSize[pixelFormat]; + jpeg_set_defaults(cinfo); + if(jpegQual>=0) + { + jpeg_set_quality(cinfo, jpegQual, TRUE); + if(jpegQual>=96) cinfo->dct_method=JDCT_ISLOW; + else cinfo->dct_method=JDCT_FASTEST; + } + if(subsamp==TJSAMP_GRAY) + jpeg_set_colorspace(cinfo, JCS_GRAYSCALE); + else + jpeg_set_colorspace(cinfo, JCS_YCbCr); + + cinfo->comp_info[0].h_samp_factor=tjMCUWidth[subsamp]/8; + cinfo->comp_info[1].h_samp_factor=1; + cinfo->comp_info[2].h_samp_factor=1; + cinfo->comp_info[0].v_samp_factor=tjMCUHeight[subsamp]/8; + cinfo->comp_info[1].v_samp_factor=1; + cinfo->comp_info[2].v_samp_factor=1; + + return retval; +} + +static int setDecompDefaults(struct jpeg_decompress_struct *dinfo, + int pixelFormat) +{ + int retval=0; + + switch(pixelFormat) + { + case TJPF_GRAY: + dinfo->out_color_space=JCS_GRAYSCALE; break; + #if JCS_EXTENSIONS==1 + case TJPF_RGB: + dinfo->out_color_space=JCS_EXT_RGB; break; + case TJPF_BGR: + dinfo->out_color_space=JCS_EXT_BGR; break; + case TJPF_RGBX: + dinfo->out_color_space=JCS_EXT_RGBX; break; + case TJPF_BGRX: + dinfo->out_color_space=JCS_EXT_BGRX; break; + case TJPF_XRGB: + dinfo->out_color_space=JCS_EXT_XRGB; break; + case TJPF_XBGR: + dinfo->out_color_space=JCS_EXT_XBGR; break; + #if JCS_ALPHA_EXTENSIONS==1 + case TJPF_RGBA: + dinfo->out_color_space=JCS_EXT_RGBA; break; + case TJPF_BGRA: + dinfo->out_color_space=JCS_EXT_BGRA; break; + case TJPF_ARGB: + dinfo->out_color_space=JCS_EXT_ARGB; break; + case TJPF_ABGR: + dinfo->out_color_space=JCS_EXT_ABGR; break; + #endif + #else + case TJPF_RGB: + case TJPF_BGR: + case TJPF_RGBX: + case TJPF_BGRX: + case TJPF_XRGB: + case TJPF_XBGR: + case TJPF_RGBA: + case TJPF_BGRA: + case TJPF_ARGB: + case TJPF_ABGR: + dinfo->out_color_space=JCS_RGB; break; + #endif + default: + _throw("Unsupported pixel format"); + } + + bailout: + return retval; +} + + +static int getSubsamp(j_decompress_ptr dinfo) +{ + int retval=-1, i, k; + for(i=0; i<NUMSUBOPT; i++) + { + if(dinfo->num_components==pixelsize[i]) + { + if(dinfo->comp_info[0].h_samp_factor==tjMCUWidth[i]/8 + && dinfo->comp_info[0].v_samp_factor==tjMCUHeight[i]/8) + { + int match=0; + for(k=1; k<dinfo->num_components; k++) + { + if(dinfo->comp_info[k].h_samp_factor==1 + && dinfo->comp_info[k].v_samp_factor==1) + match++; + } + if(match==dinfo->num_components-1) + { + retval=i; break; + } + } + } + } + return retval; +} + + +#ifndef JCS_EXTENSIONS + +/* Conversion functions to emulate the colorspace extensions. This allows the + TurboJPEG wrapper to be used with libjpeg */ + +#define TORGB(PS, ROFFSET, GOFFSET, BOFFSET) { \ + int rowPad=pitch-width*PS; \ + while(height--) \ + { \ + unsigned char *endOfRow=src+width*PS; \ + while(src<endOfRow) \ + { \ + dst[RGB_RED]=src[ROFFSET]; \ + dst[RGB_GREEN]=src[GOFFSET]; \ + dst[RGB_BLUE]=src[BOFFSET]; \ + dst+=RGB_PIXELSIZE; src+=PS; \ + } \ + src+=rowPad; \ + } \ +} + +static unsigned char *toRGB(unsigned char *src, int width, int pitch, + int height, int pixelFormat, unsigned char *dst) +{ + unsigned char *retval=src; + switch(pixelFormat) + { + case TJPF_RGB: + #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3 + retval=dst; TORGB(3, 0, 1, 2); + #endif + break; + case TJPF_BGR: + #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3 + retval=dst; TORGB(3, 2, 1, 0); + #endif + break; + case TJPF_RGBX: + case TJPF_RGBA: + #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4 + retval=dst; TORGB(4, 0, 1, 2); + #endif + break; + case TJPF_BGRX: + case TJPF_BGRA: + #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4 + retval=dst; TORGB(4, 2, 1, 0); + #endif + break; + case TJPF_XRGB: + case TJPF_ARGB: + #if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4 + retval=dst; TORGB(4, 1, 2, 3); + #endif + break; + case TJPF_XBGR: + case TJPF_ABGR: + #if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4 + retval=dst; TORGB(4, 3, 2, 1); + #endif + break; + } + return retval; +} -static const int hsampfactor[NUMSUBOPT]={1, 2, 2, 1}; -static const int vsampfactor[NUMSUBOPT]={1, 1, 2, 1}; -static const int pixelsize[NUMSUBOPT]={3, 3, 3, 1}; +#define FROMRGB(PS, ROFFSET, GOFFSET, BOFFSET, SETALPHA) { \ + int rowPad=pitch-width*PS; \ + while(height--) \ + { \ + unsigned char *endOfRow=dst+width*PS; \ + while(dst<endOfRow) \ + { \ + dst[ROFFSET]=src[RGB_RED]; \ + dst[GOFFSET]=src[RGB_GREEN]; \ + dst[BOFFSET]=src[RGB_BLUE]; \ + SETALPHA \ + dst+=PS; src+=RGB_PIXELSIZE; \ + } \ + dst+=rowPad; \ + } \ +} -#define _throw(c) {sprintf(lasterror, "%s", c); retval=-1; goto bailout;} -#define checkhandle(h) jpgstruct *j=(jpgstruct *)h; \ - if(!j) {sprintf(lasterror, "Invalid handle"); return -1;} +static void fromRGB(unsigned char *src, unsigned char *dst, int width, + int pitch, int height, int pixelFormat) +{ + switch(pixelFormat) + { + case TJPF_RGB: + #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=3 + FROMRGB(3, 0, 1, 2,); + #endif + break; + case TJPF_BGR: + #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=3 + FROMRGB(3, 2, 1, 0,); + #endif + break; + case TJPF_RGBX: + #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4 + FROMRGB(4, 0, 1, 2,); + #endif + break; + case TJPF_RGBA: + #if RGB_RED!=0 || RGB_GREEN!=1 || RGB_BLUE!=2 || RGB_PIXELSIZE!=4 + FROMRGB(4, 0, 1, 2, dst[3]=0xFF;); + #endif + break; + case TJPF_BGRX: + #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4 + FROMRGB(4, 2, 1, 0,); + #endif + break; + case TJPF_BGRA: + #if RGB_RED!=2 || RGB_GREEN!=1 || RGB_BLUE!=0 || RGB_PIXELSIZE!=4 + FROMRGB(4, 2, 1, 0, dst[3]=0xFF;); return; + #endif + break; + case TJPF_XRGB: + #if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4 + FROMRGB(4, 1, 2, 3,); return; + #endif + break; + case TJPF_ARGB: + #if RGB_RED!=1 || RGB_GREEN!=2 || RGB_BLUE!=3 || RGB_PIXELSIZE!=4 + FROMRGB(4, 1, 2, 3, dst[0]=0xFF;); return; + #endif + break; + case TJPF_XBGR: + #if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4 + FROMRGB(4, 3, 2, 1,); return; + #endif + break; + case TJPF_ABGR: + #if RGB_RED!=3 || RGB_GREEN!=2 || RGB_BLUE!=1 || RGB_PIXELSIZE!=4 + FROMRGB(4, 3, 2, 1, dst[0]=0xFF;); return; + #endif + break; + } +} +#endif -// CO -static boolean empty_output_buffer(struct jpeg_compress_struct *cinfo) +/* General API functions */ + +DLLEXPORT char* DLLCALL tjGetErrorStr(void) +{ + return errStr; +} + + +DLLEXPORT int DLLCALL tjDestroy(tjhandle handle) +{ + getinstance(handle); + if(setjmp(this->jerr.setjmp_buffer)) return -1; + if(this->init&COMPRESS) jpeg_destroy_compress(cinfo); + if(this->init&DECOMPRESS) jpeg_destroy_decompress(dinfo); + free(this); + return 0; +} + + +/* Compressor */ + +static boolean empty_output_buffer(j_compress_ptr cinfo) { ERREXIT(cinfo, JERR_BUFFER_SIZE); return TRUE; } -static void destination_noop(struct jpeg_compress_struct *cinfo) +static void dst_noop(j_compress_ptr cinfo) { } +static tjhandle _tjInitCompress(tjinstance *this) +{ + /* This is also straight out of example.c */ + this->cinfo.err=jpeg_std_error(&this->jerr.pub); + this->jerr.pub.error_exit=my_error_exit; + this->jerr.pub.output_message=my_output_message; + + if(setjmp(this->jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. */ + if(this) free(this); return NULL; + } + + jpeg_create_compress(&this->cinfo); + this->cinfo.dest=&this->jdst; + this->jdst.init_destination=dst_noop; + this->jdst.empty_output_buffer=empty_output_buffer; + this->jdst.term_destination=dst_noop; + + this->init|=COMPRESS; + return (tjhandle)this; +} + 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; + tjinstance *this=NULL; + if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL) + { + snprintf(errStr, JMSG_LENGTH_MAX, + "tjInitCompress(): Memory allocation failure"); + return NULL; } + MEMZERO(this, sizeof(tjinstance)); + return _tjInitCompress(this); +} + - 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; +DLLEXPORT unsigned long DLLCALL tjBufSize(int width, int height, + int jpegSubsamp) +{ + unsigned long retval=0; int mcuw, mcuh, chromasf; + if(width<1 || height<1 || jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT) + _throw("tjBufSize(): Invalid argument"); - j->initc=1; - return (tjhandle)j; + // This allows for rare corner cases in which a JPEG image can actually be + // larger than the uncompressed input (we wouldn't mention it if it hadn't + // happened before.) + mcuw=tjMCUWidth[jpegSubsamp]; + mcuh=tjMCUHeight[jpegSubsamp]; + chromasf=jpegSubsamp==TJSAMP_GRAY? 0: 4*64/(mcuw*mcuh); + retval=PAD(width, mcuw) * PAD(height, mcuh) * (2 + chromasf) + 2048; + + bailout: + return retval; } @@ -114,311 +519,332 @@ DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height) { unsigned long retval=0; if(width<1 || height<1) - _throw("Invalid argument in TJBUFSIZE()"); + _throw("TJBUFSIZE(): Invalid argument"); // This allows for rare corner cases in which a JPEG image can actually be // larger than the uncompressed input (we wouldn't mention it if it hadn't // happened before.) - retval=((width+15)&(~15)) * ((height+15)&(~15)) * 6 + 2048; + retval=PAD(width, 16) * PAD(height, 16) * 6 + 2048; bailout: return retval; } -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) +DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags) { int i, retval=0; 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 && ps!=1) - _throw("This compressor can only handle 24-bit and 32-bit RGB or 8-bit grayscale 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(ps==1) j->cinfo.in_color_space = JCS_GRAYSCALE; - #if JCS_EXTENSIONS==1 - else 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" + #ifndef JCS_EXTENSIONS + unsigned char *rgbBuf=NULL; #endif - if(flags&TJ_FORCEMMX) putenv("JSIMD_FORCEMMX=1"); - else if(flags&TJ_FORCESSE) putenv("JSIMD_FORCESSE=1"); - else if(flags&TJ_FORCESSE2) putenv("JSIMD_FORCESSE2=1"); + getinstance(handle) + if((this->init&COMPRESS)==0) + _throw("tjCompress2(): Instance has not been initialized for compression"); - if(setjmp(j->jerr.jb)) - { // this will execute if LIBJPEG has an error + if(srcBuf==NULL || width<=0 || pitch<0 || height<=0 || pixelFormat<0 + || pixelFormat>=TJ_NUMPF || jpegBuf==NULL || jpegSize==NULL + || jpegSubsamp<0 || jpegSubsamp>=NUMSUBOPT || jpegQual<0 || jpegQual>100) + _throw("tjCompress2(): Invalid argument"); + + if(setjmp(this->jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. */ retval=-1; goto bailout; } - jpeg_set_defaults(&j->cinfo); + if(pitch==0) pitch=width*tjPixelSize[pixelFormat]; - 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); - if(qual>=96) j->cinfo.dct_method=JDCT_ISLOW; - else j->cinfo.dct_method=JDCT_FASTEST; + #ifndef JCS_EXTENSIONS + if(pixelFormat!=TJPF_GRAY) + { + rgbBuf=(unsigned char *)malloc(width*height*RGB_PIXELSIZE); + if(!rgbBuf) _throw("tjCompress2(): Memory allocation failure"); + srcBuf=toRGB(srcBuf, width, pitch, height, pixelFormat, rgbBuf); + pitch=width*RGB_PIXELSIZE; + } + #endif + + cinfo->image_width=width; + cinfo->image_height=height; + + if(flags&TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1"); + else if(flags&TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1"); + else if(flags&TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1"); - 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; + if(setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual)==-1) + return -1; - j->jdms.next_output_byte = dstbuf; - j->jdms.free_in_buffer = TJBUFSIZE(j->cinfo.image_width, j->cinfo.image_height); + this->jdst.next_output_byte=*jpegBuf; + this->jdst.free_in_buffer=tjBufSize(width, height, jpegSubsamp); - jpeg_start_compress(&j->cinfo, TRUE); + jpeg_start_compress(cinfo, TRUE); if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL) - _throw("Memory allocation failed in tjCompress()"); + _throw("tjCompress2(): Memory allocation failure"); 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]; + if(flags&TJFLAG_BOTTOMUP) row_pointer[i]=&srcBuf[(height-i-1)*pitch]; + else row_pointer[i]=&srcBuf[i*pitch]; } - while(j->cinfo.next_scanline<j->cinfo.image_height) + while(cinfo->next_scanline<cinfo->image_height) { - jpeg_write_scanlines(&j->cinfo, &row_pointer[j->cinfo.next_scanline], - j->cinfo.image_height-j->cinfo.next_scanline); + jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline], + cinfo->image_height-cinfo->next_scanline); } - jpeg_finish_compress(&j->cinfo); - *size=TJBUFSIZE(j->cinfo.image_width, j->cinfo.image_height) - -(unsigned long)(j->jdms.free_in_buffer); + jpeg_finish_compress(cinfo); + *jpegSize=tjBufSize(width, height, jpegSubsamp) + -(unsigned long)(this->jdst.free_in_buffer); bailout: - if(j->cinfo.global_state>CSTATE_START) jpeg_abort_compress(&j->cinfo); + if(cinfo->global_state>CSTATE_START) jpeg_abort_compress(cinfo); + #ifndef JCS_EXTENSIONS + if(rgbBuf && rgbBuf!=srcBuf) free(rgbBuf); + #endif if(row_pointer) free(row_pointer); return retval; } +DLLEXPORT int DLLCALL tjCompress(tjhandle handle, unsigned char *srcBuf, + int width, int pitch, int height, int pixelSize, unsigned char *jpegBuf, + unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags) +{ + int retval=0; unsigned long size; + retval=tjCompress2(handle, srcBuf, width, pitch, height, + getPixelFormat(pixelSize, flags), &jpegBuf, &size, jpegSubsamp, jpegQual, + flags); + *jpegSize=size; + return retval; +} + -// DEC +/* Decompressor */ -static boolean fill_input_buffer (struct jpeg_decompress_struct *dinfo) +static boolean fill_input_buffer(j_decompress_ptr dinfo) { ERREXIT(dinfo, JERR_BUFFER_SIZE); return TRUE; } -static void skip_input_data (struct jpeg_decompress_struct *dinfo, long num_bytes) +static void skip_input_data(j_decompress_ptr 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) +static void src_noop(j_decompress_ptr dinfo) { } -DLLEXPORT tjhandle DLLCALL tjInitDecompress(void) +static tjhandle _tjInitDecompress(tjinstance *this) { - 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; + /* This is also straight out of example.c */ + this->dinfo.err=jpeg_std_error(&this->jerr.pub); + this->jerr.pub.error_exit=my_error_exit; + this->jerr.pub.output_message=my_output_message; + + if(setjmp(this->jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. */ + if(this) free(this); 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; + jpeg_create_decompress(&this->dinfo); + this->dinfo.src=&this->jsrc; + this->jsrc.init_source=src_noop; + this->jsrc.fill_input_buffer=fill_input_buffer; + this->jsrc.skip_input_data=skip_input_data; + this->jsrc.resync_to_restart=jpeg_resync_to_restart; + this->jsrc.term_source=src_noop; + + this->init|=DECOMPRESS; + return (tjhandle)this; +} - j->initd=1; - return (tjhandle)j; +DLLEXPORT tjhandle DLLCALL tjInitDecompress(void) +{ + tjinstance *this; + if((this=(tjinstance *)malloc(sizeof(tjinstance)))==NULL) + { + snprintf(errStr, JMSG_LENGTH_MAX, + "tjInitDecompress(): Memory allocation failure"); + return NULL; + } + MEMZERO(this, sizeof(tjinstance)); + return _tjInitDecompress(this); } -DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle h, - unsigned char *srcbuf, unsigned long size, - int *width, int *height, int *jpegsub) +DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle handle, + unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, + int *jpegSubsamp) { - int i, k, retval=0; + int retval=0; - checkhandle(h); + getinstance(handle); + if((this->init&DECOMPRESS)==0) + _throw("tjDecompressHeader2(): Instance has not been initialized for decompression"); - if(srcbuf==NULL || size<=0 || width==NULL || height==NULL || jpegsub==NULL) - _throw("Invalid argument in tjDecompressHeader2()"); - if(!j->initd) _throw("Instance has not been initialized for decompression"); + if(jpegBuf==NULL || jpegSize<=0 || width==NULL || height==NULL + || jpegSubsamp==NULL) + _throw("tjDecompressHeader2(): Invalid argument"); - if(setjmp(j->jerr.jb)) - { // this will execute if LIBJPEG has an error + if(setjmp(this->jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. */ return -1; } - j->jsms.bytes_in_buffer = size; - j->jsms.next_input_byte = srcbuf; + this->jsrc.bytes_in_buffer=jpegSize; + this->jsrc.next_input_byte=jpegBuf; + jpeg_read_header(dinfo, TRUE); - jpeg_read_header(&j->dinfo, TRUE); + *width=dinfo->image_width; + *height=dinfo->image_height; + *jpegSubsamp=getSubsamp(dinfo); - *width=j->dinfo.image_width; *height=j->dinfo.image_height; - *jpegsub=-1; - for(i=0; i<NUMSUBOPT; i++) - { - if(j->dinfo.num_components==pixelsize[i]) - { - if(j->dinfo.comp_info[0].h_samp_factor==hsampfactor[i] - && j->dinfo.comp_info[0].v_samp_factor==vsampfactor[i]) - { - int match=0; - for(k=1; k<j->dinfo.num_components; k++) - { - if(j->dinfo.comp_info[k].h_samp_factor==1 - && j->dinfo.comp_info[k].v_samp_factor==1) - match++; - } - if(match==j->dinfo.num_components-1) - { - *jpegsub=i; break; - } - } - } - } + jpeg_abort_decompress(dinfo); - jpeg_abort_decompress(&j->dinfo); - - if(*jpegsub<0) _throw("Could not determine subsampling type for JPEG image"); - if(*width<1 || *height<1) _throw("Invalid data returned in header"); + if(*jpegSubsamp<0) + _throw("tjDecompressHeader2(): Could not determine subsampling type for JPEG image"); + if(*width<1 || *height<1) + _throw("tjDecompressHeader2(): Invalid data returned in header"); bailout: return retval; } +DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle handle, + unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height) +{ + int jpegSubsamp; + return tjDecompressHeader2(handle, jpegBuf, jpegSize, width, height, + &jpegSubsamp); +} -DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle h, - unsigned char *srcbuf, unsigned long size, - int *width, int *height) + +DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors) { - int jpegsub; - return tjDecompressHeader2(h, srcbuf, size, width, height, &jpegsub); + if(numscalingfactors==NULL) + { + snprintf(errStr, JMSG_LENGTH_MAX, + "tjGetScalingFactors(): Invalid argument"); + return NULL; + } + + *numscalingfactors=NUMSF; + return (tjscalingfactor *)sf; } -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) +DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch, + int height, int pixelFormat, int flags) { int i, retval=0; JSAMPROW *row_pointer=NULL; + int jpegwidth, jpegheight, scaledw, scaledh; + #ifndef JCS_EXTENSIONS + unsigned char *rgbBuf=NULL; + unsigned char *_dstBuf=NULL; int _pitch=0; + #endif - 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 && ps!=1) - _throw("This decompressor can only handle 24-bit and 32-bit RGB or 8-bit grayscale output"); - if(!j->initd) _throw("Instance has not been initialized for decompression"); + getinstance(handle); + if((this->init&DECOMPRESS)==0) + _throw("tjDecompress2(): Instance has not been initialized for decompression"); - if(pitch==0) pitch=width*ps; + if(jpegBuf==NULL || jpegSize<=0 || dstBuf==NULL || width<0 || pitch<0 + || height<0 || pixelFormat<0 || pixelFormat>=TJ_NUMPF) + _throw("tjDecompress2(): Invalid argument"); - if(flags&TJ_FORCEMMX) putenv("JSIMD_FORCEMMX=1"); - else if(flags&TJ_FORCESSE) putenv("JSIMD_FORCESSE=1"); - else if(flags&TJ_FORCESSE2) putenv("JSIMD_FORCESSE2=1"); + if(flags&TJFLAG_FORCEMMX) putenv("JSIMD_FORCEMMX=1"); + else if(flags&TJFLAG_FORCESSE) putenv("JSIMD_FORCESSE=1"); + else if(flags&TJFLAG_FORCESSE2) putenv("JSIMD_FORCESSE2=1"); - if(setjmp(j->jerr.jb)) - { // this will execute if LIBJPEG has an error + if(setjmp(this->jerr.setjmp_buffer)) + { + /* If we get here, the JPEG code has signaled an error. */ retval=-1; goto bailout; } - j->jsms.bytes_in_buffer = size; - j->jsms.next_input_byte = srcbuf; + this->jsrc.bytes_in_buffer=jpegSize; + this->jsrc.next_input_byte=jpegBuf; + jpeg_read_header(dinfo, TRUE); + if(setDecompDefaults(dinfo, pixelFormat)==-1) + { + retval=-1; goto bailout; + } - jpeg_read_header(&j->dinfo, TRUE); + if(flags&TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling=FALSE; - if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL) - _throw("Memory allocation failed in tjDecompress()"); - for(i=0; i<height; i++) + jpegwidth=dinfo->image_width; jpegheight=dinfo->image_height; + if(width==0) width=jpegwidth; + if(height==0) height=jpegheight; + for(i=0; i<NUMSF; i++) { - if(flags&TJ_BOTTOMUP) row_pointer[i]= &dstbuf[(height-i-1)*pitch]; - else row_pointer[i]= &dstbuf[i*pitch]; + scaledw=TJSCALED(jpegwidth, sf[i]); + scaledh=TJSCALED(jpegheight, sf[i]); + if(scaledw<=width && scaledh<=height) + break; + } + if(scaledw>width || scaledh>height) + _throw("tjDecompress2(): Could not scale down to desired image dimensions"); + width=scaledw; height=scaledh; + dinfo->scale_num=sf[i].num; + dinfo->scale_denom=sf[i].denom; + + jpeg_start_decompress(dinfo); + if(pitch==0) pitch=dinfo->output_width*tjPixelSize[pixelFormat]; + + #ifndef JCS_EXTENSIONS + if(pixelFormat!=TJPF_GRAY && + (RGB_RED!=tjRedOffset[pixelFormat] || + RGB_GREEN!=tjGreenOffset[pixelFormat] || + RGB_BLUE!=tjBlueOffset[pixelFormat] || + RGB_PIXELSIZE!=tjPixelSize[pixelFormat])) + { + rgbBuf=(unsigned char *)malloc(width*height*3); + if(!rgbBuf) _throw("tjDecompress2(): Memory allocation failure"); + _pitch=pitch; pitch=width*3; + _dstBuf=dstBuf; dstBuf=rgbBuf; } - - if(ps==1) j->dinfo.out_color_space = JCS_GRAYSCALE; - #if JCS_EXTENSIONS==1 - else 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 - if(flags&TJ_FASTUPSAMPLE) j->dinfo.do_fancy_upsampling=FALSE; - - jpeg_start_decompress(&j->dinfo); - while(j->dinfo.output_scanline<j->dinfo.output_height) + if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW) + *dinfo->output_height))==NULL) + _throw("tjDecompress2(): Memory allocation failure"); + for(i=0; i<(int)dinfo->output_height; i++) + { + if(flags&TJFLAG_BOTTOMUP) + row_pointer[i]=&dstBuf[(dinfo->output_height-i-1)*pitch]; + else row_pointer[i]=&dstBuf[i*pitch]; + } + while(dinfo->output_scanline<dinfo->output_height) { - jpeg_read_scanlines(&j->dinfo, &row_pointer[j->dinfo.output_scanline], - j->dinfo.output_height-j->dinfo.output_scanline); + jpeg_read_scanlines(dinfo, &row_pointer[dinfo->output_scanline], + dinfo->output_height-dinfo->output_scanline); } - jpeg_finish_decompress(&j->dinfo); + jpeg_finish_decompress(dinfo); + + #ifndef JCS_EXTENSIONS + fromRGB(rgbBuf, _dstBuf, width, _pitch, height, pixelFormat); + #endif bailout: - if(j->dinfo.global_state>DSTATE_START) jpeg_abort_decompress(&j->dinfo); + if(dinfo->global_state>DSTATE_START) jpeg_abort_decompress(dinfo); + #ifndef JCS_EXTENSIONS + if(rgbBuf && rgbBuf!=dstBuf) free(rgbBuf); + #endif if(row_pointer) free(row_pointer); return retval; } - -// General - -DLLEXPORT char* DLLCALL tjGetErrorStr(void) -{ - return lasterror; -} - -DLLEXPORT int DLLCALL tjDestroy(tjhandle h) +DLLEXPORT int DLLCALL tjDecompress(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, unsigned char *dstBuf, int width, int pitch, + int height, int pixelSize, int flags) { - 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; + return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch, + height, getPixelFormat(pixelSize, flags), flags); } diff --git a/common/turbojpeg.h b/common/turbojpeg.h index 6e3e259..ab8adda 100644 --- a/common/turbojpeg.h +++ b/common/turbojpeg.h @@ -1,255 +1,529 @@ -/* Copyright (C)2004 Landmark Graphics Corporation - * Copyright (C)2005, 2006 Sun Microsystems, Inc. - * Copyright (C)2009-2011 D. R. Commander +/* + * Copyright (C)2009-2012 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: * - * 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. + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. * - * This 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 SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. */ -#if (defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__)) \ - && defined(_WIN32) && defined(DLLDEFINE) +#ifndef __TURBOJPEG_H__ +#define __TURBOJPEG_H__ + +#if 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_420, TJ_GRAYSCALE}; -#define TJ_411 TJ_420 /* for backward compatibility with VirtualGL <= 2.1.x, - TurboVNC <= 0.6, and TurboJPEG/IPP */ - - -/* Flags */ -#define TJ_BGR 1 - /* The components of each pixel in the source/destination bitmap are stored - in B,G,R order, not R,G,B */ -#define TJ_BOTTOMUP 2 - /* The source/destination bitmap is stored in bottom-up (Windows, OpenGL) - order, not top-down (X11) order */ -#define TJ_FORCEMMX 8 - /* Turn off CPU auto-detection and force TurboJPEG to use MMX code - (IPP and 32-bit libjpeg-turbo versions only) */ -#define TJ_FORCESSE 16 - /* Turn off CPU auto-detection and force TurboJPEG to use SSE code - (32-bit IPP and 32-bit libjpeg-turbo versions only) */ -#define TJ_FORCESSE2 32 - /* Turn off CPU auto-detection and force TurboJPEG to use SSE2 code - (32-bit IPP and 32-bit libjpeg-turbo versions only) */ -#define TJ_ALPHAFIRST 64 - /* If the source/destination bitmap is 32 bpp, assume that each pixel is - ARGB/XRGB (or ABGR/XBGR if TJ_BGR is also specified) */ -#define TJ_FORCESSE3 128 - /* Turn off CPU auto-detection and force TurboJPEG to use SSE3 code - (64-bit IPP version only) */ -#define TJ_FASTUPSAMPLE 256 - /* Use fast, inaccurate 4:2:2 and 4:2:0 YUV upsampling routines - (libjpeg and libjpeg-turbo versions only) */ +/** + * @addtogroup TurboJPEG Lite + * TurboJPEG API. This API provides an interface for generating and decoding + * JPEG images in memory. + * + * @{ + */ + + +/** + * The number of chrominance subsampling options + */ +#define TJ_NUMSAMP 5 + +/** + * Chrominance subsampling options. + * When an image is converted from the RGB to the YCbCr colorspace as part of + * the JPEG compression process, some of the Cb and Cr (chrominance) components + * can be discarded or averaged together 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.) This is called + * "chrominance subsampling". + */ +enum TJSAMP +{ + /** + * 4:4:4 chrominance subsampling (no chrominance subsampling). The JPEG or + * YUV image will contain one chrominance component for every pixel in the + * source image. + */ + TJSAMP_444=0, + /** + * 4:2:2 chrominance subsampling. The JPEG or YUV image will contain one + * chrominance component for every 2x1 block of pixels in the source image. + */ + TJSAMP_422, + /** + * 4:2:0 chrominance subsampling. The JPEG or YUV image will contain one + * chrominance component for every 2x2 block of pixels in the source image. + */ + TJSAMP_420, + /** + * Grayscale. The JPEG or YUV image will contain no chrominance components. + */ + TJSAMP_GRAY, + /** + * 4:4:0 chrominance subsampling. The JPEG or YUV image will contain one + * chrominance component for every 1x2 block of pixels in the source image. + */ + TJSAMP_440 +}; + +/** + * MCU block width (in pixels) for a given level of chrominance subsampling. + * MCU block sizes: + * - 8x8 for no subsampling or grayscale + * - 16x8 for 4:2:2 + * - 8x16 for 4:4:0 + * - 16x16 for 4:2:0 + */ +static const int tjMCUWidth[TJ_NUMSAMP] = {8, 16, 16, 8, 8}; + +/** + * MCU block height (in pixels) for a given level of chrominance subsampling. + * MCU block sizes: + * - 8x8 for no subsampling or grayscale + * - 16x8 for 4:2:2 + * - 8x16 for 4:4:0 + * - 16x16 for 4:2:0 + */ +static const int tjMCUHeight[TJ_NUMSAMP] = {8, 8, 16, 8, 16}; + + +/** + * The number of pixel formats + */ +#define TJ_NUMPF 11 + +/** + * Pixel formats + */ +enum TJPF +{ + /** + * RGB pixel format. The red, green, and blue components in the image are + * stored in 3-byte pixels in the order R, G, B from lowest to highest byte + * address within each pixel. + */ + TJPF_RGB=0, + /** + * BGR pixel format. The red, green, and blue components in the image are + * stored in 3-byte pixels in the order B, G, R from lowest to highest byte + * address within each pixel. + */ + TJPF_BGR, + /** + * RGBX pixel format. The red, green, and blue components in the image are + * stored in 4-byte pixels in the order R, G, B from lowest to highest byte + * address within each pixel. The X component is ignored when compressing + * and undefined when decompressing. + */ + TJPF_RGBX, + /** + * BGRX pixel format. The red, green, and blue components in the image are + * stored in 4-byte pixels in the order B, G, R from lowest to highest byte + * address within each pixel. The X component is ignored when compressing + * and undefined when decompressing. + */ + TJPF_BGRX, + /** + * XBGR pixel format. The red, green, and blue components in the image are + * stored in 4-byte pixels in the order R, G, B from highest to lowest byte + * address within each pixel. The X component is ignored when compressing + * and undefined when decompressing. + */ + TJPF_XBGR, + /** + * XRGB pixel format. The red, green, and blue components in the image are + * stored in 4-byte pixels in the order B, G, R from highest to lowest byte + * address within each pixel. The X component is ignored when compressing + * and undefined when decompressing. + */ + TJPF_XRGB, + /** + * Grayscale pixel format. Each 1-byte pixel represents a luminance + * (brightness) level from 0 to 255. + */ + TJPF_GRAY, + /** + * RGBA pixel format. This is the same as @ref TJPF_RGBX, except that when + * decompressing, the X component is guaranteed to be 0xFF, which can be + * interpreted as an opaque alpha channel. + */ + TJPF_RGBA, + /** + * BGRA pixel format. This is the same as @ref TJPF_BGRX, except that when + * decompressing, the X component is guaranteed to be 0xFF, which can be + * interpreted as an opaque alpha channel. + */ + TJPF_BGRA, + /** + * ABGR pixel format. This is the same as @ref TJPF_XBGR, except that when + * decompressing, the X component is guaranteed to be 0xFF, which can be + * interpreted as an opaque alpha channel. + */ + TJPF_ABGR, + /** + * ARGB pixel format. This is the same as @ref TJPF_XRGB, except that when + * decompressing, the X component is guaranteed to be 0xFF, which can be + * interpreted as an opaque alpha channel. + */ + TJPF_ARGB +}; + +/** + * Red offset (in bytes) for a given pixel format. This specifies the number + * of bytes that the red component is offset from the start of the pixel. For + * instance, if a pixel of format TJ_BGRX is stored in <tt>char pixel[]</tt>, + * then the red component will be <tt>pixel[tjRedOffset[TJ_BGRX]]</tt>. + */ +static const int tjRedOffset[TJ_NUMPF] = {0, 2, 0, 2, 3, 1, 0, 0, 2, 3, 1}; +/** + * Green offset (in bytes) for a given pixel format. This specifies the number + * of bytes that the green component is offset from the start of the pixel. + * For instance, if a pixel of format TJ_BGRX is stored in + * <tt>char pixel[]</tt>, then the green component will be + * <tt>pixel[tjGreenOffset[TJ_BGRX]]</tt>. + */ +static const int tjGreenOffset[TJ_NUMPF] = {1, 1, 1, 1, 2, 2, 0, 1, 1, 2, 2}; +/** + * Blue offset (in bytes) for a given pixel format. This specifies the number + * of bytes that the Blue component is offset from the start of the pixel. For + * instance, if a pixel of format TJ_BGRX is stored in <tt>char pixel[]</tt>, + * then the blue component will be <tt>pixel[tjBlueOffset[TJ_BGRX]]</tt>. + */ +static const int tjBlueOffset[TJ_NUMPF] = {2, 0, 2, 0, 1, 3, 0, 2, 0, 1, 3}; + +/** + * Pixel size (in bytes) for a given pixel format. + */ +static const int tjPixelSize[TJ_NUMPF] = {3, 3, 4, 4, 4, 4, 1, 4, 4, 4, 4}; +/** + * The uncompressed source/destination image is stored in bottom-up (Windows, + * OpenGL) order, not top-down (X11) order. + */ +#define TJFLAG_BOTTOMUP 2 +/** + * Turn off CPU auto-detection and force TurboJPEG to use MMX code (IPP and + * 32-bit libjpeg-turbo versions only.) + */ +#define TJFLAG_FORCEMMX 8 +/** + * Turn off CPU auto-detection and force TurboJPEG to use SSE code (32-bit IPP + * and 32-bit libjpeg-turbo versions only) + */ +#define TJFLAG_FORCESSE 16 +/** + * Turn off CPU auto-detection and force TurboJPEG to use SSE2 code (32-bit IPP + * and 32-bit libjpeg-turbo versions only) + */ +#define TJFLAG_FORCESSE2 32 +/** + * Turn off CPU auto-detection and force TurboJPEG to use SSE3 code (64-bit IPP + * version only) + */ +#define TJFLAG_FORCESSE3 128 +/** + * Use fast, inaccurate chrominance upsampling routines in the JPEG + * decompressor (libjpeg and libjpeg-turbo versions only) + */ +#define TJFLAG_FASTUPSAMPLE 256 + + +/** + * Scaling factor + */ +typedef struct +{ + /** + * Numerator + */ + int num; + /** + * Denominator + */ + int denom; +} tjscalingfactor; + + +/** + * TurboJPEG instance handle + */ typedef void* tjhandle; -#define TJPAD(p) (((p)+3)&(~3)) -#ifndef max - #define max(a,b) ((a)>(b)?(a):(b)) -#endif + +/** + * Pad the given width to the nearest 32-bit boundary + */ +#define TJPAD(width) (((width)+3)&(~3)) + +/** + * Compute the scaled value of <tt>dimension</tt> using the given scaling + * factor. This macro performs the integer equivalent of <tt>ceil(dimension * + * scalingFactor)</tt>. + */ +#define TJSCALED(dimension, scalingFactor) ((dimension * scalingFactor.num \ + + scalingFactor.denom - 1) / scalingFactor.denom) #ifdef __cplusplus extern "C" { #endif -/* API follows */ - -/* - tjhandle tjInitCompress(void) +/** + * Create a TurboJPEG compressor instance. + * + * @return a handle to the newly-created instance, or NULL if an error + * occurred (see #tjGetErrorStr().) + */ +DLLEXPORT tjhandle DLLCALL 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 may cause performance to suffer in some - TurboJPEG implementations. - RETURNS: NULL on error +/** + * Compress an RGB or grayscale image into a JPEG image. + * + * @param handle a handle to a TurboJPEG compressor or transformer instance + * @param srcBuf pointer to an image buffer containing RGB or grayscale pixels + * to be compressed + * @param width width (in pixels) of the source image + * @param pitch bytes per line of the source image. Normally, this should be + * <tt>width * #tjPixelSize[pixelFormat]</tt> if the image is unpadded, + * or <tt>#TJPAD(width * #tjPixelSize[pixelFormat])</tt> if each line of + * the image is padded to the nearest 32-bit boundary, as is the case + * for Windows bitmaps. You can also be clever and use this parameter + * to skip lines, etc. Setting this parameter to 0 is the equivalent of + * setting it to <tt>width * #tjPixelSize[pixelFormat]</tt>. + * @param height height (in pixels) of the source image + * @param pixelFormat pixel format of the source image (see @ref TJPF + * "Pixel formats".) + * @param jpegBuf address of a pointer to an image buffer that will receive the + * JPEG image. TurboJPEG has the ability to reallocate the JPEG buffer + * to accommodate the size of the JPEG image. Thus, you can choose to: + * -# pre-allocate the JPEG buffer with an arbitrary size using + * #tjAlloc() and let TurboJPEG grow the buffer as needed, + * -# set <tt>*jpegBuf</tt> to NULL to tell TurboJPEG to allocate the + * buffer for you, or + * -# pre-allocate the buffer to a "worst case" size determined by + * calling #tjBufSize(). This should ensure that the buffer never has + * to be re-allocated (setting #TJFLAG_NOREALLOC guarantees this.) + * . + * If you choose option 1, <tt>*jpegSize</tt> should be set to the + * size of your pre-allocated buffer. In any case, unless you have + * set #TJFLAG_NOREALLOC, you should always check <tt>*jpegBuf</tt> upon + * return from this function, as it may have changed. + * @param jpegSize pointer to an unsigned long variable that holds the size of + * the JPEG image buffer. If <tt>*jpegBuf</tt> points to a + * pre-allocated buffer, then <tt>*jpegSize</tt> should be set to the + * size of the buffer. Upon return, <tt>*jpegSize</tt> will contain the + * size of the JPEG image (in bytes.) + * @param jpegSubsamp the level of chrominance subsampling to be used when + * generating the JPEG image (see @ref TJSAMP + * "Chrominance subsampling options".) + * @param jpegQual the image quality of the generated JPEG image (1 = worst, + 100 = best) + * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP + * "flags". + * + * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().) */ -DLLEXPORT tjhandle DLLCALL tjInitCompress(void); +DLLEXPORT int DLLCALL tjCompress2(tjhandle handle, unsigned char *srcBuf, + int width, int pitch, int height, int pixelFormat, unsigned char **jpegBuf, + unsigned long *jpegSize, int jpegSubsamp, int jpegQual, int flags); + + +/** + * The maximum size of the buffer (in bytes) required to hold a JPEG image with + * the given parameters. The number of bytes returned by this function is + * larger than the size of the uncompressed source image. The reason for this + * is that the JPEG format uses 16-bit coefficients, and it is thus possible + * for a very high-quality JPEG image with very high frequency content to + * expand rather than compress when converted to the JPEG format. Such images + * represent a very rare corner case, but since there is no way to predict the + * size of a JPEG image prior to compression, the corner case has to be + * handled. + * + * @param width width of the image (in pixels) + * @param height height of the image (in pixels) + * @param jpegSubsamp the level of chrominance subsampling to be used when + * generating the JPEG image (see @ref TJSAMP + * "Chrominance subsampling options".) + * + * @return the maximum size of the buffer (in bytes) required to hold the + * image, or -1 if the arguments are out of bounds. + */ +DLLEXPORT unsigned long DLLCALL tjBufSize(int width, int height, + int jpegSubsamp); -/* - 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 RGB or - grayscale pixels to be compressed - [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. Setting this parameter to 0 is the equivalent of setting it to - width*pixelsize. - [INPUT] height = height (in pixels) of the source image - [INPUT] pixelsize = size (in bytes) of each pixel in the source image - RGBX/BGRX/XRGB/XBGR: 4, RGB/BGR: 3, Grayscale: 1 - [INPUT] dstbuf = pointer to user-allocated image buffer that will receive - the JPEG image. Use the TJBUFSIZE(width, height) function to determine - the appropriate size for this buffer based on the image width and height. - [OUTPUT] size = pointer to unsigned long that receives the size (in bytes) - of the compressed image - [INPUT] jpegsubsamp = Specifies either 4:2:0, 4:2:2, 4:4:4, or grayscale - 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_420: 4:2:0 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 flags described in the - "Flags" section above - - RETURNS: 0 on success, -1 on error +/** + * Create a TurboJPEG decompressor instance. + * + * @return a handle to the newly-created instance, or NULL if an error + * occurred (see #tjGetErrorStr().) */ -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 tjhandle DLLCALL tjInitDecompress(void); -/* - unsigned long TJBUFSIZE(int width, int height) +/** + * Retrieve information about a JPEG image without decompressing it. + * + * @param handle a handle to a TurboJPEG decompressor or transformer instance + * @param jpegBuf pointer to a buffer containing a JPEG image + * @param jpegSize size of the JPEG image (in bytes) + * @param width pointer to an integer variable that will receive the width (in + * pixels) of the JPEG image + * @param height pointer to an integer variable that will receive the height + * (in pixels) of the JPEG image + * @param jpegSubsamp pointer to an integer variable that will receive the + * level of chrominance subsampling used when compressing the JPEG image + * (see @ref TJSAMP "Chrominance subsampling options".) + * + * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().) +*/ +DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle handle, + unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height, + int *jpegSubsamp); - Convenience function that returns the maximum size of the buffer required to - hold a JPEG image with the given width and height - RETURNS: -1 if arguments are out of bounds +/** + * Returns a list of fractional scaling factors that the JPEG decompressor in + * this implementation of TurboJPEG supports. + * + * @param numscalingfactors pointer to an integer variable that will receive + * the number of elements in the list + * + * @return a pointer to a list of fractional scaling factors, or NULL if an + * error is encountered (see #tjGetErrorStr().) */ -DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height); - +DLLEXPORT tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors); -/* - 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 may cause performance to suffer in some - TurboJPEG implementations. +/** + * Decompress a JPEG image to an RGB or grayscale image. + * + * @param handle a handle to a TurboJPEG decompressor or transformer instance + * @param jpegBuf pointer to a buffer containing the JPEG image to decompress + * @param jpegSize size of the JPEG image (in bytes) + * @param dstBuf pointer to an image buffer that will receive the decompressed + * image. This buffer should normally be <tt>pitch * scaledHeight</tt> + * bytes in size, where <tt>scaledHeight</tt> can be determined by + * calling #TJSCALED() with the JPEG image height and one of the scaling + * factors returned by #tjGetScalingFactors(). The dstBuf pointer may + * also be used to decompress into a specific region of a larger buffer. + * @param width desired width (in pixels) of the destination image. If this is + * smaller than the width of the JPEG image being decompressed, then + * TurboJPEG will use scaling in the JPEG decompressor to generate the + * largest possible image that will fit within the desired width. If + * width is set to 0, then only the height will be considered when + * determining the scaled image size. + * @param pitch bytes per line of the destination image. Normally, this is + * <tt>scaledWidth * #tjPixelSize[pixelFormat]</tt> if the decompressed + * image is unpadded, else <tt>#TJPAD(scaledWidth * + * #tjPixelSize[pixelFormat])</tt> if each line of the decompressed + * image is padded to the nearest 32-bit boundary, as is the case for + * Windows bitmaps. (NOTE: <tt>scaledWidth</tt> can be determined by + * calling #TJSCALED() with the JPEG image width and one of the scaling + * factors returned by #tjGetScalingFactors().) You can also be clever + * and use the pitch parameter to skip lines, etc. Setting this + * parameter to 0 is the equivalent of setting it to <tt>scaledWidth + * * #tjPixelSize[pixelFormat]</tt>. + * @param height desired height (in pixels) of the destination image. If this + * is smaller than the height of the JPEG image being decompressed, then + * TurboJPEG will use scaling in the JPEG decompressor to generate the + * largest possible image that will fit within the desired height. If + * height is set to 0, then only the width will be considered when + * determining the scaled image size. + * @param pixelFormat pixel format of the destination image (see @ref + * TJPF "Pixel formats".) + * @param flags the bitwise OR of one or more of the @ref TJFLAG_BOTTOMUP + * "flags". + * + * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().) + */ +DLLEXPORT int DLLCALL tjDecompress2(tjhandle handle, + unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, + int width, int pitch, int height, int pixelFormat, int flags); - RETURNS: NULL on error -*/ -DLLEXPORT tjhandle DLLCALL tjInitDecompress(void); +/** + * Destroy a TurboJPEG compressor, decompressor, or transformer instance. + * + * @param handle a handle to a TurboJPEG compressor, decompressor or + * transformer instance + * + * @return 0 if successful, or -1 if an error occurred (see #tjGetErrorStr().) + */ +DLLEXPORT int DLLCALL tjDestroy(tjhandle handle); -/* - int tjDecompressHeader2(tjhandle j, - unsigned char *srcbuf, unsigned long size, - int *width, int *height, int *jpegsubsamp) - - [INPUT] j = instance handle previously returned from a call to - tjInitDecompress() - [INPUT] srcbuf = pointer to a user-allocated buffer containing a JPEG image - [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 - [OUTPUT] jpegsubsamp = type of chrominance subsampling used when compressing - the JPEG image - - RETURNS: 0 on success, -1 on error -*/ -DLLEXPORT int DLLCALL tjDecompressHeader2(tjhandle j, - unsigned char *srcbuf, unsigned long size, - int *width, int *height, int *jpegsubsamp); -/* - Legacy version of the above function -*/ -DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle j, - unsigned char *srcbuf, unsigned long size, - int *width, int *height); +/** + * Returns a descriptive error message explaining why the last command failed. + * + * @return a descriptive error message explaining why the last command failed. + */ +DLLEXPORT char* DLLCALL tjGetErrorStr(void); -/* - 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 that 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. Setting this parameter to 0 is the equivalent of setting it - to width*pixelsize. - [INPUT] height = height (in pixels) of the destination image - [INPUT] pixelsize = size (in bytes) of each pixel in the destination image - RGBX/BGRX/XRGB/XBGR: 4, RGB/BGR: 3, Grayscale: 1 - [INPUT] flags = the bitwise OR of one or more of the flags described in the - "Flags" section above. - - 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); +/* Backward compatibility functions and macros (nothing to see here) */ +#define NUMSUBOPT TJ_NUMSAMP +#define TJ_444 TJSAMP_444 +#define TJ_422 TJSAMP_422 +#define TJ_420 TJSAMP_420 +#define TJ_411 TJSAMP_420 +#define TJ_GRAYSCALE TJSAMP_GRAY +#define TJ_BGR 1 +#define TJ_BOTTOMUP TJFLAG_BOTTOMUP +#define TJ_FORCEMMX TJFLAG_FORCEMMX +#define TJ_FORCESSE TJFLAG_FORCESSE +#define TJ_FORCESSE2 TJFLAG_FORCESSE2 +#define TJ_ALPHAFIRST 64 +#define TJ_FORCESSE3 TJFLAG_FORCESSE3 +#define TJ_FASTUPSAMPLE TJFLAG_FASTUPSAMPLE -/* - int tjDestroy(tjhandle h) +DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height); - Frees structures associated with a compression or decompression instance - - [INPUT] h = instance handle (returned from a previous call to - tjInitCompress() or tjInitDecompress() +DLLEXPORT int DLLCALL tjCompress(tjhandle handle, unsigned char *srcBuf, + int width, int pitch, int height, int pixelSize, unsigned char *dstBuf, + unsigned long *compressedSize, int jpegSubsamp, int jpegQual, int flags); - RETURNS: 0 on success, -1 on error -*/ -DLLEXPORT int DLLCALL tjDestroy(tjhandle h); +DLLEXPORT int DLLCALL tjDecompressHeader(tjhandle handle, + unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height); +DLLEXPORT int DLLCALL tjDecompress(tjhandle handle, + unsigned char *jpegBuf, unsigned long jpegSize, unsigned char *dstBuf, + int width, int pitch, int height, int pixelSize, int flags); -/* - char *tjGetErrorStr(void) - - Returns a descriptive error message explaining why the last command failed -*/ -DLLEXPORT char* DLLCALL tjGetErrorStr(void); +/** + * @} + */ #ifdef __cplusplus } #endif + +#endif diff --git a/configure.ac b/configure.ac index 211cad6..ddfe5e0 100644 --- a/configure.ac +++ b/configure.ac @@ -612,31 +612,18 @@ A copy of libjpeg may be obtained from: http://ijg.org/files/ [AC_MSG_RESULT(no)]) fi fi -fi - -AC_ARG_WITH(turbovnc, -[ --with-turbovnc use TurboVNC encoder instead of TightVNC encoder],,) -AC_MSG_CHECKING(whether to enable TurboVNC encoder) -if test "x$with_turbovnc" = "xyes"; then if test "x$HAVE_LIBJPEG_TURBO" != "xtrue"; then - AC_MSG_ERROR([ + AC_MSG_WARN([ ========================================================================== -*** The TurboVNC encoder requires libjpeg-turbo, which was not detected. -You can obtain libjpeg-turbo from: -https://sourceforge.net/projects/libjpeg-turbo/files/ -Optionally, you can pass --without-turbovnc to configure to use the -TightVNC encoder instead. *** +*** The libjpeg library you are building against is not libjpeg-turbo. +Performance will be reduced. You can obtain libjpeg-turbo from: +https://sourceforge.net/projects/libjpeg-turbo/files/ *** ========================================================================== ]) - fi - AC_DEFINE(HAVE_TURBOVNC, 1, TurboVNC support enabled) - AC_MSG_RESULT(yes) -else - AC_MSG_RESULT(no) -fi + fi -AM_CONDITIONAL(HAVE_TURBOVNC, test "x$with_turbovnc" = "xyes" ) +fi AC_ARG_WITH(png, [ --without-png disable support for png] @@ -648,7 +635,7 @@ AC_ARG_WITH(png, # -without-png with_png="no" # -with-png=/foo/dir with_png="/foo/dir" -if test "x$with_png" != "xno" -a "x$with_turbovnc" != "xyes"; then +if test "x$with_png" != "xno"; then if test ! -z "$with_png" -a "x$with_png" != "xyes"; then # add user supplied directory to flags: saved_CPPFLAGS="$CPPFLAGS" diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 6a29b84..c0cd4c3 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -46,11 +46,7 @@ EXTRA_DIST=tableinit24.c tableinittctemplate.c tabletranstemplate.c \ if HAVE_LIBZ ZLIBSRCS = zlib.c zrle.c zrleoutstream.c zrlepalettehelper.c ../common/zywrletemplate.c if HAVE_LIBJPEG -if HAVE_TURBOVNC -TIGHTSRCS = turbo.c ../common/turbojpeg.c -else -TIGHTSRCS = tight.c -endif +TIGHTSRCS = tight.c ../common/turbojpeg.c endif endif diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 3f4b896..12d1d72 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2011 D. R. Commander + * Copyright (C) 2011-2012 D. R. Commander * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin * Copyright (C) 2002 RealVNC Ltd. * OSXvnc Copyright (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>. @@ -86,10 +86,10 @@ static int compat_mkdir(const char *path, int mode) #define mkdir compat_mkdir #endif -#ifdef LIBVNCSERVER_HAVE_TURBOVNC +#ifdef LIBVNCSERVER_HAVE_LIBJPEG /* * Map of quality levels to provide compatibility with TightVNC/TigerVNC - * clients + * clients. This emulates the behavior of the TigerVNC Server. */ static const int tight2turbo_qual[10] = { @@ -383,10 +383,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, #if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; #endif -#ifdef LIBVNCSERVER_HAVE_TURBOVNC - cl->tightSubsampLevel = TIGHT_DEFAULT_SUBSAMP; -#endif #ifdef LIBVNCSERVER_HAVE_LIBJPEG + cl->turboSubsampLevel = TURBO_DEFAULT_SUBSAMP; { int i; for (i = 0; i < 4; i++) @@ -1969,11 +1967,12 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) cl->enableServerIdentity = FALSE; #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightQualityLevel = -1; -#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_TURBOVNC) || defined(LIBVNCSERVER_HAVE_LIBPNG) +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; #endif -#ifdef LIBVNCSERVER_HAVE_TURBOVNC - cl->tightSubsampLevel = TIGHT_DEFAULT_SUBSAMP; +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + cl->turboSubsampLevel = TURBO_DEFAULT_SUBSAMP; + cl->turboQualityLevel = -1; #endif #endif @@ -2105,29 +2104,26 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) rfbLog("Using compression level %d for client %s\n", cl->tightCompressLevel, cl->host); #endif -#ifdef LIBVNCSERVER_HAVE_TURBOVNC - } else if ( enc >= (uint32_t)rfbEncodingSubsamp1X && - enc <= (uint32_t)rfbEncodingSubsampGray ) { - cl->tightSubsampLevel = enc & 0xFF; - rfbLog("Using JPEG subsampling %d for client %s\n", - cl->tightSubsampLevel, cl->host); - } else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 && - enc <= (uint32_t)rfbEncodingQualityLevel9 ) { - cl->tightQualityLevel = tight2turbo_qual[enc & 0x0F]; - cl->tightSubsampLevel = tight2turbo_subsamp[enc & 0x0F]; - rfbLog("Using JPEG subsampling %d, Q%d for client %s\n", - cl->tightSubsampLevel, cl->tightQualityLevel, cl->host); - } else if ( enc >= (uint32_t)rfbEncodingFineQualityLevel0 + 1 && - enc <= (uint32_t)rfbEncodingFineQualityLevel100 ) { - cl->tightQualityLevel = enc & 0xFF; - rfbLog("Using image quality level %d for client %s\n", - cl->tightQualityLevel, cl->host); -#else } else if ( enc >= (uint32_t)rfbEncodingQualityLevel0 && enc <= (uint32_t)rfbEncodingQualityLevel9 ) { cl->tightQualityLevel = enc & 0x0F; rfbLog("Using image quality level %d for client %s\n", cl->tightQualityLevel, cl->host); +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + cl->turboQualityLevel = tight2turbo_qual[enc & 0x0F]; + cl->turboSubsampLevel = tight2turbo_subsamp[enc & 0x0F]; + rfbLog("Using JPEG subsampling %d, Q%d for client %s\n", + cl->turboSubsampLevel, cl->turboQualityLevel, cl->host); + } else if ( enc >= (uint32_t)rfbEncodingFineQualityLevel0 + 1 && + enc <= (uint32_t)rfbEncodingFineQualityLevel100 ) { + cl->turboQualityLevel = enc & 0xFF; + rfbLog("Using fine quality level %d for client %s\n", + cl->turboQualityLevel, cl->host); + } else if ( enc >= (uint32_t)rfbEncodingSubsamp1X && + enc <= (uint32_t)rfbEncodingSubsampGray ) { + cl->turboSubsampLevel = enc & 0xFF; + rfbLog("Using subsampling level %d for client %s\n", + cl->turboSubsampLevel, cl->host); #endif } else #endif diff --git a/libvncserver/tight.c b/libvncserver/tight.c index 474e9e3..3650458 100644 --- a/libvncserver/tight.c +++ b/libvncserver/tight.c @@ -2,9 +2,20 @@ * tight.c * * Routines to implement Tight Encoding + * + * Our Tight encoder is based roughly on the TurboVNC v0.6 encoder with some + * additional enhancements from TurboVNC 1.1. For lower compression levels, + * this encoder provides a tremendous reduction in CPU usage (and subsequently, + * an increase in throughput for CPU-limited environments) relative to the + * TightVNC encoder, whereas Compression Level 9 provides a low-bandwidth mode + * that behaves similarly to Compression Levels 5-9 in the old TightVNC + * encoder. */ /* + * Copyright (C) 2010-2012 D. R. Commander. All Rights Reserved. + * Copyright (C) 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved. * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * @@ -20,27 +31,18 @@ * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ -/*#include <stdio.h>*/ #include <rfb/rfb.h> #include "private.h" -#ifdef WIN32 -#define XMD_H -#undef FAR -#define NEEDFAR_POINTERS -#endif - -#ifdef _RPCNDR_H /* This Windows header typedefs 'boolean', jpeglib has to know */ -#define HAVE_BOOLEAN -#endif #ifdef LIBVNCSERVER_HAVE_LIBPNG #include <png.h> #endif -#include <jpeglib.h> +#include "turbojpeg.h" + /* Note: The following constant should not be changed. */ #define TIGHT_MIN_TO_COMPRESS 12 @@ -50,9 +52,6 @@ #define MIN_SOLID_SUBRECT_SIZE 2048 #define MAX_SPLIT_TILE_SIZE 16 -/* May be set to TRUE with "-lazytight" Xvnc option. */ -rfbBool rfbTightDisableGradient = FALSE; - /* * There is so much access of the Tight encoding static data buffers * that we resort to using thread local storage instead of having @@ -68,30 +67,24 @@ rfbBool rfbTightDisableGradient = FALSE; /* This variable is set on every rfbSendRectEncodingTight() call. */ static TLS rfbBool usePixelFormat24 = FALSE; + /* Compression level stuff. The following array contains various encoder parameters for each of 10 compression levels (0..9). Last three parameters correspond to JPEG quality levels (0..9). */ typedef struct TIGHT_CONF_s { int maxRectSize, maxRectWidth; - int monoMinRectSize, gradientMinRectSize; - int idxZlibLevel, monoZlibLevel, rawZlibLevel, gradientZlibLevel; - int gradientThreshold, gradientThreshold24; + int monoMinRectSize; + int idxZlibLevel, monoZlibLevel, rawZlibLevel; int idxMaxColorsDivisor; - int jpegQuality, jpegThreshold, jpegThreshold24; + int palMaxColorsWithJPEG; } TIGHT_CONF; -static TIGHT_CONF tightConf[10] = { - { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 }, - { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 }, - { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 }, - { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 }, - { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 }, - { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 }, - { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 }, - { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 }, - { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 }, - { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } +static TIGHT_CONF tightConf[4] = { + { 65536, 2048, 6, 0, 0, 0, 4, 24 }, // 0 (used only without JPEG) + { 65536, 2048, 32, 1, 1, 1, 96, 24 }, // 1 + { 65536, 2048, 32, 3, 3, 2, 96, 96 }, // 2 (used only with JPEG) + { 65536, 2048, 32, 7, 7, 5, 96, 256 } // 9 }; #ifdef LIBVNCSERVER_HAVE_LIBPNG @@ -113,8 +106,14 @@ static TIGHT_PNG_CONF tightPngConf[10] = { }; #endif -static TLS int compressLevel = 0; -static TLS int qualityLevel = 0; +static TLS int compressLevel = 1; +static TLS int qualityLevel = 95; +static TLS int subsampLevel = TJ_444; + +static const int subsampLevel2tjsubsamp[4] = { + TJ_444, TJ_420, TJ_422, TJ_GRAYSCALE +}; + /* Stuff dealing with palettes. */ @@ -150,22 +149,24 @@ static TLS char *tightBeforeBuf = NULL; static TLS int tightAfterBufSize = 0; static TLS char *tightAfterBuf = NULL; -static TLS int *prevRowBuf = NULL; +static TLS tjhandle j = NULL; -void rfbTightCleanup(rfbScreenInfoPtr screen) +void rfbTightCleanup (rfbScreenInfoPtr screen) { - if(tightBeforeBufSize) { - free(tightBeforeBuf); - tightBeforeBufSize=0; - tightBeforeBuf = NULL; - } - if(tightAfterBufSize) { - free(tightAfterBuf); - tightAfterBufSize=0; - tightAfterBuf = NULL; - } + if (tightBeforeBufSize) { + free (tightBeforeBuf); + tightBeforeBufSize = 0; + tightBeforeBuf = NULL; + } + if (tightAfterBufSize) { + free (tightAfterBuf); + tightAfterBufSize = 0; + tightAfterBuf = NULL; + } + if (j) tjDestroy(j); } + /* Prototypes for static functions. */ static rfbBool SendRectEncodingTight(rfbClientPtr cl, int x, int y, @@ -176,13 +177,13 @@ static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h, uint32_t colorValue, int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr); static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); + uint32_t *colorPtr, rfbBool needSameColor); static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); + uint32_t *colorPtr, rfbBool needSameColor); static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); + uint32_t *colorPtr, rfbBool needSameColor); static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); + uint32_t *colorPtr, rfbBool needSameColor); static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); @@ -192,49 +193,40 @@ static rfbBool SendSolidRect (rfbClientPtr cl); static rfbBool SendMonoRect (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool SendIndexedRect (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool SendFullColorRect (rfbClientPtr cl, int x, int y, int w, int h); -static rfbBool SendGradientRect (rfbClientPtr cl, int x, int y, int w, int h); - -static rfbBool CompressData(rfbClientPtr cl, int streamId, int dataLen, - int zlibLevel, int zlibStrategy); -static rfbBool SendCompressedData(rfbClientPtr cl, int compressedLen); - -static void FillPalette8(int count); -static void FillPalette16(int count); -static void FillPalette32(int count); -static void PaletteReset(void); -static int PaletteInsert(uint32_t rgb, int numPixels, int bpp); +static rfbBool CompressData (rfbClientPtr cl, int streamId, int dataLen, + int zlibLevel, int zlibStrategy); +static rfbBool SendCompressedData (rfbClientPtr cl, char *buf, + int compressedLen); -static void Pack24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int count); +static void FillPalette8 (int count); +static void FillPalette16 (int count); +static void FillPalette32 (int count); +static void FastFillPalette16 (rfbClientPtr cl, uint16_t *data, int w, + int pitch, int h); +static void FastFillPalette32 (rfbClientPtr cl, uint32_t *data, int w, + int pitch, int h); -static void EncodeIndexedRect16(uint8_t *buf, int count); -static void EncodeIndexedRect32(uint8_t *buf, int count); +static void PaletteReset (void); +static int PaletteInsert (uint32_t rgb, int numPixels, int bpp); -static void EncodeMonoRect8(uint8_t *buf, int w, int h); -static void EncodeMonoRect16(uint8_t *buf, int w, int h); -static void EncodeMonoRect32(uint8_t *buf, int w, int h); +static void Pack24 (rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, + int count); -static void FilterGradient24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int w, int h); -static void FilterGradient16(rfbClientPtr cl, uint16_t *buf, rfbPixelFormat *fmt, int w, int h); -static void FilterGradient32(rfbClientPtr cl, uint32_t *buf, rfbPixelFormat *fmt, int w, int h); +static void EncodeIndexedRect16 (uint8_t *buf, int count); +static void EncodeIndexedRect32 (uint8_t *buf, int count); -static int DetectSmoothImage(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); -static unsigned long DetectSmoothImage24(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); -static unsigned long DetectSmoothImage16(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); -static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); +static void EncodeMonoRect8 (uint8_t *buf, int w, int h); +static void EncodeMonoRect16 (uint8_t *buf, int w, int h); +static void EncodeMonoRect32 (uint8_t *buf, int w, int h); -static rfbBool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, - int quality); +static rfbBool SendJpegRect (rfbClientPtr cl, int x, int y, int w, int h, + int quality); static void PrepareRowForImg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); static void PrepareRowForImg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); static void PrepareRowForImg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); static void PrepareRowForImg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void JpegInitDestination(j_compress_ptr cinfo); -static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo); -static void JpegTermDestination(j_compress_ptr cinfo); -static void JpegSetDstManager(j_compress_ptr cinfo); - #ifdef LIBVNCSERVER_HAVE_LIBPNG static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h); static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h); @@ -257,10 +249,10 @@ rfbNumCodedRectsTight(rfbClientPtr cl, /* No matter how many rectangles we will send if LastRect markers are used to terminate rectangle stream. */ if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE) - return 0; + return 0; - maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize; - maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth; + maxRectSize = tightConf[compressLevel].maxRectSize; + maxRectWidth = tightConf[compressLevel].maxRectWidth; if (w > maxRectWidth || w * h > maxRectSize) { subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; @@ -311,7 +303,34 @@ SendRectEncodingTight(rfbClientPtr cl, rfbSendUpdateBuf(cl); compressLevel = cl->tightCompressLevel; - qualityLevel = cl->tightQualityLevel; + qualityLevel = cl->turboQualityLevel; + subsampLevel = cl->turboSubsampLevel; + + /* We only allow compression levels that have a demonstrable performance + benefit. CL 0 with JPEG reduces CPU usage for workloads that have low + numbers of unique colors, but the same thing can be accomplished by + using CL 0 without JPEG (AKA "Lossless Tight.") For those same + low-color workloads, CL 2 can provide typically 20-40% better + compression than CL 1 (with a commensurate increase in CPU usage.) For + high-color workloads, CL 1 should always be used, as higher compression + levels increase CPU usage for these workloads without providing any + significant reduction in bandwidth. */ + if (qualityLevel != -1) { + if (compressLevel < 1) compressLevel = 1; + if (compressLevel > 2) compressLevel = 2; + } + + /* With JPEG disabled, CL 2 offers no significant bandwidth savings over + CL 1, so we don't include it. */ + else if (compressLevel > 1) compressLevel = 1; + + /* CL 9 (which maps internally to CL 3) is included mainly for backward + compatibility with TightVNC Compression Levels 5-9. It should be used + only in extremely low-bandwidth cases in which it can be shown to have a + benefit. For low-color workloads, it provides typically only 10-20% + better compression than CL 2 with JPEG and CL 1 without JPEG, and it + uses, on average, twice as much CPU time. */ + if (compressLevel == 9) compressLevel = 3; if ( cl->format.depth == 24 && cl->format.redMax == 0xFF && cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) { @@ -331,7 +350,7 @@ SendRectEncodingTight(rfbClientPtr cl, tightBeforeBuf = (char *)malloc(tightBeforeBufSize); else tightBeforeBuf = (char *)realloc(tightBeforeBuf, - tightBeforeBufSize); + tightBeforeBufSize); } /* Calculate maximum number of rows in one non-solid rectangle. */ @@ -359,15 +378,24 @@ SendRectEncodingTight(rfbClientPtr cl, } dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? - MAX_SPLIT_TILE_SIZE : (y + h - dy); + MAX_SPLIT_TILE_SIZE : (y + h - dy); for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) { dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ? - MAX_SPLIT_TILE_SIZE : (x + w - dx); + MAX_SPLIT_TILE_SIZE : (x + w - dx); if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) { + if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1) { + uint32_t r = (colorValue >> 16) & 0xFF; + uint32_t g = (colorValue >> 8) & 0xFF; + uint32_t b = (colorValue) & 0xFF; + double y = (0.257 * (double)r) + (0.504 * (double)g) + + (0.098 * (double)b) + 16.; + colorValue = (int)y + (((int)y) << 8) + (((int)y) << 16); + } + /* Get dimensions of solid-color area. */ FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y), @@ -415,12 +443,12 @@ SendRectEncodingTight(rfbClientPtr cl, /* Send remaining rectangles (at right and bottom). */ if ( x_best + w_best != x + w && - !SendRectEncodingTight(cl, x_best+w_best, y_best, - w-(x_best-x)-w_best, h_best) ) + !SendRectEncodingTight(cl, x_best + w_best, y_best, + w - (x_best-x) - w_best, h_best) ) return FALSE; if ( y_best + h_best != y + h && - !SendRectEncodingTight(cl, x, y_best+h_best, - w, h-(y_best-y)-h_best) ) + !SendRectEncodingTight(cl, x, y_best + h_best, + w, h - (y_best-y) - h_best) ) return FALSE; /* Return after all recursive calls are done. */ @@ -437,6 +465,7 @@ SendRectEncodingTight(rfbClientPtr cl, return SendRectSimple(cl, x, y, w, h); } + static void FindBestSolidArea(rfbClientPtr cl, int x, @@ -456,16 +485,16 @@ FindBestSolidArea(rfbClientPtr cl, for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? - MAX_SPLIT_TILE_SIZE : (y + h - dy); + MAX_SPLIT_TILE_SIZE : (y + h - dy); dw = (w_prev > MAX_SPLIT_TILE_SIZE) ? - MAX_SPLIT_TILE_SIZE : w_prev; + MAX_SPLIT_TILE_SIZE : w_prev; if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE)) break; for (dx = x + dw; dx < x + w_prev;) { dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ? - MAX_SPLIT_TILE_SIZE : (x + w_prev - dx); + MAX_SPLIT_TILE_SIZE : (x + w_prev - dx); if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE)) break; dx += dw; @@ -482,6 +511,7 @@ FindBestSolidArea(rfbClientPtr cl, *h_ptr = h_best; } + static void ExtendSolidArea(rfbClientPtr cl, int x, @@ -525,6 +555,7 @@ ExtendSolidArea(rfbClientPtr cl, *w_ptr += cx - (*x_ptr + *w_ptr); } + /* * Check if a rectangle is all of the same color. If needSameColor is * set to non-zero, then also check that its color equals to the @@ -544,6 +575,7 @@ static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint3 } } + #define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ \ static rfbBool \ @@ -554,8 +586,8 @@ CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \ uint##bpp##_t colorValue; \ int dx, dy; \ \ - fbptr = (uint##bpp##_t *) \ - &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + x * (bpp/8)]; \ + fbptr = (uint##bpp##_t *)&cl->scaledScreen->frameBuffer \ + [y * cl->scaledScreen->paddedWidthInBytes + x * (bpp/8)]; \ \ colorValue = *fbptr; \ if (needSameColor && (uint32_t)colorValue != *colorPtr) \ @@ -566,7 +598,8 @@ CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \ if (colorValue != fbptr[dx]) \ return FALSE; \ } \ - fbptr = (uint##bpp##_t *)((uint8_t *)fbptr + cl->scaledScreen->paddedWidthInBytes); \ + fbptr = (uint##bpp##_t *)((uint8_t *)fbptr \ + + cl->scaledScreen->paddedWidthInBytes); \ } \ \ *colorPtr = (uint32_t)colorValue; \ @@ -598,7 +631,7 @@ SendRectSimple(rfbClientPtr cl, int x, int y, int w, int h) tightBeforeBuf = (char *)malloc(tightBeforeBufSize); else tightBeforeBuf = (char *)realloc(tightBeforeBuf, - tightBeforeBufSize); + tightBeforeBufSize); } if (tightAfterBufSize < maxAfterSize) { @@ -607,7 +640,7 @@ SendRectSimple(rfbClientPtr cl, int x, int y, int w, int h) tightAfterBuf = (char *)malloc(tightAfterBufSize); else tightAfterBuf = (char *)realloc(tightAfterBuf, - tightAfterBufSize); + tightAfterBufSize); } if (w > maxRectWidth || w * h > maxRectSize) { @@ -618,7 +651,7 @@ SendRectSimple(rfbClientPtr cl, int x, int y, int w, int h) for (dx = 0; dx < w; dx += maxRectWidth) { rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx; rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy; - if (!SendSubrect(cl, x+dx, y+dy, rw, rh)) + if (!SendSubrect(cl, x + dx, y + dy, rw, rh)) return FALSE; } } @@ -649,39 +682,68 @@ SendSubrect(rfbClientPtr cl, if (!SendTightHeader(cl, x, y, w, h)) return FALSE; - fbptr = (cl->scaledScreen->frameBuffer + (cl->scaledScreen->paddedWidthInBytes * y) + fbptr = (cl->scaledScreen->frameBuffer + + (cl->scaledScreen->paddedWidthInBytes * y) + (x * (cl->scaledScreen->bitsPerPixel / 8))); - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, - &cl->format, fbptr, tightBeforeBuf, - cl->scaledScreen->paddedWidthInBytes, w, h); + if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1) + return SendJpegRect(cl, x, y, w, h, qualityLevel); paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor; + if(qualityLevel != -1) + paletteMaxColors = tightConf[compressLevel].palMaxColorsWithJPEG; if ( paletteMaxColors < 2 && w * h >= tightConf[compressLevel].monoMinRectSize ) { paletteMaxColors = 2; } - switch (cl->format.bitsPerPixel) { - case 8: - FillPalette8(w * h); - break; - case 16: - FillPalette16(w * h); - break; - default: - FillPalette32(w * h); + + if (cl->format.bitsPerPixel == cl->screen->serverFormat.bitsPerPixel && + cl->format.redMax == cl->screen->serverFormat.redMax && + cl->format.greenMax == cl->screen->serverFormat.greenMax && + cl->format.blueMax == cl->screen->serverFormat.blueMax && + cl->format.bitsPerPixel >= 16) { + + /* This is so we can avoid translating the pixels when compressing + with JPEG, since it is unnecessary */ + switch (cl->format.bitsPerPixel) { + case 16: + FastFillPalette16(cl, (uint16_t *)fbptr, w, + cl->scaledScreen->paddedWidthInBytes / 2, h); + break; + default: + FastFillPalette32(cl, (uint32_t *)fbptr, w, + cl->scaledScreen->paddedWidthInBytes / 4, h); + } + + if(paletteNumColors != 0 || qualityLevel == -1) { + (*cl->translateFn)(cl->translateLookupTable, + &cl->screen->serverFormat, &cl->format, fbptr, + tightBeforeBuf, + cl->scaledScreen->paddedWidthInBytes, w, h); + } + } + else { + (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, + &cl->format, fbptr, tightBeforeBuf, + cl->scaledScreen->paddedWidthInBytes, w, h); + + switch (cl->format.bitsPerPixel) { + case 8: + FillPalette8(w * h); + break; + case 16: + FillPalette16(w * h); + break; + default: + FillPalette32(w * h); + } } switch (paletteNumColors) { case 0: /* Truecolor image */ - if (DetectSmoothImage(cl, &cl->format, w, h)) { - if (qualityLevel != -1) { - success = SendJpegRect(cl, x, y, w, h, - tightConf[qualityLevel].jpegQuality); - } else { - success = SendGradientRect(cl, x, y, w, h); - } + if (qualityLevel != -1) { + success = SendJpegRect(cl, x, y, w, h, qualityLevel); } else { success = SendFullColorRect(cl, x, y, w, h); } @@ -696,14 +758,7 @@ SendSubrect(rfbClientPtr cl, break; default: /* Up to 256 different colors */ - if ( paletteNumColors > 96 && - qualityLevel != -1 && qualityLevel <= 3 && - DetectSmoothImage(cl, &cl->format, w, h) ) { - success = SendJpegRect(cl, x, y, w, h, - tightConf[qualityLevel].jpegQuality); - } else { - success = SendIndexedRect(cl, x, y, w, h); - } + success = SendIndexedRect(cl, x, y, w, h); } return success; } @@ -732,8 +787,10 @@ SendTightHeader(rfbClientPtr cl, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - rfbStatRecordEncodingSent(cl, cl->tightEncoding, sz_rfbFramebufferUpdateRectHeader, - sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h); + rfbStatRecordEncodingSent(cl, cl->tightEncoding, + sz_rfbFramebufferUpdateRectHeader, + sz_rfbFramebufferUpdateRectHeader + + w * (cl->format.bitsPerPixel / 8) * h); return TRUE; } @@ -762,7 +819,7 @@ SendSolidRect(rfbClientPtr cl) memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len); cl->ublen += len; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, len+1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, len + 1); return TRUE; } @@ -795,7 +852,12 @@ SendMonoRect(rfbClientPtr cl, dataLen = (w + 7) / 8; dataLen *= h; - cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; + if (tightConf[compressLevel].monoZlibLevel == 0 && + cl->tightEncoding != rfbEncodingTightPng) + cl->updateBuf[cl->ublen++] = + (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4); + else + cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; cl->updateBuf[cl->ublen++] = rfbTightFilterPalette; cl->updateBuf[cl->ublen++] = 1; @@ -866,7 +928,12 @@ SendIndexedRect(rfbClientPtr cl, } /* Prepare tight encoding header. */ - cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; + if (tightConf[compressLevel].idxZlibLevel == 0 && + cl->tightEncoding != rfbEncodingTightPng) + cl->updateBuf[cl->ublen++] = + (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4); + else + cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; cl->updateBuf[cl->ublen++] = rfbTightFilterPalette; cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1); @@ -886,9 +953,11 @@ SendIndexedRect(rfbClientPtr cl, } else entryLen = 4; - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen); + memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, + paletteNumColors * entryLen); cl->ublen += paletteNumColors * entryLen; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteNumColors * entryLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, + 3 + paletteNumColors * entryLen); break; case 16: @@ -901,7 +970,8 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2); cl->ublen += paletteNumColors * 2; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteNumColors * 2); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, + 3 + paletteNumColors * 2); break; default: @@ -934,7 +1004,11 @@ SendFullColorRect(rfbClientPtr cl, return FALSE; } - cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ + if (tightConf[compressLevel].rawZlibLevel == 0 && + cl->tightEncoding != rfbEncodingTightPng) + cl->updateBuf[cl->ublen++] = (char)(rfbTightNoZlib << 4); + else + cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); if (usePixelFormat24) { @@ -949,53 +1023,6 @@ SendFullColorRect(rfbClientPtr cl, } static rfbBool -SendGradientRect(rfbClientPtr cl, - int x, - int y, - int w, - int h) -{ - int streamId = 3; - int len; - -#ifdef LIBVNCSERVER_HAVE_LIBPNG - if (CanSendPngRect(cl, w, h)) { - return SendPngRect(cl, x, y, w, h); - } -#endif - - if (cl->format.bitsPerPixel == 8) - return SendFullColorRect(cl, x, y, w, h); - - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - if (prevRowBuf == NULL) - prevRowBuf = (int *)malloc(2048 * 3 * sizeof(int)); - - cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; - cl->updateBuf[cl->ublen++] = rfbTightFilterGradient; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 2); - - if (usePixelFormat24) { - FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h); - len = 3; - } else if (cl->format.bitsPerPixel == 32) { - FilterGradient32(cl, (uint32_t *)tightBeforeBuf, &cl->format, w, h); - len = 4; - } else { - FilterGradient16(cl, (uint16_t *)tightBeforeBuf, &cl->format, w, h); - len = 2; - } - - return CompressData(cl, streamId, w * h * len, - tightConf[compressLevel].gradientZlibLevel, - Z_FILTERED); -} - -static rfbBool CompressData(rfbClientPtr cl, int streamId, int dataLen, @@ -1012,6 +1039,9 @@ CompressData(rfbClientPtr cl, return TRUE; } + if (zlibLevel == 0) + return SendCompressedData (cl, tightBeforeBuf, dataLen); + pz = &cl->zsStruct[streamId]; /* Initialize compression stream if needed. */ @@ -1044,15 +1074,16 @@ CompressData(rfbClientPtr cl, } /* Actual compression. */ - if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK || - pz->avail_in != 0 || pz->avail_out == 0 ) { + if (deflate(pz, Z_SYNC_FLUSH) != Z_OK || + pz->avail_in != 0 || pz->avail_out == 0) { return FALSE; } - return SendCompressedData(cl, tightAfterBufSize - pz->avail_out); + return SendCompressedData(cl, tightAfterBuf, + tightAfterBufSize - pz->avail_out); } -static rfbBool SendCompressedData(rfbClientPtr cl, +static rfbBool SendCompressedData(rfbClientPtr cl, char *buf, int compressedLen) { int i, portionLen; @@ -1079,7 +1110,7 @@ static rfbBool SendCompressedData(rfbClientPtr cl, if (!rfbSendUpdateBuf(cl)) return FALSE; } - memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen); + memcpy(&cl->updateBuf[cl->ublen], &buf[i], portionLen); cl->ublen += portionLen; } rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, compressedLen); @@ -1087,6 +1118,7 @@ static rfbBool SendCompressedData(rfbClientPtr cl, return TRUE; } + /* * Code to determine how many different colors used in rectangle. */ @@ -1133,6 +1165,7 @@ FillPalette8(int count) } } + #define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ \ static void \ @@ -1198,13 +1231,117 @@ FillPalette##bpp(int count) { \ DEFINE_FILL_PALETTE_FUNCTION(16) DEFINE_FILL_PALETTE_FUNCTION(32) +#define DEFINE_FAST_FILL_PALETTE_FUNCTION(bpp) \ + \ +static void \ +FastFillPalette##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, \ + int pitch, int h) \ +{ \ + uint##bpp##_t c0, c1, ci, mask, c0t, c1t, cit; \ + int i, j, i2 = 0, j2, n0, n1, ni; \ + \ + if (cl->translateFn != rfbTranslateNone) { \ + mask = cl->screen->serverFormat.redMax \ + << cl->screen->serverFormat.redShift; \ + mask |= cl->screen->serverFormat.greenMax \ + << cl->screen->serverFormat.greenShift; \ + mask |= cl->screen->serverFormat.blueMax \ + << cl->screen->serverFormat.blueShift; \ + } else mask = ~0; \ + \ + c0 = data[0] & mask; \ + for (j = 0; j < h; j++) { \ + for (i = 0; i < w; i++) { \ + if ((data[j * pitch + i] & mask) != c0) \ + goto done; \ + } \ + } \ + done: \ + if (j >= h) { \ + paletteNumColors = 1; /* Solid rectangle */ \ + return; \ + } \ + if (paletteMaxColors < 2) { \ + paletteNumColors = 0; /* Full-color encoding preferred */ \ + return; \ + } \ + \ + n0 = j * w + i; \ + c1 = data[j * pitch + i] & mask; \ + n1 = 0; \ + i++; if (i >= w) {i = 0; j++;} \ + for (j2 = j; j2 < h; j2++) { \ + for (i2 = i; i2 < w; i2++) { \ + ci = data[j2 * pitch + i2] & mask; \ + if (ci == c0) { \ + n0++; \ + } else if (ci == c1) { \ + n1++; \ + } else \ + goto done2; \ + } \ + i = 0; \ + } \ + done2: \ + (*cl->translateFn)(cl->translateLookupTable, \ + &cl->screen->serverFormat, &cl->format, \ + (char *)&c0, (char *)&c0t, bpp/8, 1, 1); \ + (*cl->translateFn)(cl->translateLookupTable, \ + &cl->screen->serverFormat, &cl->format, \ + (char *)&c1, (char *)&c1t, bpp/8, 1, 1); \ + if (j2 >= h) { \ + if (n0 > n1) { \ + monoBackground = (uint32_t)c0t; \ + monoForeground = (uint32_t)c1t; \ + } else { \ + monoBackground = (uint32_t)c1t; \ + monoForeground = (uint32_t)c0t; \ + } \ + paletteNumColors = 2; /* Two colors */ \ + return; \ + } \ + \ + PaletteReset(); \ + PaletteInsert (c0t, (uint32_t)n0, bpp); \ + PaletteInsert (c1t, (uint32_t)n1, bpp); \ + \ + ni = 1; \ + i2++; if (i2 >= w) {i2 = 0; j2++;} \ + for (j = j2; j < h; j++) { \ + for (i = i2; i < w; i++) { \ + if ((data[j * pitch + i] & mask) == ci) { \ + ni++; \ + } else { \ + (*cl->translateFn)(cl->translateLookupTable, \ + &cl->screen->serverFormat, \ + &cl->format, (char *)&ci, \ + (char *)&cit, bpp/8, 1, 1); \ + if (!PaletteInsert (cit, (uint32_t)ni, bpp)) \ + return; \ + ci = data[j * pitch + i] & mask; \ + ni = 1; \ + } \ + } \ + i2 = 0; \ + } \ + \ + (*cl->translateFn)(cl->translateLookupTable, \ + &cl->screen->serverFormat, &cl->format, \ + (char *)&ci, (char *)&cit, bpp/8, 1, 1); \ + PaletteInsert (cit, (uint32_t)ni, bpp); \ +} + +DEFINE_FAST_FILL_PALETTE_FUNCTION(16) +DEFINE_FAST_FILL_PALETTE_FUNCTION(32) + /* * Functions to operate with palette structures. */ -#define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF)) -#define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF)) +#define HASH_FUNC16(rgb) ((int)((((rgb) >> 8) + (rgb)) & 0xFF)) +#define HASH_FUNC32(rgb) ((int)((((rgb) >> 16) + ((rgb) >> 8)) & 0xFF)) + static void PaletteReset(void) @@ -1213,6 +1350,7 @@ PaletteReset(void) memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *)); } + static int PaletteInsert(uint32_t rgb, int numPixels, @@ -1353,6 +1491,7 @@ EncodeIndexedRect##bpp(uint8_t *buf, int count) { \ DEFINE_IDX_ENCODE_FUNCTION(16) DEFINE_IDX_ENCODE_FUNCTION(32) + #define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ \ static void \ @@ -1409,379 +1548,110 @@ DEFINE_MONO_ENCODE_FUNCTION(32) /* - * ``Gradient'' filter for 24-bit color samples. - * Should be called only when redMax, greenMax and blueMax are 255. - * Color components assumed to be byte-aligned. + * JPEG compression stuff. */ -static void -FilterGradient24(rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, int w, int h) +static rfbBool +SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) { - uint32_t *buf32; - uint32_t pix32; - int *prevRowPtr; - int shiftBits[3]; - int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; - int prediction; - int x, y, c; - - buf32 = (uint32_t *)buf; - memset (prevRowBuf, 0, w * 3 * sizeof(int)); - - if (!cl->screen->serverFormat.bigEndian == !fmt->bigEndian) { - shiftBits[0] = fmt->redShift; - shiftBits[1] = fmt->greenShift; - shiftBits[2] = fmt->blueShift; - } else { - shiftBits[0] = 24 - fmt->redShift; - shiftBits[1] = 24 - fmt->greenShift; - shiftBits[2] = 24 - fmt->blueShift; - } - - for (y = 0; y < h; y++) { - for (c = 0; c < 3; c++) { - pixUpper[c] = 0; - pixHere[c] = 0; - } - prevRowPtr = prevRowBuf; - for (x = 0; x < w; x++) { - pix32 = *buf32++; - for (c = 0; c < 3; c++) { - pixUpperLeft[c] = pixUpper[c]; - pixLeft[c] = pixHere[c]; - pixUpper[c] = *prevRowPtr; - pixHere[c] = (int)(pix32 >> shiftBits[c] & 0xFF); - *prevRowPtr++ = pixHere[c]; - - prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; - if (prediction < 0) { - prediction = 0; - } else if (prediction > 0xFF) { - prediction = 0xFF; - } - *buf++ = (char)(pixHere[c] - prediction); - } - } - } -} - - -/* - * ``Gradient'' filter for other color depths. - */ - -#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ - \ -static void \ -FilterGradient##bpp(rfbClientPtr cl, uint##bpp##_t *buf, \ - rfbPixelFormat *fmt, int w, int h) { \ - uint##bpp##_t pix, diff; \ - rfbBool endianMismatch; \ - int *prevRowPtr; \ - int maxColor[3], shiftBits[3]; \ - int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; \ - int prediction; \ - int x, y, c; \ - \ - memset (prevRowBuf, 0, w * 3 * sizeof(int)); \ - \ - endianMismatch = (!cl->screen->serverFormat.bigEndian != !fmt->bigEndian); \ - \ - maxColor[0] = fmt->redMax; \ - maxColor[1] = fmt->greenMax; \ - maxColor[2] = fmt->blueMax; \ - shiftBits[0] = fmt->redShift; \ - shiftBits[1] = fmt->greenShift; \ - shiftBits[2] = fmt->blueShift; \ - \ - for (y = 0; y < h; y++) { \ - for (c = 0; c < 3; c++) { \ - pixUpper[c] = 0; \ - pixHere[c] = 0; \ - } \ - prevRowPtr = prevRowBuf; \ - for (x = 0; x < w; x++) { \ - pix = *buf; \ - if (endianMismatch) { \ - pix = Swap##bpp(pix); \ - } \ - diff = 0; \ - for (c = 0; c < 3; c++) { \ - pixUpperLeft[c] = pixUpper[c]; \ - pixLeft[c] = pixHere[c]; \ - pixUpper[c] = *prevRowPtr; \ - pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \ - *prevRowPtr++ = pixHere[c]; \ - \ - prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \ - if (prediction < 0) { \ - prediction = 0; \ - } else if (prediction > maxColor[c]) { \ - prediction = maxColor[c]; \ - } \ - diff |= ((pixHere[c] - prediction) & maxColor[c]) \ - << shiftBits[c]; \ - } \ - if (endianMismatch) { \ - diff = Swap##bpp(diff); \ - } \ - *buf++ = diff; \ - } \ - } \ -} - -DEFINE_GRADIENT_FILTER_FUNCTION(16) -DEFINE_GRADIENT_FILTER_FUNCTION(32) - - -/* - * Code to guess if given rectangle is suitable for smooth image - * compression (by applying "gradient" filter or JPEG coder). - */ + unsigned char *srcbuf; + int ps = cl->screen->serverFormat.bitsPerPixel / 8; + int subsamp = subsampLevel2tjsubsamp[subsampLevel]; + unsigned long size = 0; + int flags = 0, pitch; + unsigned char *tmpbuf = NULL; -#define JPEG_MIN_RECT_SIZE 4096 - -#define DETECT_SUBROW_WIDTH 7 -#define DETECT_MIN_WIDTH 8 -#define DETECT_MIN_HEIGHT 8 - -static int -DetectSmoothImage (rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h) -{ - long avgError; + if (cl->screen->serverFormat.bitsPerPixel == 8) + return SendFullColorRect(cl, x, y, w, h); - if ( cl->screen->serverFormat.bitsPerPixel == 8 || fmt->bitsPerPixel == 8 || - w < DETECT_MIN_WIDTH || h < DETECT_MIN_HEIGHT ) { + if (ps < 2) { + rfbLog("Error: JPEG requires 16-bit, 24-bit, or 32-bit pixel format.\n"); return 0; } - - if (qualityLevel != -1) { - if (w * h < JPEG_MIN_RECT_SIZE) { - return 0; - } - } else { - if ( rfbTightDisableGradient || - w * h < tightConf[compressLevel].gradientMinRectSize ) { + if (!j) { + if ((j = tjInitCompress()) == NULL) { + rfbLog("JPEG Error: %s\n", tjGetErrorStr()); return 0; } } - if (fmt->bitsPerPixel == 32) { - if (usePixelFormat24) { - avgError = DetectSmoothImage24(cl, fmt, w, h); - if (qualityLevel != -1) { - return (avgError < tightConf[qualityLevel].jpegThreshold24); - } - return (avgError < tightConf[compressLevel].gradientThreshold24); - } else { - avgError = DetectSmoothImage32(cl, fmt, w, h); + if (tightAfterBufSize < TJBUFSIZE(w, h)) { + if (tightAfterBuf == NULL) + tightAfterBuf = (char *)malloc(TJBUFSIZE(w, h)); + else + tightAfterBuf = (char *)realloc(tightAfterBuf, + TJBUFSIZE(w, h)); + if (!tightAfterBuf) { + rfbLog("Memory allocation failure!\n"); + return 0; } - } else { - avgError = DetectSmoothImage16(cl, fmt, w, h); - } - if (qualityLevel != -1) { - return (avgError < tightConf[qualityLevel].jpegThreshold); - } - return (avgError < tightConf[compressLevel].gradientThreshold); -} - -static unsigned long -DetectSmoothImage24 (rfbClientPtr cl, - rfbPixelFormat *fmt, - int w, - int h) -{ - int off; - int x, y, d, dx, c; - int diffStat[256]; - int pixelCount = 0; - int pix, left[3]; - unsigned long avgError; - - /* If client is big-endian, color samples begin from the second - byte (offset 1) of a 32-bit pixel value. */ - off = (fmt->bigEndian != 0); - - memset(diffStat, 0, 256*sizeof(int)); - - y = 0, x = 0; - while (y < h && x < w) { - for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { - for (c = 0; c < 3; c++) { - left[c] = (int)tightBeforeBuf[((y+d)*w+x+d)*4+off+c] & 0xFF; - } - for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { - for (c = 0; c < 3; c++) { - pix = (int)tightBeforeBuf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF; - diffStat[abs(pix - left[c])]++; - left[c] = pix; - } - pixelCount++; + tightAfterBufSize = TJBUFSIZE(w, h); + } + + if (ps == 2) { + uint16_t *srcptr, pix; + unsigned char *dst; + int inRed, inGreen, inBlue, i, j; + + if((tmpbuf = (unsigned char *)malloc(w * h * 3)) == NULL) + rfbLog("Memory allocation failure!\n"); + srcptr = (uint16_t *)&cl->scaledScreen->frameBuffer + [y * cl->scaledScreen->paddedWidthInBytes + x * ps]; + dst = tmpbuf; + for(j = 0; j < h; j++) { + uint16_t *srcptr2 = srcptr; + unsigned char *dst2 = dst; + for (i = 0; i < w; i++) { + pix = *srcptr2++; + inRed = (int) (pix >> cl->screen->serverFormat.redShift + & cl->screen->serverFormat.redMax); + inGreen = (int) (pix >> cl->screen->serverFormat.greenShift + & cl->screen->serverFormat.greenMax); + inBlue = (int) (pix >> cl->screen->serverFormat.blueShift + & cl->screen->serverFormat.blueMax); + *dst2++ = (uint8_t)((inRed * 255 + + cl->screen->serverFormat.redMax / 2) + / cl->screen->serverFormat.redMax); + *dst2++ = (uint8_t)((inGreen * 255 + + cl->screen->serverFormat.greenMax / 2) + / cl->screen->serverFormat.greenMax); + *dst2++ = (uint8_t)((inBlue * 255 + + cl->screen->serverFormat.blueMax / 2) + / cl->screen->serverFormat.blueMax); } + srcptr += cl->scaledScreen->paddedWidthInBytes / ps; + dst += w * 3; } - if (w > h) { - x += h; - y = 0; - } else { - x = 0; - y += w; + srcbuf = tmpbuf; + pitch = w * 3; + ps = 3; + } else { + if (cl->screen->serverFormat.bigEndian && ps == 4) + flags |= TJ_ALPHAFIRST; + if (cl->screen->serverFormat.redShift == 16 + && cl->screen->serverFormat.blueShift == 0) + flags |= TJ_BGR; + if (cl->screen->serverFormat.bigEndian) + flags ^= TJ_BGR; + pitch = cl->scaledScreen->paddedWidthInBytes; + srcbuf = (unsigned char *)&cl->scaledScreen->frameBuffer + [y * pitch + x * ps]; + } + + if (tjCompress(j, srcbuf, w, pitch, h, ps, (unsigned char *)tightAfterBuf, + &size, subsamp, quality, flags) == -1) { + rfbLog("JPEG Error: %s\n", tjGetErrorStr()); + if (tmpbuf) { + free(tmpbuf); + tmpbuf = NULL; } - } - - if (diffStat[0] * 33 / pixelCount >= 95) return 0; - - avgError = 0; - for (c = 1; c < 8; c++) { - avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); - if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) - return 0; } - for (; c < 256; c++) { - avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); - } - avgError /= (pixelCount * 3 - diffStat[0]); - - return avgError; -} - -#define DEFINE_DETECT_FUNCTION(bpp) \ - \ -static unsigned long \ -DetectSmoothImage##bpp (rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h) {\ - rfbBool endianMismatch; \ - uint##bpp##_t pix; \ - int maxColor[3], shiftBits[3]; \ - int x, y, d, dx, c; \ - int diffStat[256]; \ - int pixelCount = 0; \ - int sample, sum, left[3]; \ - unsigned long avgError; \ - \ - endianMismatch = (!cl->screen->serverFormat.bigEndian != !fmt->bigEndian); \ - \ - maxColor[0] = fmt->redMax; \ - maxColor[1] = fmt->greenMax; \ - maxColor[2] = fmt->blueMax; \ - shiftBits[0] = fmt->redShift; \ - shiftBits[1] = fmt->greenShift; \ - shiftBits[2] = fmt->blueShift; \ - \ - memset(diffStat, 0, 256*sizeof(int)); \ - \ - y = 0, x = 0; \ - while (y < h && x < w) { \ - for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { \ - pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d]; \ - if (endianMismatch) { \ - pix = Swap##bpp(pix); \ - } \ - for (c = 0; c < 3; c++) { \ - left[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \ - } \ - for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { \ - pix = ((uint##bpp##_t *)tightBeforeBuf)[(y+d)*w+x+d+dx]; \ - if (endianMismatch) { \ - pix = Swap##bpp(pix); \ - } \ - sum = 0; \ - for (c = 0; c < 3; c++) { \ - sample = (int)(pix >> shiftBits[c] & maxColor[c]); \ - sum += abs(sample - left[c]); \ - left[c] = sample; \ - } \ - if (sum > 255) \ - sum = 255; \ - diffStat[sum]++; \ - pixelCount++; \ - } \ - } \ - if (w > h) { \ - x += h; \ - y = 0; \ - } else { \ - x = 0; \ - y += w; \ - } \ - } \ - \ - if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90) \ - return 0; \ - \ - avgError = 0; \ - for (c = 1; c < 8; c++) { \ - avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \ - if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) \ - return 0; \ - } \ - for (; c < 256; c++) { \ - avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \ - } \ - avgError /= (pixelCount - diffStat[0]); \ - \ - return avgError; \ -} - -DEFINE_DETECT_FUNCTION(16) -DEFINE_DETECT_FUNCTION(32) - - -/* - * JPEG compression stuff. - */ -static TLS struct jpeg_destination_mgr jpegDstManager; -static TLS rfbBool jpegError = FALSE; -static TLS int jpegDstDataLen = 0; - -static rfbBool -SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - uint8_t *srcBuf; - JSAMPROW rowPointer[1]; - int dy; - - if (cl->screen->serverFormat.bitsPerPixel == 8) - return SendFullColorRect(cl, x, y, w, h); - - srcBuf = (uint8_t *)malloc(w * 3); - if (srcBuf == NULL) { - return SendFullColorRect(cl, x, y, w, h); + if (tmpbuf) { + free(tmpbuf); + tmpbuf = NULL; } - rowPointer[0] = srcBuf; - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - - cinfo.image_width = w; - cinfo.image_height = h; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE); - - JpegSetDstManager (&cinfo); - - jpeg_start_compress(&cinfo, TRUE); - - for (dy = 0; dy < h; dy++) { - PrepareRowForImg(cl, srcBuf, x, y + dy, w); - jpeg_write_scanlines(&cinfo, rowPointer, 1); - if (jpegError) - break; - } - - if (!jpegError) - jpeg_finish_compress(&cinfo); - - jpeg_destroy_compress(&cinfo); - free(srcBuf); - - if (jpegError) - return SendFullColorRect(cl, x, y, w, h); if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) @@ -1791,7 +1661,7 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); - return SendCompressedData(cl, jpegDstDataLen); + return SendCompressedData(cl, tightAfterBuf, (int)size); } static void @@ -1871,43 +1741,6 @@ DEFINE_JPEG_GET_ROW_FUNCTION(16) DEFINE_JPEG_GET_ROW_FUNCTION(32) /* - * Destination manager implementation for JPEG library. - */ - -static void -JpegInitDestination(j_compress_ptr cinfo) -{ - jpegError = FALSE; - jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf; - jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize; -} - -static boolean -JpegEmptyOutputBuffer(j_compress_ptr cinfo) -{ - jpegError = TRUE; - jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf; - jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize; - - return TRUE; -} - -static void -JpegTermDestination(j_compress_ptr cinfo) -{ - jpegDstDataLen = tightAfterBufSize - jpegDstManager.free_in_buffer; -} - -static void -JpegSetDstManager(j_compress_ptr cinfo) -{ - jpegDstManager.init_destination = JpegInitDestination; - jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer; - jpegDstManager.term_destination = JpegTermDestination; - cinfo->dest = &jpegDstManager; -} - -/* * PNG compression stuff. */ @@ -2062,6 +1895,6 @@ static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h) { rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); /* rfbLog("<< SendPngRect\n"); */ - return SendCompressedData(cl, pngDstDataLen); + return SendCompressedData(cl, tightAfterBuf, pngDstDataLen); } #endif diff --git a/libvncserver/turbo.c b/libvncserver/turbo.c deleted file mode 100644 index f205b6d..0000000 --- a/libvncserver/turbo.c +++ /dev/null @@ -1,1566 +0,0 @@ -/* - * turbo.c - * - * Routines to implement TurboVNC Encoding - */ - -/* - * Copyright (C) 2010-2012 D. R. Commander. All Rights Reserved. - * Copyright (C) 2005-2008 Sun Microsystems, Inc. All Rights Reserved. - * Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved. - * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. - * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This software 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 - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -#include <rfb/rfb.h> -#include "private.h" - -#include "turbojpeg.h" - - -/* Note: The following constant should not be changed. */ -#define TIGHT_MIN_TO_COMPRESS 12 - -/* The parameters below may be adjusted. */ -#define MIN_SPLIT_RECT_SIZE 4096 -#define MIN_SOLID_SUBRECT_SIZE 2048 -#define MAX_SPLIT_TILE_SIZE 16 - -/* - * There is so much access of the Tight encoding static data buffers - * that we resort to using thread local storage instead of having - * per-client data. - */ -#if LIBVNCSERVER_HAVE_LIBPTHREAD && LIBVNCSERVER_HAVE_TLS && !defined(TLS) && defined(__linux__) -#define TLS __thread -#endif -#ifndef TLS -#define TLS -#endif - -/* This variable is set on every rfbSendRectEncodingTight() call. */ -static TLS rfbBool usePixelFormat24 = FALSE; - - -/* Compression level stuff. The following array contains various - encoder parameters for each of 10 compression levels (0..9). - Last three parameters correspond to JPEG quality levels (0..9). */ - -typedef struct TIGHT_CONF_s { - int maxRectSize, maxRectWidth; - int monoMinRectSize; - int idxZlibLevel, monoZlibLevel, rawZlibLevel; - int idxMaxColorsDivisor; - int palMaxColorsWithJPEG; -} TIGHT_CONF; - -static TIGHT_CONF tightConf[3] = { - { 65536, 2048, 6, 0, 0, 0, 4, 24 }, - { 65536, 2048, 32, 1, 1, 1, 96, 24 }, - { 65536, 2048, 32, 3, 3, 2, 96, 96 } -}; - -static TLS int compressLevel = 1; -static TLS int qualityLevel = 95; -static TLS int subsampLevel = TJ_444; - -static const int subsampLevel2tjsubsamp[4] = { - TJ_444, TJ_420, TJ_422, TJ_GRAYSCALE -}; - - -/* Stuff dealing with palettes. */ - -typedef struct COLOR_LIST_s { - struct COLOR_LIST_s *next; - int idx; - uint32_t rgb; -} COLOR_LIST; - -typedef struct PALETTE_ENTRY_s { - COLOR_LIST *listNode; - int numPixels; -} PALETTE_ENTRY; - -typedef struct PALETTE_s { - PALETTE_ENTRY entry[256]; - COLOR_LIST *hash[256]; - COLOR_LIST list[256]; -} PALETTE; - -/* TODO: move into rfbScreen struct */ -static TLS int paletteNumColors = 0; -static TLS int paletteMaxColors = 0; -static TLS uint32_t monoBackground = 0; -static TLS uint32_t monoForeground = 0; -static TLS PALETTE palette; - -/* Pointers to dynamically-allocated buffers. */ - -static TLS int tightBeforeBufSize = 0; -static TLS char *tightBeforeBuf = NULL; - -static TLS int tightAfterBufSize = 0; -static TLS char *tightAfterBuf = NULL; - -static TLS tjhandle j = NULL; - -void rfbTightCleanup (rfbScreenInfoPtr screen) -{ - if (tightBeforeBufSize) { - free (tightBeforeBuf); - tightBeforeBufSize = 0; - tightBeforeBuf = NULL; - } - if (tightAfterBufSize) { - free (tightAfterBuf); - tightAfterBufSize = 0; - tightAfterBuf = NULL; - } - if (j) tjDestroy(j); -} - - -/* Prototypes for static functions. */ - -static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t colorValue, int *w_ptr, int *h_ptr); -static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t colorValue, - int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr); -static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); - -static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h); -static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); -static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h); - -static rfbBool SendSolidRect (rfbClientPtr cl); -static rfbBool SendMonoRect (rfbClientPtr cl, int w, int h); -static rfbBool SendIndexedRect (rfbClientPtr cl, int w, int h); -static rfbBool SendFullColorRect (rfbClientPtr cl, int w, int h); - -static rfbBool CompressData (rfbClientPtr cl, int streamId, int dataLen, - int zlibLevel, int zlibStrategy); -static rfbBool SendCompressedData (rfbClientPtr cl, char *buf, - int compressedLen); - -static void FillPalette8 (int count); -static void FillPalette16 (int count); -static void FillPalette32 (int count); -static void FastFillPalette16 (rfbClientPtr cl, uint16_t *data, int w, - int pitch, int h); -static void FastFillPalette32 (rfbClientPtr cl, uint32_t *data, int w, - int pitch, int h); - -static void PaletteReset (void); -static int PaletteInsert (uint32_t rgb, int numPixels, int bpp); - -static void Pack24 (rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, - int count); - -static void EncodeIndexedRect16 (uint8_t *buf, int count); -static void EncodeIndexedRect32 (uint8_t *buf, int count); - -static void EncodeMonoRect8 (uint8_t *buf, int w, int h); -static void EncodeMonoRect16 (uint8_t *buf, int w, int h); -static void EncodeMonoRect32 (uint8_t *buf, int w, int h); - -static rfbBool SendJpegRect (rfbClientPtr cl, int x, int y, int w, int h, - int quality); - -/* - * Tight encoding implementation. - */ - -int -rfbNumCodedRectsTight(rfbClientPtr cl, - int x, - int y, - int w, - int h) -{ - int maxRectSize, maxRectWidth; - int subrectMaxWidth, subrectMaxHeight; - - /* No matter how many rectangles we will send if LastRect markers - are used to terminate rectangle stream. */ - if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE) - return 0; - - maxRectSize = tightConf[compressLevel].maxRectSize; - maxRectWidth = tightConf[compressLevel].maxRectWidth; - - if (w > maxRectWidth || w * h > maxRectSize) { - subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; - subrectMaxHeight = maxRectSize / subrectMaxWidth; - return (((w - 1) / maxRectWidth + 1) * - ((h - 1) / subrectMaxHeight + 1)); - } else { - return 1; - } -} - -rfbBool -rfbSendRectEncodingTight(rfbClientPtr cl, - int x, - int y, - int w, - int h) -{ - int nMaxRows; - uint32_t colorValue; - int dx, dy, dw, dh; - int x_best, y_best, w_best, h_best; - char *fbptr; - - rfbSendUpdateBuf(cl); - - compressLevel = cl->tightCompressLevel; - qualityLevel = cl->tightQualityLevel; - subsampLevel = cl->tightSubsampLevel; - - /* We only allow compression levels that have a demonstrable performance - benefit. CL 0 with JPEG reduces CPU usage for workloads that have low - numbers of unique colors, but the same thing can be accomplished by - using CL 0 without JPEG (AKA "Lossless Tight.") CL 2 is a mixed bag. - It can be shown to reduce bandwidth (and commensurately increase CPU - usage) by typically 30-40% relative to CL 1, but only when it is used in - conjunction with high-quality JPEG, and only on workloads that have low - numbers of unique colors. Increasing the amount of Zlib compression - beyond CL 2 cannot be shown to provide any significant bandwidth savings - except in very rare corner cases that are not performance-critical to - begin with, and higher Zlib levels increase CPU usage exponentially. */ - if (qualityLevel != -1) { - if (compressLevel < 1) compressLevel = 1; - if (compressLevel > 2) compressLevel = 2; - } - - /* With JPEG disabled, increasing the Zlib compression level beyond CL 1 - offers no significant bandwidth savings, and the CPU usage starts to - increase exponentially. */ - else if (compressLevel > 1) compressLevel = 1; - - if ( cl->format.depth == 24 && cl->format.redMax == 0xFF && - cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) { - usePixelFormat24 = TRUE; - } else { - usePixelFormat24 = FALSE; - } - - if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE) - return SendRectSimple(cl, x, y, w, h); - - /* Make sure we can write at least one pixel into tightBeforeBuf. */ - - if (tightBeforeBufSize < 4) { - tightBeforeBufSize = 4; - if (tightBeforeBuf == NULL) - tightBeforeBuf = (char *)malloc(tightBeforeBufSize); - else - tightBeforeBuf = (char *)realloc(tightBeforeBuf, - tightBeforeBufSize); - } - - /* Calculate maximum number of rows in one non-solid rectangle. */ - - { - int maxRectSize, maxRectWidth, nMaxWidth; - - maxRectSize = tightConf[compressLevel].maxRectSize; - maxRectWidth = tightConf[compressLevel].maxRectWidth; - nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; - nMaxRows = maxRectSize / nMaxWidth; - } - - /* Try to find large solid-color areas and send them separately. */ - - for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { - - /* If a rectangle becomes too large, send its upper part now. */ - - if (dy - y >= nMaxRows) { - if (!SendRectSimple(cl, x, y, w, nMaxRows)) - return 0; - y += nMaxRows; - h -= nMaxRows; - } - - dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? - MAX_SPLIT_TILE_SIZE : (y + h - dy); - - for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) { - - dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ? - MAX_SPLIT_TILE_SIZE : (x + w - dx); - - if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) { - - if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1) { - uint32_t r = (colorValue >> 16) & 0xFF; - uint32_t g = (colorValue >> 8) & 0xFF; - uint32_t b = (colorValue) & 0xFF; - double y = (0.257 * (double)r) + (0.504 * (double)g) - + (0.098 * (double)b) + 16.; - colorValue = (int)y + (((int)y) << 8) + (((int)y) << 16); - } - - /* Get dimensions of solid-color area. */ - - FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y), - colorValue, &w_best, &h_best); - - /* Make sure a solid rectangle is large enough - (or the whole rectangle is of the same color). */ - - if ( w_best * h_best != w * h && - w_best * h_best < MIN_SOLID_SUBRECT_SIZE ) - continue; - - /* Try to extend solid rectangle to maximum size. */ - - x_best = dx; y_best = dy; - ExtendSolidArea(cl, x, y, w, h, colorValue, - &x_best, &y_best, &w_best, &h_best); - - /* Send rectangles at top and left to solid-color area. */ - - if ( y_best != y && - !SendRectSimple(cl, x, y, w, y_best-y) ) - return FALSE; - if ( x_best != x && - !rfbSendRectEncodingTight(cl, x, y_best, - x_best-x, h_best) ) - return FALSE; - - /* Send solid-color rectangle. */ - - if (!SendTightHeader(cl, x_best, y_best, w_best, h_best)) - return FALSE; - - fbptr = (cl->scaledScreen->frameBuffer + - (cl->scaledScreen->paddedWidthInBytes * y_best) + - (x_best * (cl->scaledScreen->bitsPerPixel / 8))); - - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, - &cl->format, fbptr, tightBeforeBuf, - cl->scaledScreen->paddedWidthInBytes, 1, 1); - - if (!SendSolidRect(cl)) - return FALSE; - - /* Send remaining rectangles (at right and bottom). */ - - if ( x_best + w_best != x + w && - !rfbSendRectEncodingTight(cl, x_best + w_best, y_best, - w - (x_best-x) - w_best, h_best) ) - return FALSE; - if ( y_best + h_best != y + h && - !rfbSendRectEncodingTight(cl, x, y_best + h_best, - w, h - (y_best-y) - h_best) ) - return FALSE; - - /* Return after all recursive calls are done. */ - - return TRUE; - } - - } - - } - - /* No suitable solid-color rectangles found. */ - - return SendRectSimple(cl, x, y, w, h); -} - - -static void -FindBestSolidArea(rfbClientPtr cl, - int x, - int y, - int w, - int h, - uint32_t colorValue, - int *w_ptr, - int *h_ptr) -{ - int dx, dy, dw, dh; - int w_prev; - int w_best = 0, h_best = 0; - - w_prev = w; - - for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { - - dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? - MAX_SPLIT_TILE_SIZE : (y + h - dy); - dw = (w_prev > MAX_SPLIT_TILE_SIZE) ? - MAX_SPLIT_TILE_SIZE : w_prev; - - if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE)) - break; - - for (dx = x + dw; dx < x + w_prev;) { - dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ? - MAX_SPLIT_TILE_SIZE : (x + w_prev - dx); - if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE)) - break; - dx += dw; - } - - w_prev = dx - x; - if (w_prev * (dy + dh - y) > w_best * h_best) { - w_best = w_prev; - h_best = dy + dh - y; - } - } - - *w_ptr = w_best; - *h_ptr = h_best; -} - - -static void -ExtendSolidArea(rfbClientPtr cl, - int x, - int y, - int w, - int h, - uint32_t colorValue, - int *x_ptr, - int *y_ptr, - int *w_ptr, - int *h_ptr) -{ - int cx, cy; - - /* Try to extend the area upwards. */ - for ( cy = *y_ptr - 1; - cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); - cy-- ); - *h_ptr += *y_ptr - (cy + 1); - *y_ptr = cy + 1; - - /* ... downwards. */ - for ( cy = *y_ptr + *h_ptr; - cy < y + h && - CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); - cy++ ); - *h_ptr += cy - (*y_ptr + *h_ptr); - - /* ... to the left. */ - for ( cx = *x_ptr - 1; - cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); - cx-- ); - *w_ptr += *x_ptr - (cx + 1); - *x_ptr = cx + 1; - - /* ... to the right. */ - for ( cx = *x_ptr + *w_ptr; - cx < x + w && - CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); - cx++ ); - *w_ptr += cx - (*x_ptr + *w_ptr); -} - - -/* - * Check if a rectangle is all of the same color. If needSameColor is - * set to non-zero, then also check that its color equals to the - * *colorPtr value. The result is 1 if the test is successfull, and in - * that case new color will be stored in *colorPtr. - */ - -static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor) -{ - switch(cl->screen->serverFormat.bitsPerPixel) { - case 32: - return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor); - case 16: - return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor); - default: - return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor); - } -} - - -#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ - \ -static rfbBool \ -CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \ - uint32_t* colorPtr, rfbBool needSameColor) \ -{ \ - uint##bpp##_t *fbptr; \ - uint##bpp##_t colorValue; \ - int dx, dy; \ - \ - fbptr = (uint##bpp##_t *)&cl->scaledScreen->frameBuffer \ - [y * cl->scaledScreen->paddedWidthInBytes + x * (bpp/8)]; \ - \ - colorValue = *fbptr; \ - if (needSameColor && (uint32_t)colorValue != *colorPtr) \ - return FALSE; \ - \ - for (dy = 0; dy < h; dy++) { \ - for (dx = 0; dx < w; dx++) { \ - if (colorValue != fbptr[dx]) \ - return FALSE; \ - } \ - fbptr = (uint##bpp##_t *)((uint8_t *)fbptr \ - + cl->scaledScreen->paddedWidthInBytes); \ - } \ - \ - *colorPtr = (uint32_t)colorValue; \ - return TRUE; \ -} - -DEFINE_CHECK_SOLID_FUNCTION(8) -DEFINE_CHECK_SOLID_FUNCTION(16) -DEFINE_CHECK_SOLID_FUNCTION(32) - -static rfbBool -SendRectSimple(rfbClientPtr cl, int x, int y, int w, int h) -{ - int maxBeforeSize, maxAfterSize; - int maxRectSize, maxRectWidth; - int subrectMaxWidth, subrectMaxHeight; - int dx, dy; - int rw, rh; - - maxRectSize = tightConf[compressLevel].maxRectSize; - maxRectWidth = tightConf[compressLevel].maxRectWidth; - - maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8); - maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12; - - if (tightBeforeBufSize < maxBeforeSize) { - tightBeforeBufSize = maxBeforeSize; - if (tightBeforeBuf == NULL) - tightBeforeBuf = (char *)malloc(tightBeforeBufSize); - else - tightBeforeBuf = (char *)realloc(tightBeforeBuf, - tightBeforeBufSize); - } - - if (tightAfterBufSize < maxAfterSize) { - tightAfterBufSize = maxAfterSize; - if (tightAfterBuf == NULL) - tightAfterBuf = (char *)malloc(tightAfterBufSize); - else - tightAfterBuf = (char *)realloc(tightAfterBuf, - tightAfterBufSize); - } - - if (w > maxRectWidth || w * h > maxRectSize) { - subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; - subrectMaxHeight = maxRectSize / subrectMaxWidth; - - for (dy = 0; dy < h; dy += subrectMaxHeight) { - for (dx = 0; dx < w; dx += maxRectWidth) { - rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx; - rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy; - if (!SendSubrect(cl, x + dx, y + dy, rw, rh)) - return FALSE; - } - } - } else { - if (!SendSubrect(cl, x, y, w, h)) - return FALSE; - } - - return TRUE; -} - -static rfbBool -SendSubrect(rfbClientPtr cl, - int x, - int y, - int w, - int h) -{ - char *fbptr; - rfbBool success = FALSE; - - /* Send pending data if there is more than 128 bytes. */ - if (cl->ublen > 128) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - if (!SendTightHeader(cl, x, y, w, h)) - return FALSE; - - fbptr = (cl->scaledScreen->frameBuffer - + (cl->scaledScreen->paddedWidthInBytes * y) - + (x * (cl->scaledScreen->bitsPerPixel / 8))); - - if (subsampLevel == TJ_GRAYSCALE && qualityLevel != -1) - return SendJpegRect(cl, x, y, w, h, qualityLevel); - - paletteMaxColors = w * h / tightConf[compressLevel].idxMaxColorsDivisor; - if(qualityLevel != -1) - paletteMaxColors = tightConf[compressLevel].palMaxColorsWithJPEG; - if ( paletteMaxColors < 2 && - w * h >= tightConf[compressLevel].monoMinRectSize ) { - paletteMaxColors = 2; - } - - if (cl->format.bitsPerPixel == cl->screen->serverFormat.bitsPerPixel && - cl->format.redMax == cl->screen->serverFormat.redMax && - cl->format.greenMax == cl->screen->serverFormat.greenMax && - cl->format.blueMax == cl->screen->serverFormat.blueMax && - cl->format.bitsPerPixel >= 16) { - - /* This is so we can avoid translating the pixels when compressing - with JPEG, since it is unnecessary */ - switch (cl->format.bitsPerPixel) { - case 16: - FastFillPalette16(cl, (uint16_t *)fbptr, w, - cl->scaledScreen->paddedWidthInBytes / 2, h); - break; - default: - FastFillPalette32(cl, (uint32_t *)fbptr, w, - cl->scaledScreen->paddedWidthInBytes / 4, h); - } - - if(paletteNumColors != 0 || qualityLevel == -1) { - (*cl->translateFn)(cl->translateLookupTable, - &cl->screen->serverFormat, &cl->format, fbptr, - tightBeforeBuf, - cl->scaledScreen->paddedWidthInBytes, w, h); - } - } - else { - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, - &cl->format, fbptr, tightBeforeBuf, - cl->scaledScreen->paddedWidthInBytes, w, h); - - switch (cl->format.bitsPerPixel) { - case 8: - FillPalette8(w * h); - break; - case 16: - FillPalette16(w * h); - break; - default: - FillPalette32(w * h); - } - } - - switch (paletteNumColors) { - case 0: - /* Truecolor image */ - if (qualityLevel != -1) { - success = SendJpegRect(cl, x, y, w, h, qualityLevel); - } else { - success = SendFullColorRect(cl, w, h); - } - break; - case 1: - /* Solid rectangle */ - success = SendSolidRect(cl); - break; - case 2: - /* Two-color rectangle */ - success = SendMonoRect(cl, w, h); - break; - default: - /* Up to 256 different colors */ - success = SendIndexedRect(cl, w, h); - } - return success; -} - -static rfbBool -SendTightHeader(rfbClientPtr cl, - int x, - int y, - int w, - int h) -{ - rfbFramebufferUpdateRectHeader rect; - - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingTight); - - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; - - rfbStatRecordEncodingSent(cl, rfbEncodingTight, - sz_rfbFramebufferUpdateRectHeader, - sz_rfbFramebufferUpdateRectHeader - + w * (cl->format.bitsPerPixel / 8) * h); - - return TRUE; -} - -/* - * Subencoding implementations. - */ - -static rfbBool -SendSolidRect(rfbClientPtr cl) -{ - int len; - - if (usePixelFormat24) { - Pack24(cl, tightBeforeBuf, &cl->format, 1); - len = 3; - } else - len = cl->format.bitsPerPixel / 8; - - if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4); - memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len); - cl->ublen += len; - - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, len + 1); - - return TRUE; -} - -static rfbBool -SendMonoRect(rfbClientPtr cl, - int w, - int h) -{ - int streamId = 1; - int paletteLen, dataLen; - - if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + - 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - /* Prepare tight encoding header. */ - dataLen = (w + 7) / 8; - dataLen *= h; - - if (tightConf[compressLevel].monoZlibLevel == 0) - cl->updateBuf[cl->ublen++] = - (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4); - else - cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; - cl->updateBuf[cl->ublen++] = rfbTightFilterPalette; - cl->updateBuf[cl->ublen++] = 1; - - /* Prepare palette, convert image. */ - switch (cl->format.bitsPerPixel) { - - case 32: - EncodeMonoRect32((uint8_t *)tightBeforeBuf, w, h); - - ((uint32_t *)tightAfterBuf)[0] = monoBackground; - ((uint32_t *)tightAfterBuf)[1] = monoForeground; - if (usePixelFormat24) { - Pack24(cl, tightAfterBuf, &cl->format, 2); - paletteLen = 6; - } else - paletteLen = 8; - - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen); - cl->ublen += paletteLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteLen); - break; - - case 16: - EncodeMonoRect16((uint8_t *)tightBeforeBuf, w, h); - - ((uint16_t *)tightAfterBuf)[0] = (uint16_t)monoBackground; - ((uint16_t *)tightAfterBuf)[1] = (uint16_t)monoForeground; - - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4); - cl->ublen += 4; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 7); - break; - - default: - EncodeMonoRect8((uint8_t *)tightBeforeBuf, w, h); - - cl->updateBuf[cl->ublen++] = (char)monoBackground; - cl->updateBuf[cl->ublen++] = (char)monoForeground; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 5); - } - - return CompressData(cl, streamId, dataLen, - tightConf[compressLevel].monoZlibLevel, - Z_DEFAULT_STRATEGY); -} - -static rfbBool -SendIndexedRect(rfbClientPtr cl, - int w, - int h) -{ - int streamId = 2; - int i, entryLen; - - if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + - paletteNumColors * cl->format.bitsPerPixel / 8 > - UPDATE_BUF_SIZE ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - /* Prepare tight encoding header. */ - if (tightConf[compressLevel].idxZlibLevel == 0) - cl->updateBuf[cl->ublen++] = - (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4); - else - cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; - cl->updateBuf[cl->ublen++] = rfbTightFilterPalette; - cl->updateBuf[cl->ublen++] = (char)(paletteNumColors - 1); - - /* Prepare palette, convert image. */ - switch (cl->format.bitsPerPixel) { - - case 32: - EncodeIndexedRect32((uint8_t *)tightBeforeBuf, w * h); - - for (i = 0; i < paletteNumColors; i++) { - ((uint32_t *)tightAfterBuf)[i] = - palette.entry[i].listNode->rgb; - } - if (usePixelFormat24) { - Pack24(cl, tightAfterBuf, &cl->format, paletteNumColors); - entryLen = 3; - } else - entryLen = 4; - - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, - paletteNumColors * entryLen); - cl->ublen += paletteNumColors * entryLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, - 3 + paletteNumColors * entryLen); - break; - - case 16: - EncodeIndexedRect16((uint8_t *)tightBeforeBuf, w * h); - - for (i = 0; i < paletteNumColors; i++) { - ((uint16_t *)tightAfterBuf)[i] = - (uint16_t)palette.entry[i].listNode->rgb; - } - - memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2); - cl->ublen += paletteNumColors * 2; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, - 3 + paletteNumColors * 2); - break; - - default: - return FALSE; /* Should never happen. */ - } - - return CompressData(cl, streamId, w * h, - tightConf[compressLevel].idxZlibLevel, - Z_DEFAULT_STRATEGY); -} - -static rfbBool -SendFullColorRect(rfbClientPtr cl, - int w, - int h) -{ - int streamId = 0; - int len; - - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - if (tightConf[compressLevel].rawZlibLevel == 0) - cl->updateBuf[cl->ublen++] = (char)(rfbTightNoZlib << 4); - else - cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); - - if (usePixelFormat24) { - Pack24(cl, tightBeforeBuf, &cl->format, w * h); - len = 3; - } else - len = cl->format.bitsPerPixel / 8; - - return CompressData(cl, streamId, w * h * len, - tightConf[compressLevel].rawZlibLevel, - Z_DEFAULT_STRATEGY); -} - -static rfbBool -CompressData(rfbClientPtr cl, - int streamId, - int dataLen, - int zlibLevel, - int zlibStrategy) -{ - z_streamp pz; - int err; - - if (dataLen < TIGHT_MIN_TO_COMPRESS) { - memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen); - cl->ublen += dataLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, dataLen); - return TRUE; - } - - if (zlibLevel == 0) - return SendCompressedData (cl, tightBeforeBuf, dataLen); - - pz = &cl->zsStruct[streamId]; - - /* Initialize compression stream if needed. */ - if (!cl->zsActive[streamId]) { - pz->zalloc = Z_NULL; - pz->zfree = Z_NULL; - pz->opaque = Z_NULL; - - err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS, - MAX_MEM_LEVEL, zlibStrategy); - if (err != Z_OK) - return FALSE; - - cl->zsActive[streamId] = TRUE; - cl->zsLevel[streamId] = zlibLevel; - } - - /* Prepare buffer pointers. */ - pz->next_in = (Bytef *)tightBeforeBuf; - pz->avail_in = dataLen; - pz->next_out = (Bytef *)tightAfterBuf; - pz->avail_out = tightAfterBufSize; - - /* Change compression parameters if needed. */ - if (zlibLevel != cl->zsLevel[streamId]) { - if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) { - return FALSE; - } - cl->zsLevel[streamId] = zlibLevel; - } - - /* Actual compression. */ - if (deflate(pz, Z_SYNC_FLUSH) != Z_OK || - pz->avail_in != 0 || pz->avail_out == 0) { - return FALSE; - } - - return SendCompressedData(cl, tightAfterBuf, - tightAfterBufSize - pz->avail_out); -} - -static rfbBool SendCompressedData(rfbClientPtr cl, char *buf, - int compressedLen) -{ - int i, portionLen; - - cl->updateBuf[cl->ublen++] = compressedLen & 0x7F; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); - if (compressedLen > 0x7F) { - cl->updateBuf[cl->ublen-1] |= 0x80; - cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); - if (compressedLen > 0x3FFF) { - cl->updateBuf[cl->ublen-1] |= 0x80; - cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); - } - } - - portionLen = UPDATE_BUF_SIZE; - for (i = 0; i < compressedLen; i += portionLen) { - if (i + portionLen > compressedLen) { - portionLen = compressedLen - i; - } - if (cl->ublen + portionLen > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - memcpy(&cl->updateBuf[cl->ublen], &buf[i], portionLen); - cl->ublen += portionLen; - } - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, compressedLen); - - return TRUE; -} - - -/* - * Code to determine how many different colors used in rectangle. - */ - -static void -FillPalette8(int count) -{ - uint8_t *data = (uint8_t *)tightBeforeBuf; - uint8_t c0, c1; - int i, n0, n1; - - paletteNumColors = 0; - - c0 = data[0]; - for (i = 1; i < count && data[i] == c0; i++); - if (i == count) { - paletteNumColors = 1; - return; /* Solid rectangle */ - } - - if (paletteMaxColors < 2) - return; - - n0 = i; - c1 = data[i]; - n1 = 0; - for (i++; i < count; i++) { - if (data[i] == c0) { - n0++; - } else if (data[i] == c1) { - n1++; - } else - break; - } - if (i == count) { - if (n0 > n1) { - monoBackground = (uint32_t)c0; - monoForeground = (uint32_t)c1; - } else { - monoBackground = (uint32_t)c1; - monoForeground = (uint32_t)c0; - } - paletteNumColors = 2; /* Two colors */ - } -} - - -#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ - \ -static void \ -FillPalette##bpp(int count) { \ - uint##bpp##_t *data = (uint##bpp##_t *)tightBeforeBuf; \ - uint##bpp##_t c0, c1, ci; \ - int i, n0, n1, ni; \ - \ - c0 = data[0]; \ - for (i = 1; i < count && data[i] == c0; i++); \ - if (i >= count) { \ - paletteNumColors = 1; /* Solid rectangle */ \ - return; \ - } \ - \ - if (paletteMaxColors < 2) { \ - paletteNumColors = 0; /* Full-color encoding preferred */ \ - return; \ - } \ - \ - n0 = i; \ - c1 = data[i]; \ - n1 = 0; \ - for (i++; i < count; i++) { \ - ci = data[i]; \ - if (ci == c0) { \ - n0++; \ - } else if (ci == c1) { \ - n1++; \ - } else \ - break; \ - } \ - if (i >= count) { \ - if (n0 > n1) { \ - monoBackground = (uint32_t)c0; \ - monoForeground = (uint32_t)c1; \ - } else { \ - monoBackground = (uint32_t)c1; \ - monoForeground = (uint32_t)c0; \ - } \ - paletteNumColors = 2; /* Two colors */ \ - return; \ - } \ - \ - PaletteReset(); \ - PaletteInsert (c0, (uint32_t)n0, bpp); \ - PaletteInsert (c1, (uint32_t)n1, bpp); \ - \ - ni = 1; \ - for (i++; i < count; i++) { \ - if (data[i] == ci) { \ - ni++; \ - } else { \ - if (!PaletteInsert (ci, (uint32_t)ni, bpp)) \ - return; \ - ci = data[i]; \ - ni = 1; \ - } \ - } \ - PaletteInsert (ci, (uint32_t)ni, bpp); \ -} - -DEFINE_FILL_PALETTE_FUNCTION(16) -DEFINE_FILL_PALETTE_FUNCTION(32) - -#define DEFINE_FAST_FILL_PALETTE_FUNCTION(bpp) \ - \ -static void \ -FastFillPalette##bpp(rfbClientPtr cl, uint##bpp##_t *data, int w, \ - int pitch, int h) \ -{ \ - uint##bpp##_t c0, c1, ci, mask, c0t, c1t, cit; \ - int i, j, i2 = 0, j2, n0, n1, ni; \ - \ - if (cl->translateFn != rfbTranslateNone) { \ - mask = cl->screen->serverFormat.redMax \ - << cl->screen->serverFormat.redShift; \ - mask |= cl->screen->serverFormat.greenMax \ - << cl->screen->serverFormat.greenShift; \ - mask |= cl->screen->serverFormat.blueMax \ - << cl->screen->serverFormat.blueShift; \ - } else mask = ~0; \ - \ - c0 = data[0] & mask; \ - for (j = 0; j < h; j++) { \ - for (i = 0; i < w; i++) { \ - if ((data[j * pitch + i] & mask) != c0) \ - goto done; \ - } \ - } \ - done: \ - if (j >= h) { \ - paletteNumColors = 1; /* Solid rectangle */ \ - return; \ - } \ - if (paletteMaxColors < 2) { \ - paletteNumColors = 0; /* Full-color encoding preferred */ \ - return; \ - } \ - \ - n0 = j * w + i; \ - c1 = data[j * pitch + i] & mask; \ - n1 = 0; \ - i++; if (i >= w) {i = 0; j++;} \ - for (j2 = j; j2 < h; j2++) { \ - for (i2 = i; i2 < w; i2++) { \ - ci = data[j2 * pitch + i2] & mask; \ - if (ci == c0) { \ - n0++; \ - } else if (ci == c1) { \ - n1++; \ - } else \ - goto done2; \ - } \ - i = 0; \ - } \ - done2: \ - (*cl->translateFn)(cl->translateLookupTable, \ - &cl->screen->serverFormat, &cl->format, \ - (char *)&c0, (char *)&c0t, bpp/8, 1, 1); \ - (*cl->translateFn)(cl->translateLookupTable, \ - &cl->screen->serverFormat, &cl->format, \ - (char *)&c1, (char *)&c1t, bpp/8, 1, 1); \ - if (j2 >= h) { \ - if (n0 > n1) { \ - monoBackground = (uint32_t)c0t; \ - monoForeground = (uint32_t)c1t; \ - } else { \ - monoBackground = (uint32_t)c1t; \ - monoForeground = (uint32_t)c0t; \ - } \ - paletteNumColors = 2; /* Two colors */ \ - return; \ - } \ - \ - PaletteReset(); \ - PaletteInsert (c0t, (uint32_t)n0, bpp); \ - PaletteInsert (c1t, (uint32_t)n1, bpp); \ - \ - ni = 1; \ - i2++; if (i2 >= w) {i2 = 0; j2++;} \ - for (j = j2; j < h; j++) { \ - for (i = i2; i < w; i++) { \ - if ((data[j * pitch + i] & mask) == ci) { \ - ni++; \ - } else { \ - (*cl->translateFn)(cl->translateLookupTable, \ - &cl->screen->serverFormat, \ - &cl->format, (char *)&ci, \ - (char *)&cit, bpp/8, 1, 1); \ - if (!PaletteInsert (cit, (uint32_t)ni, bpp)) \ - return; \ - ci = data[j * pitch + i] & mask; \ - ni = 1; \ - } \ - } \ - i2 = 0; \ - } \ - \ - (*cl->translateFn)(cl->translateLookupTable, \ - &cl->screen->serverFormat, &cl->format, \ - (char *)&ci, (char *)&cit, bpp/8, 1, 1); \ - PaletteInsert (cit, (uint32_t)ni, bpp); \ -} - -DEFINE_FAST_FILL_PALETTE_FUNCTION(16) -DEFINE_FAST_FILL_PALETTE_FUNCTION(32) - - -/* - * Functions to operate with palette structures. - */ - -#define HASH_FUNC16(rgb) ((int)((((rgb) >> 8) + (rgb)) & 0xFF)) -#define HASH_FUNC32(rgb) ((int)((((rgb) >> 16) + ((rgb) >> 8)) & 0xFF)) - - -static void -PaletteReset(void) -{ - paletteNumColors = 0; - memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *)); -} - - -static int -PaletteInsert(uint32_t rgb, - int numPixels, - int bpp) -{ - COLOR_LIST *pnode; - COLOR_LIST *prev_pnode = NULL; - int hash_key, idx, new_idx, count; - - hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb); - - pnode = palette.hash[hash_key]; - - while (pnode != NULL) { - if (pnode->rgb == rgb) { - /* Such palette entry already exists. */ - new_idx = idx = pnode->idx; - count = palette.entry[idx].numPixels + numPixels; - if (new_idx && palette.entry[new_idx-1].numPixels < count) { - do { - palette.entry[new_idx] = palette.entry[new_idx-1]; - palette.entry[new_idx].listNode->idx = new_idx; - new_idx--; - } - while (new_idx && palette.entry[new_idx-1].numPixels < count); - palette.entry[new_idx].listNode = pnode; - pnode->idx = new_idx; - } - palette.entry[new_idx].numPixels = count; - return paletteNumColors; - } - prev_pnode = pnode; - pnode = pnode->next; - } - - /* Check if palette is full. */ - if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) { - paletteNumColors = 0; - return 0; - } - - /* Move palette entries with lesser pixel counts. */ - for ( idx = paletteNumColors; - idx > 0 && palette.entry[idx-1].numPixels < numPixels; - idx-- ) { - palette.entry[idx] = palette.entry[idx-1]; - palette.entry[idx].listNode->idx = idx; - } - - /* Add new palette entry into the freed slot. */ - pnode = &palette.list[paletteNumColors]; - if (prev_pnode != NULL) { - prev_pnode->next = pnode; - } else { - palette.hash[hash_key] = pnode; - } - pnode->next = NULL; - pnode->idx = idx; - pnode->rgb = rgb; - palette.entry[idx].listNode = pnode; - palette.entry[idx].numPixels = numPixels; - - return (++paletteNumColors); -} - - -/* - * Converting 32-bit color samples into 24-bit colors. - * Should be called only when redMax, greenMax and blueMax are 255. - * Color components assumed to be byte-aligned. - */ - -static void Pack24(rfbClientPtr cl, - char *buf, - rfbPixelFormat *fmt, - int count) -{ - uint32_t *buf32; - uint32_t pix; - int r_shift, g_shift, b_shift; - - buf32 = (uint32_t *)buf; - - if (!cl->screen->serverFormat.bigEndian == !fmt->bigEndian) { - r_shift = fmt->redShift; - g_shift = fmt->greenShift; - b_shift = fmt->blueShift; - } else { - r_shift = 24 - fmt->redShift; - g_shift = 24 - fmt->greenShift; - b_shift = 24 - fmt->blueShift; - } - - while (count--) { - pix = *buf32++; - *buf++ = (char)(pix >> r_shift); - *buf++ = (char)(pix >> g_shift); - *buf++ = (char)(pix >> b_shift); - } -} - - -/* - * Converting truecolor samples into palette indices. - */ - -#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \ - \ -static void \ -EncodeIndexedRect##bpp(uint8_t *buf, int count) { \ - COLOR_LIST *pnode; \ - uint##bpp##_t *src; \ - uint##bpp##_t rgb; \ - int rep = 0; \ - \ - src = (uint##bpp##_t *) buf; \ - \ - while (count--) { \ - rgb = *src++; \ - while (count && *src == rgb) { \ - rep++, src++, count--; \ - } \ - pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \ - while (pnode != NULL) { \ - if ((uint##bpp##_t)pnode->rgb == rgb) { \ - *buf++ = (uint8_t)pnode->idx; \ - while (rep) { \ - *buf++ = (uint8_t)pnode->idx; \ - rep--; \ - } \ - break; \ - } \ - pnode = pnode->next; \ - } \ - } \ -} - -DEFINE_IDX_ENCODE_FUNCTION(16) -DEFINE_IDX_ENCODE_FUNCTION(32) - - -#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ - \ -static void \ -EncodeMonoRect##bpp(uint8_t *buf, int w, int h) { \ - uint##bpp##_t *ptr; \ - uint##bpp##_t bg; \ - unsigned int value, mask; \ - int aligned_width; \ - int x, y, bg_bits; \ - \ - ptr = (uint##bpp##_t *) buf; \ - bg = (uint##bpp##_t) monoBackground; \ - aligned_width = w - w % 8; \ - \ - for (y = 0; y < h; y++) { \ - for (x = 0; x < aligned_width; x += 8) { \ - for (bg_bits = 0; bg_bits < 8; bg_bits++) { \ - if (*ptr++ != bg) \ - break; \ - } \ - if (bg_bits == 8) { \ - *buf++ = 0; \ - continue; \ - } \ - mask = 0x80 >> bg_bits; \ - value = mask; \ - for (bg_bits++; bg_bits < 8; bg_bits++) { \ - mask >>= 1; \ - if (*ptr++ != bg) { \ - value |= mask; \ - } \ - } \ - *buf++ = (uint8_t)value; \ - } \ - \ - mask = 0x80; \ - value = 0; \ - if (x >= w) \ - continue; \ - \ - for (; x < w; x++) { \ - if (*ptr++ != bg) { \ - value |= mask; \ - } \ - mask >>= 1; \ - } \ - *buf++ = (uint8_t)value; \ - } \ -} - -DEFINE_MONO_ENCODE_FUNCTION(8) -DEFINE_MONO_ENCODE_FUNCTION(16) -DEFINE_MONO_ENCODE_FUNCTION(32) - - -/* - * JPEG compression stuff. - */ - -static rfbBool -SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) -{ - unsigned char *srcbuf; - int ps = cl->screen->serverFormat.bitsPerPixel / 8; - int subsamp = subsampLevel2tjsubsamp[subsampLevel]; - unsigned long size = 0; - int flags = 0, pitch; - unsigned char *tmpbuf = NULL; - - if (cl->screen->serverFormat.bitsPerPixel == 8) - return SendFullColorRect(cl, w, h); - - if (ps < 2) { - rfbLog("Error: JPEG requires 16-bit, 24-bit, or 32-bit pixel format.\n"); - return 0; - } - if (!j) { - if ((j = tjInitCompress()) == NULL) { - rfbLog("JPEG Error: %s\n", tjGetErrorStr()); - return 0; - } - } - - if (tightAfterBufSize < TJBUFSIZE(w, h)) { - if (tightAfterBuf == NULL) - tightAfterBuf = (char *)malloc(TJBUFSIZE(w, h)); - else - tightAfterBuf = (char *)realloc(tightAfterBuf, - TJBUFSIZE(w, h)); - if (!tightAfterBuf) { - rfbLog("Memory allocation failure!\n"); - return 0; - } - tightAfterBufSize = TJBUFSIZE(w, h); - } - - if (ps == 2) { - uint16_t *srcptr, pix; - unsigned char *dst; - int inRed, inGreen, inBlue, i, j; - - if((tmpbuf = (unsigned char *)malloc(w * h * 3)) == NULL) - rfbLog("Memory allocation failure!\n"); - srcptr = (uint16_t *)&cl->scaledScreen->frameBuffer - [y * cl->scaledScreen->paddedWidthInBytes + x * ps]; - dst = tmpbuf; - for(j = 0; j < h; j++) { - uint16_t *srcptr2 = srcptr; - unsigned char *dst2 = dst; - for (i = 0; i < w; i++) { - pix = *srcptr2++; - inRed = (int) (pix >> cl->screen->serverFormat.redShift - & cl->screen->serverFormat.redMax); - inGreen = (int) (pix >> cl->screen->serverFormat.greenShift - & cl->screen->serverFormat.greenMax); - inBlue = (int) (pix >> cl->screen->serverFormat.blueShift - & cl->screen->serverFormat.blueMax); - *dst2++ = (uint8_t)((inRed * 255 - + cl->screen->serverFormat.redMax / 2) - / cl->screen->serverFormat.redMax); - *dst2++ = (uint8_t)((inGreen * 255 - + cl->screen->serverFormat.greenMax / 2) - / cl->screen->serverFormat.greenMax); - *dst2++ = (uint8_t)((inBlue * 255 - + cl->screen->serverFormat.blueMax / 2) - / cl->screen->serverFormat.blueMax); - } - srcptr += cl->scaledScreen->paddedWidthInBytes / ps; - dst += w * 3; - } - srcbuf = tmpbuf; - pitch = w * 3; - ps = 3; - } else { - if (cl->screen->serverFormat.bigEndian && ps == 4) - flags |= TJ_ALPHAFIRST; - if (cl->screen->serverFormat.redShift == 16 - && cl->screen->serverFormat.blueShift == 0) - flags |= TJ_BGR; - if (cl->screen->serverFormat.bigEndian) - flags ^= TJ_BGR; - pitch = cl->scaledScreen->paddedWidthInBytes; - srcbuf = (unsigned char *)&cl->scaledScreen->frameBuffer - [y * pitch + x * ps]; - } - - if (tjCompress(j, srcbuf, w, pitch, h, ps, (unsigned char *)tightAfterBuf, - &size, subsamp, quality, flags) == -1) { - rfbLog("JPEG Error: %s\n", tjGetErrorStr()); - if (tmpbuf) { - free(tmpbuf); - tmpbuf = NULL; - } - return 0; - } - - if (tmpbuf) { - free(tmpbuf); - tmpbuf = NULL; - } - - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); - - return SendCompressedData(cl, tightAfterBuf, (int)size); -} @@ -673,9 +673,10 @@ typedef struct _rfbClientRec { int afterEncBufLen; #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) uint32_t tightEncoding; /* rfbEncodingTight or rfbEncodingTightPng */ -#ifdef LIBVNCSERVER_HAVE_TURBOVNC +#ifdef LIBVNCSERVER_HAVE_LIBJPEG /* TurboVNC Encoding support (extends TightVNC) */ - int tightSubsampLevel; + int turboSubsampLevel; + int turboQualityLevel; // 1-100 scale #endif #endif @@ -875,8 +876,8 @@ extern rfbBool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w, #define TIGHT_DEFAULT_COMPRESSION 6 -#ifdef LIBVNCSERVER_HAVE_TURBOVNC -#define TIGHT_DEFAULT_SUBSAMP 0 +#ifdef LIBVNCSERVER_HAVE_LIBJPEG +#define TURBO_DEFAULT_SUBSAMP 0 #endif extern rfbBool rfbTightDisableGradient; diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h index d99fbe5..acb2e7b 100644 --- a/rfb/rfbproto.h +++ b/rfb/rfbproto.h @@ -469,7 +469,6 @@ typedef struct { * 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels. */ -#ifdef LIBVNCSERVER_HAVE_TURBOVNC #define rfbEncodingFineQualityLevel0 0xFFFFFE00 #define rfbEncodingFineQualityLevel100 0xFFFFFE64 #define rfbEncodingSubsamp1X 0xFFFFFD00 @@ -478,7 +477,6 @@ typedef struct { #define rfbEncodingSubsampGray 0xFFFFFD03 #define rfbEncodingSubsamp8X 0xFFFFFD04 #define rfbEncodingSubsamp16X 0xFFFFFD05 -#endif #define rfbEncodingCompressLevel0 0xFFFFFF00 #define rfbEncodingCompressLevel1 0xFFFFFF01 @@ -734,12 +732,13 @@ typedef struct { * bit 3: if 1, then compression stream 3 should be reset; * bits 7-4: if 1000 (0x08), then the compression type is "fill", * if 1001 (0x09), then the compression type is "jpeg", - * (TurboVNC) if 1010 (0x0A), then the compression type is "basic" - * and no Zlib compression was used, - * (TurboVNC) if 1110 (0x0E), then the compression type is "basic", - * no Zlib compression was used, and a "filter id" byte follows - * this byte, - * (TightVNC) if 1010 (0x0A), then the compression type is "png", + * (Tight only) if 1010 (0x0A), then the compression type is + * "basic" and no Zlib compression was used, + * (Tight only) if 1110 (0x0E), then the compression type is + * "basic", no Zlib compression was used, and a "filter id" byte + * follows this byte, + * (TightPng only) if 1010 (0x0A), then the compression type is + * "png", * if 0xxx, then the compression type is "basic" and Zlib * compression was used, * values greater than 1010 are not valid. @@ -858,11 +857,8 @@ typedef struct { #define rfbTightExplicitFilter 0x04 #define rfbTightFill 0x08 #define rfbTightJpeg 0x09 -#ifdef LIBVNCSERVER_HAVE_TURBOVNC #define rfbTightNoZlib 0x0A -#else #define rfbTightPng 0x0A -#endif #define rfbTightMaxSubencoding 0x0A /* Filters to improve compression efficiency */ diff --git a/test/Makefile.am b/test/Makefile.am index 30498c2..11aea37 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,3 +1,14 @@ +# TurboJPEG wrapper tests + +noinst_PROGRAMS=tjunittest tjbench + +tjunittest_SOURCES=tjunittest.c ../common/turbojpeg.c ../common/turbojpeg.h \ + tjutil.c tjutil.h + +tjbench_SOURCES=tjbench.c ../common/turbojpeg.c ../common/turbojpeg.h \ + tjutil.c tjutil.h bmp.c bmp.h + + INCLUDES = -I$(top_srcdir) LDADD = ../libvncserver/libvncserver.la ../libvncclient/libvncclient.la @WSOCKLIB@ diff --git a/test/bmp.c b/test/bmp.c new file mode 100644 index 0000000..7dcbf13 --- /dev/null +++ b/test/bmp.c @@ -0,0 +1,389 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * Copyright (C)2010, 2012 D. R. Commander + * + * This library is free software and may be redistributed and/or modified under + * the terms of the wxWindows Library License, Version 3.1 or (at your option) + * any later version. The full license is in the LICENSE.txt file included + * with this distribution. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * wxWindows Library License for more details. +*/ + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#ifdef _WIN32 + #include <io.h> +#else + #include <unistd.h> +#endif +#include "./tjutil.h" +#include "./bmp.h" + +#define byteswap(i) ( \ + (((i) & 0xff000000) >> 24) | \ + (((i) & 0x00ff0000) >> 8) | \ + (((i) & 0x0000ff00) << 8) | \ + (((i) & 0x000000ff) << 24) ) + +#define byteswap16(i) ( \ + (((i) & 0xff00) >> 8) | \ + (((i) & 0x00ff) << 8) ) + +static __inline int littleendian(void) +{ + unsigned int value=1; + unsigned char *ptr=(unsigned char *)(&value); + if(ptr[0]==1 && ptr[3]==0) return 1; + else return 0; +} + +#ifndef BI_BITFIELDS +#define BI_BITFIELDS 3L +#endif +#ifndef BI_RGB +#define BI_RGB 0L +#endif + +#define BMPHDRSIZE 54 +typedef struct _bmphdr +{ + unsigned short bfType; + unsigned int bfSize; + unsigned short bfReserved1, bfReserved2; + unsigned int bfOffBits; + + unsigned int biSize; + int biWidth, biHeight; + unsigned short biPlanes, biBitCount; + unsigned int biCompression, biSizeImage; + int biXPelsPerMeter, biYPelsPerMeter; + unsigned int biClrUsed, biClrImportant; +} bmphdr; + +static const char *__bmperr="No error"; + +static const int ps[BMPPIXELFORMATS]={3, 4, 3, 4, 4, 4}; +static const int roffset[BMPPIXELFORMATS]={0, 0, 2, 2, 3, 1}; +static const int goffset[BMPPIXELFORMATS]={1, 1, 1, 1, 2, 2}; +static const int boffset[BMPPIXELFORMATS]={2, 2, 0, 0, 1, 3}; + +#define _throw(m) {__bmperr=m; retcode=-1; goto finally;} +#define _unix(f) {if((f)==-1) _throw(strerror(errno));} +#define _catch(f) {if((f)==-1) {retcode=-1; goto finally;}} + +#define readme(fd, addr, size) \ + if((bytesread=read(fd, addr, (size)))==-1) _throw(strerror(errno)); \ + if(bytesread!=(size)) _throw("Read error"); + +void pixelconvert(unsigned char *srcbuf, enum BMPPIXELFORMAT srcformat, + int srcpitch, unsigned char *dstbuf, enum BMPPIXELFORMAT dstformat, int dstpitch, + int w, int h, int flip) +{ + unsigned char *srcptr, *srcptr0, *dstptr, *dstptr0; + int i, j; + + srcptr=flip? &srcbuf[srcpitch*(h-1)]:srcbuf; + for(j=0, dstptr=dstbuf; j<h; j++, + srcptr+=flip? -srcpitch:srcpitch, dstptr+=dstpitch) + { + for(i=0, srcptr0=srcptr, dstptr0=dstptr; i<w; i++, + srcptr0+=ps[srcformat], dstptr0+=ps[dstformat]) + { + dstptr0[roffset[dstformat]]=srcptr0[roffset[srcformat]]; + dstptr0[goffset[dstformat]]=srcptr0[goffset[srcformat]]; + dstptr0[boffset[dstformat]]=srcptr0[boffset[srcformat]]; + } + } +} + +int loadppm(int *fd, unsigned char **buf, int *w, int *h, + enum BMPPIXELFORMAT f, int align, int dstbottomup, int ascii) +{ + FILE *fs=NULL; int retcode=0, scalefactor, dstpitch; + unsigned char *tempbuf=NULL; char temps[255], temps2[255]; + int numread=0, totalread=0, pixel[3], i, j; + + if((fs=fdopen(*fd, "r"))==NULL) _throw(strerror(errno)); + + do + { + if(!fgets(temps, 255, fs)) _throw("Read error"); + if(strlen(temps)==0 || temps[0]=='\n') continue; + if(sscanf(temps, "%s", temps2)==1 && temps2[1]=='#') continue; + switch(totalread) + { + case 0: + if((numread=sscanf(temps, "%d %d %d", w, h, &scalefactor))==EOF) + _throw("Read error"); + break; + case 1: + if((numread=sscanf(temps, "%d %d", h, &scalefactor))==EOF) + _throw("Read error"); + break; + case 2: + if((numread=sscanf(temps, "%d", &scalefactor))==EOF) + _throw("Read error"); + break; + } + totalread+=numread; + } while(totalread<3); + if((*w)<1 || (*h)<1 || scalefactor<1) _throw("Corrupt PPM header"); + + dstpitch=(((*w)*ps[f])+(align-1))&(~(align-1)); + if((*buf=(unsigned char *)malloc(dstpitch*(*h)))==NULL) + _throw("Memory allocation error"); + if(ascii) + { + for(j=0; j<*h; j++) + { + for(i=0; i<*w; i++) + { + if(fscanf(fs, "%d%d%d", &pixel[0], &pixel[1], &pixel[2])!=3) + _throw("Read error"); + (*buf)[j*dstpitch+i*ps[f]+roffset[f]]=(unsigned char)(pixel[0]*255/scalefactor); + (*buf)[j*dstpitch+i*ps[f]+goffset[f]]=(unsigned char)(pixel[1]*255/scalefactor); + (*buf)[j*dstpitch+i*ps[f]+boffset[f]]=(unsigned char)(pixel[2]*255/scalefactor); + } + } + } + else + { + if(scalefactor!=255) + _throw("Binary PPMs must have 8-bit components"); + if((tempbuf=(unsigned char *)malloc((*w)*(*h)*3))==NULL) + _throw("Memory allocation error"); + if(fread(tempbuf, (*w)*(*h)*3, 1, fs)!=1) _throw("Read error"); + pixelconvert(tempbuf, BMP_RGB, (*w)*3, *buf, f, dstpitch, *w, *h, dstbottomup); + } + + finally: + if(fs) {fclose(fs); *fd=-1;} + if(tempbuf) free(tempbuf); + return retcode; +} + + +int loadbmp(char *filename, unsigned char **buf, int *w, int *h, + enum BMPPIXELFORMAT f, int align, int dstbottomup) +{ + int fd=-1, bytesread, srcpitch, srcbottomup=1, srcps, dstpitch, + retcode=0; + unsigned char *tempbuf=NULL; + bmphdr bh; int flags=O_RDONLY; + + dstbottomup=dstbottomup? 1:0; + #ifdef _WIN32 + flags|=O_BINARY; + #endif + if(!filename || !buf || !w || !h || f<0 || f>BMPPIXELFORMATS-1 || align<1) + _throw("invalid argument to loadbmp()"); + if((align&(align-1))!=0) + _throw("Alignment must be a power of 2"); + _unix(fd=open(filename, flags)); + + readme(fd, &bh.bfType, sizeof(unsigned short)); + if(!littleendian()) bh.bfType=byteswap16(bh.bfType); + + if(bh.bfType==0x3650) + { + _catch(loadppm(&fd, buf, w, h, f, align, dstbottomup, 0)); + goto finally; + } + if(bh.bfType==0x3350) + { + _catch(loadppm(&fd, buf, w, h, f, align, dstbottomup, 1)); + goto finally; + } + + readme(fd, &bh.bfSize, sizeof(unsigned int)); + readme(fd, &bh.bfReserved1, sizeof(unsigned short)); + readme(fd, &bh.bfReserved2, sizeof(unsigned short)); + readme(fd, &bh.bfOffBits, sizeof(unsigned int)); + readme(fd, &bh.biSize, sizeof(unsigned int)); + readme(fd, &bh.biWidth, sizeof(int)); + readme(fd, &bh.biHeight, sizeof(int)); + readme(fd, &bh.biPlanes, sizeof(unsigned short)); + readme(fd, &bh.biBitCount, sizeof(unsigned short)); + readme(fd, &bh.biCompression, sizeof(unsigned int)); + readme(fd, &bh.biSizeImage, sizeof(unsigned int)); + readme(fd, &bh.biXPelsPerMeter, sizeof(int)); + readme(fd, &bh.biYPelsPerMeter, sizeof(int)); + readme(fd, &bh.biClrUsed, sizeof(unsigned int)); + readme(fd, &bh.biClrImportant, sizeof(unsigned int)); + + if(!littleendian()) + { + bh.bfSize=byteswap(bh.bfSize); + bh.bfOffBits=byteswap(bh.bfOffBits); + bh.biSize=byteswap(bh.biSize); + bh.biWidth=byteswap(bh.biWidth); + bh.biHeight=byteswap(bh.biHeight); + bh.biPlanes=byteswap16(bh.biPlanes); + bh.biBitCount=byteswap16(bh.biBitCount); + bh.biCompression=byteswap(bh.biCompression); + bh.biSizeImage=byteswap(bh.biSizeImage); + bh.biXPelsPerMeter=byteswap(bh.biXPelsPerMeter); + bh.biYPelsPerMeter=byteswap(bh.biYPelsPerMeter); + bh.biClrUsed=byteswap(bh.biClrUsed); + bh.biClrImportant=byteswap(bh.biClrImportant); + } + + if(bh.bfType!=0x4d42 || bh.bfOffBits<BMPHDRSIZE + || bh.biWidth<1 || bh.biHeight==0) + _throw("Corrupt bitmap header"); + if((bh.biBitCount!=24 && bh.biBitCount!=32) || bh.biCompression!=BI_RGB) + _throw("Only uncompessed RGB bitmaps are supported"); + + *w=bh.biWidth; *h=bh.biHeight; srcps=bh.biBitCount/8; + if(*h<0) {*h=-(*h); srcbottomup=0;} + srcpitch=(((*w)*srcps)+3)&(~3); + dstpitch=(((*w)*ps[f])+(align-1))&(~(align-1)); + + if(srcpitch*(*h)+bh.bfOffBits!=bh.bfSize) _throw("Corrupt bitmap header"); + if((tempbuf=(unsigned char *)malloc(srcpitch*(*h)))==NULL + || (*buf=(unsigned char *)malloc(dstpitch*(*h)))==NULL) + _throw("Memory allocation error"); + if(lseek(fd, (long)bh.bfOffBits, SEEK_SET)!=(long)bh.bfOffBits) + _throw(strerror(errno)); + _unix(bytesread=read(fd, tempbuf, srcpitch*(*h))); + if(bytesread!=srcpitch*(*h)) _throw("Read error"); + + pixelconvert(tempbuf, BMP_BGR, srcpitch, *buf, f, dstpitch, *w, *h, + srcbottomup!=dstbottomup); + + finally: + if(tempbuf) free(tempbuf); + if(fd!=-1) close(fd); + return retcode; +} + +#define writeme(fd, addr, size) \ + if((byteswritten=write(fd, addr, (size)))==-1) _throw(strerror(errno)); \ + if(byteswritten!=(size)) _throw("Write error"); + +int saveppm(char *filename, unsigned char *buf, int w, int h, + enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup) +{ + FILE *fs=NULL; int retcode=0; + unsigned char *tempbuf=NULL; + + if((fs=fopen(filename, "wb"))==NULL) _throw(strerror(errno)); + if(fprintf(fs, "P6\n")<1) _throw("Write error"); + if(fprintf(fs, "%d %d\n", w, h)<1) _throw("Write error"); + if(fprintf(fs, "255\n")<1) _throw("Write error"); + + if((tempbuf=(unsigned char *)malloc(w*h*3))==NULL) + _throw("Memory allocation error"); + + pixelconvert(buf, f, srcpitch, tempbuf, BMP_RGB, w*3, w, h, + srcbottomup); + + if((fwrite(tempbuf, w*h*3, 1, fs))!=1) _throw("Write error"); + + finally: + if(tempbuf) free(tempbuf); + if(fs) fclose(fs); + return retcode; +} + +int savebmp(char *filename, unsigned char *buf, int w, int h, + enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup) +{ + int fd=-1, byteswritten, dstpitch, retcode=0; + int flags=O_RDWR|O_CREAT|O_TRUNC; + unsigned char *tempbuf=NULL; char *temp; + bmphdr bh; int mode; + + #ifdef _WIN32 + flags|=O_BINARY; mode=_S_IREAD|_S_IWRITE; + #else + mode=S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + #endif + if(!filename || !buf || w<1 || h<1 || f<0 || f>BMPPIXELFORMATS-1 || srcpitch<0) + _throw("bad argument to savebmp()"); + + if(srcpitch==0) srcpitch=w*ps[f]; + + if((temp=strrchr(filename, '.'))!=NULL) + { + if(!strcasecmp(temp, ".ppm")) + return saveppm(filename, buf, w, h, f, srcpitch, srcbottomup); + } + + _unix(fd=open(filename, flags, mode)); + dstpitch=((w*3)+3)&(~3); + + bh.bfType=0x4d42; + bh.bfSize=BMPHDRSIZE+dstpitch*h; + bh.bfReserved1=0; bh.bfReserved2=0; + bh.bfOffBits=BMPHDRSIZE; + bh.biSize=40; + bh.biWidth=w; bh.biHeight=h; + bh.biPlanes=0; bh.biBitCount=24; + bh.biCompression=BI_RGB; bh.biSizeImage=0; + bh.biXPelsPerMeter=0; bh.biYPelsPerMeter=0; + bh.biClrUsed=0; bh.biClrImportant=0; + + if(!littleendian()) + { + bh.bfType=byteswap16(bh.bfType); + bh.bfSize=byteswap(bh.bfSize); + bh.bfOffBits=byteswap(bh.bfOffBits); + bh.biSize=byteswap(bh.biSize); + bh.biWidth=byteswap(bh.biWidth); + bh.biHeight=byteswap(bh.biHeight); + bh.biPlanes=byteswap16(bh.biPlanes); + bh.biBitCount=byteswap16(bh.biBitCount); + bh.biCompression=byteswap(bh.biCompression); + bh.biSizeImage=byteswap(bh.biSizeImage); + bh.biXPelsPerMeter=byteswap(bh.biXPelsPerMeter); + bh.biYPelsPerMeter=byteswap(bh.biYPelsPerMeter); + bh.biClrUsed=byteswap(bh.biClrUsed); + bh.biClrImportant=byteswap(bh.biClrImportant); + } + + writeme(fd, &bh.bfType, sizeof(unsigned short)); + writeme(fd, &bh.bfSize, sizeof(unsigned int)); + writeme(fd, &bh.bfReserved1, sizeof(unsigned short)); + writeme(fd, &bh.bfReserved2, sizeof(unsigned short)); + writeme(fd, &bh.bfOffBits, sizeof(unsigned int)); + writeme(fd, &bh.biSize, sizeof(unsigned int)); + writeme(fd, &bh.biWidth, sizeof(int)); + writeme(fd, &bh.biHeight, sizeof(int)); + writeme(fd, &bh.biPlanes, sizeof(unsigned short)); + writeme(fd, &bh.biBitCount, sizeof(unsigned short)); + writeme(fd, &bh.biCompression, sizeof(unsigned int)); + writeme(fd, &bh.biSizeImage, sizeof(unsigned int)); + writeme(fd, &bh.biXPelsPerMeter, sizeof(int)); + writeme(fd, &bh.biYPelsPerMeter, sizeof(int)); + writeme(fd, &bh.biClrUsed, sizeof(unsigned int)); + writeme(fd, &bh.biClrImportant, sizeof(unsigned int)); + + if((tempbuf=(unsigned char *)malloc(dstpitch*h))==NULL) + _throw("Memory allocation error"); + + pixelconvert(buf, f, srcpitch, tempbuf, BMP_BGR, dstpitch, w, h, + !srcbottomup); + + if((byteswritten=write(fd, tempbuf, dstpitch*h))!=dstpitch*h) + _throw(strerror(errno)); + + finally: + if(tempbuf) free(tempbuf); + if(fd!=-1) close(fd); + return retcode; +} + +const char *bmpgeterr(void) +{ + return __bmperr; +} diff --git a/test/bmp.h b/test/bmp.h new file mode 100644 index 0000000..055b1ee --- /dev/null +++ b/test/bmp.h @@ -0,0 +1,49 @@ +/* Copyright (C)2004 Landmark Graphics Corporation + * Copyright (C)2005 Sun Microsystems, Inc. + * Copyright (C)2011 D. R. Commander + * + * This library is free software and may be redistributed and/or modified under + * the terms of the wxWindows Library License, Version 3.1 or (at your option) + * any later version. The full license is in the LICENSE.txt file included + * with this distribution. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * wxWindows Library License for more details. +*/ + +// This provides rudimentary facilities for loading and saving true color +// BMP and PPM files + +#ifndef __BMP_H__ +#define __BMP_H__ + +#define BMPPIXELFORMATS 6 +enum BMPPIXELFORMAT {BMP_RGB=0, BMP_RGBX, BMP_BGR, BMP_BGRX, BMP_XBGR, BMP_XRGB}; + +#ifdef __cplusplus +extern "C" { +#endif + +// This will load a Windows bitmap from a file and return a buffer with the +// specified pixel format, scanline alignment, and orientation. The width and +// height are returned in w and h. + +int loadbmp(char *filename, unsigned char **buf, int *w, int *h, + enum BMPPIXELFORMAT f, int align, int dstbottomup); + +// This will save a buffer with the specified pixel format, pitch, orientation, +// width, and height as a 24-bit Windows bitmap or PPM (the filename determines +// which format to use) + +int savebmp(char *filename, unsigned char *buf, int w, int h, + enum BMPPIXELFORMAT f, int srcpitch, int srcbottomup); + +const char *bmpgeterr(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test/tjbench.c b/test/tjbench.c new file mode 100644 index 0000000..b1f343f --- /dev/null +++ b/test/tjbench.c @@ -0,0 +1,658 @@ +/* + * Copyright (C)2009-2012 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <errno.h> +#include "./bmp.h" +#include "./tjutil.h" +#include "./turbojpeg.h" + + +#define _throw(op, err) { \ + printf("ERROR in line %d while %s:\n%s\n", __LINE__, op, err); \ + retval=-1; goto bailout;} +#define _throwunix(m) _throw(m, strerror(errno)) +#define _throwtj(m) _throw(m, tjGetErrorStr()) +#define _throwbmp(m) _throw(m, bmpgeterr()) + +int flags=0, decomponly=0, quiet=0, dotile=0, pf=TJPF_BGR; +char *ext="ppm"; +const char *pixFormatStr[TJ_NUMPF]= +{ + "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "GRAY" +}; +const int bmpPF[TJ_NUMPF]= +{ + BMP_RGB, BMP_BGR, BMP_RGBX, BMP_BGRX, BMP_XBGR, BMP_XRGB, -1 +}; +const char *subNameLong[TJ_NUMSAMP]= +{ + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0" +}; +const char *subName[NUMSUBOPT]={"444", "422", "420", "GRAY", "440"}; +tjscalingfactor *scalingfactors=NULL, sf={1, 1}; int nsf=0; +double benchtime=5.0; + + +char *sigfig(double val, int figs, char *buf, int len) +{ + char format[80]; + int digitsafterdecimal=figs-(int)ceil(log10(fabs(val))); + if(digitsafterdecimal<1) snprintf(format, 80, "%%.0f"); + else snprintf(format, 80, "%%.%df", digitsafterdecimal); + snprintf(buf, len, format, val); + return buf; +} + + +/* Decompression test */ +int decomptest(unsigned char *srcbuf, unsigned char **jpegbuf, + unsigned long *jpegsize, unsigned char *dstbuf, int w, int h, + int subsamp, int jpegqual, char *filename, int tilew, int tileh) +{ + char tempstr[1024], sizestr[20]="\0", qualstr[6]="\0", *ptr; + FILE *file=NULL; tjhandle handle=NULL; + int row, col, i, dstbufalloc=0, retval=0; + double start, elapsed; + int ps=tjPixelSize[pf]; + int bufsize; + int scaledw=TJSCALED(w, sf); + int scaledh=TJSCALED(h, sf); + int pitch=scaledw*ps; + int ntilesw=(w+tilew-1)/tilew, ntilesh=(h+tileh-1)/tileh; + unsigned char *dstptr, *dstptr2; + + if(jpegqual>0) + { + snprintf(qualstr, 6, "_Q%d", jpegqual); + qualstr[5]=0; + } + + if((handle=tjInitDecompress())==NULL) + _throwtj("executing tjInitDecompress()"); + + bufsize=pitch*scaledh; + if(dstbuf==NULL) + { + if((dstbuf=(unsigned char *)malloc(bufsize)) == NULL) + _throwunix("allocating image buffer"); + dstbufalloc=1; + } + /* Set the destination buffer to gray so we know whether the decompressor + attempted to write to it */ + memset(dstbuf, 127, bufsize); + + /* Execute once to preload cache */ + if(tjDecompress2(handle, jpegbuf[0], jpegsize[0], dstbuf, scaledw, + pitch, scaledh, pf, flags)==-1) + _throwtj("executing tjDecompress2()"); + + /* Benchmark */ + for(i=0, start=gettime(); (elapsed=gettime()-start)<benchtime; i++) + { + int tile=0; + for(row=0, dstptr=dstbuf; row<ntilesh; row++, dstptr+=pitch*tileh) + { + for(col=0, dstptr2=dstptr; col<ntilesw; col++, tile++, dstptr2+=ps*tilew) + { + int width=dotile? min(tilew, w-col*tilew):scaledw; + int height=dotile? min(tileh, h-row*tileh):scaledh; + if(tjDecompress2(handle, jpegbuf[tile], jpegsize[tile], dstptr2, width, + pitch, height, pf, flags)==-1) + _throwtj("executing tjDecompress2()"); + } + } + } + + if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()"); + handle=NULL; + + if(quiet) + { + printf("%s\n", + sigfig((double)(w*h)/1000000.*(double)i/elapsed, 4, tempstr, 1024)); + } + else + { + printf("D--> Frame rate: %f fps\n", (double)i/elapsed); + printf(" Dest. throughput: %f Megapixels/sec\n", + (double)(w*h)/1000000.*(double)i/elapsed); + } + if(sf.num!=1 || sf.denom!=1) + snprintf(sizestr, 20, "%d_%d", sf.num, sf.denom); + else if(tilew!=w || tileh!=h) + snprintf(sizestr, 20, "%dx%d", tilew, tileh); + else snprintf(sizestr, 20, "full"); + if(decomponly) + snprintf(tempstr, 1024, "%s_%s.%s", filename, sizestr, ext); + else + snprintf(tempstr, 1024, "%s_%s%s_%s.%s", filename, subName[subsamp], + qualstr, sizestr, ext); + if(savebmp(tempstr, dstbuf, scaledw, scaledh, bmpPF[pf], pitch, + (flags&TJFLAG_BOTTOMUP)!=0)==-1) + _throwbmp("saving bitmap"); + ptr=strrchr(tempstr, '.'); + snprintf(ptr, 1024-(ptr-tempstr), "-err.%s", ext); + if(srcbuf && sf.num==1 && sf.denom==1) + { + if(!quiet) printf("Compression error written to %s.\n", tempstr); + if(subsamp==TJ_GRAYSCALE) + { + int index, index2; + for(row=0, index=0; row<h; row++, index+=pitch) + { + for(col=0, index2=index; col<w; col++, index2+=ps) + { + int rindex=index2+tjRedOffset[pf]; + int gindex=index2+tjGreenOffset[pf]; + int bindex=index2+tjBlueOffset[pf]; + int y=(int)((double)srcbuf[rindex]*0.299 + + (double)srcbuf[gindex]*0.587 + + (double)srcbuf[bindex]*0.114 + 0.5); + if(y>255) y=255; if(y<0) y=0; + dstbuf[rindex]=abs(dstbuf[rindex]-y); + dstbuf[gindex]=abs(dstbuf[gindex]-y); + dstbuf[bindex]=abs(dstbuf[bindex]-y); + } + } + } + else + { + for(row=0; row<h; row++) + for(col=0; col<w*ps; col++) + dstbuf[pitch*row+col] + =abs(dstbuf[pitch*row+col]-srcbuf[pitch*row+col]); + } + if(savebmp(tempstr, dstbuf, w, h, bmpPF[pf], pitch, + (flags&TJFLAG_BOTTOMUP)!=0)==-1) + _throwbmp("saving bitmap"); + } + + bailout: + if(file) {fclose(file); file=NULL;} + if(handle) {tjDestroy(handle); handle=NULL;} + if(dstbuf && dstbufalloc) {free(dstbuf); dstbuf=NULL;} + return retval; +} + + +void dotest(unsigned char *srcbuf, int w, int h, int subsamp, int jpegqual, + char *filename) +{ + char tempstr[1024], tempstr2[80]; + FILE *file=NULL; tjhandle handle=NULL; + unsigned char **jpegbuf=NULL, *tmpbuf=NULL, *srcptr, *srcptr2; + double start, elapsed; + int totaljpegsize=0, row, col, i, tilew=w, tileh=h, retval=0; + unsigned long *jpegsize=NULL; + int ps=tjPixelSize[pf], ntilesw=1, ntilesh=1, pitch=w*ps; + + if((tmpbuf=(unsigned char *)malloc(pitch*h)) == NULL) + _throwunix("allocating temporary image buffer"); + + if(!quiet) + printf(">>>>> %s (%s) <--> JPEG %s Q%d <<<<<\n", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "Bottom-up":"Top-down", subNameLong[subsamp], + jpegqual); + + for(tilew=dotile? 8:w, tileh=dotile? 8:h; ; tilew*=2, tileh*=2) + { + if(tilew>w) tilew=w; if(tileh>h) tileh=h; + ntilesw=(w+tilew-1)/tilew; ntilesh=(h+tileh-1)/tileh; + + if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *) + *ntilesw*ntilesh))==NULL) + _throwunix("allocating JPEG tile array"); + memset(jpegbuf, 0, sizeof(unsigned char *)*ntilesw*ntilesh); + if((jpegsize=(unsigned long *)malloc(sizeof(unsigned long) + *ntilesw*ntilesh))==NULL) + _throwunix("allocating JPEG size array"); + memset(jpegsize, 0, sizeof(unsigned long)*ntilesw*ntilesh); + + for(i=0; i<ntilesw*ntilesh; i++) + { + if((jpegbuf[i]=(unsigned char *)malloc(tjBufSize(tilew, tileh, + subsamp)))==NULL) + _throwunix("allocating JPEG tiles"); + } + + /* Compression test */ + if(quiet==1) + printf("%s\t%s\t%s\t%d\t", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "BU":"TD", subNameLong[subsamp], jpegqual); + for(i=0; i<h; i++) + memcpy(&tmpbuf[pitch*i], &srcbuf[w*ps*i], w*ps); + if((handle=tjInitCompress())==NULL) + _throwtj("executing tjInitCompress()"); + + /* Execute once to preload cache */ + if(tjCompress2(handle, srcbuf, tilew, pitch, tileh, pf, &jpegbuf[0], + &jpegsize[0], subsamp, jpegqual, flags)==-1) + _throwtj("executing tjCompress2()"); + + /* Benchmark */ + for(i=0, start=gettime(); (elapsed=gettime()-start)<benchtime; i++) + { + int tile=0; + totaljpegsize=0; + for(row=0, srcptr=srcbuf; row<ntilesh; row++, srcptr+=pitch*tileh) + { + for(col=0, srcptr2=srcptr; col<ntilesw; col++, tile++, + srcptr2+=ps*tilew) + { + int width=min(tilew, w-col*tilew); + int height=min(tileh, h-row*tileh); + if(tjCompress2(handle, srcptr2, width, pitch, height, pf, + &jpegbuf[tile], &jpegsize[tile], subsamp, jpegqual, flags)==-1) + _throwtj("executing tjCompress()2"); + totaljpegsize+=jpegsize[tile]; + } + } + } + + if(tjDestroy(handle)==-1) _throwtj("executing tjDestroy()"); + handle=NULL; + + if(quiet==1) printf("%-4d %-4d\t", tilew, tileh); + if(quiet) + { + printf("%s%c%s%c", + sigfig((double)(w*h)/1000000.*(double)i/elapsed, 4, tempstr, 1024), + quiet==2? '\n':'\t', + sigfig((double)(w*h*ps)/(double)totaljpegsize, 4, tempstr2, 80), + quiet==2? '\n':'\t'); + } + else + { + printf("\n%s size: %d x %d\n", dotile? "Tile":"Image", tilew, + tileh); + printf("C--> Frame rate: %f fps\n", (double)i/elapsed); + printf(" Output image size: %d bytes\n", totaljpegsize); + printf(" Compression ratio: %f:1\n", + (double)(w*h*ps)/(double)totaljpegsize); + printf(" Source throughput: %f Megapixels/sec\n", + (double)(w*h)/1000000.*(double)i/elapsed); + printf(" Output bit stream: %f Megabits/sec\n", + (double)totaljpegsize*8./1000000.*(double)i/elapsed); + } + if(tilew==w && tileh==h) + { + snprintf(tempstr, 1024, "%s_%s_Q%d.jpg", filename, subName[subsamp], + jpegqual); + if((file=fopen(tempstr, "wb"))==NULL) + _throwunix("opening reference image"); + if(fwrite(jpegbuf[0], jpegsize[0], 1, file)!=1) + _throwunix("writing reference image"); + fclose(file); file=NULL; + if(!quiet) printf("Reference image written to %s\n", tempstr); + } + + /* Decompression test */ + if(decomptest(srcbuf, jpegbuf, jpegsize, tmpbuf, w, h, subsamp, jpegqual, + filename, tilew, tileh)==-1) + goto bailout; + + for(i=0; i<ntilesw*ntilesh; i++) + { + if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL; + } + free(jpegbuf); jpegbuf=NULL; + free(jpegsize); jpegsize=NULL; + + if(tilew==w && tileh==h) break; + } + + bailout: + if(file) {fclose(file); file=NULL;} + if(jpegbuf) + { + for(i=0; i<ntilesw*ntilesh; i++) + { + if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL; + } + free(jpegbuf); jpegbuf=NULL; + } + if(jpegsize) {free(jpegsize); jpegsize=NULL;} + if(tmpbuf) {free(tmpbuf); tmpbuf=NULL;} + if(handle) {tjDestroy(handle); handle=NULL;} + return; +} + + +void dodecomptest(char *filename) +{ + FILE *file=NULL; tjhandle handle=NULL; + unsigned char **jpegbuf=NULL, *srcbuf=NULL; + unsigned long *jpegsize=NULL, srcsize; + int w=0, h=0, subsamp=-1, _w, _h, _tilew, _tileh, _subsamp; + char *temp=NULL; + int i, tilew, tileh, ntilesw=1, ntilesh=1, retval=0; + + if((file=fopen(filename, "rb"))==NULL) + _throwunix("opening file"); + if(fseek(file, 0, SEEK_END)<0 || (srcsize=ftell(file))<0) + _throwunix("determining file size"); + if((srcbuf=(unsigned char *)malloc(srcsize))==NULL) + _throwunix("allocating memory"); + if(fseek(file, 0, SEEK_SET)<0) + _throwunix("setting file position"); + if(fread(srcbuf, srcsize, 1, file)<1) + _throwunix("reading JPEG data"); + fclose(file); file=NULL; + + temp=strrchr(filename, '.'); + if(temp!=NULL) *temp='\0'; + + if((handle=tjInitDecompress())==NULL) + _throwtj("executing tjInitDecompress()"); + if(tjDecompressHeader2(handle, srcbuf, srcsize, &w, &h, &subsamp)==-1) + _throwtj("executing tjDecompressHeader2()"); + + if(quiet==1) + { + printf("All performance values in Mpixels/sec\n\n"); + printf("Bitmap\tBitmap\tJPEG\t%s %s \tXform\tComp\tDecomp\n", + dotile? "Tile ":"Image", dotile? "Tile ":"Image"); + printf("Format\tOrder\tSubsamp\tWidth Height\tPerf \tRatio\tPerf\n\n"); + } + else if(!quiet) + { + printf(">>>>> JPEG %s --> %s (%s) <<<<<\n", subNameLong[subsamp], + pixFormatStr[pf], (flags&TJFLAG_BOTTOMUP)? "Bottom-up":"Top-down"); + } + + for(tilew=dotile? 16:w, tileh=dotile? 16:h; ; tilew*=2, tileh*=2) + { + if(tilew>w) tilew=w; if(tileh>h) tileh=h; + ntilesw=(w+tilew-1)/tilew; ntilesh=(h+tileh-1)/tileh; + + if((jpegbuf=(unsigned char **)malloc(sizeof(unsigned char *) + *ntilesw*ntilesh))==NULL) + _throwunix("allocating JPEG tile array"); + memset(jpegbuf, 0, sizeof(unsigned char *)*ntilesw*ntilesh); + if((jpegsize=(unsigned long *)malloc(sizeof(unsigned long) + *ntilesw*ntilesh))==NULL) + _throwunix("allocating JPEG size array"); + memset(jpegsize, 0, sizeof(unsigned long)*ntilesw*ntilesh); + + for(i=0; i<ntilesw*ntilesh; i++) + { + if((jpegbuf[i]=(unsigned char *)malloc(tjBufSize(tilew, tileh, + subsamp)))==NULL) + _throwunix("allocating JPEG tiles"); + } + + _w=w; _h=h; _tilew=tilew; _tileh=tileh; + if(!quiet) + { + printf("\n%s size: %d x %d", dotile? "Tile":"Image", _tilew, + _tileh); + if(sf.num!=1 || sf.denom!=1) + printf(" --> %d x %d", TJSCALED(_w, sf), TJSCALED(_h, sf)); + printf("\n"); + } + else if(quiet==1) + { + printf("%s\t%s\t%s\t", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "BU":"TD", subNameLong[subsamp]); + printf("%-4d %-4d\t", tilew, tileh); + } + + _subsamp=subsamp; + if(quiet==1) printf("N/A\tN/A\t"); + jpegsize[0]=srcsize; + memcpy(jpegbuf[0], srcbuf, srcsize); + + if(w==tilew) _tilew=_w; + if(h==tileh) _tileh=_h; + if(decomptest(NULL, jpegbuf, jpegsize, NULL, _w, _h, _subsamp, 0, + filename, _tilew, _tileh)==-1) + goto bailout; + else if(quiet==1) printf("N/A\n"); + + for(i=0; i<ntilesw*ntilesh; i++) + { + free(jpegbuf[i]); jpegbuf[i]=NULL; + } + free(jpegbuf); jpegbuf=NULL; + if(jpegsize) {free(jpegsize); jpegsize=NULL;} + + if(tilew==w && tileh==h) break; + } + + bailout: + if(file) {fclose(file); file=NULL;} + if(jpegbuf) + { + for(i=0; i<ntilesw*ntilesh; i++) + { + if(jpegbuf[i]) free(jpegbuf[i]); jpegbuf[i]=NULL; + } + free(jpegbuf); jpegbuf=NULL; + } + if(jpegsize) {free(jpegsize); jpegsize=NULL;} + if(srcbuf) {free(srcbuf); srcbuf=NULL;} + if(handle) {tjDestroy(handle); handle=NULL;} + return; +} + + +void usage(char *progname) +{ + int i; + printf("USAGE: %s\n", progname); + printf(" <Inputfile (BMP|PPM)> <%% Quality> [options]\n\n"); + printf(" %s\n", progname); + printf(" <Inputfile (JPG)> [options]\n\n"); + printf("Options:\n\n"); + printf("-bmp = Generate output images in Windows Bitmap format (default=PPM)\n"); + printf("-bottomup = Test bottom-up compression/decompression\n"); + printf("-tile = Test performance of the codec when the image is encoded as separate\n"); + printf(" tiles of varying sizes.\n"); + printf("-forcemmx, -forcesse, -forcesse2, -forcesse3 =\n"); + printf(" Force MMX, SSE, SSE2, or SSE3 code paths in the underlying codec\n"); + printf("-rgb, -bgr, -rgbx, -bgrx, -xbgr, -xrgb =\n"); + printf(" Test the specified color conversion path in the codec (default: BGR)\n"); + printf("-fastupsample = Use fast, inaccurate upsampling code to perform 4:2:2 and 4:2:0\n"); + printf(" YUV decoding\n"); + printf("-quiet = Output results in tabular rather than verbose format\n"); + printf("-scale M/N = scale down the width/height of the decompressed JPEG image by a\n"); + printf(" factor of M/N (M/N = "); + for(i=0; i<nsf; i++) + { + printf("%d/%d", scalingfactors[i].num, scalingfactors[i].denom); + if(nsf==2 && i!=nsf-1) printf(" or "); + else if(nsf>2) + { + if(i!=nsf-1) printf(", "); + if(i==nsf-2) printf("or "); + } + } + printf(")\n"); + printf("-benchtime <t> = Run each benchmark for at least <t> seconds (default = 5.0)\n\n"); + printf("NOTE: If the quality is specified as a range (e.g. 90-100), a separate\n"); + printf("test will be performed for all quality values in the range.\n\n"); + exit(1); +} + + +int main(int argc, char *argv[]) +{ + unsigned char *srcbuf=NULL; int w, h, i, j; + int minqual=-1, maxqual=-1; char *temp; + int minarg=2; int retval=0; + + if((scalingfactors=tjGetScalingFactors(&nsf))==NULL || nsf==0) + _throwtj("executing tjGetScalingFactors()"); + + if(argc<minarg) usage(argv[0]); + + temp=strrchr(argv[1], '.'); + if(temp!=NULL) + { + if(!strcasecmp(temp, ".bmp")) ext="bmp"; + if(!strcasecmp(temp, ".jpg") || !strcasecmp(temp, ".jpeg")) decomponly=1; + } + + printf("\n"); + + if(!decomponly) + { + minarg=3; + if(argc<minarg) usage(argv[0]); + if((minqual=atoi(argv[2]))<1 || minqual>100) + { + puts("ERROR: Quality must be between 1 and 100."); + exit(1); + } + if((temp=strchr(argv[2], '-'))!=NULL && strlen(temp)>1 + && sscanf(&temp[1], "%d", &maxqual)==1 && maxqual>minqual && maxqual>=1 + && maxqual<=100) {} + else maxqual=minqual; + } + + if(argc>minarg) + { + for(i=minarg; i<argc; i++) + { + if(!strcasecmp(argv[i], "-tile")) + { + dotile=1; + } + if(!strcasecmp(argv[i], "-forcesse3")) + { + printf("Forcing SSE3 code\n\n"); + flags|=TJFLAG_FORCESSE3; + } + if(!strcasecmp(argv[i], "-forcesse2")) + { + printf("Forcing SSE2 code\n\n"); + flags|=TJFLAG_FORCESSE2; + } + if(!strcasecmp(argv[i], "-forcesse")) + { + printf("Forcing SSE code\n\n"); + flags|=TJFLAG_FORCESSE; + } + if(!strcasecmp(argv[i], "-forcemmx")) + { + printf("Forcing MMX code\n\n"); + flags|=TJFLAG_FORCEMMX; + } + if(!strcasecmp(argv[i], "-fastupsample")) + { + printf("Using fast upsampling code\n\n"); + flags|=TJFLAG_FASTUPSAMPLE; + } + if(!strcasecmp(argv[i], "-rgb")) pf=TJPF_RGB; + if(!strcasecmp(argv[i], "-rgbx")) pf=TJPF_RGBX; + if(!strcasecmp(argv[i], "-bgr")) pf=TJPF_BGR; + if(!strcasecmp(argv[i], "-bgrx")) pf=TJPF_BGRX; + if(!strcasecmp(argv[i], "-xbgr")) pf=TJPF_XBGR; + if(!strcasecmp(argv[i], "-xrgb")) pf=TJPF_XRGB; + if(!strcasecmp(argv[i], "-bottomup")) flags|=TJFLAG_BOTTOMUP; + if(!strcasecmp(argv[i], "-quiet")) quiet=1; + if(!strcasecmp(argv[i], "-qq")) quiet=2; + if(!strcasecmp(argv[i], "-scale") && i<argc-1) + { + int temp1=0, temp2=0, match=0; + if(sscanf(argv[++i], "%d/%d", &temp1, &temp2)==2) + { + for(j=0; j<nsf; j++) + { + if(temp1==scalingfactors[j].num && temp2==scalingfactors[j].denom) + { + sf=scalingfactors[j]; + match=1; break; + } + } + if(!match) usage(argv[0]); + } + else usage(argv[0]); + } + if(!strcasecmp(argv[i], "-benchtime") && i<argc-1) + { + double temp=atof(argv[++i]); + if(temp>0.0) benchtime=temp; + else usage(argv[0]); + } + if(!strcmp(argv[i], "-?")) usage(argv[0]); + if(!strcasecmp(argv[i], "-bmp")) ext="bmp"; + } + } + + if((sf.num!=1 || sf.denom!=1) && dotile) + { + printf("Disabling tiled compression/decompression tests, because those tests do not\n"); + printf("work when scaled decompression is enabled.\n"); + dotile=0; + } + + if(!decomponly) + { + if(loadbmp(argv[1], &srcbuf, &w, &h, bmpPF[pf], 1, + (flags&TJFLAG_BOTTOMUP)!=0)==-1) + _throwbmp("loading bitmap"); + temp=strrchr(argv[1], '.'); + if(temp!=NULL) *temp='\0'; + } + + if(quiet==1 && !decomponly) + { + printf("All performance values in Mpixels/sec\n\n"); + printf("Bitmap\tBitmap\tJPEG\tJPEG\t%s %s \tComp\tComp\tDecomp\n", + dotile? "Tile ":"Image", dotile? "Tile ":"Image"); + printf("Format\tOrder\tSubsamp\tQual\tWidth Height\tPerf \tRatio\tPerf\n\n"); + } + + if(decomponly) + { + dodecomptest(argv[1]); + printf("\n"); + goto bailout; + } + for(i=maxqual; i>=minqual; i--) + dotest(srcbuf, w, h, TJ_GRAYSCALE, i, argv[1]); + printf("\n"); + for(i=maxqual; i>=minqual; i--) + dotest(srcbuf, w, h, TJ_420, i, argv[1]); + printf("\n"); + for(i=maxqual; i>=minqual; i--) + dotest(srcbuf, w, h, TJ_422, i, argv[1]); + printf("\n"); + for(i=maxqual; i>=minqual; i--) + dotest(srcbuf, w, h, TJ_444, i, argv[1]); + printf("\n"); + + bailout: + if(srcbuf) free(srcbuf); + return retval; +} diff --git a/test/tjunittest.c b/test/tjunittest.c new file mode 100644 index 0000000..c9960eb --- /dev/null +++ b/test/tjunittest.c @@ -0,0 +1,461 @@ +/* + * Copyright (C)2009-2012 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This program tests the various code paths in the TurboJPEG C Wrapper + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "./tjutil.h" +#include "./turbojpeg.h" +#ifdef _WIN32 + #include <time.h> + #define random() rand() +#endif + + +#define _throwtj() {printf("TurboJPEG ERROR:\n%s\n", tjGetErrorStr()); \ + bailout();} +#define _tj(f) {if((f)==-1) _throwtj();} +#define _throw(m) {printf("ERROR: %s\n", m); bailout();} + +const char *subNameLong[TJ_NUMSAMP]= +{ + "4:4:4", "4:2:2", "4:2:0", "GRAY", "4:4:0" +}; +const char *subName[TJ_NUMSAMP]={"444", "422", "420", "GRAY", "440"}; + +const char *pixFormatStr[TJ_NUMPF]= +{ + "RGB", "BGR", "RGBX", "BGRX", "XBGR", "XRGB", "Grayscale", + "RGBA", "BGRA", "ABGR", "ARGB" +}; + +const int alphaOffset[TJ_NUMPF] = {-1, -1, -1, -1, -1, -1, -1, 3, 3, 0, 0}; + +const int _3byteFormats[]={TJPF_RGB, TJPF_BGR}; +const int _4byteFormats[]={TJPF_RGBX, TJPF_BGRX, TJPF_XBGR, TJPF_XRGB}; +const int _onlyGray[]={TJPF_GRAY}; +const int _onlyRGB[]={TJPF_RGB}; + +int exitStatus=0; +#define bailout() {exitStatus=-1; goto bailout;} + + +void initBuf(unsigned char *buf, int w, int h, int pf, int flags) +{ + int roffset=tjRedOffset[pf]; + int goffset=tjGreenOffset[pf]; + int boffset=tjBlueOffset[pf]; + int ps=tjPixelSize[pf]; + int index, row, col, halfway=16; + + memset(buf, 0, w*h*ps); + if(pf==TJPF_GRAY) + { + for(row=0; row<h; row++) + { + for(col=0; col<w; col++) + { + if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col; + else index=row*w+col; + if(((row/8)+(col/8))%2==0) buf[index]=(row<halfway)? 255:0; + else buf[index]=(row<halfway)? 76:226; + } + } + } + else + { + for(row=0; row<h; row++) + { + for(col=0; col<w; col++) + { + if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col; + else index=row*w+col; + if(((row/8)+(col/8))%2==0) + { + if(row<halfway) + { + buf[index*ps+roffset]=255; + buf[index*ps+goffset]=255; + buf[index*ps+boffset]=255; + } + } + else + { + buf[index*ps+roffset]=255; + if(row>=halfway) buf[index*ps+goffset]=255; + } + } + } + } +} + + +#define checkval(v, cv) { \ + if(v<cv-1 || v>cv+1) { \ + printf("\nComp. %s at %d,%d should be %d, not %d\n", \ + #v, row, col, cv, v); \ + retval=0; exitStatus=-1; goto bailout; \ + }} + +#define checkval0(v) { \ + if(v>1) { \ + printf("\nComp. %s at %d,%d should be 0, not %d\n", #v, row, col, v); \ + retval=0; exitStatus=-1; goto bailout; \ + }} + +#define checkval255(v) { \ + if(v<254) { \ + printf("\nComp. %s at %d,%d should be 255, not %d\n", #v, row, col, v); \ + retval=0; exitStatus=-1; goto bailout; \ + }} + + +int checkBuf(unsigned char *buf, int w, int h, int pf, int subsamp, + tjscalingfactor sf, int flags) +{ + int roffset=tjRedOffset[pf]; + int goffset=tjGreenOffset[pf]; + int boffset=tjBlueOffset[pf]; + int aoffset=alphaOffset[pf]; + int ps=tjPixelSize[pf]; + int index, row, col, retval=1; + int halfway=16*sf.num/sf.denom; + int blocksize=8*sf.num/sf.denom; + + for(row=0; row<h; row++) + { + for(col=0; col<w; col++) + { + unsigned char r, g, b, a; + if(flags&TJFLAG_BOTTOMUP) index=(h-row-1)*w+col; + else index=row*w+col; + r=buf[index*ps+roffset]; + g=buf[index*ps+goffset]; + b=buf[index*ps+boffset]; + a=aoffset>=0? buf[index*ps+aoffset]:0xFF; + if(((row/blocksize)+(col/blocksize))%2==0) + { + if(row<halfway) + { + checkval255(r); checkval255(g); checkval255(b); + } + else + { + checkval0(r); checkval0(g); checkval0(b); + } + } + else + { + if(subsamp==TJSAMP_GRAY) + { + if(row<halfway) + { + checkval(r, 76); checkval(g, 76); checkval(b, 76); + } + else + { + checkval(r, 226); checkval(g, 226); checkval(b, 226); + } + } + else + { + if(row<halfway) + { + checkval255(r); checkval0(g); checkval0(b); + } + else + { + checkval255(r); checkval255(g); checkval0(b); + } + } + } + checkval255(a); + } + } + + bailout: + if(retval==0) + { + printf("\n"); + for(row=0; row<h; row++) + { + for(col=0; col<w; col++) + { + printf("%.3d/%.3d/%.3d ", buf[(row*w+col)*ps+roffset], + buf[(row*w+col)*ps+goffset], buf[(row*w+col)*ps+boffset]); + } + printf("\n"); + } + } + return retval; +} + + +void writeJPEG(unsigned char *jpegBuf, unsigned long jpegSize, char *filename) +{ + FILE *file=fopen(filename, "wb"); + if(!file || fwrite(jpegBuf, jpegSize, 1, file)!=1) + { + printf("ERROR: Could not write to %s.\n%s\n", filename, strerror(errno)); + bailout(); + } + + bailout: + if(file) fclose(file); +} + + +void compTest(tjhandle handle, unsigned char **dstBuf, + unsigned long *dstSize, int w, int h, int pf, char *basename, + int subsamp, int jpegQual, int flags) +{ + char tempStr[1024]; unsigned char *srcBuf=NULL; + double t; + + printf("%s %s -> %s Q%d ... ", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down ", subNameLong[subsamp], + jpegQual); + + if((srcBuf=(unsigned char *)malloc(w*h*tjPixelSize[pf]))==NULL) + _throw("Memory allocation failure"); + initBuf(srcBuf, w, h, pf, flags); + if(*dstBuf && *dstSize>0) memset(*dstBuf, 0, *dstSize); + + t=gettime(); + *dstSize=tjBufSize(w, h, subsamp); + _tj(tjCompress2(handle, srcBuf, w, 0, h, pf, dstBuf, dstSize, subsamp, + jpegQual, flags)); + t=gettime()-t; + + snprintf(tempStr, 1024, "%s_enc_%s_%s_%s_Q%d.jpg", basename, + pixFormatStr[pf], (flags&TJFLAG_BOTTOMUP)? "BU":"TD", subName[subsamp], + jpegQual); + writeJPEG(*dstBuf, *dstSize, tempStr); + printf("Done."); + printf(" %f ms\n Result in %s\n", t*1000., tempStr); + + bailout: + if(srcBuf) free(srcBuf); +} + + +void _decompTest(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, int w, int h, int pf, char *basename, int subsamp, + int flags, tjscalingfactor sf) +{ + unsigned char *dstBuf=NULL; + int _hdrw=0, _hdrh=0, _hdrsubsamp=-1; double t; + int scaledWidth=TJSCALED(w, sf); + int scaledHeight=TJSCALED(h, sf); + unsigned long dstSize=0; + + printf("JPEG -> %s %s ", pixFormatStr[pf], + (flags&TJFLAG_BOTTOMUP)? "Bottom-Up":"Top-Down "); + if(sf.num!=1 || sf.denom!=1) + printf("%d/%d ... ", sf.num, sf.denom); + else printf("... "); + + _tj(tjDecompressHeader2(handle, jpegBuf, jpegSize, &_hdrw, &_hdrh, + &_hdrsubsamp)); + if(_hdrw!=w || _hdrh!=h || _hdrsubsamp!=subsamp) + _throw("Incorrect JPEG header"); + + dstSize=scaledWidth*scaledHeight*tjPixelSize[pf]; + if((dstBuf=(unsigned char *)malloc(dstSize))==NULL) + _throw("Memory allocation failure"); + memset(dstBuf, 0, dstSize); + + t=gettime(); + _tj(tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, scaledWidth, 0, + scaledHeight, pf, flags)); + t=gettime()-t; + + if(checkBuf(dstBuf, scaledWidth, scaledHeight, pf, subsamp, sf, flags)) + printf("Passed."); + else printf("FAILED!"); + printf(" %f ms\n", t*1000.); + + bailout: + if(dstBuf) free(dstBuf); +} + + +void decompTest(tjhandle handle, unsigned char *jpegBuf, + unsigned long jpegSize, int w, int h, int pf, char *basename, int subsamp, + int flags) +{ + int i, n=0; + tjscalingfactor *sf=tjGetScalingFactors(&n), sf1={1, 1}; + if(!sf || !n) _throwtj(); + + if((subsamp==TJSAMP_444 || subsamp==TJSAMP_GRAY)) + { + for(i=0; i<n; i++) + _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, + flags, sf[i]); + } + else + _decompTest(handle, jpegBuf, jpegSize, w, h, pf, basename, subsamp, flags, + sf1); + + bailout: + printf("\n"); +} + + +void doTest(int w, int h, const int *formats, int nformats, int subsamp, + char *basename) +{ + tjhandle chandle=NULL, dhandle=NULL; + unsigned char *dstBuf=NULL; + unsigned long size=0; int pfi, pf, i; + + size=tjBufSize(w, h, subsamp); + if((dstBuf=(unsigned char *)malloc(size))==NULL) + _throw("Memory allocation failure."); + + if((chandle=tjInitCompress())==NULL || (dhandle=tjInitDecompress())==NULL) + _throwtj(); + + for(pfi=0; pfi<nformats; pfi++) + { + for(i=0; i<2; i++) + { + int flags=0; + if(subsamp==TJSAMP_422 || subsamp==TJSAMP_420 || subsamp==TJSAMP_440) + flags|=TJFLAG_FASTUPSAMPLE; + if(i==1) flags|=TJFLAG_BOTTOMUP; + pf=formats[pfi]; + compTest(chandle, &dstBuf, &size, w, h, pf, basename, subsamp, 100, + flags); + decompTest(dhandle, dstBuf, size, w, h, pf, basename, subsamp, + flags); + if(pf>=TJPF_RGBX && pf<=TJPF_XRGB) + decompTest(dhandle, dstBuf, size, w, h, pf+(TJPF_RGBA-TJPF_RGBX), + basename, subsamp, flags); + } + } + + bailout: + if(chandle) tjDestroy(chandle); + if(dhandle) tjDestroy(dhandle); + + if(dstBuf) free(dstBuf); +} + + +void bufSizeTest(void) +{ + int w, h, i, subsamp; + unsigned char *srcBuf=NULL, *jpegBuf=NULL; + tjhandle handle=NULL; + unsigned long jpegSize=0; + + if((handle=tjInitCompress())==NULL) _throwtj(); + + printf("Buffer size regression test\n"); + for(subsamp=0; subsamp<TJ_NUMSAMP; subsamp++) + { + for(w=1; w<48; w++) + { + int maxh=(w==1)? 2048:48; + for(h=1; h<maxh; h++) + { + if(h%100==0) printf("%.4d x %.4d\b\b\b\b\b\b\b\b\b\b\b", w, h); + if((srcBuf=(unsigned char *)malloc(w*h*4))==NULL) + _throw("Memory allocation failure"); + if((jpegBuf=(unsigned char *)malloc(tjBufSize(w, h, subsamp))) + ==NULL) + _throw("Memory allocation failure"); + jpegSize=tjBufSize(w, h, subsamp); + + for(i=0; i<w*h*4; i++) + { + if(random()<RAND_MAX/2) srcBuf[i]=0; + else srcBuf[i]=255; + } + + _tj(tjCompress2(handle, srcBuf, w, 0, h, TJPF_BGRX, &jpegBuf, + &jpegSize, subsamp, 100, 0)); + free(srcBuf); srcBuf=NULL; + free(jpegBuf); jpegBuf=NULL; + + if((srcBuf=(unsigned char *)malloc(h*w*4))==NULL) + _throw("Memory allocation failure"); + if((jpegBuf=(unsigned char *)malloc(tjBufSize(h, w, subsamp))) + ==NULL) + _throw("Memory allocation failure"); + jpegSize=tjBufSize(h, w, subsamp); + + for(i=0; i<h*w*4; i++) + { + if(random()<RAND_MAX/2) srcBuf[i]=0; + else srcBuf[i]=255; + } + + _tj(tjCompress2(handle, srcBuf, h, 0, w, TJPF_BGRX, &jpegBuf, + &jpegSize, subsamp, 100, 0)); + free(srcBuf); srcBuf=NULL; + free(jpegBuf); jpegBuf=NULL; + } + } + } + printf("Done. \n"); + + bailout: + if(srcBuf) free(srcBuf); + if(jpegBuf) free(jpegBuf); + if(handle) tjDestroy(handle); +} + + +int main(int argc, char *argv[]) +{ + #ifdef _WIN32 + srand((unsigned int)time(NULL)); + #endif + doTest(35, 39, _3byteFormats, 2, TJSAMP_444, "test"); + doTest(39, 41, _4byteFormats, 4, TJSAMP_444, "test"); + doTest(41, 35, _3byteFormats, 2, TJSAMP_422, "test"); + doTest(35, 39, _4byteFormats, 4, TJSAMP_422, "test"); + doTest(39, 41, _3byteFormats, 2, TJSAMP_420, "test"); + doTest(41, 35, _4byteFormats, 4, TJSAMP_420, "test"); + doTest(35, 39, _3byteFormats, 2, TJSAMP_440, "test"); + doTest(39, 41, _4byteFormats, 4, TJSAMP_440, "test"); + doTest(35, 39, _onlyGray, 1, TJSAMP_GRAY, "test"); + doTest(39, 41, _3byteFormats, 2, TJSAMP_GRAY, "test"); + doTest(41, 35, _4byteFormats, 4, TJSAMP_GRAY, "test"); + bufSizeTest(); + + return exitStatus; +} diff --git a/test/tjutil.c b/test/tjutil.c new file mode 100644 index 0000000..6618d15 --- /dev/null +++ b/test/tjutil.c @@ -0,0 +1,66 @@ +/* + * Copyright (C)2011 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + +#include <windows.h> + +static double getfreq(void) +{ + LARGE_INTEGER freq; + if(!QueryPerformanceFrequency(&freq)) return 0.0; + return (double)freq.QuadPart; +} + +static double f=-1.0; + +double gettime(void) +{ + LARGE_INTEGER t; + if(f<0.0) f=getfreq(); + if(f==0.0) return (double)GetTickCount()/1000.; + else + { + QueryPerformanceCounter(&t); + return (double)t.QuadPart/f; + } +} + +#else + +#include <stdlib.h> +#include <sys/time.h> + +double gettime(void) +{ + struct timeval tv; + if(gettimeofday(&tv, NULL)<0) return 0.0; + else return (double)tv.tv_sec+((double)tv.tv_usec/1000000.); +} + +#endif diff --git a/test/tjutil.h b/test/tjutil.h new file mode 100644 index 0000000..bdad348 --- /dev/null +++ b/test/tjutil.h @@ -0,0 +1,47 @@ +/* + * Copyright (C)2011 D. R. Commander. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the libjpeg-turbo Project nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS", + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef _WIN32 + #ifndef __MINGW32__ + #include <stdio.h> + #define snprintf(str, n, format, ...) \ + _snprintf_s(str, n, _TRUNCATE, format, __VA_ARGS__) + #endif + #define strcasecmp stricmp + #define strncasecmp strnicmp +#endif + +#ifndef min + #define min(a,b) ((a)<(b)?(a):(b)) +#endif + +#ifndef max + #define max(a,b) ((a)>(b)?(a):(b)) +#endif + +extern double gettime(void); |