Actually, upon further inspection, what is now limiting the performance is the ComparingUpdateTracker. When I turn it off, CPU usage drops to what I would expect from the optimizations that were just made, but then the network usage goes way up, as if it's sending duplicate frames. Thus, the aggregate performance is worse, even though the server CPU performance is about the same as TurboVNC.
Can someone please explain what ComparingUpdateTracker does and why it exists? TightVNC didn't have anything like that. This is somewhat of a deep architectural issue with the way TigerVNC hooks into the X server, rather than an optimization issue, so I really need some insight from someone who was involved in the early days of the project. On 8/11/11 7:36 AM, DRC wrote: > Per the report linked to below, things are now performing pretty much > identically to TurboVNC at the low levels. At the high levels, I am > still seeing too much CPU overhead, and I suspect that a lot of it is > due to the ImageGetter class. I'm working on a way of accessing the > framebuffer directly (any sage advice is appreciated.) I will update > everyone once the high-level performance is also performing up to par. > This has been an incredibly difficult problem to solve. I really was > expecting to go in and do some tuning and be done, but so far I've had > to basically rewrite the encoder. > > > On 8/11/11 6:18 AM, dcomman...@users.sourceforge.net wrote: >> Revision: 4626 >> http://tigervnc.svn.sourceforge.net/tigervnc/?rev=4626&view=rev >> Author: dcommander >> Date: 2011-08-11 11:18:34 +0000 (Thu, 11 Aug 2011) >> >> Log Message: >> ----------- >> Ported encoding optimizations from TurboVNC. The changes to the Tight >> parameters were determined through extensive low-level profiling (see >> http://www.virtualgl.org/pmwiki/uploads/About/turbototiger.pdf). The other >> enhancements involved: (1) porting the solid subrectangle pre-computation >> code from TightVNC/TurboVNC (it makes a pretty big difference-- see report), >> (2) encapsulating the JPEG encoder in its own class (this eliminates a >> buffer copy, and the JPEG buffer is now set to a decent size where it >> shouldn't ever need to be paged or re-allocated, except in rare corner >> cases), (3) adding support for last rect. encoding (necessary to support the >> solid rectangle pre-computation enhancements. >> >> Modified Paths: >> -------------- >> trunk/common/rdr/MemOutStream.h >> trunk/common/rfb/CMakeLists.txt >> trunk/common/rfb/SMsgWriterV3.cxx >> trunk/common/rfb/TightEncoder.cxx >> trunk/common/rfb/TightEncoder.h >> trunk/common/rfb/VNCSConnectionST.cxx >> trunk/common/rfb/tightEncode.h >> >> Added Paths: >> ----------- >> trunk/common/rfb/JpegCompressor.cxx >> trunk/common/rfb/JpegCompressor.h >> >> Modified: trunk/common/rdr/MemOutStream.h >> =================================================================== >> --- trunk/common/rdr/MemOutStream.h 2011-08-11 09:14:54 UTC (rev 4625) >> +++ trunk/common/rdr/MemOutStream.h 2011-08-11 11:18:34 UTC (rev 4626) >> @@ -55,7 +55,7 @@ >> >> const void* data() { return (const void*)start; } >> >> - private: >> + protected: >> >> // overrun() either doubles the buffer or adds enough space for nItems >> of >> // size itemSize bytes. >> >> Modified: trunk/common/rfb/CMakeLists.txt >> =================================================================== >> --- trunk/common/rfb/CMakeLists.txt 2011-08-11 09:14:54 UTC (rev 4625) >> +++ trunk/common/rfb/CMakeLists.txt 2011-08-11 11:18:34 UTC (rev 4626) >> @@ -22,6 +22,7 @@ >> HTTPServer.cxx >> HextileDecoder.cxx >> HextileEncoder.cxx >> + JpegCompressor.cxx >> KeyRemapper.cxx >> LogWriter.cxx >> Logger.cxx >> >> Added: trunk/common/rfb/JpegCompressor.cxx >> =================================================================== >> --- trunk/common/rfb/JpegCompressor.cxx (rev 0) >> +++ trunk/common/rfb/JpegCompressor.cxx 2011-08-11 11:18:34 UTC (rev >> 4626) >> @@ -0,0 +1,216 @@ >> +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. >> + * Copyright (C) 2011 D. R. Commander. All Rights Reserved. >> + * >> + * This is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This software is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this software; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, >> + * USA. >> + */ >> + >> +#include <rfb/JpegCompressor.h> >> +#include <rdr/Exception.h> >> +#include <rfb/Rect.h> >> +#include <rfb/PixelFormat.h> >> + >> +using namespace rfb; >> + >> +// >> +// Error manager implmentation for the JPEG library >> +// >> + >> +static void >> +JpegErrorExit(j_common_ptr cinfo) >> +{ >> + JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err; >> + >> + (*cinfo->err->output_message)(cinfo); >> + longjmp(err->jmpBuffer, 1); >> +} >> + >> +static void >> +JpegOutputMessage(j_common_ptr cinfo) >> +{ >> + JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)cinfo->err; >> + >> + (*cinfo->err->format_message)(cinfo, err->lastError); >> +} >> + >> +// >> +// Destination manager implementation for the JPEG library. >> +// >> + >> +static void >> +JpegInitDestination(j_compress_ptr cinfo) >> +{ >> + JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest; >> + JpegCompressor *jc = dest->instance; >> + >> + jc->clear(); >> + dest->pub.next_output_byte = jc->getptr(); >> + dest->pub.free_in_buffer = jc->getend() - jc->getptr(); >> +} >> + >> +static boolean >> +JpegEmptyOutputBuffer(j_compress_ptr cinfo) >> +{ >> + JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest; >> + JpegCompressor *jc = dest->instance; >> + >> + jc->setptr(dest->pub.next_output_byte); >> + jc->overrun(jc->getend() - jc->getstart(), 1); >> + dest->pub.next_output_byte = jc->getptr(); >> + dest->pub.free_in_buffer = jc->getend() - jc->getptr(); >> + >> + return TRUE; >> +} >> + >> +static void >> +JpegTermDestination(j_compress_ptr cinfo) >> +{ >> + JPEG_DEST_MGR *dest = (JPEG_DEST_MGR *)cinfo->dest; >> + JpegCompressor *jc = dest->instance; >> + >> + jc->setptr(dest->pub.next_output_byte); >> +} >> + >> +JpegCompressor::JpegCompressor(int bufferLen) : MemOutStream(bufferLen) >> +{ >> + cinfo.err = jpeg_std_error(&err.pub); >> + snprintf(err.lastError, JMSG_LENGTH_MAX, "No error"); >> + err.pub.error_exit = JpegErrorExit; >> + err.pub.output_message = JpegOutputMessage; >> + >> + if(setjmp(err.jmpBuffer)) { >> + // this will execute if libjpeg has an error >> + throw rdr::Exception(err.lastError); >> + } >> + >> + jpeg_create_compress(&cinfo); >> + >> + dest.pub.init_destination = JpegInitDestination; >> + dest.pub.empty_output_buffer = JpegEmptyOutputBuffer; >> + dest.pub.term_destination = JpegTermDestination; >> + dest.instance = this; >> + cinfo.dest = (struct jpeg_destination_mgr *)&dest; >> +} >> + >> +JpegCompressor::~JpegCompressor(void) >> +{ >> + if(setjmp(err.jmpBuffer)) { >> + // this will execute if libjpeg has an error >> + return; >> + } >> + >> + jpeg_destroy_compress(&cinfo); >> +} >> + >> +void JpegCompressor::compress(rdr::U8 *buf, const Rect& r, >> + const PixelFormat& pf, int quality, JPEG_SUBSAMP subsamp) >> +{ >> + int w = r.width(); >> + int h = r.height(); >> + int pixelsize; >> + rdr::U8 *srcBuf = NULL; >> + bool srcBufIsTemp = false; >> + JSAMPROW *rowPointer = NULL; >> + >> + if(setjmp(err.jmpBuffer)) { >> + // this will execute if libjpeg has an error >> + jpeg_abort_compress(&cinfo); >> + if (srcBufIsTemp && srcBuf) delete[] srcBuf; >> + if (rowPointer) delete[] rowPointer; >> + throw rdr::Exception(err.lastError); >> + } >> + >> + cinfo.image_width = w; >> + cinfo.image_height = h; >> + cinfo.in_color_space = JCS_RGB; >> + pixelsize = 3; >> + >> +#ifdef JCS_EXTENSIONS >> + // Try to have libjpeg read directly from our native format >> + if(pf.is888()) { >> + int redShift, greenShift, blueShift; >> + >> + if(pf.bigEndian) { >> + redShift = 24 - pf.redShift; >> + greenShift = 24 - pf.greenShift; >> + blueShift = 24 - pf.blueShift; >> + } else { >> + redShift = pf.redShift; >> + greenShift = pf.greenShift; >> + blueShift = pf.blueShift; >> + } >> + >> + if(redShift == 0 && greenShift == 8 && blueShift == 16) >> + cinfo.in_color_space = JCS_EXT_RGBX; >> + if(redShift == 16 && greenShift == 8 && blueShift == 0) >> + cinfo.in_color_space = JCS_EXT_BGRX; >> + if(redShift == 24 && greenShift == 16 && blueShift == 8) >> + cinfo.in_color_space = JCS_EXT_XBGR; >> + if(redShift == 8 && greenShift == 16 && blueShift == 24) >> + cinfo.in_color_space = JCS_EXT_XRGB; >> + >> + if (cinfo.in_color_space != JCS_RGB) { >> + srcBuf = (rdr::U8 *)buf; >> + pixelsize = 4; >> + } >> + } >> +#endif >> + >> + if (cinfo.in_color_space == JCS_RGB) { >> + srcBuf = new rdr::U8[w * h * pixelsize]; >> + srcBufIsTemp = true; >> + pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w * h); >> + } >> + >> + cinfo.input_components = pixelsize; >> + >> + jpeg_set_defaults(&cinfo); >> + jpeg_set_quality(&cinfo, quality, TRUE); >> + if(quality >= 96) cinfo.dct_method = JDCT_ISLOW; >> + else cinfo.dct_method = JDCT_FASTEST; >> + >> + switch (subsamp) { >> + case SUBSAMP_420: >> + cinfo.comp_info[0].h_samp_factor = 2; >> + cinfo.comp_info[0].v_samp_factor = 2; >> + break; >> + case SUBSAMP_422: >> + cinfo.comp_info[0].h_samp_factor = 2; >> + cinfo.comp_info[0].v_samp_factor = 1; >> + break; >> + default: >> + cinfo.comp_info[0].h_samp_factor = 1; >> + cinfo.comp_info[0].v_samp_factor = 1; >> + } >> + >> + rowPointer = new JSAMPROW[h]; >> + for (int dy = 0; dy < h; dy++) >> + rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * w * pixelsize]); >> + >> + jpeg_start_compress(&cinfo, TRUE); >> + while (cinfo.next_scanline < cinfo.image_height) >> + jpeg_write_scanlines(&cinfo, &rowPointer[cinfo.next_scanline], >> + cinfo.image_height - cinfo.next_scanline); >> + >> + jpeg_finish_compress(&cinfo); >> + >> + if (srcBufIsTemp) delete[] srcBuf; >> + delete[] rowPointer; >> +} >> + >> +void JpegCompressor::writeBytes(const void* data, int length) >> +{ >> + throw rdr::Exception("writeBytes() is not valid with a JpegCompressor >> instance. Use compress() instead."); >> +} >> >> Added: trunk/common/rfb/JpegCompressor.h >> =================================================================== >> --- trunk/common/rfb/JpegCompressor.h (rev 0) >> +++ trunk/common/rfb/JpegCompressor.h 2011-08-11 11:18:34 UTC (rev >> 4626) >> @@ -0,0 +1,87 @@ >> +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. >> + * Copyright (C) 2011 D. R. Commander. All Rights Reserved. >> + * >> + * This is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License as published by >> + * the Free Software Foundation; either version 2 of the License, or >> + * (at your option) any later version. >> + * >> + * This software is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + * >> + * You should have received a copy of the GNU General Public License >> + * along with this software; if not, write to the Free Software >> + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, >> + * USA. >> + */ >> + >> +// >> +// JpegCompressor compresses RGB input into a JPEG image and stores it in >> +// an underlying MemOutStream >> +// >> + >> +#ifndef __RFB_JPEGCOMPRESSOR_H__ >> +#define __RFB_JPEGCOMPRESSOR_H__ >> + >> +#include <rdr/MemOutStream.h> >> +#include <rfb/PixelFormat.h> >> +#include <rfb/Rect.h> >> + >> +#include <stdio.h> >> +extern "C" { >> +#include <jpeglib.h> >> +} >> +#include <setjmp.h> >> + >> +namespace rfb { >> + >> + typedef struct { >> + struct jpeg_error_mgr pub; >> + jmp_buf jmpBuffer; >> + char lastError[JMSG_LENGTH_MAX]; >> + } JPEG_ERROR_MGR; >> + >> + class JpegCompressor; >> + >> + typedef struct { >> + struct jpeg_destination_mgr pub; >> + JpegCompressor *instance; >> + } JPEG_DEST_MGR; >> + >> + enum JPEG_SUBSAMP { >> + SUBSAMP_NONE, >> + SUBSAMP_422, >> + SUBSAMP_420 >> + }; >> + >> + class JpegCompressor : public rdr::MemOutStream { >> + >> + public: >> + >> + JpegCompressor(int bufferLen = 128*1024); >> + virtual ~JpegCompressor(); >> + >> + void compress(rdr::U8 *, const Rect&, const PixelFormat&, int, >> + JPEG_SUBSAMP); >> + >> + void writeBytes(const void*, int); >> + >> + inline rdr::U8* getstart() { return start; } >> + >> + inline int overrun(int itemSize, int nItems) { >> + return MemOutStream::overrun(itemSize, nItems); >> + } >> + >> + private: >> + >> + struct jpeg_compress_struct cinfo; >> + JPEG_ERROR_MGR err; >> + JPEG_DEST_MGR dest; >> + >> + }; >> + >> +} // end of namespace rfb >> + >> +#endif >> >> Modified: trunk/common/rfb/SMsgWriterV3.cxx >> =================================================================== >> --- trunk/common/rfb/SMsgWriterV3.cxx 2011-08-11 09:14:54 UTC (rev >> 4625) >> +++ trunk/common/rfb/SMsgWriterV3.cxx 2011-08-11 11:18:34 UTC (rev >> 4626) >> @@ -1,5 +1,6 @@ >> /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. >> * Copyright 2009 Pierre Ossman for Cendio AB >> + * Copyright (C) 2011 D. R. Commander. 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 >> @@ -178,15 +179,20 @@ >> startMsg(msgTypeFramebufferUpdate); >> os->pad(1); >> >> - if (wsccb) >> - nRects++; >> - if (needSetDesktopName) >> - nRects++; >> + if (nRects != 0xFFFF) { >> + if (wsccb) >> + nRects++; >> + if (needSetDesktopName) >> + nRects++; >> + } >> >> os->writeU16(nRects); >> >> nRectsInUpdate = 0; >> - nRectsInHeader = nRects; >> + if (nRects == 0xFFFF) >> + nRectsInHeader = 0; >> + else >> + nRectsInHeader = nRects; >> >> writePseudoRects(); >> } >> @@ -208,6 +214,15 @@ >> throw Exception("SMsgWriterV3::writeFramebufferUpdateEnd: " >> "nRects out of sync"); >> >> + if (nRectsInHeader == 0) { >> + // Send last rect. marker >> + os->writeS16(0); >> + os->writeS16(0); >> + os->writeU16(0); >> + os->writeU16(0); >> + os->writeU32(pseudoEncodingLastRect); >> + } >> + >> if (os == updateOS) { >> os = realOS; >> startMsg(msgTypeFramebufferUpdate); >> >> Modified: trunk/common/rfb/TightEncoder.cxx >> =================================================================== >> --- trunk/common/rfb/TightEncoder.cxx 2011-08-11 09:14:54 UTC (rev >> 4625) >> +++ trunk/common/rfb/TightEncoder.cxx 2011-08-11 11:18:34 UTC (rev >> 4626) >> @@ -1,4 +1,5 @@ >> /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. >> + * Copyright (C) 2011 D. R. Commander. 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 >> @@ -30,19 +31,19 @@ >> >> // Adjustable parameters. >> // FIXME: Get rid of #defines >> -#define TIGHT_JPEG_MIN_RECT_SIZE 1024 >> -#define TIGHT_DETECT_MIN_WIDTH 8 >> -#define TIGHT_DETECT_MIN_HEIGHT 8 >> +#define TIGHT_MAX_SPLIT_TILE_SIZE 16 >> +#define TIGHT_MIN_SPLIT_RECT_SIZE 4096 >> +#define TIGHT_MIN_SOLID_SUBRECT_SIZE 2048 >> >> // >> // 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). >> // >> -// NOTE: s_conf[9].maxRectSize should be >= s_conf[i].maxRectSize, >> -// where i in [0..8]. RequiredBuffSize() method depends on this. >> -// FIXME: Is this comment obsolete? >> -// >> +// NOTE: The parameters used in this encoder are the result of painstaking >> +// research by The VirtualGL Project using RFB session captures from a >> variety >> +// of both 2D and 3D applications. See http://www.VirtualGL.org for the >> full >> +// reports. >> >> // NOTE: The JPEG quality and subsampling levels below were obtained >> // experimentally by the VirtualGL Project. They represent the approximate >> @@ -63,18 +64,18 @@ >> // 0 = JPEG quality 15, 4:2:0 subsampling (ratio ~= 100:1) >> >> const TIGHT_CONF TightEncoder::conf[10] = { >> - { 512, 32, 6, 0, 0, 0, 4, 15, SUBSAMP_420 }, // 0 >> - { 2048, 64, 6, 1, 1, 1, 8, 29, SUBSAMP_420 }, // 1 >> - { 4096, 128, 8, 3, 3, 2, 24, 41, SUBSAMP_420 }, // 2 >> - { 8192, 256, 12, 5, 5, 2, 32, 42, SUBSAMP_422 }, // 3 >> - { 16384, 512, 12, 6, 7, 3, 32, 62, SUBSAMP_422 }, // 4 >> - { 32768, 512, 12, 7, 8, 4, 32, 77, SUBSAMP_422 }, // 5 >> - { 65536, 1024, 16, 7, 8, 5, 32, 79, SUBSAMP_NONE }, // 6 >> - { 65536, 1024, 16, 8, 9, 6, 64, 86, SUBSAMP_NONE }, // 7 >> - { 65536, 2048, 24, 9, 9, 7, 64, 92, SUBSAMP_NONE }, // 8 >> - { 65536, 2048, 32, 9, 9, 9, 96,100, SUBSAMP_NONE } // 9 >> + { 65536, 2048, 6, 0, 0, 0, 4, 24, 15, SUBSAMP_420 }, // 0 >> + { 65536, 2048, 6, 1, 1, 1, 8, 24, 29, SUBSAMP_420 }, // 1 >> + { 65536, 2048, 8, 3, 3, 2, 24, 96, 41, SUBSAMP_420 }, // 2 >> + { 65536, 2048, 12, 5, 5, 2, 32, 96, 42, SUBSAMP_422 }, // 3 >> + { 65536, 2048, 12, 6, 7, 3, 32, 96, 62, SUBSAMP_422 }, // 4 >> + { 65536, 2048, 12, 7, 8, 4, 32, 96, 77, SUBSAMP_422 }, // 5 >> + { 65536, 2048, 16, 7, 8, 5, 32, 96, 79, SUBSAMP_NONE }, // 6 >> + { 65536, 2048, 16, 8, 9, 6, 64, 96, 86, SUBSAMP_NONE }, // 7 >> + { 65536, 2048, 24, 9, 9, 7, 64, 96, 92, SUBSAMP_NONE }, // 8 >> + { 65536, 2048, 32, 9, 9, 9, 96, 96,100, SUBSAMP_NONE } // 9 >> }; >> -const int TightEncoder::defaultCompressLevel = 6; >> +const int TightEncoder::defaultCompressLevel = 1; >> >> // FIXME: Not good to mirror TightEncoder's members here. >> static const TIGHT_CONF* s_pconf; >> @@ -129,11 +130,114 @@ >> } >> } >> >> +bool TightEncoder::checkSolidTile(Rect& r, ImageGetter *ig, rdr::U32* >> colorPtr, >> + bool needSameColor) >> +{ >> + switch (writer->bpp()) { >> + case 32: >> + return checkSolidTile32(r, ig, writer, colorPtr, needSameColor); >> + case 16: >> + return checkSolidTile16(r, ig, writer, colorPtr, needSameColor); >> + default: >> + return checkSolidTile8(r, ig, writer, colorPtr, needSameColor); >> + } >> +} >> + >> +void TightEncoder::findBestSolidArea(Rect& r, ImageGetter *ig, >> + rdr::U32 colorValue, Rect& bestr) >> +{ >> + int dx, dy, dw, dh; >> + int w_prev; >> + Rect sr; >> + int w_best = 0, h_best = 0; >> + >> + bestr.tl.x = bestr.br.x = r.tl.x; >> + bestr.tl.y = bestr.br.y = r.tl.y; >> + >> + w_prev = r.width(); >> + >> + for (dy = r.tl.y; dy < r.br.y; dy += TIGHT_MAX_SPLIT_TILE_SIZE) { >> + >> + dh = (dy + TIGHT_MAX_SPLIT_TILE_SIZE <= r.br.y) ? >> + TIGHT_MAX_SPLIT_TILE_SIZE : (r.br.y - dy); >> + dw = (w_prev > TIGHT_MAX_SPLIT_TILE_SIZE) ? >> + TIGHT_MAX_SPLIT_TILE_SIZE : w_prev; >> + >> + sr.setXYWH(r.tl.x, dy, dw, dh); >> + if (!checkSolidTile(sr, ig, &colorValue, true)) >> + break; >> + >> + for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) { >> + dw = (dx + TIGHT_MAX_SPLIT_TILE_SIZE <= r.tl.x + w_prev) ? >> + TIGHT_MAX_SPLIT_TILE_SIZE : (r.tl.x + w_prev - dx); >> + sr.setXYWH(dx, dy, dw, dh); >> + if (!checkSolidTile(sr, ig, &colorValue, true)) >> + break; >> + dx += dw; >> + } >> + >> + w_prev = dx - r.tl.x; >> + if (w_prev * (dy + dh - r.tl.y) > w_best * h_best) { >> + w_best = w_prev; >> + h_best = dy + dh - r.tl.y; >> + } >> + } >> + >> + bestr.br.x = bestr.tl.x + w_best; >> + bestr.br.y = bestr.tl.y + h_best; >> +} >> + >> +void TightEncoder::extendSolidArea(const Rect& r, ImageGetter *ig, >> + rdr::U32 colorValue, Rect& er) >> +{ >> + int cx, cy; >> + Rect sr; >> + >> + // Try to extend the area upwards. >> + for (cy = er.tl.y - 1; ; cy--) { >> + sr.setXYWH(er.tl.x, cy, er.width(), 1); >> + if (cy < r.tl.y || !checkSolidTile(sr, ig, &colorValue, true)) >> + break; >> + } >> + er.tl.y = cy + 1; >> + >> + // ... downwards. >> + for (cy = er.br.y; ; cy++) { >> + sr.setXYWH(er.tl.x, cy, er.width(), 1); >> + if (cy >= r.br.y || !checkSolidTile(sr, ig, &colorValue, true)) >> + break; >> + } >> + er.br.y = cy; >> + >> + // ... to the left. >> + for (cx = er.tl.x - 1; ; cx--) { >> + sr.setXYWH(cx, er.tl.y, 1, er.height()); >> + if (cx < r.tl.x || !checkSolidTile(sr, ig, &colorValue, true)) >> + break; >> + } >> + er.tl.x = cx + 1; >> + >> + // ... to the right. >> + for (cx = er.br.x; ; cx++) { >> + sr.setXYWH(cx, er.tl.y, 1, er.height()); >> + if (cx >= r.br.x || !checkSolidTile(sr, ig, &colorValue, true)) >> + break; >> + } >> + er.br.x = cx; >> +} >> + >> int TightEncoder::getNumRects(const Rect &r) >> { >> + ConnParams* cp = writer->getConnParams(); >> const unsigned int w = r.width(); >> const unsigned int h = r.height(); >> >> + // If last rect. encoding is enabled, we can use the higher-performance >> + // code that pre-computes solid rectangles. In that case, we don't care >> + // about the rectangle count. >> + if (cp->supportsLastRect && w * h >= TIGHT_MIN_SPLIT_RECT_SIZE) >> + return 0; >> + >> // Will this rectangle split into subrects? >> bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize; >> if (!rectTooBig) >> @@ -150,7 +254,7 @@ >> ((h - 1) / subrectMaxHeight + 1)); >> } >> >> -bool TightEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual) >> +void TightEncoder::sendRectSimple(const Rect& r, ImageGetter* ig) >> { >> // Shortcuts to rectangle coordinates and dimensions. >> const int x = r.tl.x; >> @@ -158,15 +262,11 @@ >> const unsigned int w = r.width(); >> const unsigned int h = r.height(); >> >> - // Copy members of current TightEncoder instance to static variables. >> - s_pconf = pconf; >> - s_pjconf = pjconf; >> - >> // Encode small rects as is. >> bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize; >> if (!rectTooBig) { >> writeSubrect(r, ig); >> - return true; >> + return; >> } >> >> // Compute max sub-rectangle size. >> @@ -186,10 +286,110 @@ >> writeSubrect(sr, ig); >> } >> } >> +} >> + >> +bool TightEncoder::writeRect(const Rect& _r, ImageGetter* ig, Rect* actual) >> +{ >> + ConnParams* cp = writer->getConnParams(); >> + >> + // Shortcuts to rectangle coordinates and dimensions. >> + Rect r = _r; >> + int x = r.tl.x; >> + int y = r.tl.y; >> + unsigned int w = r.width(); >> + unsigned int h = r.height(); >> + >> + // Copy members of current TightEncoder instance to static variables. >> + s_pconf = pconf; >> + s_pjconf = pjconf; >> + >> + // Encode small rects as is. >> + if (!cp->supportsLastRect || w * h < TIGHT_MIN_SPLIT_RECT_SIZE) { >> + sendRectSimple(r, ig); >> + return true; >> + } >> + >> + // Split big rects into separately encoded subrects. >> + Rect sr, bestr; >> + unsigned int dx, dy, dw, dh; >> + rdr::U32 colorValue; >> + int maxRectSize = s_pconf->maxRectSize; >> + int maxRectWidth = s_pconf->maxRectWidth; >> + int nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; >> + int nMaxRows = s_pconf->maxRectSize / nMaxWidth; >> + >> + // Try to find large solid-color areas and send them separately. >> + for (dy = y; dy < y + h; dy += TIGHT_MAX_SPLIT_TILE_SIZE) { >> + >> + // If a rectangle becomes too large, send its upper part now. >> + if (dy - y >= nMaxRows) { >> + sr.setXYWH(x, y, w, nMaxRows); >> + sendRectSimple(sr, ig); >> + r.tl.y += nMaxRows; >> + y = r.tl.y; >> + h = r.height(); >> + } >> + >> + dh = (dy + TIGHT_MAX_SPLIT_TILE_SIZE <= y + h) ? >> + TIGHT_MAX_SPLIT_TILE_SIZE : (y + h - dy); >> + >> + for (dx = x; dx < x + w; dx += TIGHT_MAX_SPLIT_TILE_SIZE) { >> + >> + dw = (dx + TIGHT_MAX_SPLIT_TILE_SIZE <= x + w) ? >> + TIGHT_MAX_SPLIT_TILE_SIZE : (x + w - dx); >> + >> + sr.setXYWH(dx, dy, dw, dh); >> + if (checkSolidTile(sr, ig, &colorValue, false)) { >> + >> + // Get dimensions of solid-color area. >> + sr.setXYWH(dx, dy, r.br.x - dx, r.br.y - dy); >> + findBestSolidArea(sr, ig, colorValue, bestr); >> + >> + // Make sure a solid rectangle is large enough >> + // (or the whole rectangle is of the same color). >> + if (bestr.area() != r.area() >> + && bestr.area() < TIGHT_MIN_SOLID_SUBRECT_SIZE) >> + continue; >> + >> + // Try to extend solid rectangle to maximum size. >> + extendSolidArea(r, ig, colorValue, bestr); >> + >> + // Send rectangles at top and left to solid-color area. >> + if (bestr.tl.y != y) { >> + sr.setXYWH(x, y, w, bestr.tl.y - y); >> + sendRectSimple(sr, ig); >> + } >> + if (bestr.tl.x != x) { >> + sr.setXYWH(x, bestr.tl.y, bestr.tl.x - x, bestr.height()); >> + writeRect(sr, ig, NULL); >> + } >> + >> + // Send solid-color rectangle. >> + writeSubrect(bestr, ig, true); >> + >> + // Send remaining rectangles (at right and bottom). >> + if (bestr.br.x != r.br.x) { >> + sr.setXYWH(bestr.br.x, bestr.tl.y, r.br.x - bestr.br.x, >> + bestr.height()); >> + writeRect(sr, ig, NULL); >> + } >> + if (bestr.br.y != r.br.y) { >> + sr.setXYWH(x, bestr.br.y, w, r.br.y - bestr.br.y); >> + writeRect(sr, ig, NULL); >> + } >> + >> + return true; >> + } >> + } >> + } >> + >> + // No suitable solid-color rectangles found. >> + sendRectSimple(r, ig); >> return true; >> } >> >> -void TightEncoder::writeSubrect(const Rect& r, ImageGetter* ig) >> +void TightEncoder::writeSubrect(const Rect& r, ImageGetter* ig, >> + bool forceSolid) >> { >> rdr::U8* imageBuf = writer->getImageBuf(r.area()); >> ConnParams* cp = writer->getConnParams(); >> @@ -197,11 +397,11 @@ >> >> switch (writer->bpp()) { >> case 8: >> - tightEncode8(r, &mos, zos, imageBuf, cp, ig); break; >> + tightEncode8(r, &mos, zos, jc, imageBuf, cp, ig, forceSolid); break; >> case 16: >> - tightEncode16(r, &mos, zos, imageBuf, cp, ig); break; >> + tightEncode16(r, &mos, zos, jc, imageBuf, cp, ig, forceSolid); break; >> case 32: >> - tightEncode32(r, &mos, zos, imageBuf, cp, ig); break; >> + tightEncode32(r, &mos, zos, jc, imageBuf, cp, ig, forceSolid); break; >> } >> >> writer->startRect(r, encodingTight); >> >> Modified: trunk/common/rfb/TightEncoder.h >> =================================================================== >> --- trunk/common/rfb/TightEncoder.h 2011-08-11 09:14:54 UTC (rev 4625) >> +++ trunk/common/rfb/TightEncoder.h 2011-08-11 11:18:34 UTC (rev 4626) >> @@ -1,4 +1,5 @@ >> /* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. >> + * Copyright (C) 2011 D. R. Commander >> * >> * This is free software; you can redistribute it and/or modify >> * it under the terms of the GNU General Public License as published by >> @@ -20,6 +21,7 @@ >> >> #include <rdr/MemOutStream.h> >> #include <rdr/ZlibOutStream.h> >> +#include <rfb/JpegCompressor.h> >> #include <rfb/Encoder.h> >> >> // FIXME: Check if specifying extern "C" is really necessary. >> @@ -30,19 +32,14 @@ >> >> namespace rfb { >> >> - enum subsampEnum { >> - SUBSAMP_NONE, >> - SUBSAMP_422, >> - SUBSAMP_420 >> - }; >> - >> struct TIGHT_CONF { >> unsigned int maxRectSize, maxRectWidth; >> unsigned int monoMinRectSize; >> int idxZlibLevel, monoZlibLevel, rawZlibLevel; >> int idxMaxColorsDivisor; >> + int palMaxColorsWithJPEG; >> int jpegQuality; >> - subsampEnum jpegSubSample; >> + JPEG_SUBSAMP jpegSubSample; >> }; >> >> // >> @@ -67,11 +64,19 @@ >> >> private: >> TightEncoder(SMsgWriter* writer); >> - void writeSubrect(const Rect& r, ImageGetter* ig); >> + bool checkSolidTile(Rect& r, ImageGetter *ig, rdr::U32* colorPtr, >> + bool needSameColor); >> + void extendSolidArea(const Rect& r, ImageGetter *ig, >> + rdr::U32 colorValue, Rect& er); >> + void findBestSolidArea(Rect& r, ImageGetter* ig, rdr::U32 colorValue, >> + Rect& bestr); >> + void sendRectSimple(const Rect& r, ImageGetter* ig); >> + void writeSubrect(const Rect& r, ImageGetter* ig, bool forceSolid = >> false); >> >> SMsgWriter* writer; >> rdr::MemOutStream mos; >> rdr::ZlibOutStream zos[4]; >> + JpegCompressor jc; >> >> static const int defaultCompressLevel; >> static const TIGHT_CONF conf[]; >> >> Modified: trunk/common/rfb/VNCSConnectionST.cxx >> =================================================================== >> --- trunk/common/rfb/VNCSConnectionST.cxx 2011-08-11 09:14:54 UTC (rev >> 4625) >> +++ trunk/common/rfb/VNCSConnectionST.cxx 2011-08-11 11:18:34 UTC (rev >> 4626) >> @@ -722,8 +722,14 @@ >> std::vector<Rect>::const_iterator i; >> ui.changed.get_rects(&rects); >> for (i = rects.begin(); i != rects.end(); i++) { >> - if (i->width() && i->height()) >> - nRects += writer()->getNumRects(*i); >> + if (i->width() && i->height()) { >> + int nUpdateRects = writer()->getNumRects(*i); >> + if (nUpdateRects == 0 && cp.currentEncoding() == encodingTight) { >> + nRects = 0xFFFF; break; >> + } >> + else >> + nRects += nUpdateRects; >> + } >> } >> >> writer()->writeFramebufferUpdateStart(nRects); >> >> Modified: trunk/common/rfb/tightEncode.h >> =================================================================== >> --- trunk/common/rfb/tightEncode.h 2011-08-11 09:14:54 UTC (rev 4625) >> +++ trunk/common/rfb/tightEncode.h 2011-08-11 11:18:34 UTC (rev 4626) >> @@ -45,14 +45,13 @@ >> #define SWAP_PIXEL CONCAT2E(SWAP,BPP) >> #define HASH_FUNCTION CONCAT2E(HASH_FUNC,BPP) >> #define PACK_PIXELS CONCAT2E(packPixels,BPP) >> -#define DETECT_SMOOTH_IMAGE CONCAT2E(detectSmoothImage,BPP) >> #define ENCODE_SOLID_RECT CONCAT2E(encodeSolidRect,BPP) >> #define ENCODE_FULLCOLOR_RECT CONCAT2E(encodeFullColorRect,BPP) >> #define ENCODE_MONO_RECT CONCAT2E(encodeMonoRect,BPP) >> #define ENCODE_INDEXED_RECT CONCAT2E(encodeIndexedRect,BPP) >> -#define PREPARE_JPEG_ROW CONCAT2E(prepareJpegRow,BPP) >> #define ENCODE_JPEG_RECT CONCAT2E(encodeJpegRect,BPP) >> #define FILL_PALETTE CONCAT2E(fillPalette,BPP) >> +#define CHECK_SOLID_TILE CONCAT2E(checkSolidTile,BPP) >> >> #ifndef TIGHT_ONCE >> #define TIGHT_ONCE >> @@ -203,54 +202,6 @@ >> } >> } >> >> -// >> -// Destination manager implementation for the JPEG library. >> -// FIXME: Implement JPEG compression in new rdr::JpegOutStream class. >> -// >> - >> -// FIXME: Keeping a MemOutStream instance may consume too much space. >> -rdr::MemOutStream s_jpeg_os; >> - >> -static struct jpeg_destination_mgr s_jpegDstManager; >> -static JOCTET *s_jpegDstBuffer; >> -static size_t s_jpegDstBufferLen; >> - >> -static void >> -JpegInitDestination(j_compress_ptr cinfo) >> -{ >> - s_jpeg_os.clear(); >> - s_jpegDstManager.next_output_byte = s_jpegDstBuffer; >> - s_jpegDstManager.free_in_buffer = s_jpegDstBufferLen; >> -} >> - >> -static boolean >> -JpegEmptyOutputBuffer(j_compress_ptr cinfo) >> -{ >> - s_jpeg_os.writeBytes(s_jpegDstBuffer, s_jpegDstBufferLen); >> - s_jpegDstManager.next_output_byte = s_jpegDstBuffer; >> - s_jpegDstManager.free_in_buffer = s_jpegDstBufferLen; >> - >> - return TRUE; >> -} >> - >> -static void >> -JpegTermDestination(j_compress_ptr cinfo) >> -{ >> - int dataLen = s_jpegDstBufferLen - s_jpegDstManager.free_in_buffer; >> - s_jpeg_os.writeBytes(s_jpegDstBuffer, dataLen); >> -} >> - >> -static void >> -JpegSetDstManager(j_compress_ptr cinfo, JOCTET *buf, size_t buflen) >> -{ >> - s_jpegDstBuffer = buf; >> - s_jpegDstBufferLen = buflen; >> - s_jpegDstManager.init_destination = JpegInitDestination; >> - s_jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer; >> - s_jpegDstManager.term_destination = JpegTermDestination; >> - cinfo->dest = &s_jpegDstManager; >> -} >> - >> #endif // #ifndef TIGHT_ONCE >> >> static void ENCODE_SOLID_RECT (rdr::OutStream *os, >> @@ -262,7 +213,7 @@ >> #if (BPP != 8) >> static void ENCODE_INDEXED_RECT (rdr::OutStream *os, rdr::ZlibOutStream >> zos[4], >> PIXEL_T *buf, const PixelFormat& pf, >> const Rect& r); >> -static void ENCODE_JPEG_RECT (rdr::OutStream *os, >> +static void ENCODE_JPEG_RECT (rdr::OutStream *os, JpegCompressor& jc, >> PIXEL_T *buf, const PixelFormat& pf, >> const Rect& r); >> #endif >> >> @@ -295,41 +246,24 @@ >> } >> >> // >> -// Function to guess if a given rectangle is suitable for JPEG compression. >> -// Returns true is it looks like a good data for JPEG, false otherwise. >> -// >> -// FIXME: Scan the image and determine is it really good for JPEG. >> -// >> - >> -#if (BPP != 8) >> -static bool DETECT_SMOOTH_IMAGE (PIXEL_T *buf, const Rect& r) >> -{ >> - if (r.width() < TIGHT_DETECT_MIN_WIDTH || >> - r.height() < TIGHT_DETECT_MIN_HEIGHT || >> - r.area() < TIGHT_JPEG_MIN_RECT_SIZE || >> - s_pjconf == NULL) >> - return 0; >> - >> - return 1; >> -} >> -#endif >> - >> -// FIXME: Split rectangles into smaller ones! >> -// FIXME: Compare encoder code with 1.3 before the final version. >> - >> -// >> // Main function of the Tight encoder >> // >> >> void TIGHT_ENCODE (const Rect& r, rdr::OutStream *os, >> - rdr::ZlibOutStream zos[4], void* buf, ConnParams* cp >> + rdr::ZlibOutStream zos[4], JpegCompressor &jc, void* buf, >> + ConnParams* cp >> #ifdef EXTRA_ARGS >> - , EXTRA_ARGS >> + , EXTRA_ARGS, >> #endif >> - ) >> + bool forceSolid) >> { >> const PixelFormat& pf = cp->pf(); >> - GET_IMAGE_INTO_BUF(r, buf); >> + if(forceSolid) { >> + GET_IMAGE_INTO_BUF(Rect(r.tl.x, r.tl.y, r.tl.x + 1, r.tl.y + 1), buf); >> + } >> + else { >> + GET_IMAGE_INTO_BUF(r, buf); >> + } >> PIXEL_T* pixels = (PIXEL_T*)buf; >> >> #if (BPP == 32) >> @@ -338,23 +272,24 @@ >> s_pack24 = pf.is888(); >> #endif >> >> - s_palMaxColors = r.area() / s_pconf->idxMaxColorsDivisor; >> - if (s_palMaxColors < 2 && r.area() >= s_pconf->monoMinRectSize) { >> - s_palMaxColors = 2; >> + if (forceSolid) >> + s_palNumColors = 1; >> + else { >> + s_palMaxColors = r.area() / s_pconf->idxMaxColorsDivisor; >> + if (s_pjconf != NULL) s_palMaxColors = s_pconf->palMaxColorsWithJPEG; >> + if (s_palMaxColors < 2 && r.area() >= s_pconf->monoMinRectSize) { >> + s_palMaxColors = 2; >> + } >> + >> + FILL_PALETTE(pixels, r.area()); >> } >> - // FIXME: Temporary limitation for switching to JPEG earlier. >> - if (s_palMaxColors > 96 && s_pjconf != NULL) { >> - s_palMaxColors = 96; >> - } >> >> - FILL_PALETTE(pixels, r.area()); >> - >> switch (s_palNumColors) { >> case 0: >> // Truecolor image >> #if (BPP != 8) >> - if (s_pjconf != NULL && DETECT_SMOOTH_IMAGE(pixels, r)) { >> - ENCODE_JPEG_RECT(os, pixels, pf, r); >> + if (s_pjconf != NULL) { >> + ENCODE_JPEG_RECT(os, jc, pixels, pf, r); >> break; >> } >> #endif >> @@ -518,106 +453,16 @@ >> // >> >> #if (BPP != 8) >> -static void ENCODE_JPEG_RECT (rdr::OutStream *os, PIXEL_T *buf, >> - const PixelFormat& pf, const Rect& r) >> +static void ENCODE_JPEG_RECT (rdr::OutStream *os, JpegCompressor& jc, >> + PIXEL_T *buf, const PixelFormat& pf, >> + const Rect& r) >> { >> - int w = r.width(); >> - int h = r.height(); >> - int pixelsize; >> - rdr::U8 *srcBuf = NULL; >> - bool srcBufIsTemp = false; >> - >> - struct jpeg_compress_struct cinfo; >> - struct jpeg_error_mgr jerr; >> - >> - cinfo.err = jpeg_std_error(&jerr); >> - jpeg_create_compress(&cinfo); >> - >> - cinfo.image_width = w; >> - cinfo.image_height = h; >> - cinfo.in_color_space = JCS_RGB; >> - pixelsize = 3; >> - >> -#ifdef JCS_EXTENSIONS >> - // Try to have libjpeg read directly from our native format >> - if(pf.is888()) { >> - int redShift, greenShift, blueShift; >> - >> - if(pf.bigEndian) { >> - redShift = 24 - pf.redShift; >> - greenShift = 24 - pf.greenShift; >> - blueShift = 24 - pf.blueShift; >> - } else { >> - redShift = pf.redShift; >> - greenShift = pf.greenShift; >> - blueShift = pf.blueShift; >> - } >> - >> - if(redShift == 0 && greenShift == 8 && blueShift == 16) >> - cinfo.in_color_space = JCS_EXT_RGBX; >> - if(redShift == 16 && greenShift == 8 && blueShift == 0) >> - cinfo.in_color_space = JCS_EXT_BGRX; >> - if(redShift == 24 && greenShift == 16 && blueShift == 8) >> - cinfo.in_color_space = JCS_EXT_XBGR; >> - if(redShift == 8 && greenShift == 16 && blueShift == 24) >> - cinfo.in_color_space = JCS_EXT_XRGB; >> - >> - if (cinfo.in_color_space != JCS_RGB) { >> - srcBuf = (rdr::U8 *)buf; >> - pixelsize = 4; >> - } >> - } >> -#endif >> - >> - if (cinfo.in_color_space == JCS_RGB) { >> - srcBuf = new rdr::U8[w * h * pixelsize]; >> - srcBufIsTemp = true; >> - pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w * h); >> - } >> - >> - cinfo.input_components = pixelsize; >> - >> - jpeg_set_defaults(&cinfo); >> - jpeg_set_quality(&cinfo, s_pjconf->jpegQuality, TRUE); >> - if(s_pjconf->jpegQuality >= 96) cinfo.dct_method = JDCT_ISLOW; >> - else cinfo.dct_method = JDCT_FASTEST; >> - >> - switch (s_pjconf->jpegSubSample) { >> - case SUBSAMP_420: >> - cinfo.comp_info[0].h_samp_factor = 2; >> - cinfo.comp_info[0].v_samp_factor = 2; >> - break; >> - case SUBSAMP_422: >> - cinfo.comp_info[0].h_samp_factor = 2; >> - cinfo.comp_info[0].v_samp_factor = 1; >> - break; >> - default: >> - cinfo.comp_info[0].h_samp_factor = 1; >> - cinfo.comp_info[0].v_samp_factor = 1; >> - } >> - >> - rdr::U8 *dstBuf = new rdr::U8[2048]; >> - JpegSetDstManager(&cinfo, dstBuf, 2048); >> - >> - JSAMPROW *rowPointer = new JSAMPROW[h]; >> - for (int dy = 0; dy < h; dy++) >> - rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * w * pixelsize]); >> - >> - jpeg_start_compress(&cinfo, TRUE); >> - while (cinfo.next_scanline < cinfo.image_height) >> - jpeg_write_scanlines(&cinfo, &rowPointer[cinfo.next_scanline], >> - cinfo.image_height - cinfo.next_scanline); >> - >> - jpeg_finish_compress(&cinfo); >> - jpeg_destroy_compress(&cinfo); >> - >> - if (srcBufIsTemp) delete[] srcBuf; >> - delete[] dstBuf; >> - delete[] rowPointer; >> - >> + jc.clear(); >> + jc.compress((rdr::U8 *)buf, r, pf, s_pjconf->jpegQuality, >> + s_pjconf->jpegSubSample); >> os->writeU8(0x09 << 4); >> - os->writeCompactLength(s_jpeg_os.length()); >> - os->writeBytes(s_jpeg_os.data(), s_jpeg_os.length()); >> + os->writeCompactLength(jc.length()); >> + os->writeBytes(jc.data(), jc.length()); >> } >> #endif // #if (BPP != 8) >> >> @@ -726,18 +571,47 @@ >> } >> #endif // #if (BPP == 8) >> >> +bool CHECK_SOLID_TILE(Rect& r, ImageGetter* ig, SMsgWriter* writer, >> + rdr::U32 *colorPtr, bool needSameColor) >> +{ >> + PIXEL_T *buf; >> + PIXEL_T colorValue; >> + int dx, dy; >> + Rect sr; >> + >> + buf = (PIXEL_T *)writer->getImageBuf(r.area()); >> + sr.setXYWH(r.tl.x, r.tl.y, 1, 1); >> + GET_IMAGE_INTO_BUF(sr, buf); >> + >> + colorValue = *buf; >> + if (needSameColor && (rdr::U32)colorValue != *colorPtr) >> + return false; >> + >> + for (dy = 0; dy < r.height(); dy++) { >> + Rect sr; >> + sr.setXYWH(r.tl.x, r.tl.y + dy, r.width(), 1); >> + GET_IMAGE_INTO_BUF(sr, buf); >> + for (dx = 0; dx < r.width(); dx++) { >> + if (colorValue != buf[dx]) >> + return false; >> + } >> + } >> + >> + *colorPtr = (rdr::U32)colorValue; >> + return true; >> +} >> + >> #undef PIXEL_T >> #undef WRITE_PIXEL >> #undef TIGHT_ENCODE >> #undef SWAP_PIXEL >> #undef HASH_FUNCTION >> #undef PACK_PIXELS >> -#undef DETECT_SMOOTH_IMAGE >> #undef ENCODE_SOLID_RECT >> #undef ENCODE_FULLCOLOR_RECT >> #undef ENCODE_MONO_RECT >> #undef ENCODE_INDEXED_RECT >> -#undef PREPARE_JPEG_ROW >> #undef ENCODE_JPEG_RECT >> #undef FILL_PALETTE >> +#undef CHECK_SOLID_TILE >> } >> >> >> This was sent by the SourceForge.net collaborative development platform, the >> world's largest Open Source development site. >> >> ------------------------------------------------------------------------------ >> Get a FREE DOWNLOAD! and learn more about uberSVN rich system, >> user administration capabilities and model configuration. Take >> the hassle out of deploying and managing Subversion and the >> tools developers use with it. >> http://p.sf.net/sfu/wandisco-dev2dev >> _______________________________________________ >> Tigervnc-commits mailing list >> tigervnc-comm...@lists.sourceforge.net >> https://lists.sourceforge.net/lists/listinfo/tigervnc-commits ------------------------------------------------------------------------------ Get a FREE DOWNLOAD! and learn more about uberSVN rich system, user administration capabilities and model configuration. Take the hassle out of deploying and managing Subversion and the tools developers use with it. http://p.sf.net/sfu/wandisco-dev2dev _______________________________________________ Tigervnc-devel mailing list Tigervnc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/tigervnc-devel