new jpeg loader works

This commit is contained in:
John Cupitt 2011-11-24 21:53:40 +00:00
parent 2c641c6644
commit ab0f72db62
5 changed files with 585 additions and 93 deletions

13
TODO
View File

@ -1,6 +1,15 @@
- file.c has lazy open stuff in, needs work though - get image.c using the new system
how do we handle loaders that don't need to be lazy (eg. tiled tiff load) move format/* to deprecated
try jpeg save
try tiff load
- return missing just after read_xmp() in master?

View File

@ -36,6 +36,7 @@
#include <vips/vips.h> #include <vips/vips.h>
#include <vips/internal.h> #include <vips/internal.h>
#include <vips/debug.h>
/** /**
* SECTION: file * SECTION: file
@ -245,20 +246,24 @@ vips_file_print_class( VipsObjectClass *object_class, VipsBuf *buf )
vips_buf_appends( buf, ") " ); vips_buf_appends( buf, ") " );
} }
vips_buf_appendf( buf, "priority=%d ", class->priority ); vips_buf_appendf( buf, "priority=%d", class->priority );
} }
static void static void
vips_file_class_init( VipsFileClass *class ) vips_file_class_init( VipsFileClass *class )
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class; VipsObjectClass *object_class = (VipsObjectClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "file"; object_class->nickname = "file";
object_class->description = _( "load and save image files" ); object_class->description = _( "load and save image files" );
object_class->print_class = vips_file_print_class; object_class->print_class = vips_file_print_class;
VIPS_ARG_STRING( class, "filename", 12, VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ), _( "Filename" ),
_( "File filename" ), _( "File filename" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
@ -331,6 +336,16 @@ vips_file_map( const char *base, VipsSListMap2Fn fn, void *a, void *b )
G_DEFINE_ABSTRACT_TYPE( VipsFileLoad, vips_file_load, VIPS_TYPE_FILE ); G_DEFINE_ABSTRACT_TYPE( VipsFileLoad, vips_file_load, VIPS_TYPE_FILE );
static void
vips_file_load_dispose( GObject *gobject )
{
VipsFileLoad *load = VIPS_FILE_LOAD( gobject );
VIPS_UNREF( load->real );
G_OBJECT_CLASS( vips_file_load_parent_class )->dispose( gobject );
}
static void static void
vips_file_load_print_class( VipsObjectClass *object_class, VipsBuf *buf ) vips_file_load_print_class( VipsObjectClass *object_class, VipsBuf *buf )
{ {
@ -338,24 +353,19 @@ vips_file_load_print_class( VipsObjectClass *object_class, VipsBuf *buf )
VIPS_OBJECT_CLASS( vips_file_load_parent_class )-> VIPS_OBJECT_CLASS( vips_file_load_parent_class )->
print_class( object_class, buf ); print_class( object_class, buf );
vips_buf_appends( buf, ", " );
if( class->is_a ) if( class->is_a )
vips_buf_appends( buf, "is_a " ); vips_buf_appends( buf, ", is_a" );
/*
if( class->header )
vips_buf_appends( buf, "header " );
if( class->load )
vips_buf_appends( buf, "load " );
*/
if( class->get_flags ) if( class->get_flags )
vips_buf_appends( buf, "get_flags " ); vips_buf_appends( buf, ", get_flags" );
if( class->header )
vips_buf_appends( buf, ", header" );
if( class->load )
vips_buf_appends( buf, ", load" );
} }
static size_t static size_t
disc_threshold( void ) vips_get_disc_threshold( void )
{ {
static gboolean done = FALSE; static gboolean done = FALSE;
static size_t threshold; static size_t threshold;
@ -375,42 +385,13 @@ disc_threshold( void )
if( vips__disc_threshold ) if( vips__disc_threshold )
threshold = vips__parse_size( vips__disc_threshold ); threshold = vips__parse_size( vips__disc_threshold );
VIPS_DEBUG_MSG( "disc_threshold: %zd bytes\n", threshold ); VIPS_DEBUG_MSG( "vips_get_disc_threshold: "
"%zd bytes\n", threshold );
} }
return( threshold ); return( threshold );
} }
/* Make the real underlying image: either a direct disc file, or a temp file
* somewhere.
*/
static int
vips_file_load_new_real( VipsFileLoad *load )
{
/* We open via disc if:
* - 'disc' is set
* - disc_threshold() has not been set to zero
* - the format does not support lazy read
* - the uncompressed image will be larger than disc_threshold()
*/
load->real = NULL;
if( lazy->disc &&
disc_threshold() &&
!(vips_format_get_flags( lazy->format, lazy->filename ) &
VIPS_FORMAT_PARTIAL) &&
VIPS_IMAGE_SIZEOF_IMAGE( lazy->image ) > disc_threshold() )
if( !(real = vips_image_new_disc_temp( "%s.v" )) )
return( NULL );
/* Otherwise, fall back to a "p".
*/
if( !real &&
!(real = vips_image_new()) )
return( NULL );
return( real );
}
/* Our start function ... do the lazy open, if necessary, and return a region /* Our start function ... do the lazy open, if necessary, and return a region
* on the new image. * on the new image.
*/ */
@ -421,8 +402,32 @@ vips_file_load_start_cb( VipsImage *out, void *a, void *dummy )
VipsFileLoadClass *class = VIPS_FILE_LOAD_GET_CLASS( a ); VipsFileLoadClass *class = VIPS_FILE_LOAD_GET_CLASS( a );
if( !load->real ) { if( !load->real ) {
if( vips_file_load_new_real( load ) || const size_t disc_threshold = vips_get_disc_threshold();
class->load( load ) || const size_t image_size = VIPS_IMAGE_SIZEOF_IMAGE( load->out );
/* We open via disc if:
* - 'disc' is set
* - disc-threshold has not been set to zero
* - the format does not support lazy read
* - the uncompressed image will be larger than
* vips_get_disc_threshold()
*/
if( load->disc &&
disc_threshold &&
(load->flags & VIPS_FORMAT_PARTIAL) &&
image_size > disc_threshold )
if( !(load->real = vips_image_new_disc_temp( "%s.v" )) )
return( NULL );
/* Otherwise, fall back to a "p".
*/
if( !load->real &&
!(load->real = vips_image_new()) )
return( NULL );
/* Read the image in.
*/
if( class->load( load ) ||
vips_image_pio_input( load->real ) ) { vips_image_pio_input( load->real ) ) {
VIPS_UNREF( load->real ); VIPS_UNREF( load->real );
return( NULL ); return( NULL );
@ -463,10 +468,9 @@ vips_file_load_build( VipsObject *object )
g_object_set( object, "out", vips_image_new(), NULL ); g_object_set( object, "out", vips_image_new(), NULL );
if( class->get_flags ) if( class->get_flags &&
g_object_set( load, class->get_flags( load ) )
"flags", class->get_flags( load ), return( -1 );
NULL );
if( VIPS_OBJECT_CLASS( vips_file_load_parent_class )-> if( VIPS_OBJECT_CLASS( vips_file_load_parent_class )->
build( object ) ) build( object ) )
@ -481,14 +485,14 @@ vips_file_load_build( VipsObject *object )
return( -1 ); return( -1 );
vips_demand_hint( load->out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); vips_demand_hint( load->out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
/* Then 'start' creates the real image and 'gen' paints 'out' with /* Then 'start' creates the real image and 'gen' fetches pixels for
* pixels from the real image on demand. * 'out' from real on demand.
*/ */
if( vips_image_generate( load->out, if( vips_image_generate( load->out,
vips_file_load_start_cb, vips_file_load_start_cb,
vips_file_load_generate_cb, vips_file_load_generate_cb,
vips_stop_one, vips_stop_one,
lazy, NULL ) ) load, NULL ) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );
@ -497,13 +501,24 @@ vips_file_load_build( VipsObject *object )
static void static void
vips_file_load_class_init( VipsFileLoadClass *class ) vips_file_load_class_init( VipsFileLoadClass *class )
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class; VipsObjectClass *object_class = (VipsObjectClass *) class;
gobject_class->dispose = vips_file_load_dispose;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "fileload"; object_class->nickname = "fileload";
object_class->description = _( "file loaders" ); object_class->description = _( "file loaders" );
object_class->print_class = vips_file_load_print_class; object_class->print_class = vips_file_load_print_class;
object_class->build = vips_file_load_build; object_class->build = vips_file_load_build;
VIPS_ARG_IMAGE( class, "out", 2,
_( "Output" ),
_( "Output image" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsFileLoad, out ) );
VIPS_ARG_ENUM( class, "flags", 6, VIPS_ARG_ENUM( class, "flags", 6,
_( "Flags" ), _( "Flags" ),
_( "Flags for this file" ), _( "Flags for this file" ),
@ -511,24 +526,19 @@ vips_file_load_class_init( VipsFileLoadClass *class )
G_STRUCT_OFFSET( VipsFileLoad, flags ), G_STRUCT_OFFSET( VipsFileLoad, flags ),
VIPS_TYPE_FILE_FLAGS, VIPS_FILE_NONE ); VIPS_TYPE_FILE_FLAGS, VIPS_FILE_NONE );
VIPS_ARG_IMAGE( class, "out", 1, VIPS_ARG_BOOL( class, "disc", 7,
_( "Output" ), _( "Disc" ),
_( "Output image" ), _( "Open to disc" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsFileLoad, out ) );
VIPS_ARG_BOOL( class, "memory", 6,
_( "Memory" ),
_( "Open to memory" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsFileLoad, memory ), G_STRUCT_OFFSET( VipsFileLoad, disc ),
FALSE ); TRUE );
} }
static void static void
vips_file_load_init( VipsFileLoad *object ) vips_file_load_init( VipsFileLoad *load )
{ {
load->disc = TRUE;
} }
/* Can this file open this file? /* Can this file open this file?
@ -619,14 +629,18 @@ vips_file_save_build( VipsObject *object )
static void static void
vips_file_save_class_init( VipsFileSaveClass *class ) vips_file_save_class_init( VipsFileSaveClass *class )
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class; VipsObjectClass *object_class = (VipsObjectClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "filesave"; object_class->nickname = "filesave";
object_class->description = _( "file savers" ); object_class->description = _( "file savers" );
object_class->print_class = vips_file_save_print_class; object_class->print_class = vips_file_save_print_class;
object_class->build = vips_file_save_build; object_class->build = vips_file_save_build;
VIPS_ARG_IMAGE( class, "in", 1, VIPS_ARG_IMAGE( class, "in", 0,
_( "Input" ), _( "Input" ),
_( "Image to save" ), _( "Image to save" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
@ -740,3 +754,16 @@ vips_file_write( VipsImage *in, const char *filename, ... )
return( result ); return( result );
} }
/* Called from iofuncs to init all operations in this dir. Use a plugin system
* instead?
*/
void
vips_file_operation_init( void )
{
extern GType vips_file_load_jpeg_get_type( void );
#ifdef HAVE_JPEG
vips_file_load_jpeg_get_type();
#endif /*HAVE_JPEG*/
}

View File

@ -123,12 +123,49 @@ typedef struct _VipsFileLoadJpeg {
*/ */
gboolean fail; gboolean fail;
/* For some jpeg CMYK formats we have to invert pels on read.
*/
gboolean invert_pels;
} VipsFileLoadJpeg; } VipsFileLoadJpeg;
typedef VipsFileLoadJpegClass VipsFileLoadJpeg; typedef VipsFileLoadClass VipsFileLoadJpegClass;
G_DEFINE_TYPE( VipsFileLoadJpeg, vips_file_load_jpeg, VIPS_TYPE_FILE_LOAD ); G_DEFINE_TYPE( VipsFileLoadJpeg, vips_file_load_jpeg, VIPS_TYPE_FILE_LOAD );
static int
vips_file_load_jpeg_build( VipsObject *object )
{
VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) object;
if( jpeg->shrink != 1 &&
jpeg->shrink != 2 &&
jpeg->shrink != 4 &&
jpeg->shrink != 8 ) {
vips_error( "VipsFormatLoadJpeg",
_( "bad shrink factor %d" ), jpeg->shrink );
return( -1 );
}
if( VIPS_OBJECT_CLASS( vips_file_load_jpeg_parent_class )->
build( object ) )
return( -1 );
return( 0 );
}
static gboolean
vips_file_load_jpeg_is_a( const char *filename )
{
unsigned char buf[2];
if( vips__get_bytes( filename, buf, 2 ) )
if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 )
return( TRUE );
return( FALSE );
}
/* Define a new error handler for when we bomb out. /* Define a new error handler for when we bomb out.
*/ */
typedef struct { typedef struct {
@ -184,6 +221,54 @@ new_error_exit( j_common_ptr cinfo )
longjmp( eman->jmp, 1 ); longjmp( eman->jmp, 1 );
} }
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to
* do 255-pel.
*/
static int
vips_file_load_jpeg_read_header( VipsFileLoadJpeg *jpeg,
struct jpeg_decompress_struct *cinfo, VipsImage *out )
{
int type;
/* Read JPEG header. libjpeg will set out_color_space sanely for us
* for YUV YCCK etc.
*/
jpeg_read_header( cinfo, TRUE );
cinfo->scale_denom = jpeg->shrink;
cinfo->scale_num = 1;
jpeg_calc_output_dimensions( cinfo );
switch( cinfo->out_color_space ) {
case JCS_GRAYSCALE:
type = IM_TYPE_B_W;
break;
case JCS_CMYK:
type = IM_TYPE_CMYK;
/* Photoshop writes CMYK JPEG inverted :-( Maybe this is a
* way to spot photoshop CMYK JPGs.
*/
if( cinfo->saw_Adobe_marker )
jpeg->invert_pels = TRUE;
break;
case JCS_RGB:
default:
type = IM_TYPE_sRGB;
break;
}
/* Set VIPS header.
*/
vips_image_init_fields( out,
cinfo->output_width, cinfo->output_height,
cinfo->output_components,
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, type,
1.0, 1.0 );
return( 0 );
}
#ifdef HAVE_EXIF #ifdef HAVE_EXIF
#ifdef DEBUG_VERBOSE #ifdef DEBUG_VERBOSE
/* Print exif for debugging ... hacked from exif-0.6.9/actions.c /* Print exif for debugging ... hacked from exif-0.6.9/actions.c
@ -485,47 +570,416 @@ attach_thumbnail( IMAGE *im, ExifData *ed )
} }
#endif /*HAVE_EXIF*/ #endif /*HAVE_EXIF*/
static int static int
vips_file_load_jpeg_build( VipsObject *object ) read_exif( IMAGE *im, void *data, int data_length )
{ {
VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) object; char *data_copy;
if( jpeg->shrink != 1 && /* Only use the first one.
jpeg->shrink != 2 && */
jpeg->shrink != 4 && if( im_header_get_typeof( im, IM_META_EXIF_NAME ) ) {
jpeg->shrink != 8 ) { #ifdef DEBUG
vips_error( "VipsFormatLoadJpeg", printf( "read_exif: second EXIF block, ignoring\n" );
_( "bad shrink factor %d" ), jpeg->shrink ); #endif /*DEBUG*/
return( 0 );
}
#ifdef DEBUG
printf( "read_exif: attaching %d bytes of exif\n", data_length );
#endif /*DEBUG*/
/* Always attach a copy of the unparsed exif data.
*/
if( !(data_copy = im_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
if( im_meta_set_blob( im, IM_META_EXIF_NAME,
(im_callback_fn) im_free, data_copy, data_length ) ) {
im_free( data_copy );
return( -1 ); return( -1 );
} }
if( VIPS_OBJECT_CLASS( vips_file_load_jpeg_parent_class )-> #ifdef HAVE_EXIF
build( object ) ) {
ExifData *ed;
if( !(ed = exif_data_new_from_data( data, data_length )) )
return( -1 ); return( -1 );
if( ed->size > 0 ) {
VipsExif ve;
#ifdef DEBUG_VERBOSE
show_tags( ed );
show_values( ed );
#endif /*DEBUG_VERBOSE*/
/* Attach informational fields for what we find.
FIXME ... better to have this in the UI layer?
Or we could attach non-human-readable tags here (int,
double etc) and then move the human stuff to the UI
layer?
*/
ve.image = im;
ve.ed = ed;
exif_data_foreach_content( ed,
(ExifDataForeachContentFunc) attach_exif_content, &ve );
/* Look for resolution fields and use them to set the VIPS
* xres/yres fields.
*/
set_vips_resolution( im, ed );
attach_thumbnail( im, ed );
}
exif_data_free( ed );
}
#endif /*HAVE_EXIF*/
return( 0 ); return( 0 );
} }
static int
read_xmp( IMAGE *im, void *data, int data_length )
{
char *data_copy;
/* XMP sections start "http". Only use the first one.
*/
if( im_header_get_typeof( im, VIPS_META_XMP_NAME ) ) {
#ifdef DEBUG
printf( "read_xmp: second XMP block, ignoring\n" );
#endif /*DEBUG*/
return( 0 );
}
#ifdef DEBUG
printf( "read_xmp: attaching %d bytes of XMP\n", data_length );
#endif /*DEBUG*/
/* Always attach a copy of the unparsed exif data.
*/
if( !(data_copy = im_malloc( NULL, data_length )) )
return( -1 );
memcpy( data_copy, data, data_length );
if( im_meta_set_blob( im, VIPS_META_XMP_NAME,
(im_callback_fn) im_free, data_copy, data_length ) ) {
im_free( data_copy );
return( -1 );
}
return( 0 );
}
/* Number of app2 sections we can capture. Each one can be 64k, so 6400k should
* be enough for anyone (haha).
*/
#define MAX_APP2_SECTIONS (100)
static int
vips_file_load_jpeg_meta( VipsFileLoadJpeg *jpeg,
struct jpeg_decompress_struct *cinfo, VipsImage *out )
{
/* Capture app2 sections here for assembly.
*/
void *app2_data[MAX_APP2_SECTIONS] = { 0 };
int app2_data_length[MAX_APP2_SECTIONS] = { 0 };
int data_length;
jpeg_saved_marker_ptr p;
int i;
/* Interlaced jpegs need lots of memory to read, so our caller needs
* to know.
*/
vips_image_set_int( out, "jpeg-multiscan",
jpeg_has_multiple_scans( cinfo ) );
/* Look for EXIF and ICC profile.
*/
for( p = cinfo->marker_list; p; p = p->next ) {
#ifdef DEBUG
{
printf( "vips_file_load_jpeg_read_header: "
"seen %d bytes of APP%d\n",
p->data_length,
p->marker - JPEG_APP0 );
for( i = 0; i < 10; i++ )
printf( "\t%d) '%c' (%d)\n",
i, p->data[i], p->data[i] );
}
#endif /*DEBUG*/
switch( p->marker ) {
case JPEG_APP0 + 1:
/* Possible EXIF or XMP data.
*/
if( p->data_length > 4 &&
im_isprefix( "Exif", (char *) p->data ) &&
read_exif( out, p->data, p->data_length ) )
return( -1 );
if( p->data_length > 4 &&
im_isprefix( "http", (char *) p->data ) &&
read_xmp( out, p->data, p->data_length ) )
return( -1 );
break;
case JPEG_APP0 + 2:
/* ICC profile.
*/
if( p->data_length > 14 &&
im_isprefix( "ICC_PROFILE",
(char *) p->data ) ) {
/* cur_marker numbers from 1, according to
* spec.
*/
int cur_marker = p->data[12] - 1;
if( cur_marker >= 0 &&
cur_marker < MAX_APP2_SECTIONS ) {
app2_data[cur_marker] = p->data + 14;
app2_data_length[cur_marker] =
p->data_length - 14;
}
}
break;
default:
break;
}
}
/* Assemble ICC sections.
*/
data_length = 0;
for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ )
data_length += app2_data_length[i];
if( data_length ) {
unsigned char *data;
int x;
#ifdef DEBUG
printf( "vips_file_load_jpeg_read_header: "
"assembled %d byte ICC profile\n",
data_length );
#endif /*DEBUG*/
data = g_malloc( data_length );
x = 0;
for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) {
memcpy( data + x, app2_data[i], app2_data_length[i] );
x += app2_data_length[i];
}
vips_image_set_blob( out, VIPS_META_ICC_NAME,
(VipsCallbackFn) g_free, data, data_length );
}
return( 0 );
}
/* Read just the image header into ->out.
*/
static int
vips_file_load_jpeg_header( VipsFileLoad *load )
{
VipsFile *file = VIPS_FILE( load );
VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) load;
struct jpeg_decompress_struct cinfo;
ErrorManager eman;
FILE *fp;
int result;
/* 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.
*/
if( !(fp = vips__file_open_read( file->filename, NULL, FALSE )) )
return( -1 );
eman.fp = fp;
jpeg_stdio_src( &cinfo, fp );
/* 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 = vips_file_load_jpeg_read_header( jpeg, &cinfo, load->out );
/* Get extra metadata too.
*/
if( !result )
result = vips_file_load_jpeg_meta( jpeg, &cinfo, load->out );
/* Close and tidy.
*/
fclose( fp );
eman.fp = NULL;
jpeg_destroy_decompress( &cinfo );
return( result );
}
/* Read a cinfo to a VIPS image.
*/
static int
vips_file_load_jpeg_read_image( VipsFileLoadJpeg *jpeg,
struct jpeg_decompress_struct *cinfo, VipsImage *out )
{
int x, y, sz;
JSAMPROW row_pointer[1];
/* Check VIPS.
*/
if( vips_image_wio_output( out ) )
return( -1 );
/* Get size of output line and make a buffer.
*/
sz = cinfo->output_width * cinfo->output_components;
row_pointer[0] = (JSAMPLE *) (*cinfo->mem->alloc_large)
( (j_common_ptr) cinfo, JPOOL_IMAGE, sz );
/* Start up decompressor.
*/
jpeg_start_decompress( cinfo );
/* Process image.
*/
for( y = 0; y < out->Ysize; y++ ) {
/* We set an error handler that longjmps() out, so I don't
* think this can fail.
*/
jpeg_read_scanlines( cinfo, &row_pointer[0], 1 );
if( jpeg->invert_pels ) {
for( x = 0; x < sz; x++ )
row_pointer[0][x] = 255 - row_pointer[0][x];
}
if( vips_image_write_line( out, y, row_pointer[0] ) )
return( -1 );
}
/* Stop decompressor.
*/
jpeg_finish_decompress( cinfo );
return( 0 );
}
static int
vips_file_load_jpeg_load( VipsFileLoad *load )
{
VipsFile *file = VIPS_FILE( load );
VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) load;
struct jpeg_decompress_struct cinfo;
ErrorManager eman;
FILE *fp;
int result;
/* 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.
*/
if( !(fp = vips__file_open_read( file->filename, NULL, FALSE )) )
return( -1 );
eman.fp = fp;
jpeg_stdio_src( &cinfo, fp );
/* Convert!
*/
result = vips_file_load_jpeg_read_header( jpeg, &cinfo, load->real );
if( !result )
result = vips_file_load_jpeg_read_image( jpeg,
&cinfo, load->real );
/* Close and tidy.
*/
fclose( fp );
eman.fp = NULL;
jpeg_destroy_decompress( &cinfo );
if( eman.pub.num_warnings != 0 ) {
if( jpeg->fail ) {
vips_error( "VipsFileLoadJpeg",
"%s", vips_error_buffer() );
result = -1;
}
else {
vips_warn( "VipsFileLoadJpeg",
_( "read gave %ld warnings" ),
eman.pub.num_warnings );
vips_warn( "VipsFileLoadJpeg",
"%s", vips_error_buffer() );
}
}
return( result );
}
static void static void
vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class ) vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class )
{ {
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class; VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsFileLoadClass *load_class = (VipsFileLoadClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "jpegload"; object_class->nickname = "jpegload";
object_class->description = _( "load jpeg from file" ); object_class->description = _( "load jpeg from file" );
object_class->build = vips_file_load_jpeg_build; object_class->build = vips_file_load_jpeg_build;
VIPS_ARG_INT( class, "shrink", 5, load_class->is_a = vips_file_load_jpeg_is_a;
load_class->header = vips_file_load_jpeg_header;
load_class->load = vips_file_load_jpeg_load;
VIPS_ARG_INT( class, "shrink", 10,
_( "Shrink" ), _( "Shrink" ),
_( "Shrink factor on load" ), _( "Shrink factor on load" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsFileLoadJpeg, shrink ), G_STRUCT_OFFSET( VipsFileLoadJpeg, shrink ),
1, 16, 1 ); 1, 16, 1 );
VIPS_ARG_BOOL( class, "fail", 6, VIPS_ARG_BOOL( class, "fail", 11,
_( "Fail" ), _( "Fail" ),
_( "Fail on first warning" ), _( "Fail on first warning" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
@ -535,10 +989,8 @@ vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class )
} }
static void static void
vips_file_load_jpeg_init( VipsFileLoadJpeg *object ) vips_file_load_jpeg_init( VipsFileLoadJpeg *jpeg )
{ {
jpeg->shrink = 1; jpeg->shrink = 1;
} }

View File

@ -73,7 +73,7 @@ typedef struct _VipsFileClass {
int priority; int priority;
/* Null-terminated list of recommended suffixes, eg. ".tif", ".tiff". /* Null-terminated list of recommended suffixes, eg. ".tif", ".tiff".
* This is used by both load and save, so it's in the base class. * This can be used by both load and save, so it's in the base class.
*/ */
const char **suffs; const char **suffs;
@ -113,9 +113,9 @@ typedef struct _VipsFileLoad {
VipsFile parent_object; VipsFile parent_object;
/*< public >*/ /*< public >*/
/* Open to memory (default is to open via disc). /* Open to disc (default is to open to memory).
*/ */
gboolean memory; gboolean disc;
/* Flags read from the file. /* Flags read from the file.
*/ */
@ -125,7 +125,8 @@ typedef struct _VipsFileLoad {
*/ */
VipsImage *out; VipsImage *out;
/* The behind-the-scenes real image we decompress to. /* The behind-the-scenes real image we decompress to. This can be a
* disc file or a memory buffer.
*/ */
VipsImage *real; VipsImage *real;
@ -140,15 +141,15 @@ typedef struct _VipsFileLoadClass {
*/ */
gboolean (*is_a)( const char * ); gboolean (*is_a)( const char * );
/* Get the flags for this file in this file. /* Get the flags for this file.
*/ */
VipsFileFlags (*get_flags)( VipsFileLoad * ); int (*get_flags)( VipsFileLoad * );
/* Read the header into @out. /* Read the header into @out.
*/ */
int (*header)( VipsFileLoad * ); int (*header)( VipsFileLoad * );
/* Read the whole image into @out. /* Read the whole image into real. It gets copied to out later.
*/ */
int (*load)( VipsFileLoad * ); int (*load)( VipsFileLoad * );
@ -199,6 +200,8 @@ const char *vips_file_find_save( const char *filename );
int vips_file_read( const char *filename, VipsImage **out, ... ); int vips_file_read( const char *filename, VipsImage **out, ... );
int vips_file_write( VipsImage *in, const char *filename, ... ); int vips_file_write( VipsImage *in, const char *filename, ... );
void vips_file_operation_init( void );
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /*__cplusplus*/ #endif /*__cplusplus*/

View File

@ -231,6 +231,7 @@ vips_init( const char *argv0 )
*/ */
vips_arithmetic_operation_init(); vips_arithmetic_operation_init();
vips_conversion_operation_init(); vips_conversion_operation_init();
vips_file_operation_init();
/* Load up any plugins in the vips libdir. We don't error on failure, /* Load up any plugins in the vips libdir. We don't error on failure,
* it's too annoying to have VIPS refuse to start because of a broken * it's too annoying to have VIPS refuse to start because of a broken