Split out the pixel format conversion magic to its own class and make
TransImageGetter be just an ImageGetter interface to that functionality. This
allows more flexible use of the pixel conversion routines.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4338 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/rfb/PixelTransformer.cxx b/common/rfb/PixelTransformer.cxx
new file mode 100644
index 0000000..4c57d1b
--- /dev/null
+++ b/common/rfb/PixelTransformer.cxx
@@ -0,0 +1,302 @@
+/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
+ * Copyright 2011 Pierre Ossman <ossman@cendio.se> 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
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rfb/PixelFormat.h>
+#include <rfb/Exception.h>
+#include <rfb/ColourMap.h>
+#include <rfb/TrueColourMap.h>
+#include <rfb/PixelBuffer.h>
+#include <rfb/ColourCube.h>
+#include <rfb/PixelTransformer.h>
+
+using namespace rfb;
+
+static void noTransFn(void* table_,
+                      const PixelFormat& inPF, void* inPtr, int inStride,
+                      const PixelFormat& outPF, void* outPtr, int outStride,
+                      int width, int height)
+{
+  rdr::U8* ip = (rdr::U8*)inPtr;
+  rdr::U8* op = (rdr::U8*)outPtr;
+  int inStrideBytes = inStride * (inPF.bpp/8);
+  int outStrideBytes = outStride * (outPF.bpp/8);
+  int widthBytes = width * (outPF.bpp/8);
+
+  while (height > 0) {
+    memcpy(op, ip, widthBytes);
+    ip += inStrideBytes;
+    op += outStrideBytes;
+    height--;
+  }
+}
+
+#define BPPOUT 8
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+#define BPPOUT 16
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+#define BPPOUT 32
+#include "transInitTempl.h"
+#define BPPIN 8
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 16
+#include "transTempl.h"
+#undef BPPIN
+#define BPPIN 32
+#include "transTempl.h"
+#undef BPPIN
+#undef BPPOUT
+
+
+// Translation functions.  Note that transSimple* is only used for 8/16bpp and
+// transRGB* is used for 16/32bpp
+
+static transFnType transSimpleFns[][3] = {
+  { transSimple8to8,  transSimple8to16,  transSimple8to32 },
+  { transSimple16to8, transSimple16to16, transSimple16to32 },
+};
+static transFnType transRGBFns[][3] = {
+  { transRGB16to8, transRGB16to16, transRGB16to32 },
+  { transRGB32to8, transRGB32to16, transRGB32to32 }
+};
+static transFnType transRGBCubeFns[][3] = {
+  { transRGBCube16to8, transRGBCube16to16, transRGBCube16to32 },
+  { transRGBCube32to8, transRGBCube32to16, transRGBCube32to32 }
+};
+
+// Table initialisation functions.
+
+typedef void (*initCMtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                 ColourMap* cm, const PixelFormat& outPF);
+typedef void (*initTCtoTCFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                 const PixelFormat& outPF);
+typedef void (*initCMtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                   ColourMap* cm, ColourCube* cube);
+typedef void (*initTCtoCubeFnType)(rdr::U8** tablep, const PixelFormat& inPF,
+                                   ColourCube* cube);
+
+
+static initCMtoTCFnType initSimpleCMtoTCFns[] = {
+    initSimpleCMtoTC8, initSimpleCMtoTC16, initSimpleCMtoTC32
+};
+
+static initTCtoTCFnType initSimpleTCtoTCFns[] = {
+    initSimpleTCtoTC8, initSimpleTCtoTC16, initSimpleTCtoTC32
+};
+
+static initCMtoCubeFnType initSimpleCMtoCubeFns[] = {
+    initSimpleCMtoCube8, initSimpleCMtoCube16, initSimpleCMtoCube32
+};
+
+static initTCtoCubeFnType initSimpleTCtoCubeFns[] = {
+    initSimpleTCtoCube8, initSimpleTCtoCube16, initSimpleTCtoCube32
+};
+
+static initTCtoTCFnType initRGBTCtoTCFns[] = {
+    initRGBTCtoTC8, initRGBTCtoTC16, initRGBTCtoTC32
+};
+
+static initTCtoCubeFnType initRGBTCtoCubeFns[] = {
+    initRGBTCtoCube8, initRGBTCtoCube16, initRGBTCtoCube32
+};
+
+
+PixelTransformer::PixelTransformer(bool econ)
+  : economic(econ), cmCallback(0), table(0), transFn(0), cube(0)
+{
+}
+
+PixelTransformer::~PixelTransformer()
+{
+  delete [] table;
+}
+
+void PixelTransformer::init(const PixelFormat& inPF_, ColourMap* inCM_,
+                            const PixelFormat& outPF_, ColourCube* cube_,
+                            setCMFnType cmCallback_, void *cbData_)
+{
+  inPF = inPF_;
+  inCM = inCM_;
+
+  outPF = outPF_;
+  cube = cube_;
+  cmCallback = cmCallback_;
+  cbData = cbData_;
+
+  if (table)
+    delete [] table;
+  table = NULL;
+  transFn = NULL;
+
+  if ((inPF.bpp != 8) && (inPF.bpp != 16) && (inPF.bpp != 32))
+    throw Exception("PixelTransformer: bpp in not 8, 16 or 32");
+
+  if ((outPF.bpp != 8) && (outPF.bpp != 16) && (outPF.bpp != 32))
+    throw Exception("PixelTransformer: bpp out not 8, 16 or 32");
+
+  if (!outPF.trueColour) {
+    if (outPF.bpp != 8)
+      throw Exception("PixelTransformer: outPF has color map but not 8bpp");
+
+    if (!inPF.trueColour) {
+      if (inPF.bpp != 8)
+        throw Exception("PixelTransformer: inPF has colorMap but not 8bpp");
+      if (!inCM)
+        throw Exception("PixelTransformer: inPF has colorMap but no colour map specified");
+
+      // CM to CM/Cube
+
+      if (cube) {
+        transFn = transSimpleFns[0][0];
+        (*initSimpleCMtoCubeFns[0]) (&table, inPF, inCM, cube);
+      } else {
+        transFn = noTransFn;
+        setColourMapEntries(0, 256);
+      }
+      return;
+    }
+
+    // TC to CM/Cube
+
+    ColourCube defaultCube(6,6,6);
+    if (!cube) cube = &defaultCube;
+
+    if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) {
+      transFn = transRGBCubeFns[inPF.bpp/32][0];
+      (*initRGBTCtoCubeFns[0]) (&table, inPF, cube);
+    } else {
+      transFn = transSimpleFns[inPF.bpp/16][0];
+      (*initSimpleTCtoCubeFns[0]) (&table, inPF, cube);
+    }
+
+    if (cube != &defaultCube)
+      return;
+
+    if (!cmCallback)
+      throw Exception("PixelTransformer: Neither colour map callback nor colour cube provided");
+
+    cmCallback(0, 216, cube, cbData);
+    cube = 0;
+    return;
+  }
+
+  if (inPF.equal(outPF)) {
+    transFn = noTransFn;
+    return;
+  }
+
+  if (!inPF.trueColour) {
+
+    // CM to TC
+
+    if (inPF.bpp != 8)
+      throw Exception("PixelTransformer: inPF has colorMap but not 8bpp");
+    if (!inCM)
+      throw Exception("PixelTransformer: inPF has colorMap but no colour map specified");
+
+    transFn = transSimpleFns[0][outPF.bpp/16];
+    (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, inPF, inCM, outPF);
+    return;
+  }
+
+  // TC to TC
+
+  if ((inPF.bpp > 16) || (economic && (inPF.bpp == 16))) {
+    transFn = transRGBFns[inPF.bpp/32][outPF.bpp/16];
+    (*initRGBTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF);
+  } else {
+    transFn = transSimpleFns[inPF.bpp/16][outPF.bpp/16];
+    (*initSimpleTCtoTCFns[outPF.bpp/16]) (&table, inPF, outPF);
+  }
+}
+
+void PixelTransformer::setColourMapEntries(int firstCol, int nCols)
+{
+  if (nCols == 0)
+    nCols = (1 << inPF.depth) - firstCol;
+
+  if (inPF.trueColour) return; // shouldn't be called in this case
+
+  if (outPF.trueColour) {
+    (*initSimpleCMtoTCFns[outPF.bpp/16]) (&table, inPF, inCM, outPF);
+  } else if (cube) {
+    (*initSimpleCMtoCubeFns[outPF.bpp/16]) (&table, inPF, inCM, cube);
+  } else {
+    if (!cmCallback)
+      throw Exception("PixelTransformer: Neither colour map callback nor colour cube provided");
+    cmCallback(firstCol, nCols, inCM, cbData);
+  }
+}
+
+void PixelTransformer::translatePixels(void* inPtr, void* outPtr,
+                                       int nPixels) const
+{
+  if (!transFn)
+    throw Exception("PixelTransformer: not initialised yet");
+
+  (*transFn)(table, inPF, inPtr, nPixels,
+             outPF, outPtr, nPixels, nPixels, 1);
+}
+
+void PixelTransformer::translateRect(void* inPtr, int inStride,
+                                     Rect inRect,
+                                     void* outPtr, int outStride,
+                                     Point outCoord) const
+{
+  char *in, *out;
+
+  if (!transFn)
+    throw Exception("PixelTransformer: not initialised yet");
+
+  in = (char*)inPtr;
+  in += inPF.bpp/8 * inRect.tl.x;
+  in += (inStride * inPF.bpp/8) * inRect.tl.y;
+
+  out = (char*)outPtr;
+  out += outPF.bpp/8 * outCoord.x;
+  out += (outStride * outPF.bpp/8) * outCoord.y;
+
+  (*transFn)(table, inPF, in, inStride,
+             outPF, out, outStride,
+             inRect.width(), inRect.height());
+}