blob: 0de87b4cabc0be1ecbc4853a03e4b914401f4a84 [file] [log] [blame]
#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;
} else 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);
}