Custom Memory Source Manager for libjpeg

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.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.