// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. // Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. // // This file is part of the VNC system. // // The VNC system 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 program 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 program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // // TightVNC distribution homepage on the Web: http://www.tightvnc.com/ // // If the source code for the VNC system is not available from the place // whence you received this file, check http://www.uk.research.att.com/vnc or contact // the authors on vnc@uk.research.att.com for information on obtaining it. // vncEncodeZlib // This file implements the vncEncoder-derived vncEncodeZlib class. // This class overrides some vncEncoder functions to produce a bitmap // to Zlib encoder. Zlib is much more efficient than RAW format on // most screen data and usually twice as efficient as hextile. Of // course, zlib compression uses more CPU time on the server. // However, over slower (64kbps or less) connections, the reduction // in data transmitted usually outweighs the extra latency added // while the server CPU performs the compression algorithms. #include "vncEncodeZlib.h" vncEncodeZlib::vncEncodeZlib() { m_buffer = NULL; m_bufflen = 0; compStreamInited = false; } vncEncodeZlib::~vncEncodeZlib() { if (m_buffer != NULL) { delete [] m_buffer; m_buffer = NULL; } if ( compStreamInited == true ) { deflateEnd( &compStream ); } compStreamInited = false; } void vncEncodeZlib::Init() { vncEncoder::Init(); } UINT vncEncodeZlib::RequiredBuffSize(UINT width, UINT height) { int result; // The zlib library specifies a maximum compressed size of // the raw size plus one percent plus 8 bytes. We also need // to cover the zlib header space. result = vncEncoder::RequiredBuffSize(width, height); result += ((result / 100) + 8) + sz_rfbZlibHeader; return result; } UINT vncEncodeZlib::NumCodedRects(RECT &rect) { /****************************************************************** return 1; ******************************************************************/ const int rectW = rect.right - rect.left; const int rectH = rect.bottom - rect.top; // Return the number of rectangles needed to encode the given // update. ( ZLIB_MAX_SIZE(rectW) / rectW ) is the number of lines in // each maximum size rectangle. return (( rectH - 1 ) / ( ZLIB_MAX_SIZE( rectW ) / rectW ) + 1 ); } /***************************************************************************** * * Routines to implement zlib Encoding (LZ+Huffman compression) by calling * the included zlib library. */ // Encode the rectangle using zlib compression inline UINT vncEncodeZlib::EncodeRect(BYTE *source, VSocket *outConn, BYTE *dest, const RECT &rect, int offx, int offy) { int totalSize = 0; int partialSize = 0; int maxLines; int linesRemaining; offsetx = offx; offsety = offy; RECT partialRect; const int rectW = rect.right - rect.left; const int rectH = rect.bottom - rect.top; partialRect.right = rect.right; partialRect.left = rect.left; partialRect.top = rect.top; partialRect.bottom = rect.bottom; maxLines = ( ZLIB_MAX_SIZE(rectW) / rectW ); linesRemaining = rectH; while ( linesRemaining > 0 ) { int linesToComp; if ( maxLines < linesRemaining ) linesToComp = maxLines; else linesToComp = linesRemaining; partialRect.bottom = partialRect.top + linesToComp; partialSize = EncodeOneRect( source, dest, partialRect ); totalSize += partialSize; linesRemaining -= linesToComp; partialRect.top += linesToComp; if (( linesRemaining > 0 ) && ( partialSize > 0 )) { // Send the encoded data outConn->SendQueued( (char *)dest, partialSize ); transmittedSize += partialSize; } } transmittedSize += partialSize; return partialSize; } // Encode the rectangle using zlib compression inline UINT vncEncodeZlib::EncodeOneRect(BYTE *source, BYTE *dest, const RECT &rect) { int totalCompDataLen = 0; int previousTotalOut; int deflateResult; const int rectW = rect.right - rect.left; const int rectH = rect.bottom - rect.top; const int rawDataSize = (rectW*rectH*m_remoteformat.bitsPerPixel / 8); const int maxCompSize = (rawDataSize + (rawDataSize/100) + 8); // Send as raw if the update is too small to compress. if (rawDataSize < VNC_ENCODE_ZLIB_MIN_COMP_SIZE) return vncEncoder::EncodeRect(source, dest, rect, offsetx, offsety); // Create the rectangle header rfbFramebufferUpdateRectHeader *surh=(rfbFramebufferUpdateRectHeader *)dest; surh->r.x = (CARD16) rect.left ; surh->r.y = (CARD16) rect.top ; surh->r.w = (CARD16) (rectW); surh->r.h = (CARD16) (rectH); surh->r.x = Swap16IfLE(surh->r.x- offsetx); surh->r.y = Swap16IfLE(surh->r.y- offsety); surh->r.w = Swap16IfLE(surh->r.w); surh->r.h = Swap16IfLE(surh->r.h); surh->encoding = Swap32IfLE(rfbEncodingZlib); dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8; rectangleOverhead += sz_rfbFramebufferUpdateRectHeader; // create a space big enough for the Zlib encoded pixels if (m_bufflen < rawDataSize) { if (m_buffer != NULL) { delete [] m_buffer; m_buffer = NULL; } m_buffer = new BYTE [rawDataSize+1]; if (m_buffer == NULL) return vncEncoder::EncodeRect(source, dest, rect, offsetx, offsety); m_bufflen = rawDataSize; } // Translate the data into our new buffer Translate(source, m_buffer, rect); // Initialize input/output buffer assignment for compressor state. compStream.avail_in = rawDataSize; compStream.next_in = m_buffer; compStream.avail_out = maxCompSize; compStream.next_out = (dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbZlibHeader); compStream.data_type = Z_BINARY; // If necessary, the first time, initialize the compressor state. if ( compStreamInited == false ) { compStream.total_in = 0; compStream.total_out = 0; compStream.zalloc = Z_NULL; compStream.zfree = Z_NULL; compStream.opaque = Z_NULL; deflateResult = deflateInit2( &compStream, m_compresslevel, Z_DEFLATED, MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY ); if ( deflateResult != Z_OK ) { return vncEncoder::EncodeRect(source, dest, rect, offsetx, offsety); } compStreamInited = true; } // Record previous total output size. previousTotalOut = compStream.total_out; // Compress the raw data into the result buffer. deflateResult = deflate( &compStream, Z_SYNC_FLUSH ); if ( deflateResult != Z_OK ) { return vncEncoder::EncodeRect(source, dest, rect, offsetx, offsety); } // Calculate size of compressed data. totalCompDataLen = compStream.total_out - previousTotalOut; // Format the ZlibHeader rfbZlibHeader *zlibh=(rfbZlibHeader *)(dest+sz_rfbFramebufferUpdateRectHeader); zlibh->nBytes = Swap32IfLE(totalCompDataLen); // Update statistics encodedSize += sz_rfbZlibHeader + totalCompDataLen; // Return the amount of data sent return sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + totalCompDataLen; } /* VOID vncEncodeZlib::UpdateZLibDictionary( AGENT_CTX * lpAgentContext ) { if( lpAgentContext->dictionaries[0] ) setdictionary( &compStream, lpAgentContext->dictionaries[0]->bDictBuffer, lpAgentContext->dictionaries[0]->dwDictLength ); } VOID vncEncodeZlib::DumpZLibDictionary( AGENT_CTX * lpAgentContext ) { SendZlibDictionary( lpAgentContext, 0, &compStream ); } */