Commit 413ca0df authored by Christian Beier's avatar Christian Beier

Merge branch 'turbovnc'

Conflicts, resolved manually:
	AUTHORS
parents 25245736 1df6bffd
......@@ -35,7 +35,7 @@ Noriaki Yamazaki, Ben Klopfenstein, Vic Lee, Christian Beier,
Alexander Dorokhine, Corentin Chary, Wouter Van Meir, George Kiagiadakis,
Joel Martin, Gernot Tenchio, William Roberts, Cristian Rodríguez,
George Fleury, Kan-Ru Chen, Steve Guo, Luca Stauble, Peter Watkins,
Kyle J. McKay, Mateus Cesar Groess and Philip Van Hoof.
Kyle J. McKay, Mateus Cesar Groess, Philip Van Hoof and D. R. Commander.
Probably I forgot quite a few people sending a patch here and there, which
really made a difference. Without those, some obscure bugs still would
......
......@@ -5,6 +5,9 @@ include(CheckFunctionExists)
include(CheckIncludeFile)
include(CheckTypeSize)
include(TestBigEndian)
include(CheckCSourceCompiles)
include(CheckCXXSourceCompiles)
include(CheckCSourceRuns)
set(PACKAGE_NAME "LibVNCServer")
set(FULL_PACKAGE_NAME "LibVNCServer")
......@@ -30,6 +33,40 @@ find_package(X11)
find_package(OpenSSL)
find_library(LIBGCRYPT_LIBRARIES gcrypt)
# Check whether the version of libjpeg we found was libjpeg-turbo and print a
# warning if not.
set(CMAKE_REQUIRED_LIBRARIES ${JPEG_LIBRARIES})
set(CMAKE_REQUIRED_FLAGS -I${JPEG_INCLUDE_DIR})
set(JPEG_TEST_SOURCE "\n
#include <stdio.h>\n
#include <jpeglib.h>\n
int main(void) {\n
struct jpeg_compress_struct cinfo;\n
struct jpeg_error_mgr jerr;\n
cinfo.err=jpeg_std_error(&jerr);\n
jpeg_create_compress(&cinfo);\n
cinfo.input_components = 3;\n
jpeg_set_defaults(&cinfo);\n
cinfo.in_color_space = JCS_EXT_RGB;\n
jpeg_default_colorspace(&cinfo);\n
return 0;\n
}")
if(CMAKE_CROSSCOMPILING)
check_c_source_compiles("${JPEG_TEST_SOURCE}" FOUND_LIBJPEG_TURBO)
else()
check_c_source_runs("${JPEG_TEST_SOURCE}" FOUND_LIBJPEG_TURBO)
endif()
set(CMAKE_REQUIRED_LIBRARIES)
set(CMAKE_REQUIRED_FLAGS)
set(CMAKE_REQUIRED_DEFINITIONS)
if(NOT FOUND_LIBJPEG_TURBO)
message(WARNING "*** 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/ ***")
endif()
set(CMAKE_REQUIRED_LIBRARIES resolv)
check_function_exists(__b64_ntop HAVE_B64)
......@@ -166,13 +203,13 @@ endif(ZLIB_FOUND)
if(JPEG_FOUND)
add_definitions(-DLIBVNCSERVER_HAVE_LIBJPEG)
include_directories(${JPEG_INCLUDE_DIR})
set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c)
set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c ${COMMON_DIR}/turbojpeg.c)
endif(JPEG_FOUND)
if(PNG_FOUND)
add_definitions(-DLIBVNCSERVER_HAVE_LIBPNG)
include_directories(${PNG_INCLUDE_DIR})
set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c)
set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c ${COMMON_DIR}/turbojpeg.c)
endif(PNG_FOUND)
set(LIBVNCSERVER_SOURCES
......
/*
* 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.
*/
/* TurboJPEG/OSS: this implements the TurboJPEG API using libjpeg-turbo */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef JCS_EXTENSIONS
#define JPEG_INTERNAL_OPTIONS
#endif
#include <jpeglib.h>
#include <jerror.h>
#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 (based on example in example.c) */
static char errStr[JMSG_LENGTH_MAX]="No error";
struct my_error_mgr
{
struct jpeg_error_mgr pub;
jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr *my_error_ptr;
static void my_error_exit(j_common_ptr cinfo)
{
my_error_ptr myerr=(my_error_ptr)cinfo->err;
(*cinfo->err->output_message)(cinfo);
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, errStr);
}
/* Global structures, macros, etc. */
enum {COMPRESS=1, DECOMPRESS=2};
typedef struct _tjinstance
{
struct jpeg_compress_struct cinfo;
struct jpeg_decompress_struct dinfo;
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;
}
#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; \
} \
}
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
/* 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 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)
{
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);
}
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");
// 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;
}
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height)
{
unsigned long retval=0;
if(width<1 || height<1)
_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=PAD(width, 16) * PAD(height, 16) * 6 + 2048;
bailout:
return retval;
}
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;
#ifndef JCS_EXTENSIONS
unsigned char *rgbBuf=NULL;
#endif
getinstance(handle)
if((this->init&COMPRESS)==0)
_throw("tjCompress2(): Instance has not been initialized for compression");
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;
}
if(pitch==0) pitch=width*tjPixelSize[pixelFormat];
#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");
if(setCompDefaults(cinfo, pixelFormat, jpegSubsamp, jpegQual)==-1)
return -1;
this->jdst.next_output_byte=*jpegBuf;
this->jdst.free_in_buffer=tjBufSize(width, height, jpegSubsamp);
jpeg_start_compress(cinfo, TRUE);
if((row_pointer=(JSAMPROW *)malloc(sizeof(JSAMPROW)*height))==NULL)
_throw("tjCompress2(): Memory allocation failure");
for(i=0; i<height; i++)
{
if(flags&TJFLAG_BOTTOMUP) row_pointer[i]=&srcBuf[(height-i-1)*pitch];
else row_pointer[i]=&srcBuf[i*pitch];
}
while(cinfo->next_scanline<cinfo->image_height)
{
jpeg_write_scanlines(cinfo, &row_pointer[cinfo->next_scanline],
cinfo->image_height-cinfo->next_scanline);
}
jpeg_finish_compress(cinfo);
*jpegSize=tjBufSize(width, height, jpegSubsamp)
-(unsigned long)(this->jdst.free_in_buffer);
bailout:
if(cinfo->global_state>CSTATE_START) jpeg_abort_compress(cinfo);
#ifndef JCS_EXTENSIONS
if(rgbBuf) 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;
}
/* Decompressor */
static boolean fill_input_buffer(j_decompress_ptr dinfo)
{
ERREXIT(dinfo, JERR_BUFFER_SIZE);
return TRUE;
}
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 src_noop(j_decompress_ptr dinfo)
{
}
static tjhandle _tjInitDecompress(tjinstance *this)
{
/* 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(&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;
}
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 handle,
unsigned char *jpegBuf, unsigned long jpegSize, int *width, int *height,
int *jpegSubsamp)
{
int retval=0;
getinstance(handle);
if((this->init&DECOMPRESS)==0)
_throw("tjDecompressHeader2(): Instance has not been initialized for decompression");
if(jpegBuf==NULL || jpegSize<=0 || width==NULL || height==NULL
|| jpegSubsamp==NULL)
_throw("tjDecompressHeader2(): Invalid argument");
if(setjmp(this->jerr.setjmp_buffer))
{
/* If we get here, the JPEG code has signaled an error. */
return -1;
}
this->jsrc.bytes_in_buffer=jpegSize;
this->jsrc.next_input_byte=jpegBuf;
jpeg_read_header(dinfo, TRUE);
*width=dinfo->image_width;
*height=dinfo->image_height;
*jpegSubsamp=getSubsamp(dinfo);
jpeg_abort_decompress(dinfo);
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 tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors)
{
if(numscalingfactors==NULL)
{
snprintf(errStr, JMSG_LENGTH_MAX,
"tjGetScalingFactors(): Invalid argument");
return NULL;
}
*numscalingfactors=NUMSF;
return (tjscalingfactor *)sf;
}
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
getinstance(handle);
if((this->init&DECOMPRESS)==0)
_throw("tjDecompress2(): Instance has not been initialized for decompression");
if(jpegBuf==NULL || jpegSize<=0 || dstBuf==NULL || width<0 || pitch<0
|| height<0 || pixelFormat<0 || pixelFormat>=TJ_NUMPF)
_throw("tjDecompress2(): Invalid argument");
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(this->jerr.setjmp_buffer))
{
/* If we get here, the JPEG code has signaled an error. */
retval=-1;
goto bailout;
}
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;
}
if(flags&TJFLAG_FASTUPSAMPLE) dinfo->do_fancy_upsampling=FALSE;
jpegwidth=dinfo->image_width; jpegheight=dinfo->image_height;
if(width==0) width=jpegwidth;
if(height==0) height=jpegheight;
for(i=0; i<NUMSF; i++)
{
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;
}
#endif
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(dinfo, &row_pointer[dinfo->output_scanline],
dinfo->output_height-dinfo->output_scanline);
}
jpeg_finish_decompress(dinfo);
#ifndef JCS_EXTENSIONS
fromRGB(rgbBuf, _dstBuf, width, _pitch, height, pixelFormat);
#endif
bailout:
if(dinfo->global_state>DSTATE_START) jpeg_abort_decompress(dinfo);
#ifndef JCS_EXTENSIONS
if(rgbBuf) free(rgbBuf);
#endif
if(row_pointer) free(row_pointer);
return retval;
}
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)
{
return tjDecompress2(handle, jpegBuf, jpegSize, dstBuf, width, pitch,
height, getPixelFormat(pixelSize, flags), flags);
}
/*
* 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.
*/
#ifndef __TURBOJPEG_H__
#define __TURBOJPEG_H__
#if defined(_WIN32) && defined(DLLDEFINE)
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
#define DLLCALL
/**
* @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;
/**
* 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
/**
* 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);
/**
* 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 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);
/**
* Create a TurboJPEG decompressor instance.
*
* @return a handle to the newly-created instance, or NULL if an error
* occurred (see #tjGetErrorStr().)
*/
DLLEXPORT tjhandle DLLCALL tjInitDecompress(void);
/**
* 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);
/**
* 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 tjscalingfactor* DLLCALL tjGetScalingFactors(int *numscalingfactors);
/**
* 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);
/**
* 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);
/**
* 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);
/* 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
DLLEXPORT unsigned long DLLCALL TJBUFSIZE(int width, int height);
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);
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);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif
......@@ -528,7 +528,11 @@ AC_ARG_WITH(jpeg,
# -without-jpeg with_jpeg="no"
# -with-jpeg=/foo/dir with_jpeg="/foo/dir"
HAVE_LIBJPEG_TURBO="false"
if test "x$with_jpeg" != "xno"; then
AC_ARG_VAR(JPEG_LDFLAGS,
[Linker flags to use when linking with libjpeg, e.g. -L/foo/dir/lib -Wl,-static -ljpeg -Wl,-shared. This overrides the linker flags set by --with-jpeg.])
if test ! -z "$with_jpeg" -a "x$with_jpeg" != "xyes"; then
# add user supplied directory to flags:
saved_CPPFLAGS="$CPPFLAGS"
......@@ -544,9 +548,19 @@ if test "x$with_jpeg" != "xno"; then
LDFLAGS="$LDFLAGS -R$with_jpeg/lib"
fi
fi
if test "x$JPEG_LDFLAGS" != "x"; then
LDFLAGS="$saved_LDFLAGS"
LIBS="$LIBS $JPEG_LDFLAGS"
else
LIBS="-ljpeg"
fi
AC_CHECK_HEADER(jpeglib.h, HAVE_JPEGLIB_H="true")
AC_MSG_CHECKING(for jpeg_CreateCompress in libjpeg)
if test "x$HAVE_JPEGLIB_H" = "xtrue"; then
AC_CHECK_LIB(jpeg, jpeg_CreateCompress, , HAVE_JPEGLIB_H="")
AC_LINK_IFELSE([AC_LANG_CALL([], [jpeg_CreateCompress])],
[AC_MSG_RESULT(yes);
AC_DEFINE(HAVE_LIBJPEG, 1, libjpeg support enabled)],
[AC_MSG_RESULT(no); HAVE_JPEGLIB_H=""])
fi
if test ! -z "$with_jpeg" -a "x$with_jpeg" != "xyes"; then
if test "x$HAVE_JPEGLIB_H" != "xtrue"; then
......@@ -563,13 +577,52 @@ if test "x$with_jpeg" != "xno"; then
This may lead to reduced performance, especially over slow links.
If libjpeg is in a non-standard location use --with-jpeg=DIR to
indicate the header file is in DIR/include/jpeglib.h and the library
in DIR/lib/libjpeg.a. A copy of libjpeg may be obtained from:
ftp://ftp.uu.net/graphics/jpeg/
in DIR/lib/libjpeg.a. You can also set the JPEG_LDFLAGS variable to
specify more detailed linker flags. A copy of libjpeg-turbo may be
obtained from: https://sourceforge.net/projects/libjpeg-turbo/files/
A copy of libjpeg may be obtained from: http://ijg.org/files/
==========================================================================
])
sleep 5
fi
fi
if test "x$HAVE_JPEGLIB_H" = "xtrue"; then
AC_MSG_CHECKING(whether JPEG library is libjpeg-turbo)
m4_define([LJT_TEST],
[AC_LANG_PROGRAM([#include <stdio.h>
#include <jpeglib.h>],
[struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err=jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
cinfo.input_components = 3;
jpeg_set_defaults(&cinfo);
cinfo.in_color_space = JCS_EXT_RGB;
jpeg_default_colorspace(&cinfo);
return 0;])]
)
if test "x$cross_compiling" != "xyes"; then
AC_RUN_IFELSE([LJT_TEST],
[HAVE_LIBJPEG_TURBO="true"; AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)])
else
AC_LINK_IFELSE([LJT_TEST],
[HAVE_LIBJPEG_TURBO="true"; AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)])
fi
fi
if test "x$HAVE_LIBJPEG_TURBO" != "xtrue"; then
AC_MSG_WARN([
==========================================================================
*** 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
fi
AC_ARG_WITH(png,
......
......@@ -46,7 +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
TIGHTSRCS = tight.c
TIGHTSRCS = tight.c ../common/turbojpeg.c
endif
endif
......
......@@ -3,6 +3,7 @@
*/
/*
* 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,6 +87,21 @@ static int compat_mkdir(const char *path, int mode)
#define mkdir compat_mkdir
#endif
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
/*
* Map of quality levels to provide compatibility with TightVNC/TigerVNC
* clients. This emulates the behavior of the TigerVNC Server.
*/
static const int tight2turbo_qual[10] = {
15, 29, 41, 42, 62, 77, 79, 86, 92, 100
};
static const int tight2turbo_subsamp[10] = {
1, 1, 1, 2, 2, 2, 0, 0, 0, 0
};
#endif
static void rfbProcessClientProtocolVersion(rfbClientPtr cl);
static void rfbProcessClientNormalMessage(rfbClientPtr cl);
static void rfbProcessClientInitMessage(rfbClientPtr cl);
......@@ -383,6 +399,7 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION;
#endif
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
cl->turboSubsampLevel = TURBO_DEFAULT_SUBSAMP;
{
int i;
for (i = 0; i < 4; i++)
......@@ -1963,6 +1980,16 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
cl->enableSupportedMessages = FALSE;
cl->enableSupportedEncodings = FALSE;
cl->enableServerIdentity = FALSE;
#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG)
cl->tightQualityLevel = -1;
#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG)
cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION;
#endif
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
cl->turboSubsampLevel = TURBO_DEFAULT_SUBSAMP;
cl->turboQualityLevel = -1;
#endif
#endif
for (i = 0; i < msg.se.nEncodings; i++) {
......@@ -2097,6 +2124,22 @@ rfbProcessClientNormalMessage(rfbClientPtr cl)
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
{
......
......@@ -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 (cl->tightCompressLevel == 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) {
......@@ -948,53 +1022,6 @@ SendFullColorRect(rfbClientPtr cl,
Z_DEFAULT_STRATEGY);
}
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,
......@@ -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
......@@ -1870,43 +1740,6 @@ PrepareRowForImg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) {
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
......@@ -681,6 +681,11 @@ typedef struct _rfbClientRec {
int afterEncBufLen;
#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG)
uint32_t tightEncoding; /* rfbEncodingTight or rfbEncodingTightPng */
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
/* TurboVNC Encoding support (extends TightVNC) */
int turboSubsampLevel;
int turboQualityLevel; // 1-100 scale
#endif
#endif
#ifdef LIBVNCSERVER_WITH_WEBSOCKETS
......@@ -880,6 +885,10 @@ extern rfbBool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w,
#define TIGHT_DEFAULT_COMPRESSION 6
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
#define TURBO_DEFAULT_SUBSAMP 0
#endif
extern rfbBool rfbTightDisableGradient;
extern int rfbNumCodedRectsTight(rfbClientPtr cl, int x,int y,int w,int h);
......
......@@ -13,7 +13,9 @@
*/
/*
* Copyright (C) 2009-2010 D. R. Commander. All Rights Reserved.
* Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin
* Copyright (C) 2004-2008 Sun Microsystems, Inc. All Rights Reserved.
* Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
......@@ -457,6 +459,8 @@ typedef struct {
/*
* Special encoding numbers:
* 0xFFFFFD00 .. 0xFFFFFD05 -- subsampling level
* 0xFFFFFE00 .. 0xFFFFFE64 -- fine-grained quality level (0-100 scale)
* 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels;
* 0xFFFFFF10 .. 0xFFFFFF1F -- mouse cursor shape data;
* 0xFFFFFF20 .. 0xFFFFFF2F -- various protocol extensions;
......@@ -465,6 +469,15 @@ typedef struct {
* 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels.
*/
#define rfbEncodingFineQualityLevel0 0xFFFFFE00
#define rfbEncodingFineQualityLevel100 0xFFFFFE64
#define rfbEncodingSubsamp1X 0xFFFFFD00
#define rfbEncodingSubsamp4X 0xFFFFFD01
#define rfbEncodingSubsamp2X 0xFFFFFD02
#define rfbEncodingSubsampGray 0xFFFFFD03
#define rfbEncodingSubsamp8X 0xFFFFFD04
#define rfbEncodingSubsamp16X 0xFFFFFD05
#define rfbEncodingCompressLevel0 0xFFFFFF00
#define rfbEncodingCompressLevel1 0xFFFFFF01
#define rfbEncodingCompressLevel2 0xFFFFFF02
......@@ -719,12 +732,20 @@ 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",
* if 1001 (0x0A), then the compression type is "png",
* if 0xxx, then the compression type is "basic",
* (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.
*
* If the compression type is "basic", then bits 6..4 of the
* compression control byte (those xxx in 0xxx) specify the following:
* If the compression type is "basic" and Zlib compression was used, then bits
* 6..4 of the compression control byte (those xxx in 0xxx) specify the
* following:
*
* bits 5-4: decimal representation is the index of a particular zlib
* stream which should be used for decompressing the data;
......@@ -836,6 +857,7 @@ typedef struct {
#define rfbTightExplicitFilter 0x04
#define rfbTightFill 0x08
#define rfbTightJpeg 0x09
#define rfbTightNoZlib 0x0A
#define rfbTightPng 0x0A
#define rfbTightMaxSubencoding 0x0A
......
INCLUDES = -I$(top_srcdir)
# TurboJPEG wrapper tests
noinst_PROGRAMS=tjunittest tjbench
tjunittest_SOURCES=tjunittest.c ../common/turbojpeg.c ../common/turbojpeg.h \
tjutil.c tjutil.h
tjbench_SOURCES=tjbench.c ../common/turbojpeg.c ../common/turbojpeg.h \
tjutil.c tjutil.h bmp.c bmp.h
tjbench_LDADD=$(LDADD) -lm
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/common
LDADD = ../libvncserver/libvncserver.la ../libvncclient/libvncclient.la @WSOCKLIB@
if HAVE_LIBPTHREAD
......
/* 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;
}
/* 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
/*
* 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;
}
/*
* 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;
}
/*
* 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
/*
* 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);
......@@ -36,7 +36,7 @@ x11vnc_SOURCES = 8to24.c appshare.c avahi.c cleanup.c connections.c cursor.c gui
if HAVE_SYSTEM_LIBVNCSERVER
INCLUDES_LIBVNCSERVER = @SYSTEM_LIBVNCSERVER_CFLAGS@
else
INCLUDES_LIBVNCSERVER =
INCLUDES_LIBVNCSERVER = -I${top_srcdir}
endif
INCLUDES = $(INCLUDES_LIBVNCSERVER) @X_CFLAGS@ @AVAHI_CFLAGS@
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment