From 296eb8b54ec28e06976a274e5de86d33c0060a1b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 9 Aug 2014 17:14:49 +0100 Subject: [PATCH 1/7] fix pngload with libpng >=1.6.11 thaks Lovell --- ChangeLog | 1 + libvips/foreign/vipspng.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1eb41b48..5d72f281 100644 --- a/ChangeLog +++ b/ChangeLog @@ -9,6 +9,7 @@ - vipsthumbnail shrinks to 1/2 window_size: faster and better quality - vipsthumbnail defaults to bicubic + nosharpen - better rounding behaviour for fixed-point bicubic reduces noise +- fix pngload with libpng >=1.6.11 4/7/14 started 7.40.4 - fix vips_rawsave_fd(), thanks aferrero2707 diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index c3a36f56..c8fefc7c 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -48,6 +48,8 @@ * still set color_type to alpha * 16/7/13 * - more robust error handling from libpng + * 9/8/14 + * - don't check profiles, helps with libpng >=1.6.11 */ /* @@ -188,6 +190,13 @@ read_new( VipsImage *out, gboolean readbehind ) user_error_function, user_warning_function )) ) return( NULL ); +#ifdef PNG_SKIP_sRGB_CHECK_PROFILE + /* Prevent libpng (>=1.6.11) verifying sRGB profiles. + */ + png_set_option( read->pPng, + PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON ); +#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/ + /* Catch PNG errors from png_create_info_struct(). */ if( setjmp( png_jmpbuf( read->pPng ) ) ) @@ -717,6 +726,13 @@ write_new( VipsImage *in ) user_error_function, user_warning_function )) ) return( NULL ); +#ifdef PNG_SKIP_sRGB_CHECK_PROFILE + /* Prevent libpng (>=1.6.11) verifying sRGB profiles. + */ + png_set_option( write->pPng, + PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON ); +#endif /*PNG_SKIP_sRGB_CHECK_PROFILE*/ + /* Catch PNG errors from png_create_info_struct(). */ if( setjmp( png_jmpbuf( write->pPng ) ) ) From 385b2ea5fab7ff568f1ac36ce195d7949a2eaf7c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 9 Aug 2014 22:21:10 +0100 Subject: [PATCH 2/7] do argb -> rgba for openslide read associated --- ChangeLog | 1 + libvips/foreign/openslide2vips.c | 82 ++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/ChangeLog b/ChangeLog index 5d72f281..523c3bb5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,6 +10,7 @@ - vipsthumbnail defaults to bicubic + nosharpen - better rounding behaviour for fixed-point bicubic reduces noise - fix pngload with libpng >=1.6.11 +- fix colour for openslide read associated 4/7/14 started 7.40.4 - fix vips_rawsave_fd(), thanks aferrero2707 diff --git a/libvips/foreign/openslide2vips.c b/libvips/foreign/openslide2vips.c index 136c1339..93b1c031 100644 --- a/libvips/foreign/openslide2vips.c +++ b/libvips/foreign/openslide2vips.c @@ -43,6 +43,8 @@ * - use openslide_detect_vendor() on >= 3.4.0 * 30/7/14 * - add autocrop toggle + * 9/8/14 + * - do argb -> rgba for associated as well */ /* @@ -390,6 +392,45 @@ vips__openslide_read_header( const char *filename, VipsImage *out, return( 0 ); } +/* Convert from ARGB to RGBA and undo premultiplication. + * + * We throw away transparency. Formats like Mirax use transparent + bg + * colour for areas with no useful pixels. But if we output + * transparent pixels and then convert to RGB for jpeg write later, we + * would have to pass the bg colour down the pipe somehow. The + * structure of dzsave makes this tricky. + * + * We could output plain RGB instead, but that would break + * compatibility with older vipses. + */ +static void +argb2rgba( uint32_t *buf, int n, uint32_t bg ) +{ + int i; + + for( i = 0; i < n; i++ ) { + uint32_t *p = buf + i; + uint32_t x = *p; + uint8_t a = x >> 24; + VipsPel *out = (VipsPel *) p; + + if( a != 0 ) { + out[0] = 255 * ((x >> 16) & 255) / a; + out[1] = 255 * ((x >> 8) & 255) / a; + out[2] = 255 * (x & 255) / a; + out[3] = 255; + } + else { + /* Use background color. + */ + out[0] = (bg >> 16) & 255; + out[1] = (bg >> 8) & 255; + out[2] = bg & 255; + out[3] = 255; + } + } +} + static int vips__openslide_generate( VipsRegion *out, void *_seq, void *_rslide, void *unused, gboolean *stop ) @@ -401,7 +442,6 @@ vips__openslide_generate( VipsRegion *out, uint32_t *buf = (uint32_t *) VIPS_REGION_ADDR( out, r->left, r->top ); const char *error; - int i; VIPS_DEBUG_MSG( "vips__openslide_generate: %dx%d @ %dx%d\n", r->width, r->height, r->left, r->top ); @@ -434,39 +474,9 @@ vips__openslide_generate( VipsRegion *out, return( -1 ); } - /* Convert from ARGB to RGBA and undo premultiplication. Since we are - * inside a cache, we know buf must be continuous. - * - * We throw away transparency. Formats like Mirax use transparent + bg - * colour for areas with no useful pixels. But if we output - * transparent pixels and then convert to RGB for jpeg write later, we - * would have to pass the bg colour down the pipe somehow. The - * structure of dzsave makes this tricky. - * - * We could output plain RGB instead, but that would break - * compatibility with older vipses. + /* Since we are inside a cache, we know buf must be continuous. */ - for( i = 0; i < n; i++ ) { - uint32_t *p = buf + i; - uint32_t x = *p; - uint8_t a = x >> 24; - VipsPel *out = (VipsPel *) p; - - if( a != 0 ) { - out[0] = 255 * ((x >> 16) & 255) / a; - out[1] = 255 * ((x >> 8) & 255) / a; - out[2] = 255 * (x & 255) / a; - out[3] = 255; - } - else { - /* Use background color. - */ - out[0] = (bg >> 16) & 255; - out[1] = (bg >> 8) & 255; - out[2] = bg & 255; - out[3] = 255; - } - } + argb2rgba( buf, n, bg ); return( 0 ); } @@ -519,6 +529,7 @@ vips__openslide_read_associated( const char *filename, VipsImage *out, { ReadSlide *rslide; VipsImage *raw; + uint32_t *buf; const char *error; VIPS_DEBUG_MSG( "vips__openslide_read_associated: %s %s\n", @@ -532,14 +543,15 @@ vips__openslide_read_associated( const char *filename, VipsImage *out, if( !(rslide = readslide_new( filename, raw, 0, FALSE, associated )) || vips_image_write_prepare( raw ) ) return( -1 ); - openslide_read_associated_image( rslide->osr, rslide->associated, - (uint32_t *) VIPS_IMAGE_ADDR( raw, 0, 0 ) ); + buf = (uint32_t *) VIPS_IMAGE_ADDR( raw, 0, 0 ); + openslide_read_associated_image( rslide->osr, rslide->associated, buf ); error = openslide_get_error( rslide->osr ); if( error ) { vips_error( "openslide2vips", _( "reading associated image: %s" ), error ); return( -1 ); } + argb2rgba( buf, raw->Xsize * raw->Ysize, rslide->bg ); if( vips_image_write( raw, out ) ) return( -1 ); From ed3c56fcbfda7144d3adcd82c16f3b2e7a7f7f2d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 11 Aug 2014 10:26:12 +0100 Subject: [PATCH 3/7] add an anti-alias filter to vipsthumbnail vipsthumbnail blurs slightly between shrink and affine, if the affine will be doing a large shrink --- ChangeLog | 5 ++-- tools/vipsthumbnail.c | 61 +++++++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/ChangeLog b/ChangeLog index 523c3bb5..43676cb2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -6,8 +6,9 @@ - argh fix affine, again, there were sometimes black bars with nohalo and the vips8 interface - pngsave in interlaced mode makes a copy of the image, so it's always seq -- vipsthumbnail shrinks to 1/2 window_size: faster and better quality -- vipsthumbnail defaults to bicubic + nosharpen +- vipsthumbnail shrinks to 1/2 window_size +- vipsthumbnail has an anti-alias filter between shrink and affine +- vipsthumbnail defaults to bicubic - better rounding behaviour for fixed-point bicubic reduces noise - fix pngload with libpng >=1.6.11 - fix colour for openslide read associated diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index 75d4ee58..34a22e34 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -52,9 +52,10 @@ * 30/6/14 * - fix interlaced thumbnail output, thanks lovell * 3/8/14 - * - box shrink less, use interpolator more, if the window_size is large + * - box shrink less, use interpolator more, if window_size is large * enough - * - default to bicubic + nosharpen if bicubic is available + * - default to bicubic if available + * - add an anti-alias filter between shrink and affine */ #ifdef HAVE_CONFIG_H @@ -72,7 +73,7 @@ #define ORIENTATION ("exif-ifd0-Orientation") -/* Default settings. We change the default to bicubic + nosharpen in main() if +/* Default settings. We change the default to bicubic in main() if * this vips has been compiled with bicubic support. */ @@ -391,12 +392,15 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, VipsImage **t = (VipsImage **) vips_object_local_array( process, 10 ); VipsInterpretation interpretation = linear_processing ? VIPS_INTERPRETATION_XYZ : VIPS_INTERPRETATION_sRGB; + const int window_size = + interp ? vips_interpolate_get_window_size( interp ) : 2; int shrink; double residual; int tile_width; int tile_height; int nlines; + double sigma; /* RAD needs special unpacking. */ @@ -474,6 +478,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, * has been used ... but it never will, since thread1 will block on * this cache lock. */ + vips_get_tile_size( in, &tile_width, &tile_height, &nlines ); if( vips_tilecache( in, &t[4], @@ -482,12 +487,38 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, "max_tiles", (nlines * 2) / 10, "access", VIPS_ACCESS_SEQUENTIAL, "threaded", TRUE, - NULL ) || - vips_affine( t[4], &t[5], residual, 0, 0, residual, - "interpolate", interp, - NULL ) ) + NULL ) ) return( NULL ); - in = t[5]; + in = t[4]; + + /* If the final affine will be doing a large downsample, we can get + * nasty aliasing on hard edges. Blur before affine to smooth this out. + * + * Don't blur for very small shrinks, blur with radius 1 for x1.5 + * shrinks, blur radius 2 for x2.5 shrinks and above, etc. + */ + sigma = ((1.0 / residual) - 0.5) / 1.5; + if( sigma > 0.1 ) { + if( vips_gaussmat( &t[9], sigma, 0.2, + "separable", TRUE, + "integer", TRUE, + NULL ) || + vips_convsep( in, &t[5], t[9], NULL ) ) + return( NULL ); + vips_info( "vipsthumbnail", "anti-alias, sigma %g", + sigma ); +#ifdef DEBUG + printf( "anti-alias blur matrix is:\n" ); + vips_matrixprint( t[9], NULL ); +#endif /*DEBUG*/ + in = t[5]; + } + + if( vips_affine( in, &t[6], residual, 0, 0, residual, + "interpolate", interp, + NULL ) ) + return( NULL ); + in = t[6]; vips_info( "vipsthumbnail", "residual scale by %g", residual ); vips_info( "vipsthumbnail", "%s interpolation", @@ -511,10 +542,10 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, } else { vips_info( "vipsthumbnail", "converting to sRGB" ); - if( vips_colourspace( in, &t[6], + if( vips_colourspace( in, &t[7], VIPS_INTERPRETATION_sRGB, NULL ) ) return( NULL ); - in = t[6]; + in = t[7]; } } else if( export_profile && @@ -530,13 +561,13 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, vips_info( "vipsthumbnail", "exporting with profile %s", export_profile ); - if( vips_icc_transform( in, &t[6], export_profile, + if( vips_icc_transform( in, &t[7], export_profile, "input_profile", import_profile, "embedded", TRUE, NULL ) ) return( NULL ); - in = t[6]; + in = t[7]; } /* If we are upsampling, don't sharpen, since nearest looks dumb @@ -687,13 +718,11 @@ main( int argc, char **argv ) textdomain( GETTEXT_PACKAGE ); setlocale( LC_ALL, "" ); - /* Does this vips have bicubic? Default to that + nosharpen if it + /* Does this vips have bicubic? Default to that if it * does. */ - if( vips_type_find( "VipsInterpolate", "bicubic" ) ) { + if( vips_type_find( "VipsInterpolate", "bicubic" ) ) interpolator = "bicubic"; - convolution_mask = "none"; - } context = g_option_context_new( _( "- thumbnail generator" ) ); From ce428ffa9474949c7a07b8807ab3119c43de3e2f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 11 Aug 2014 16:48:51 +0100 Subject: [PATCH 4/7] more vipsthumbnail fixes - handle CMYK images - use unbuffered mode, we have a cache already - don't try to anti-alias on upscale --- TODO | 1 + tools/vipsthumbnail.c | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/TODO b/TODO index ca19f927..577294ec 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,4 @@ +- can we pick the vipsthumbnail int shrink factor more intelligently? - vips_object_unref_outputs() needs docs ... bindings will need it diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index 34a22e34..a9bd0d90 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -56,6 +56,8 @@ * enough * - default to bicubic if available * - add an anti-alias filter between shrink and affine + * - support CMYK + * - use SEQ_UNBUF for a memory saving */ #ifdef HAVE_CONFIG_H @@ -317,7 +319,7 @@ thumbnail_open( VipsObject *process, const char *filename ) jpegshrink ); if( !(im = vips_image_new_from_file( filename, - "access", VIPS_ACCESS_SEQUENTIAL, + "access", VIPS_ACCESS_SEQUENTIAL_UNBUFFERED, "shrink", jpegshrink, NULL )) ) return( NULL ); @@ -326,7 +328,7 @@ thumbnail_open( VipsObject *process, const char *filename ) /* All other formats. */ if( !(im = vips_image_new_from_file( filename, - "access", VIPS_ACCESS_SEQUENTIAL, + "access", VIPS_ACCESS_SEQUENTIAL_UNBUFFERED, NULL )) ) return( NULL ); } @@ -345,7 +347,7 @@ thumbnail_interpolator( VipsObject *process, VipsImage *in ) calculate_shrink( in, &residual, NULL ); /* For images smaller than the thumbnail, we upscale with nearest - * neighbor. Otherwise we makes thumbnails that look fuzzy and awful. + * neighbor. Otherwise we make thumbnails that look fuzzy and awful. */ if( !(interp = VIPS_INTERPOLATE( vips_object_new_from_string( g_type_class_ref( VIPS_TYPE_INTERPOLATE ), @@ -392,8 +394,6 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, VipsImage **t = (VipsImage **) vips_object_local_array( process, 10 ); VipsInterpretation interpretation = linear_processing ? VIPS_INTERPRETATION_XYZ : VIPS_INTERPRETATION_sRGB; - const int window_size = - interp ? vips_interpolate_get_window_size( interp ) : 2; int shrink; double residual; @@ -415,12 +415,16 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, } /* In linear mode, we import right at the start. + * + * We also have to import the whole image if it's CMYK, since + * vips_colourspace() (see below) doesn't know about CMYK. * * This is only going to work for images in device space. If you have * an image in PCS which also has an attached profile, strange things * will happen. */ - if( linear_processing && + if( (linear_processing || + in->Type == VIPS_INTERPRETATION_CMYK) && in->Coding == VIPS_CODING_NONE && (in->BandFmt == VIPS_FORMAT_UCHAR || in->BandFmt == VIPS_FORMAT_USHORT) && @@ -498,7 +502,8 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, * shrinks, blur radius 2 for x2.5 shrinks and above, etc. */ sigma = ((1.0 / residual) - 0.5) / 1.5; - if( sigma > 0.1 ) { + if( residual < 1.0 && + sigma > 0.1 ) { if( vips_gaussmat( &t[9], sigma, 0.2, "separable", TRUE, "integer", TRUE, From 2f72a9925fa0deedc993d606ebb3236cd01c4dd3 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 12 Aug 2014 13:37:16 +0100 Subject: [PATCH 5/7] more docs work --- ChangeLog | 3 + TODO | 7 +- configure.ac | 6 +- doc/reference/binding.xml | 12 ++ doc/reference/extending.xml | 357 ++++++++++++++++++++++++++++++++++-- 5 files changed, 367 insertions(+), 18 deletions(-) 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(). From 7b496fb2f53d5f1867a686df0713798d6361a4cb Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 12 Aug 2014 13:45:03 +0100 Subject: [PATCH 6/7] udpate notes --- TODO | 3 +++ 1 file changed, 3 insertions(+) diff --git a/TODO b/TODO index 2e07af81..eb7323a0 100644 --- a/TODO +++ b/TODO @@ -12,6 +12,9 @@ - vips_init() has a deprecation warning, bizarre +- vips_affine() for simple resize is slow at the command-line, since it has to + be non-seq ... make a seq version that can only up/down size + - check and fix up docs From fe7bd9bdcc384dca56ee9c37f54982ae91311c68 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 13 Aug 2014 13:57:52 +0100 Subject: [PATCH 7/7] add vips_resize() a wrapper over affine that just scales ... much faster from the command-line, since it can set SEQ mode --- ChangeLog | 1 + libvips/create/gaussmat.c | 4 +- libvips/include/vips/resample.h | 3 + libvips/resample/Makefile.am | 1 + libvips/resample/affine.c | 2 +- libvips/resample/resample.c | 2 + libvips/resample/resize.c | 196 ++++++++++++++++++++++++++++++++ libvips/resample/similarity.c | 4 +- 8 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 libvips/resample/resize.c diff --git a/ChangeLog b/ChangeLog index a7cf43c5..0744dbc9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ 25/7/14 started 7.41.0 - start working on --disable-deprecated +- add vips_resize() 12/8/14 started 7.40.5 - more doc fixes diff --git a/libvips/create/gaussmat.c b/libvips/create/gaussmat.c index e1f624cf..145f5821 100644 --- a/libvips/create/gaussmat.c +++ b/libvips/create/gaussmat.c @@ -157,8 +157,8 @@ vips_gaussmat_class_init( VipsGaussmatClass *class ) vobject_class->build = vips_gaussmat_build; VIPS_ARG_DOUBLE( class, "sigma", 2, - _( "Radius" ), - _( "Radius of Gaussian" ), + _( "Sigma" ), + _( "Sigma of Gaussian" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsGaussmat, sigma ), 0.000001, 10000.0, 1.0 ); diff --git a/libvips/include/vips/resample.h b/libvips/include/vips/resample.h index e692952c..bee29ea9 100644 --- a/libvips/include/vips/resample.h +++ b/libvips/include/vips/resample.h @@ -43,6 +43,9 @@ int vips_shrink( VipsImage *in, VipsImage **out, __attribute__((sentinel)); int vips_similarity( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_resize( VipsImage *in, VipsImage **out, + double h_scale, double v_scale, ... ) + __attribute__((sentinel)); int vips_affine( VipsImage *in, VipsImage **out, double a, double b, double c, double d, ... ) __attribute__((sentinel)); diff --git a/libvips/resample/Makefile.am b/libvips/resample/Makefile.am index c0e61caf..24228c1e 100644 --- a/libvips/resample/Makefile.am +++ b/libvips/resample/Makefile.am @@ -9,6 +9,7 @@ libresample_la_SOURCES = \ quadratic.c \ resample.c \ similarity.c \ + resize.c \ presample.h \ shrink.c \ interpolate.c \ diff --git a/libvips/resample/affine.c b/libvips/resample/affine.c index d01e3cd5..6123ad8f 100644 --- a/libvips/resample/affine.c +++ b/libvips/resample/affine.c @@ -1,4 +1,4 @@ -/* im_affine() ... affine transform with a supplied interpolator. +/* affine transform with a supplied interpolator. * * * Copyright N. Dessipris diff --git a/libvips/resample/resample.c b/libvips/resample/resample.c index f6718feb..9c59bdcc 100644 --- a/libvips/resample/resample.c +++ b/libvips/resample/resample.c @@ -115,10 +115,12 @@ vips_resample_operation_init( void ) extern GType vips_quadratic_get_type( void ); extern GType vips_affine_get_type( void ); extern GType vips_similarity_get_type( void ); + extern GType vips_resize_get_type( void ); vips_shrink_get_type(); vips_quadratic_get_type(); vips_affine_get_type(); vips_similarity_get_type(); + vips_resize_get_type(); } diff --git a/libvips/resample/resize.c b/libvips/resample/resize.c new file mode 100644 index 00000000..9b6b757d --- /dev/null +++ b/libvips/resample/resize.c @@ -0,0 +1,196 @@ +/* resize an image ... a simple wrapper over affine + * + * 13/8/14 + * - from affine.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG_VERBOSE +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "presample.h" + +typedef struct _VipsResize { + VipsResample parent_instance; + + double h_scale; + double v_scale; + VipsInterpolate *interpolate; + double idx; + double idy; + +} VipsResize; + +typedef VipsResampleClass VipsResizeClass; + +G_DEFINE_TYPE( VipsResize, vips_resize, VIPS_TYPE_RESAMPLE ); + +static int +vips_resize_build( VipsObject *object ) +{ + VipsResample *resample = VIPS_RESAMPLE( object ); + VipsResize *resize = (VipsResize *) object; + + VipsImage **t = (VipsImage **) + vips_object_local_array( object, 4 ); + + double a, b, c, d; + + if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) ) + return( -1 ); + + a = resize->h_scale; + b = 0.0; + c = 0.0; + d = resize->v_scale; + + if( vips_affine( resample->in, &t[0], a, b, c, d, + "interpolate", resize->interpolate, + "idx", resize->idx, + "idy", resize->idy, + NULL ) || + vips_image_write( t[0], resample->out ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_resize_class_init( VipsResizeClass *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_resize_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "resize"; + vobject_class->description = _( "resize an image" ); + vobject_class->build = vips_resize_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_DOUBLE( class, "h_scale", 113, + _( "Horizontal scale factor" ), + _( "Scale image by this factor in the horizontal axis" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsResize, h_scale ), + 0, 10000000, 0 ); + + VIPS_ARG_DOUBLE( class, "v_scale", 114, + _( "Vertical scale factor" ), + _( "Scale image by this factor in the vertical axis" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsResize, v_scale ), + 0, 10000000, 0 ); + + VIPS_ARG_INTERPOLATE( class, "interpolate", 2, + _( "Interpolate" ), + _( "Interpolate pixels with this" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsResize, interpolate ) ); + + VIPS_ARG_DOUBLE( class, "idx", 115, + _( "Input offset" ), + _( "Horizontal input displacement" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsResize, idx ), + -10000000, 10000000, 0 ); + + VIPS_ARG_DOUBLE( class, "idy", 116, + _( "Input offset" ), + _( "Vertical input displacement" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsResize, idy ), + -10000000, 10000000, 0 ); +} + +static void +vips_resize_init( VipsResize *resize ) +{ +} + +/** + * vips_resize: + * @in: input image + * @out: output image + * @h_scale: horizontal scale factor + * @v_scale: vertical scale factor + * + * Optional arguments: + * + * @interpolate: interpolate pixels with this + * @idx: input horizontal offset + * @idy: input vertical offset + * + * @interpolate defaults to bilinear. + * + * @idx, @idy default to zero. + * + * See also: vips_shrink(), vips_affine(), #VipsInterpolate. + * + * Returns: 0 on success, -1 on error + */ +int +vips_resize( VipsImage *in, VipsImage **out, + double h_scale, double v_scale, ... ) +{ + va_list ap; + int result; + + va_start( ap, v_scale ); + result = vips_call_split( "affine", ap, in, out, h_scale, v_scale ); + va_end( ap ); + + return( result ); +} + + + diff --git a/libvips/resample/similarity.c b/libvips/resample/similarity.c index ffdb8ff2..d9f2c48e 100644 --- a/libvips/resample/similarity.c +++ b/libvips/resample/similarity.c @@ -1,8 +1,8 @@ -/* simple wrapper over vips_similarity() to make scale / rotate easy from the +/* simple wrapper over vips_affine() to make scale / rotate easy from the * command-line * * 3/10/13 - * - from similarity.c + * - from affine.c * 25/10/13 * - oops, reverse rotation direction to match the convention used in the * rest of vips