From 0353465019fc40d80101e9a819211b4e88c6b33c Mon Sep 17 00:00:00 2001 From: Volodymyr Samokhatko Date: Fri, 17 Mar 2023 10:11:34 +0100 Subject: [PATCH 1/3] CMake: check for sched.h --- CMakeLists.txt | 1 + include/rfb/rfbconfig.h.cmakein | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e391f422..5b5a6959b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,6 +189,7 @@ check_include_file("sys/stat.h" LIBVNCSERVER_HAVE_SYS_STAT_H) check_include_file("sys/time.h" LIBVNCSERVER_HAVE_SYS_TIME_H) check_include_file("sys/types.h" LIBVNCSERVER_HAVE_SYS_TYPES_H) check_include_file("sys/wait.h" LIBVNCSERVER_HAVE_SYS_WAIT_H) +check_include_file("sched.h" LIBVNCSERVER_HAVE_SCHED_H) check_include_file("unistd.h" LIBVNCSERVER_HAVE_UNISTD_H) check_include_file("sys/resource.h" LIBVNCSERVER_HAVE_SYS_RESOURCE_H) diff --git a/include/rfb/rfbconfig.h.cmakein b/include/rfb/rfbconfig.h.cmakein index d50c3c965..d6c97f371 100644 --- a/include/rfb/rfbconfig.h.cmakein +++ b/include/rfb/rfbconfig.h.cmakein @@ -108,6 +108,9 @@ /* Define to 1 if you have */ #cmakedefine LIBVNCSERVER_HAVE_SYS_RESOURCE_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine LIBVNCSERVER_HAVE_SCHED_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine LIBVNCSERVER_HAVE_UNISTD_H 1 From e4f3e72dc9ae2c35c88d566fd1f1292193d583d0 Mon Sep 17 00:00:00 2001 From: Volodymyr Samokhatko Date: Fri, 17 Mar 2023 11:15:12 +0100 Subject: [PATCH 2/3] CMake: check for sys/sysctl.h --- CMakeLists.txt | 1 + include/rfb/rfbconfig.h.cmakein | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b5a6959b..5eeac7d7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,6 +186,7 @@ check_include_file("netinet/in.h" LIBVNCSERVER_HAVE_NETINET_IN_H) check_include_file("sys/endian.h" LIBVNCSERVER_HAVE_SYS_ENDIAN_H) check_include_file("sys/socket.h" LIBVNCSERVER_HAVE_SYS_SOCKET_H) check_include_file("sys/stat.h" LIBVNCSERVER_HAVE_SYS_STAT_H) +check_include_file("sys/sysctl.h" LIBVNCSERVER_HAVE_SYS_SYSCTL_H) check_include_file("sys/time.h" LIBVNCSERVER_HAVE_SYS_TIME_H) check_include_file("sys/types.h" LIBVNCSERVER_HAVE_SYS_TYPES_H) check_include_file("sys/wait.h" LIBVNCSERVER_HAVE_SYS_WAIT_H) diff --git a/include/rfb/rfbconfig.h.cmakein b/include/rfb/rfbconfig.h.cmakein index d6c97f371..9efc943af 100644 --- a/include/rfb/rfbconfig.h.cmakein +++ b/include/rfb/rfbconfig.h.cmakein @@ -93,6 +93,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine LIBVNCSERVER_HAVE_SYS_STAT_H 1 +/* Define to 1 if you have the header file. */ +#cmakedefine LIBVNCSERVER_HAVE_SYS_SYSCTL_H 1 + /* Define to 1 if you have the header file. */ #cmakedefine LIBVNCSERVER_HAVE_SYS_TIME_H 1 From 1390b337ba3f2c36ed1a8a9ec2d88cd6de1ca425 Mon Sep 17 00:00:00 2001 From: Volodymyr Samokhatko Date: Thu, 9 Mar 2023 10:01:13 +0100 Subject: [PATCH 3/3] tight: multithreaded --- include/rfb/rfb.h | 16 + src/libvncserver/cargs.c | 21 + src/libvncserver/main.c | 84 + src/libvncserver/rfbserver.c | 9 + src/libvncserver/tight.c | 2824 ++++++++++++++++++---------------- 5 files changed, 1644 insertions(+), 1310 deletions(-) diff --git a/include/rfb/rfb.h b/include/rfb/rfb.h index 79a446f1b..df2312594 100644 --- a/include/rfb/rfb.h +++ b/include/rfb/rfb.h @@ -84,6 +84,10 @@ typedef UINT32 in_addr_t; #endif #endif +/* Maximum number of threads to use for multithreaded encoding, regardless of + the CPU count */ +#define MAX_ENCODING_THREADS 8 + struct _rfbClientRec; struct _rfbScreenInfo; struct rfbCursor; @@ -372,6 +376,8 @@ typedef struct _rfbScreenInfo #ifdef LIBVNCSERVER_HAVE_LIBZ rfbSetXCutTextUTF8ProcPtr setXCutTextUTF8; #endif + rfbBool rfbMT; + int rfbNumThreads; } rfbScreenInfo, *rfbScreenInfoPtr; @@ -705,6 +711,16 @@ typedef struct _rfbClientRec { rfbBool tightUsePixelFormat24; void *tightTJ; int tightPngDstDataLen; + + /* Multithreaded tight encoding. */ + + rfbBool threadInit; +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + pthread_t thnd[MAX_ENCODING_THREADS]; +#elif defined(LIBVNCSERVER_HAVE_WIN32THREADS) + uintptr_t thnd[MAX_ENCODING_THREADS]; +#endif + #endif #endif } rfbClientRec, *rfbClientPtr; diff --git a/src/libvncserver/cargs.c b/src/libvncserver/cargs.c index 277438e1c..e8c9af75a 100644 --- a/src/libvncserver/cargs.c +++ b/src/libvncserver/cargs.c @@ -60,6 +60,13 @@ rfbUsage(void) fprintf(stderr, "-listenv6 ipv6addr listen for IPv6 connections only on network interface with\n"); fprintf(stderr, " addr ipv6addr. '-listen localhost' and hostname work too.\n"); #endif +#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS) + fprintf(stderr, "-nomt disable multithreaded Tight encoding\n"); + fprintf(stderr, "-nthreads N specify number of threads (1 <= N <= %d) to use with\n", + MAX_ENCODING_THREADS); + fprintf(stderr, " multithreaded Tight encoding [default: 1 per CPU core,\n"); + fprintf(stderr, " max. %d]\n", MAX_ENCODING_THREADS); +#endif for(extension=rfbGetExtensionIterator();extension;extension=extension->next) if(extension->usage) @@ -202,6 +209,20 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) } rfbScreen->listen6Interface = argv[++i]; #endif +#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS) + } else if (strcmp(argv[i], "-nomt") == 0) { + rfbScreen->rfbMT = FALSE; + } else if (strcmp(argv[i], "-nthreads") == 0) { + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->rfbNumThreads = atoi(argv[++i]); + if (rfbScreen->rfbNumThreads < 1 || rfbScreen->rfbNumThreads > MAX_ENCODING_THREADS) { + rfbUsage(); + return FALSE; + } +#endif #ifdef LIBVNCSERVER_WITH_WEBSOCKETS } else if (strcmp(argv[i], "-sslkeyfile") == 0) { /* -sslkeyfile sslkeyfile */ if (i + 1 >= *argc) { diff --git a/src/libvncserver/main.c b/src/libvncserver/main.c index 32519f14a..531905c4a 100644 --- a/src/libvncserver/main.c +++ b/src/libvncserver/main.c @@ -10,6 +10,13 @@ * see GPL (latest version) for full details */ +#include + +#if LIBVNCSERVER_HAVE_SCHED_H +#define _GNU_SOURCE +#include +#endif + #ifdef __STRICT_ANSI__ #define _BSD_SOURCE #endif @@ -25,6 +32,10 @@ #define true -1 #endif +#ifdef LIBVNCSERVER_HAVE_SYS_SYSCTL_H +#include +#endif + #ifdef LIBVNCSERVER_HAVE_SYS_TIME_H #include #endif @@ -55,6 +66,43 @@ char rfbEndianTest = (1==0); char rfbEndianTest = (1==1); #endif +#ifndef min +inline static int min(int a, int b) +{ + return a > b ? b : a; +} +#endif + +#ifdef __APPLE__ +typedef struct cpu_set { + uint64_t count; +} cpu_set_t; + +static inline void +CPU_ZERO(cpu_set_t *cs) { cs->count = 0; } + +static inline int +CPU_COUNT(cpu_set_t *cs) { return __builtin_popcountll(cs->count); } + +int sched_getaffinity(pid_t pid, size_t cpu_size, cpu_set_t *cpu_set) +{ + int i; + int32_t core_count = 0; + size_t len = sizeof(core_count); + int ret = sysctlbyname("machdep.cpu.core_count", &core_count, &len, 0, 0); + if (ret) { + rfbErr("error while get core count %d\n", ret); + return -1; + } + cpu_set->count = 0; + for (i = 0; i < core_count; i++) { + cpu_set->count |= (1 << i); + } + + return 0; +} +#endif + /* * Protocol extensions */ @@ -996,11 +1044,47 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->permitFileTransfer = FALSE; + screen->rfbMT = TRUE; + screen->rfbNumThreads = 0; + if(!rfbProcessArguments(screen,argc,argv)) { free(screen); return NULL; } +#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS) +#if defined(WIN32) || defined(__MINGW32__) + DWORD64 dwProcessAffinity, dwSystemAffinity; + GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity); +#if defined(__MINGW32__) + const int np = __builtin_popcountll(dwProcessAffinity); +#else + const int np = __popcnt64(dwProcessAffinity); +#endif +#elif LIBVNCSERVER_HAVE_SCHED_H + cpu_set_t cs; + CPU_ZERO(&cs); + int cpus = -1; + if (sched_getaffinity(0, sizeof(cs), &cs) == 0) { + cpus = CPU_COUNT(&cs); + } + const int np = cpus; +#else + const int np = -1; +#endif + if (np == -1 && screen->rfbMT) { + rfbLog("WARNING: Could not determine CPU count. Multithreaded encoding disabled.\n"); + screen->rfbMT = FALSE; + } + if (!screen->rfbMT) screen->rfbNumThreads = 1; + else if (screen->rfbNumThreads < 1) screen->rfbNumThreads = min(np, 4); + if (screen->rfbNumThreads > np) { + rfbLog("NOTICE: Encoding thread count has been clamped to CPU count\n"); + screen->rfbNumThreads = np; + } +#endif + + #ifdef WIN32 { DWORD dummy=255; diff --git a/src/libvncserver/rfbserver.c b/src/libvncserver/rfbserver.c index 3fed80e43..a49c7ef6c 100644 --- a/src/libvncserver/rfbserver.c +++ b/src/libvncserver/rfbserver.c @@ -504,6 +504,15 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, } } +#ifdef LIBVNCSERVER_HAVE_LIBJPEG + /* Multithreaded tight encoding. */ + + cl->threadInit = FALSE; +#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS) + memset(cl->thnd, 0, sizeof(cl->thnd)); +#endif +#endif + for(extension = rfbGetExtensionIterator(); extension; extension=extension->next) { void* data = NULL; diff --git a/src/libvncserver/tight.c b/src/libvncserver/tight.c index 2dcf1ab8e..d2be2613d 100644 --- a/src/libvncserver/tight.c +++ b/src/libvncserver/tight.c @@ -2,47 +2,44 @@ * 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. +/* Copyright (C) 2010-2012, 2014, 2017, 2022 D. R. Commander. + * All Rights Reserved. + * Copyright (C) 2005-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved. + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ +#include +#include +#include +#include #include -#include "private.h" - +#if LIBVNCSERVER_HAVE_UNISTD_H +#include +#endif +#include +#include "turbojpeg.h" #ifdef LIBVNCSERVER_HAVE_LIBPNG #include #endif -#include "turbojpeg.h" - /* Note: The following constant should not be changed. */ #define TIGHT_MIN_TO_COMPRESS 12 @@ -55,6 +52,15 @@ #define TIGHT_MAX_RECT_SIZE 65536 #define TIGHT_MAX_RECT_WIDTH 2048 + +#ifndef min +inline static int min(int a, int b) +{ + return a > b ? b : a; +} +#endif + + /* 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). */ @@ -114,402 +120,601 @@ typedef struct PALETTE_s { PALETTE_ENTRY entry[256]; COLOR_LIST *hash[256]; COLOR_LIST list[256]; - int numColors; - int maxColors; - uint32_t monoBackground; - uint32_t monoForeground; -} PALETTE, *palettePtr; +} PALETTE; + +void ShutdownTightThreads(rfbClientPtr cl); void rfbFreeTightData (rfbClientPtr cl) { - if (cl->tightTJ) { - tjDestroy(cl->tightTJ); - /* Set freed resource handle to 0! */ - cl->tightTJ = 0; - } + ShutdownTightThreads(cl); } +typedef struct _threadparam { + rfbClientPtr cl; + int x, y, w, h, id, _ublen, *ublen; + char *tightBeforeBuf; + int tightBeforeBufSize; + char *tightAfterBuf; + int tightAfterBufSize; + char *updateBuf; + int updateBufSize; + int paletteNumColors, paletteMaxColors; + uint32_t monoBackground, monoForeground; + PALETTE palette; + tjhandle j; + int bytessent; + int streamId, baseStreamId, nStreams; + MUTEX(ready); + MUTEX(done); + rfbBool status, deadyet; + int paddedWidthInBytes; + int bitsPerPixel; + rfbPixelFormat serverFormat; + int compressLevel; + int qualityLevel; + int subsampLevel; + rfbBool usePixelFormat24; +#ifdef LIBVNCSERVER_HAVE_LIBPNG + int tightPngDstDataLen; +#endif +} threadparam; + + /* Prototypes for static functions. */ -static rfbBool SendRectEncodingTight(rfbClientPtr cl, int x, int y, - int w, int h); -static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t colorValue, int *w_ptr, int *h_ptr); -static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t colorValue, - int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr); -static rfbBool CheckSolidTile (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile8 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile16 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); -static rfbBool CheckSolidTile32 (rfbClientPtr cl, int x, int y, int w, int h, - uint32_t *colorPtr, rfbBool needSameColor); - -static rfbBool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h); -static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); - -static rfbBool SendSolidRect (rfbClientPtr cl); -static rfbBool SendMonoRect (rfbClientPtr cl, int x, int y, int w, int h, uint32_t monoForeground, uint32_t monoBackground); -static rfbBool SendIndexedRect (palettePtr palette, 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 CompressData (rfbClientPtr cl, int streamId, int dataLen, - int zlibLevel, int zlibStrategy); - -static void FillPalette8 (palettePtr palette, rfbClientPtr cl, int count); -static void FillPalette16 (palettePtr palette, rfbClientPtr cl, int count); -static void FillPalette32 (palettePtr palette, rfbClientPtr cl, int count); -static void FastFillPalette16 (palettePtr palette, rfbClientPtr cl, uint16_t *data, int w, - int pitch, int h); -static void FastFillPalette32 (palettePtr palette, rfbClientPtr cl, uint32_t *data, int w, - int pitch, int h); - -static void PaletteReset (palettePtr palette); -static int PaletteInsert (palettePtr palette, uint32_t rgb, int numPixels, int bpp); - -static void Pack24 (rfbClientPtr cl, char *buf, rfbPixelFormat *fmt, - int count); - -static void EncodeIndexedRect16 (palettePtr palette, uint8_t *buf, int count); -static void EncodeIndexedRect32 (palettePtr palette, uint8_t *buf, int count); - -static void EncodeMonoRect8 (uint8_t *buf, int w, int h, uint32_t monoBackground); -static void EncodeMonoRect16 (uint8_t *buf, int w, int h, uint32_t monoBackground); -static void EncodeMonoRect32 (uint8_t *buf, int w, int h, uint32_t monoBackground); - -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 FindBestSolidArea(rfbClientPtr cl, int paddedWidthInBytes, rfbPixelFormat *serverFormat, int x, int y, int w, int h, + uint32_t colorValue, int *w_ptr, int *h_ptr); +static void ExtendSolidArea(rfbClientPtr cl, int paddedWidthInBytes, rfbPixelFormat *serverFormat, 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 paddedWidthInBytes, rfbPixelFormat *serverFormat, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor); +static rfbBool CheckSolidTile8(rfbClientPtr cl, int paddedWidthInBytes, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor); +static rfbBool CheckSolidTile16(rfbClientPtr cl, int paddedWidthInBytes, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor); +static rfbBool CheckSolidTile32(rfbClientPtr cl, int paddedWidthInBytes, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor); + +static rfbBool SendRectSimple(threadparam *t, int x, int y, int w, int h); +static rfbBool SendSubrect(threadparam *t, int x, int y, int w, int h); +static rfbBool SendTightHeader(threadparam *t, int x, int y, int w, int h); + +static rfbBool SendSolidRect(threadparam *t); +static rfbBool SendMonoRect(threadparam *t, int x, int y, int w, int h); +static rfbBool SendIndexedRect(threadparam *t, int x, int y, int w, int h); +static rfbBool SendFullColorRect(threadparam *t, int x, int y, int w, int h); + +static rfbBool CompressData(threadparam *t, int streamId, int dataLen, + int zlibLevel, int zlibStrategy); +static rfbBool SendCompressedData(threadparam *t, char *buf, int compressedLen); + +static void FillPalette8(threadparam *t, int count); +static void FillPalette16(threadparam *t, int count); +static void FillPalette32(threadparam *t, int count); +static void FastFillPalette16(threadparam *t, uint16_t *data, int w, int pitch, + int h); +static void FastFillPalette32(threadparam *t, uint32_t *data, int w, int pitch, + int h); + +static void PaletteReset(threadparam *t); +static int PaletteInsert(threadparam *t, uint32_t rgb, int numPixels, int bpp); + +static void Pack24(char *buf, rfbPixelFormat *serverFmt, rfbPixelFormat *fmt, int count); + +static void EncodeIndexedRect16(threadparam *t, uint8_t *buf, int count); +static void EncodeIndexedRect32(threadparam *t, uint8_t *buf, int count); + +static void EncodeMonoRect8(threadparam *t, uint8_t *buf, int w, int h); +static void EncodeMonoRect16(threadparam *t, uint8_t *buf, int w, int h); +static void EncodeMonoRect32(threadparam *t, uint8_t *buf, int w, int h); + +static rfbBool SendJpegRect(threadparam *t, int x, int y, int w, int h, + int quality); + +static rfbBool SendRectEncodingTight(threadparam *t, int x, int y, int w, int h); + +static THREAD_ROUTINE_RETURN_TYPE TightThreadFunc(void *param); +static rfbBool CheckUpdateBuf(threadparam *t, int bytes); #ifdef LIBVNCSERVER_HAVE_LIBPNG -static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h); +static void PrepareRowForImg(threadparam *t, int paddedWidthInBytes, rfbPixelFormat *serverFormat, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg24(threadparam *t, int paddedWidthInBytes, rfbPixelFormat *serverFormat, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg16(threadparam *t, int paddedWidthInBytes, rfbPixelFormat *serverFormat, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg32(threadparam *t, int paddedWidthInBytes, rfbPixelFormat *serverFormat, uint8_t *dst, int x, int y, int count); +static rfbBool SendPngRect(threadparam *t, int x, int y, int w, int h); static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h); #endif + /* * Tight encoding implementation. */ -int -rfbNumCodedRectsTight(rfbClientPtr cl, - int x, - int y, - int w, - int h) +int rfbNumCodedRectsTight(rfbClientPtr cl, int x, int y, int w, int h) { - int subrectMaxWidth, subrectMaxHeight; + int subrectMaxWidth, subrectMaxHeight; + + /* No matter how many rectangles we will send if LastRect markers + are used to terminate rectangle stream. */ + if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE) + return 0; + + if (w > TIGHT_MAX_RECT_WIDTH || w * h > TIGHT_MAX_RECT_SIZE) { + subrectMaxWidth = (w > TIGHT_MAX_RECT_WIDTH) ? TIGHT_MAX_RECT_WIDTH : w; + subrectMaxHeight = TIGHT_MAX_RECT_SIZE / subrectMaxWidth; + return ((w - 1) / TIGHT_MAX_RECT_WIDTH + 1) * ((h - 1) / subrectMaxHeight + 1); + } else { + return 1; + } +} - /* 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; - if (w > TIGHT_MAX_RECT_WIDTH || w * h > TIGHT_MAX_RECT_SIZE) { - subrectMaxWidth = (w > TIGHT_MAX_RECT_WIDTH) ? TIGHT_MAX_RECT_WIDTH : w; - subrectMaxHeight = TIGHT_MAX_RECT_SIZE / subrectMaxWidth; - return (((w - 1) / TIGHT_MAX_RECT_WIDTH + 1) * - ((h - 1) / subrectMaxHeight + 1)); - } else { - return 1; +/* + * Translate compression level into Tight compression level + */ + +int rfbTightCompressLevel(rfbClientPtr cl) +{ + int tightCompressLevel = cl->tightCompressLevel; + + /* If the interframe comparison engine is set to automatic, then + interframe comparison will be enabled for compression levels 5 and + above. Thus, we map 5-8 internally to 0-4 so that we can get + interframe-enabled equivalents of all of the documented TurboVNC modes. + */ + if (tightCompressLevel >= 5 && tightCompressLevel <= 8) + tightCompressLevel -= 5; + + /* 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 with JPEG 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 (cl->turboQualityLevel != -1) { + if (tightCompressLevel < 1) tightCompressLevel = 1; + if (tightCompressLevel > 2) tightCompressLevel = 2; + } + + /* With JPEG disabled, CL 2 offers no significant bandwidth savings over + CL 1, so we don't include it. */ + else if (tightCompressLevel > 1) tightCompressLevel = 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) tightCompressLevel = 3; + + return tightCompressLevel; +} + + +static void InitThreads(rfbClientPtr cl) +{ + int err = 0, i; + + if (cl->threadInit) return; + + cl->tightTJ = calloc(MAX_ENCODING_THREADS, sizeof(threadparam)); + threadparam *tparam = (threadparam*) cl->tightTJ; + + tparam[0].ublen = &cl->ublen; + tparam[0].updateBuf = cl->updateBuf; + for (i = 1; i < MAX_ENCODING_THREADS; i++) { + tparam[i].ublen = &tparam[i]._ublen; + tparam[i].id = i; + } + rfbLog("Using %d thread%s for Tight encoding\n", cl->screen->rfbNumThreads, + cl->screen->rfbNumThreads == 1 ? "" : "s"); + if (cl->screen->rfbNumThreads > 1) { + for (i = 1; i < cl->screen->rfbNumThreads; i++) { + if (!tparam[i].updateBuf) { + tparam[i].updateBufSize = UPDATE_BUF_SIZE; + tparam[i].updateBuf = (char *)malloc(tparam[i].updateBufSize); + if (!tparam[i].updateBuf) { + rfbLog("Memory allocation failure! %d: %s\n", i + 1, strerror(errno)); + return; + } + } + INIT_MUTEX(tparam[i].ready); + LOCK(tparam[i].ready); + INIT_MUTEX(tparam[i].done); + LOCK(tparam[i].done); +#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD + if ((err = pthread_create(&cl->thnd[i], NULL, TightThreadFunc, + &tparam[i])) != 0) { + rfbLog("Could not start thread %d: %s\n", i + 1, + strerror(err == -1 ? errno : err)); + return; + } +#elif defined(LIBVNCSERVER_HAVE_WIN32THREADS) + cl->thnd[i] = _beginthread(TightThreadFunc, 0, &tparam[i]); +#endif } + } + cl->threadInit = TRUE; } -rfbBool -rfbSendRectEncodingTight(rfbClientPtr cl, - int x, - int y, - int w, - int h) +void ShutdownTightThreads(rfbClientPtr cl) { - cl->tightEncoding = rfbEncodingTight; - return SendRectEncodingTight(cl, x, y, w, h); + int i; + + if (!cl->threadInit) return; + threadparam *tparam = (threadparam*) cl->tightTJ; +#if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS) + if (cl->screen->rfbNumThreads > 1) { + for (i = 1; i < cl->screen->rfbNumThreads; i++) { + if (cl->thnd[i]) { + tparam[i].deadyet = TRUE; + UNLOCK(tparam[i].ready); + THREAD_JOIN(cl->thnd[i]); + cl->thnd[i] = 0; + TINI_MUTEX(tparam[i].ready); + TINI_MUTEX(tparam[i].done); + } + } + } +#endif + for (i = 0; i < cl->screen->rfbNumThreads; i++) { + free(tparam[i].tightAfterBuf); + free(tparam[i].tightBeforeBuf); + if (i != 0) free(tparam[i].updateBuf); + if (tparam[i].j) tjDestroy(tparam[i].j); + memset(&tparam[i], 0, sizeof(threadparam)); + } + free(cl->tightTJ); + cl->threadInit = FALSE; } -rfbBool -rfbSendRectEncodingTightPng(rfbClientPtr cl, - int x, - int y, - int w, - int h) +static THREAD_ROUTINE_RETURN_TYPE TightThreadFunc(void *param) { - cl->tightEncoding = rfbEncodingTightPng; - return SendRectEncodingTight(cl, x, y, w, h); + threadparam *t = (threadparam *)param; + + while (!t->deadyet) { + LOCK(t->ready); + if (t->deadyet) break; + t->status = SendRectEncodingTight(t, t->x, t->y, t->w, t->h); + UNLOCK(t->done); + } + return THREAD_ROUTINE_RETURN_VALUE; } -rfbBool -SendRectEncodingTight(rfbClientPtr cl, - int x, - int y, - int w, - int h) +static rfbBool CheckUpdateBuf(threadparam *t, int bytes) { - int nMaxRows; - uint32_t colorValue; - int dx, dy, dw, dh; - int x_best, y_best, w_best, h_best; - char *fbptr; - - rfbSendUpdateBuf(cl); - - /* 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 (cl->turboQualityLevel != -1) { - if (cl->tightCompressLevel < 1) cl->tightCompressLevel = 1; - if (cl->tightCompressLevel > 2) cl->tightCompressLevel = 2; + rfbClientPtr cl = t->cl; + + if (t->id == 0) { + if ((*t->ublen) + bytes > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } else { + if ((*t->ublen) + bytes > t->updateBufSize) { + t->updateBufSize += UPDATE_BUF_SIZE; + t->updateBuf = (char *)realloc(t->updateBuf, t->updateBufSize); + if (!t->updateBuf) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + return FALSE; + } } + } + return TRUE; +} - /* With JPEG disabled, CL 2 offers no significant bandwidth savings over - CL 1, so we don't include it. */ - else if (cl->tightCompressLevel > 1) cl->tightCompressLevel = 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) cl->tightCompressLevel = 3; - - if ( cl->format.depth == 24 && cl->format.redMax == 0xFF && - cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) { - cl->tightUsePixelFormat24 = TRUE; - } else { - cl->tightUsePixelFormat24 = FALSE; + +rfbBool mtSendRectEncodingTight(rfbClientPtr cl, int x, int y, int w, int h) +{ + rfbBool status = TRUE; + int i, nt; + + if (!cl->threadInit) { + InitThreads(cl); + if (!cl->threadInit) return FALSE; + } + + const int compressLevel = rfbTightCompressLevel(cl); + const int qualityLevel = cl->turboQualityLevel; + const int subsampLevel = cl->turboSubsampLevel; + const rfbBool usePixelFormat24 = cl->format.depth == 24 && cl->format.redMax == 0xFF && + cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF; + + nt = min(cl->screen->rfbNumThreads, w * h / TIGHT_MAX_RECT_SIZE); + if (nt < 1) nt = 1; + + threadparam *tparam = (threadparam*) cl->tightTJ; + for (i = 0; i < nt; i++) { + tparam[i].status = TRUE; + tparam[i].cl = cl; + tparam[i].x = x; + tparam[i].y = h / nt * i + y; + tparam[i].w = w; + tparam[i].h = (i == nt - 1) ? (h - (h / nt * i)) : h / nt; + tparam[i].bytessent = 0; + if (i < 4) { + int n = min(nt, 4); + tparam[i].baseStreamId = 4 / n * i; + if (i == n - 1) tparam[i].nStreams = 4 - tparam[i].baseStreamId; + else tparam[i].nStreams = 4 / n; + tparam[i].streamId = tparam[i].baseStreamId; } + tparam[i].paddedWidthInBytes = cl->scaledScreen->paddedWidthInBytes; + tparam[i].bitsPerPixel = cl->scaledScreen->bitsPerPixel; + tparam[i].serverFormat = cl->scaledScreen->serverFormat; + tparam[i].compressLevel = compressLevel; + tparam[i].qualityLevel = qualityLevel; + tparam[i].subsampLevel = subsampLevel; + tparam[i].usePixelFormat24 = usePixelFormat24; + } + if (nt > 1) { + for (i = 1; i < nt; i++) UNLOCK(tparam[i].ready); + } + + status &= SendRectEncodingTight(&tparam[0], tparam[0].x, tparam[0].y, + tparam[0].w, tparam[0].h); + if (!status) return FALSE; + rfbStatRecordEncodingSent(cl, cl->tightEncoding, + tparam[0].bytessent, + sz_rfbFramebufferUpdateRectHeader + + w * (cl->format.bitsPerPixel / 8) * h); + + if (nt > 1) { + for (i = 1; i < nt; i++) { + LOCK(tparam[i].done); + status &= tparam[i].status; + } + if (status == FALSE) return FALSE; + if ((*tparam[0].ublen) > 0) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + for (i = 1; i < nt; i++) { + if ((*tparam[i].ublen) > 0 && + rfbWriteExact(cl, tparam[i].updateBuf, *tparam[i].ublen) < 0) { + rfbLogPerror("rfbSendRectEncodingTight: write"); + rfbCloseClient(cl); + return FALSE; + } + (*tparam[i].ublen) = 0; + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, tparam[i].bytessent); + } + } - if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE) - return SendRectSimple(cl, x, y, w, h); + return status; +} - /* Make sure we can write at least one pixel into cl->beforeEncBuf. */ - if (!cl->beforeEncBuf || cl->beforeEncBufSize < 4) { - if (cl->beforeEncBuf == NULL) - cl->beforeEncBuf = (char *)malloc(4); - else { - char *reallocedBeforeEncBuf = (char *)realloc(cl->beforeEncBuf, 4); - if (!reallocedBeforeEncBuf) return FALSE; - cl->beforeEncBuf = reallocedBeforeEncBuf; - } - if(!cl->beforeEncBuf) - { - rfbLog("SendRectEncodingTight: failed to allocate memory\n"); - return FALSE; - } - cl->beforeEncBufSize = 4; - } +rfbBool +rfbSendRectEncodingTight(rfbClientPtr cl, int x, int y, int w, int h) +{ + cl->tightEncoding = rfbEncodingTight; + return mtSendRectEncodingTight(cl, x, y, w, h); +} - /* Calculate maximum number of rows in one non-solid rectangle. */ - { - int nMaxWidth; +rfbBool +rfbSendRectEncodingTightPng(rfbClientPtr cl, int x, int y, int w, int h) +{ + cl->tightEncoding = rfbEncodingTightPng; + return mtSendRectEncodingTight(cl, x, y, w, h); +} + - nMaxWidth = (w > TIGHT_MAX_RECT_WIDTH) ? TIGHT_MAX_RECT_WIDTH : w; - nMaxRows = TIGHT_MAX_RECT_SIZE / nMaxWidth; +static rfbBool SendRectEncodingTight(threadparam *t, int x, int y, int w, int h) +{ + int nMaxRows; + uint32_t colorValue = 0; + int dx, dy, dw, dh; + int x_best, y_best, w_best, h_best; + char *fbptr; + rfbClientPtr cl = t->cl; + + if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE) + return SendRectSimple(t, x, y, w, h); + + /* Make sure we can write at least one pixel into tightBeforeBuf. */ + + if (t->tightBeforeBufSize < 4) { + t->tightBeforeBufSize = 4; + if (t->tightBeforeBuf == NULL) + t->tightBeforeBuf = (char *)malloc(t->tightBeforeBufSize); + else + t->tightBeforeBuf = (char *)realloc(t->tightBeforeBuf, + t->tightBeforeBufSize); + if (!t->tightBeforeBuf) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + return FALSE; } + } - /* Try to find large solid-color areas and send them separately. */ + /* Calculate maximum number of rows in one non-solid rectangle. */ - for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { + { + int nMaxWidth; - /* If a rectangle becomes too large, send its upper part now. */ + nMaxWidth = (w > TIGHT_MAX_RECT_WIDTH) ? TIGHT_MAX_RECT_WIDTH : w; + nMaxRows = TIGHT_MAX_RECT_SIZE / nMaxWidth; + } - if (dy - y >= nMaxRows) { - if (!SendRectSimple(cl, x, y, w, nMaxRows)) - return 0; - y += nMaxRows; - h -= nMaxRows; - } + /* Try to find large solid-color areas and send them separately. */ - dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? - MAX_SPLIT_TILE_SIZE : (y + h - dy); + for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { - for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) { + /* If a rectangle becomes too large, send its upper part now. */ - dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ? - MAX_SPLIT_TILE_SIZE : (x + w - dx); + if (dy - y >= nMaxRows) { + if (!SendRectSimple(t, x, y, w, nMaxRows)) + return 0; + y += nMaxRows; + h -= nMaxRows; + } - if (CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, FALSE)) { + dh = + (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? MAX_SPLIT_TILE_SIZE : (y + h - dy); - if (cl->turboSubsampLevel == TJ_GRAYSCALE && cl->turboQualityLevel != -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); - } + for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) { - /* Get dimensions of solid-color area. */ + dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ? + MAX_SPLIT_TILE_SIZE : (x + w - dx); - FindBestSolidArea(cl, dx, dy, w - (dx - x), h - (dy - y), - colorValue, &w_best, &h_best); + if (CheckSolidTile(cl, t->paddedWidthInBytes, &t->serverFormat, dx, dy, dw, dh, &colorValue, FALSE)) { - /* Make sure a solid rectangle is large enough - (or the whole rectangle is of the same color). */ + if (t->subsampLevel == TJ_GRAYSCALE && t->qualityLevel != -1) { + uint32_t r = (colorValue >> 16) & 0xFF; + uint32_t g = (colorValue >> 8) & 0xFF; + uint32_t b = (colorValue) & 0xFF; + double lum = (0.257 * (double)r) + (0.504 * (double)g) + + (0.098 * (double)b) + 16.; + colorValue = (int)lum + (((int)lum) << 8) + (((int)lum) << 16); + } - if ( w_best * h_best != w * h && - w_best * h_best < MIN_SOLID_SUBRECT_SIZE ) - continue; + /* Get dimensions of solid-color area. */ - /* Try to extend solid rectangle to maximum size. */ + FindBestSolidArea(cl, t->paddedWidthInBytes, &t->serverFormat, dx, dy, w - (dx - x), h - (dy - y), colorValue, + &w_best, &h_best); - x_best = dx; y_best = dy; - ExtendSolidArea(cl, x, y, w, h, colorValue, - &x_best, &y_best, &w_best, &h_best); + /* Make sure a solid rectangle is large enough + (or the whole rectangle is of the same color). */ - /* Send rectangles at top and left to solid-color area. */ + if (w_best * h_best != w * h && + w_best * h_best < MIN_SOLID_SUBRECT_SIZE) + continue; - if ( y_best != y && - !SendRectSimple(cl, x, y, w, y_best-y) ) - return FALSE; - if ( x_best != x && - !SendRectEncodingTight(cl, x, y_best, - x_best-x, h_best) ) - return FALSE; + /* Try to extend solid rectangle to maximum size. */ - /* Send solid-color rectangle. */ + x_best = dx; y_best = dy; + ExtendSolidArea(cl, t->paddedWidthInBytes, &t->serverFormat, x, y, w, h, colorValue, &x_best, &y_best, + &w_best, &h_best); - if (!rfbSendTightHeader(cl, x_best, y_best, w_best, h_best)) - return FALSE; + /* Send rectangles at top and left to solid-color area. */ - fbptr = (cl->scaledScreen->frameBuffer + - (cl->scaledScreen->paddedWidthInBytes * y_best) + - (x_best * (cl->scaledScreen->bitsPerPixel / 8))); + if (y_best != y && !SendRectSimple(t, x, y, w, y_best - y)) + return FALSE; + if (x_best != x && + !SendRectEncodingTight(t, x, y_best, x_best - x, h_best)) + return FALSE; - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, - &cl->format, fbptr, cl->beforeEncBuf, - cl->scaledScreen->paddedWidthInBytes, 1, 1); + /* Send solid-color rectangle. */ - if (!SendSolidRect(cl)) - return FALSE; + if (!SendTightHeader(t, x_best, y_best, w_best, h_best)) + return FALSE; - /* Send remaining rectangles (at right and bottom). */ + fbptr = (cl->scaledScreen->frameBuffer + (t->paddedWidthInBytes * y_best) + + (x_best * (t->bitsPerPixel / 8))); - if ( x_best + w_best != x + w && - !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) ) - return FALSE; + (*cl->translateFn) (cl->translateLookupTable, &t->serverFormat, + &cl->format, fbptr, t->tightBeforeBuf, + t->paddedWidthInBytes, 1, 1); - /* Return after all recursive calls are done. */ + if (!SendSolidRect(t)) + return FALSE; - return TRUE; - } + /* Send remaining rectangles (at right and bottom). */ - } + if (x_best + w_best != x + w && + !SendRectEncodingTight(t, x_best + w_best, y_best, + w - (x_best - x) - w_best, h_best)) + return FALSE; + if (y_best + h_best != y + h && + !SendRectEncodingTight(t, x, y_best + h_best, + w, h - (y_best - y) - h_best)) + return FALSE; + + /* Return after all recursive calls are done. */ + + return TRUE; + } } - /* No suitable solid-color rectangles found. */ + } + + /* No suitable solid-color rectangles found. */ - return SendRectSimple(cl, x, y, w, h); + return SendRectSimple(t, x, y, w, h); } -static void -FindBestSolidArea(rfbClientPtr cl, - int x, - int y, - int w, - int h, - uint32_t colorValue, - int *w_ptr, - int *h_ptr) +static void FindBestSolidArea(rfbClientPtr cl, int paddedWidthInBytes, rfbPixelFormat *serverFormat, int x, int y, int w, int h, + uint32_t colorValue, int *w_ptr, int *h_ptr) { - int dx, dy, dw, dh; - int w_prev; - int w_best = 0, h_best = 0; + int dx, dy, dw, dh; + int w_prev; + int w_best = 0, h_best = 0; - w_prev = w; + w_prev = w; - for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { + for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { - dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? - MAX_SPLIT_TILE_SIZE : (y + h - dy); - dw = (w_prev > MAX_SPLIT_TILE_SIZE) ? - MAX_SPLIT_TILE_SIZE : w_prev; + dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? + MAX_SPLIT_TILE_SIZE : (y + h - dy); + dw = (w_prev > MAX_SPLIT_TILE_SIZE) ? + MAX_SPLIT_TILE_SIZE : w_prev; - if (!CheckSolidTile(cl, x, dy, dw, dh, &colorValue, TRUE)) - break; + if (!CheckSolidTile(cl, paddedWidthInBytes, serverFormat, x, dy, dw, dh, &colorValue, TRUE)) + break; - for (dx = x + dw; dx < x + w_prev;) { - dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ? - MAX_SPLIT_TILE_SIZE : (x + w_prev - dx); - if (!CheckSolidTile(cl, dx, dy, dw, dh, &colorValue, TRUE)) - break; - dx += dw; - } + for (dx = x + dw; dx < x + w_prev;) { + dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ? + MAX_SPLIT_TILE_SIZE : (x + w_prev - dx); + if (!CheckSolidTile(cl, paddedWidthInBytes, serverFormat, dx, dy, dw, dh, &colorValue, TRUE)) + break; + dx += dw; + } - w_prev = dx - x; - if (w_prev * (dy + dh - y) > w_best * h_best) { - w_best = w_prev; - h_best = dy + dh - y; - } + w_prev = dx - x; + if (w_prev * (dy + dh - y) > w_best * h_best) { + w_best = w_prev; + h_best = dy + dh - y; } + } - *w_ptr = w_best; - *h_ptr = h_best; + *w_ptr = w_best; + *h_ptr = h_best; } -static void -ExtendSolidArea(rfbClientPtr cl, - int x, - int y, - int w, - int h, - uint32_t colorValue, - int *x_ptr, - int *y_ptr, - int *w_ptr, - int *h_ptr) +static void ExtendSolidArea(rfbClientPtr cl, int paddedWidthInBytes, rfbPixelFormat *serverFormat, int x, int y, int w, int h, + uint32_t colorValue, int *x_ptr, int *y_ptr, + int *w_ptr, int *h_ptr) { - int cx, cy; - - /* Try to extend the area upwards. */ - for ( cy = *y_ptr - 1; - cy >= y && CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); - cy-- ); - *h_ptr += *y_ptr - (cy + 1); - *y_ptr = cy + 1; - - /* ... downwards. */ - for ( cy = *y_ptr + *h_ptr; - cy < y + h && - CheckSolidTile(cl, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); - cy++ ); - *h_ptr += cy - (*y_ptr + *h_ptr); - - /* ... to the left. */ - for ( cx = *x_ptr - 1; - cx >= x && CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); - cx-- ); - *w_ptr += *x_ptr - (cx + 1); - *x_ptr = cx + 1; - - /* ... to the right. */ - for ( cx = *x_ptr + *w_ptr; - cx < x + w && - CheckSolidTile(cl, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); - cx++ ); - *w_ptr += cx - (*x_ptr + *w_ptr); + int cx, cy; + + /* Try to extend the area upwards. */ + for (cy = *y_ptr - 1; + cy >= y && CheckSolidTile(cl, paddedWidthInBytes, serverFormat, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); + cy--); + *h_ptr += *y_ptr - (cy + 1); + *y_ptr = cy + 1; + + /* ... downwards. */ + for (cy = *y_ptr + *h_ptr; + cy < y + h && + CheckSolidTile(cl, paddedWidthInBytes, serverFormat, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); + cy++); + *h_ptr += cy - (*y_ptr + *h_ptr); + + /* ... to the left. */ + for (cx = *x_ptr - 1; + cx >= x && CheckSolidTile(cl, paddedWidthInBytes, serverFormat, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); + cx--); + *w_ptr += *x_ptr - (cx + 1); + *x_ptr = cx + 1; + + /* ... to the right. */ + for (cx = *x_ptr + *w_ptr; + cx < x + w && + CheckSolidTile(cl, paddedWidthInBytes, serverFormat, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); + cx++); + *w_ptr += cx - (*x_ptr + *w_ptr); } @@ -520,571 +725,572 @@ ExtendSolidArea(rfbClientPtr cl, * that case new color will be stored in *colorPtr. */ -static rfbBool CheckSolidTile(rfbClientPtr cl, int x, int y, int w, int h, uint32_t* colorPtr, rfbBool needSameColor) +static rfbBool CheckSolidTile(rfbClientPtr cl, int paddedWidthInBytes, rfbPixelFormat *serverFormat, int x, int y, int w, int h, + uint32_t *colorPtr, rfbBool needSameColor) { - switch(cl->screen->serverFormat.bitsPerPixel) { + switch (serverFormat->bitsPerPixel) { case 32: - return CheckSolidTile32(cl, x, y, w, h, colorPtr, needSameColor); + return CheckSolidTile32(cl, paddedWidthInBytes, x, y, w, h, colorPtr, needSameColor); case 16: - return CheckSolidTile16(cl, x, y, w, h, colorPtr, needSameColor); + return CheckSolidTile16(cl, paddedWidthInBytes, x, y, w, h, colorPtr, needSameColor); default: - return CheckSolidTile8(cl, x, y, w, h, colorPtr, needSameColor); - } + return CheckSolidTile8(cl, paddedWidthInBytes, x, y, w, h, colorPtr, needSameColor); + } } #define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ \ -static rfbBool \ -CheckSolidTile##bpp(rfbClientPtr cl, int x, int y, int w, int h, \ - uint32_t* colorPtr, rfbBool needSameColor) \ +static rfbBool CheckSolidTile##bpp(rfbClientPtr cl, int paddedWidthInBytes, int x, int y, int w, int h, \ + uint32_t *colorPtr, rfbBool needSameColor) \ { \ - uint##bpp##_t *fbptr; \ - uint##bpp##_t colorValue; \ - int dx, dy; \ + uint##bpp##_t *fbptr; \ + uint##bpp##_t colorValue; \ + int dx, dy; \ \ - fbptr = (uint##bpp##_t *)&cl->scaledScreen->frameBuffer \ - [y * cl->scaledScreen->paddedWidthInBytes + x * (bpp/8)]; \ + fbptr = \ + (uint##bpp##_t *)&cl->scaledScreen->frameBuffer[y * paddedWidthInBytes + x * (bpp / 8)]; \ \ - colorValue = *fbptr; \ - if (needSameColor && (uint32_t)colorValue != *colorPtr) \ - return FALSE; \ + colorValue = *fbptr; \ + if (needSameColor && (uint32_t)colorValue != *colorPtr) \ + return FALSE; \ \ - for (dy = 0; dy < h; dy++) { \ - for (dx = 0; dx < w; dx++) { \ - if (colorValue != fbptr[dx]) \ - return FALSE; \ - } \ - fbptr = (uint##bpp##_t *)((uint8_t *)fbptr \ - + cl->scaledScreen->paddedWidthInBytes); \ + for (dy = 0; dy < h; dy++) { \ + for (dx = 0; dx < w; dx++) { \ + if (colorValue != fbptr[dx]) \ + return FALSE; \ } \ + fbptr = (uint##bpp##_t *)((uint8_t *)fbptr + paddedWidthInBytes); \ + } \ \ - *colorPtr = (uint32_t)colorValue; \ - return TRUE; \ + *colorPtr = (uint32_t)colorValue; \ + return TRUE; \ } DEFINE_CHECK_SOLID_FUNCTION(8) DEFINE_CHECK_SOLID_FUNCTION(16) DEFINE_CHECK_SOLID_FUNCTION(32) -static rfbBool -SendRectSimple(rfbClientPtr cl, int x, int y, int w, int h) -{ - int maxBeforeSize, maxAfterSize; - int subrectMaxWidth, subrectMaxHeight; - int dx, dy; - int rw, rh; - - maxBeforeSize = TIGHT_MAX_RECT_SIZE * (cl->format.bitsPerPixel / 8); - maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12; - - if (!cl->beforeEncBuf || cl->beforeEncBufSize < maxBeforeSize) { - if (cl->beforeEncBuf == NULL) - cl->beforeEncBuf = (char *)malloc(maxBeforeSize); - else { - char *reallocedBeforeEncBuf = (char *)realloc(cl->beforeEncBuf, maxBeforeSize); - if (!reallocedBeforeEncBuf) return FALSE; - cl->beforeEncBuf = reallocedBeforeEncBuf; - } - if (cl->beforeEncBuf) - cl->beforeEncBufSize = maxBeforeSize; - } - if (!cl->afterEncBuf || cl->afterEncBufSize < maxAfterSize) { - if (cl->afterEncBuf == NULL) - cl->afterEncBuf = (char *)malloc(maxAfterSize); - else { - char *reallocedAfterEncBuf = (char *)realloc(cl->afterEncBuf, maxAfterSize); - if (!reallocedAfterEncBuf) return FALSE; - cl->afterEncBuf = reallocedAfterEncBuf; - } - if(cl->afterEncBuf) - cl->afterEncBufSize = maxAfterSize; +static rfbBool SendRectSimple(threadparam *t, int x, int y, int w, int h) +{ + int maxBeforeSize, maxAfterSize; + int subrectMaxWidth, subrectMaxHeight; + int dx, dy; + int rw, rh; + rfbClientPtr cl = t->cl; + + maxBeforeSize = TIGHT_MAX_RECT_SIZE * (cl->format.bitsPerPixel / 8); + maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12; + + if (t->tightBeforeBufSize < maxBeforeSize) { + t->tightBeforeBufSize = maxBeforeSize; + if (t->tightBeforeBuf == NULL) + t->tightBeforeBuf = (char *)malloc(t->tightBeforeBufSize); + else + t->tightBeforeBuf = (char *)realloc(t->tightBeforeBuf, + t->tightBeforeBufSize); + if (!t->tightBeforeBuf) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + return FALSE; } + } - if (!cl->beforeEncBuf || !cl->afterEncBuf) - { - rfbLog("SendRectSimple: failed to allocate memory\n"); - return FALSE; + if (t->tightAfterBufSize < maxAfterSize) { + t->tightAfterBufSize = maxAfterSize; + if (t->tightAfterBuf == NULL) + t->tightAfterBuf = (char *)malloc(t->tightAfterBufSize); + else + t->tightAfterBuf = (char *)realloc(t->tightAfterBuf, + t->tightAfterBufSize); + if (!t->tightAfterBuf) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + return FALSE; } - - if (w > TIGHT_MAX_RECT_WIDTH || w * h > TIGHT_MAX_RECT_SIZE) { - subrectMaxWidth = (w > TIGHT_MAX_RECT_WIDTH) ? TIGHT_MAX_RECT_WIDTH: w; - subrectMaxHeight = TIGHT_MAX_RECT_SIZE / subrectMaxWidth; - - for (dy = 0; dy < h; dy += subrectMaxHeight) { - for (dx = 0; dx < w; dx += TIGHT_MAX_RECT_WIDTH) { - rw = (dx + TIGHT_MAX_RECT_WIDTH < w) ? TIGHT_MAX_RECT_WIDTH : w - dx; - rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy; - if (!SendSubrect(cl, x + dx, y + dy, rw, rh)) - return FALSE; - } - } - } else { - if (!SendSubrect(cl, x, y, w, h)) - return FALSE; + } + + if (w > TIGHT_MAX_RECT_WIDTH || w * h > TIGHT_MAX_RECT_SIZE) { + subrectMaxWidth = (w > TIGHT_MAX_RECT_WIDTH) ? TIGHT_MAX_RECT_WIDTH : w; + subrectMaxHeight = TIGHT_MAX_RECT_SIZE / subrectMaxWidth; + + for (dy = 0; dy < h; dy += subrectMaxHeight) { + for (dx = 0; dx < w; dx += TIGHT_MAX_RECT_WIDTH) { + rw = (dx + TIGHT_MAX_RECT_WIDTH < w) ? TIGHT_MAX_RECT_WIDTH : w - dx; + rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy; + if (!SendSubrect(t, x + dx, y + dy, rw, rh)) + return FALSE; + } } + } else { + if (!SendSubrect(t, x, y, w, h)) + return FALSE; + } - return TRUE; + return TRUE; } -static rfbBool -SendSubrect(rfbClientPtr cl, - int x, - int y, - int w, - int h) -{ - char *fbptr; - PALETTE palette; - rfbBool success = FALSE; - - /* Send pending data if there is more than 128 bytes. */ - if (cl->ublen > 128) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - if (!rfbSendTightHeader(cl, x, y, w, h)) +static rfbBool SendSubrect(threadparam *t, int x, int y, int w, int h) +{ + char *fbptr; + rfbBool success = FALSE; + rfbClientPtr cl = t->cl; + + /* Send pending data if there is more than 128 bytes. */ + if (t->id == 0) { + if ((*t->ublen) > 128) { + if (!rfbSendUpdateBuf(cl)) return FALSE; - - fbptr = (cl->scaledScreen->frameBuffer - + (cl->scaledScreen->paddedWidthInBytes * y) - + (x * (cl->scaledScreen->bitsPerPixel / 8))); - - if (cl->turboSubsampLevel == TJ_GRAYSCALE && cl->turboQualityLevel != -1) - return SendJpegRect(cl, x, y, w, h, cl->turboQualityLevel); - - palette.maxColors = w * h / tightConf[cl->tightCompressLevel].idxMaxColorsDivisor; - if(cl->turboQualityLevel != -1) - palette.maxColors = tightConf[cl->tightCompressLevel].palMaxColorsWithJPEG; - if ( palette.maxColors < 2 && - w * h >= tightConf[cl->tightCompressLevel].monoMinRectSize ) { - palette.maxColors = 2; + } + } + + if (!SendTightHeader(t, x, y, w, h)) + return FALSE; + + fbptr = + (cl->scaledScreen->frameBuffer + (t->paddedWidthInBytes * y) + (x * (t->bitsPerPixel / 8))); + + if (t->subsampLevel == TJ_GRAYSCALE && t->qualityLevel != -1 && + t->bitsPerPixel > 8) + return SendJpegRect(t, x, y, w, h, t->qualityLevel); + + t->paletteMaxColors = w * h / tightConf[t->compressLevel].idxMaxColorsDivisor; + if (t->qualityLevel != -1) + t->paletteMaxColors = tightConf[t->compressLevel].palMaxColorsWithJPEG; + if (t->paletteMaxColors < 2 && + w * h >= tightConf[t->compressLevel].monoMinRectSize) { + t->paletteMaxColors = 2; + } + + if (cl->format.bitsPerPixel == t->serverFormat.bitsPerPixel && + cl->format.redMax == t->serverFormat.redMax && + cl->format.greenMax == t->serverFormat.greenMax && + cl->format.blueMax == t->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(t, (uint16_t *)fbptr, w, t->paddedWidthInBytes / 2, + h); + break; + default: + FastFillPalette32(t, (uint32_t *)fbptr, w, t->paddedWidthInBytes / 4, + 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(&palette, cl, (uint16_t *)fbptr, w, - cl->scaledScreen->paddedWidthInBytes / 2, h); - break; - default: - FastFillPalette32(&palette, cl, (uint32_t *)fbptr, w, - cl->scaledScreen->paddedWidthInBytes / 4, h); - } - - if(palette.numColors != 0 || cl->turboQualityLevel == -1) { - (*cl->translateFn)(cl->translateLookupTable, - &cl->screen->serverFormat, &cl->format, fbptr, - cl->beforeEncBuf, - cl->scaledScreen->paddedWidthInBytes, w, h); - } + if (t->paletteNumColors != 0 || t->qualityLevel == -1) { + (*cl->translateFn) (cl->translateLookupTable, &t->serverFormat, + &cl->format, fbptr, t->tightBeforeBuf, + t->paddedWidthInBytes, w, h); } - else { - (*cl->translateFn)(cl->translateLookupTable, &cl->screen->serverFormat, - &cl->format, fbptr, cl->beforeEncBuf, - cl->scaledScreen->paddedWidthInBytes, w, h); - - switch (cl->format.bitsPerPixel) { - case 8: - FillPalette8(&palette, cl, w * h); - break; - case 16: - FillPalette16(&palette, cl, w * h); - break; - default: - FillPalette32(&palette, cl, w * h); - } + } else { + (*cl->translateFn) (cl->translateLookupTable, &t->serverFormat, + &cl->format, fbptr, t->tightBeforeBuf, + t->paddedWidthInBytes, w, h); + + switch (cl->format.bitsPerPixel) { + case 8: + FillPalette8(t, w * h); + break; + case 16: + FillPalette16(t, w * h); + break; + default: + FillPalette32(t, w * h); } + } - switch (palette.numColors) { + switch (t->paletteNumColors) { case 0: - /* Truecolor image */ - if (cl->turboQualityLevel != -1) { - success = SendJpegRect(cl, x, y, w, h, cl->turboQualityLevel); - } else { - success = SendFullColorRect(cl, x, y, w, h); - } - break; + /* Truecolor image */ + if (t->qualityLevel != -1) { + success = SendJpegRect(t, x, y, w, h, t->qualityLevel); + } else { + success = SendFullColorRect(t, x, y, w, h); + } + break; case 1: - /* Solid rectangle */ - success = SendSolidRect(cl); - break; + /* Solid rectangle */ + success = SendSolidRect(t); + break; case 2: - /* Two-color rectangle */ - success = SendMonoRect(cl, x, y, w, h, palette.monoForeground, palette.monoBackground); - break; + /* Two-color rectangle */ + success = SendMonoRect(t, x, y, w, h); + break; default: - /* Up to 256 different colors */ - success = SendIndexedRect(&palette, cl, x, y, w, h); - } - return success; + /* Up to 256 different colors */ + success = SendIndexedRect(t, x, y, w, h); + } + return success; } -rfbBool -rfbSendTightHeader(rfbClientPtr cl, - int x, - int y, - int w, - int h) + +static rfbBool SendTightHeader(threadparam *t, int x, int y, int w, int h) { - rfbFramebufferUpdateRectHeader rect; + rfbFramebufferUpdateRectHeader rect; - if (cl->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } + if (!CheckUpdateBuf(t, sz_rfbFramebufferUpdateRectHeader)) + return FALSE; - rect.r.x = Swap16IfLE(x); - rect.r.y = Swap16IfLE(y); - rect.r.w = Swap16IfLE(w); - rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(cl->tightEncoding); + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(t->cl->tightEncoding); - memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, - sz_rfbFramebufferUpdateRectHeader); - cl->ublen += sz_rfbFramebufferUpdateRectHeader; + memcpy(&t->updateBuf[*t->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + (*t->ublen) += sz_rfbFramebufferUpdateRectHeader; - rfbStatRecordEncodingSent(cl, cl->tightEncoding, - sz_rfbFramebufferUpdateRectHeader, - sz_rfbFramebufferUpdateRectHeader - + w * (cl->format.bitsPerPixel / 8) * h); + t->bytessent += sz_rfbFramebufferUpdateRectHeader; - return TRUE; + return TRUE; } + +rfbBool rfbSendTightHeader(rfbClientPtr cl, int x, int y, int w, int h) +{ + if (!cl->threadInit) { + InitThreads(cl); + if (!cl->threadInit) return FALSE; + } + + threadparam *tparam = (threadparam*) cl->tightTJ; + tparam[0].cl = cl; + + return SendTightHeader(&tparam[0], x, y, w, h); +} + + /* * Subencoding implementations. */ -static rfbBool -SendSolidRect(rfbClientPtr cl) +static rfbBool SendSolidRect(threadparam *t) { - int len; + int len; + rfbClientPtr cl = t->cl; - if (cl->tightUsePixelFormat24) { - Pack24(cl, cl->beforeEncBuf, &cl->format, 1); - len = 3; - } else - len = cl->format.bitsPerPixel / 8; + if (t->usePixelFormat24) { + Pack24(t->tightBeforeBuf, &t->serverFormat, &cl->format, 1); + len = 3; + } else + len = cl->format.bitsPerPixel / 8; - if (cl->ublen + 1 + len > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } + if (!CheckUpdateBuf(t, 1 + len)) + return FALSE; - cl->updateBuf[cl->ublen++] = (char)(rfbTightFill << 4); - memcpy (&cl->updateBuf[cl->ublen], cl->beforeEncBuf, len); - cl->ublen += len; + t->updateBuf[(*t->ublen)++] = (char)(rfbTightFill << 4); + memcpy(&t->updateBuf[*t->ublen], t->tightBeforeBuf, len); + (*t->ublen) += len; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, len + 1); + t->bytessent += len + 1; - return TRUE; + return TRUE; } -static rfbBool -SendMonoRect(rfbClientPtr cl, - int x, - int y, - int w, - int h, - uint32_t monoForeground, - uint32_t monoBackground) + +static rfbBool SendMonoRect(threadparam *t, int x, int y, int w, int h) { - int streamId = 1; - int paletteLen, dataLen; + int streamId = t->streamId; + int paletteLen, dataLen; + rfbClientPtr cl = t->cl; #ifdef LIBVNCSERVER_HAVE_LIBPNG if (CanSendPngRect(cl, w, h)) { /* TODO: setup palette maybe */ - return SendPngRect(cl, x, y, w, h); + return SendPngRect(t, x, y, w, h); /* TODO: destroy palette maybe */ } #endif - if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + - 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - /* Prepare tight encoding header. */ - dataLen = (w + 7) / 8; - dataLen *= h; + if (!CheckUpdateBuf(t, TIGHT_MIN_TO_COMPRESS + 6 + + 2 * cl->format.bitsPerPixel / 8)) + return FALSE; + + if (t->nStreams > 0) { + t->streamId++; + if (t->streamId >= t->baseStreamId + t->nStreams) + t->streamId = t->baseStreamId; + } + + /* Prepare tight encoding header. */ + dataLen = (w + 7) / 8; + dataLen *= h; + + if (tightConf[t->compressLevel].monoZlibLevel == 0 || t->id > 3) + t->updateBuf[(*t->ublen)++] = + (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4); + else + t->updateBuf[(*t->ublen)++] = (streamId | rfbTightExplicitFilter) << 4; + t->updateBuf[(*t->ublen)++] = rfbTightFilterPalette; + t->updateBuf[(*t->ublen)++] = 1; + + /* Prepare palette, convert image. */ + switch (cl->format.bitsPerPixel) { + case 32: + EncodeMonoRect32(t, (uint8_t *)t->tightBeforeBuf, w, h); - if (tightConf[cl->tightCompressLevel].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; + ((uint32_t *)t->tightAfterBuf)[0] = t->monoBackground; + ((uint32_t *)t->tightAfterBuf)[1] = t->monoForeground; + if (t->usePixelFormat24) { + Pack24(t->tightAfterBuf, &t->serverFormat, &cl->format, 2); + paletteLen = 6; + } else + paletteLen = 8; - /* Prepare palette, convert image. */ - switch (cl->format.bitsPerPixel) { - - case 32: - EncodeMonoRect32((uint8_t *)cl->beforeEncBuf, w, h, monoBackground); - - ((uint32_t *)cl->afterEncBuf)[0] = monoBackground; - ((uint32_t *)cl->afterEncBuf)[1] = monoForeground; - if (cl->tightUsePixelFormat24) { - Pack24(cl, cl->afterEncBuf, &cl->format, 2); - paletteLen = 6; - } else - paletteLen = 8; - - memcpy(&cl->updateBuf[cl->ublen], cl->afterEncBuf, paletteLen); - cl->ublen += paletteLen; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteLen); - break; + memcpy(&t->updateBuf[*t->ublen], t->tightAfterBuf, paletteLen); + (*t->ublen) += paletteLen; + t->bytessent += 3 + paletteLen; + break; case 16: - EncodeMonoRect16((uint8_t *)cl->beforeEncBuf, w, h, monoBackground); + EncodeMonoRect16(t, (uint8_t *)t->tightBeforeBuf, w, h); - ((uint16_t *)cl->afterEncBuf)[0] = (uint16_t)monoBackground; - ((uint16_t *)cl->afterEncBuf)[1] = (uint16_t)monoForeground; + ((uint16_t *)t->tightAfterBuf)[0] = (uint16_t)t->monoBackground; + ((uint16_t *)t->tightAfterBuf)[1] = (uint16_t)t->monoForeground; - memcpy(&cl->updateBuf[cl->ublen], cl->afterEncBuf, 4); - cl->ublen += 4; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 7); - break; + memcpy(&t->updateBuf[*t->ublen], t->tightAfterBuf, 4); + (*t->ublen) += 4; + t->bytessent += 7; + break; default: - EncodeMonoRect8((uint8_t *)cl->beforeEncBuf, w, h, monoBackground); + EncodeMonoRect8(t, (uint8_t *)t->tightBeforeBuf, w, h); - cl->updateBuf[cl->ublen++] = (char)monoBackground; - cl->updateBuf[cl->ublen++] = (char)monoForeground; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 5); - } + t->updateBuf[(*t->ublen)++] = (char)t->monoBackground; + t->updateBuf[(*t->ublen)++] = (char)t->monoForeground; + t->bytessent += 5; + } - return CompressData(cl, streamId, dataLen, - tightConf[cl->tightCompressLevel].monoZlibLevel, - Z_DEFAULT_STRATEGY); + return CompressData(t, streamId, dataLen, + tightConf[t->compressLevel].monoZlibLevel, + Z_DEFAULT_STRATEGY); } -static rfbBool -SendIndexedRect(palettePtr palette, - rfbClientPtr cl, - int x, - int y, - int w, - int h) + +static rfbBool SendIndexedRect(threadparam *t, int x, int y, int w, int h) { - int streamId = 2; - int i, entryLen; + int streamId = t->streamId; + int i, entryLen; + rfbClientPtr cl = t->cl; #ifdef LIBVNCSERVER_HAVE_LIBPNG - if (CanSendPngRect(cl, w, h)) { - return SendPngRect(cl, x, y, w, h); - } + if (CanSendPngRect(cl, w, h)) { + return SendPngRect(t, x, y, w, h); + } #endif - if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + - palette->numColors * cl->format.bitsPerPixel / 8 > - UPDATE_BUF_SIZE ) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - /* Prepare tight encoding header. */ - if (tightConf[cl->tightCompressLevel].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)(palette->numColors - 1); - - /* Prepare palette, convert image. */ - switch (cl->format.bitsPerPixel) { - + if (!CheckUpdateBuf(t, TIGHT_MIN_TO_COMPRESS + 6 + + t->paletteNumColors * cl->format.bitsPerPixel / 8)) + return FALSE; + + if (t->nStreams > 0) { + t->streamId++; + if (t->streamId >= t->baseStreamId + t->nStreams) + t->streamId = t->baseStreamId; + } + + /* Prepare tight encoding header. */ + if (tightConf[t->compressLevel].idxZlibLevel == 0 || t->id > 3) + t->updateBuf[(*t->ublen)++] = + (char)((rfbTightNoZlib | rfbTightExplicitFilter) << 4); + else + t->updateBuf[(*t->ublen)++] = (streamId | rfbTightExplicitFilter) << 4; + t->updateBuf[(*t->ublen)++] = rfbTightFilterPalette; + t->updateBuf[(*t->ublen)++] = (char)(t->paletteNumColors - 1); + + /* Prepare palette, convert image. */ + switch (cl->format.bitsPerPixel) { case 32: - EncodeIndexedRect32(palette, (uint8_t *)cl->beforeEncBuf, w * h); - - for (i = 0; i < palette->numColors; i++) { - ((uint32_t *)cl->afterEncBuf)[i] = - palette->entry[i].listNode->rgb; - } - if (cl->tightUsePixelFormat24) { - Pack24(cl, cl->afterEncBuf, &cl->format, palette->numColors); - entryLen = 3; - } else - entryLen = 4; - - memcpy(&cl->updateBuf[cl->ublen], cl->afterEncBuf, - (size_t) palette->numColors * entryLen); - cl->ublen += palette->numColors * entryLen; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, - 3 + palette->numColors * entryLen); - break; + EncodeIndexedRect32(t, (uint8_t *)t->tightBeforeBuf, w * h); + + for (i = 0; i < t->paletteNumColors; i++) + ((uint32_t *)t->tightAfterBuf)[i] = t->palette.entry[i].listNode->rgb; + if (t->usePixelFormat24) { + Pack24(t->tightAfterBuf, &t->serverFormat, &cl->format, t->paletteNumColors); + entryLen = 3; + } else + entryLen = 4; + + memcpy(&t->updateBuf[*t->ublen], t->tightAfterBuf, + (size_t)t->paletteNumColors * entryLen); + (*t->ublen) += t->paletteNumColors * entryLen; + t->bytessent += 3 + t->paletteNumColors * entryLen; + break; case 16: - EncodeIndexedRect16(palette, (uint8_t *)cl->beforeEncBuf, w * h); + EncodeIndexedRect16(t, (uint8_t *)t->tightBeforeBuf, w * h); - for (i = 0; i < palette->numColors; i++) { - ((uint16_t *)cl->afterEncBuf)[i] = - (uint16_t)palette->entry[i].listNode->rgb; - } + for (i = 0; i < t->paletteNumColors; i++) { + ((uint16_t *)t->tightAfterBuf)[i] = + (uint16_t)t->palette.entry[i].listNode->rgb; + } - memcpy(&cl->updateBuf[cl->ublen], cl->afterEncBuf, palette->numColors * 2); - cl->ublen += palette->numColors * 2; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, - 3 + palette->numColors * 2); - break; + memcpy(&t->updateBuf[*t->ublen], t->tightAfterBuf, + t->paletteNumColors * 2); + (*t->ublen) += t->paletteNumColors * 2; + t->bytessent += 3 + t->paletteNumColors * 2; + break; default: - return FALSE; /* Should never happen. */ - } + return FALSE; /* Should never happen. */ + } - return CompressData(cl, streamId, w * h, - tightConf[cl->tightCompressLevel].idxZlibLevel, - Z_DEFAULT_STRATEGY); + return CompressData(t, streamId, w * h, + tightConf[t->compressLevel].idxZlibLevel, + Z_DEFAULT_STRATEGY); } -static rfbBool -SendFullColorRect(rfbClientPtr cl, - int x, - int y, - int w, - int h) + +static rfbBool SendFullColorRect(threadparam *t, int x, int y, int w, int h) { - int streamId = 0; - int len; + int streamId = t->streamId; + int len; + rfbClientPtr cl = t->cl; #ifdef LIBVNCSERVER_HAVE_LIBPNG if (CanSendPngRect(cl, w, h)) { - return SendPngRect(cl, x, y, w, h); + return SendPngRect(t, x, y, w, h); } #endif - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - - if (tightConf[cl->tightCompressLevel].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 (cl->tightUsePixelFormat24) { - Pack24(cl, cl->beforeEncBuf, &cl->format, w * h); - len = 3; - } else - len = cl->format.bitsPerPixel / 8; - - return CompressData(cl, streamId, w * h * len, - tightConf[cl->tightCompressLevel].rawZlibLevel, - Z_DEFAULT_STRATEGY); + if (!CheckUpdateBuf(t, TIGHT_MIN_TO_COMPRESS + 1)) + return FALSE; + + if (t->nStreams > 0) { + t->streamId++; + if (t->streamId >= t->baseStreamId + t->nStreams) + t->streamId = t->baseStreamId; + } + + if (tightConf[t->compressLevel].rawZlibLevel == 0 || t->id > 3) + t->updateBuf[(*t->ublen)++] = (char)(rfbTightNoZlib << 4); + else + t->updateBuf[(*t->ublen)++] = streamId << 4; + t->bytessent++; + + if (t->usePixelFormat24) { + Pack24(t->tightBeforeBuf, &t->serverFormat, &cl->format, w * h); + len = 3; + } else + len = cl->format.bitsPerPixel / 8; + + return CompressData(t, streamId, w * h * len, + tightConf[t->compressLevel].rawZlibLevel, + Z_DEFAULT_STRATEGY); } -static rfbBool -CompressData(rfbClientPtr cl, - int streamId, - int dataLen, - int zlibLevel, - int zlibStrategy) -{ - z_streamp pz; - int err; - - if (dataLen < TIGHT_MIN_TO_COMPRESS) { - memcpy(&cl->updateBuf[cl->ublen], cl->beforeEncBuf, dataLen); - cl->ublen += dataLen; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, dataLen); - return TRUE; - } - - if (zlibLevel == 0) - return rfbSendCompressedDataTight(cl, cl->beforeEncBuf, dataLen); - - pz = &cl->zsStruct[streamId]; - - /* Initialize compression stream if needed. */ - if (!cl->zsActive[streamId]) { - pz->zalloc = Z_NULL; - pz->zfree = Z_NULL; - pz->opaque = Z_NULL; - err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS, - MAX_MEM_LEVEL, zlibStrategy); - if (err != Z_OK) - return FALSE; - - cl->zsActive[streamId] = TRUE; - cl->zsLevel[streamId] = zlibLevel; - } +static rfbBool CompressData(threadparam *t, int streamId, int dataLen, + int zlibLevel, int zlibStrategy) +{ + z_streamp pz; + int err; + rfbClientPtr cl = t->cl; + + if (dataLen < TIGHT_MIN_TO_COMPRESS) { + memcpy(&t->updateBuf[*t->ublen], t->tightBeforeBuf, dataLen); + (*t->ublen) += dataLen; + t->bytessent += dataLen; + return TRUE; + } + + /* Tight encoding has only a limited number of Zlib streams (4). The + streams must all be left open as long as the client is connected, or + performance suffers. Thus, multiple threads can't use the same Zlib + stream. We divide the pool of 4 evenly among the available threads (up + to the first 4 threads), and if each thread has more than one stream, it + cycles between them in a round-robin fashion. If we have more than 4 + threads, then threads 5 and beyond must encode their data without Zlib + compression. */ + if (zlibLevel == 0 || t->id > 3) + return SendCompressedData(t, t->tightBeforeBuf, dataLen); + + pz = &cl->zsStruct[streamId]; + + /* Initialize compression stream if needed. */ + if (!cl->zsActive[streamId]) { + pz->zalloc = Z_NULL; + pz->zfree = Z_NULL; + pz->opaque = Z_NULL; + + err = deflateInit2(pz, zlibLevel, Z_DEFLATED, MAX_WBITS, MAX_MEM_LEVEL, + zlibStrategy); + if (err != Z_OK) + return FALSE; + + cl->zsActive[streamId] = TRUE; + cl->zsLevel[streamId] = zlibLevel; + } + + /* Prepare buffer pointers. */ + pz->next_in = (Bytef *)t->tightBeforeBuf; + pz->avail_in = dataLen; + pz->next_out = (Bytef *)t->tightAfterBuf; + pz->avail_out = t->tightAfterBufSize; + + /* Change compression parameters if needed. */ + if (zlibLevel != cl->zsLevel[streamId]) { + if (deflateParams(pz, zlibLevel, zlibStrategy) != Z_OK) + return FALSE; + cl->zsLevel[streamId] = zlibLevel; + } + + /* Actual compression. */ + if (deflate(pz, Z_SYNC_FLUSH) != Z_OK || pz->avail_in != 0 || + pz->avail_out == 0) + return FALSE; + + return SendCompressedData(t, t->tightAfterBuf, + t->tightAfterBufSize - pz->avail_out); +} - /* Prepare buffer pointers. */ - pz->next_in = (Bytef *)cl->beforeEncBuf; - pz->avail_in = dataLen; - pz->next_out = (Bytef *)cl->afterEncBuf; - pz->avail_out = cl->afterEncBufSize; - /* Change compression parameters if needed. */ - if (zlibLevel != cl->zsLevel[streamId]) { - if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) { - return FALSE; - } - cl->zsLevel[streamId] = zlibLevel; - } - - /* Actual compression. */ - if (deflate(pz, Z_SYNC_FLUSH) != Z_OK || - pz->avail_in != 0 || pz->avail_out == 0) { - return FALSE; +static rfbBool SendCompressedData(threadparam *t, char *buf, int compressedLen) +{ + int i, portionLen; + + t->updateBuf[(*t->ublen)++] = compressedLen & 0x7F; + t->bytessent++; + if (compressedLen > 0x7F) { + t->updateBuf[(*t->ublen) - 1] |= 0x80; + t->updateBuf[(*t->ublen)++] = compressedLen >> 7 & 0x7F; + t->bytessent++; + if (compressedLen > 0x3FFF) { + t->updateBuf[(*t->ublen) - 1] |= 0x80; + t->updateBuf[(*t->ublen)++] = compressedLen >> 14 & 0xFF; + t->bytessent++; } - - return rfbSendCompressedDataTight(cl, cl->afterEncBuf, - cl->afterEncBufSize - pz->avail_out); + } + + portionLen = UPDATE_BUF_SIZE; + for (i = 0; i < compressedLen; i += portionLen) { + if (i + portionLen > compressedLen) + portionLen = compressedLen - i; + if (!CheckUpdateBuf(t, portionLen)) + return FALSE; + memcpy(&t->updateBuf[*t->ublen], &buf[i], portionLen); + (*t->ublen) += portionLen; + } + t->bytessent += compressedLen; + return TRUE; } -rfbBool rfbSendCompressedDataTight(rfbClientPtr cl, char *buf, - int compressedLen) + +rfbBool rfbSendCompressedDataTight(rfbClientPtr cl, char *buf, int compressedLen) { - int i, portionLen; - - cl->updateBuf[cl->ublen++] = compressedLen & 0x7F; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); - if (compressedLen > 0x7F) { - cl->updateBuf[cl->ublen-1] |= 0x80; - cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); - if (compressedLen > 0x3FFF) { - cl->updateBuf[cl->ublen-1] |= 0x80; - cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF; - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); - } - } + if (!cl->threadInit) { + InitThreads(cl); + if (!cl->threadInit) return FALSE; + } - portionLen = UPDATE_BUF_SIZE; - for (i = 0; i < compressedLen; i += portionLen) { - if (i + portionLen > compressedLen) { - portionLen = compressedLen - i; - } - if (cl->ublen + portionLen > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } - memcpy(&cl->updateBuf[cl->ublen], &buf[i], portionLen); - cl->ublen += portionLen; - } - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, compressedLen); + threadparam *tparam = (threadparam*) cl->tightTJ; + tparam[0].cl = cl; - return TRUE; + return SendCompressedData(&tparam[0], buf, compressedLen); } @@ -1092,212 +1298,208 @@ rfbBool rfbSendCompressedDataTight(rfbClientPtr cl, char *buf, * Code to determine how many different colors used in rectangle. */ -static void -FillPalette8(palettePtr palette, rfbClientPtr cl, int count) +static void FillPalette8(threadparam *t, int count) { - uint8_t *data = (uint8_t *)cl->beforeEncBuf; - uint8_t c0, c1; - int i, n0, n1; - - palette->numColors = 0; - - c0 = data[0]; - for (i = 1; i < count && data[i] == c0; i++); - if (i == count) { - palette->numColors = 1; - return; /* Solid rectangle */ - } - - if (palette->maxColors < 2) - return; - - n0 = i; - c1 = data[i]; - n1 = 0; - for (i++; i < count; i++) { - if (data[i] == c0) { - n0++; - } else if (data[i] == c1) { - n1++; - } else - break; - } - if (i == count) { - if (n0 > n1) { - palette->monoBackground = (uint32_t)c0; - palette->monoForeground = (uint32_t)c1; - } else { - palette->monoBackground = (uint32_t)c1; - palette->monoForeground = (uint32_t)c0; - } - palette->numColors = 2; /* Two colors */ + uint8_t *data = (uint8_t *)t->tightBeforeBuf; + uint8_t c0, c1; + int i, n0, n1; + + t->paletteNumColors = 0; + + c0 = data[0]; + for (i = 1; i < count && data[i] == c0; i++); + if (i == count) { + t->paletteNumColors = 1; + return; /* Solid rectangle */ + } + + if (t->paletteMaxColors < 2) + return; + + n0 = i; + c1 = data[i]; + n1 = 0; + for (i++; i < count; i++) { + if (data[i] == c0) + n0++; + else if (data[i] == c1) + n1++; + else + break; + } + if (i == count) { + if (n0 > n1) { + t->monoBackground = (uint32_t)c0; + t->monoForeground = (uint32_t)c1; + } else { + t->monoBackground = (uint32_t)c1; + t->monoForeground = (uint32_t)c0; } + t->paletteNumColors = 2; /* Two colors */ + } } -#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ - \ -static void \ -FillPalette##bpp(palettePtr palette, rfbClientPtr cl, int count) { \ - uint##bpp##_t *data = (uint##bpp##_t *)cl->beforeEncBuf; \ - uint##bpp##_t c0, c1, ci; \ - int i, n0, n1, ni; \ - \ - c0 = data[0]; \ - for (i = 1; i < count && data[i] == c0; i++); \ - if (i >= count) { \ - palette->numColors = 1; /* Solid rectangle */ \ - return; \ - } \ - \ - if (palette->maxColors < 2) { \ - palette->numColors = 0; /* Full-color encoding preferred */ \ - return; \ - } \ - \ - n0 = i; \ - c1 = data[i]; \ - n1 = 0; \ - for (i++; i < count; i++) { \ - ci = data[i]; \ - if (ci == c0) { \ - n0++; \ - } else if (ci == c1) { \ - n1++; \ - } else \ - break; \ - } \ - if (i >= count) { \ - if (n0 > n1) { \ - palette->monoBackground = (uint32_t)c0; \ - palette->monoForeground = (uint32_t)c1; \ - } else { \ - palette->monoBackground = (uint32_t)c1; \ - palette->monoForeground = (uint32_t)c0; \ - } \ - palette->numColors = 2; /* Two colors */ \ - return; \ - } \ - \ - PaletteReset(palette); \ - PaletteInsert (palette, c0, (uint32_t)n0, bpp); \ - PaletteInsert (palette, c1, (uint32_t)n1, bpp); \ - \ - ni = 1; \ - for (i++; i < count; i++) { \ - if (data[i] == ci) { \ - ni++; \ - } else { \ - if (!PaletteInsert (palette, ci, (uint32_t)ni, bpp)) \ - return; \ - ci = data[i]; \ - ni = 1; \ - } \ - } \ - PaletteInsert (palette, ci, (uint32_t)ni, bpp); \ +#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ + \ +static void FillPalette##bpp(threadparam *t, int count) \ +{ \ + uint##bpp##_t *data = (uint##bpp##_t *)t->tightBeforeBuf; \ + uint##bpp##_t c0, c1, ci; \ + int i, n0, n1, ni; \ + \ + c0 = data[0]; \ + for (i = 1; i < count && data[i] == c0; i++); \ + if (i >= count) { \ + t->paletteNumColors = 1; /* Solid rectangle */ \ + return; \ + } \ + \ + if (t->paletteMaxColors < 2) { \ + t->paletteNumColors = 0; /* Full-color encoding preferred */ \ + return; \ + } \ + \ + n0 = i; \ + c1 = data[i]; \ + n1 = 0; \ + for (i++; i < count; i++) { \ + ci = data[i]; \ + if (ci == c0) \ + n0++; \ + else if (ci == c1) \ + n1++; \ + else \ + break; \ + } \ + if (i >= count) { \ + if (n0 > n1) { \ + t->monoBackground = (uint32_t)c0; \ + t->monoForeground = (uint32_t)c1; \ + } else { \ + t->monoBackground = (uint32_t)c1; \ + t->monoForeground = (uint32_t)c0; \ + } \ + t->paletteNumColors = 2; /* Two colors */ \ + return; \ + } \ + \ + PaletteReset(t); \ + PaletteInsert(t, c0, (uint32_t)n0, bpp); \ + PaletteInsert(t, c1, (uint32_t)n1, bpp); \ + \ + ni = 1; \ + for (i++; i < count; i++) { \ + if (data[i] == ci) { \ + ni++; \ + } else { \ + if (!PaletteInsert(t, ci, (uint32_t)ni, bpp)) \ + return; \ + ci = data[i]; \ + ni = 1; \ + } \ + } \ + PaletteInsert(t, ci, (uint32_t)ni, bpp); \ } DEFINE_FILL_PALETTE_FUNCTION(16) DEFINE_FILL_PALETTE_FUNCTION(32) -#define DEFINE_FAST_FILL_PALETTE_FUNCTION(bpp) \ - \ -static void \ -FastFillPalette##bpp(palettePtr palette, 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) { \ - palette->numColors = 1; /* Solid rectangle */ \ - return; \ - } \ - if (palette->maxColors < 2) { \ - palette->numColors = 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) { \ - palette->monoBackground = (uint32_t)c0t; \ - palette->monoForeground = (uint32_t)c1t; \ - } else { \ - palette->monoBackground = (uint32_t)c1t; \ - palette->monoForeground = (uint32_t)c0t; \ - } \ - palette->numColors = 2; /* Two colors */ \ - return; \ - } \ - \ - PaletteReset(palette); \ - PaletteInsert (palette, c0t, (uint32_t)n0, bpp); \ - PaletteInsert (palette, 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 (palette, 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 (palette, cit, (uint32_t)ni, bpp); \ + +#define DEFINE_FAST_FILL_PALETTE_FUNCTION(bpp) \ + \ +static void FastFillPalette##bpp(threadparam *t, 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; \ + rfbClientPtr cl = t->cl; \ + \ + if (cl->translateFn != rfbTranslateNone) { \ + mask = t->serverFormat.redMax << t->serverFormat.redShift; \ + mask |= t->serverFormat.greenMax << t->serverFormat.greenShift; \ + mask |= t->serverFormat.blueMax << t->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) { \ + t->paletteNumColors = 1; /* Solid rectangle */ \ + return; \ + } \ + if (t->paletteMaxColors < 2) { \ + t->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, &t->serverFormat, \ + &cl->format, (char *)&c0, (char *)&c0t, bpp / 8, \ + 1, 1); \ + (*cl->translateFn) (cl->translateLookupTable, &t->serverFormat, \ + &cl->format, (char *)&c1, (char *)&c1t, bpp / 8, \ + 1, 1); \ + if (j2 >= h) { \ + if (n0 > n1) { \ + t->monoBackground = (uint32_t)c0t; \ + t->monoForeground = (uint32_t)c1t; \ + } else { \ + t->monoBackground = (uint32_t)c1t; \ + t->monoForeground = (uint32_t)c0t; \ + } \ + t->paletteNumColors = 2; /* Two colors */ \ + return; \ + } \ + \ + PaletteReset(t); \ + PaletteInsert(t, c0t, (uint32_t)n0, bpp); \ + PaletteInsert(t, 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, &t->serverFormat, \ + &cl->format, (char *)&ci, (char *)&cit, bpp / 8, \ + 1, 1); \ + if (!PaletteInsert(t, cit, (uint32_t)ni, bpp)) \ + return; \ + ci = data[j * pitch + i] & mask; \ + ni = 1; \ + } \ + } \ + i2 = 0; \ + } \ + \ + (*cl->translateFn) (cl->translateLookupTable, &t->serverFormat, \ + &cl->format, (char *)&ci, (char *)&cit, bpp / 8, \ + 1, 1); \ + PaletteInsert(t, cit, (uint32_t)ni, bpp); \ } DEFINE_FAST_FILL_PALETTE_FUNCTION(16) @@ -1312,78 +1514,72 @@ DEFINE_FAST_FILL_PALETTE_FUNCTION(32) #define HASH_FUNC32(rgb) ((int)((((rgb) >> 16) + ((rgb) >> 8)) & 0xFF)) -static void -PaletteReset(palettePtr palette) +static void PaletteReset(threadparam *t) { - palette->numColors = 0; - memset(palette->hash, 0, 256 * sizeof(COLOR_LIST *)); + t->paletteNumColors = 0; + memset(t->palette.hash, 0, 256 * sizeof(COLOR_LIST *)); } -static int -PaletteInsert(palettePtr palette, - uint32_t rgb, - int numPixels, - int bpp) +static int PaletteInsert(threadparam *t, uint32_t rgb, int numPixels, int bpp) { - COLOR_LIST *pnode; - COLOR_LIST *prev_pnode = NULL; - int hash_key, idx, new_idx, count; - - hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb); - - pnode = palette->hash[hash_key]; - - while (pnode != NULL) { - if (pnode->rgb == rgb) { - /* Such palette entry already exists. */ - new_idx = idx = pnode->idx; - count = palette->entry[idx].numPixels + numPixels; - if (new_idx && palette->entry[new_idx-1].numPixels < count) { - do { - palette->entry[new_idx] = palette->entry[new_idx-1]; - palette->entry[new_idx].listNode->idx = new_idx; - new_idx--; - } - while (new_idx && palette->entry[new_idx-1].numPixels < count); - palette->entry[new_idx].listNode = pnode; - pnode->idx = new_idx; - } - palette->entry[new_idx].numPixels = count; - return palette->numColors; - } - prev_pnode = pnode; - pnode = pnode->next; + COLOR_LIST *pnode; + COLOR_LIST *prev_pnode = NULL; + int hash_key, idx, new_idx, count; + + hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb); + + pnode = t->palette.hash[hash_key]; + + while (pnode != NULL) { + if (pnode->rgb == rgb) { + /* Such palette entry already exists. */ + new_idx = idx = pnode->idx; + count = t->palette.entry[idx].numPixels + numPixels; + if (new_idx && t->palette.entry[new_idx - 1].numPixels < count) { + do { + t->palette.entry[new_idx] = t->palette.entry[new_idx - 1]; + t->palette.entry[new_idx].listNode->idx = new_idx; + new_idx--; + } while (new_idx && t->palette.entry[new_idx - 1].numPixels < count); + t->palette.entry[new_idx].listNode = pnode; + pnode->idx = new_idx; + } + t->palette.entry[new_idx].numPixels = count; + return t->paletteNumColors; } - - /* Check if palette is full. */ - if (palette->numColors == 256 || palette->numColors == palette->maxColors) { - palette->numColors = 0; - return 0; - } - - /* Move palette entries with lesser pixel counts. */ - for ( idx = palette->numColors; - idx > 0 && palette->entry[idx-1].numPixels < numPixels; - idx-- ) { - palette->entry[idx] = palette->entry[idx-1]; - palette->entry[idx].listNode->idx = idx; - } - - /* Add new palette entry into the freed slot. */ - pnode = &palette->list[palette->numColors]; - if (prev_pnode != NULL) { - prev_pnode->next = pnode; - } else { - palette->hash[hash_key] = pnode; - } - pnode->next = NULL; - pnode->idx = idx; - pnode->rgb = rgb; - palette->entry[idx].listNode = pnode; - palette->entry[idx].numPixels = numPixels; - - return (++palette->numColors); + prev_pnode = pnode; + pnode = pnode->next; + } + + /* Check if palette is full. */ + if (t->paletteNumColors == 256 || + t->paletteNumColors == t->paletteMaxColors) { + t->paletteNumColors = 0; + return 0; + } + + /* Move palette entries with lesser pixel counts. */ + for (idx = t->paletteNumColors; + idx > 0 && t->palette.entry[idx - 1].numPixels < numPixels; + idx--) { + t->palette.entry[idx] = t->palette.entry[idx - 1]; + t->palette.entry[idx].listNode->idx = idx; + } + + /* Add new palette entry into the freed slot. */ + pnode = &t->palette.list[t->paletteNumColors]; + if (prev_pnode != NULL) + prev_pnode->next = pnode; + else + t->palette.hash[hash_key] = pnode; + pnode->next = NULL; + pnode->idx = idx; + pnode->rgb = rgb; + t->palette.entry[idx].listNode = pnode; + t->palette.entry[idx].numPixels = numPixels; + + return ++(t->paletteNumColors); } @@ -1393,33 +1589,30 @@ PaletteInsert(palettePtr palette, * Color components assumed to be byte-aligned. */ -static void Pack24(rfbClientPtr cl, - char *buf, - rfbPixelFormat *fmt, - int count) +static void Pack24(char *buf, rfbPixelFormat *serverFmt, rfbPixelFormat *fmt, int count) { - uint32_t *buf32; - uint32_t pix; - int r_shift, g_shift, b_shift; - - buf32 = (uint32_t *)buf; - - if (!cl->screen->serverFormat.bigEndian == !fmt->bigEndian) { - r_shift = fmt->redShift; - g_shift = fmt->greenShift; - b_shift = fmt->blueShift; - } else { - r_shift = 24 - fmt->redShift; - g_shift = 24 - fmt->greenShift; - b_shift = 24 - fmt->blueShift; - } - - while (count--) { - pix = *buf32++; - *buf++ = (char)(pix >> r_shift); - *buf++ = (char)(pix >> g_shift); - *buf++ = (char)(pix >> b_shift); - } + uint32_t *buf32; + uint32_t pix; + int r_shift, g_shift, b_shift; + + buf32 = (uint32_t *)buf; + + if (!serverFmt->bigEndian == !fmt->bigEndian) { + r_shift = fmt->redShift; + g_shift = fmt->greenShift; + b_shift = fmt->blueShift; + } else { + r_shift = 24 - fmt->redShift; + g_shift = 24 - fmt->greenShift; + b_shift = 24 - fmt->blueShift; + } + + while (count--) { + pix = *buf32++; + *buf++ = (char)(pix >> r_shift); + *buf++ = (char)(pix >> g_shift); + *buf++ = (char)(pix >> b_shift); + } } @@ -1427,89 +1620,86 @@ static void Pack24(rfbClientPtr cl, * Converting truecolor samples into palette indices. */ -#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \ - \ -static void \ -EncodeIndexedRect##bpp(palettePtr palette, uint8_t *buf, int count) { \ - COLOR_LIST *pnode; \ - uint##bpp##_t *src; \ - uint##bpp##_t rgb; \ - int rep = 0; \ - \ - src = (uint##bpp##_t *) buf; \ - \ - while (count--) { \ - rgb = *src++; \ - while (count && *src == rgb) { \ - rep++, src++, count--; \ - } \ - pnode = palette->hash[HASH_FUNC##bpp(rgb)]; \ - while (pnode != NULL) { \ - if ((uint##bpp##_t)pnode->rgb == rgb) { \ - *buf++ = (uint8_t)pnode->idx; \ - while (rep) { \ - *buf++ = (uint8_t)pnode->idx; \ - rep--; \ - } \ - break; \ - } \ - pnode = pnode->next; \ - } \ - } \ +#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \ + \ +static void EncodeIndexedRect##bpp(threadparam *t, uint8_t *buf, int count) \ +{ \ + COLOR_LIST *pnode; \ + uint##bpp##_t *src; \ + uint##bpp##_t rgb; \ + int rep = 0; \ + \ + src = (uint##bpp##_t *)buf; \ + \ + while (count--) { \ + rgb = *src++; \ + while (count && *src == rgb) \ + rep++, src++, count--; \ + pnode = t->palette.hash[HASH_FUNC##bpp(rgb)]; \ + while (pnode != NULL) { \ + if ((uint##bpp##_t)pnode->rgb == rgb) { \ + *buf++ = (uint8_t)pnode->idx; \ + while (rep) { \ + *buf++ = (uint8_t)pnode->idx; \ + rep--; \ + } \ + break; \ + } \ + pnode = pnode->next; \ + } \ + } \ } DEFINE_IDX_ENCODE_FUNCTION(16) DEFINE_IDX_ENCODE_FUNCTION(32) -#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ - \ -static void \ -EncodeMonoRect##bpp(uint8_t *buf, int w, int h, uint32_t monoBackground) { \ - uint##bpp##_t *ptr; \ - uint##bpp##_t bg; \ - unsigned int value, mask; \ - int aligned_width; \ - int x, y, bg_bits; \ - \ - ptr = (uint##bpp##_t *) buf; \ - bg = (uint##bpp##_t) monoBackground; \ - aligned_width = w - w % 8; \ - \ - for (y = 0; y < h; y++) { \ - for (x = 0; x < aligned_width; x += 8) { \ - for (bg_bits = 0; bg_bits < 8; bg_bits++) { \ - if (*ptr++ != bg) \ - break; \ - } \ - if (bg_bits == 8) { \ - *buf++ = 0; \ - continue; \ - } \ - mask = 0x80 >> bg_bits; \ - value = mask; \ - for (bg_bits++; bg_bits < 8; bg_bits++) { \ - mask >>= 1; \ - if (*ptr++ != bg) { \ - value |= mask; \ - } \ - } \ - *buf++ = (uint8_t)value; \ - } \ - \ - mask = 0x80; \ - value = 0; \ - if (x >= w) \ - continue; \ - \ - for (; x < w; x++) { \ - if (*ptr++ != bg) { \ - value |= mask; \ - } \ - mask >>= 1; \ - } \ - *buf++ = (uint8_t)value; \ - } \ +#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ + \ +static void EncodeMonoRect##bpp(threadparam *t, uint8_t *buf, int w, int h) \ +{ \ + uint##bpp##_t *ptr; \ + uint##bpp##_t bg; \ + unsigned int value, mask; \ + int aligned_width; \ + int x, y, bg_bits; \ + \ + ptr = (uint##bpp##_t *)buf; \ + bg = (uint##bpp##_t)t->monoBackground; \ + aligned_width = w - w % 8; \ + \ + for (y = 0; y < h; y++) { \ + for (x = 0; x < aligned_width; x += 8) { \ + for (bg_bits = 0; bg_bits < 8; bg_bits++) { \ + if (*ptr++ != bg) \ + break; \ + } \ + if (bg_bits == 8) { \ + *buf++ = 0; \ + continue; \ + } \ + mask = 0x80 >> bg_bits; \ + value = mask; \ + for (bg_bits++; bg_bits < 8; bg_bits++) { \ + mask >>= 1; \ + if (*ptr++ != bg) \ + value |= mask; \ + } \ + *buf++ = (uint8_t)value; \ + } \ + \ + mask = 0x80; \ + value = 0; \ + if (x >= w) \ + continue; \ + \ + for (; x < w; x++) { \ + if (*ptr++ != bg) \ + value |= mask; \ + mask >>= 1; \ + } \ + *buf++ = (uint8_t)value; \ + } \ } DEFINE_MONO_ENCODE_FUNCTION(8) @@ -1517,149 +1707,172 @@ DEFINE_MONO_ENCODE_FUNCTION(16) DEFINE_MONO_ENCODE_FUNCTION(32) +#define DEFINE_RGB_CONVERT_FUNCTION(bpp) \ + \ +static unsigned char *ConvertRGB##bpp(rfbClientPtr cl, int paddedWidthInBytes, rfbPixelFormat *serverFormat, int x, int y, \ + int w, int h) \ +{ \ + uint##bpp##_t *srcptr, pix; \ + unsigned char *dst, *tmpbuf; \ + int inRed, inGreen, inBlue, i, j, ps = bpp / 8; \ + \ + tmpbuf = (unsigned char *)malloc((size_t)w * h * 3); \ + if (!tmpbuf) \ + return NULL; \ + srcptr = (uint##bpp##_t *)&cl->scaledScreen->frameBuffer[y * paddedWidthInBytes + x * ps]; \ + dst = tmpbuf; \ + for (j = 0; j < h; j++) { \ + uint##bpp##_t *srcptr2 = srcptr; \ + unsigned char *dst2 = dst; \ + for (i = 0; i < w; i++) { \ + pix = *srcptr2++; \ + inRed = \ + (int)(pix >> serverFormat->redShift & serverFormat->redMax); \ + inGreen = \ + (int)(pix >> serverFormat->greenShift & serverFormat->greenMax); \ + inBlue = \ + (int)(pix >> serverFormat->blueShift & serverFormat->blueMax); \ + *dst2++ = (uint8_t)((inRed * 255 + serverFormat->redMax / 2) / \ + serverFormat->redMax); \ + *dst2++ = (uint8_t)((inGreen * 255 + serverFormat->greenMax / 2) / \ + serverFormat->greenMax); \ + *dst2++ = (uint8_t)((inBlue * 255 + serverFormat->blueMax / 2) / \ + serverFormat->blueMax); \ + } \ + srcptr += paddedWidthInBytes / ps; \ + dst += w * 3; \ + } \ + return tmpbuf; \ +} + +DEFINE_RGB_CONVERT_FUNCTION(16) +DEFINE_RGB_CONVERT_FUNCTION(32) + + /* * JPEG compression stuff. */ -static rfbBool -SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) +static rfbBool SendJpegRect(threadparam *t, int x, int y, int w, int h, + int quality) { - unsigned char *srcbuf; - int ps = cl->screen->serverFormat.bitsPerPixel / 8; - int subsamp = subsampLevel2tjsubsamp[cl->turboSubsampLevel]; - unsigned long size = 0; - int flags = 0, pitch; - unsigned char *tmpbuf = NULL; - - if (cl->screen->serverFormat.bitsPerPixel == 8) - return SendFullColorRect(cl, x, y, w, h); - - if (ps < 2) { - rfbLog("Error: JPEG requires 16-bit, 24-bit, or 32-bit pixel format.\n"); - return 0; - } - if (!cl->tightTJ) { - if ((cl->tightTJ = tjInitCompress()) == NULL) { - rfbLog("JPEG Error: %s\n", tjGetErrorStr()); - return 0; - } + unsigned char *srcbuf; + int ps = t->serverFormat.bitsPerPixel / 8; + int subsamp = subsampLevel2tjsubsamp[t->subsampLevel]; + unsigned long size = 0; + int flags = 0, pitch; + unsigned char *tmpbuf = NULL; + unsigned long jpegDstDataLen; + rfbClientPtr cl = t->cl; + + if (t->serverFormat.bitsPerPixel == 8) { + return SendFullColorRect(t, x, y, w, h); + } + + if (ps < 2) { + rfbLog("Error: JPEG requires 16-bit, 24-bit, or 32-bit pixel format.\n"); + return FALSE; + } + if (!t->j) { + if ((t->j = tjInitCompress()) == NULL) { + rfbLog("JPEG Error: %s\n", tjGetErrorStr()); + return FALSE; } + } - if (!cl->afterEncBuf || cl->afterEncBufSize < TJBUFSIZE(w, h)) { - if (cl->afterEncBuf == NULL) - cl->afterEncBuf = (char *)malloc(TJBUFSIZE(w, h)); - else { - char *reallocedAfterEncBuf = (char *)realloc(cl->afterEncBuf, TJBUFSIZE(w, h)); - if (!reallocedAfterEncBuf) return FALSE; - cl->afterEncBuf = reallocedAfterEncBuf; - } - if (!cl->afterEncBuf) - { - rfbLog("SendJpegRect: failed to allocate memory\n"); - return FALSE; - } - cl->afterEncBufSize = TJBUFSIZE(w, h); - } - - if (ps == 2) { - uint16_t *srcptr, pix; - unsigned char *dst; - int inRed, inGreen, inBlue, i, j; - - if((tmpbuf = (unsigned char *)malloc((size_t)w * h * 3)) == NULL) - rfbLog("Memory allocation failure!\n"); - srcptr = (uint16_t *)&cl->scaledScreen->frameBuffer - [y * cl->scaledScreen->paddedWidthInBytes + x * ps]; - dst = tmpbuf; - for(j = 0; j < h; j++) { - uint16_t *srcptr2 = srcptr; - unsigned char *dst2 = dst; - for (i = 0; i < w; i++) { - pix = *srcptr2++; - inRed = (int) (pix >> cl->screen->serverFormat.redShift - & cl->screen->serverFormat.redMax); - inGreen = (int) (pix >> cl->screen->serverFormat.greenShift - & cl->screen->serverFormat.greenMax); - inBlue = (int) (pix >> cl->screen->serverFormat.blueShift - & cl->screen->serverFormat.blueMax); - *dst2++ = (uint8_t)((inRed * 255 - + cl->screen->serverFormat.redMax / 2) - / cl->screen->serverFormat.redMax); - *dst2++ = (uint8_t)((inGreen * 255 - + cl->screen->serverFormat.greenMax / 2) - / cl->screen->serverFormat.greenMax); - *dst2++ = (uint8_t)((inBlue * 255 - + cl->screen->serverFormat.blueMax / 2) - / cl->screen->serverFormat.blueMax); - } - srcptr += cl->scaledScreen->paddedWidthInBytes / ps; - dst += w * 3; - } - srcbuf = tmpbuf; - pitch = w * 3; - ps = 3; - } else { - if (cl->screen->serverFormat.bigEndian && ps == 4) - flags |= TJ_ALPHAFIRST; - if (cl->screen->serverFormat.redShift == 16 - && cl->screen->serverFormat.blueShift == 0) - flags |= TJ_BGR; - if (cl->screen->serverFormat.bigEndian) - flags ^= TJ_BGR; - pitch = cl->scaledScreen->paddedWidthInBytes; - srcbuf = (unsigned char *)&cl->scaledScreen->frameBuffer - [y * pitch + x * ps]; - } - - if (tjCompress(cl->tightTJ, srcbuf, w, pitch, h, ps, (unsigned char *)cl->afterEncBuf, - &size, subsamp, quality, flags) == -1) { - rfbLog("JPEG Error: %s\n", tjGetErrorStr()); - if (tmpbuf) { - free(tmpbuf); - tmpbuf = NULL; - } - return 0; + if (t->tightAfterBufSize < TJBUFSIZE(w, h)) { + if (t->tightAfterBuf == NULL) + t->tightAfterBuf = (char *)malloc(TJBUFSIZE(w, h)); + else + t->tightAfterBuf = (char *)realloc(t->tightAfterBuf, TJBUFSIZE(w, h)); + if (!t->tightAfterBuf) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + return FALSE; } - - if (tmpbuf) { - free(tmpbuf); - tmpbuf = NULL; + t->tightAfterBufSize = TJBUFSIZE(w, h); + } + + if (ps == 2) { + tmpbuf = ConvertRGB16(cl, t->paddedWidthInBytes, &t->serverFormat, x, y, w, h); + if (!tmpbuf) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + return FALSE; } - - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; + srcbuf = tmpbuf; + pitch = w * 3; + ps = 3; + } else if (t->serverFormat.depth == 30) { + tmpbuf = ConvertRGB32(cl, t->paddedWidthInBytes, &t->serverFormat, x, y, w, h); + if (!tmpbuf) { + rfbLog("Memory allocation failure! %s\n", strerror(errno)); + return FALSE; } + srcbuf = tmpbuf; + pitch = w * 3; + ps = 3; + } else { + if (t->serverFormat.bigEndian && ps == 4) flags |= TJ_ALPHAFIRST; + if (t->serverFormat.redShift == 16 && t->serverFormat.blueShift == 0) + flags |= TJ_BGR; + if (t->serverFormat.bigEndian) flags ^= TJ_BGR; + srcbuf = (unsigned char *) + &cl->scaledScreen->frameBuffer[y * t->paddedWidthInBytes + x * ps]; + pitch = t->paddedWidthInBytes; + } + + if (tjCompress(t->j, srcbuf, w, pitch, h, ps, + (unsigned char *)t->tightAfterBuf, &size, subsamp, quality, + flags) == -1) { + rfbLog("JPEG Error: %s\n", tjGetErrorStr()); + free(tmpbuf); tmpbuf = NULL; + return FALSE; + } + jpegDstDataLen = (int)size; + + free(tmpbuf); tmpbuf = NULL; + + if (!CheckUpdateBuf(t, TIGHT_MIN_TO_COMPRESS + 1)) + return FALSE; + + t->updateBuf[(*t->ublen)++] = (char)(rfbTightJpeg << 4); + t->bytessent++; + + return SendCompressedData(t, t->tightAfterBuf, jpegDstDataLen); +} - cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); +/* + * PNG compression stuff. + */ - return rfbSendCompressedDataTight(cl, cl->afterEncBuf, (int)size); -} +#ifdef LIBVNCSERVER_HAVE_LIBPNG static void -PrepareRowForImg(rfbClientPtr cl, +PrepareRowForImg(threadparam *t, + int paddedWidthInBytes, + rfbPixelFormat *serverFormat, uint8_t *dst, int x, int y, int count) { - if (cl->screen->serverFormat.bitsPerPixel == 32) { - if ( cl->screen->serverFormat.redMax == 0xFF && - cl->screen->serverFormat.greenMax == 0xFF && - cl->screen->serverFormat.blueMax == 0xFF ) { - PrepareRowForImg24(cl, dst, x, y, count); + if (serverFormat->bitsPerPixel == 32) { + if ( serverFormat->redMax == 0xFF && + serverFormat->greenMax == 0xFF && + serverFormat->blueMax == 0xFF ) { + PrepareRowForImg24(t, paddedWidthInBytes, serverFormat, dst, x, y, count); } else { - PrepareRowForImg32(cl, dst, x, y, count); + PrepareRowForImg32(t, paddedWidthInBytes, serverFormat, dst, x, y, count); } } else { /* 16 bpp assumed. */ - PrepareRowForImg16(cl, dst, x, y, count); + PrepareRowForImg16(t, paddedWidthInBytes, serverFormat, dst, x, y, count); } } static void -PrepareRowForImg24(rfbClientPtr cl, +PrepareRowForImg24(threadparam *t, + int paddedWidthInBytes, + rfbPixelFormat *serverFormat, uint8_t *dst, int x, int y, @@ -1669,55 +1882,49 @@ PrepareRowForImg24(rfbClientPtr cl, uint32_t pix; fbptr = (uint32_t *) - &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + x * 4]; + &t->cl->scaledScreen->frameBuffer[y * paddedWidthInBytes + x * 4]; while (count--) { pix = *fbptr++; - *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.redShift); - *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.greenShift); - *dst++ = (uint8_t)(pix >> cl->screen->serverFormat.blueShift); + *dst++ = (uint8_t)(pix >> serverFormat->redShift); + *dst++ = (uint8_t)(pix >> serverFormat->greenShift); + *dst++ = (uint8_t)(pix >> serverFormat->blueShift); } } -#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \ +#define DEFINE_GET_ROW_FUNCTION(bpp) \ \ static void \ -PrepareRowForImg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) { \ +PrepareRowForImg##bpp(threadparam *t, int paddedWidthInBytes, rfbPixelFormat *serverFormat, uint8_t *dst, int x, int y, int count) { \ uint##bpp##_t *fbptr; \ uint##bpp##_t pix; \ int inRed, inGreen, inBlue; \ \ fbptr = (uint##bpp##_t *) \ - &cl->scaledScreen->frameBuffer[y * cl->scaledScreen->paddedWidthInBytes + \ + &t->cl->scaledScreen->frameBuffer[y * paddedWidthInBytes + \ x * (bpp / 8)]; \ \ while (count--) { \ pix = *fbptr++; \ \ inRed = (int) \ - (pix >> cl->screen->serverFormat.redShift & cl->screen->serverFormat.redMax); \ + (pix >> serverFormat->redShift & serverFormat->redMax); \ inGreen = (int) \ - (pix >> cl->screen->serverFormat.greenShift & cl->screen->serverFormat.greenMax); \ + (pix >> serverFormat->greenShift & serverFormat->greenMax); \ inBlue = (int) \ - (pix >> cl->screen->serverFormat.blueShift & cl->screen->serverFormat.blueMax); \ + (pix >> serverFormat->blueShift & serverFormat->blueMax); \ \ - *dst++ = (uint8_t)((inRed * 255 + cl->screen->serverFormat.redMax / 2) / \ - cl->screen->serverFormat.redMax); \ - *dst++ = (uint8_t)((inGreen * 255 + cl->screen->serverFormat.greenMax / 2) / \ - cl->screen->serverFormat.greenMax); \ - *dst++ = (uint8_t)((inBlue * 255 + cl->screen->serverFormat.blueMax / 2) / \ - cl->screen->serverFormat.blueMax); \ + *dst++ = (uint8_t)((inRed * 255 + serverFormat->redMax / 2) / \ + serverFormat->redMax); \ + *dst++ = (uint8_t)((inGreen * 255 + serverFormat->greenMax / 2) / \ + serverFormat->greenMax); \ + *dst++ = (uint8_t)((inBlue * 255 + serverFormat->blueMax / 2) / \ + serverFormat->blueMax); \ } \ } -DEFINE_JPEG_GET_ROW_FUNCTION(16) -DEFINE_JPEG_GET_ROW_FUNCTION(32) - -/* - * PNG compression stuff. - */ - -#ifdef LIBVNCSERVER_HAVE_LIBPNG +DEFINE_GET_ROW_FUNCTION(16) +DEFINE_GET_ROW_FUNCTION(32) static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h) { if (cl->tightEncoding != rfbEncodingTightPng) { @@ -1741,10 +1948,10 @@ static void pngWriteData(png_structp png_ptr, png_bytep data, buffer_reserve(&vs->tight.png, vs->tight.png.offset + length); memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length); #endif - rfbClientPtr cl = png_get_io_ptr(png_ptr); - memcpy(cl->afterEncBuf + cl->tightPngDstDataLen, data, length); + threadparam *t = png_get_io_ptr(png_ptr); + memcpy(t->tightAfterBuf + t->tightPngDstDataLen, data, length); - cl->tightPngDstDataLen += length; + t->tightPngDstDataLen += length; } static void pngFlushData(png_structp png_ptr) @@ -1762,19 +1969,19 @@ static void pngFree(png_structp png_ptr, png_voidp ptr) free(ptr); } -static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h) { +static rfbBool SendPngRect(threadparam *t, int x, int y, int w, int h) { /* rfbLog(">> SendPngRect x:%d, y:%d, w:%d, h:%d\n", x, y, w, h); */ png_byte color_type; png_structp png_ptr; png_infop info_ptr; png_colorp png_palette = NULL; - int level = tightPngConf[cl->tightCompressLevel].png_zlib_level; - int filters = tightPngConf[cl->tightCompressLevel].png_filters; + int level = tightPngConf[t->cl->tightCompressLevel].png_zlib_level; + int filters = tightPngConf[t->cl->tightCompressLevel].png_filters; uint8_t *buf; int dy; - cl->tightPngDstDataLen = 0; + t->tightPngDstDataLen = 0; png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, NULL, pngMalloc, pngFree); @@ -1789,7 +1996,7 @@ static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h) { return FALSE; } - png_set_write_fn(png_ptr, (void *) cl, pngWriteData, pngFlushData); + png_set_write_fn(png_ptr, (void *) t, pngWriteData, pngFlushData); png_set_compression_level(png_ptr, level); png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters); @@ -1846,10 +2053,10 @@ static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h) { if (color_type == PNG_COLOR_TYPE_PALETTE) { memcpy(buf, vs->tight.tight.buffer + (dy * w), w); } else { - PrepareRowForImg(cl, buf, x, y + dy, w); + PrepareRowForImg(t, t->paddedWidthInBytes, &t->serverFormat buf, x, y + dy, w); } #else - PrepareRowForImg(cl, buf, x, y + dy, w); + PrepareRowForImg(t, t->paddedWidthInBytes, &t->serverFormat, buf, x, y + dy, w); #endif png_write_row(png_ptr, buf); } @@ -1863,15 +2070,12 @@ static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h) { /* done v */ - if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { - if (!rfbSendUpdateBuf(cl)) - return FALSE; - } + if (!CheckUpdateBuf(t, TIGHT_MIN_TO_COMPRESS + 1)) + return FALSE; - cl->updateBuf[cl->ublen++] = (char)(rfbTightPng << 4); - rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); + t->updateBuf[(*t->ublen)++] = (char)(rfbTightPng << 4); + ++t->bytessent; - /* rfbLog("<< SendPngRect\n"); */ - return rfbSendCompressedDataTight(cl, cl->afterEncBuf, cl->tightPngDstDataLen); + return SendCompressedData(t, t->tightAfterBuf, t->tightPngDstDataLen); } #endif