add im_bufjpeg2vips()

add a thing to open a jpeg from a memory buffer, handy for processing
thumbnails from exif data
This commit is contained in:
John Cupitt 2011-04-20 15:23:34 +01:00
parent 1a42e75f80
commit c4f5c71507
4 changed files with 247 additions and 2 deletions

View File

@ -49,6 +49,7 @@
- im_tiff2vips() int/uint mixup for rows_per_strip, thanks Bubba
- removed the links feature, won't work with vips8
- got rid of the tools/ subdirs
- added im_bufjpeg2vips()
30/11/10 started 7.24.0
- bump for new stable

2
TODO
View File

@ -1,4 +1,4 @@
- add open jpeg from a mem buffer
- need vips_image_invalidate_area()
- leak check, again

View File

@ -33,6 +33,8 @@
* 21/2/10
* - only accept the first APP1 block which starts "Exif..." as exif
* data, some jpegs seem to have several APP1s, argh
* 20/4/2011
* - added im_bufjpeg2vips()
*/
/*
@ -698,7 +700,7 @@ jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
fail_on_warn = TRUE;
}
/* Make jpeg compression object.
/* Make jpeg dcompression object.
*/
cinfo.err = jpeg_std_error( &eman.pub );
eman.pub.error_exit = new_error_exit;
@ -752,6 +754,226 @@ jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
return( result );
}
/* Just like the above, but we read from a memory buffer.
*/
typedef struct {
/* Public jpeg fields.
*/
struct jpeg_source_mgr pub;
/* Private stuff during read.
*/
gboolean start_of_file; /* have we gotten any data yet? */
JOCTET *buf;
size_t len;
} InputBuffer;
/*
* Initialize source --- called by jpeg_read_header
* before any data is actually read.
*/
static void
init_source (j_decompress_ptr cinfo)
{
InputBuffer *src = (InputBuffer *) cinfo->src;
/* We reset the empty-input-file flag for each image,
* but we don't clear the input buffer.
* This is correct behavior for reading a series of images from one source.
*/
src->start_of_file = TRUE;
}
/*
* Fill the input buffer --- called whenever buffer is emptied.
*
* In typical applications, this should read fresh data into the buffer
* (ignoring the current state of next_input_byte & bytes_in_buffer),
* reset the pointer & count to the start of the buffer, and return TRUE
* indicating that the buffer has been reloaded. It is not necessary to
* fill the buffer entirely, only to obtain at least one more byte.
*
* There is no such thing as an EOF return. If the end of the file has been
* reached, the routine has a choice of ERREXIT() or inserting fake data into
* the buffer. In most cases, generating a warning message and inserting a
* fake EOI marker is the best course of action --- this will allow the
* decompressor to output however much of the image is there. However,
* the resulting error message is misleading if the real problem is an empty
* input file, so we handle that case specially.
*
* In applications that need to be able to suspend compression due to input
* not being available yet, a FALSE return indicates that no more data can be
* obtained right now, but more may be forthcoming later. In this situation,
* the decompressor will return to its caller (with an indication of the
* number of scanlines it has read, if any). The application should resume
* decompression after it has loaded more data into the input buffer. Note
* that there are substantial restrictions on the use of suspension --- see
* the documentation.
*
* When suspending, the decompressor will back up to a convenient restart point
* (typically the start of the current MCU). next_input_byte & bytes_in_buffer
* indicate where the restart point will be if the current call returns FALSE.
* Data beyond this point must be rescanned after resumption, so move it to
* the front of the buffer rather than discarding it.
*/
static boolean
fill_input_buffer (j_decompress_ptr cinfo)
{
InputBuffer *src = (InputBuffer *) cinfo->src;
size_t nbytes;
if (src->start_of_file) {
nbytes = src->len;
}
else {
WARNMS(cinfo, JWRN_JPEG_EOF);
/* Insert a fake EOI marker */
src->buf[0] = (JOCTET) 0xFF;
src->buf[1] = (JOCTET) JPEG_EOI;
nbytes = 2;
}
src->pub.next_input_byte = src->buf;
src->pub.bytes_in_buffer = nbytes;
src->start_of_file = FALSE;
return TRUE;
}
/*
* Skip data --- used to skip over a potentially large amount of
* uninteresting data (such as an APPn marker).
*
* Writers of suspendable-input applications must note that skip_input_data
* is not granted the right to give a suspension return. If the skip extends
* beyond the data currently in the buffer, the buffer can be marked empty so
* that the next read will cause a fill_input_buffer call that can suspend.
* Arranging for additional bytes to be discarded before reloading the input
* buffer is the application writer's problem.
*/
static void
skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
InputBuffer *src = (InputBuffer *) cinfo->src;
/* Just skip fwd.
*/
if (num_bytes > 0) {
src->pub.next_input_byte += (size_t) num_bytes;
src->pub.bytes_in_buffer -= (size_t) num_bytes;
}
}
/*
* An additional method that can be provided by data source modules is the
* resync_to_restart method for error recovery in the presence of RST markers.
* For the moment, this source module just uses the default resync method
* provided by the JPEG library. That method assumes that no backtracking
* is possible.
*/
/*
* Terminate source --- called by jpeg_finish_decompress
* after all data has been read. Often a no-op.
*
* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
* application must deal with any cleanup that should happen even
* for error exit.
*/
static void
term_source (j_decompress_ptr cinfo)
{
/* no work necessary here */
}
/*
* Prepare for input from a memory buffer. The caller needs to free the
* buffer after decompress is done, we don't take ownership.
*/
static void
buf_source (j_decompress_ptr cinfo, void *buf, size_t len)
{
InputBuffer *src;
/* The source object and input buffer are made permanent so that a series
* of JPEG images can be read from the same file by calling jpeg_stdio_src
* only before the first one. (If we discarded the buffer at the end of
* one image, we'd likely lose the start of the next one.)
* This makes it unsafe to use this manager and a different source
* manager serially with the same JPEG object. Caveat programmer.
*/
if (cinfo->src == NULL) { /* first time for this JPEG object? */
cinfo->src = (struct jpeg_source_mgr *)
(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
sizeof(InputBuffer));
src = (InputBuffer *) cinfo->src;
src->buf = buf;
src->len = len;
}
src = (InputBuffer *) cinfo->src;
src->pub.init_source = init_source;
src->pub.fill_input_buffer = fill_input_buffer;
src->pub.skip_input_data = skip_input_data;
src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
src->pub.term_source = term_source;
src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
src->pub.next_input_byte = NULL; /* until buffer loaded */
}
/* Read a JPEG memory buffer into a VIPS image.
*/
static int
bufjpeg2vips( void *buf, size_t len, IMAGE *out, gboolean header_only )
{
char *p, *q;
struct jpeg_decompress_struct cinfo;
ErrorManager eman;
int result;
gboolean invert_pels;
/* Make jpeg dcompression object.
*/
cinfo.err = jpeg_std_error( &eman.pub );
eman.pub.error_exit = new_error_exit;
eman.pub.output_message = new_output_message;
eman.fp = NULL;
if( setjmp( eman.jmp ) ) {
/* Here for longjmp() from new_error_exit().
*/
jpeg_destroy_decompress( &cinfo );
return( -1 );
}
jpeg_create_decompress( &cinfo );
/* Make input.
*/
buf_source( &cinfo, buf, len );
/* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile).
*/
jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff );
jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff );
/* Convert!
*/
result = read_jpeg_header( &cinfo, out, &invert_pels, 1 );
if( !header_only && !result )
result = read_jpeg_image( &cinfo, out, invert_pels );
/* Close and tidy.
*/
jpeg_destroy_decompress( &cinfo );
return( result );
}
/**
* im_jpeg2vips:
* @filename: file to load
@ -818,6 +1040,27 @@ im_jpeg2vips( const char *filename, IMAGE *out )
return( jpeg2vips( filename, out, FALSE ) );
}
/**
* im_bufjpeg2vips:
* @buf: memory area to load
* @len: size of memory area
* @out: image to write
*
* Read a JPEG-formatted memory block into a VIPS image. It can read most
* 8-bit JPEG images, including CMYK and YCbCr.
*
* This function is handy for processing JPEG image thumbnails.
*
* See also: #VipsFormat, im_jpeg2vips().
*
* Returns: 0 on success, -1 on error.
*/
int
im_bufjpeg2vips( void *buf, size_t len, IMAGE *out )
{
return( bufjpeg2vips( buf, len, out, FALSE ) );
}
static int
isjpeg( const char *filename )
{

View File

@ -123,6 +123,7 @@ int vips_format_write( VipsImage *in, const char *filename );
/* Low-level read/write operations.
*/
int im_jpeg2vips( const char *filename, VipsImage *out );
int im_bufjpeg2vips( void *buf, size_t len, VipsImage *out );
int im_vips2jpeg( VipsImage *in, const char *filename );
int im_vips2mimejpeg( VipsImage *in, int qfac );
int im_vips2bufjpeg( VipsImage *in, VipsImage *out, int qfac, char **obuf, int *olen );