Move image encoding logic into a central EncodeManager class

This allows us to apply a lot more server logic
independently of which encoder is in use.

Most of this class are things moved over from the
Tight encoder.
diff --git a/common/rfb/ZRLEEncoder.cxx b/common/rfb/ZRLEEncoder.cxx
index 54613e2..d3afe74 100644
--- a/common/rfb/ZRLEEncoder.cxx
+++ b/common/rfb/ZRLEEncoder.cxx
@@ -1,4 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2014 Pierre Ossman for Cendio AB
  * 
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,7 +20,7 @@
 #include <rfb/Exception.h>
 #include <rfb/encodings.h>
 #include <rfb/ConnParams.h>
-#include <rfb/SMsgWriter.h>
+#include <rfb/Palette.h>
 #include <rfb/SConnection.h>
 #include <rfb/ZRLEEncoder.h>
 #include <rfb/Configuration.h>
@@ -28,87 +29,223 @@
 
 IntParameter zlibLevel("ZlibLevel","Zlib compression level",-1);
 
-static inline void writeOpaque24A(rdr::OutStream* os, rdr::U32 u)
-{
-  os->check(3);
-  os->writeU8(((rdr::U8*)&u)[0]);
-  os->writeU8(((rdr::U8*)&u)[1]);
-  os->writeU8(((rdr::U8*)&u)[2]);
-}
-static inline void writeOpaque24B(rdr::OutStream* os, rdr::U32 u)
-{
-  os->check(3);
-  os->writeU8(((rdr::U8*)&u)[1]);
-  os->writeU8(((rdr::U8*)&u)[2]);
-  os->writeU8(((rdr::U8*)&u)[3]);
-}
-
-#define BPP 8
-#include <rfb/zrleEncode.h>
-#undef BPP
-#define BPP 16
-#include <rfb/zrleEncode.h>
-#undef BPP
-#define BPP 32
-#include <rfb/zrleEncode.h>
-#define CPIXEL 24A
-#include <rfb/zrleEncode.h>
-#undef CPIXEL
-#define CPIXEL 24B
-#include <rfb/zrleEncode.h>
-#undef CPIXEL
-#undef BPP
-
 ZRLEEncoder::ZRLEEncoder(SConnection* conn)
-  : Encoder(conn), zos(0,0,zlibLevel), mos(129*1024)
+  : Encoder(conn, encodingZRLE, EncoderPlain, 127),
+  zos(0,0,zlibLevel), mos(129*1024)
 {
+  zos.setUnderlying(&mos);
 }
 
 ZRLEEncoder::~ZRLEEncoder()
 {
+  zos.setUnderlying(NULL);
 }
 
-void ZRLEEncoder::writeRect(const Rect& r, PixelBuffer* pb)
+bool ZRLEEncoder::isSupported()
 {
-  const PixelFormat& pf = conn->cp.pf();
+  return conn->cp.supportsEncoding(encodingZRLE);
+}
 
-  rdr::U8* imageBuf = conn->writer()->getImageBuf(64 * 64 * 4 + 4);
-  mos.clear();
+void ZRLEEncoder::writeRect(const PixelBuffer* pb, const Palette& palette)
+{
+  int x, y;
+  Rect tile;
 
-  switch (pf.bpp) {
-  case 8:
-    zrleEncode8(r, &mos, &zos, imageBuf, pf, pb);
-    break;
-  case 16:
-    zrleEncode16(r, &mos, &zos, imageBuf, pf, pb);
-    break;
-  case 32:
-    {
-      Pixel maxPixel = pf.pixelFromRGB((rdr::U16)-1, (rdr::U16)-1, (rdr::U16)-1);
-      bool fitsInLS3Bytes = maxPixel < (1<<24);
-      bool fitsInMS3Bytes = (maxPixel & 0xff) == 0;
+  rdr::OutStream* os;
 
-      if ((fitsInLS3Bytes && pf.isLittleEndian()) ||
-          (fitsInMS3Bytes && pf.isBigEndian()))
-      {
-        zrleEncode24A(r, &mos, &zos, imageBuf, pf, pb);
-      }
-      else if ((fitsInLS3Bytes && pf.isBigEndian()) ||
-               (fitsInMS3Bytes && pf.isLittleEndian()))
-      {
-        zrleEncode24B(r, &mos, &zos, imageBuf, pf, pb);
-      }
+  // A bit of a special case
+  if (palette.size() == 1) {
+    Encoder::writeSolidRect(pb, palette);
+    return;
+  }
+
+  for (y = 0;y < pb->height();y += 64) {
+    tile.tl.y = y;
+    tile.br.y = y + 64;
+    if (tile.br.y > pb->height())
+      tile.br.y = pb->height();
+
+    for (x = 0;x < pb->width();x += 64) {
+      tile.tl.x = x;
+      tile.br.x = x + 64;
+      if (tile.br.x > pb->width())
+        tile.br.x = pb->width();
+
+      if (palette.size() == 0)
+        writeRawTile(tile, pb, palette);
+      else if (palette.size() <= 16)
+        writePaletteTile(tile, pb, palette);
       else
-      {
-        zrleEncode32(r, &mos, &zos, imageBuf, pf, pb);
-      }
-      break;
+        writePaletteRLETile(tile, pb, palette);
     }
   }
 
-  conn->writer()->startRect(r, encodingZRLE);
-  rdr::OutStream* os = conn->getOutStream();
+  zos.flush();
+
+  os = conn->getOutStream();
+
   os->writeU32(mos.length());
   os->writeBytes(mos.data(), mos.length());
-  conn->writer()->endRect();
+
+  mos.clear();
 }
+
+void ZRLEEncoder::writeSolidRect(int width, int height,
+                                 const PixelFormat& pf,
+                                 const rdr::U8* colour)
+{
+  int tiles;
+
+  rdr::OutStream* os;
+
+  tiles = ((width + 63)/64) * ((height + 63)/64);
+
+  while (tiles--) {
+    zos.writeU8(1);
+    writePixels(colour, pf, 1);
+  }
+
+  zos.flush();
+
+  os = conn->getOutStream();
+
+  os->writeU32(mos.length());
+  os->writeBytes(mos.data(), mos.length());
+
+  mos.clear();
+}
+
+void ZRLEEncoder::writePaletteTile(const Rect& tile, const PixelBuffer* pb,
+                                   const Palette& palette)
+{
+  const rdr::U8* buffer;
+  int stride;
+
+  buffer = pb->getBuffer(tile, &stride);
+
+  switch (pb->getPF().bpp) {
+  case 32:
+    writePaletteTile(tile.width(), tile.height(),
+                     (rdr::U32*)buffer, stride,
+                     pb->getPF(), palette);
+    break;
+  case 16:
+    writePaletteTile(tile.width(), tile.height(),
+                     (rdr::U16*)buffer, stride,
+                     pb->getPF(), palette);
+    break;
+  default:
+    writePaletteTile(tile.width(), tile.height(),
+                     (rdr::U8*)buffer, stride,
+                     pb->getPF(), palette);
+  }
+}
+
+void ZRLEEncoder::writePaletteRLETile(const Rect& tile, const PixelBuffer* pb,
+                                      const Palette& palette)
+{
+  const rdr::U8* buffer;
+  int stride;
+
+  buffer = pb->getBuffer(tile, &stride);
+
+  switch (pb->getPF().bpp) {
+  case 32:
+    writePaletteRLETile(tile.width(), tile.height(),
+                        (rdr::U32*)buffer, stride,
+                        pb->getPF(), palette);
+    break;
+  case 16:
+    writePaletteRLETile(tile.width(), tile.height(),
+                        (rdr::U16*)buffer, stride,
+                        pb->getPF(), palette);
+    break;
+  default:
+    writePaletteRLETile(tile.width(), tile.height(),
+                        (rdr::U8*)buffer, stride,
+                        pb->getPF(), palette);
+  }
+}
+
+void ZRLEEncoder::writeRawTile(const Rect& tile, const PixelBuffer* pb,
+                               const Palette& palette)
+{
+  const rdr::U8* buffer;
+  int stride;
+
+  int w, h, stride_bytes;
+
+  buffer = pb->getBuffer(tile, &stride);
+
+  zos.writeU8(0); // Empty palette (i.e. raw pixels)
+
+  w = tile.width();
+  h = tile.height();
+  stride_bytes = stride * pb->getPF().bpp/8;
+  while (h--) {
+    writePixels(buffer, pb->getPF(), w);
+    buffer += stride_bytes;
+  }
+}
+
+void ZRLEEncoder::writePalette(const PixelFormat& pf, const Palette& palette)
+{
+  rdr::U8 buffer[256*4];
+  int i;
+
+  if (pf.bpp == 32) {
+    rdr::U32* buf;
+    buf = (rdr::U32*)buffer;
+    for (i = 0;i < palette.size();i++)
+      *buf++ = palette.getColour(i);
+  } else if (pf.bpp == 16) {
+    rdr::U16* buf;
+    buf = (rdr::U16*)buffer;
+    for (i = 0;i < palette.size();i++)
+      *buf++ = palette.getColour(i);
+  } else {
+    rdr::U8* buf;
+    buf = (rdr::U8*)buffer;
+    for (i = 0;i < palette.size();i++)
+      *buf++ = palette.getColour(i);
+  }
+
+  writePixels(buffer, pf, palette.size());
+}
+
+void ZRLEEncoder::writePixels(const rdr::U8* buffer, const PixelFormat& pf,
+                              unsigned int count)
+{
+  Pixel maxPixel;
+  rdr::U8 pixBuf[4];
+
+  maxPixel = pf.pixelFromRGB((rdr::U16)-1, (rdr::U16)-1, (rdr::U16)-1);
+  pf.bufferFromPixel(pixBuf, maxPixel);
+
+  if ((pf.bpp != 32) || ((pixBuf[0] != 0) && (pixBuf[3] != 0))) {
+    zos.writeBytes(buffer, count * pf.bpp/8);
+    return;
+  }
+
+  if (pixBuf[0] == 0)
+    buffer++;
+
+  while (count--) {
+    zos.writeBytes(buffer, 3);
+    buffer += 4;
+  }
+}
+
+//
+// Including BPP-dependent implementation of the encoder.
+//
+
+#define BPP 8
+#include <rfb/ZRLEEncoderBPP.cxx>
+#undef BPP
+#define BPP 16
+#include <rfb/ZRLEEncoderBPP.cxx>
+#undef BPP
+#define BPP 32
+#include <rfb/ZRLEEncoderBPP.cxx>
+#undef BPP