diff --git a/ChangeLog b/ChangeLog index 43676cb2..7a65488b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +12/8/14 started 7.40.5 +- more doc fixes + 25/7/14 started 7.40.5 - fix a race in im_maxpos_avg() - limit n_thr on tiny images diff --git a/TODO b/TODO index 577294ec..4d015a25 100644 --- a/TODO +++ b/TODO @@ -1,12 +1,15 @@ - can we pick the vipsthumbnail int shrink factor more intelligently? +- did we include the exif patch in the latest windows build? check on laptop + + we don't seem to have this fix in the repository! + - vips_object_unref_outputs() needs docs ... bindings will need it -- copy-paste blog post about writing vips8 operations into 'extending' - - vips_init() has a deprecation warning, bizarre + - check and fix up docs index items should all be links diff --git a/configure.ac b/configure.ac index e9327fff..99f05c92 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # also update the version number in the m4 macros below -AC_INIT([vips], [7.40.5], [vipsip@jiscmail.ac.uk]) +AC_INIT([vips], [7.40.6], [vipsip@jiscmail.ac.uk]) # required for gobject-introspection AC_PREREQ(2.62) @@ -18,7 +18,7 @@ AC_CONFIG_MACRO_DIR([m4]) # user-visible library versioning m4_define([vips_major_version], [7]) m4_define([vips_minor_version], [40]) -m4_define([vips_micro_version], [5]) +m4_define([vips_micro_version], [6]) m4_define([vips_version], [vips_major_version.vips_minor_version.vips_micro_version]) @@ -38,7 +38,7 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date` # binary interface changes not backwards compatible?: reset age to 0 LIBRARY_CURRENT=38 -LIBRARY_REVISION=3 +LIBRARY_REVISION=4 LIBRARY_AGE=0 # patched into include/vips/version.h diff --git a/doc/reference/binding.xml b/doc/reference/binding.xml index ffe5c4af..bcddc786 100644 --- a/doc/reference/binding.xml +++ b/doc/reference/binding.xml @@ -19,6 +19,18 @@ Write this section once the vips8 Python binding is done. + + +If you are writing a language binding, you won't need these. Instead, make +a new operation with vips_operation_new() (all it does is look up the +operation by name with vips_type_find(), then call g_object_new() for you), +then use vips_argument_map() and friends to loop over the operation's +arguments setting them. Once you have set all arguments, use +vips_cache_operation_build() to look up the operation in the cache and +either build or dup it. If something goes wrong, you'll need to use +vips_object_unref_outputs() and g_object_unref() to free the +partially-built object. + diff --git a/doc/reference/extending.xml b/doc/reference/extending.xml index faba973d..24b4940d 100644 --- a/doc/reference/extending.xml +++ b/doc/reference/extending.xml @@ -14,23 +14,354 @@ How to add operations to VIPS - - Adding operations to VIPS + + A simple point-to-point operation + - All about subclassing. Copy-paste from the blog post about extending - vips8. + All operations are subclasses of #VipsOperation, which in turn + subclasses #VipsObject and then %GObject. You need to define a new + instance struct and a new class struct. + + +typedef struct _Negative { + VipsOperation parent_instance; + + VipsImage *in; + VipsImage *out; + + int image_max; + +} Negative; + +typedef struct _NegativeClass { + VipsOperationClass parent_class; + + /* No new class members needed for this op. + */ + +} NegativeClass; + -If you are writing a language binding, you won't need these. Instead, make -a new operation with vips_operation_new() (all it does is look up the -operation by name with vips_type_find(), then call g_object_new() for you), -then use vips_argument_map() and friends to loop over the operation's -arguments setting them. Once you have set all arguments, use -vips_cache_operation_build() to look up the operation in the cache and -either build or dup it. If something goes wrong, you'll need to use -vips_object_unref_outputs() and g_object_unref() to free the -partially-built object. + This operation will find the photographic negative of an unsigned + 8-bit image, optionally letting you specify the value which the pixels + "pivot" about. It doesn't need any class members (ie. values common + to all operations of this type), so the second struct is empty. See + vips_invert() for a more complete version of this operation that's + actually in the library. + + + + GObject has a handy macro to write some of the boilerplate for you. + + +G_DEFINE_TYPE( Negative, negative, VIPS_TYPE_OPERATION ); + + + This defines a function called negative_get_type(), + which registers this new class and returns its #GType (a + pointer-sized integer). negative_get_type() in turn needs two + functions, negative_init(), to initialise a new instance, and + negative_class_init(), to initialise a new class. + + + + negative_init() is very simple, it just sets the default value for + our optional class parameter. + + +static void +negative_init( Negative *negative ) +{ + negative->image_max = 255; +} + + + + + negative_class_init() is more complicated: it has to set various + fields in various superclasses. + + +static void +negative_class_init( NegativeClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "negative"; + object_class->description = "photographic negative"; + object_class->build = negative_build; + + VIPS_ARG_IMAGE( class, "in", 1, + "Input", + "Input image", + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( Negative, in ) ); + + VIPS_ARG_IMAGE( class, "out", 2, + "Output", + "Output image", + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( Negative, out ) ); + + VIPS_ARG_INT( class, "image_max", 4, + "Image maximum", + "Maximum value in image: pivot about this", + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( Negative, image_max ), + 0, 255, 255 ); +} + + + + + In %GObject, it needs to set the getters and setters for this class. vips + has a generic get/set system, so any subclass of #VipsObject needs to + use the vips ones. + + + + In #VipsObject, it needs to set the operation @nickname and @description, + and set a build function (see below). @nickname is used to refer to + this operation in the API, @description is used to explain this + operation to users and will be translated into their language. + + + + Finally, it needs to set the arguments this class constructor + takes. There are a set of handy macros for doing this. The first few + parameters are always the same and mean: class pointer for argument, + argument name, argument priority (bindings expect required arguments in + order of priority), long argument name (this one is internationalised + and displayed to users), description (again, users can see this), + some flags describing the argument, and finally the position of the + member in the struct. + + + + Integer arguments take three more values: the minimum, maximum and + default value for the argument. + + + + The build function is the thing VipsObject calls after supplying + arguments. It checks that all required arguments have been set and are + valid and constructs the object. After build, the object is expected + to be ready for use. + + +static int +negative_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + Negative *negative = (Negative *) object; + + if( VIPS_OBJECT_CLASS( negative_parent_class )->build( object ) ) + return( -1 ); + + if( vips_check_uncoded( class->nickname, negative->in ) || + vips_check_format( class->nickname, negative->in, VIPS_FORMAT_UCHAR ) ) + return( -1 ); + + g_object_set( object, "out", vips_image_new(), NULL ); + + if( vips_image_pipelinev( negative->out, + VIPS_DEMAND_STYLE_THINSTRIP, negative->in, NULL ) ) + return( -1 ); + + if( vips_image_generate( negative->out, + vips_start_one, + negative_generate, + vips_stop_one, + negative->in, negative ) ) + return( -1 ); + + return( 0 ); +} + + + + + negative_build() first chains up to the superclass: this will check + that all input arguments have been supplied and are sane. + + + + Next, it adds its own checks. This is a demo operation, so we just + work for uncoded, unsigned 8-bit images. + + + + Next, it creates the output image. This needs to be set with + g_object_set() so that vips can see that it has been assigned. vips + will also handle the reference counting for you. + + + + vips_image_pipelinev() links our new image onto the input image and + notes that this operation prefers to work in lines. + + + + Finally, vips_image_generate() attaches a set of callbacks to the + output image to generate chunks of it on request. vips_start_one() + and vips_stop_one() are convenience functions that make the input + region for you. + + + + And then the actual image processing. + + +static int +negative_generate( VipsRegion *or, + void *vseq, void *a, void *b, gboolean *stop ) +{ + /* The area of the output region we have been asked to make. + */ + VipsRect *r = &or->valid; + + /* The sequence value ... the thing returned by vips_start_one(). + */ + VipsRegion *ir = (VipsRegion *) vseq; + + Negative *negative = (Negative *) b; + int line_size = r->width * negative->in->Bands; + + int x, y; + + /* Request matching part of input region. + */ + if( vips_region_prepare( ir, r ) ) + return( -1 ); + + for( y = 0; y < r->height; y++ ) { + unsigned char *p = (unsigned char *) + VIPS_REGION_ADDR( ir, r->left, r->top + y ); + unsigned char *q = (unsigned char *) + VIPS_REGION_ADDR( or, r->left, r->top + y ); + + for( x = 0; x < line_size; x++ ) + q[x] = negative->image_max - p[x]; + } + + return( 0 ); +} + + + + + This has to calculate a section of the output image. The output + #VipsRegion, @or, contains a #VipsRect called @valid which is the + area needing calculation. negative_generate() asks for the + corresponding pixels from the input region, then loops over the + area. VIPS_REGION_ADDR() is a simple macro that does pointer arithmetic + for you: you need to stay within the valid area. + + + + To add the operation to vips, just call negative_get_type(). You + can then use @negative from any of the vips interfaces. For example, + in Python you'd use it like this: + + +out = in.negative(image_max = 128) + + + + + From the command-line it'd look like this: + + +$ vips negative in.png out.tif --image-max 128 + + + + + And from C like this: + + +VipsImage *in; +VipsImage *out; +if( vips_call( "negative", in, &out, "image_max", 128, NULL ) ) + ... error + + + + + Unfortunately that will do almost no compile-time type checking, + so all vips operations have a tiny extra wrapper to add a bit of + safety. For example: + + +static int +negative( VipsImage *in, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "negative", ap, in, out ); + va_end( ap ); + + return( result ); +} + + + + + And now you can write: + + +if( negative( in, &out, "image_max", 128, NULL ) ) + ... error + + + and it's at least a bit safer. + + + + + + Other types of operation + + Change the _build() function to make other types of operation. + + + + Use vips_image_generate() with vips_start_many() to make operations + which demand pixels from more than one image at once, such as image + plus image. + + + + Use vips_sink() instead of vips_image_generate() to loop over an image + and calculate a value. vips uses this for the statistics operations, + like vips_avg(). + + + + Use vips_image_wio_input() to get an entire image into memory so you + can read it with a pointer. This will obviously not scale well to + very large images, but some operations, like FFTs or flood-fill, need + the whole image to be available at once. + + + + Make area operations, like filters, by enlarging the #VipsRect that + _generate() is given before calling vips_image_prepare(). You can + enlarge the input image, so that the output image is the same size as + the original input, by using vips_embed() within the _build() function. + + + + Make things like flips and rotates by making larger changes to the + #VipsRect in _generate().