/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. * Copyright (C) 2011 D. R. Commander. All Rights Reserved. * Copyright 2014 Pierre Ossman for Cendio AB * * 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 #include #include #include #include #include #include #include #include using namespace rfb; struct TightConf { int idxZlibLevel, monoZlibLevel, rawZlibLevel; }; // // Compression level stuff. The following array contains zlib // settings for each of 10 compression levels (0..9). // // 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. static const TightConf conf[10] = { { 0, 0, 0 }, // 0 { 1, 1, 1 }, // 1 { 3, 3, 2 }, // 2 { 5, 5, 2 }, // 3 { 6, 7, 3 }, // 4 { 7, 8, 4 }, // 5 { 7, 8, 5 }, // 6 { 8, 9, 6 }, // 7 { 9, 9, 7 }, // 8 { 9, 9, 9 } // 9 }; TightEncoder::TightEncoder(SConnection* conn) : Encoder(conn, encodingTight, EncoderPlain, 256) { setCompressLevel(-1); } TightEncoder::~TightEncoder() { } bool TightEncoder::isSupported() { return conn->cp.supportsEncoding(encodingTight); } void TightEncoder::setCompressLevel(int level) { if (level < 0 || level > 9) level = 2; idxZlibLevel = conf[level].idxZlibLevel; monoZlibLevel = conf[level].monoZlibLevel; rawZlibLevel = conf[level].rawZlibLevel; } void TightEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) { switch (palette.size()) { case 0: writeFullColourRect(pb, palette); break; case 1: Encoder::writeSolidRect(pb, palette); break; case 2: writeMonoRect(pb, palette); break; default: writeIndexedRect(pb, palette); } } void TightEncoder::writeSolidRect(int width, int height, const PixelFormat& pf, const rdr::U8* colour) { rdr::OutStream* os; os = conn->getOutStream(); os->writeU8(tightFill << 4); writePixels(colour, pf, 1, os); } void TightEncoder::writeMonoRect(const PixelBuffer* pb, const Palette& palette) { const rdr::U8* buffer; int stride; buffer = pb->getBuffer(pb->getRect(), &stride); switch (pb->getPF().bpp) { case 32: writeMonoRect(pb->width(), pb->height(), (rdr::U32*)buffer, stride, pb->getPF(), palette); break; case 16: writeMonoRect(pb->width(), pb->height(), (rdr::U16*)buffer, stride, pb->getPF(), palette); break; default: writeMonoRect(pb->width(), pb->height(), (rdr::U8*)buffer, stride, pb->getPF(), palette); } } void TightEncoder::writeIndexedRect(const PixelBuffer* pb, const Palette& palette) { const rdr::U8* buffer; int stride; buffer = pb->getBuffer(pb->getRect(), &stride); switch (pb->getPF().bpp) { case 32: writeIndexedRect(pb->width(), pb->height(), (rdr::U32*)buffer, stride, pb->getPF(), palette); break; case 16: writeIndexedRect(pb->width(), pb->height(), (rdr::U16*)buffer, stride, pb->getPF(), palette); break; default: // It's more efficient to just do raw pixels writeFullColourRect(pb, palette); } } void TightEncoder::writeFullColourRect(const PixelBuffer* pb, const Palette& palette) { const int streamId = 0; rdr::OutStream* os; rdr::OutStream* zos; int length; const rdr::U8* buffer; int stride, h; os = conn->getOutStream(); os->writeU8(streamId << 4); // Set up compression if ((pb->getPF().bpp != 32) || !pb->getPF().is888()) length = pb->getRect().area() * pb->getPF().bpp/8; else length = pb->getRect().area() * 3; zos = getZlibOutStream(streamId, rawZlibLevel, length); // And then just dump all the raw pixels buffer = pb->getBuffer(pb->getRect(), &stride); h = pb->height(); while (h--) { writePixels(buffer, pb->getPF(), pb->width(), zos); buffer += stride * pb->getPF().bpp/8; } // Finish the zlib stream flushZlibOutStream(zos); } void TightEncoder::writePixels(const rdr::U8* buffer, const PixelFormat& pf, unsigned int count, rdr::OutStream* os) { rdr::U8 rgb[2048]; if ((pf.bpp != 32) || !pf.is888()) { os->writeBytes(buffer, count * pf.bpp/8); return; } while (count) { unsigned int iter_count; iter_count = sizeof(rgb)/3; if (iter_count > count) iter_count = count; pf.rgbFromBuffer(rgb, buffer, iter_count); os->writeBytes(rgb, iter_count * 3); buffer += iter_count * pf.bpp/8; count -= iter_count; } } void TightEncoder::writeCompact(rdr::OutStream* os, rdr::U32 value) { rdr::U8 b; b = value & 0x7F; if (value <= 0x7F) { os->writeU8(b); } else { os->writeU8(b | 0x80); b = value >> 7 & 0x7F; if (value <= 0x3FFF) { os->writeU8(b); } else { os->writeU8(b | 0x80); os->writeU8(value >> 14 & 0xFF); } } } rdr::OutStream* TightEncoder::getZlibOutStream(int streamId, int level, size_t length) { // Minimum amount of data to be compressed. This value should not be // changed, doing so will break compatibility with existing clients. if (length < 12) return conn->getOutStream(); assert(streamId >= 0); assert(streamId < 4); zlibStreams[streamId].setUnderlying(&memStream); zlibStreams[streamId].setCompressionLevel(level); return &zlibStreams[streamId]; } void TightEncoder::flushZlibOutStream(rdr::OutStream* os_) { rdr::OutStream* os; rdr::ZlibOutStream* zos; zos = dynamic_cast(os_); if (zos == NULL) return; zos->flush(); zos->setUnderlying(NULL); os = conn->getOutStream(); writeCompact(os, memStream.length()); os->writeBytes(memStream.data(), memStream.length()); memStream.clear(); } // // Including BPP-dependent implementation of the encoder. // #define BPP 8 #include #undef BPP #define BPP 16 #include #undef BPP #define BPP 32 #include #undef BPP