Further optimizations to the Tight encoder to eliminate getImage() overhead.  The encoder now directly accesses the framebuffer for solid rectangle computation, JPEG encoding, and color counting (if pixel translation is not required.)  Also moved everything in tightEncode.h into the TightEncoder class to eliminate all of the static mess (this will be important later on if we decide to multi-thread the encoder.)


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4631 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/rfb/Encoder.h b/common/rfb/Encoder.h
index 2a6e2f6..893c013 100644
--- a/common/rfb/Encoder.h
+++ b/common/rfb/Encoder.h
@@ -20,6 +20,7 @@
 
 #include <rfb/Rect.h>
 #include <rfb/encodings.h>
+#include <rfb/TransImageGetter.h>
 
 namespace rfb {
   class SMsgWriter;
@@ -38,7 +39,8 @@
     // writeRect() tries to write the given rectangle.  If it is unable to
     // write the whole rectangle it returns false and sets actual to the actual
     // rectangle which was updated.
-    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual)=0;
+    virtual bool writeRect(const Rect& r, TransImageGetter* ig,
+                           Rect* actual)=0;
 
     static bool supported(int encoding);
     static Encoder* createEncoder(int encoding, SMsgWriter* writer);
diff --git a/common/rfb/HextileEncoder.cxx b/common/rfb/HextileEncoder.cxx
index ba71d56..73f1f57 100644
--- a/common/rfb/HextileEncoder.cxx
+++ b/common/rfb/HextileEncoder.cxx
@@ -58,7 +58,8 @@
 {
 }
 
-bool HextileEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+bool HextileEncoder::writeRect(const Rect& r, TransImageGetter* ig,
+                               Rect* actual)
 {
   writer->startRect(r, encodingHextile);
   rdr::OutStream* os = writer->getOutStream();
diff --git a/common/rfb/HextileEncoder.h b/common/rfb/HextileEncoder.h
index c78107a..6b89643 100644
--- a/common/rfb/HextileEncoder.h
+++ b/common/rfb/HextileEncoder.h
@@ -25,7 +25,7 @@
   class HextileEncoder : public Encoder {
   public:
     static Encoder* create(SMsgWriter* writer);
-    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual bool writeRect(const Rect& r, TransImageGetter* ig, Rect* actual);
     virtual ~HextileEncoder();
   private:
     HextileEncoder(SMsgWriter* writer);
diff --git a/common/rfb/JpegCompressor.cxx b/common/rfb/JpegCompressor.cxx
index e203560..a55fee7 100644
--- a/common/rfb/JpegCompressor.cxx
+++ b/common/rfb/JpegCompressor.cxx
@@ -114,7 +114,7 @@
   jpeg_destroy_compress(&cinfo);
 }
 
-void JpegCompressor::compress(rdr::U8 *buf, const Rect& r,
+void JpegCompressor::compress(rdr::U8 *buf, int pitch, const Rect& r,
   const PixelFormat& pf, int quality, JPEG_SUBSAMP subsamp)
 {
   int w = r.width();
@@ -168,10 +168,13 @@
   }
 #endif
 
+  if (pitch == 0) pitch = w * pixelsize;
+
   if (cinfo.in_color_space == JCS_RGB) {
     srcBuf = new rdr::U8[w * h * pixelsize];
     srcBufIsTemp = true;
-    pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w * h);
+    pf.rgbFromBuffer(srcBuf, (const rdr::U8 *)buf, w, pitch, h);
+    pitch = w * pixelsize;
   }
 
   cinfo.input_components = pixelsize;
@@ -197,7 +200,7 @@
 
   rowPointer = new JSAMPROW[h];
   for (int dy = 0; dy < h; dy++)
-    rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * w * pixelsize]);
+    rowPointer[dy] = (JSAMPROW)(&srcBuf[dy * pitch]);
 
   jpeg_start_compress(&cinfo, TRUE);
   while (cinfo.next_scanline < cinfo.image_height)
diff --git a/common/rfb/JpegCompressor.h b/common/rfb/JpegCompressor.h
index 5a9c2fd..6860b41 100644
--- a/common/rfb/JpegCompressor.h
+++ b/common/rfb/JpegCompressor.h
@@ -63,7 +63,7 @@
     JpegCompressor(int bufferLen = 128*1024);
     virtual ~JpegCompressor();
 
-    void compress(rdr::U8 *, const Rect&, const PixelFormat&, int,
+    void compress(rdr::U8 *, int, const Rect&, const PixelFormat&, int,
       JPEG_SUBSAMP);
 
     void writeBytes(const void*, int);
diff --git a/common/rfb/PixelBuffer.h b/common/rfb/PixelBuffer.h
index 4a13923..fc35a7d 100644
--- a/common/rfb/PixelBuffer.h
+++ b/common/rfb/PixelBuffer.h
@@ -68,6 +68,7 @@
     //   The pointer is to the top-left pixel of the specified Rect.
     //   The buffer stride (in pixels) is returned.
     virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) = 0;
+    virtual rdr::U8* getPixelsRW(const Rect& r, int* stride) = 0;
 
     // Get pixel data for a given part of the buffer
     //   Data is copied into the supplied buffer, with the specified
@@ -107,7 +108,7 @@
     virtual int getStride() const;
 
     // Get a pointer to specified pixel data
-    virtual rdr::U8* getPixelsRW(const Rect& r, int* stride);
+    rdr::U8* getPixelsRW(const Rect& r, int* stride);
     virtual const rdr::U8* getPixelsR(const Rect& r, int* stride) {
       return getPixelsRW(r, stride);
     }
diff --git a/common/rfb/PixelFormat.cxx b/common/rfb/PixelFormat.cxx
index 11c2d7a..013cceb 100644
--- a/common/rfb/PixelFormat.cxx
+++ b/common/rfb/PixelFormat.cxx
@@ -1,5 +1,6 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright 2009 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
  * it under the terms of the GNU General Public License as published by
@@ -295,6 +296,51 @@
 }
 
 
+void PixelFormat::rgbFromBuffer(rdr::U8* dst, const rdr::U8* src,
+                                int w, int pitch, int h, ColourMap* cm) const
+{
+  rdr::U8 *rowptr, *colptr;
+
+  if (is888()) {
+    // Optimised common case
+    int rindex, gindex, bindex;
+
+    if (bigEndian) {
+      rindex = (24 - redShift)/8;
+      gindex = (24 - greenShift)/8;
+      bindex = (24 - blueShift)/8;
+    } else {
+      rindex = redShift/8;
+      gindex = greenShift/8;
+      bindex = blueShift/8;
+    }
+
+    for(rowptr = (rdr::U8 *)src; rowptr < &src[pitch * h]; rowptr += pitch) {
+      for(colptr = rowptr; colptr < &rowptr[w * 4]; colptr += 4) {
+        *(dst++) = colptr[rindex];
+        *(dst++) = colptr[gindex];
+        *(dst++) = colptr[bindex];
+      }
+    }
+  } else {
+    // Generic code
+    Pixel p;
+    rdr::U8 r, g, b;
+
+    for(rowptr = (rdr::U8 *)src; rowptr < &src[pitch * h]; rowptr += pitch) {
+      for(colptr = rowptr; colptr < &rowptr[w * bpp/8]; colptr += bpp/8) {
+        p = pixelFromBuffer(colptr);
+
+        rgbFromPixel(p, cm, &r, &g, &b);
+        *(dst++) = r;
+        *(dst++) = g;
+        *(dst++) = b;
+      }
+    }
+  }
+}
+
+
 void PixelFormat::print(char* str, int len) const
 {
   // Unfortunately snprintf is not widely available so we build the string up
diff --git a/common/rfb/PixelFormat.h b/common/rfb/PixelFormat.h
index c1de09a..6566e38 100644
--- a/common/rfb/PixelFormat.h
+++ b/common/rfb/PixelFormat.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
@@ -59,6 +60,8 @@
 
     void rgbFromBuffer(rdr::U16* dst, const rdr::U8* src, int pixels, ColourMap* cm=0) const;
     void rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int pixels, ColourMap* cm=0) const;
+    void rgbFromBuffer(rdr::U8* dst, const rdr::U8* src, int w, int pitch,
+                       int h, ColourMap* cm=0) const;
 
     void print(char* str, int len) const;
     bool parse(const char* str);
diff --git a/common/rfb/PixelTransformer.cxx b/common/rfb/PixelTransformer.cxx
index ea43d9c..c737f4d 100644
--- a/common/rfb/PixelTransformer.cxx
+++ b/common/rfb/PixelTransformer.cxx
@@ -1,5 +1,6 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ * 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
@@ -320,3 +321,8 @@
              outPF, out, outStride,
              inRect.width(), inRect.height());
 }
+
+bool PixelTransformer::willTransform(void)
+{
+  return transFn != NULL && transFn != noTransFn;
+}
diff --git a/common/rfb/PixelTransformer.h b/common/rfb/PixelTransformer.h
index 54b05a5..a368b63 100644
--- a/common/rfb/PixelTransformer.h
+++ b/common/rfb/PixelTransformer.h
@@ -1,5 +1,6 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
+ * 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
@@ -20,6 +21,7 @@
 #ifndef __RFB_PIXELTRANSFORMER_H__
 #define __RFB_PIXELTRANSFORMER_H__
 
+#include <stdlib.h>
 #include <rfb/Rect.h>
 #include <rfb/PixelFormat.h>
 
@@ -83,6 +85,8 @@
     void translateRect(void* inPtr, int inStride, Rect inRect,
                        void* outPtr, int outStride, Point outCoord) const;
 
+    bool willTransform(void);
+
   private:
     bool economic;
 
diff --git a/common/rfb/RREEncoder.cxx b/common/rfb/RREEncoder.cxx
index b000e9d..1b86986 100644
--- a/common/rfb/RREEncoder.cxx
+++ b/common/rfb/RREEncoder.cxx
@@ -46,7 +46,7 @@
 {
 }
 
-bool RREEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+bool RREEncoder::writeRect(const Rect& r, TransImageGetter* ig, Rect* actual)
 {
   int w = r.width();
   int h = r.height();
diff --git a/common/rfb/RREEncoder.h b/common/rfb/RREEncoder.h
index 1281410..6178d57 100644
--- a/common/rfb/RREEncoder.h
+++ b/common/rfb/RREEncoder.h
@@ -26,7 +26,7 @@
   class RREEncoder : public Encoder {
   public:
     static Encoder* create(SMsgWriter* writer);
-    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual bool writeRect(const Rect& r, TransImageGetter* ig, Rect* actual);
     virtual ~RREEncoder();
   private:
     RREEncoder(SMsgWriter* writer);
diff --git a/common/rfb/RawEncoder.cxx b/common/rfb/RawEncoder.cxx
index a2545b6..5612cb8 100644
--- a/common/rfb/RawEncoder.cxx
+++ b/common/rfb/RawEncoder.cxx
@@ -36,7 +36,7 @@
 {
 }
 
-bool RawEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+bool RawEncoder::writeRect(const Rect& r, TransImageGetter* ig, Rect* actual)
 {
   int x = r.tl.x;
   int y = r.tl.y;
diff --git a/common/rfb/RawEncoder.h b/common/rfb/RawEncoder.h
index 1b9ad92..34dba0b 100644
--- a/common/rfb/RawEncoder.h
+++ b/common/rfb/RawEncoder.h
@@ -25,7 +25,7 @@
   class RawEncoder : public Encoder {
   public:
     static Encoder* create(SMsgWriter* writer);
-    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual bool writeRect(const Rect& r, TransImageGetter* ig, Rect* actual);
     virtual ~RawEncoder();
   private:
     RawEncoder(SMsgWriter* writer);
diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx
index 2262be0..f0a97c5 100644
--- a/common/rfb/SMsgWriter.cxx
+++ b/common/rfb/SMsgWriter.cxx
@@ -142,7 +142,7 @@
   vlog.error("writeNoDataUpdate() called");
 }
 
-void SMsgWriter::writeRects(const UpdateInfo& ui, ImageGetter* ig,
+void SMsgWriter::writeRects(const UpdateInfo& ui, TransImageGetter* ig,
                             Region* updatedRegion)
 {
   std::vector<Rect> rects;
@@ -164,13 +164,13 @@
   }
 }
 
-bool SMsgWriter::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+bool SMsgWriter::writeRect(const Rect& r, TransImageGetter* ig, Rect* actual)
 {
   return writeRect(r, cp->currentEncoding(), ig, actual);
 }
 
 bool SMsgWriter::writeRect(const Rect& r, int encoding,
-                           ImageGetter* ig, Rect* actual)
+                           TransImageGetter* ig, Rect* actual)
 {
   if (!encoders[encoding]) {
     encoders[encoding] = Encoder::createEncoder(encoding, this);
diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h
index 007d758..8112d01 100644
--- a/common/rfb/SMsgWriter.h
+++ b/common/rfb/SMsgWriter.h
@@ -129,7 +129,7 @@
     // before the first writeRects() call and writeFrameBufferUpdateEnd() after
     // the last one.  It returns the actual region sent to the client, which
     // may be smaller than the update passed in.
-    virtual void writeRects(const UpdateInfo& update, ImageGetter* ig,
+    virtual void writeRects(const UpdateInfo& update, TransImageGetter* ig,
                             Region* updatedRegion);
 
     // To construct a framebuffer update you can call
@@ -144,9 +144,9 @@
     // writeRect() tries to write the given rectangle.  If it is unable to
     // write the whole rectangle it returns false and sets actual to the actual
     // rectangle which was updated.
-    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual bool writeRect(const Rect& r, TransImageGetter* ig, Rect* actual);
     virtual bool writeRect(const Rect& r, int encoding,
-                           ImageGetter* ig, Rect* actual);
+                           TransImageGetter* ig, Rect* actual);
 
     virtual void writeCopyRect(const Rect& r, int srcX, int srcY);
 
diff --git a/common/rfb/TightEncoder.cxx b/common/rfb/TightEncoder.cxx
index 52673a6..fa7ee9a 100644
--- a/common/rfb/TightEncoder.cxx
+++ b/common/rfb/TightEncoder.cxx
@@ -17,7 +17,6 @@
  * USA.
  */
 #include <rdr/OutStream.h>
-#include <rfb/ImageGetter.h>
 #include <rfb/encodings.h>
 #include <rfb/ConnParams.h>
 #include <rfb/SMsgWriter.h>
@@ -77,16 +76,10 @@
 };
 const int TightEncoder::defaultCompressLevel = 1;
 
-// FIXME: Not good to mirror TightEncoder's members here.
-static const TIGHT_CONF* s_pconf;
-static const TIGHT_CONF* s_pjconf;
-
 //
 // Including BPP-dependent implementation of the encoder.
 //
 
-#define EXTRA_ARGS ImageGetter* ig
-#define GET_IMAGE_INTO_BUF(r,buf) ig->getImage(buf, r);
 #define BPP 8
 #include <rfb/tightEncode.h>
 #undef BPP
@@ -130,21 +123,20 @@
   }
 }
 
-bool TightEncoder::checkSolidTile(Rect& r, ImageGetter *ig, rdr::U32* colorPtr,
+bool TightEncoder::checkSolidTile(Rect& r, rdr::U32* colorPtr,
                                   bool needSameColor)
 {
-  switch (writer->bpp()) {
+  switch (serverpf.bpp) {
   case 32:
-    return checkSolidTile32(r, ig, writer, colorPtr, needSameColor);
+    return checkSolidTile32(r, colorPtr, needSameColor);
   case 16:
-    return checkSolidTile16(r, ig, writer, colorPtr, needSameColor);
+    return checkSolidTile16(r, colorPtr, needSameColor);
   default:
-    return checkSolidTile8(r, ig, writer, colorPtr, needSameColor);
+    return checkSolidTile8(r, colorPtr, needSameColor);
   }
 }
 
-void TightEncoder::findBestSolidArea(Rect& r, ImageGetter *ig,
-                                     rdr::U32 colorValue, Rect& bestr)
+void TightEncoder::findBestSolidArea(Rect& r, rdr::U32 colorValue, Rect& bestr)
 {
   int dx, dy, dw, dh;
   int w_prev;
@@ -164,16 +156,16 @@
       TIGHT_MAX_SPLIT_TILE_SIZE : w_prev;
 
     sr.setXYWH(r.tl.x, dy, dw, dh);
-    if (!checkSolidTile(sr, ig, &colorValue, true))
+    if (!checkSolidTile(sr, &colorValue, true))
       break;
 
     for (dx = r.tl.x + dw; dx < r.tl.x + w_prev;) {
       dw = (dx + TIGHT_MAX_SPLIT_TILE_SIZE <= r.tl.x + w_prev) ?
         TIGHT_MAX_SPLIT_TILE_SIZE : (r.tl.x + w_prev - dx);
       sr.setXYWH(dx, dy, dw, dh);
-      if (!checkSolidTile(sr, ig, &colorValue, true))
+      if (!checkSolidTile(sr, &colorValue, true))
         break;
-	    dx += dw;
+      dx += dw;
     }
 
     w_prev = dx - r.tl.x;
@@ -187,8 +179,8 @@
   bestr.br.y = bestr.tl.y + h_best;
 }
 
-void TightEncoder::extendSolidArea(const Rect& r, ImageGetter *ig,
-                                   rdr::U32 colorValue, Rect& er)
+void TightEncoder::extendSolidArea(const Rect& r, rdr::U32 colorValue,
+                                   Rect& er)
 {
   int cx, cy;
   Rect sr;
@@ -196,7 +188,7 @@
   // Try to extend the area upwards.
   for (cy = er.tl.y - 1; ; cy--) {
     sr.setXYWH(er.tl.x, cy, er.width(), 1);
-    if (cy < r.tl.y || !checkSolidTile(sr, ig, &colorValue, true))
+    if (cy < r.tl.y || !checkSolidTile(sr, &colorValue, true))
       break;
   }
   er.tl.y = cy + 1;
@@ -204,7 +196,7 @@
   // ... downwards.
   for (cy = er.br.y; ; cy++) {
     sr.setXYWH(er.tl.x, cy, er.width(), 1);
-    if (cy >= r.br.y || !checkSolidTile(sr, ig, &colorValue, true))
+    if (cy >= r.br.y || !checkSolidTile(sr, &colorValue, true))
       break;
   }
   er.br.y = cy;
@@ -212,7 +204,7 @@
   // ... to the left.
   for (cx = er.tl.x - 1; ; cx--) {
     sr.setXYWH(cx, er.tl.y, 1, er.height());
-    if (cx < r.tl.x || !checkSolidTile(sr, ig, &colorValue, true))
+    if (cx < r.tl.x || !checkSolidTile(sr, &colorValue, true))
       break;
   }
   er.tl.x = cx + 1;
@@ -220,7 +212,7 @@
   // ... to the right.
   for (cx = er.br.x; ; cx++) {
     sr.setXYWH(cx, er.tl.y, 1, er.height());
-    if (cx >= r.br.x || !checkSolidTile(sr, ig, &colorValue, true))
+    if (cx >= r.br.x || !checkSolidTile(sr, &colorValue, true))
       break;
   }
   er.br.x = cx;
@@ -254,7 +246,7 @@
           ((h - 1) / subrectMaxHeight + 1));
 }
 
-void TightEncoder::sendRectSimple(const Rect& r, ImageGetter* ig)
+void TightEncoder::sendRectSimple(const Rect& r)
 {
   // Shortcuts to rectangle coordinates and dimensions.
   const int x = r.tl.x;
@@ -265,7 +257,7 @@
   // Encode small rects as is.
   bool rectTooBig = w > pconf->maxRectWidth || w * h > pconf->maxRectSize;
   if (!rectTooBig) {
-    writeSubrect(r, ig);
+    writeSubrect(r);
     return;
   }
 
@@ -283,14 +275,18 @@
       sw = (dx + pconf->maxRectWidth < w) ? pconf->maxRectWidth : w - dx;
       sh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy;
       sr.setXYWH(x + dx, y + dy, sw, sh);
-      writeSubrect(sr, ig);
+      writeSubrect(sr);
     }
   }
 }
 
-bool TightEncoder::writeRect(const Rect& _r, ImageGetter* ig, Rect* actual)
+bool TightEncoder::writeRect(const Rect& _r, TransImageGetter* _ig,
+                             Rect* actual)
 {
+  ig = _ig;
+  serverpf = ig->getPixelBuffer()->getPF();
   ConnParams* cp = writer->getConnParams();
+  clientpf = cp->pf();
 
   // Shortcuts to rectangle coordinates and dimensions.
   Rect r = _r;
@@ -299,13 +295,9 @@
   unsigned int w = r.width();
   unsigned int h = r.height();
 
-  // Copy members of current TightEncoder instance to static variables.
-  s_pconf = pconf;
-  s_pjconf = pjconf;
-
   // Encode small rects as is.
   if (!cp->supportsLastRect || w * h < TIGHT_MIN_SPLIT_RECT_SIZE) {
-    sendRectSimple(r, ig);
+    sendRectSimple(r);
     return true;
   }
 
@@ -313,10 +305,10 @@
   Rect sr, bestr;
   unsigned int dx, dy, dw, dh;
   rdr::U32 colorValue;
-  int maxRectSize = s_pconf->maxRectSize;
-  int maxRectWidth = s_pconf->maxRectWidth;
+  int maxRectSize = pconf->maxRectSize;
+  int maxRectWidth = pconf->maxRectWidth;
   int nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w;
-  int nMaxRows = s_pconf->maxRectSize / nMaxWidth;
+  int nMaxRows = pconf->maxRectSize / nMaxWidth;
 
   // Try to find large solid-color areas and send them separately.
   for (dy = y; dy < y + h; dy += TIGHT_MAX_SPLIT_TILE_SIZE) {
@@ -324,7 +316,7 @@
     // If a rectangle becomes too large, send its upper part now.
     if (dy - y >= nMaxRows) {
       sr.setXYWH(x, y, w, nMaxRows);
-      sendRectSimple(sr, ig);
+      sendRectSimple(sr);
       r.tl.y += nMaxRows;
       y = r.tl.y;
       h = r.height();
@@ -339,11 +331,11 @@
         TIGHT_MAX_SPLIT_TILE_SIZE : (x + w - dx);
  
       sr.setXYWH(dx, dy, dw, dh);
-      if (checkSolidTile(sr, ig, &colorValue, false)) {
+      if (checkSolidTile(sr, &colorValue, false)) {
 
         // Get dimensions of solid-color area.
         sr.setXYWH(dx, dy, r.br.x - dx, r.br.y - dy);
-        findBestSolidArea(sr, ig, colorValue, bestr);
+        findBestSolidArea(sr, colorValue, bestr);
 
         // Make sure a solid rectangle is large enough
         // (or the whole rectangle is of the same color).
@@ -352,30 +344,30 @@
           continue;
 
         // Try to extend solid rectangle to maximum size.
-        extendSolidArea(r, ig, colorValue, bestr);
+        extendSolidArea(r, colorValue, bestr);
  
         // Send rectangles at top and left to solid-color area.
         if (bestr.tl.y != y) {
           sr.setXYWH(x, y, w, bestr.tl.y - y);
-          sendRectSimple(sr, ig);
+          sendRectSimple(sr);
         }
         if (bestr.tl.x != x) {
           sr.setXYWH(x, bestr.tl.y, bestr.tl.x - x, bestr.height());
-          writeRect(sr, ig, NULL);
+          writeRect(sr, _ig, NULL);
         }
 
         // Send solid-color rectangle.
-        writeSubrect(bestr, ig, true);
+        writeSubrect(bestr, true);
 
         // Send remaining rectangles (at right and bottom).
         if (bestr.br.x != r.br.x) {
           sr.setXYWH(bestr.br.x, bestr.tl.y, r.br.x - bestr.br.x,
             bestr.height());
-          writeRect(sr, ig, NULL);
+          writeRect(sr, _ig, NULL);
         }
         if (bestr.br.y != r.br.y) {
           sr.setXYWH(x, bestr.br.y, w, r.br.y - bestr.br.y);
-          writeRect(sr, ig, NULL);
+          writeRect(sr, _ig, NULL);
         }
 
         return true;
@@ -384,24 +376,21 @@
   }
 
   // No suitable solid-color rectangles found.
-  sendRectSimple(r, ig);
+  sendRectSimple(r);
   return true;
 }
 
-void TightEncoder::writeSubrect(const Rect& r, ImageGetter* ig,
-  bool forceSolid)
+void TightEncoder::writeSubrect(const Rect& r, bool forceSolid)
 {
-  rdr::U8* imageBuf = writer->getImageBuf(r.area());
-  ConnParams* cp = writer->getConnParams();
   mos.clear();
 
-  switch (writer->bpp()) {
+  switch (clientpf.bpp) {
   case 8:
-    tightEncode8(r, &mos, zos, jc, imageBuf, cp, ig, forceSolid);  break;
+    tightEncode8(r, &mos, forceSolid);  break;
   case 16:
-    tightEncode16(r, &mos, zos, jc, imageBuf, cp, ig, forceSolid); break;
+    tightEncode16(r, &mos, forceSolid); break;
   case 32:
-    tightEncode32(r, &mos, zos, jc, imageBuf, cp, ig, forceSolid); break;
+    tightEncode32(r, &mos, forceSolid); break;
   }
 
   writer->startRect(r, encodingTight);
diff --git a/common/rfb/TightEncoder.h b/common/rfb/TightEncoder.h
index 064a834..ae9672c 100644
--- a/common/rfb/TightEncoder.h
+++ b/common/rfb/TightEncoder.h
@@ -22,6 +22,7 @@
 #include <rdr/MemOutStream.h>
 #include <rdr/ZlibOutStream.h>
 #include <rfb/JpegCompressor.h>
+#include <rfb/TransImageGetter.h>
 #include <rfb/Encoder.h>
 
 // FIXME: Check if specifying extern "C" is really necessary.
@@ -43,6 +44,28 @@
   };
 
   //
+  // C-style structures to store palette entries and compression paramentes.
+  // Such code probably should be converted into C++ classes.
+  //
+
+  struct TIGHT_COLOR_LIST {
+    TIGHT_COLOR_LIST *next;
+    int idx;
+    rdr::U32 rgb;
+  };
+
+  struct TIGHT_PALETTE_ENTRY {
+    TIGHT_COLOR_LIST *listNode;
+    int numPixels;
+  };
+
+  struct TIGHT_PALETTE {
+    TIGHT_PALETTE_ENTRY entry[256];
+    TIGHT_COLOR_LIST *hash[256];
+    TIGHT_COLOR_LIST list[256];
+  };
+
+  //
   // Compression level stuff. The following array contains various
   // encoder parameters for each of 10 compression levels (0..9).
   // Last three parameters correspond to JPEG quality levels (0..9).
@@ -59,24 +82,72 @@
     virtual void setCompressLevel(int level);
     virtual void setQualityLevel(int level);
     virtual int getNumRects(const Rect &r);
-    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual bool writeRect(const Rect& r, TransImageGetter* ig, Rect* actual);
     virtual ~TightEncoder();
 
   private:
     TightEncoder(SMsgWriter* writer);
-    bool checkSolidTile(Rect& r, ImageGetter *ig, rdr::U32* colorPtr,
-                        bool needSameColor);
-    void extendSolidArea(const Rect& r, ImageGetter *ig,
-                         rdr::U32 colorValue, Rect& er);
-    void findBestSolidArea(Rect& r, ImageGetter* ig, rdr::U32 colorValue,
-                           Rect& bestr);
-    void sendRectSimple(const Rect& r, ImageGetter* ig);
-    void writeSubrect(const Rect& r, ImageGetter* ig, bool forceSolid = false);
+    bool checkSolidTile(Rect& r, rdr::U32* colorPtr, bool needSameColor);
+    void extendSolidArea(const Rect& r, rdr::U32 colorValue, Rect& er);
+    void findBestSolidArea(Rect& r, rdr::U32 colorValue, Rect& bestr);
+    void sendRectSimple(const Rect& r);
+    void writeSubrect(const Rect& r, bool forceSolid = false);
+
+    void compressData(rdr::OutStream *os, rdr::ZlibOutStream *zos,
+                      const void *buf, unsigned int length, int zlibLevel);
+
+    int paletteInsert(rdr::U32 rgb, int numPixels, int bpp);
+    void paletteReset(void);
+
+    void fastFillPalette8(const Rect &r, rdr::U8 *data, int stride);
+    void fastFillPalette16(const Rect &r, rdr::U16 *data, int stride);
+    void fastFillPalette32(const Rect &r, rdr::U32 *data, int stride);
+
+    void fillPalette8(rdr::U8 *data, int count);
+    void fillPalette16(rdr::U16 *data, int count);
+    void fillPalette32(rdr::U32 *data, int count);
+
+    unsigned int packPixels8(rdr::U8 *buf, unsigned int count);
+    unsigned int packPixels16(rdr::U16 *buf, unsigned int count);
+    unsigned int packPixels32(rdr::U32 *buf, unsigned int count);
+
+    void tightEncode8(const Rect& r, rdr::OutStream *os, bool forceSolid);
+    void tightEncode16(const Rect& r, rdr::OutStream *os, bool forceSolid);
+    void tightEncode32(const Rect& r, rdr::OutStream *os, bool forceSolid);
+
+    bool checkSolidTile8(Rect& r, rdr::U32 *colorPtr, bool needSameColor);
+    bool checkSolidTile16(Rect& r, rdr::U32 *colorPtr, bool needSameColor);
+    bool checkSolidTile32(Rect& r, rdr::U32 *colorPtr, bool needSameColor);
+
+    void encodeSolidRect8(rdr::OutStream *os, rdr::U8 *buf);
+    void encodeSolidRect16(rdr::OutStream *os, rdr::U16 *buf);
+    void encodeSolidRect32(rdr::OutStream *os, rdr::U32 *buf);
+
+    void encodeFullColorRect8(rdr::OutStream *os, rdr::U8 *buf, const Rect& r);
+    void encodeFullColorRect16(rdr::OutStream *os, rdr::U16 *buf, const Rect& r);
+    void encodeFullColorRect32(rdr::OutStream *os, rdr::U32 *buf, const Rect& r);
+
+    void encodeMonoRect8(rdr::OutStream *os, rdr::U8 *buf, const Rect& r);
+    void encodeMonoRect16(rdr::OutStream *os, rdr::U16 *buf, const Rect& r);
+    void encodeMonoRect32(rdr::OutStream *os, rdr::U32 *buf, const Rect& r);
+
+    void encodeIndexedRect16(rdr::OutStream *os, rdr::U16 *buf, const Rect& r);
+    void encodeIndexedRect32(rdr::OutStream *os, rdr::U32 *buf, const Rect& r);
+
+    void encodeJpegRect16(rdr::OutStream *os, rdr::U16 *buf, int, const Rect& r);
+    void encodeJpegRect32(rdr::OutStream *os, rdr::U32 *buf, int, const Rect& r);
 
     SMsgWriter* writer;
     rdr::MemOutStream mos;
     rdr::ZlibOutStream zos[4];
     JpegCompressor jc;
+    TransImageGetter *ig;
+    PixelFormat serverpf, clientpf;
+
+    bool pack24;
+    int palMaxColors, palNumColors;
+    rdr::U32 monoBackground, monoForeground;
+    TIGHT_PALETTE palette;
 
     static const int defaultCompressLevel;
     static const TIGHT_CONF conf[];
diff --git a/common/rfb/TransImageGetter.cxx b/common/rfb/TransImageGetter.cxx
index f415455..e0c60a4 100644
--- a/common/rfb/TransImageGetter.cxx
+++ b/common/rfb/TransImageGetter.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
@@ -55,6 +56,14 @@
   PixelTransformer::setColourMapEntries(firstCol, nCols);
 }
 
+rdr::U8 *TransImageGetter::getPixelsRW(const Rect &r, int *stride)
+{
+  if (!offset.equals(Point(0, 0)))
+    return pb->getPixelsRW(r.translate(offset.negate()), stride);
+  else
+    return pb->getPixelsRW(r, stride);
+}
+
 void TransImageGetter::getImage(void* outPtr, const Rect& r, int outStride)
 {
   int inStride;
@@ -77,4 +86,3 @@
   if (self->writer)
     self->writer->writeSetColourMapEntries(firstColour, nColours, cm);
 }
-
diff --git a/common/rfb/TransImageGetter.h b/common/rfb/TransImageGetter.h
index 7942247..8fde743 100644
--- a/common/rfb/TransImageGetter.h
+++ b/common/rfb/TransImageGetter.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
@@ -71,11 +72,15 @@
     // padding will be outStride-r.width() pixels).
     void getImage(void* outPtr, const Rect& r, int outStride=0);
 
+    rdr::U8 *getPixelsRW(const Rect &r, int *stride);
+
     // setPixelBuffer() changes the pixel buffer to be used.  The new pixel
     // buffer MUST have the same pixel format as the old one - if not you
     // should call init() instead.
     void setPixelBuffer(PixelBuffer* pb_) { pb = pb_; }
 
+    PixelBuffer *getPixelBuffer(void) { return pb; }
+
     // setOffset() sets an offset which is subtracted from the coordinates of
     // the rectangle given to getImage().
     void setOffset(const Point& offset_) { offset = offset_; }
diff --git a/common/rfb/ZRLEEncoder.cxx b/common/rfb/ZRLEEncoder.cxx
index ef0dd9f..a83d79f 100644
--- a/common/rfb/ZRLEEncoder.cxx
+++ b/common/rfb/ZRLEEncoder.cxx
@@ -69,7 +69,7 @@
     delete mos;
 }
 
-bool ZRLEEncoder::writeRect(const Rect& r, ImageGetter* ig, Rect* actual)
+bool ZRLEEncoder::writeRect(const Rect& r, TransImageGetter* ig, Rect* actual)
 {
   rdr::U8* imageBuf = writer->getImageBuf(64 * 64 * 4 + 4);
   mos->clear();
diff --git a/common/rfb/ZRLEEncoder.h b/common/rfb/ZRLEEncoder.h
index 7768917..3a0f52a 100644
--- a/common/rfb/ZRLEEncoder.h
+++ b/common/rfb/ZRLEEncoder.h
@@ -27,7 +27,7 @@
   class ZRLEEncoder : public Encoder {
   public:
     static Encoder* create(SMsgWriter* writer);
-    virtual bool writeRect(const Rect& r, ImageGetter* ig, Rect* actual);
+    virtual bool writeRect(const Rect& r, TransImageGetter* ig, Rect* actual);
     virtual ~ZRLEEncoder();
 
     // setMaxLen() sets the maximum size in bytes of any ZRLE rectangle.  This
diff --git a/common/rfb/tightEncode.h b/common/rfb/tightEncode.h
index d4c8f6e..9b2b29d 100644
--- a/common/rfb/tightEncode.h
+++ b/common/rfb/tightEncode.h
@@ -26,8 +26,6 @@
 // GET_IMAGE_INTO_BUF - gets a rectangle of pixel data into a buffer
 //
 
-#include <rdr/OutStream.h>
-#include <rdr/ZlibOutStream.h>
 #include <assert.h>
 
 namespace rfb {
@@ -40,79 +38,35 @@
 #endif
 
 #define PIXEL_T rdr::CONCAT2E(U,BPP)
-#define WRITE_PIXEL CONCAT2E(writeOpaque,BPP)
-#define TIGHT_ENCODE CONCAT2E(tightEncode,BPP)
-#define SWAP_PIXEL CONCAT2E(SWAP,BPP)
+#define TIGHT_ENCODE TightEncoder::CONCAT2E(tightEncode,BPP)
 #define HASH_FUNCTION CONCAT2E(HASH_FUNC,BPP)
-#define PACK_PIXELS CONCAT2E(packPixels,BPP)
-#define ENCODE_SOLID_RECT CONCAT2E(encodeSolidRect,BPP)
-#define ENCODE_FULLCOLOR_RECT CONCAT2E(encodeFullColorRect,BPP)
-#define ENCODE_MONO_RECT CONCAT2E(encodeMonoRect,BPP)
-#define ENCODE_INDEXED_RECT CONCAT2E(encodeIndexedRect,BPP)
-#define ENCODE_JPEG_RECT CONCAT2E(encodeJpegRect,BPP)
-#define FILL_PALETTE CONCAT2E(fillPalette,BPP)
-#define CHECK_SOLID_TILE CONCAT2E(checkSolidTile,BPP)
+#define PACK_PIXELS TightEncoder::CONCAT2E(packPixels,BPP)
+#define ENCODE_SOLID_RECT TightEncoder::CONCAT2E(encodeSolidRect,BPP)
+#define ENCODE_FULLCOLOR_RECT TightEncoder::CONCAT2E(encodeFullColorRect,BPP)
+#define ENCODE_MONO_RECT TightEncoder::CONCAT2E(encodeMonoRect,BPP)
+#define ENCODE_INDEXED_RECT TightEncoder::CONCAT2E(encodeIndexedRect,BPP)
+#define ENCODE_JPEG_RECT TightEncoder::CONCAT2E(encodeJpegRect,BPP)
+#define FAST_FILL_PALETTE TightEncoder::CONCAT2E(fastFillPalette,BPP)
+#define FILL_PALETTE TightEncoder::CONCAT2E(fillPalette,BPP)
+#define CHECK_SOLID_TILE TightEncoder::CONCAT2E(checkSolidTile,BPP)
 
 #ifndef TIGHT_ONCE
 #define TIGHT_ONCE
 
 //
-// C-style structures to store palette entries and compression paramentes.
-// Such code probably should be converted into C++ classes.
-//
-
-struct TIGHT_COLOR_LIST {
-  TIGHT_COLOR_LIST *next;
-  int idx;
-  rdr::U32 rgb;
-};
-
-struct TIGHT_PALETTE_ENTRY {
-  TIGHT_COLOR_LIST *listNode;
-  int numPixels;
-};
-
-struct TIGHT_PALETTE {
-  TIGHT_PALETTE_ENTRY entry[256];
-  TIGHT_COLOR_LIST *hash[256];
-  TIGHT_COLOR_LIST list[256];
-};
-
-// FIXME: Is it really a good idea to use static variables for this?
-static bool s_pack24;             // use 24-bit packing for 32-bit pixels
-
-// FIXME: Make a separate class for palette operations.
-static int s_palMaxColors, s_palNumColors;
-static rdr::U32 s_monoBackground, s_monoForeground;
-static TIGHT_PALETTE s_palette;
-
-//
-// Swapping bytes in pixels.
-// FIXME: Use a sort of ImageGetter that does not convert pixel format?
-//
-
-#ifndef SWAP16
-#define SWAP16(n) ((((n) & 0xff) << 8) | (((n) >> 8) & 0xff))
-#endif
-#ifndef SWAP32
-#define SWAP32(n) (((n) >> 24) | (((n) & 0x00ff0000) >> 8) | \
-                   (((n) & 0x0000ff00) << 8) | ((n) << 24))
-#endif
-
-//
 // Functions to operate on palette structures.
 //
 
 #define HASH_FUNC16(rgb) ((int)(((rgb >> 8) + rgb) & 0xFF))
 #define HASH_FUNC32(rgb) ((int)(((rgb >> 16) + (rgb >> 8)) & 0xFF))
 
-static void paletteReset(void)
+void TightEncoder::paletteReset(void)
 {
-  s_palNumColors = 0;
-  memset(s_palette.hash, 0, 256 * sizeof(TIGHT_COLOR_LIST *));
+  palNumColors = 0;
+  memset(palette.hash, 0, 256 * sizeof(TIGHT_COLOR_LIST *));
 }
 
-static int paletteInsert(rdr::U32 rgb, int numPixels, int bpp)
+int TightEncoder::paletteInsert(rdr::U32 rgb, int numPixels, int bpp)
 {
   TIGHT_COLOR_LIST *pnode;
   TIGHT_COLOR_LIST *prev_pnode = NULL;
@@ -120,59 +74,59 @@
 
   hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb);
 
-  pnode = s_palette.hash[hash_key];
+  pnode = palette.hash[hash_key];
 
   while (pnode != NULL) {
     if (pnode->rgb == rgb) {
       // Such palette entry already exists.
       new_idx = idx = pnode->idx;
-      count = s_palette.entry[idx].numPixels + numPixels;
-      if (new_idx && s_palette.entry[new_idx-1].numPixels < count) {
+      count = palette.entry[idx].numPixels + numPixels;
+      if (new_idx && palette.entry[new_idx-1].numPixels < count) {
         do {
-          s_palette.entry[new_idx] = s_palette.entry[new_idx-1];
-          s_palette.entry[new_idx].listNode->idx = new_idx;
+          palette.entry[new_idx] = palette.entry[new_idx-1];
+          palette.entry[new_idx].listNode->idx = new_idx;
           new_idx--;
         }
         while (new_idx &&
-          s_palette.entry[new_idx-1].numPixels < count);
-        s_palette.entry[new_idx].listNode = pnode;
+          palette.entry[new_idx-1].numPixels < count);
+        palette.entry[new_idx].listNode = pnode;
         pnode->idx = new_idx;
       }
-      s_palette.entry[new_idx].numPixels = count;
-      return s_palNumColors;
+      palette.entry[new_idx].numPixels = count;
+      return palNumColors;
     }
     prev_pnode = pnode;
     pnode = pnode->next;
   }
 
   // Check if palette is full.
-  if ( s_palNumColors == 256 || s_palNumColors == s_palMaxColors ) {
-    s_palNumColors = 0;
+  if ( palNumColors == 256 || palNumColors == palMaxColors ) {
+    palNumColors = 0;
     return 0;
   }
 
   // Move palette entries with lesser pixel counts.
-  for ( idx = s_palNumColors;
-  idx > 0 && s_palette.entry[idx-1].numPixels < numPixels;
+  for ( idx = palNumColors;
+  idx > 0 && palette.entry[idx-1].numPixels < numPixels;
   idx-- ) {
-    s_palette.entry[idx] = s_palette.entry[idx-1];
-    s_palette.entry[idx].listNode->idx = idx;
+    palette.entry[idx] = palette.entry[idx-1];
+    palette.entry[idx].listNode->idx = idx;
   }
 
   // Add new palette entry into the freed slot.
-  pnode = &s_palette.list[s_palNumColors];
+  pnode = &palette.list[palNumColors];
   if (prev_pnode != NULL) {
     prev_pnode->next = pnode;
   } else {
-    s_palette.hash[hash_key] = pnode;
+    palette.hash[hash_key] = pnode;
   }
   pnode->next = NULL;
   pnode->idx = idx;
   pnode->rgb = rgb;
-  s_palette.entry[idx].listNode = pnode;
-  s_palette.entry[idx].numPixels = numPixels;
+  palette.entry[idx].listNode = pnode;
+  palette.entry[idx].numPixels = numPixels;
 
-  return (++s_palNumColors);
+  return (++palNumColors);
 }
 
 //
@@ -180,16 +134,16 @@
 // size is less than TIGHT_MIN_TO_COMPRESS bytes.
 //
 
-static void compressData(rdr::OutStream *os, rdr::ZlibOutStream *zos,
-                         const void *buf, const PixelFormat& pf,
-                         unsigned int length, int zlibLevel)
+void TightEncoder::compressData(rdr::OutStream *os, rdr::ZlibOutStream *zos,
+                                const void *buf, unsigned int length,
+                                int zlibLevel)
 {
   if (length < TIGHT_MIN_TO_COMPRESS) {
     os->writeBytes(buf, length);
   } else {
     // FIXME: Using a temporary MemOutStream may be not efficient.
     //        Maybe use the same static object used in the JPEG coder?
-    int maxBeforeSize = s_pconf->maxRectSize * (pf.bpp / 8);
+    int maxBeforeSize = pconf->maxRectSize * (clientpf.bpp / 8);
     int maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12;
     rdr::MemOutStream mem_os(maxAfterSize);
     zos->setUnderlying(&mem_os);
@@ -204,41 +158,25 @@
 
 #endif  // #ifndef TIGHT_ONCE
 
-static void ENCODE_SOLID_RECT     (rdr::OutStream *os,
-                                   PIXEL_T *buf, const PixelFormat& pf);
-static void ENCODE_FULLCOLOR_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
-                                   PIXEL_T *buf, const PixelFormat& pf, const Rect& r);
-static void ENCODE_MONO_RECT      (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
-                                   PIXEL_T *buf, const PixelFormat& pf, const Rect& r);
-#if (BPP != 8)
-static void ENCODE_INDEXED_RECT   (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
-                                   PIXEL_T *buf, const PixelFormat& pf, const Rect& r);
-static void ENCODE_JPEG_RECT      (rdr::OutStream *os, JpegCompressor& jc,
-                                   PIXEL_T *buf, const PixelFormat& pf, const Rect& r);
-#endif
-
-static void FILL_PALETTE (PIXEL_T *data, int count);
-
 //
 // Convert 32-bit color samples into 24-bit colors, in place.
 // Performs packing only when redMax, greenMax and blueMax are all 255.
 // Color components are assumed to be byte-aligned.
 //
 
-static inline unsigned int PACK_PIXELS (PIXEL_T *buf, unsigned int count,
-                                        const PixelFormat& pf)
+unsigned int PACK_PIXELS (PIXEL_T *buf, unsigned int count)
 {
 #if (BPP != 32)
   return count * sizeof(PIXEL_T);
 #else
-  if (!s_pack24)
+  if (!pack24)
     return count * sizeof(PIXEL_T);
 
   rdr::U32 pix;
   rdr::U8 *dst = (rdr::U8 *)buf;
   for (unsigned int i = 0; i < count; i++) {
     pix = *buf++;
-    pf.rgbFromBuffer(dst, (rdr::U8*)&pix, 1, NULL);
+    clientpf.rgbFromBuffer(dst, (rdr::U8*)&pix, 1, NULL);
     dst += 3;
   }
   return count * 3;
@@ -249,64 +187,67 @@
 // Main function of the Tight encoder
 //
 
-void TIGHT_ENCODE (const Rect& r, rdr::OutStream *os,
-                  rdr::ZlibOutStream zos[4], JpegCompressor &jc, void* buf,
-                  ConnParams* cp
-#ifdef EXTRA_ARGS
-                  , EXTRA_ARGS,
-#endif
-                  bool forceSolid)
+void TIGHT_ENCODE (const Rect& r, rdr::OutStream *os, bool forceSolid)
 {
-  const PixelFormat& pf = cp->pf();
-  if(forceSolid) {
-    GET_IMAGE_INTO_BUF(Rect(r.tl.x, r.tl.y, r.tl.x + 1, r.tl.y + 1), buf);
-  }
-  else {
-    GET_IMAGE_INTO_BUF(r, buf);
-  }
-  PIXEL_T* pixels = (PIXEL_T*)buf;
+  int stride = r.width();
+  PIXEL_T *pixels = (PIXEL_T *)ig->getPixelsRW(r, &stride);
 
 #if (BPP == 32)
   // Check if it's necessary to pack 24-bit pixels, and
   // compute appropriate shift values if necessary.
-  s_pack24 = pf.is888();
+  pack24 = clientpf.is888();
 #endif
 
   if (forceSolid)
-    s_palNumColors = 1;
+    palNumColors = 1;
   else {
-    s_palMaxColors = r.area() / s_pconf->idxMaxColorsDivisor;
-    if (s_pjconf != NULL) s_palMaxColors = s_pconf->palMaxColorsWithJPEG;
-    if (s_palMaxColors < 2 && r.area() >= s_pconf->monoMinRectSize) {
-      s_palMaxColors = 2;
+    palMaxColors = r.area() / pconf->idxMaxColorsDivisor;
+    if (pjconf != NULL) palMaxColors = pconf->palMaxColorsWithJPEG;
+    if (palMaxColors < 2 && r.area() >= pconf->monoMinRectSize) {
+      palMaxColors = 2;
     }
 
-    FILL_PALETTE(pixels, r.area());
+    if (clientpf.equal(serverpf) && clientpf.bpp >= 16) {
+      // 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) {
+        pixels = (PIXEL_T *)writer->getImageBuf(r.area());
+        stride = r.width();
+        ig->getImage(pixels, r);
+      }
+    }
+    else {
+      pixels = (PIXEL_T *)writer->getImageBuf(r.area());
+      stride = r.width();
+      ig->getImage(pixels, r);
+      FILL_PALETTE(pixels, r.area());
+    }
   }
 
-  switch (s_palNumColors) {
+  switch (palNumColors) {
   case 0:
     // Truecolor image
 #if (BPP != 8)
-    if (s_pjconf != NULL) {
-      ENCODE_JPEG_RECT(os, jc, pixels, pf, r);
+    if (pjconf != NULL) {
+      ENCODE_JPEG_RECT(os, pixels, stride, r);
       break;
     }
 #endif
-    ENCODE_FULLCOLOR_RECT(os, zos, pixels, pf, r);
+    ENCODE_FULLCOLOR_RECT(os, pixels, r);
     break;
   case 1:
     // Solid rectangle
-    ENCODE_SOLID_RECT(os, pixels, pf);
+    ENCODE_SOLID_RECT(os, pixels);
     break;
   case 2:
     // Two-color rectangle
-    ENCODE_MONO_RECT(os, zos, pixels, pf, r);
+    ENCODE_MONO_RECT(os, pixels, r);
     break;
 #if (BPP != 8)
   default:
     // Up to 256 different colors
-    ENCODE_INDEXED_RECT(os, zos, pixels, pf, r);
+    ENCODE_INDEXED_RECT(os, pixels, r);
 #endif
   }
 }
@@ -315,35 +256,33 @@
 // Subencoding implementations.
 //
 
-static void ENCODE_SOLID_RECT (rdr::OutStream *os, PIXEL_T *buf, const PixelFormat& pf)
+void ENCODE_SOLID_RECT (rdr::OutStream *os, PIXEL_T *buf)
 {
   os->writeU8(0x08 << 4);
 
-  int length = PACK_PIXELS(buf, 1, pf);
+  int length = PACK_PIXELS(buf, 1);
   os->writeBytes(buf, length);
 }
 
-static void ENCODE_FULLCOLOR_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
-                                   PIXEL_T *buf, const PixelFormat& pf, const Rect& r)
+void ENCODE_FULLCOLOR_RECT (rdr::OutStream *os, PIXEL_T *buf, const Rect& r)
 {
   const int streamId = 0;
   os->writeU8(streamId << 4);
 
-  int length = PACK_PIXELS(buf, r.area(), pf);
-  compressData(os, &zos[streamId], buf, pf, length, s_pconf->rawZlibLevel);
+  int length = PACK_PIXELS(buf, r.area());
+  compressData(os, &zos[streamId], buf, length, pconf->rawZlibLevel);
 }
 
-static void ENCODE_MONO_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
-                              PIXEL_T *buf, const PixelFormat& pf, const Rect& r)
+void ENCODE_MONO_RECT (rdr::OutStream *os, PIXEL_T *buf, const Rect& r)
 {
   const int streamId = 1;
   os->writeU8((streamId | 0x04) << 4);
   os->writeU8(0x01);
 
   // Write the palette
-  PIXEL_T pal[2] = { (PIXEL_T)s_monoBackground, (PIXEL_T)s_monoForeground };
+  PIXEL_T pal[2] = { (PIXEL_T)monoBackground, (PIXEL_T)monoForeground };
   os->writeU8(1);
-  os->writeBytes(pal, PACK_PIXELS(pal, 2, pf));
+  os->writeBytes(pal, PACK_PIXELS(pal, 2));
 
   // Encode the data in-place
   PIXEL_T *src = buf;
@@ -355,7 +294,7 @@
   int aligned_width;
   int x, y, bg_bits;
 
-  bg = (PIXEL_T) s_monoBackground;
+  bg = (PIXEL_T) monoBackground;
   aligned_width = w - w % 8;
 
   for (y = 0; y < h; y++) {
@@ -396,12 +335,11 @@
   // Write the data
   int length = (w + 7) / 8;
   length *= h;
-  compressData(os, &zos[streamId], buf, pf, length, s_pconf->monoZlibLevel);
+  compressData(os, &zos[streamId], buf, length, pconf->monoZlibLevel);
 }
 
 #if (BPP != 8)
-static void ENCODE_INDEXED_RECT (rdr::OutStream *os, rdr::ZlibOutStream zos[4],
-                                 PIXEL_T *buf, const PixelFormat& pf, const Rect& r)
+void ENCODE_INDEXED_RECT (rdr::OutStream *os, PIXEL_T *buf, const Rect& r)
 {
   const int streamId = 2;
   os->writeU8((streamId | 0x04) << 4);
@@ -410,10 +348,10 @@
   // Write the palette
   {
     PIXEL_T pal[256];
-    for (int i = 0; i < s_palNumColors; i++)
-      pal[i] = (PIXEL_T)s_palette.entry[i].listNode->rgb;
-    os->writeU8((rdr::U8)(s_palNumColors - 1));
-    os->writeBytes(pal, PACK_PIXELS(pal, s_palNumColors, pf));
+    for (int i = 0; i < palNumColors; i++)
+      pal[i] = (PIXEL_T)palette.entry[i].listNode->rgb;
+    os->writeU8((rdr::U8)(palNumColors - 1));
+    os->writeBytes(pal, PACK_PIXELS(pal, palNumColors));
   }
 
   // Encode data in-place
@@ -429,7 +367,7 @@
     while (count && *src == rgb) {
       rep++, src++, count--;
     }
-    pnode = s_palette.hash[HASH_FUNCTION(rgb)];
+    pnode = palette.hash[HASH_FUNCTION(rgb)];
     while (pnode != NULL) {
       if ((PIXEL_T)pnode->rgb == rgb) {
         *dst++ = (rdr::U8)pnode->idx;
@@ -444,7 +382,7 @@
   }
 
   // Write the data
-  compressData(os, &zos[streamId], buf, pf, r.area(), s_pconf->idxZlibLevel);
+  compressData(os, &zos[streamId], buf, r.area(), pconf->idxZlibLevel);
 }
 #endif  // #if (BPP != 8)
 
@@ -453,13 +391,12 @@
 //
 
 #if (BPP != 8)
-static void ENCODE_JPEG_RECT (rdr::OutStream *os, JpegCompressor& jc,
-                              PIXEL_T *buf, const PixelFormat& pf,
-                              const Rect& r)
+void ENCODE_JPEG_RECT (rdr::OutStream *os, PIXEL_T *buf, int stride,
+                       const Rect& r)
 {
   jc.clear();
-  jc.compress((rdr::U8 *)buf, r, pf, s_pjconf->jpegQuality,
-    s_pjconf->jpegSubSample);
+  jc.compress((rdr::U8 *)buf, stride * serverpf.bpp / 8, r, serverpf,
+    pjconf->jpegQuality, pjconf->jpegSubSample);
   os->writeU8(0x09 << 4);
   os->writeCompactLength(jc.length());
   os->writeBytes(jc.data(), jc.length());
@@ -471,21 +408,22 @@
 //
 
 #if (BPP == 8)
-static void FILL_PALETTE (PIXEL_T *data, int count)
+
+void FILL_PALETTE (PIXEL_T *data, int count)
 {
   PIXEL_T c0, c1;
   int i, n0, n1;
 
-  s_palNumColors = 0;
+  palNumColors = 0;
 
   c0 = data[0];
   for (i = 1; i < count && data[i] == c0; i++);
   if (i == count) {
-    s_palNumColors = 1;
+    palNumColors = 1;
     return;                       // Solid rectangle
   }
 
-  if (s_palMaxColors < 2)
+  if (palMaxColors < 2)
     return;
 
   n0 = i;
@@ -501,17 +439,23 @@
   }
   if (i == count) {
     if (n0 > n1) {
-      s_monoBackground = (rdr::U32)c0;
-      s_monoForeground = (rdr::U32)c1;
+      monoBackground = (rdr::U32)c0;
+      monoForeground = (rdr::U32)c1;
     } else {
-      s_monoBackground = (rdr::U32)c1;
-      s_monoForeground = (rdr::U32)c0;
+      monoBackground = (rdr::U32)c1;
+      monoForeground = (rdr::U32)c0;
     }
-    s_palNumColors = 2;           // Two colors
+    palNumColors = 2;           // Two colors
   }
 }
+
+void FAST_FILL_PALETTE (const Rect& r, PIXEL_T *data, int stride)
+{
+}
+
 #else   // (BPP != 8)
-static void FILL_PALETTE (PIXEL_T *data, int count)
+
+void FILL_PALETTE (PIXEL_T *data, int count)
 {
   PIXEL_T c0, c1, ci = 0;
   int i, n0, n1, ni;
@@ -519,12 +463,12 @@
   c0 = data[0];
   for (i = 1; i < count && data[i] == c0; i++);
   if (i >= count) {
-    s_palNumColors = 1;           // Solid rectangle
+    palNumColors = 1;           // Solid rectangle
     return;
   }
 
-  if (s_palMaxColors < 2) {
-    s_palNumColors = 0;           // Full-color format preferred
+  if (palMaxColors < 2) {
+    palNumColors = 0;           // Full-color format preferred
     return;
   }
 
@@ -542,13 +486,13 @@
   }
   if (i >= count) {
     if (n0 > n1) {
-      s_monoBackground = (rdr::U32)c0;
-      s_monoForeground = (rdr::U32)c1;
+      monoBackground = (rdr::U32)c0;
+      monoForeground = (rdr::U32)c1;
     } else {
-      s_monoBackground = (rdr::U32)c1;
-      s_monoForeground = (rdr::U32)c0;
+      monoBackground = (rdr::U32)c1;
+      monoForeground = (rdr::U32)c0;
     }
-    s_palNumColors = 2;           // Two colors
+    palNumColors = 2;           // Two colors
     return;
   }
 
@@ -569,32 +513,137 @@
   }
   paletteInsert (ci, (rdr::U32)ni, BPP);
 }
+
+void FAST_FILL_PALETTE (const Rect& r, PIXEL_T *data, int stride)
+{
+  PIXEL_T c0, c1, ci = 0, mask, c0t, c1t, cit;
+  int n0, n1, ni;
+  int w = r.width(), h = r.height();
+  PIXEL_T *rowptr, *colptr, *rowptr2, *colptr2, *dataend = &data[stride * h];
+  bool willTransform = ig->willTransform();
+
+  if (willTransform) {
+    mask = serverpf.redMax << serverpf.redShift;
+    mask |= serverpf.greenMax << serverpf.greenShift;
+    mask |= serverpf.blueMax << serverpf.blueShift;
+  }
+  else mask = ~0;
+
+  c0 = data[0] & mask;
+  n0 = 0;
+  for (rowptr = data; rowptr < dataend; rowptr += stride) {
+    for (colptr = rowptr; colptr < &rowptr[w]; colptr++) {
+      if (((*colptr) & mask) != c0)
+        goto soliddone;
+      n0++;
+    }
+  }
+
+  soliddone:
+  if (rowptr >= dataend) {
+    palNumColors = 1;           // Solid rectangle
+    return;
+  }
+  if (palMaxColors < 2) {
+    palNumColors = 0;           // Full-color format preferred
+    return;
+  }
+
+  c1 = *colptr & mask;
+  n1 = 0;
+  colptr++;
+  if (colptr >= &rowptr[w]) {
+    rowptr += stride;  colptr = rowptr;
+  }
+  colptr2 = colptr;
+  for (rowptr2 = rowptr; rowptr2 < dataend;) {
+    for (; colptr2 < &rowptr2[w]; colptr2++) {
+      ci = (*colptr2) & mask;
+      if (ci == c0) {
+        n0++;
+      } else if (ci == c1) {
+        n1++;
+      } else
+        goto monodone;
+    }
+    rowptr2 += stride;
+    colptr2 = rowptr2;
+  }
+
+  monodone:
+  if (willTransform) {
+    ig->translateRect(&c0, 1, Rect(0, 0, 1, 1), &c0t, 1, Point(0, 0));
+    ig->translateRect(&c1, 1, Rect(0, 0, 1, 1), &c1t, 1, Point(0, 0));
+  }
+  else {
+    c0t = c0;  c1t = c1;
+  }
+
+  if (colptr2 >= dataend) {
+    if (n0 > n1) {
+      monoBackground = (rdr::U32)c0t;
+      monoForeground = (rdr::U32)c1t;
+    } else {
+      monoBackground = (rdr::U32)c1t;
+      monoForeground = (rdr::U32)c0t;
+    }
+    palNumColors = 2;           // Two colors
+    return;
+  }
+
+  paletteReset();
+  paletteInsert (c0t, (rdr::U32)n0, BPP);
+  paletteInsert (c1t, (rdr::U32)n1, BPP);
+
+  ni = 1;
+  colptr2++;
+  if (colptr2 >= &rowptr2[w]) {
+    rowptr2 += stride;  colptr2 = rowptr2;
+  }
+  colptr = colptr2;
+  for (rowptr = rowptr2; rowptr < dataend;) {
+    for (; colptr < &rowptr[w]; colptr++) {
+      if (((*colptr) & mask) == ci) {
+        ni++;
+      } else {
+        if (willTransform)
+          ig->translateRect(&ci, 1, Rect(0, 0, 1, 1), &cit, 1, Point(0, 0));
+        else
+          cit = ci;
+        if (!paletteInsert (cit, (rdr::U32)ni, BPP))
+          return;
+        ci = (*colptr) & mask;
+        ni = 1;
+      }
+    }
+    rowptr += stride;
+    colptr = rowptr;
+  }
+  ig->translateRect(&ci, 1, Rect(0, 0, 1, 1), &cit, 1, Point(0, 0));
+  paletteInsert (cit, (rdr::U32)ni, BPP);
+}
+
 #endif  // #if (BPP == 8)
 
-bool CHECK_SOLID_TILE(Rect& r, ImageGetter* ig, SMsgWriter* writer,
-                      rdr::U32 *colorPtr, bool needSameColor)
+bool CHECK_SOLID_TILE(Rect& r, rdr::U32 *colorPtr, bool needSameColor)
 {
-  PIXEL_T *buf;
-  PIXEL_T colorValue;
+  PIXEL_T *buf, colorValue;
   int dx, dy;
-  Rect sr;
+  int w = r.width(), h = r.height();
 
-  buf = (PIXEL_T *)writer->getImageBuf(r.area());
-  sr.setXYWH(r.tl.x, r.tl.y, 1, 1);
-  GET_IMAGE_INTO_BUF(sr, buf);
+  int stride = w;
+  buf = (PIXEL_T *)ig->getPixelsRW(r, &stride);
 
   colorValue = *buf;
   if (needSameColor && (rdr::U32)colorValue != *colorPtr)
     return false;
 
-  for (dy = 0; dy < r.height(); dy++) {
-    Rect sr;
-    sr.setXYWH(r.tl.x, r.tl.y + dy, r.width(), 1);
-    GET_IMAGE_INTO_BUF(sr, buf);
-    for (dx = 0; dx < r.width(); dx++) {
+  for (dy = 0; dy < h; dy++) {
+    for (dx = 0; dx < w; dx++) {
       if (colorValue != buf[dx])
         return false;
     }
+    buf += stride;
   }
 
   *colorPtr = (rdr::U32)colorValue;
@@ -602,9 +651,7 @@
 }
 
 #undef PIXEL_T
-#undef WRITE_PIXEL
 #undef TIGHT_ENCODE
-#undef SWAP_PIXEL
 #undef HASH_FUNCTION
 #undef PACK_PIXELS
 #undef ENCODE_SOLID_RECT
@@ -612,6 +659,7 @@
 #undef ENCODE_MONO_RECT
 #undef ENCODE_INDEXED_RECT
 #undef ENCODE_JPEG_RECT
+#undef FAST_FILL_PALETTE
 #undef FILL_PALETTE
 #undef CHECK_SOLID_TILE
 }