Split decoders into a read and decode step
We need to split these steps up in preparation for multi-core
support. Reading needs to be done in a serial manner, whilst
decoding can be done in parallel.
This also involved a rather large cleanup of the Tight decoder.
diff --git a/common/rfb/TightDecoder.cxx b/common/rfb/TightDecoder.cxx
index 96749a9..cab49fd 100644
--- a/common/rfb/TightDecoder.cxx
+++ b/common/rfb/TightDecoder.cxx
@@ -1,5 +1,6 @@
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright 2004-2005 Cendio AB.
+ * Copyright 2009-2015 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
@@ -17,14 +18,23 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
+
+#include <assert.h>
+
#include <rdr/InStream.h>
+#include <rdr/MemInStream.h>
+#include <rdr/OutStream.h>
+
#include <rfb/ConnParams.h>
+#include <rfb/Exception.h>
#include <rfb/PixelBuffer.h>
+#include <rfb/TightConstants.h>
#include <rfb/TightDecoder.h>
using namespace rfb;
-#define TIGHT_MAX_WIDTH 2048
+static const int TIGHT_MAX_WIDTH = 2048;
+static const int TIGHT_MIN_TO_COMPRESS = 12;
#define BPP 8
#include <rfb/tightDecode.h>
@@ -45,29 +55,355 @@
}
void TightDecoder::readRect(const Rect& r, rdr::InStream* is,
- const ConnParams& cp, ModifiablePixelBuffer* pb)
+ const ConnParams& cp, rdr::OutStream* os)
{
- this->is = is;
- this->pb = pb;
- clientpf = pb->getPF();
- serverpf = cp.pf();
+ rdr::U8 comp_ctl;
- if (clientpf.equal(serverpf)) {
- /* Decode directly into the framebuffer (fast path) */
+ comp_ctl = is->readU8();
+ os->writeU8(comp_ctl);
+
+ comp_ctl >>= 4;
+
+ // "Fill" compression type.
+ if (comp_ctl == tightFill) {
+ if (cp.pf().is888())
+ os->copyBytes(is, 3);
+ else
+ os->copyBytes(is, cp.pf().bpp/8);
+ return;
+ }
+
+ // "JPEG" compression type.
+ if (comp_ctl == tightJpeg) {
+ rdr::U32 len;
+
+ len = readCompact(is);
+ os->writeOpaque32(len);
+ os->copyBytes(is, len);
+ return;
+ }
+
+ // Quit on unsupported compression type.
+ if (comp_ctl > tightMaxSubencoding)
+ throw Exception("TightDecoder: bad subencoding value received");
+
+ // "Basic" compression type.
+
+ int palSize = 0;
+
+ if (r.width() > TIGHT_MAX_WIDTH)
+ throw Exception("TightDecoder: too large rectangle (%d pixels)", r.width());
+
+ // Possible palette
+ if ((comp_ctl & tightExplicitFilter) != 0) {
+ rdr::U8 filterId;
+
+ filterId = is->readU8();
+ os->writeU8(filterId);
+
+ switch (filterId) {
+ case tightFilterPalette:
+ palSize = is->readU8() + 1;
+ os->writeU8(palSize - 1);
+
+ if (cp.pf().is888())
+ os->copyBytes(is, palSize * 3);
+ else
+ os->copyBytes(is, palSize * cp.pf().bpp/8);
+ break;
+ case tightFilterGradient:
+ if (cp.pf().bpp == 8)
+ throw Exception("TightDecoder: invalid BPP for gradient filter");
+ break;
+ case tightFilterCopy:
+ break;
+ default:
+ throw Exception("TightDecoder: unknown filter code received");
+ }
+ }
+
+ size_t rowSize, dataSize;
+
+ if (palSize != 0) {
+ if (palSize <= 2)
+ rowSize = (r.width() + 7) / 8;
+ else
+ rowSize = r.width();
+ } else if (cp.pf().is888()) {
+ rowSize = r.width() * 3;
+ } else {
+ rowSize = r.width() * cp.pf().bpp/8;
+ }
+
+ dataSize = r.height() * rowSize;
+
+ if (dataSize < TIGHT_MIN_TO_COMPRESS)
+ os->copyBytes(is, dataSize);
+ else {
+ rdr::U32 len;
+
+ len = readCompact(is);
+ os->writeOpaque32(len);
+ os->copyBytes(is, len);
+ }
+}
+
+void TightDecoder::decodeRect(const Rect& r, const void* buffer,
+ size_t buflen, const ConnParams& cp,
+ ModifiablePixelBuffer* pb)
+{
+ const rdr::U8* bufptr;
+ const PixelFormat& pf = cp.pf();
+
+ rdr::U8 comp_ctl;
+
+ bufptr = (const rdr::U8*)buffer;
+
+ assert(buflen >= 1);
+
+ comp_ctl = *bufptr;
+ bufptr += 1;
+ buflen -= 1;
+
+ // Flush zlib streams if we are told by the server to do so.
+ for (int i = 0; i < 4; i++) {
+ if (comp_ctl & 1) {
+ zis[i].reset();
+ }
+ comp_ctl >>= 1;
+ }
+
+ // "Fill" compression type.
+ if (comp_ctl == tightFill) {
+ if (pf.is888()) {
+ rdr::U8 pix[4];
+
+ assert(buflen >= 3);
+
+ pf.bufferFromRGB(pix, bufptr, 1);
+ pb->fillRect(pf, r, pix);
+ } else {
+ assert(buflen >= (size_t)pf.bpp/8);
+ pb->fillRect(pf, r, bufptr);
+ }
+ return;
+ }
+
+ // "JPEG" compression type.
+ if (comp_ctl == tightJpeg) {
+ rdr::U32 len;
+
+ int stride;
+ rdr::U8 *buf;
+
+ assert(buflen >= 4);
+
+ memcpy(&len, bufptr, 4);
+ bufptr += 4;
+ buflen -= 4;
+
+ // We always use direct decoding with JPEG images
+ buf = pb->getBufferRW(r, &stride);
+ jd.decompress(bufptr, len, buf, stride, r, pb->getPF());
+ pb->commitBufferRW(r);
+ return;
+ }
+
+ // Quit on unsupported compression type.
+ assert(comp_ctl <= tightMaxSubencoding);
+
+ // "Basic" compression type.
+
+ int palSize = 0;
+ rdr::U8 palette[256 * 4];
+ bool useGradient = false;
+
+ if ((comp_ctl & tightExplicitFilter) != 0) {
+ rdr::U8 filterId;
+
+ assert(buflen >= 1);
+
+ filterId = *bufptr;
+ bufptr += 1;
+ buflen -= 1;
+
+ switch (filterId) {
+ case tightFilterPalette:
+ assert(buflen >= 1);
+
+ palSize = *bufptr + 1;
+ bufptr += 1;
+ buflen -= 1;
+
+ if (pf.is888()) {
+ rdr::U8 tightPalette[palSize * 3];
+
+ assert(buflen >= sizeof(tightPalette));
+
+ memcpy(tightPalette, bufptr, sizeof(tightPalette));
+ bufptr += sizeof(tightPalette);
+ buflen -= sizeof(tightPalette);
+
+ pf.bufferFromRGB(palette, tightPalette, palSize);
+ } else {
+ size_t len;
+
+ len = palSize * pf.bpp/8;
+
+ assert(buflen >= len);
+
+ memcpy(palette, bufptr, len);
+ bufptr += len;
+ buflen -= len;
+ }
+ break;
+ case tightFilterGradient:
+ useGradient = true;
+ break;
+ case tightFilterCopy:
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ // Determine if the data should be decompressed or just copied.
+ size_t rowSize, dataSize;
+ rdr::U8* netbuf;
+
+ netbuf = NULL;
+
+ if (palSize != 0) {
+ if (palSize <= 2)
+ rowSize = (r.width() + 7) / 8;
+ else
+ rowSize = r.width();
+ } else if (pf.is888()) {
+ rowSize = r.width() * 3;
+ } else {
+ rowSize = r.width() * pf.bpp/8;
+ }
+
+ dataSize = r.height() * rowSize;
+
+ if (dataSize < TIGHT_MIN_TO_COMPRESS)
+ assert(buflen >= dataSize);
+ else {
+ rdr::U32 len;
+ int streamId;
+ rdr::MemInStream* ms;
+
+ assert(buflen >= 4);
+
+ memcpy(&len, bufptr, 4);
+ bufptr += 4;
+ buflen -= 4;
+
+ assert(buflen >= len);
+
+ streamId = comp_ctl & 0x03;
+ ms = new rdr::MemInStream(bufptr, len);
+ zis[streamId].setUnderlying(ms, len);
+
+ // Allocate buffer and decompress the data
+ netbuf = new rdr::U8[dataSize];
+
+ zis[streamId].readBytes(netbuf, dataSize);
+ zis[streamId].reset();
+
+ delete ms;
+
+ bufptr = netbuf;
+ buflen = dataSize;
+ }
+
+ // Time to decode the actual data
+ bool directDecode;
+
+ rdr::U8* outbuf;
+ int stride;
+
+ if (pb->getPF().equal(pf)) {
+ // Decode directly into the framebuffer (fast path)
directDecode = true;
} else {
- /* Decode into an intermediate buffer and use pixel translation */
+ // Decode into an intermediate buffer and use pixel translation
directDecode = false;
}
- switch (serverpf.bpp) {
- case 8:
- tightDecode8 (r); break;
- case 16:
- tightDecode16(r); break;
- case 32:
- tightDecode32(r); break;
+ if (directDecode)
+ outbuf = pb->getBufferRW(r, &stride);
+ else {
+ outbuf = new rdr::U8[r.area() * pf.bpp/8];
+ stride = r.width();
}
+
+ if (palSize == 0) {
+ // Truecolor data
+ if (useGradient) {
+ if (pf.is888())
+ FilterGradient24(bufptr, pf, (rdr::U32*)outbuf, stride, r);
+ else {
+ switch (pf.bpp) {
+ case 8:
+ assert(false);
+ break;
+ case 16:
+ FilterGradient(bufptr, pf, (rdr::U16*)outbuf, stride, r);
+ break;
+ case 32:
+ FilterGradient(bufptr, pf, (rdr::U32*)outbuf, stride, r);
+ break;
+ }
+ }
+ } else {
+ // Copy
+ rdr::U8* ptr = outbuf;
+ const rdr::U8* srcPtr = bufptr;
+ int w = r.width();
+ int h = r.height();
+ if (pf.is888()) {
+ while (h > 0) {
+ pf.bufferFromRGB(ptr, srcPtr, w);
+ ptr += stride * pf.bpp/8;
+ srcPtr += w * 3;
+ h--;
+ }
+ } else {
+ while (h > 0) {
+ memcpy(ptr, srcPtr, w * pf.bpp/8);
+ ptr += stride * pf.bpp/8;
+ srcPtr += w * pf.bpp/8;
+ h--;
+ }
+ }
+ }
+ } else {
+ // Indexed color
+ switch (pf.bpp) {
+ case 8:
+ FilterPalette((const rdr::U8*)palette, palSize,
+ bufptr, (rdr::U8*)outbuf, stride, r);
+ break;
+ case 16:
+ FilterPalette((const rdr::U16*)palette, palSize,
+ bufptr, (rdr::U16*)outbuf, stride, r);
+ break;
+ case 32:
+ FilterPalette((const rdr::U32*)palette, palSize,
+ bufptr, (rdr::U32*)outbuf, stride, r);
+ break;
+ }
+ }
+
+ if (directDecode)
+ pb->commitBufferRW(r);
+ else {
+ pb->imageRect(pf, r, outbuf);
+ delete [] outbuf;
+ }
+
+ delete [] netbuf;
}
rdr::U32 TightDecoder::readCompact(rdr::InStream* is)