📄 vncencodetight.cpp
字号:
// Copyright (C) 2000 Constantin Kaplinsky. All Rights Reserved.
// 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.
// vncEncodeTight
// This file implements the vncEncoder-derived vncEncodeTight class.
// This class overrides some vncEncoder functions to produce a bitmap
// to Tight encoder. Tight is much more efficient than RAW format on
// most screen data and usually 2..10 times as efficient as hextile.
// It's also more efficient than Zlib encoding in most cases.
// But note that tight compression may use more CPU time on the server.
// However, over slower (128kbps or less) connections, the reduction
// in data transmitted usually outweighs the extra latency added
// while the server CPU performs the compression algorithms.
#include "vncEncodeTight.h"
// 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: m_conf[9].maxRectSize should be >= m_conf[i].maxRectSize,
// where i in [0..8]. RequiredBuffSize() method depends on this.
const TIGHT_CONF vncEncodeTight::m_conf[10] = {
{ 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 },
{ 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 },
{ 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 },
{ 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 },
{ 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 },
{ 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 },
{ 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 },
{ 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 },
{ 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 },
{ 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 }
};
vncEncodeTight::vncEncodeTight()
{
m_buffer = NULL;
m_bufflen = 0;
m_hdrBuffer = new BYTE [sz_rfbFramebufferUpdateRectHeader + 8 + 256*4];
m_prevRowBuf = NULL;
for (int i = 0; i < 4; i++)
m_zsActive[i] = false;
}
vncEncodeTight::~vncEncodeTight()
{
if (m_buffer != NULL) {
delete[] m_buffer;
m_buffer = NULL;
}
delete[] m_hdrBuffer;
for (int i = 0; i < 4; i++) {
if (m_zsActive[i])
deflateEnd(&m_zsStruct[i]);
m_zsActive[i] = false;
}
}
void
vncEncodeTight::Init()
{
vncEncoder::Init();
}
/*****************************************************************************
*
* Routines to implement Tight Encoding.
*
*/
UINT
vncEncodeTight::RequiredBuffSize(UINT width, UINT height)
{
// FIXME: Use actual compression level instead of 9?
int result = m_conf[9].maxRectSize * (m_remoteformat.bitsPerPixel / 8);
result += result / 100 + 16;
return result;
}
UINT
vncEncodeTight::NumCodedRects(RECT &rect)
{
const int w = rect.right - rect.left;
const int h = rect.bottom - rect.top;
// No matter how many rectangles we will send if LastRect markers
// are used to terminate rectangle stream.
if (m_use_lastrect && w * h >= MIN_SPLIT_RECT_SIZE) {
return 0;
}
const int maxRectSize = m_conf[m_compresslevel].maxRectSize;
const int maxRectWidth = m_conf[m_compresslevel].maxRectWidth;
if (w > maxRectWidth || w * h > maxRectSize) {
const int subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
const int subrectMaxHeight = maxRectSize / subrectMaxWidth;
return (((w - 1) / maxRectWidth + 1) *
((h - 1) / subrectMaxHeight + 1));
} else {
return 1;
}
}
UINT
vncEncodeTight::EncodeRect(BYTE *source, VSocket *outConn, BYTE *dest,
const RECT &rect, int offx, int offy)
{
int x = rect.left, y = rect.top;
int w = rect.right - x, h = rect.bottom - y;
offsetx = offx;
offsety = offy;
const int maxRectSize = m_conf[m_compresslevel].maxRectSize;
const int rawDataSize = maxRectSize * (m_remoteformat.bitsPerPixel / 8);
if (m_bufflen < rawDataSize) {
if (m_buffer != NULL)
delete [] m_buffer;
m_buffer = new BYTE [rawDataSize+1];
if (m_buffer == NULL)
return vncEncoder::EncodeRect(source, dest, rect, offsetx, offsety);
m_bufflen = rawDataSize;
}
if ( m_remoteformat.depth == 24 && m_remoteformat.redMax == 0xFF &&
m_remoteformat.greenMax == 0xFF && m_remoteformat.blueMax == 0xFF ) {
m_usePixelFormat24 = true;
} else {
m_usePixelFormat24 = false;
}
if (!m_use_lastrect || w * h < MIN_SPLIT_RECT_SIZE)
return EncodeRectSimple(source, outConn, dest, rect);
// Calculate maximum number of rows in one non-solid rectangle.
int nMaxRows;
{
int maxRectSize = m_conf[m_compresslevel].maxRectSize;
int maxRectWidth = m_conf[m_compresslevel].maxRectWidth;
int nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
nMaxRows = maxRectSize / nMaxWidth;
}
// Try to find large solid-color areas and send them separately.
CARD32 colorValue;
int x_best, y_best, w_best, h_best;
int dx, dy, dw, dh;
for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) {
// If a rectangle becomes too large, send its upper part now.
if (dy - y >= nMaxRows) {
RECT upperRect;
SetRect(&upperRect, x, y, x + w, y + nMaxRows);
int size = EncodeRectSimple(source, outConn, dest, upperRect);
outConn->SendQueued((char *)dest, size);
y += nMaxRows;
h -= nMaxRows;
}
dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ?
MAX_SPLIT_TILE_SIZE : (y + h - dy);
for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) {
dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ?
MAX_SPLIT_TILE_SIZE : (x + w - dx);
if (CheckSolidTile(source, dx, dy, dw, dh, &colorValue, FALSE)) {
// Get dimensions of solid-color area.
FindBestSolidArea(source, dx, dy, w - (dx - x), h - (dy - y),
colorValue, &w_best, &h_best);
// Make sure a solid rectangle is large enough
// (or the whole rectangle is of the same color).
if ( w_best * h_best != w * h &&
w_best * h_best < MIN_SOLID_SUBRECT_SIZE )
continue;
// Try to extend solid rectangle to maximum size.
x_best = dx; y_best = dy;
ExtendSolidArea(source, x, y, w, h, colorValue,
&x_best, &y_best, &w_best, &h_best);
// Compute dimensions of surrounding rectangles.
RECT rects[4];
SetRect(&rects[0],
x, y, x + w, y_best);
SetRect(&rects[1],
x, y_best, x_best, y_best + h_best);
SetRect(&rects[2],
x_best + w_best, y_best, x + w, y_best + h_best);
SetRect(&rects[3],
x, y_best + h_best, x + w, y + h);
// Send solid-color area and surrounding rectangles.
for (int i = 0; i < 4; i++) {
if (i == 2) {
RECT onePixel;
SetRect(&onePixel,
x_best, y_best, x_best + 1, y_best + 1);
Translate(source, m_buffer, onePixel);
SendTightHeader(x_best, y_best, w_best, h_best);
int size = SendSolidRect(dest);
outConn->SendQueued((char *)m_hdrBuffer, m_hdrBufferBytes);
outConn->SendQueued((char *)dest, size);
encodedSize += (m_hdrBufferBytes + size -
sz_rfbFramebufferUpdateRectHeader);
transmittedSize += (m_hdrBufferBytes + size);
}
if ( rects[i].left == rects[i].right ||
rects[i].top == rects[i].bottom ) {
continue;
}
int size = EncodeRect(source, outConn, dest, rects[i], offsetx, offsety);
outConn->SendQueued((char *)dest, size);
}
// Return after all recursive calls done (0 == data sent).
return 0;
}
}
}
// No suitable solid-color rectangles found.
return EncodeRectSimple(source, outConn, dest, rect);
}
void
vncEncodeTight::FindBestSolidArea(BYTE *source, int x, int y, int w, int h,
CARD32 colorValue, int *w_ptr, int *h_ptr)
{
int dx, dy, dw, dh;
int w_prev;
int w_best = 0, h_best = 0;
w_prev = w;
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;
if (!CheckSolidTile(source, 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(source, 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_ptr = w_best;
*h_ptr = h_best;
}
void vncEncodeTight::ExtendSolidArea(BYTE *source, int x, int y, int w, int h,
CARD32 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(source, *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(source, *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(source, 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(source, cx, *y_ptr, 1, *h_ptr,
&colorValue, TRUE);
cx++ );
*w_ptr += cx - (*x_ptr + *w_ptr);
}
bool
vncEncodeTight::CheckSolidTile(BYTE *source, int x, int y, int w, int h,
CARD32 *colorPtr, bool needSameColor)
{
switch(m_localformat.bitsPerPixel) {
case 32:
return CheckSolidTile32(source, x, y, w, h, colorPtr, needSameColor);
case 16:
return CheckSolidTile16(source, x, y, w, h, colorPtr, needSameColor);
default:
return CheckSolidTile8(source, x, y, w, h, colorPtr, needSameColor);
}
}
#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \
\
bool \
vncEncodeTight::CheckSolidTile##bpp(BYTE *source, int x, int y, int w, int h, \
CARD32 *colorPtr, bool needSameColor) \
{ \
CARD##bpp *fbptr; \
CARD##bpp colorValue; \
int dx, dy; \
\
fbptr = (CARD##bpp *) \
&source[y * m_bytesPerRow + x * (bpp/8)]; \
\
colorValue = *fbptr; \
if (needSameColor && (CARD32)colorValue != *colorPtr) \
return false; \
\
for (dy = 0; dy < h; dy++) { \
for (dx = 0; dx < w; dx++) { \
if (colorValue != fbptr[dx]) \
return false; \
} \
fbptr = (CARD##bpp *)((BYTE *)fbptr + m_bytesPerRow); \
} \
\
*colorPtr = (CARD32)colorValue; \
return true; \
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -