// 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. // vncEncodeRRE // This file implements the vncEncoder-derived vncEncodeRRE class. // This class overrides some vncEncoder functions to produce a bitmap // to RRE encoder. RRE is much more efficient than RAW format on // most screen data. #include "vncEncodeRRE.h" vncEncodeRRE::vncEncodeRRE() { m_buffer = NULL; m_bufflen = 0; } vncEncodeRRE::~vncEncodeRRE() { if (m_buffer != NULL) { delete [] m_buffer; m_buffer = NULL; } } void vncEncodeRRE::Init() { vncEncoder::Init(); } UINT vncEncodeRRE::RequiredBuffSize(UINT width, UINT height) { return vncEncoder::RequiredBuffSize(width, height); } UINT vncEncodeRRE::NumCodedRects(RECT &rect) { return 1; } /***************************************************************************** * * Routines to implement Rise-and-Run-length Encoding (RRE). This code is * based on krw's original javatel rfbserver. * This code courtesy of tjr */ /* * rreBeforeBuf contains pixel data in the client's format. * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is * larger than the raw data or if it exceeds rreAfterBufSize then * normal encoding is used instead. */ static int rreAfterBufLen; static int subrectEncode8 (CARD8 *data, CARD8 *buf, int w, int h, int maxBytes); static int subrectEncode16 (CARD16 *data, CARD8 *buf, int w, int h, int maxBytes); static int subrectEncode32 (CARD32 *data, CARD8 *buf, int w, int h, int maxBytes); static CARD32 getBgColour (char *data, int size, int bpp); /* * subrectEncode() encodes the given multicoloured rectangle as a background * colour overwritten by single-coloured rectangles. It returns the number * of subrectangles in the encoded buffer, or -1 if subrect encoding won't * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The * single-colour rectangle partition is not optimal, but does find the biggest * horizontal or vertical rectangle top-left anchored to each consecutive * coordinate position. * * The coding scheme is simply [...] where each * is []. * * This code has been modified from tjr's original by Wez(jnw) */ #define DEFINE_SUBRECT_ENCODE(bpp) \ static int \ subrectEncode##bpp( \ CARD##bpp *data, \ CARD8 *buf, \ int w, \ int h, \ int maxBytes \ ) \ { \ CARD##bpp cl; \ rfbRectangle subrect; \ int x,y; \ int i,j; \ int hx,hy,vx,vy; \ int hyflag; \ CARD##bpp *seg; \ CARD##bpp *line; \ int hw,hh,vw,vh; \ int thex,they,thew,theh; \ int numsubs = 0; \ int newLen; \ CARD##bpp bg = (CARD##bpp)getBgColour((char*)data,w*h,bpp); \ \ /* Set the background colour value */ \ *((CARD##bpp *)buf) = bg; \ \ rreAfterBufLen = (bpp/8); \ \ for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ } \ vy = j-1; \ \ /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ * We'll choose the bigger of the two. \ */ \ hw = hx-x+1; \ hh = hy-y+1; \ vw = vx-x+1; \ vh = vy-y+1; \ \ thex = x; \ they = y; \ \ if ((hw*hh) > (vw*vh)) { \ thew = hw; \ theh = hh; \ } else { \ thew = vw; \ theh = vh; \ } \ \ subrect.x = Swap16IfLE(thex); \ subrect.y = Swap16IfLE(they); \ subrect.w = Swap16IfLE(thew); \ subrect.h = Swap16IfLE(theh); \ \ newLen = rreAfterBufLen + (bpp/8) + sz_rfbRectangle; \ if ((newLen > (w * h * (bpp/8))) || (newLen > maxBytes)) \ return -1; \ \ numsubs += 1; \ *((CARD##bpp *)(buf + rreAfterBufLen)) = cl; \ rreAfterBufLen += (bpp/8); \ memcpy(&buf[rreAfterBufLen],&subrect, sz_rfbRectangle); \ rreAfterBufLen += sz_rfbRectangle; \ \ /* \ * Now mark the subrect as done. \ */ \ for (j=they; j < (they+theh); j++) { \ for (i=thex; i < (thex+thew); i++) { \ data[j*w+i] = bg; \ } \ } \ } \ } \ } \ \ return numsubs; \ } DEFINE_SUBRECT_ENCODE(8) DEFINE_SUBRECT_ENCODE(16) DEFINE_SUBRECT_ENCODE(32) /* * getBgColour() gets the most prevalent colour in a byte array. */ static CARD32 getBgColour(char *data, int size, int bpp) { #define NUMCLRS 256 static int counts[NUMCLRS]; int i,j,k; int maxcount = 0; CARD8 maxclr = 0; if (bpp != 8) { if (bpp == 16) { return ((CARD16 *)data)[0]; } else if (bpp == 32) { return ((CARD32 *)data)[0]; } else { fprintf(stderr,"getBgColour: bpp %d?\n",bpp); exit(1); } } for (i=0; i= NUMCLRS) { fprintf(stderr, "%s: unusual colour = %d\n", "getBgColour",k); exit(1); } counts[k] += 1; if (counts[k] > maxcount) { maxcount = counts[k]; maxclr = ((CARD8 *)data)[j]; } } return maxclr; } // Encode the rectangle using RRE inline UINT vncEncodeRRE::EncodeRect(BYTE *source, BYTE *dest, const RECT &rect, int offx, int offy) { int subrects = -1; const UINT rectW = rect.right - rect.left; const UINT rectH = rect.bottom - rect.top; // 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 - offx); surh->r.y = Swap16IfLE(surh->r.y - offy); surh->r.w = Swap16IfLE(surh->r.w); surh->r.h = Swap16IfLE(surh->r.h); surh->encoding = Swap32IfLE(rfbEncodingRRE); // create a space big enough for the RRE encoded pixels size_t rectSize = rectW * rectH * (m_remoteformat.bitsPerPixel / 8); if (m_bufflen < rectSize) { if (m_buffer != NULL) { delete [] m_buffer; m_buffer = NULL; } m_buffer = new BYTE [rectSize + 1]; if (m_buffer == NULL) return vncEncoder::EncodeRect(source, dest, rect, offx, offy); m_bufflen = rectSize; } // Translate the data into our new buffer Translate(source, m_buffer, rect); // Choose the appropriate encoding routine (for speed...) switch(m_remoteformat.bitsPerPixel) { case 8: subrects = subrectEncode8( m_buffer, dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader, rectW, rectH, (int)(m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader) ); break; case 16: subrects = subrectEncode16( (CARD16 *)m_buffer, (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader), rectW, rectH, (int)(m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader) ); break; case 32: subrects = subrectEncode32( (CARD32 *)m_buffer, (CARD8 *)(dest+sz_rfbFramebufferUpdateRectHeader+sz_rfbRREHeader), rectW, rectH, (int)(m_bufflen-sz_rfbFramebufferUpdateRectHeader-sz_rfbRREHeader) ); break; } // If we couldn't encode the rectangles then just send the data raw if (subrects < 0) return vncEncoder::EncodeRect(source, dest, rect, offx, offy); // Send the RREHeader rfbRREHeader *rreh=(rfbRREHeader *)(dest+sz_rfbFramebufferUpdateRectHeader); rreh->nSubrects = Swap32IfLE(subrects); // Update statistics for this rectangle. rectangleOverhead += sz_rfbFramebufferUpdateRectHeader; dataSize += ( rectW * rectH * m_remoteformat.bitsPerPixel) / 8; encodedSize += sz_rfbRREHeader + (m_remoteformat.bitsPerPixel / 8) + (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8)); transmittedSize += sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + (m_remoteformat.bitsPerPixel / 8) + (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8)); // Return the amount of data sent return sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + (m_remoteformat.bitsPerPixel / 8) + (subrects * (sz_rfbRectangle + m_remoteformat.bitsPerPixel / 8)); }