diff --git a/ChangeLog b/ChangeLog index 5af03869..e44fe7dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -12,6 +12,11 @@ - better thread safety for vips8 operation dispatch - better thread safety for upstream / downstream image linking - added "rs" open mode, removed "rd" +- added vips_operation_get_flags() ... system for attaching sets of flags to + operations +- added VIPS_OPERATION_SEQUENTIAL flag +- vips8 command-line interface uses this to turn sequential mode on + automatically when possible 18/6/12 started 7.28.9 - slightly more memory debugging output diff --git a/TODO b/TODO index a1a19b70..06c486a9 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,3 @@ - - test "rs" mode can't see the code for "rd" mode? diff --git a/libvips/arithmetic/arithmetic.c b/libvips/arithmetic/arithmetic.c index 3fa79cd4..b746dccb 100644 --- a/libvips/arithmetic/arithmetic.c +++ b/libvips/arithmetic/arithmetic.c @@ -370,6 +370,7 @@ vips_arithmetic_class_init( VipsArithmeticClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -378,6 +379,8 @@ vips_arithmetic_class_init( VipsArithmeticClass *class ) vobject_class->description = _( "arithmetic operations" ); vobject_class->build = vips_arithmetic_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "out", 100, _( "Output" ), _( "Output image" ), diff --git a/libvips/conversion/bandary.c b/libvips/conversion/bandary.c index 673825ea..45bb4647 100644 --- a/libvips/conversion/bandary.c +++ b/libvips/conversion/bandary.c @@ -163,6 +163,7 @@ vips_bandary_class_init( VipsBandaryClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_bandary_class_init\n" ); @@ -173,6 +174,7 @@ vips_bandary_class_init( VipsBandaryClass *class ) vobject_class->description = _( "operations on image bands" ); vobject_class->build = vips_bandary_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; } static void diff --git a/libvips/conversion/cast.c b/libvips/conversion/cast.c index a18c2357..ee420694 100644 --- a/libvips/conversion/cast.c +++ b/libvips/conversion/cast.c @@ -462,6 +462,7 @@ vips_cast_class_init( VipsCastClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_cast_class_init\n" ); @@ -472,6 +473,8 @@ vips_cast_class_init( VipsCastClass *class ) vobject_class->description = _( "cast an image" ); vobject_class->build = vips_cast_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "in", 1, _( "Input" ), _( "Input image" ), diff --git a/libvips/conversion/copy.c b/libvips/conversion/copy.c index 7570ef70..73927fd0 100644 --- a/libvips/conversion/copy.c +++ b/libvips/conversion/copy.c @@ -304,6 +304,7 @@ vips_copy_class_init( VipsCopyClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_copy_class_init\n" ); @@ -314,6 +315,8 @@ vips_copy_class_init( VipsCopyClass *class ) vobject_class->description = _( "copy an image" ); vobject_class->build = vips_copy_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "in", 1, _( "Input" ), _( "Input image" ), diff --git a/libvips/conversion/embed.c b/libvips/conversion/embed.c index 1cf54d50..e90203a9 100644 --- a/libvips/conversion/embed.c +++ b/libvips/conversion/embed.c @@ -507,6 +507,7 @@ vips_embed_class_init( VipsEmbedClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_embed_class_init\n" ); @@ -517,6 +518,8 @@ vips_embed_class_init( VipsEmbedClass *class ) vobject_class->description = _( "embed an image in a larger image" ); vobject_class->build = vips_embed_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "in", -1, _( "Input" ), _( "Input image" ), diff --git a/libvips/conversion/extract.c b/libvips/conversion/extract.c index 495c1005..d1b334e0 100644 --- a/libvips/conversion/extract.c +++ b/libvips/conversion/extract.c @@ -180,6 +180,7 @@ vips_extract_area_class_init( VipsExtractAreaClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_extract_area_class_init\n" ); @@ -190,6 +191,8 @@ vips_extract_area_class_init( VipsExtractAreaClass *class ) vobject_class->description = _( "extract an area from an image" ); vobject_class->build = vips_extract_area_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "input", 0, _( "Input" ), _( "Input image" ), diff --git a/libvips/conversion/flatten.c b/libvips/conversion/flatten.c index 93946ddc..d4cd0461 100644 --- a/libvips/conversion/flatten.c +++ b/libvips/conversion/flatten.c @@ -358,6 +358,7 @@ vips_flatten_class_init( VipsFlattenClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_flatten_class_init\n" ); @@ -368,6 +369,8 @@ vips_flatten_class_init( VipsFlattenClass *class ) vobject_class->description = _( "flatten alpha out of an image" ); vobject_class->build = vips_flatten_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "in", 1, _( "Input" ), _( "Input image" ), diff --git a/libvips/conversion/ifthenelse.c b/libvips/conversion/ifthenelse.c index e0756e33..e2ac66a2 100644 --- a/libvips/conversion/ifthenelse.c +++ b/libvips/conversion/ifthenelse.c @@ -458,6 +458,7 @@ vips_ifthenelse_class_init( VipsIfthenelseClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_ifthenelse_class_init\n" ); @@ -468,6 +469,8 @@ vips_ifthenelse_class_init( VipsIfthenelseClass *class ) vobject_class->description = _( "ifthenelse an image" ); vobject_class->build = vips_ifthenelse_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "cond", -2, _( "Condition" ), _( "Condition input image" ), diff --git a/libvips/conversion/insert.c b/libvips/conversion/insert.c index 6b39c1cd..c100fa82 100644 --- a/libvips/conversion/insert.c +++ b/libvips/conversion/insert.c @@ -339,6 +339,7 @@ vips_insert_class_init( VipsInsertClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_insert_class_init\n" ); @@ -349,6 +350,8 @@ vips_insert_class_init( VipsInsertClass *class ) vobject_class->description = _( "insert an image" ); vobject_class->build = vips_insert_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "main", -1, _( "Main" ), _( "Main input image" ), diff --git a/libvips/conversion/join.c b/libvips/conversion/join.c index 815f4ccb..bcc6e307 100644 --- a/libvips/conversion/join.c +++ b/libvips/conversion/join.c @@ -200,6 +200,7 @@ vips_join_class_init( VipsJoinClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_join_class_init\n" ); @@ -210,6 +211,8 @@ vips_join_class_init( VipsJoinClass *class ) vobject_class->description = _( "join a pair of images" ); vobject_class->build = vips_join_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "in1", -1, _( "in1" ), _( "First input image" ), diff --git a/libvips/conversion/recomb.c b/libvips/conversion/recomb.c index 6d1b7ce8..197674ea 100644 --- a/libvips/conversion/recomb.c +++ b/libvips/conversion/recomb.c @@ -188,6 +188,7 @@ vips_recomb_class_init( VipsRecombClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -196,6 +197,8 @@ vips_recomb_class_init( VipsRecombClass *class ) object_class->description = _( "linear recombination with matrix" ); object_class->build = vips_recomb_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_IMAGE( class, "in", 0, _( "Input" ), _( "Input image argument" ), diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 1ca49054..7787b017 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -702,7 +702,7 @@ vips_foreign_load_temp( VipsForeignLoad *load ) /* You can't reuse sequential operations. */ - vips_operation_set_nocache( VIPS_OPERATION( load ), TRUE ); + load->nocache = TRUE; return( vips_image_new() ); } @@ -889,11 +889,26 @@ vips_foreign_load_build( VipsObject *object ) return( 0 ); } +static VipsOperationFlags +vips_foreign_load_real_get_flags( VipsOperation *operation ) +{ + VipsForeignLoad *load = VIPS_FOREIGN_LOAD( operation ); + VipsOperationFlags flags; + + flags = VIPS_OPERATION_CLASS( vips_foreign_load_parent_class )-> + get_flags( operation ); + if( load->nocache ) + flags |= VIPS_OPERATION_NOCACHE; + + return( flags ); +} + static void vips_foreign_load_class_init( VipsForeignLoadClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsOperationClass *operation_class = (VipsOperationClass *) class; gobject_class->dispose = vips_foreign_load_dispose; gobject_class->set_property = vips_object_set_property; @@ -905,6 +920,8 @@ vips_foreign_load_class_init( VipsForeignLoadClass *class ) object_class->nickname = "fileload"; object_class->description = _( "file loaders" ); + operation_class->get_flags = vips_foreign_load_real_get_flags; + VIPS_ARG_IMAGE( class, "out", 2, _( "Output" ), _( "Output image" ), diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am index ed64a858..138c57a4 100644 --- a/libvips/include/vips/Makefile.am +++ b/libvips/include/vips/Makefile.am @@ -63,6 +63,7 @@ vips_scan_headers = \ ${top_srcdir}/libvips/include/vips/conversion.h \ ${top_srcdir}/libvips/include/vips/util.h \ ${top_srcdir}/libvips/include/vips/image.h \ + ${top_srcdir}/libvips/include/vips/operation.h \ ${top_srcdir}/libvips/include/vips/object.h enumtypes.h: $(vips_scan_headers) Makefile diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index e313bd2e..44bc558d 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -57,6 +57,9 @@ GType vips_band_format_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_BAND_FORMAT (vips_band_format_get_type()) GType vips_coding_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_CODING (vips_coding_get_type()) +/* enumerations from "../../../libvips/include/vips/operation.h" */ +GType vips_operation_flags_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_OPERATION_FLAGS (vips_operation_flags_get_type()) /* enumerations from "../../../libvips/include/vips/object.h" */ GType vips_argument_flags_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_ARGUMENT_FLAGS (vips_argument_flags_get_type()) diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index a1d42e40..cbe30807 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -136,6 +136,10 @@ typedef struct _VipsForeignLoad { * disc foreign or a memory buffer. This must be set by ->load(). */ VipsImage *real; + + /* Set this to tag the operation as nocache. + */ + gboolean nocache; } VipsForeignLoad; typedef struct _VipsForeignLoadClass { diff --git a/libvips/include/vips/operation.h b/libvips/include/vips/operation.h index bae34ae6..8689dcc2 100644 --- a/libvips/include/vips/operation.h +++ b/libvips/include/vips/operation.h @@ -36,6 +36,27 @@ extern "C" { #include +/** + * VipsOperationFlags: + * @VIPS_OPERATION_NONE: no flags + * @VIPS_OPERATION_SEQUENTIAL: can work sequentially + * @VIPS_OPERATION_NOCACHE: must not be cached + * + * Flags we associate with an operation. + * + * @VIPS_OPERATION_SEQUENTIAL means that the operation works like vips_copy: + * it can happily process images top-to-bottom with only small non-local + * references. + * + * @VIPS_OPERATION_NOCACHE means that the operation must not be cached by + * vips. + */ +typedef enum /*< flags >*/ { + VIPS_OPERATION_NONE = 0, + VIPS_OPERATION_SEQUENTIAL = 1, + VIPS_OPERATION_NOCACHE = 2 +} VipsOperationFlags; + #define VIPS_TYPE_OPERATION (vips_operation_get_type()) #define VIPS_OPERATION( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ @@ -66,12 +87,6 @@ typedef struct _VipsOperation { guint hash; gboolean found_hash; - /* Set this before the end of _build() to stop this operation from - * being cached. Some things, like sequential read from a TIFF file, - * can't be reused. - */ - gboolean nocache; - } VipsOperation; typedef struct _VipsOperationClass { @@ -80,10 +95,17 @@ typedef struct _VipsOperationClass { /* Print the usage message. */ void (*usage)( struct _VipsOperationClass *, VipsBuf * ); + + /* Return a set of operation flags. If @get_flags is NULL, just use + * flags. + */ + VipsOperationFlags (*get_flags)( VipsOperation * ); + VipsOperationFlags flags; } VipsOperationClass; GType vips_operation_get_type( void ); +VipsOperationFlags vips_operation_get_flags( VipsOperation *operation ); void vips_operation_class_print_usage( VipsOperationClass *operation_class ); int vips_operation_call_valist( VipsOperation *operation, va_list ap ); @@ -96,7 +118,6 @@ void vips_call_options( GOptionGroup *group, VipsOperation *operation ); int vips_call_argv( VipsOperation *operation, int argc, char **argv ); void vips_cache_drop_all( void ); -void vips_operation_set_nocache( VipsOperation *operation, gboolean nocache ); int vips_cache_operation_buildp( VipsOperation **operation ); VipsOperation *vips_cache_operation_build( VipsOperation *operation ); void vips_cache_print( void ); diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index 0d538ae4..3c9cca3e 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -46,6 +46,7 @@ vips_scan_headers = \ ${top_srcdir}/libvips/include/vips/arithmetic.h \ ${top_srcdir}/libvips/include/vips/util.h \ ${top_srcdir}/libvips/include/vips/image.h \ + ${top_srcdir}/libvips/include/vips/operation.h \ ${top_srcdir}/libvips/include/vips/object.h enumtypes.c: $(vips_scan_headers) Makefile diff --git a/libvips/iofuncs/cache.c b/libvips/iofuncs/cache.c index 920b3591..55273763 100644 --- a/libvips/iofuncs/cache.c +++ b/libvips/iofuncs/cache.c @@ -693,7 +693,8 @@ vips_cache_operation_buildp( VipsOperation **operation ) if( !hit ) { if( vips__cache_trace ) { - if( (*operation)->nocache ) + if( vips_operation_get_flags( *operation ) & + VIPS_OPERATION_NOCACHE ) printf( "vips cache: uncacheable %p\n ", *operation ); else @@ -706,7 +707,8 @@ vips_cache_operation_buildp( VipsOperation **operation ) g_mutex_lock( vips_cache_lock ); - if( !(*operation)->nocache ) { + if( !(vips_operation_get_flags( *operation ) & + VIPS_OPERATION_NOCACHE) ) { vips_cache_ref( *operation ); g_hash_table_insert( vips_cache_table, *operation, *operation ); diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index c8eb8554..676d279b 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -492,6 +492,25 @@ vips_coding_get_type( void ) return( etype ); } +/* enumerations from "../../libvips/include/vips/operation.h" */ +GType +vips_operation_flags_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GFlagsValue values[] = { + {VIPS_OPERATION_NONE, "VIPS_OPERATION_NONE", "none"}, + {VIPS_OPERATION_SEQUENTIAL, "VIPS_OPERATION_SEQUENTIAL", "sequential"}, + {VIPS_OPERATION_NOCACHE, "VIPS_OPERATION_NOCACHE", "nocache"}, + {0, NULL, NULL} + }; + + etype = g_flags_register_static( "VipsOperationFlags", values ); + } + + return( etype ); +} /* enumerations from "../../libvips/include/vips/object.h" */ GType vips_argument_flags_get_type( void ) diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index d809fe48..1c27fde7 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -1466,12 +1466,27 @@ vips_object_set_argument_from_string( VipsObject *object, if( g_type_is_a( otype, VIPS_TYPE_IMAGE ) ) { VipsImage *out; + VipsOperationFlags flags; + + flags = 0; + if( VIPS_IS_OPERATION( object ) ) + flags = vips_operation_get_flags( + VIPS_OPERATION( object ) ); /* Read the filename. vips_foreign_load_options() * handles embedded options. */ - if( vips_foreign_load_options( value, &out, NULL ) ) - return( -1 ); + if( flags & VIPS_OPERATION_SEQUENTIAL ) { + if( vips_foreign_load_options( value, &out, + "sequential", TRUE, + NULL ) ) + return( -1 ); + } + else { + if( vips_foreign_load_options( value, &out, + NULL ) ) + return( -1 ); + } g_value_init( &gvalue, VIPS_TYPE_IMAGE ); g_value_set_object( &gvalue, out ); diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index 7c6cda0d..e74e8d33 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -148,6 +148,24 @@ vips_operation_usage( VipsOperationClass *class, VipsBuf *buf ) vips_argument_class_map( object_class, (VipsArgumentClassMapFn) vips_operation_class_usage_arg, buf, &usage ); + + /* Show flags. + */ + if( class->flags ) { + GFlagsValue *value; + VipsOperationFlags flags; + GFlagsClass *flags_class = + g_type_class_ref( VIPS_TYPE_OPERATION_FLAGS ); + + vips_buf_appendf( buf, "operation flags: " ); + flags = class->flags; + while( flags && (value = + g_flags_get_first_value( flags_class, flags )) ) { + vips_buf_appendf( buf, "%s ", value->value_nick ); + flags &= ~value->value; + } + vips_buf_appends( buf, "\n" ); + } } static void * @@ -233,6 +251,14 @@ vips_operation_summary( VipsObject *object, VipsBuf *buf ) summary( object, buf ); } +static VipsOperationFlags +vips_operation_real_get_flags( VipsOperation *operation ) +{ + VipsOperationClass *class = VIPS_OPERATION_GET_CLASS( operation ); + + return( class->flags ); +} + static void vips_operation_class_init( VipsOperationClass *class ) { @@ -248,6 +274,7 @@ vips_operation_class_init( VipsOperationClass *class ) vobject_class->dump = vips_operation_dump; class->usage = vips_operation_usage; + class->get_flags = vips_operation_real_get_flags; } static void @@ -257,6 +284,22 @@ vips_operation_init( VipsOperation *operation ) */ } +/** + * vips_operation_get_flags: + * @operation: operation to fetch flags from + * + * Returns the set of flags for this operation. + * + * Returns: 0 on success, or -1 on error. + */ +VipsOperationFlags +vips_operation_get_flags( VipsOperation *operation ) +{ + VipsOperationClass *class = VIPS_OPERATION_GET_CLASS( operation ); + + return( class->get_flags( operation ) ); +} + /** * vips_operation_class_print_usage: (skip) * @operation_class: class to print usage for @@ -913,24 +956,3 @@ vips_call_argv( VipsOperation *operation, int argc, char **argv ) return( 0 ); } - -/** - * vips_operation_set_nocache: - * @operation: operation to set - * @nocache: TRUE means don't cache this operation - * - * Set this before the end of _build() to stop this operation being cached. - * Some operations, like sequential read from a TIFF file, for example, cannot - * be reused. - */ -void -vips_operation_set_nocache( VipsOperation *operation, gboolean nocache ) -{ -#ifdef VIPS_DEBUG - printf( "vips_operation_set_nocache: " ); - vips_object_print_name( VIPS_OBJECT( operation ) ); - printf( " %d\n", nocache ); -#endif /*VIPS_DEBUG*/ - - operation->nocache = nocache; -} diff --git a/libvips/resample/shrink.c b/libvips/resample/shrink.c index f038c28d..747bdd6b 100644 --- a/libvips/resample/shrink.c +++ b/libvips/resample/shrink.c @@ -360,6 +360,7 @@ vips_shrink_class_init( VipsShrinkClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); VIPS_DEBUG_MSG( "vips_shrink_class_init\n" ); @@ -370,6 +371,8 @@ vips_shrink_class_init( VipsShrinkClass *class ) vobject_class->description = _( "shrink an image" ); vobject_class->build = vips_shrink_build; + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + VIPS_ARG_DOUBLE( class, "xshrink", 8, _( "Xshrink" ), _( "Horizontal shrink factor" ),