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)