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/internal.h>
#include <vips/debug.h>
/**
* SECTION: file
@ -245,20 +246,24 @@ vips_file_print_class( VipsObjectClass *object_class, VipsBuf *buf )
vips_buf_appends( buf, ") " );
}
vips_buf_appendf( buf, "priority=%d ", class->priority );
vips_buf_appendf( buf, "priority=%d", class->priority );
}
static void
vips_file_class_init( VipsFileClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( 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->description = _( "load and save image files" );
object_class->print_class = vips_file_print_class;
VIPS_ARG_STRING( class, "filename", 12,
VIPS_ARG_STRING( class, "filename", 1,
_( "Filename" ),
_( "File filename" ),
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 );
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
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 )->
print_class( object_class, buf );
vips_buf_appends( buf, ", " );
if( class->is_a )
vips_buf_appends( buf, "is_a " );
/*
if( class->header )
vips_buf_appends( buf, "header " );
if( class->load )
vips_buf_appends( buf, "load " );
*/
vips_buf_appends( buf, ", is_a" );
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
disc_threshold( void )
vips_get_disc_threshold( void )
{
static gboolean done = FALSE;
static size_t threshold;
@ -375,42 +385,13 @@ disc_threshold( void )
if( 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 );
}
/* 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
* 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 );
if( !load->real ) {
if( vips_file_load_new_real( load ) ||
class->load( load ) ||
const size_t disc_threshold = vips_get_disc_threshold();
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_UNREF( load->real );
return( NULL );
@ -463,10 +468,9 @@ vips_file_load_build( VipsObject *object )
g_object_set( object, "out", vips_image_new(), NULL );
if( class->get_flags )
g_object_set( load,
"flags", class->get_flags( load ),
NULL );
if( class->get_flags &&
class->get_flags( load ) )
return( -1 );
if( VIPS_OBJECT_CLASS( vips_file_load_parent_class )->
build( object ) )
@ -481,14 +485,14 @@ vips_file_load_build( VipsObject *object )
return( -1 );
vips_demand_hint( load->out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
/* Then 'start' creates the real image and 'gen' paints 'out' with
* pixels from the real image on demand.
/* Then 'start' creates the real image and 'gen' fetches pixels for
* 'out' from real on demand.
*/
if( vips_image_generate( load->out,
vips_file_load_start_cb,
vips_file_load_generate_cb,
vips_stop_one,
lazy, NULL ) )
load, NULL ) )
return( -1 );
return( 0 );
@ -497,13 +501,24 @@ vips_file_load_build( VipsObject *object )
static void
vips_file_load_class_init( VipsFileLoadClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( 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->description = _( "file loaders" );
object_class->print_class = vips_file_load_print_class;
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,
_( "Flags" ),
_( "Flags for this file" ),
@ -511,24 +526,19 @@ vips_file_load_class_init( VipsFileLoadClass *class )
G_STRUCT_OFFSET( VipsFileLoad, flags ),
VIPS_TYPE_FILE_FLAGS, VIPS_FILE_NONE );
VIPS_ARG_IMAGE( class, "out", 1,
_( "Output" ),
_( "Output image" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsFileLoad, out ) );
VIPS_ARG_BOOL( class, "memory", 6,
_( "Memory" ),
_( "Open to memory" ),
VIPS_ARG_BOOL( class, "disc", 7,
_( "Disc" ),
_( "Open to disc" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsFileLoad, memory ),
FALSE );
G_STRUCT_OFFSET( VipsFileLoad, disc ),
TRUE );
}
static void
vips_file_load_init( VipsFileLoad *object )
vips_file_load_init( VipsFileLoad *load )
{
load->disc = TRUE;
}
/* Can this file open this file?
@ -619,14 +629,18 @@ vips_file_save_build( VipsObject *object )
static void
vips_file_save_class_init( VipsFileSaveClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( 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->description = _( "file savers" );
object_class->print_class = vips_file_save_print_class;
object_class->build = vips_file_save_build;
VIPS_ARG_IMAGE( class, "in", 1,
VIPS_ARG_IMAGE( class, "in", 0,
_( "Input" ),
_( "Image to save" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
@ -740,3 +754,16 @@ vips_file_write( VipsImage *in, const char *filename, ... )
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;
/* For some jpeg CMYK formats we have to invert pels on read.
*/
gboolean invert_pels;
} VipsFileLoadJpeg;
typedef VipsFileLoadJpegClass VipsFileLoadJpeg;
typedef VipsFileLoadClass VipsFileLoadJpegClass;
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.
*/
typedef struct {
@ -184,6 +221,54 @@ new_error_exit( j_common_ptr cinfo )
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 DEBUG_VERBOSE
/* 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*/
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 &&
jpeg->shrink != 2 &&
jpeg->shrink != 4 &&
jpeg->shrink != 8 ) {
vips_error( "VipsFormatLoadJpeg",
_( "bad shrink factor %d" ), jpeg->shrink );
/* Only use the first one.
*/
if( im_header_get_typeof( im, IM_META_EXIF_NAME ) ) {
#ifdef DEBUG
printf( "read_exif: second EXIF block, ignoring\n" );
#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 );
}
if( VIPS_OBJECT_CLASS( vips_file_load_jpeg_parent_class )->
build( object ) )
#ifdef HAVE_EXIF
{
ExifData *ed;
if( !(ed = exif_data_new_from_data( data, data_length )) )
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 );
}
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
vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( 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->description = _( "load jpeg from file" );
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 factor on load" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsFileLoadJpeg, shrink ),
1, 16, 1 );
VIPS_ARG_BOOL( class, "fail", 6,
VIPS_ARG_BOOL( class, "fail", 11,
_( "Fail" ),
_( "Fail on first warning" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
@ -535,10 +989,8 @@ vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class )
}
static void
vips_file_load_jpeg_init( VipsFileLoadJpeg *object )
vips_file_load_jpeg_init( VipsFileLoadJpeg *jpeg )
{
jpeg->shrink = 1;
}

View File

@ -73,7 +73,7 @@ typedef struct _VipsFileClass {
int priority;
/* 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;
@ -113,9 +113,9 @@ typedef struct _VipsFileLoad {
VipsFile parent_object;
/*< 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.
*/
@ -125,7 +125,8 @@ typedef struct _VipsFileLoad {
*/
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;
@ -140,15 +141,15 @@ typedef struct _VipsFileLoadClass {
*/
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.
*/
int (*header)( VipsFileLoad * );
/* Read the whole image into @out.
/* Read the whole image into real. It gets copied to out later.
*/
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_write( VipsImage *in, const char *filename, ... );
void vips_file_operation_init( void );
#ifdef __cplusplus
}
#endif /*__cplusplus*/

View File

@ -231,6 +231,7 @@ vips_init( const char *argv0 )
*/
vips_arithmetic_operation_init();
vips_conversion_operation_init();
vips_file_operation_init();
/* 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