Add support for TurboVNC pseudo-encodings and Grayscale JPEG compression so that, when a TurboVNC viewer is connected, the TigerVNC Server will behave exactly like the TurboVNC Server.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4641 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/rfb/ConnParams.cxx b/common/rfb/ConnParams.cxx
index a636885..e44facc 100644
--- a/common/rfb/ConnParams.cxx
+++ b/common/rfb/ConnParams.cxx
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2011 D. R. Commander.  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
@@ -34,7 +35,8 @@
     supportsDesktopRename(false), supportsLastRect(false),
     supportsSetDesktopSize(false),
     customCompressLevel(false), compressLevel(6),
-    noJpeg(false), qualityLevel(-1), 
+    noJpeg(false), qualityLevel(-1), fineQualityLevel(-1),
+    subsampling(SUBSAMP_UNDEFINED),
     name_(0), nEncodings_(0), encodings_(0),
     currentEncoding_(encodingRaw), verStrPos(0)
 {
@@ -102,6 +104,8 @@
   compressLevel = -1;
   noJpeg = true;
   qualityLevel = -1;
+  fineQualityLevel = -1;
+  subsampling = SUBSAMP_UNDEFINED;
   currentEncoding_ = encodingRaw;
 
   for (int i = nEncodings-1; i >= 0; i--) {
@@ -132,4 +136,18 @@
     } else if (Encoder::supported(encodings[i]))
       currentEncoding_ = encodings[i];
   }
+
+  // If the TurboVNC fine quality/subsampling encodings exist, let them
+  // override the coarse TightVNC quality level
+  for (int i = nEncodings-1; i >= 0; i--) {
+    if (encodings[i] >= pseudoEncodingFineQualityLevel0 + 1 &&
+        encodings[i] <= pseudoEncodingFineQualityLevel100) {
+      noJpeg = false;
+      fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0;
+    } else if (encodings[i] >= pseudoEncodingSubsamp1X &&
+               encodings[i] <= pseudoEncodingSubsampGray) {
+      noJpeg = false;
+      subsampling = (JPEG_SUBSAMP)(encodings[i] - pseudoEncodingSubsamp1X);
+    }
+  }
 }
diff --git a/common/rfb/ConnParams.h b/common/rfb/ConnParams.h
index c25e563..f81a358 100644
--- a/common/rfb/ConnParams.h
+++ b/common/rfb/ConnParams.h
@@ -25,6 +25,7 @@
 #include <rdr/types.h>
 #include <rfb/PixelFormat.h>
 #include <rfb/ScreenSet.h>
+#include <rfb/JpegCompressor.h>
 
 namespace rdr { class InStream; }
 
@@ -84,6 +85,8 @@
     int compressLevel;
     bool noJpeg;
     int qualityLevel;
+    int fineQualityLevel;
+    JPEG_SUBSAMP subsampling;
 
   private:
 
diff --git a/common/rfb/Encoder.h b/common/rfb/Encoder.h
index 893c013..da2c5c0 100644
--- a/common/rfb/Encoder.h
+++ b/common/rfb/Encoder.h
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2011 D. R. Commander.  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
@@ -21,6 +22,7 @@
 #include <rfb/Rect.h>
 #include <rfb/encodings.h>
 #include <rfb/TransImageGetter.h>
+#include <rfb/JpegCompressor.h>
 
 namespace rfb {
   class SMsgWriter;
@@ -34,6 +36,7 @@
 
     virtual void setCompressLevel(int level) {};
     virtual void setQualityLevel(int level) {};
+    virtual void setFineQualityLevel(int quality, JPEG_SUBSAMP subsampling) {};
     virtual int getNumRects(const Rect &r) { return 1; }
 
     // writeRect() tries to write the given rectangle.  If it is unable to
diff --git a/common/rfb/JpegCompressor.cxx b/common/rfb/JpegCompressor.cxx
index 7a870b0..33f6c41 100644
--- a/common/rfb/JpegCompressor.cxx
+++ b/common/rfb/JpegCompressor.cxx
@@ -194,6 +194,8 @@
     cinfo.comp_info[0].h_samp_factor = 2;
     cinfo.comp_info[0].v_samp_factor = 1;
     break;
+  case SUBSAMP_GRAY:
+    jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE);
   default:
     cinfo.comp_info[0].h_samp_factor = 1;
     cinfo.comp_info[0].v_samp_factor = 1;
diff --git a/common/rfb/JpegCompressor.h b/common/rfb/JpegCompressor.h
index 6860b41..668c9a0 100644
--- a/common/rfb/JpegCompressor.h
+++ b/common/rfb/JpegCompressor.h
@@ -51,9 +51,11 @@
   } JPEG_DEST_MGR;
 
   enum JPEG_SUBSAMP {
-    SUBSAMP_NONE,
+    SUBSAMP_UNDEFINED = -1,
+    SUBSAMP_NONE = 0,
+    SUBSAMP_420,
     SUBSAMP_422,
-    SUBSAMP_420
+    SUBSAMP_GRAY
   };
 
   class JpegCompressor : public rdr::MemOutStream {
diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx
index 07ae37d..f47f3ee 100644
--- a/common/rfb/SMsgWriter.cxx
+++ b/common/rfb/SMsgWriter.cxx
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright (C) 2011 D. R. Commander.  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
@@ -102,6 +103,8 @@
 
   encoders[encoding]->setCompressLevel(cp->compressLevel);
   encoders[encoding]->setQualityLevel(cp->qualityLevel);
+  encoders[encoding]->setFineQualityLevel(cp->fineQualityLevel,
+                                          cp->subsampling);
 }
 
 int SMsgWriter::getNumRects(const Rect &r)
diff --git a/common/rfb/TightEncoder.cxx b/common/rfb/TightEncoder.cxx
index e35bad6..b7ef223 100644
--- a/common/rfb/TightEncoder.cxx
+++ b/common/rfb/TightEncoder.cxx
@@ -117,9 +117,21 @@
 void TightEncoder::setQualityLevel(int level)
 {
   if (level >= 0 && level <= 9) {
-    pjconf = &conf[level];
+    jpegQuality = conf[level].jpegQuality;
+    jpegSubsampling = conf[level].jpegSubsampling;
   } else {
-    pjconf = NULL;
+    jpegQuality = -1;
+    jpegSubsampling = SUBSAMP_UNDEFINED;
+  }
+}
+
+void TightEncoder::setFineQualityLevel(int quality, JPEG_SUBSAMP subsampling)
+{
+  if (quality >= 1 && quality <= 100) {
+    jpegQuality = quality;
+  }
+  if (subsampling >= SUBSAMP_NONE && subsampling <= SUBSAMP_GRAY) {
+    jpegSubsampling = subsampling;
   }
 }
 
@@ -333,6 +345,14 @@
       sr.setXYWH(dx, dy, dw, dh);
       if (checkSolidTile(sr, &colorValue, false)) {
 
+         if (jpegSubsampling == SUBSAMP_GRAY && jpegQuality != -1) {
+           Colour rgb;
+           serverpf.rgbFromPixel(colorValue, NULL, &rgb);
+           rdr::U32 lum = ((257 * rgb.r) + (504 * rgb.g) + (98 * rgb.b)
+                           + 16500) / 1000;
+           colorValue = lum + (lum << 8) + (lum << 16);
+         }
+
         // Get dimensions of solid-color area.
         sr.setXYWH(dx, dy, r.br.x - dx, r.br.y - dy);
         findBestSolidArea(sr, colorValue, bestr);
diff --git a/common/rfb/TightEncoder.h b/common/rfb/TightEncoder.h
index ae9672c..755d882 100644
--- a/common/rfb/TightEncoder.h
+++ b/common/rfb/TightEncoder.h
@@ -21,7 +21,6 @@
 
 #include <rdr/MemOutStream.h>
 #include <rdr/ZlibOutStream.h>
-#include <rfb/JpegCompressor.h>
 #include <rfb/TransImageGetter.h>
 #include <rfb/Encoder.h>
 
@@ -40,7 +39,7 @@
     int idxMaxColorsDivisor;
     int palMaxColorsWithJPEG;
     int jpegQuality;
-    JPEG_SUBSAMP jpegSubSample;
+    JPEG_SUBSAMP jpegSubsampling;
   };
 
   //
@@ -81,6 +80,7 @@
     static Encoder* create(SMsgWriter* writer);
     virtual void setCompressLevel(int level);
     virtual void setQualityLevel(int level);
+    virtual void setFineQualityLevel(int quality, JPEG_SUBSAMP subsampling);
     virtual int getNumRects(const Rect &r);
     virtual bool writeRect(const Rect& r, TransImageGetter* ig, Rect* actual);
     virtual ~TightEncoder();
@@ -153,7 +153,8 @@
     static const TIGHT_CONF conf[];
 
     const TIGHT_CONF* pconf;
-    const TIGHT_CONF* pjconf;
+    int jpegQuality;
+    JPEG_SUBSAMP jpegSubsampling;
   };
 
 }
diff --git a/common/rfb/encodings.h b/common/rfb/encodings.h
index 16cd73a..40f5f10 100644
--- a/common/rfb/encodings.h
+++ b/common/rfb/encodings.h
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyeight (C) 2011 D. R. Commander.  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
@@ -43,6 +44,16 @@
   const int pseudoEncodingCompressLevel0 = -256;
   const int pseudoEncodingCompressLevel9 = -247;
 
+  // TurboVNC-specific
+  const int pseudoEncodingFineQualityLevel0 = -512;
+  const int pseudoEncodingFineQualityLevel100 = -412;
+  const int pseudoEncodingSubsamp1X = -768;
+  const int pseudoEncodingSubsamp4X = -767;
+  const int pseudoEncodingSubsamp2X = -766;
+  const int pseudoEncodingSubsampGray = -765;
+  const int pseudoEncodingSubsamp8X = -764;
+  const int pseudoEncodingSubsamp16X = -763;
+
   int encodingNum(const char* name);
   const char* encodingName(int num);
 }
diff --git a/common/rfb/tightEncode.h b/common/rfb/tightEncode.h
index 9b2b29d..7cabb0a 100644
--- a/common/rfb/tightEncode.h
+++ b/common/rfb/tightEncode.h
@@ -200,9 +200,11 @@
 
   if (forceSolid)
     palNumColors = 1;
+  else if (jpegSubsampling == SUBSAMP_GRAY && jpegQuality != -1)
+    palNumColors = 0;
   else {
     palMaxColors = r.area() / pconf->idxMaxColorsDivisor;
-    if (pjconf != NULL) palMaxColors = pconf->palMaxColorsWithJPEG;
+    if (jpegQuality != -1) palMaxColors = pconf->palMaxColorsWithJPEG;
     if (palMaxColors < 2 && r.area() >= pconf->monoMinRectSize) {
       palMaxColors = 2;
     }
@@ -211,7 +213,7 @@
       // This is so we can avoid translating the pixels when compressing
       // with JPEG, since it is unnecessary
       FAST_FILL_PALETTE(r, pixels, stride);
-      if(palNumColors != 0 || pjconf == NULL) {
+      if(palNumColors != 0 || jpegQuality == -1) {
         pixels = (PIXEL_T *)writer->getImageBuf(r.area());
         stride = r.width();
         ig->getImage(pixels, r);
@@ -229,7 +231,7 @@
   case 0:
     // Truecolor image
 #if (BPP != 8)
-    if (pjconf != NULL) {
+    if (jpegQuality != -1) {
       ENCODE_JPEG_RECT(os, pixels, stride, r);
       break;
     }
@@ -396,7 +398,7 @@
 {
   jc.clear();
   jc.compress((rdr::U8 *)buf, stride * serverpf.bpp / 8, r, serverpf,
-    pjconf->jpegQuality, pjconf->jpegSubSample);
+    jpegQuality, jpegSubsampling);
   os->writeU8(0x09 << 4);
   os->writeCompactLength(jc.length());
   os->writeBytes(jc.data(), jc.length());