Added basic almost-functional Tight support, from tightrealvnc project. Decoder only.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@14 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/rfb/CMsgReaderV3.cxx b/rfb/CMsgReaderV3.cxx
index 1f974fd..5951981 100644
--- a/rfb/CMsgReaderV3.cxx
+++ b/rfb/CMsgReaderV3.cxx
@@ -78,6 +78,9 @@
     case pseudoEncodingCursor:
       readSetCursor(Point(x, y), Point(w, h));
       break;
+    case pseudoEncodingLastRect:
+      nUpdateRectsLeft = 1;     // this rectangle is the last one
+      break;
     default:
       readRect(Rect(x, y, x+w, y+h), encoding);
       break;
diff --git a/rfb/CMsgWriter.cxx b/rfb/CMsgWriter.cxx
index a7e4fb9..052c481 100644
--- a/rfb/CMsgWriter.cxx
+++ b/rfb/CMsgWriter.cxx
@@ -59,7 +59,7 @@
 void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect)
 {
   int nEncodings = 0;
-  rdr::U32 encodings[encodingMax+2];
+  rdr::U32 encodings[encodingMax+3];
   if (cp->supportsLocalCursor)
     encodings[nEncodings++] = pseudoEncodingCursor;
   if (cp->supportsDesktopResize)
@@ -75,6 +75,7 @@
       encodings[nEncodings++] = i;
     }
   }
+  encodings[nEncodings++] = pseudoEncodingLastRect;
   writeSetEncodings(nEncodings, encodings);
 }
   
diff --git a/rfb/ConnParams.cxx b/rfb/ConnParams.cxx
index 9552940..3a406ac 100644
--- a/rfb/ConnParams.cxx
+++ b/rfb/ConnParams.cxx
@@ -28,6 +28,7 @@
 ConnParams::ConnParams()
   : majorVersion(0), minorVersion(0), width(0), height(0), useCopyRect(false),
     supportsLocalCursor(false), supportsDesktopResize(false),
+    supportsLastRect(false), qualityLevel(-1),
     name_(0), nEncodings_(0), encodings_(0),
     currentEncoding_(encodingRaw), verStrPos(0)
 {
@@ -87,6 +88,8 @@
   nEncodings_ = nEncodings;
   useCopyRect = false;
   supportsLocalCursor = false;
+  supportsLastRect = false;
+  qualityLevel = -1;
   currentEncoding_ = encodingRaw;
 
   for (int i = nEncodings-1; i >= 0; i--) {
@@ -98,6 +101,11 @@
       supportsLocalCursor = true;
     else if (encodings[i] == pseudoEncodingDesktopSize)
       supportsDesktopResize = true;
+    else if (encodings[i] == pseudoEncodingLastRect)
+      supportsLastRect = true;
+    else if (encodings[i] >= pseudoEncodingQualityLevel0 &&
+             encodings[i] <= pseudoEncodingQualityLevel9)
+      qualityLevel = encodings[i] - pseudoEncodingQualityLevel0;
     else if (encodings[i] <= encodingMax && Encoder::supported(encodings[i]))
       currentEncoding_ = encodings[i];
   }
diff --git a/rfb/ConnParams.h b/rfb/ConnParams.h
index fb96a7a..9946bd0 100644
--- a/rfb/ConnParams.h
+++ b/rfb/ConnParams.h
@@ -71,6 +71,9 @@
 
     bool supportsLocalCursor;
     bool supportsDesktopResize;
+    bool supportsLastRect;
+
+    int qualityLevel;
 
   private:
 
diff --git a/rfb/Decoder.cxx b/rfb/Decoder.cxx
index 816cb6e..e04cbf9 100644
--- a/rfb/Decoder.cxx
+++ b/rfb/Decoder.cxx
@@ -22,6 +22,7 @@
 #include <rfb/RREDecoder.h>
 #include <rfb/HextileDecoder.h>
 #include <rfb/ZRLEDecoder.h>
+#include <rfb/TightDecoder.h>
 
 using namespace rfb;
 
@@ -65,4 +66,5 @@
   Decoder::registerDecoder(encodingRRE, RREDecoder::create);
   Decoder::registerDecoder(encodingHextile, HextileDecoder::create);
   Decoder::registerDecoder(encodingZRLE, ZRLEDecoder::create);
+  Decoder::registerDecoder(encodingTight, TightDecoder::create);
 }
diff --git a/rfb/Makefile.in b/rfb/Makefile.in
index 50bc04c..f656050 100644
--- a/rfb/Makefile.in
+++ b/rfb/Makefile.in
@@ -37,6 +37,7 @@
   ServerCore.cxx \
   SSecurityFactoryStandard.cxx \
   SSecurityVncAuth.cxx \
+  TightDecoder.cxx \
   TransImageGetter.cxx \
   UpdateTracker.cxx \
   VNCSConnectionST.cxx \
diff --git a/rfb/TightDecoder.cxx b/rfb/TightDecoder.cxx
new file mode 100644
index 0000000..4677647
--- /dev/null
+++ b/rfb/TightDecoder.cxx
@@ -0,0 +1,62 @@
+/* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
+ *    
+ * 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 <rfb/CMsgReader.h>
+#include <rfb/CMsgHandler.h>
+#include <rfb/TightDecoder.h>
+
+using namespace rfb;
+
+#define EXTRA_ARGS CMsgHandler* handler
+#define FILL_RECT(r, p) handler->fillRect(r, p)
+#define IMAGE_RECT(r, p) handler->imageRect(r, p)
+#define BPP 8
+#include <rfb/tightDecode.h>
+#undef BPP
+#define BPP 16
+#include <rfb/tightDecode.h>
+#undef BPP
+#define BPP 32
+#include <rfb/tightDecode.h>
+#undef BPP
+
+Decoder* TightDecoder::create(CMsgReader* reader)
+{
+  return new TightDecoder(reader);
+}
+
+TightDecoder::TightDecoder(CMsgReader* reader_) : reader(reader_)
+{
+}
+
+TightDecoder::~TightDecoder()
+{
+}
+
+void TightDecoder::readRect(const Rect& r, CMsgHandler* handler)
+{
+  rdr::InStream* is = reader->getInStream();
+  rdr::U8* buf = reader->getImageBuf(r.area());
+  switch (reader->bpp()) {
+  case 8:
+    tightDecode8 (r, is, zis, (rdr::U8*) buf, handler); break;
+  case 16:
+    tightDecode16(r, is, zis, (rdr::U16*)buf, handler); break;
+  case 32:
+    tightDecode32(r, is, zis, (rdr::U32*)buf, handler); break;
+  }
+}
diff --git a/rfb/TightDecoder.h b/rfb/TightDecoder.h
new file mode 100644
index 0000000..4403b38
--- /dev/null
+++ b/rfb/TightDecoder.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
+ *    
+ * 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.
+ */
+#ifndef __RFB_TIGHTDECODER_H__
+#define __RFB_TIGHTDECODER_H__
+
+#include <rdr/ZlibInStream.h>
+#include <rfb/Decoder.h>
+
+namespace rfb {
+
+  class TightDecoder : public Decoder {
+  public:
+    static Decoder* create(CMsgReader* reader);
+    virtual void readRect(const Rect& r, CMsgHandler* handler);
+    virtual ~TightDecoder();
+  private:
+    TightDecoder(CMsgReader* reader);
+    CMsgReader* reader;
+    rdr::ZlibInStream zis[4];
+  };
+}
+
+#endif
diff --git a/rfb/encodings.cxx b/rfb/encodings.cxx
index 56a64ee..db6e1e2 100644
--- a/rfb/encodings.cxx
+++ b/rfb/encodings.cxx
@@ -29,6 +29,7 @@
   if (strcasecmp(name, "CoRRE") == 0)    return encodingCoRRE;
   if (strcasecmp(name, "hextile") == 0)  return encodingHextile;
   if (strcasecmp(name, "ZRLE") == 0)     return encodingZRLE;
+  if (strcasecmp(name, "Tight") == 0)    return encodingTight;
   return -1;
 }
 
@@ -41,6 +42,7 @@
   case encodingCoRRE:    return "CoRRE";
   case encodingHextile:  return "hextile";
   case encodingZRLE:     return "ZRLE";
+  case encodingTight:    return "Tight";
   default:               return "[unknown encoding]";
   }
 }
diff --git a/rfb/encodings.h b/rfb/encodings.h
index ae104b4..f0f639b 100644
--- a/rfb/encodings.h
+++ b/rfb/encodings.h
@@ -25,6 +25,7 @@
   const unsigned int encodingRRE = 2;
   const unsigned int encodingCoRRE = 4;
   const unsigned int encodingHextile = 5;
+  const unsigned int encodingTight = 7;
   const unsigned int encodingZRLE = 16;
 
   const unsigned int encodingMax = 255;
@@ -32,6 +33,11 @@
   const unsigned int pseudoEncodingCursor = 0xffffff11;
   const unsigned int pseudoEncodingDesktopSize = 0xffffff21;
 
+  // TightVNC-specific
+  const unsigned int pseudoEncodingLastRect = 0xFFFFFF20;
+  const unsigned int pseudoEncodingQualityLevel0 = 0xFFFFFFE0;
+  const unsigned int pseudoEncodingQualityLevel9 = 0xFFFFFFE9;
+
   int encodingNum(const char* name);
   const char* encodingName(unsigned int num);
 }
diff --git a/rfb/tightDecode.h b/rfb/tightDecode.h
new file mode 100644
index 0000000..afc7599
--- /dev/null
+++ b/rfb/tightDecode.h
@@ -0,0 +1,185 @@
+/* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
+ *    
+ * 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.
+ */
+
+//
+// Tight decoding functions.
+//
+// This file is #included after having set the following macros:
+// BPP                - 8, 16 or 32
+// EXTRA_ARGS         - optional extra arguments
+// FILL_RECT          - fill a rectangle with a single colour
+// IMAGE_RECT         - draw a rectangle of pixel data from a buffer
+
+#include <rdr/InStream.h>
+#include <rdr/ZlibInStream.h>
+#include <rfb/Exception.h>
+#include <assert.h>
+
+namespace rfb {
+
+// CONCAT2E concatenates its arguments, expanding them if they are macros
+
+#ifndef CONCAT2E
+#define CONCAT2(a,b) a##b
+#define CONCAT2E(a,b) CONCAT2(a,b)
+#endif
+
+#define PIXEL_T rdr::CONCAT2E(U,BPP)
+#define READ_PIXEL CONCAT2E(readOpaque,BPP)
+#define TIGHT_DECODE CONCAT2E(tightDecode,BPP)
+
+#define TIGHT_MIN_TO_COMPRESS 12
+
+// Main function implementing Tight decoder
+
+void TIGHT_DECODE (const Rect& r, rdr::InStream* is,
+                   rdr::ZlibInStream zis[], PIXEL_T* buf
+#ifdef EXTRA_ARGS
+                      , EXTRA_ARGS
+#endif
+                      )
+{
+  rdr::U8 comp_ctl = is->readU8();
+
+  // Flush zlib streams if we are told by the server to do so.
+  for (int i = 0; i < 4; i++) {
+    /* FIXME: Implement flushing
+    if ((comp_ctl & 1) && m_tightZlibStreamActive[i]) {
+      int err = inflateEnd (&m_tightZlibStream[i]);
+      if (err != Z_OK) {
+        if (m_tightZlibStream[i].msg != NULL) {
+          vnclog.Print(0, _T("zlib inflateEnd() error: %s\n"),
+                       m_tightZlibStream[i].msg);
+        } else {
+          vnclog.Print(0, _T("zlib inflateEnd() error: %d\n"), err);
+        }
+        return;
+      }
+      m_tightZlibStreamActive[i] = FALSE;
+    }
+    */
+    comp_ctl >>= 1;
+  }
+
+  // "Fill" compression type.
+  if (comp_ctl == 0x08) {
+    PIXEL_T pix = is->READ_PIXEL();
+    FILL_RECT(r, pix);
+    return;
+  }
+
+  // "JPEG" compression type.
+  if (comp_ctl == 0x09) {
+    throw Exception("TightDecoder: FIXME: JPEG compression is not supported yet");
+	return;
+  }
+
+  // Quit on unsupported compression type.
+  if (comp_ctl > 0x09) {
+    throw Exception("TightDecoder: bad subencoding value received");
+    return;
+  }
+
+  // "Basic" compression type.
+  int palSize = 0;
+  PIXEL_T palette[256];
+  bool useGradient = false;
+
+  if ((comp_ctl & 0x04) != 0) {
+    rdr::U8 filterId = is->readU8();
+
+    switch (filterId) {
+    case 0x01:    // "palette" filter
+      palSize = is->readU8() + 1;
+      {
+        for (int i = 0; i < palSize; i++)
+          palette[i] = is->READ_PIXEL();
+      }
+      break;
+    case 0x02:    // "gradient" filter
+      useGradient = true;
+      break;
+    case 0x00:    // no filter
+      break;
+    default:
+      throw Exception("TightDecoder: unknown filter code received");
+      return;
+    }
+  }
+
+  int bppp = BPP;
+  if (palSize != 0) {
+    bppp = (palSize <= 2) ? 1 : 8;
+  }
+
+  // Determine if the data should be decompressed or just copied.
+  int rowSize = (r.width() * bppp + 7) / 8;
+  int dataSize = r.height() * rowSize;
+  rdr::InStream *input;
+  if (dataSize < TIGHT_MIN_TO_COMPRESS) {
+    input = is;
+  } else {
+    int length = is->readCompactLength();
+    int streamId = comp_ctl & 0x03;
+    zis[streamId].setUnderlying(is, length);
+    input = &zis[streamId];
+  }
+
+  if (palSize == 0) {
+    // Truecolor data
+    input->readBytes(buf, dataSize);
+    if (useGradient) {
+      // FIXME: Implement the "gradient" filter.
+    }
+  } else {
+    int x, y, b, w;
+    PIXEL_T *ptr = buf;
+    rdr::U8 bits;
+    if (palSize <= 2) {
+      // 2-color palette
+      w = (r.width() + 7) / 8;
+      for (y = 0; y < r.height(); y++) {
+        for (x = 0; x < r.width() / 8; x++) {
+          bits = input->readU8();
+          for (b = 7; b >= 0; b--)
+            *ptr++ = palette[bits >> b & 1];
+        }
+        if (r.width() % 8 != 0) {
+          bits = input->readU8();
+          for (b = 7; b >= 8 - r.width() % 8; b--) {
+            *ptr++ = palette[bits >> b & 1];
+          }
+        }
+      }
+    } else {
+      // 256-color palette
+      for (y = 0; y < r.height(); y++)
+        for (x = 0; x < r.width(); x++)
+          *ptr++ = palette[input->readU8()];
+    }
+  }
+
+  IMAGE_RECT(r, buf);
+
+  // zis->reset();   // FIXME: Is that needed?
+}
+
+#undef TIGHT_DECODE
+#undef READ_PIXEL
+#undef PIXEL_T
+}