Abstract platform rendering to "surfaces"

This will allow us to render more things than just the framebuffer.
diff --git a/vncviewer/Surface_OSX.cxx b/vncviewer/Surface_OSX.cxx
new file mode 100644
index 0000000..c51e47f
--- /dev/null
+++ b/vncviewer/Surface_OSX.cxx
@@ -0,0 +1,162 @@
+/* Copyright 2016 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
+ * 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 <assert.h>
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <FL/Fl_RGB_Image.H>
+#include <FL/Fl_Window.H>
+#include <FL/x.H>
+
+#include <rdr/Exception.h>
+
+#include "Surface.h"
+
+void Surface::clear(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+  unsigned char* out;
+  int x, y;
+
+  r = (unsigned)r * a / 255;
+  g = (unsigned)g * a / 255;
+  b = (unsigned)b * a / 255;
+
+  out = data;
+  for (y = 0;y < width();y++) {
+    for (x = 0;x < height();x++) {
+      *out++ = b;
+      *out++ = g;
+      *out++ = r;
+      *out++ = a;
+    }
+  }
+}
+
+void Surface::draw(int src_x, int src_y, int x, int y, int w, int h)
+{
+  CGRect rect;
+
+  CGContextSaveGState(fl_gc);
+
+  // Reset the transformation matrix back to the default identity
+  // matrix as otherwise we get a massive performance hit
+  CGContextConcatCTM(fl_gc, CGAffineTransformInvert(CGContextGetCTM(fl_gc)));
+
+  // macOS Coordinates are from bottom left, not top left
+  src_y = height() - (src_y + h);
+  y = Fl_Window::current()->h() - (y + h);
+
+  // We have to use clipping to partially display an image
+  rect.origin.x = x;
+  rect.origin.y = y;
+  rect.size.width = w;
+  rect.size.height = h;
+
+  CGContextClipToRect(fl_gc, rect);
+
+  rect.origin.x = x - src_x;
+  rect.origin.y = y - src_y;
+  rect.size.width = width();
+  rect.size.height = height();
+
+  CGContextDrawImage(fl_gc, rect, image);
+
+  CGContextRestoreGState(fl_gc);
+}
+
+void Surface::alloc()
+{
+  CGColorSpaceRef lut;
+  CGDataProviderRef provider;
+
+  data = new unsigned char[width() * height() * 4];
+
+  lut = CGDisplayCopyColorSpace(kCGDirectMainDisplay);
+  if (!lut) {
+    lut = CGColorSpaceCreateDeviceRGB();
+    if (!lut)
+      throw rdr::Exception("CGColorSpaceCreateDeviceRGB");
+  }
+
+  provider = CGDataProviderCreateWithData(NULL, data,
+                                          width() * height() * 4, NULL);
+  if (!provider)
+    throw rdr::Exception("CGDataProviderCreateWithData");
+
+  image = CGImageCreate(width(), height(), 8, 32, width() * 4, lut,
+                        kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little,
+                        provider, NULL, false, kCGRenderingIntentDefault);
+  CGColorSpaceRelease(lut);
+  CGDataProviderRelease(provider);
+  if (!image)
+    throw rdr::Exception("CGImageCreate");
+}
+
+void Surface::dealloc()
+{
+  CGImageRelease(image);
+  delete [] data;
+}
+
+void Surface::update(const Fl_RGB_Image* image)
+{
+  int x, y;
+  const unsigned char* in;
+  unsigned char* out;
+
+  assert(image->w() == width());
+  assert(image->h() == height());
+
+  // Convert data and pre-multiply alpha
+  in = (const unsigned char*)image->data()[0];
+  out = data;
+  for (y = 0;y < image->h();y++) {
+    for (x = 0;x < image->w();x++) {
+      switch (image->d()) {
+      case 1:
+        *out++ = in[0];
+        *out++ = in[0];
+        *out++ = in[0];
+        *out++ = 0xff;
+        break;
+      case 2:
+        *out++ = (unsigned)in[0] * in[1] / 255;
+        *out++ = (unsigned)in[0] * in[1] / 255;
+        *out++ = (unsigned)in[0] * in[1] / 255;
+        *out++ = in[1];
+        break;
+      case 3:
+        *out++ = in[2];
+        *out++ = in[1];
+        *out++ = in[0];
+        *out++ = 0xff;
+        break;
+      case 4:
+        *out++ = (unsigned)in[2] * in[3] / 255;
+        *out++ = (unsigned)in[1] * in[3] / 255;
+        *out++ = (unsigned)in[0] * in[3] / 255;
+        *out++ = in[3];
+        break;
+      }
+      in += image->d();
+    }
+    if (image->ld() != 0)
+      in += image->ld() - image->w() * image->d();
+  }
+}