From 71a32f0dc858823782eee0fcc3dd52b55c1a8a27 Mon Sep 17 00:00:00 2001 From: Constantin Kaplinsky Date: Tue, 24 Jul 2007 12:10:16 +0000 Subject: [PATCH] Added JpegCompressor abstract class and two implementations -- one cross-platform and another IRIX-specific. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2320 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- common/configure | 107 +++---- common/configure.in | 8 + common/rfb/IrixDMIC_RawToJpeg.cxx | 423 ++++++++++++++++++++++++++++ common/rfb/IrixDMIC_RawToJpeg.h | 54 ++++ common/rfb/IrixDMJpegCompressor.cxx | 153 ++++++++++ common/rfb/IrixDMJpegCompressor.h | 62 ++++ common/rfb/JpegCompressor.cxx | 193 +++++++++++++ common/rfb/JpegCompressor.h | 82 ++++++ common/rfb/Makefile.in | 4 +- 9 files changed, 1032 insertions(+), 54 deletions(-) create mode 100644 common/rfb/IrixDMIC_RawToJpeg.cxx create mode 100644 common/rfb/IrixDMIC_RawToJpeg.h create mode 100644 common/rfb/IrixDMJpegCompressor.cxx create mode 100644 common/rfb/IrixDMJpegCompressor.h create mode 100644 common/rfb/JpegCompressor.cxx create mode 100644 common/rfb/JpegCompressor.h diff --git a/common/configure b/common/configure index 10a9c029..085e4fa7 100755 --- a/common/configure +++ b/common/configure @@ -896,61 +896,10 @@ else fi fi -for ac_declaration in \ - ''\ - '#include ' \ - '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 < -$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 <&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 <&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 diff --git a/common/configure.in b/common/configure.in index 576eac7a..f5c04c1c 100644 --- a/common/configure.in +++ b/common/configure.in @@ -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 index 00000000..6b763047 --- /dev/null +++ b/common/rfb/IrixDMIC_RawToJpeg.cxx @@ -0,0 +1,423 @@ +#include +#include +#include +#include + +#include + +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 index 00000000..33674bd3 --- /dev/null +++ b/common/rfb/IrixDMIC_RawToJpeg.h @@ -0,0 +1,54 @@ +#ifndef __IRIXDMIC_RAWTOJPEG_H__ +#define __IRIXDMIC_RAWTOJPEG_H__ + +#include +#include + +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 index 00000000..57e8aa80 --- /dev/null +++ b/common/rfb/IrixDMJpegCompressor.cxx @@ -0,0 +1,153 @@ +#include +#include +#include +#include + +#include + +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 index 00000000..500884af --- /dev/null +++ b/common/rfb/IrixDMJpegCompressor.h @@ -0,0 +1,62 @@ +#ifndef __IRIXDMJPEGCOMPRESSOR_H__ +#define __IRIXDMJPEGCOMPRESSOR_H__ + +#include + +#include +#include + +#include +#include + +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 index 00000000..0914f850 --- /dev/null +++ b/common/rfb/JpegCompressor.cxx @@ -0,0 +1,193 @@ +#include + +#include + +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 index 00000000..93fdc7bf --- /dev/null +++ b/common/rfb/JpegCompressor.h @@ -0,0 +1,82 @@ +#ifndef __JPEGCOMPRESSOR_H__ +#define __JPEGCOMPRESSOR_H__ + +#include +#include +extern "C" { +#include +} + +#include +#include + +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__ + diff --git a/common/rfb/Makefile.in b/common/rfb/Makefile.in index 7885753a..93fb7399 100644 --- a/common/rfb/Makefile.in +++ b/common/rfb/Makefile.in @@ -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) -- 2.39.5