Similar to creating an in memory source manager for libjpeg decompression, we can also create a destination manager which will store the compressed data in memory.
FOr this purpose, the destination manager contains a byte vector:
typedef struct _jpeg_destination_mem_mgr
{
jpeg_destination_mgr mgr;
std::vector<unsigned char> data;
} jpeg_destination_mem_mgr; |
Initialization takes place in the callback mem_init_destination():
static void mem_init_destination( j_compress_ptr cinfo )
{
jpeg_destination_mem_mgr* dst = (jpeg_destination_mem_mgr*)cinfo->dest;
dst->data.resize( JPEG_MEM_DST_MGR_BUFFER_SIZE );
cinfo->dest->next_output_byte = dst->data.data();
cinfo->dest->free_in_buffer = dst->data.size();
} |
When the compression has finished, we need to resize the buffer to the actual size:
static void mem_term_destination( j_compress_ptr cinfo )
{
jpeg_destination_mem_mgr* dst = (jpeg_destination_mem_mgr*)cinfo->dest;
dst->data.resize( dst->data.size() - cinfo->dest->free_in_buffer );
} |
When the buffer size is not large enough, the library requests more data in the following callback:
static boolean mem_empty_output_buffer( j_compress_ptr cinfo )
{
jpeg_destination_mem_mgr* dst = (jpeg_destination_mem_mgr*)cinfo->dest;
size_t oldsize = dst->data.size();
dst->data.resize( oldsize + JPEG_MEM_DST_MGR_BUFFER_SIZE );
cinfo->dest->next_output_byte = dst->data.data() + oldsize;
cinfo->dest->free_in_buffer = JPEG_MEM_DST_MGR_BUFFER_SIZE;
return true;
} |
All callbacks are configured in jpeg_mem_dest():
static void jpeg_mem_dest( j_compress_ptr cinfo, jpeg_destination_mem_mgr * dst )
{
cinfo->dest = (jpeg_destination_mgr*)dst;
cinfo->dest->init_destination = mem_init_destination;
cinfo->dest->term_destination = mem_term_destination;
cinfo->dest->empty_output_buffer = mem_empty_output_buffer;
} |
Usage:
jpeg_destination_mem_mgr dst_mem;
jpeg_compress_struct_wrapper cinfo;
j_compress_ptr pcinfo = cinfo;
jpeg_mem_dest( cinfo, &dst_mem); |
After encoding has finished, data can be retrieved from the data member of the destination manager.
unsigned char * pjpeg_data = dst_mem.data.data();
size_t data_size = dst_mem.data.size(); |
Here is a link to the entire code: jpeg_mem_dst.h
libjpeg-turbo is an optimized plug-in replacement for libjpeg, which delivers 2-4 times the performance of the original library. However currently the current libjpeg-turbo release version have a memory source manager included when running in libjpeg v6b compatibility mode.
The following implementation fills the gap:
The callback mem_fill_input_buffer() is invoked when the library expects more data than supplied in the buffer. Since we already hold all data in the memory buffer, this can only be due to the image being prematurely terminated. There are two different ways of handling such a circumstance, either allow the library to finish processing and display the partial data, or abort processing by raising an error. The first option is activated by defining PROCESS_TRUNCATED_IMAGES.
static boolean mem_fill_input_buffer( j_decompress_ptr cinfo )
{
#ifdef PROCESS_TRUNCATED_IMAGES
jpeg_source_mgr* src = cinfo->src;
static const JOCTET EOI_BUFFER[ 2 ] = { (JOCTET)0xFF, (JOCTET)JPEG_EOI };
src->next_input_byte = EOI_BUFFER;
src->bytes_in_buffer = sizeof( EOI_BUFFER );
#else
ERREXIT( cinfo, JERR_INPUT_EOF );
#endif
return TRUE;
} |
mem_skip_input_data() is called when the library wants to skip over a certain part of the data. If the data to be skipped is less than the remaining bytes in the buffer, we simple anjust the buffer pointer. If there is not enough input data, we either raise an exception or set the input data length to zero, which will result in a call to mem_fill_input_buffer(), where an EOI marker is returned.
static void mem_skip_input_data( j_decompress_ptr cinfo, long num_bytes )
{
jpeg_source_mgr* src = (jpeg_source_mgr*)cinfo->src;
if ( 1 > num_bytes )
return;
if ( num_bytes < src->bytes_in_buffer )
{
src->next_input_byte += (size_t)num_bytes;
src->bytes_in_buffer -= (size_t)num_bytes;
}
else
{
#ifdef PROCESS_TRUNCATED_IMAGES
src->bytes_in_buffer = 0;
#else
ERREXIT( cinfo, JERR_INPUT_EOF );
#endif
}
} |
In our case, there is no need to perform initialization or termination, since the buffer which is used is managed by the caller.
static void mem_init_source( j_decompress_ptr cinfo )
{
}
static void mem_term_source( j_decompress_ptr cinfo )
{
} |
The following function performs initialization of the memory buffer. It is important to note that the bytes supplied are not being copied, and therefore the buffer must not be freed before the image processing has finished.
static void jpeg_mem_src( j_decompress_ptr cinfo, jpeg_source_mgr* src, void* buffer, long nbytes )
{
src->init_source = mem_init_source;
src->fill_input_buffer = mem_fill_input_buffer;
src->skip_input_data = mem_skip_input_data;
src->resync_to_restart = jpeg_resync_to_restart;
src->term_source = mem_term_source;
src->bytes_in_buffer = nbytes;
src->next_input_byte = (JOCTET*)buffer;
cinfo->src = src;
} |
With the above manager definition, we can easily initialize and work with the memory source manager.
jpeg_decompress_struct cinfo;
jpeg_source_mgr src_mem;
jpeg_create_decompress( &cinfo );
jpeg_mem_src( &cinfo, &src_mem, (PVOID)content, (long)dwSize );
jpeg_read_header( &cinfo, TRUE ); |
Here is the complete header file: jpeg_mem_src.h.
To facilitate resource cleanup within libjpeg encoding/decoding functions, the following classes implement RAII wrappers for the jpeg_decompress_struct and jpeg_compress_struct structures.
RAII is a C++ concept which helps to ensure that all resources are correctly deinitialized/released even in case of error/exceptions.
Usage example:
jpeg_decompress_struct_wrapper cinfo;
jpeg_decompress_struct* pcinfo = cinfo;
jpeg_win32_src_mgr src_data;
jpeg_win32_src( cinfo, &src_data, szFilename );
jpeg_read_header( cinfo, TRUE );
pcinfo->dct_method = JDCT_IFAST;
/* ... */ |
No calls to jpeg_destroy_decompress() are necessary, stack unwinding will ensure that the jpeg_decompress_struct will be automatically destroyed, even if the encoding/decoding routine has several exit/return paths.
Since the wrapper class implements an operator for jpeg_decompress_struct*, the instance can be passed as a parameter instead of “&cinfo”.
This is the wrapper for jpeg_decompress_struct:
class jpeg_decompress_struct_wrapper
{
public:
jpeg_decompress_struct_wrapper()
{
jpeg_create_decompress( &this->cinfo );
}
~jpeg_decompress_struct_wrapper()
{
jpeg_destroy_decompress( &this->cinfo );
}
operator jpeg_decompress_struct*()
{
return &this->cinfo;
}
private:
jpeg_decompress_struct cinfo;
}; |
Wrapper for jpeg_compress_struct:
class jpeg_compress_struct_wrapper
{
public:
jpeg_compress_struct_wrapper()
{
jpeg_create_compress( &this->cinfo );
}
~jpeg_compress_struct_wrapper()
{
jpeg_destroy_compress( &this->cinfo );
}
operator jpeg_compress_struct*()
{
return &this->cinfo;
}
private:
jpeg_compress_struct cinfo;
}; |