]> source.dussan.org Git - tigervnc.git/commitdiff
Added JpegCompressor abstract class and two implementations -- one cross-platform...
authorConstantin Kaplinsky <const@tightvnc.com>
Tue, 24 Jul 2007 12:10:16 +0000 (12:10 +0000)
committerConstantin Kaplinsky <const@tightvnc.com>
Tue, 24 Jul 2007 12:10:16 +0000 (12:10 +0000)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2320 3789f03b-4d11-0410-bbf8-ca57d06f2519

common/configure
common/configure.in
common/rfb/IrixDMIC_RawToJpeg.cxx [new file with mode: 0644]
common/rfb/IrixDMIC_RawToJpeg.h [new file with mode: 0644]
common/rfb/IrixDMJpegCompressor.cxx [new file with mode: 0644]
common/rfb/IrixDMJpegCompressor.h [new file with mode: 0644]
common/rfb/JpegCompressor.cxx [new file with mode: 0644]
common/rfb/JpegCompressor.h [new file with mode: 0644]
common/rfb/Makefile.in

index 10a9c029bd968186c0eb01fbadcc027e9d5ab0cb..085e4fa719ae90db45c1b8686d8e3b488b4f996a 100755 (executable)
@@ -896,61 +896,10 @@ else
   fi
 fi
 
-for ac_declaration in \
-   ''\
-   '#include <stdlib.h>' \
-   'extern "C" void std::exit (int) throw (); using std::exit;' \
-   'extern "C" void std::exit (int); using std::exit;' \
-   'extern "C" void exit (int) throw ();' \
-   'extern "C" void exit (int);' \
-   'void exit (int);'
-do
-  cat > conftest.$ac_ext <<EOF
-#line 910 "configure"
-#include "confdefs.h"
-#include <stdlib.h>
-$ac_declaration
-int main() {
-exit (42);
-; return 0; }
-EOF
-if { (eval echo configure:918: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
-  :
-else
-  echo "configure: failed program was:" >&5
-  cat conftest.$ac_ext >&5
-  rm -rf conftest*
-  continue
-fi
-rm -f conftest*
-  cat > conftest.$ac_ext <<EOF
-#line 928 "configure"
-#include "confdefs.h"
-$ac_declaration
-int main() {
-exit (42);
-; return 0; }
-EOF
-if { (eval echo configure:935: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
-  rm -rf conftest*
-  break
-else
-  echo "configure: failed program was:" >&5
-  cat conftest.$ac_ext >&5
-fi
-rm -f conftest*
-done
-if test -n "$ac_declaration"; then
-  echo '#ifdef __cplusplus' >>confdefs.h
-  echo $ac_declaration      >>confdefs.h
-  echo '#endif'             >>confdefs.h
-fi
-
-
 # Extract the first word of "ranlib", so it can be a program name with args.
 set dummy ranlib; ac_word=$2
 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
-echo "configure:954: checking for $ac_word" >&5
+echo "configure:903: checking for $ac_word" >&5
 if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -978,7 +927,7 @@ else
 fi
 
 echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
-echo "configure:982: checking whether ${MAKE-make} sets \${MAKE}" >&5
+echo "configure:931: checking whether ${MAKE-make} sets \${MAKE}" >&5
 set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
 if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
@@ -1043,6 +992,57 @@ if test "$GXX" = yes; then
   fi
 fi
 
+echo $ac_n "checking for dmICCreate in -ldmedia""... $ac_c" 1>&6
+echo "configure:997: checking for dmICCreate in -ldmedia" >&5
+ac_lib_var=`echo dmedia'_'dmICCreate | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  ac_save_LIBS="$LIBS"
+LIBS="-ldmedia  $LIBS"
+cat > conftest.$ac_ext <<EOF
+#line 1005 "configure"
+#include "confdefs.h"
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+    builtin and then its argument prototype would still apply.  */
+char dmICCreate();
+
+int main() {
+dmICCreate()
+; return 0; }
+EOF
+if { (eval echo configure:1019: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=yes"
+else
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_lib_$ac_lib_var=no"
+fi
+rm -f conftest*
+LIBS="$ac_save_LIBS"
+
+fi
+if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+  cat >> confdefs.h <<\EOF
+#define HAVE_DMEDIA 1
+EOF
+
+  PLATFORM_CXXSRCS="$PLATFORM_CXXSRCS IrixDMJpegCompressor.cxx"
+  PLATFORM_CXXSRCS="$PLATFORM_CXXSRCS IrixDMIC_RawToJpeg.cxx"
+  LIBS="$LIBS -ldmedia"
+else
+  echo "$ac_t""no" 1>&6
+fi
+
+
+
 # Check whether --with-installed-zlib or --without-installed-zlib was given.
 if test "${with_installed_zlib+set}" = set; then
   withval="$with_installed_zlib"
@@ -1438,6 +1438,7 @@ s%@CC@%$CC%g
 s%@CXX@%$CXX%g
 s%@RANLIB@%$RANLIB%g
 s%@SET_MAKE@%$SET_MAKE%g
+s%@PLATFORM_CXXSRCS@%$PLATFORM_CXXSRCS%g
 s%@ZLIB_DIR@%$ZLIB_DIR%g
 s%@ZLIB_INCLUDE@%$ZLIB_INCLUDE%g
 s%@ZLIB_LIB@%$ZLIB_LIB%g
index 576eac7a37b8ce7f16dad06cfab9961f1d46c5ec..f5c04c1cbed98fa0f41f35c4aa7328cecdb8b2a5 100644 (file)
@@ -44,6 +44,14 @@ if test "$GXX" = yes; then
   fi
 fi
 
+dnl Checks for IRIX-specific Digital Media libraries.
+AC_CHECK_LIB(dmedia, dmICCreate,
+  [AC_DEFINE(HAVE_DMEDIA)
+  PLATFORM_CXXSRCS="$PLATFORM_CXXSRCS IrixDMJpegCompressor.cxx"
+  PLATFORM_CXXSRCS="$PLATFORM_CXXSRCS IrixDMIC_RawToJpeg.cxx"
+  LIBS="$LIBS -ldmedia"])
+AC_SUBST(PLATFORM_CXXSRCS)
+
 AC_ARG_WITH(installed-zlib,
 [  --with-installed-zlib   use the version of zlib which is installed on the
                           system instead of the one distributed with VNC])
diff --git a/common/rfb/IrixDMIC_RawToJpeg.cxx b/common/rfb/IrixDMIC_RawToJpeg.cxx
new file mode 100644 (file)
index 0000000..6b76304
--- /dev/null
@@ -0,0 +1,423 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+
+#include <rfb/IrixDMIC_RawToJpeg.h>
+
+using namespace rfb;
+
+//
+// Constructor. Find a converter and create a context. Also, create
+// DMparams structures for using with dmICSetSrcParams,
+// dmICSetDstParams and dmICSetConvParams.
+//
+
+IrixDMIC_RawToJpeg::IrixDMIC_RawToJpeg(bool needRealtime)
+  : m_valid_ic(false),
+    m_srcParams(0),
+    m_dstParams(0),
+    m_convParams(0)
+{
+  if ( dmParamsCreate(&m_srcParams) != DM_SUCCESS ||
+       dmParamsCreate(&m_dstParams) != DM_SUCCESS ||
+       dmParamsCreate(&m_convParams) != DM_SUCCESS ) {
+    reportError("dmParamsCreate");
+    return;
+  }
+
+  DMparams *p;
+  const int JPEG_ID = 0x6A706567; // same as 'jpeg'
+  int id, direction, speed;
+
+  int n = dmICGetNum();
+  while (n--) {
+    if (dmParamsCreate(&p) != DM_SUCCESS) {
+      reportError("dmParamsCreate");
+      return;
+    }
+    if (dmICGetDescription(n, p) != DM_SUCCESS)
+      continue;
+    id = dmParamsGetInt(p, DM_IC_ID);
+    direction = dmParamsGetEnum(p, DM_IC_CODE_DIRECTION);
+    speed = dmParamsGetEnum(p, DM_IC_SPEED);
+    if ( id == JPEG_ID && direction == DM_IC_CODE_DIRECTION_ENCODE &&
+        (!needRealtime || speed == DM_IC_SPEED_REALTIME) ) {
+
+      // FIXME: Just save the engine name and make x0vncserver print it.
+      const char *engine = dmParamsGetString(p, DM_IC_ENGINE);
+      printf("Found JPEG encoder: \"%s\" (%d)\n", engine, n);
+
+      dmParamsDestroy(p);
+      break;
+    }
+    dmParamsDestroy(p);
+  }
+  if (n < 0) {
+    // FIXME: Unify error handling.
+    fprintf (stderr, "Error: No matching JPEG encoder found\n");
+    return;
+  }
+  if (dmICCreate(n, &m_ic) != DM_SUCCESS) {
+    reportError("dmICCreate");
+    return;
+  }
+
+  m_valid_ic = true;
+}
+
+//
+// Destructor.
+//
+
+IrixDMIC_RawToJpeg::~IrixDMIC_RawToJpeg()
+{
+  if (m_valid_ic)
+    dmICDestroy(m_ic);
+
+  if (m_convParams)
+    dmParamsDestroy(m_convParams);
+  if (m_dstParams)
+    dmParamsDestroy(m_dstParams);
+  if (m_srcParams)
+    dmParamsDestroy(m_srcParams);
+}
+
+//
+// Configure the source and destination formats.
+//
+// FIXME: Remember image size that was previously set, do not set the
+//        same size again.
+//
+
+bool
+IrixDMIC_RawToJpeg::setImageParams(int w, int h)
+{
+  if (!m_valid_ic) {
+    reportErrorNotInited();
+    return false;
+  }
+
+  // Set source image parameters.
+  DMpacking packing = DM_IMAGE_PACKING_XBGR;
+  int orient = DM_IMAGE_TOP_TO_BOTTOM;
+  if (dmSetImageDefaults(m_srcParams, w, h, packing) != DM_SUCCESS) {
+    reportError("dmSetImageDefaults");
+    return false;
+  }
+  DMstatus err = dmParamsSetEnum(m_srcParams, DM_IMAGE_ORIENTATION, orient);
+  if (err != DM_SUCCESS) {
+    reportError("dmParamsSetEnum");
+    return false;
+  }
+  if (dmICSetSrcParams(m_ic, m_srcParams) != DM_SUCCESS) {
+    reportError("dmICSetSrcParams");
+    return false;
+  }
+
+  // Set destination image parameters.
+  packing = DM_IMAGE_PACKING_CbYCrY;
+  const char *compression = DM_IMAGE_JPEG;
+  if (dmSetImageDefaults(m_dstParams, w, h, packing) != DM_SUCCESS) {
+    reportError("dmSetImageDefaults");
+    return false;
+  }
+  err = dmParamsSetEnum(m_dstParams, DM_IMAGE_ORIENTATION, orient);
+  if (err != DM_SUCCESS) {
+    reportError("dmParamsSetEnum");
+    return false;
+  }
+  err = dmParamsSetString(m_dstParams, DM_IMAGE_COMPRESSION, compression);
+  if (err != DM_SUCCESS) {
+    reportError("dmParamsSetString");
+    return false;
+  }
+  if (dmICSetDstParams(m_ic, m_dstParams) != DM_SUCCESS) {
+    reportError("dmICSetDstParams");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Set JPEG image quality level (an integer in the range 0..99).
+//
+// FIXME: Remember image quality that was previously set, do not set
+//        the same quality again.
+//
+
+bool
+IrixDMIC_RawToJpeg::setImageQuality(int quality)
+{
+  if (!m_valid_ic) {
+    reportErrorNotInited();
+    return false;
+  }
+
+  double qf = (double)quality / 100.0;
+
+  DMstatus err = dmParamsSetFloat(m_convParams, DM_IMAGE_QUALITY_SPATIAL, qf);
+  if (err != DM_SUCCESS) {
+    reportError("dmParamsSetFloat");
+    return false;
+  }
+  if (dmICSetConvParams(m_ic, m_convParams) != DM_SUCCESS) {
+    reportError("dmICSetConvParams");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Set up source buffer pool.
+//
+// NOTE: Both setImageParams() and setImageQuality() functions should
+//       be called prior to creating and registering buffer pools.
+//
+
+bool
+IrixDMIC_RawToJpeg::createSrcBufferPool(DMbufferpool *pool,
+                                        int bufCount, int bufSize)
+{
+  if (!m_valid_ic) {
+    reportErrorNotInited();
+    return false;
+  }
+
+  DMparams *p;
+  if (dmParamsCreate(&p) != DM_SUCCESS) {
+    reportError("dmParamsCreate");
+    return false;
+  }
+
+  if (dmBufferSetPoolDefaults(p, bufCount, bufSize, DM_FALSE, DM_TRUE) !=
+      DM_SUCCESS) {
+    reportError("dmBufferSetPoolDefaults");
+    dmParamsDestroy(p);
+    return false;
+  }
+  if (dmICGetSrcPoolParams(m_ic, p) != DM_SUCCESS) {
+    reportError("dmICGetSrcPoolParams");
+    dmParamsDestroy(p);
+    return false;
+  }
+  if (dmBufferCreatePool(p, pool) != DM_SUCCESS) {
+    reportError("dmBufferCreatePool");
+    dmParamsDestroy(p);
+    return false;
+  }
+
+  dmParamsDestroy(p);
+  return true;
+}
+
+//
+// Set up and register destination buffer pool.
+//
+// NOTE: Both setImageParams() and setImageQuality() functions should
+//       be called prior to creating and registering buffer pools.
+//
+
+bool
+IrixDMIC_RawToJpeg::registerDstBufferPool(DMbufferpool *pool,
+                                          int bufCount, int bufSize)
+{
+  if (!m_valid_ic) {
+    reportErrorNotInited();
+    return false;
+  }
+
+  DMparams *p;
+  if (dmParamsCreate(&p) != DM_SUCCESS) {
+    reportError("dmParamsCreate");
+    return false;
+  }
+
+  if (dmBufferSetPoolDefaults(p, bufCount, bufSize, DM_FALSE, DM_TRUE) !=
+      DM_SUCCESS) {
+    reportError("dmBufferSetPoolDefaults");
+    dmParamsDestroy(p);
+    return false;
+  }
+  if (dmICGetDstPoolParams(m_ic, p) != DM_SUCCESS) {
+    reportError("dmICGetDstPoolParams");
+    dmParamsDestroy(p);
+    return false;
+  }
+  if (dmBufferCreatePool(p, pool) != DM_SUCCESS) {
+    reportError("dmBufferCreatePool");
+    dmParamsDestroy(p);
+    return false;
+  }
+
+  dmParamsDestroy(p);
+
+  if (dmICSetDstPool(m_ic, *pool) != DM_SUCCESS) {
+    reportError("dmICSetDstPool");
+    destroyBufferPool(*pool);
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Destroy buffer pool created with either createSrcBufferPool() or
+// registerDstBufferPool().
+//
+
+void
+IrixDMIC_RawToJpeg::destroyBufferPool(DMbufferpool pool)
+{
+  if (dmBufferDestroyPool(pool) != DM_SUCCESS)
+    reportError("dmBufferDestroyPool");
+}
+
+//
+// Allocate a buffer from the specified pool.
+//
+
+bool
+IrixDMIC_RawToJpeg::allocBuffer(DMbuffer *pbuf, DMbufferpool pool)
+{
+  if (dmBufferAllocate(pool, pbuf) != DM_SUCCESS) {
+    reportError("dmBufferAllocate");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Fill in a DMbuffer with data.
+//
+// NOTE: The caller must make sure that the buffer size is no less
+//       than dataSize.
+//
+
+bool
+IrixDMIC_RawToJpeg::copyToBuffer(DMbuffer buf, const void *data, int dataSize)
+{
+  void *bufPtr = dmBufferMapData(buf);
+  memcpy(bufPtr, data, dataSize);
+
+  if (dmBufferSetSize(buf, dataSize) != DM_SUCCESS) {
+    reportError("dmBufferSetSize");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Map DMbuffer to physical memory.
+//
+
+void *
+IrixDMIC_RawToJpeg::mapBufferData(DMbuffer buf)
+{
+  return dmBufferMapData(buf);
+}
+
+//
+// Get the number of valid bytes in DMbuffer.
+//
+
+int
+IrixDMIC_RawToJpeg::getBufferSize(DMbuffer buf)
+{
+  return dmBufferGetSize(buf);
+}
+
+//
+// Free DMbuffer.
+//
+
+void
+IrixDMIC_RawToJpeg::freeBuffer(DMbuffer buf)
+{
+  if (dmBufferFree(buf) != DM_SUCCESS)
+    reportError("dmBufferFree");
+}
+
+//
+// Send input data (raw pixels) to the converter.
+//
+
+bool
+IrixDMIC_RawToJpeg::sendData(DMbuffer buf)
+{
+  if (dmICSend(m_ic, buf, 0, NULL) != DM_SUCCESS) {
+    reportError("dmICSend");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Wait until compression is finished (infinite timeout!).
+// This function should be called after sendData() and before receiveData().
+//
+// FIXME: Report errors.
+//
+
+bool
+IrixDMIC_RawToJpeg::waitConversion()
+{
+  struct pollfd ps;
+  ps.fd = dmICGetDstQueueFD(m_ic);
+  ps.events = POLLIN;
+
+  int result = poll(&ps, 1, -1);
+  if (result != 1)
+    return false;
+
+  if ((ps.revents & POLLIN) != 0) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
+//
+// Receive output (JPEG data) from the converter.
+// Call waitConversion() function first.
+//
+
+bool
+IrixDMIC_RawToJpeg::receiveData(DMbuffer *pbuf)
+{
+  if (dmICReceive(m_ic, pbuf) != DM_SUCCESS) {
+    reportError("dmICReceive");
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Report an error when a function returns DM_FAILURE.
+//
+
+void
+IrixDMIC_RawToJpeg::reportError(const char *funcName)
+{
+  char errorDetail[DM_MAX_ERROR_DETAIL];
+  const char *errorCategory = dmGetError(NULL, errorDetail);
+  fprintf(stderr, "%s() failed: %s: %s\n",
+         funcName, errorCategory, errorDetail);
+}
+
+//
+// Report an error when (m_valid_ic == false).
+//
+
+void
+IrixDMIC_RawToJpeg::reportErrorNotInited()
+{
+  fprintf(stderr, "Internal error: Image converter not initialized\n");
+}
+
diff --git a/common/rfb/IrixDMIC_RawToJpeg.h b/common/rfb/IrixDMIC_RawToJpeg.h
new file mode 100644 (file)
index 0000000..33674bd
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef __IRIXDMIC_RAWTOJPEG_H__
+#define __IRIXDMIC_RAWTOJPEG_H__
+
+#include <dmedia/dmedia.h>
+#include <dmedia/dm_imageconvert.h>
+
+namespace rfb {
+
+  //
+  // A C++ wrapper for IRIX-specific Digital Media Image Conversion
+  // library used for JPEG compression.
+  //
+
+  class IrixDMIC_RawToJpeg
+  {
+  public:
+    IrixDMIC_RawToJpeg(bool needRealtime = true);
+    virtual ~IrixDMIC_RawToJpeg();
+
+    bool isValid() const { return m_valid_ic; }
+
+    bool setImageParams(int w, int h);
+    bool setImageQuality(int quality);
+
+    bool createSrcBufferPool(DMbufferpool *pool, int bufCount, int bufSize);
+    bool registerDstBufferPool(DMbufferpool *pool, int bufCount, int bufSize);
+    static void destroyBufferPool(DMbufferpool pool);
+
+    static bool allocBuffer(DMbuffer *pbuf, DMbufferpool pool);
+    static bool copyToBuffer(DMbuffer buf, const void *data, int dataSize);
+    static int getBufferSize(DMbuffer buf);
+    static void * mapBufferData(DMbuffer buf);
+    static void freeBuffer(DMbuffer buf);
+
+    bool sendData(DMbuffer buf);
+    bool waitConversion();
+    bool receiveData(DMbuffer *pbuf);
+
+    static void reportError(const char *funcName);
+    static void reportErrorNotInited();
+
+  protected:
+    DMimageconverter m_ic;
+    bool m_valid_ic;
+
+    DMparams *m_srcParams;
+    DMparams *m_dstParams;
+    DMparams *m_convParams;
+  };
+
+}
+
+#endif // __IRIXDMIC_RAWTOJPEG_H__
+
diff --git a/common/rfb/IrixDMJpegCompressor.cxx b/common/rfb/IrixDMJpegCompressor.cxx
new file mode 100644 (file)
index 0000000..57e8aa8
--- /dev/null
@@ -0,0 +1,153 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/poll.h>
+
+#include <rfb/IrixDMJpegCompressor.h>
+
+using namespace rfb;
+
+const int IrixDMJpegCompressor::DEFAULT_QUALITY = 75;
+
+//
+// Constructor and destructor.
+//
+
+IrixDMJpegCompressor::IrixDMJpegCompressor()
+  : m_quality(DEFAULT_QUALITY),
+    m_width(0),
+    m_height(0),
+    m_bufferSize(0),
+    m_compressedData(0),
+    m_compressedLength(0)
+{
+}
+
+IrixDMJpegCompressor::~IrixDMJpegCompressor()
+{
+  if (m_bufferSize > 0) {
+    m_ic.destroyBufferPool(m_srcPool);
+    m_ic.destroyBufferPool(m_dstPool);
+  }
+
+  if (m_compressedData)
+    delete[] m_compressedData;
+}
+
+//
+// Set JPEG quality level (0..100)
+//
+// FIXME: Call m_ic.setImageQuality() when necessary.
+//
+
+void
+IrixDMJpegCompressor::setQuality(int level)
+{
+  if (level < 0)
+    level = 0;
+  if (level > 100)
+    level = 100;
+
+  m_quality = level;
+}
+
+//
+// Perform JPEG compression.
+//
+// FIXME: This function assumes that pixel format is 32-bit XBGR
+//        (depth 24), compatible with IRIX Digital Media libraries.
+//
+
+void
+IrixDMJpegCompressor::compress(const rdr::U32 *buf,
+                               const PixelFormat *fmt,
+                               int w, int h, int stride)
+{
+  // Discard previous compression results.
+  if (m_compressedData) {
+    delete[] m_compressedData;
+    m_compressedData = 0;
+  }
+  m_compressedLength = 0;
+
+  // Setup the compressor.
+  if (!updateImageSize(w, h))
+    return;
+
+  // Prepare source image data.
+  DMbuffer srcBuf;
+  if (!m_ic.allocBuffer(&srcBuf, m_srcPool)) {
+    return;
+  }
+  // FIXME: Currently, copyToBuffer() copies parts of the image incorrectly.
+  if (!m_ic.copyToBuffer(srcBuf, buf, w * h * (fmt->bpp / 8))) {
+    m_ic.freeBuffer(srcBuf);
+    return;
+  }
+
+  // Initiate compression.
+  if (!m_ic.sendData(srcBuf)) {
+    m_ic.freeBuffer(srcBuf);
+    return;
+  }
+  m_ic.freeBuffer(srcBuf);
+
+  // Wait for results.
+  if (!m_ic.waitConversion()) {
+    perror("poll");            // FIXME: Unify error handling.
+    return;
+  }
+
+  // Save the results.
+  DMbuffer dstBuf;
+  if (!m_ic.receiveData(&dstBuf)) {
+    return;
+  }
+  m_compressedLength = m_ic.getBufferSize(dstBuf);
+  m_compressedData = new char[m_compressedLength];
+  void *bufPtr = m_ic.mapBufferData(dstBuf);
+  memcpy(m_compressedData, bufPtr, m_compressedLength);
+
+  m_ic.freeBuffer(dstBuf);
+}
+
+//
+// Update image size and make sure that our buffer pools will allocate
+// properly-sized buffers.
+//
+// FIXME: Handle image quality separately.
+//
+
+bool
+IrixDMJpegCompressor::updateImageSize(int w, int h)
+{
+  // Configure image formats and parameters, set JPEG image quality level.
+  if (w != m_width || h != m_height) {
+    if (!m_ic.setImageParams(w, h) || !m_ic.setImageQuality(m_quality)) {
+      return false;
+    }
+    m_width = w;
+    m_height = h;
+  }
+
+  // Set up source and destination buffer pools.
+  int dataLen = w * h * 4;
+  if (dataLen > m_bufferSize) {
+    if (m_bufferSize > 0) {
+      m_ic.destroyBufferPool(m_srcPool);
+      m_ic.destroyBufferPool(m_dstPool);
+      m_bufferSize = 0;
+    }
+    if (!m_ic.createSrcBufferPool(&m_srcPool, 1, dataLen)) {
+      return false;
+    }
+    if (!m_ic.registerDstBufferPool(&m_dstPool, 1, dataLen)) {
+      m_ic.destroyBufferPool(m_srcPool);
+      return false;
+    }
+    m_bufferSize = dataLen;
+  }
+
+  return true;
+}
+
diff --git a/common/rfb/IrixDMJpegCompressor.h b/common/rfb/IrixDMJpegCompressor.h
new file mode 100644 (file)
index 0000000..500884a
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef __IRIXDMJPEGCOMPRESSOR_H__
+#define __IRIXDMJPEGCOMPRESSOR_H__
+
+#include <sys/types.h>
+
+#include <rdr/types.h>
+#include <rfb/PixelFormat.h>
+
+#include <rfb/JpegCompressor.h>
+#include <rfb/IrixDMIC_RawToJpeg.h>
+
+namespace rfb {
+
+  //
+  // A C++ class for performing JPEG compression.
+  // This implementation uses IRIX Digital Media libraries.
+  //
+
+  class IrixDMJpegCompressor : public JpegCompressor
+  {
+  public:
+    IrixDMJpegCompressor();
+    virtual ~IrixDMJpegCompressor();
+
+    // Check if the object has been created successfully.
+    bool isValid() const { return m_ic.isValid(); }
+
+    // Set JPEG quality level (0..100).
+    void setQuality(int level);
+
+    // Actually compress the image.
+    void compress(const rdr::U32 *buf, const PixelFormat *fmt,
+                  int w, int h, int stride);
+
+    // Access results of the compression.
+    size_t getDataLength() { return m_compressedLength; }
+    const char *getDataPtr() { return m_compressedData; }
+
+  protected:
+    // Update image size and make sure that our buffer pools will
+    // allocate properly-sized buffers.
+    bool updateImageSize(int w, int h);
+
+  protected:
+    static const int DEFAULT_QUALITY;
+    int m_quality;
+
+    IrixDMIC_RawToJpeg m_ic;
+    int m_width;
+    int m_height;
+    DMbufferpool m_srcPool;
+    DMbufferpool m_dstPool;
+    int m_bufferSize;
+
+    char *m_compressedData;
+    size_t m_compressedLength;
+  };
+
+}
+
+#endif // __IRIXDMJPEGCOMPRESSOR_H__
+
diff --git a/common/rfb/JpegCompressor.cxx b/common/rfb/JpegCompressor.cxx
new file mode 100644 (file)
index 0000000..0914f85
--- /dev/null
@@ -0,0 +1,193 @@
+#include <stdlib.h>
+
+#include <rfb/JpegCompressor.h>
+
+using namespace rfb;
+
+const int StandardJpegCompressor::ALLOC_CHUNK_SIZE = 65536;
+const int StandardJpegCompressor::DEFAULT_QUALITY = 75;
+
+//
+// Extend jpeg_destination_mgr struct with a pointer to our object.
+//
+
+typedef struct {
+  struct jpeg_destination_mgr pub;
+  StandardJpegCompressor *_this;
+} my_destination_mgr;
+
+//
+// C-compatible interface to our destination manager. It just obtains
+// a pointer to the right object and calls a corresponding C++ member
+// function on that object.
+//
+
+static void
+init_destination(j_compress_ptr cinfo)
+{
+  my_destination_mgr *dest_ptr = (my_destination_mgr *)cinfo->dest;
+  dest_ptr->_this->initDestination();
+}
+
+static boolean
+empty_output_buffer (j_compress_ptr cinfo)
+{
+  my_destination_mgr *dest_ptr = (my_destination_mgr *)cinfo->dest;
+  return (boolean)dest_ptr->_this->emptyOutputBuffer();
+}
+
+static void
+term_destination (j_compress_ptr cinfo)
+{
+  my_destination_mgr *dest_ptr = (my_destination_mgr *)cinfo->dest;
+  dest_ptr->_this->termDestination();
+}
+
+//
+// Constructor and destructor.
+//
+
+StandardJpegCompressor::StandardJpegCompressor()
+  : m_cdata(0),
+    m_cdata_allocated(0),
+    m_cdata_ready(0)
+{
+  // Initialize JPEG compression structure.
+  m_cinfo.err = jpeg_std_error(&m_jerr);
+  jpeg_create_compress(&m_cinfo);
+
+  // Set up a destination manager.
+  my_destination_mgr *dest = new my_destination_mgr;
+  dest->pub.init_destination = init_destination;
+  dest->pub.empty_output_buffer = empty_output_buffer;
+  dest->pub.term_destination = term_destination;
+  dest->_this = this;
+  m_cinfo.dest = (jpeg_destination_mgr *)dest;
+
+  // Set up a destination manager.
+  m_cinfo.input_components = 3;
+  m_cinfo.in_color_space = JCS_RGB;
+  jpeg_set_defaults(&m_cinfo);
+  jpeg_set_quality(&m_cinfo, DEFAULT_QUALITY, true);
+
+  // We prefer speed over quality.
+  m_cinfo.dct_method = JDCT_FASTEST;
+}
+
+StandardJpegCompressor::~StandardJpegCompressor()
+{
+  // Free compressed data buffer.
+  if (m_cdata)
+    free(m_cdata);
+
+  // Clean up the destination manager.
+  delete m_cinfo.dest;
+  m_cinfo.dest = NULL;
+
+  // Release the JPEG compression structure.
+  jpeg_destroy_compress(&m_cinfo);
+}
+
+//
+// Our implementation of destination manager.
+//
+
+void
+StandardJpegCompressor::initDestination()
+{
+  if (!m_cdata) {
+    size_t new_size = ALLOC_CHUNK_SIZE;
+    m_cdata = (unsigned char *)malloc(new_size);
+    m_cdata_allocated = new_size;
+  }
+
+  m_cdata_ready = 0;
+  m_cinfo.dest->next_output_byte = m_cdata;
+  m_cinfo.dest->free_in_buffer =  m_cdata_allocated;
+}
+
+bool
+StandardJpegCompressor::emptyOutputBuffer()
+{
+  size_t old_size = m_cdata_allocated;
+  size_t new_size = old_size + ALLOC_CHUNK_SIZE;
+
+  m_cdata = (unsigned char *)realloc(m_cdata, new_size);
+  m_cdata_allocated = new_size;
+
+  m_cinfo.dest->next_output_byte = &m_cdata[old_size];
+  m_cinfo.dest->free_in_buffer = new_size - old_size;
+
+  return true;
+}
+
+void
+StandardJpegCompressor::termDestination()
+{
+  m_cdata_ready = m_cdata_allocated - m_cinfo.dest->free_in_buffer;
+}
+
+//
+// Set JPEG quality level (0..100)
+//
+
+void
+StandardJpegCompressor::setQuality(int level)
+{
+  if (level < 0)
+    level = 0;
+  if (level > 100)
+    level = 100;
+
+  jpeg_set_quality(&m_cinfo, level, true);
+}
+
+//
+// Perform JPEG compression.
+//
+// FIXME: This function assumes that (fmt->bpp == 32 &&
+//        fmt->depth == 24 && fmt->redMax == 255 &&
+//        fmt->greenMax == 255 && fmt->blueMax == 255).
+//
+
+void
+StandardJpegCompressor::compress(const rdr::U32 *buf,
+                                 const PixelFormat *fmt,
+                                 int w, int h, int stride)
+{
+  m_cinfo.image_width = w;
+  m_cinfo.image_height = h;
+
+  jpeg_start_compress(&m_cinfo, TRUE);
+
+  const rdr::U32 *src = buf;
+
+  // We'll pass up to 8 rows to jpeg_write_scanlines().
+  JSAMPLE *rgb = new JSAMPLE[w * 3 * 8];
+  JSAMPROW row_pointer[8];
+  for (int i = 0; i < 8; i++)
+    row_pointer[i] = &rgb[w * 3 * i];
+
+  // Feed the pixels to the JPEG library.
+  while (m_cinfo.next_scanline < m_cinfo.image_height) {
+    int max_rows = m_cinfo.image_height - m_cinfo.next_scanline;
+    if (max_rows > 8) {
+      max_rows = 8;
+    }
+    for (int dy = 0; dy < max_rows; dy++) {
+      JSAMPLE *dst = row_pointer[dy];
+      for (int x = 0; x < w; x++) {
+        dst[x*3]   = (JSAMPLE)(src[x] >> fmt->redShift);
+        dst[x*3+1] = (JSAMPLE)(src[x] >> fmt->greenShift);
+        dst[x*3+2] = (JSAMPLE)(src[x] >> fmt->blueShift);
+      }
+      src += stride;
+    }
+    jpeg_write_scanlines(&m_cinfo, row_pointer, max_rows);
+  }
+
+  delete[] rgb;
+
+  jpeg_finish_compress(&m_cinfo);
+}
+
diff --git a/common/rfb/JpegCompressor.h b/common/rfb/JpegCompressor.h
new file mode 100644 (file)
index 0000000..93fdc7b
--- /dev/null
@@ -0,0 +1,82 @@
+#ifndef __JPEGCOMPRESSOR_H__
+#define __JPEGCOMPRESSOR_H__
+
+#include <stdio.h>
+#include <sys/types.h>
+extern "C" {
+#include <jpeg/jpeglib.h>
+}
+
+#include <rdr/types.h>
+#include <rfb/PixelFormat.h>
+
+namespace rfb {
+
+  //
+  // An abstract interface for performing JPEG compression.
+  //
+
+  class JpegCompressor
+  {
+  public:
+    virtual ~JpegCompressor() {}
+
+    // Set JPEG quality level (0..100)
+    virtual void setQuality(int level) = 0;
+
+    // Actually compress an image.
+    virtual void compress(const rdr::U32 *buf, const PixelFormat *fmt,
+                          int w, int h, int stride) = 0;
+
+    // Access results of the compression.
+    virtual size_t getDataLength() = 0;
+    virtual const char *getDataPtr() = 0;
+  };
+
+  //
+  // A C++ class for performing JPEG compression via the
+  // Independent JPEG Group's software (free JPEG library).
+  //
+
+  class StandardJpegCompressor : public JpegCompressor
+  {
+  public:
+    StandardJpegCompressor();
+    virtual ~StandardJpegCompressor();
+
+    // Set JPEG quality level (0..100)
+    virtual void setQuality(int level);
+
+    // Actually compress the image.
+    virtual void compress(const rdr::U32 *buf, const PixelFormat *fmt,
+                          int w, int h, int stride);
+
+    // Access results of the compression.
+    virtual size_t getDataLength() { return m_cdata_ready; }
+    virtual const char *getDataPtr() { return (const char *)m_cdata; }
+
+  public:
+    // Our implementation of JPEG destination manager. These three
+    // functions should never be called directly. They are made public
+    // because they should be accessible from C-compatible functions
+    // called by the JPEG library.
+    void initDestination();
+    bool emptyOutputBuffer();
+    void termDestination();
+
+  protected:
+    static const int ALLOC_CHUNK_SIZE;
+    static const int DEFAULT_QUALITY;
+
+    struct jpeg_compress_struct m_cinfo;
+    struct jpeg_error_mgr m_jerr;
+
+    unsigned char *m_cdata;
+    size_t m_cdata_allocated;
+    size_t m_cdata_ready;
+  };
+
+}
+
+#endif // __JPEGCOMPRESSOR_H__
+
index 7885753aec5da2ca4914479635cab332b614b77f..93fb73993ede85349b60770c7073c50763a4b29c 100644 (file)
@@ -20,6 +20,7 @@ CXXSRCS = \
   FileManager.cxx \
   FileReader.cxx \
   FileWriter.cxx \
+  JpegCompressor.cxx \
   HTTPServer.cxx \
   HextileDecoder.cxx \
   HextileEncoder.cxx \
@@ -64,7 +65,8 @@ CXXSRCS = \
   ZRLEDecoder.cxx \
   encodings.cxx \
   secTypes.cxx \
-  util.cxx
+  util.cxx \
+  @PLATFORM_CXXSRCS@
 
 SRCS = d3des.c $(CXXSRCS)