Move JPEG decoding into a standalone class (these should have been checked in with r4757.  Oops.)


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4758 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/common/rfb/JpegDecompressor.cxx b/common/rfb/JpegDecompressor.cxx
new file mode 100644
index 0000000..9cd891a
--- /dev/null
+++ b/common/rfb/JpegDecompressor.cxx
@@ -0,0 +1,233 @@
+/* Copyright (C) 2000-2003 Constantin Kaplinsky.  All Rights Reserved.
+ * Copyright (C) 2004-2005 Cendio AB. 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
+ * 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 <rfb/JpegDecompressor.h>
+#include <rdr/Exception.h>
+#include <rfb/Rect.h>
+#include <rfb/PixelFormat.h>
+
+#include <stdio.h>
+extern "C" {
+#include <jpeglib.h>
+}
+#include <jerror.h>
+#include <setjmp.h>
+
+using namespace rfb;
+
+
+//
+// Error manager implmentation for the JPEG library
+//
+
+struct JPEG_ERROR_MGR {
+  struct jpeg_error_mgr pub;
+  jmp_buf jmpBuffer;
+  char lastError[JMSG_LENGTH_MAX];
+};
+
+static void
+JpegErrorExit(j_common_ptr dinfo)
+{
+  JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
+
+  (*dinfo->err->output_message)(dinfo);
+  longjmp(err->jmpBuffer, 1);
+}
+
+static void
+JpegOutputMessage(j_common_ptr dinfo)
+{
+  JPEG_ERROR_MGR *err = (JPEG_ERROR_MGR *)dinfo->err;
+
+  (*dinfo->err->format_message)(dinfo, err->lastError);
+}
+
+
+//
+// Source manager implementation for the JPEG library.
+//
+
+struct JPEG_SRC_MGR {
+  struct jpeg_source_mgr pub;
+  JpegDecompressor *instance;
+};
+
+static void
+JpegNoOp(j_decompress_ptr dinfo)
+{
+}
+
+static boolean
+JpegFillInputBuffer(j_decompress_ptr dinfo)
+{
+  ERREXIT(dinfo, JERR_BUFFER_SIZE);
+  return TRUE;
+}
+
+static void
+JpegSkipInputData(j_decompress_ptr dinfo, long num_bytes)
+{
+  JPEG_SRC_MGR *src = (JPEG_SRC_MGR *)dinfo->src;
+
+  if (num_bytes < 0 || (size_t)num_bytes > src->pub.bytes_in_buffer) {
+    ERREXIT(dinfo, JERR_BUFFER_SIZE);
+  } else {
+    src->pub.next_input_byte += (size_t) num_bytes;
+    src->pub.bytes_in_buffer -= (size_t) num_bytes;
+  }
+}
+
+JpegDecompressor::JpegDecompressor(void)
+{
+  dinfo = new jpeg_decompress_struct;
+
+  err = new struct JPEG_ERROR_MGR;
+  dinfo->err = jpeg_std_error(&err->pub);
+  snprintf(err->lastError, JMSG_LENGTH_MAX, "No error");
+  err->pub.error_exit = JpegErrorExit;
+  err->pub.output_message = JpegOutputMessage;
+
+  if(setjmp(err->jmpBuffer)) {
+    // this will execute if libjpeg has an error
+    throw rdr::Exception(err->lastError);
+  }
+
+  jpeg_create_decompress(dinfo);
+
+  src = new struct JPEG_SRC_MGR;
+  src->pub.init_source = JpegNoOp;
+  src->pub.fill_input_buffer = JpegFillInputBuffer;
+  src->pub.skip_input_data = JpegSkipInputData;
+  src->pub.resync_to_restart = jpeg_resync_to_restart;
+  src->pub.term_source = JpegNoOp;
+  src->instance = this;
+  dinfo->src = (struct jpeg_source_mgr *)src;
+}
+
+JpegDecompressor::~JpegDecompressor(void)
+{
+  if(setjmp(err->jmpBuffer)) {
+    // this will execute if libjpeg has an error
+    return;
+  }
+
+  jpeg_destroy_decompress(dinfo);
+
+  delete err;
+  delete src;
+
+  delete dinfo;
+}
+
+void JpegDecompressor::decompress(const rdr::U8 *jpegBuf, int jpegBufLen,
+  rdr::U8 *buf, int pitch, const Rect& r, const PixelFormat& pf)
+{
+  int w = r.width();
+  int h = r.height();
+  int pixelsize;
+  int dstBufPitch;
+  rdr::U8 *dstBuf = NULL;
+  bool dstBufIsTemp = false;
+  JSAMPROW *rowPointer = NULL;
+
+  if(setjmp(err->jmpBuffer)) {
+    // this will execute if libjpeg has an error
+    jpeg_abort_decompress(dinfo);
+    if (dstBufIsTemp && dstBuf) delete[] dstBuf;
+    if (rowPointer) delete[] rowPointer;
+    throw rdr::Exception(err->lastError);
+  }
+
+  src->pub.next_input_byte = jpegBuf;
+  src->pub.bytes_in_buffer = jpegBufLen;
+
+  jpeg_read_header(dinfo, TRUE);
+  dinfo->out_color_space = JCS_RGB;
+  pixelsize = 3;
+  if (pitch == 0) pitch = w * pf.bpp / 8;
+  dstBufPitch = pitch;
+
+#ifdef JCS_EXTENSIONS
+  // Try to have libjpeg output directly to our native format
+  if (pf.is888()) {
+    int redShift, greenShift, blueShift;
+
+    if(pf.bigEndian) {
+      redShift = 24 - pf.redShift;
+      greenShift = 24 - pf.greenShift;
+      blueShift = 24 - pf.blueShift;
+    } else {
+      redShift = pf.redShift;
+      greenShift = pf.greenShift;
+      blueShift = pf.blueShift;
+    }
+
+    // libjpeg can only handle some "standard" formats
+    if(redShift == 0 && greenShift == 8 && blueShift == 16)
+      dinfo->out_color_space = JCS_EXT_RGBX;
+    if(redShift == 16 && greenShift == 8 && blueShift == 0)
+      dinfo->out_color_space = JCS_EXT_BGRX;
+    if(redShift == 24 && greenShift == 16 && blueShift == 8)
+      dinfo->out_color_space = JCS_EXT_XBGR;
+    if(redShift == 8 && greenShift == 16 && blueShift == 24)
+      dinfo->out_color_space = JCS_EXT_XRGB;
+
+    if (dinfo->out_color_space != JCS_RGB) {
+      dstBuf = (rdr::U8 *)buf;
+      pixelsize = 4;
+    }
+  }
+#endif
+
+  if (dinfo->out_color_space == JCS_RGB) {
+    dstBuf = new rdr::U8[w * h * pixelsize];
+    dstBufIsTemp = true;
+    dstBufPitch = w * pixelsize;
+  }
+
+  rowPointer = new JSAMPROW[h];
+  for (int dy = 0; dy < h; dy++)
+    rowPointer[dy] = (JSAMPROW)(&dstBuf[dy * dstBufPitch]);
+
+  jpeg_start_decompress(dinfo);
+
+  if (dinfo->output_width != (unsigned)r.width()
+    || dinfo->output_height != (unsigned)r.height()
+    || dinfo->output_components != pixelsize) {
+    jpeg_abort_decompress(dinfo);
+    if (dstBufIsTemp && dstBuf) delete[] dstBuf;
+    if (rowPointer) delete[] rowPointer;
+    throw rdr::Exception("Tight Decoding: Wrong JPEG data received.\n");
+  }
+
+  while (dinfo->output_scanline < dinfo->output_height) {
+    jpeg_read_scanlines(dinfo, &rowPointer[dinfo->output_scanline],
+			dinfo->output_height - dinfo->output_scanline);
+  }
+
+  if (dinfo->out_color_space == JCS_RGB)
+    pf.bufferFromRGB((rdr::U8*)buf, dstBuf, w, pitch, h);
+
+  jpeg_finish_decompress(dinfo);
+
+  if (dstBufIsTemp) delete [] dstBuf;
+  delete[] rowPointer;
+}