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
diff --git a/common/rfb/JpegCompressor.cxx b/common/rfb/JpegCompressor.cxx
new file mode 100644
index 0000000..0914f85
--- /dev/null
+++ b/common/rfb/JpegCompressor.cxx
@@ -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);
+}
+