From d438649fcb77d03409dcff0866ce02f059d528af Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 2 Aug 2014 05:14:37 +0100 Subject: [PATCH 01/17] update notes --- TODO | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/TODO b/TODO index ca01dea0..91533aca 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,25 @@ -- affine makes black lines with interp window_offset > 1, see nohalo +- vipsthumbnail -t plus large -s seems to fail? + + change caching: + + - png and jpg interlaced write need to make a memory copy before writing, + they advertse as seq, they need to be seq + + - autorotate should make a mem copy if it find rotation is necessary + + - remove second vips_cache() - experiment with size down to two sizes above in vipsthumbnail how does this affect speed and sharpness? -- vipsthumbnail -t plus large -s seems to fail? + do shrink / VIPS_MAX( window_size / 2, 1 ) + + find shrink needs a copy of the interpolator: make a single interp during + startup + + + - bicubic adds noise to 255/255/255, why? try babe.jpg background From 004c98f296c5726b2ad06c09af2894034b0888f0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 3 Aug 2014 09:34:46 +0100 Subject: [PATCH 02/17] pngsave interlaced makes a copy of the image interlaced png is hard to write: you have to make seven passes over the image to make the whole file. It advertises itself as sequential, it needs to only ake a single pass. Therefore, it must take a copy in memory of the image to write. --- ChangeLog | 1 + TODO | 3 --- libvips/foreign/vips2jpeg.c | 1 + libvips/foreign/vipspng.c | 16 ++++++++++++---- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index c278702c..35b26af0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ - don't exit() on memleak detected, just warn - add "autocrop" option to openslide load - argh fix affine, again +- pngsave in interlaced mode make a copy of the whole image 4/7/14 started 7.40.4 - fix vips_rawsave_fd(), thanks aferrero2707 diff --git a/TODO b/TODO index 91533aca..c0688602 100644 --- a/TODO +++ b/TODO @@ -2,9 +2,6 @@ change caching: - - png and jpg interlaced write need to make a memory copy before writing, - they advertse as seq, they need to be seq - - autorotate should make a mem copy if it find rotation is necessary - remove second vips_cache() diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index 06e5a121..cc56b178 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -58,6 +58,7 @@ * - attach IPCT data (app13), thanks Gary * 2/10/13 Lovell Fuller * - add optimize_coding parameter + * - add progressive mode * 12/11/13 * - add "strip" option to remove all metadata * 13/11/13 diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index f563b03e..d40faa86 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -777,10 +777,18 @@ write_vips( Write *write, int compress, int interlace ) if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); - /* Check input image. + /* Check input image. If we are writing interlaced, we need to make 7 + * passes over the image. We advertise ourselves as seq, so to ensure + * we only suck once from upstream, switch to WIO. */ - if( vips_image_pio_input( in ) ) - return( -1 ); + if( interlace ) { + if( vips_image_wio_input( in ) ) + return( -1 ); + } + else { + if( vips_image_pio_input( in ) ) + return( -1 ); + } if( compress < 0 || compress > 9 ) { vips_error( "vips2png", "%s", _( "compress should be in [0,9]" ) ); @@ -813,7 +821,7 @@ write_vips( Write *write, int compress, int interlace ) in->Xsize, in->Ysize, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); - /* Set resolution. libpnbg uses pixels per meter. + /* Set resolution. libpng uses pixels per meter. */ png_set_pHYs( write->pPng, write->pInfo, VIPS_RINT( in->Xres * 1000 ), VIPS_RINT( in->Yres * 1000 ), From ebec31fe4a554d8e695be616d67a7d01a4772903 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 3 Aug 2014 17:59:25 +0100 Subject: [PATCH 03/17] more careful p->t conversion When we convert an image from "p" to "t" in vips_image_wio_input(), zap start/gen/stop callbacks. This makes vips_region_prepare_to() read from the "t" rather than trying to gen pixels again. --- TODO | 12 +++++++----- libvips/foreign/vipspng.c | 2 +- libvips/iofuncs/image.c | 18 ++++++++++++++++++ tools/vipsthumbnail.c | 27 +++++++++++---------------- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/TODO b/TODO index c0688602..d9f4a5f3 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,13 @@ -- vipsthumbnail -t plus large -s seems to fail? +- try: - change caching: + $ vipsthumbnail Chicago.png --vips-concurrency=1 + memory: high-water mark 180.57 MB + $ vipsthumbnail Chicago.png --vips-concurrency=4 + memory: high-water mark 180.57 MB - - autorotate should make a mem copy if it find rotation is necessary + shouldn't memuse drop with concurrency 1? - - remove second vips_cache() +- bicubic adds noise to 255/255/255, why? try babe.jpg background - experiment with size down to two sizes above in vipsthumbnail @@ -18,7 +21,6 @@ -- bicubic adds noise to 255/255/255, why? try babe.jpg background diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index d40faa86..c3a36f56 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -862,7 +862,7 @@ write_vips( Write *write, int compress, int interlace ) /* Write data. */ for( i = 0; i < nb_passes; i++ ) - if( vips_sink_disc( write->in, write_png_block, write ) ) + if( vips_sink_disc( in, write_png_block, write ) ) return( -1 ); /* The setjmp() was held by our background writer: reset it. diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 10972f26..a8b7e6ab 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -2781,6 +2781,24 @@ vips_image_wio_input( VipsImage *image ) */ g_object_unref( t1 ); + /* We need to zap any start/gen/stop callbacks. If we don't, + * calling vips_region_prepare_to() later to read from this + * image will fail, since it will think it need to create the + * image, not read from it. + */ + image->start_fn = NULL; + image->generate_fn = NULL; + image->stop_fn = NULL; + image->client1 = NULL; + image->client2 = NULL; + + /* ... and that may confuse any regions which are trying to + * generate from this image. + */ + if( image->regions ) + vips_warn( "vips_image_wio_input", "%s", + "rewinding image with active regions" ); + break; case VIPS_IMAGE_OPENIN: diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index ab411e7d..c9925265 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -570,12 +570,18 @@ thumbnail_crop( VipsObject *process, VipsImage *im ) static VipsImage * thumbnail_rotate( VipsObject *process, VipsImage *im ) { - VipsImage **t = (VipsImage **) vips_object_local_array( process, 1 ); + VipsImage **t = (VipsImage **) vips_object_local_array( process, 2 ); + VipsAngle angle = get_angle( im ); - if( rotate_image ) { - if( vips_rot( im, &t[0], get_angle( im ), NULL ) ) + if( rotate_image && + angle != VIPS_ANGLE_0 ) { + /* Need to copy to memory, we have to stay seq. + */ + t[0] = vips_image_new_memory(); + if( vips_image_write( im, t[0] ) || + vips_rot( t[0], &t[1], angle, NULL ) ) return( NULL ); - im = t[0]; + im = t[1]; (void) vips_image_remove( im, ORIENTATION ); } @@ -589,8 +595,6 @@ thumbnail_rotate( VipsObject *process, VipsImage *im ) static int thumbnail_write( VipsObject *process, VipsImage *im, const char *filename ) { - VipsImage **t = (VipsImage **) vips_object_local_array( process, 1 ); - char *file; char *p; char buf[FILENAME_MAX]; @@ -622,16 +626,7 @@ thumbnail_write( VipsObject *process, VipsImage *im, const char *filename ) g_free( file ); - /* We need to cache the whole of the thumbnail before we write it - * in case we are writing an interlaced image. Interlaced png (for - * example) will make 7 passes over the image during write. - */ - if( vips_tilecache( im, &t[0], - "threaded", TRUE, - "persistent", TRUE, - "max_tiles", -1, - NULL ) || - vips_image_write_to_file( t[0], output_name, NULL ) ) { + if( vips_image_write_to_file( im, output_name, NULL ) ) { g_free( output_name ); return( -1 ); } From f1df91eebbed65aab94481e4e3a1c17a26c3c95c Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 4 Aug 2014 09:55:18 +0100 Subject: [PATCH 04/17] vipsthumbnail defaults to bicubic + noshapen if bicubic is available --- ChangeLog | 2 ++ TODO | 11 +---------- tools/vipsthumbnail.c | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 35b26af0..c51532d2 100644 --- a/ChangeLog +++ b/ChangeLog @@ -5,6 +5,8 @@ - add "autocrop" option to openslide load - argh fix affine, again - pngsave in interlaced mode make a copy of the whole image +- vipsthumbnail shrinks to 1/2 window_size +- vipsthumbnail defaults to bicubic + nosharpen 4/7/14 started 7.40.4 - fix vips_rawsave_fd(), thanks aferrero2707 diff --git a/TODO b/TODO index d9f4a5f3..8daa35c0 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,4 @@ + - try: $ vipsthumbnail Chicago.png --vips-concurrency=1 @@ -9,16 +10,6 @@ - bicubic adds noise to 255/255/255, why? try babe.jpg background -- experiment with size down to two sizes above in vipsthumbnail - - how does this affect speed and sharpness? - - do shrink / VIPS_MAX( window_size / 2, 1 ) - - find shrink needs a copy of the interpolator: make a single interp during - startup - - diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index c9925265..14d3e908 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -51,6 +51,10 @@ * unlike the main image wrt. rotation / colour / etc. * 30/6/14 * - fix interlaced thumbnail output, thanks lovell + * 3/8/14 + * - box shrink less, use interpolator more, if the window_size is large + * enough + * - default to bicubic + nosharpen if bicubic is available */ #ifdef HAVE_CONFIG_H @@ -68,6 +72,10 @@ #define ORIENTATION ("exif-ifd0-Orientation") +/* Default settings. We change the default to bicubic + nosharpen in main() if + * this vips has been compiled with bicubic support. + */ + static char *thumbnail_size = "128"; static int thumbnail_width = 128; static int thumbnail_height = 128; @@ -170,14 +178,20 @@ get_angle( VipsImage *im ) * We shrink in two stages: first, a shrink with a block average. This can * only accurately shrink by integer factors. We then do a second shrink with * a supplied interpolator to get the exact size we want. + * + * We aim to do the second shrink by roughly half the interpolator's + * window_size. */ static int -calculate_shrink( VipsImage *im, double *residual ) +calculate_shrink( VipsImage *im, double *residual, + VipsInterpolate *interp ) { VipsAngle angle = get_angle( im ); gboolean rotate = angle == VIPS_ANGLE_90 || angle == VIPS_ANGLE_270; int width = rotate_image && rotate ? im->Ysize : im->Xsize; int height = rotate_image && rotate ? im->Xsize : im->Ysize; + const int window_size = + interp ? vips_interpolate_get_window_size( interp ) : 2; VipsDirection direction; @@ -211,9 +225,11 @@ calculate_shrink( VipsImage *im, double *residual ) */ double factor2 = factor < 1.0 ? 1.0 : factor; - /* Int component of shrink. + /* Int component of factor2. + * + * We want to shrink by less for interpolators with larger windows. */ - int shrink = floor( factor2 ); + int shrink = floor( factor2 ) / VIPS_MAX( window_size / 2, 1 ); if( residual && direction == VIPS_DIRECTION_HORIZONTAL ) { @@ -243,7 +259,7 @@ calculate_shrink( VipsImage *im, double *residual ) static int thumbnail_find_jpegshrink( VipsImage *im ) { - int shrink = calculate_shrink( im, NULL ); + int shrink = calculate_shrink( im, NULL, NULL ); /* We can't use pre-shrunk images in linear mode. libjpeg shrinks in Y * (of YCbCR), not linear space. @@ -324,7 +340,7 @@ thumbnail_interpolator( VipsObject *process, VipsImage *in ) double residual; VipsInterpolate *interp; - calculate_shrink( in, &residual ); + 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. @@ -430,7 +446,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in, return( NULL ); in = t[2]; - shrink = calculate_shrink( in, &residual ); + shrink = calculate_shrink( in, &residual, interp ); vips_info( "vipsthumbnail", "integer shrink by %d", shrink ); @@ -670,6 +686,14 @@ main( int argc, char **argv ) textdomain( GETTEXT_PACKAGE ); setlocale( LC_ALL, "" ); + /* Does this vips have bicubic? Default to that + nosharpen if it + * does. + */ + if( vips_type_find( "VipsInterpolate", "bicubic" ) ) { + interpolator = "bicubic"; + convolution_mask = "none"; + } + context = g_option_context_new( _( "- thumbnail generator" ) ); g_option_context_add_main_entries( context, options, GETTEXT_PACKAGE ); From e4996d8dce2e6d499efaa68e79a8be13cd9275bf Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Mon, 4 Aug 2014 21:25:19 +0100 Subject: [PATCH 05/17] don't set shrink too low --- libvips/resample/bicubic.cpp | 3 +++ tools/vipsthumbnail.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/libvips/resample/bicubic.cpp b/libvips/resample/bicubic.cpp index 1488f8c9..66bfa5b6 100644 --- a/libvips/resample/bicubic.cpp +++ b/libvips/resample/bicubic.cpp @@ -353,6 +353,7 @@ vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate, bicubic_int_tab( out, p, bands, lskip, cxi, cyi ); + /* Handy for benchmarking @@ -360,11 +361,13 @@ vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate, bicubic_float_tab( out, p, bands, lskip, cxf, cyf ); + bicubic_notab( out, p, bands, lskip, x - ix, y - iy ); */ + break; case VIPS_FORMAT_CHAR: diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index 14d3e908..75d4ee58 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -229,7 +229,8 @@ calculate_shrink( VipsImage *im, double *residual, * * We want to shrink by less for interpolators with larger windows. */ - int shrink = floor( factor2 ) / VIPS_MAX( window_size / 2, 1 ); + int shrink = VIPS_MAX( 1, + floor( factor2 ) / VIPS_MAX( 1, window_size / 2 ) ); if( residual && direction == VIPS_DIRECTION_HORIZONTAL ) { From ebeee822bcfdaf773e52ac76d421bef0bfd9bc64 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 5 Aug 2014 10:02:11 +0100 Subject: [PATCH 06/17] better rounding behaviour for bicubic fixed-point bicubic was not rounding to nearest, introducing some noise in flat areas --- ChangeLog | 1 + TODO | 7 +------ libvips/resample/templates.h | 15 ++++++++++----- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index c51532d2..0440e2c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,7 @@ - pngsave in interlaced mode make a copy of the whole image - vipsthumbnail shrinks to 1/2 window_size - vipsthumbnail defaults to bicubic + nosharpen +- better rounding behaviour for fixed-point bicubic 4/7/14 started 7.40.4 - fix vips_rawsave_fd(), thanks aferrero2707 diff --git a/TODO b/TODO index 8daa35c0..913a087f 100644 --- a/TODO +++ b/TODO @@ -6,12 +6,7 @@ $ vipsthumbnail Chicago.png --vips-concurrency=4 memory: high-water mark 180.57 MB - shouldn't memuse drop with concurrency 1? - -- bicubic adds noise to 255/255/255, why? try babe.jpg background - - - + shouldn't memuse drop with concurrency 1 ? diff --git a/libvips/resample/templates.h b/libvips/resample/templates.h index ee52dbf0..7cb0e99a 100644 --- a/libvips/resample/templates.h +++ b/libvips/resample/templates.h @@ -183,30 +183,35 @@ bicubic_int( (cx[0] * uno_one + cx[1] * uno_two + cx[2] * uno_thr + - cx[3] * uno_fou) >> VIPS_INTERPOLATE_SHIFT; + cx[3] * uno_fou + + (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT; const int r1 = (cx[0] * dos_one + cx[1] * dos_two + cx[2] * dos_thr + - cx[3] * dos_fou) >> VIPS_INTERPOLATE_SHIFT; + cx[3] * dos_fou + + (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT; const int r2 = (cx[0] * tre_one + cx[1] * tre_two + cx[2] * tre_thr + - cx[3] * tre_fou) >> VIPS_INTERPOLATE_SHIFT; + cx[3] * tre_fou + + (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT; const int r3 = (cx[0] * qua_one + cx[1] * qua_two + cx[2] * qua_thr + - cx[3] * qua_fou) >> VIPS_INTERPOLATE_SHIFT; + cx[3] * qua_fou + + (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT; return( (cy[0] * r0 + cy[1] * r1 + cy[2] * r2 + - cy[3] * r3) >> VIPS_INTERPOLATE_SHIFT ); + cy[3] * r3 + + (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT ); } /* Floating-point bicubic, used for int/float/double types. From 46673b60d746fc0c0072f39374e45c79048c0723 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 5 Aug 2014 10:09:13 +0100 Subject: [PATCH 07/17] small docs fixups --- TODO | 11 ----------- libvips/iofuncs/object.c | 10 ++++++---- libvips/iofuncs/operation.c | 20 ++++---------------- 3 files changed, 10 insertions(+), 31 deletions(-) diff --git a/TODO b/TODO index 913a087f..dd08db4b 100644 --- a/TODO +++ b/TODO @@ -1,15 +1,4 @@ -- try: - - $ vipsthumbnail Chicago.png --vips-concurrency=1 - memory: high-water mark 180.57 MB - $ vipsthumbnail Chicago.png --vips-concurrency=4 - memory: high-water mark 180.57 MB - - shouldn't memuse drop with concurrency 1 ? - - - - vips_object_unref_outputs() needs docs ... bindings will need it diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index b2a46903..675c904f 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -67,16 +67,18 @@ * following major features: * * Functional class creation Vips objects have a very - * regular - * lifecycle: initialise, build, use, destroy. They behave rather like + * regular lifecycle: initialise, build, use, destroy. They behave rather like * function calls and are free of side-effects. * * Run-time introspection Vips objects can be fully - * introspected at - * run-time. There is not need for separate source-code analysis. + * introspected at run-time. There is not need for separate source-code + * analysis. * * Command-line interface Any vips object can be run from * the command-line with the `vips` driver program. + * + * + * */ /** diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index 049c8859..79c8d4df 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -93,24 +93,12 @@ * 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(). :wq - * - - - * - * Use vips_call() to call any vips operation from C. If you want to search - * for operations, see what arguments they need, and test argument - * properties, see - * object. Each operation also has a - * wrapper function, of course, to give type safety for required arguments. - * - * vips_call_split() lets you run an operation with the optional and required - * arguments split into separate lists. vips_call_split_option_string() lets - * you set options from strings as well. + * vips_object_unref_outputs() and g_object_unref() to free the + * partially-built object. * * Use vips_call_argv() to run any vips operation from a command-line style - * argc/argv array. - * + * argc/argv array. This is the thing used by the vips main program to + * implement the vips command-line interface. */ /** From 78885263614434ed1901c64d2ea2f92f0b0990fc Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 5 Aug 2014 19:23:56 +0100 Subject: [PATCH 08/17] fix some compiler warnings various false positives from gcc -O3 flow analysis --- ChangeLog | 9 +++++---- libvips/convolution/spcor.c | 1 + libvips/foreign/radiance.c | 5 +++++ libvips/foreign/tiff2vips.c | 3 ++- libvips/iofuncs/util.c | 4 ++-- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 0440e2c0..1eb41b48 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,11 +3,12 @@ - limit n_thr on tiny images - don't exit() on memleak detected, just warn - add "autocrop" option to openslide load -- argh fix affine, again -- pngsave in interlaced mode make a copy of the whole image -- vipsthumbnail shrinks to 1/2 window_size +- 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 -- better rounding behaviour for fixed-point bicubic +- better rounding behaviour for fixed-point bicubic reduces noise 4/7/14 started 7.40.4 - fix vips_rawsave_fd(), thanks aferrero2707 diff --git a/libvips/convolution/spcor.c b/libvips/convolution/spcor.c index edf854b7..ddcc6071 100644 --- a/libvips/convolution/spcor.c +++ b/libvips/convolution/spcor.c @@ -262,6 +262,7 @@ vips_spcor_correlation( VipsCorrelation *correlation, default: g_assert( 0 ); + return; } c2 = sqrt( sum2 ); diff --git a/libvips/foreign/radiance.c b/libvips/foreign/radiance.c index d52e0ab8..3df317fb 100644 --- a/libvips/foreign/radiance.c +++ b/libvips/foreign/radiance.c @@ -827,6 +827,11 @@ scanline_write( COLR *scanline, int width, FILE *fp ) for( i = 0; i < 4; i++ ) { for( j = 0; j < width; ) { + /* Not needed, but keeps gcc used-before-set wsrning + * quiet. + */ + cnt = 1; + /* Set beg / cnt to the start and length of the next * run longer than MINRUN. */ diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index dfbd2703..6e39a5f7 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -1053,7 +1053,8 @@ parse_header( ReadTiff *rtiff, VipsImage *out ) if( tfexists( rtiff->tiff, TIFFTAG_PLANARCONFIG ) ) { int v; - tfget16( rtiff->tiff, TIFFTAG_PLANARCONFIG, &v ); + if( !tfget16( rtiff->tiff, TIFFTAG_PLANARCONFIG, &v ) ) + return( -1 ); if( v == PLANARCONFIG_SEPARATE ) rtiff->separate = TRUE; } diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index 6166db13..4c9da487 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -1599,8 +1599,7 @@ vips_enum_from_nick( const char *domain, GType type, const char *nick ) #define BIGBUF (10000) /* Scan @buf for the first "%ns" (eg. "%12s") and substitute the - * lowest-numbered one for @sub. @buf is @len bytes - * in size. + * lowest-numbered one for @sub. @buf is @len bytes in size. * * If there are no %ns, use the first %s. */ @@ -1620,6 +1619,7 @@ vips__substitute( const char *domain, char *buf, size_t len, char *sub ) lowest_n = -1; sub_start = NULL; + sub_end = NULL; for( p = buf; (p = strchr( p, '%' )); p++ ) if( isdigit( p[1] ) ) { char *q; From 2e3baa1befd766179a1b0151dd0454f33e6bde0f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 6 Aug 2014 21:59:13 +0100 Subject: [PATCH 09/17] more docs stuff --- TODO | 2 + doc/reference/using-C.xml | 4 +- doc/reference/using-command-line.xml | 2 +- libvips/iofuncs/object.c | 109 +++++++++++++++++++++++++++ libvips/iofuncs/operation.c | 60 ++++++++++++++- 5 files changed, 173 insertions(+), 4 deletions(-) diff --git a/TODO b/TODO index dd08db4b..647d33f3 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,5 @@ +- bicubic needs a signed int lut path with rounding in the other direction + for negatives - vips_object_unref_outputs() needs docs ... bindings will need it diff --git a/doc/reference/using-C.xml b/doc/reference/using-C.xml index ed3b904f..bd5d2548 100644 --- a/doc/reference/using-C.xml +++ b/doc/reference/using-C.xml @@ -4,14 +4,14 @@ ]> - Using VIPS from C + VIPS from C 3 VIPS Library Using VIPS - How to use the VIPS library + How to use the VIPS library from C diff --git a/doc/reference/using-command-line.xml b/doc/reference/using-command-line.xml index b097a166..e5311820 100644 --- a/doc/reference/using-command-line.xml +++ b/doc/reference/using-command-line.xml @@ -11,7 +11,7 @@ Using VIPS - How to use the VIPS library + How to use the VIPS library from the command-line diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index 675c904f..d9b2f3d9 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -77,7 +77,85 @@ * Command-line interface Any vips object can be run from * the command-line with the `vips` driver program. * + * ## The #VipsObject lifecycle * + * #VipsObject s have a strictly defined lifecycle, split broadly as construct + * and then use. In detail, the stages are: + * + * 1. g_object_new(). The #VipsObject is created with g_object_new(). Objects + * in this state are blank slates and need to have their various parameters + * set. + * + * 2. g_object_set(). You loop over the #VipsArgument that the object has + * defined with vips_argument_map(). Arguments have a set of flags attached to + * them for required, optional, input, output, type, and so on. You must set + * all required arguments. + * + * 3. vips_object_build(). Call this to construct the object and get it ready + * for use. Building an object happens in four stages, see below. + * + * 4. g_object_get(). The object has now been built. You can read out any + * computed values. + * + * 5. g_object_unref(). When you are done with an object, you can unref it. + * See the section on reference counting for an explanation of the convention + * that #VipsObject uses. When the last ref to an object is released, the + * object is closed. Objects close in three stages, see below. + * + * The stages inside vips_object_build() are: + * + * 1. Chain up through the object's @build class methods. At each stage, + * each object does any initial setup and checking, then chains up to its + * superclass. + * + * 2. The innermost @build method in #VipsObject itself checks that all input + * arguments have been set and then returns. + * + * 3. All object @build methods now finish executing, from innermost to + * outermost. They know all input arguments have been checked and supplied, so + * now they set all output arguments. + * + * 4. vips_object_build() finishes the process by checking that all output + * objects have been set, and then triggering the #VipsObject::postbuild + * signal. #VipsObject::postbuild only runs if the object has constructed + * successfuly. + * + * And the stages inside close are: + * + * 1. #VipsObject::preclose. This is emitted at the start of + * the #VipsObject dispose. The object is still functioning. + * + * 2. #VipsObject::close. This runs just after all #VipsArgument held by + * the object have been released. + * + * 3. #VipsObject::postclose. This runs right at the end. The object + * pointer is still valid, but nothing else is. + * + * ## The #VipsOperation cache + * + * Because all #VipsObject are immutable, they can be cached. The cache is + * very simple to use: instead of calling vips_object_build(), instead call + * vips_cache_operation_build(). This function calculates a hash from the + * operations's input arguments and looks it up in table of all recent + * operations. If there's a hit, the new operation is unreffed, the old + * operation reffed, and the old operation returned in place of the new one. + * + * The cache size is controlled with vips_cache_set_max() and friends. + * + * ## The #VipsObject reference counting convention + * + * #VipsObject has a set of conventions to simplify reference counting. + * + * 1. All input %GObject have a ref added to them, owned by the object. When a + * #VipsObject is unreffed, all of these refs to input objects are + * automatically dropped. + * + * 2. All output %GObject hold a ref to the object. When a %GObject which is an + * output of a #VipsObject is disposed, it must drop this reference. + * #VipsObject which are outputs of other #VipsObject will do this + * automatically. + * + * See #VipsOperation for an example of #VipsObject reference counting. * */ @@ -1504,6 +1582,13 @@ vips_object_class_init( VipsObjectClass *class ) G_STRUCT_OFFSET( VipsObject, description ), "" ); + /** + * VipsObject::postbuild: + * @object: the object that has been built + * + * The ::postbuild signal is emitted once just after successful object + * construction. Return non-zero to cause object construction to fail. + */ vips_object_signals[SIG_POSTBUILD] = g_signal_new( "postbuild", G_TYPE_FROM_CLASS( class ), G_SIGNAL_RUN_LAST, @@ -1511,6 +1596,14 @@ vips_object_class_init( VipsObjectClass *class ) NULL, NULL, vips_INT__VOID, G_TYPE_INT, 0 ); + + /** + * VipsObject::preclose: + * @object: the object that is to close + * + * The ::preclose signal is emitted once just before object close + * starts. The oject is still alive. + */ vips_object_signals[SIG_PRECLOSE] = g_signal_new( "preclose", G_TYPE_FROM_CLASS( class ), G_SIGNAL_RUN_LAST, @@ -1518,6 +1611,14 @@ vips_object_class_init( VipsObjectClass *class ) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); + + /** + * VipsObject::close: + * @object: the object that is closing + * + * The ::close signal is emitted once during object close. The object + * is dying and may not work. + */ vips_object_signals[SIG_CLOSE] = g_signal_new( "close", G_TYPE_FROM_CLASS( class ), G_SIGNAL_RUN_LAST, @@ -1525,6 +1626,14 @@ vips_object_class_init( VipsObjectClass *class ) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); + + /** + * VipsObject::postclose: + * @object: the object that has closed + * + * The ::postclose signal is emitted once after object close. The + * object pointer is still valid, but nothing else. + */ vips_object_signals[SIG_POSTCLOSE] = g_signal_new( "postclose", G_TYPE_FROM_CLASS( class ), G_SIGNAL_RUN_LAST, diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index 79c8d4df..2806490f 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -94,11 +94,69 @@ * 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. + * partially-built object. See + * binding for an introduction to libvips + * introspection. * * Use vips_call_argv() to run any vips operation from a command-line style * argc/argv array. This is the thing used by the vips main program to * implement the vips command-line interface. + * + * ## #VipsOperation and reference counting + * + * After calling a #VipsOperation you are responsible for unreffing any output + * objects. For example, consider: + * + * |[ + * VipsImage *im = ...; + * VipsImage *t1; + * + * if (vips_invert (im, &t1, NULL)) + * error .. + * ]| + * + * This will invert @im and return it as a new #VipsImage, @t1. As the caller + * of vips_invert(), you are responsible for @t1 and must unref it when you no + * longer need it. If vips_invert() fails, no @t1 is returned and you don't + * need to do anything. + * + * Consider running two operations, one after the other. You could write: + * + * |[ + * VipsImage *im = ...; + * VipsImage *t1, *t2; + * + * if (vips_invert (im, &t1, NULL)) { + * g_object_unref (im); + * return -1; + * } + * g_object_unref (im); + * + * if (vips_flip (t1, &t2, VIPS_DIRECTION_HORIZONTAL, NULL)) { + * g_object_unref (t1); + * return -1; + * } + * g_object_unref (t1); + * ]| + * + * This is correct, but rather long-winded. libvips provides a handy thing to + * make a vector of auto-freeing object references. You can write this as: + * + * |[ + * VipsObject *parent = ...; + * VipsImage *im = ...; + * VipsImage *t = (VipsImage **) vips_object_local_array (parent, 2); + * + * if (vips_invert (im, &t[0], NULL) || + * vips_flip (t[0], &t[1], VIPS_DIRECTION_HORIZONTAL, NULL)) + * return -1; + * ]| + * + * where @parent is some enclosing object which will be unreffed when this + * task is complete. vips_object_local_array() makes an array of #VipsObject + * (or #VipsImage, in this case) where when @parent is freed, all non-NULL + * #VipsObject in the array are also unreffed. + * */ /** From 2b086cfe6ead1ae921dd58b69e0102f5780d8c99 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 7 Aug 2014 10:13:08 +0100 Subject: [PATCH 10/17] add signed int bicubic path better rounding for 8- and 16-bit signed int bicubic --- TODO | 2 - libvips/resample/bicubic.cpp | 81 ++++++++++++++++++++++--- libvips/resample/templates.h | 114 ++++++++++++++++++++++++++--------- 3 files changed, 158 insertions(+), 39 deletions(-) diff --git a/TODO b/TODO index 647d33f3..dd08db4b 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -- bicubic needs a signed int lut path with rounding in the other direction - for negatives - vips_object_unref_outputs() needs docs ... bindings will need it diff --git a/libvips/resample/bicubic.cpp b/libvips/resample/bicubic.cpp index 66bfa5b6..238bf991 100644 --- a/libvips/resample/bicubic.cpp +++ b/libvips/resample/bicubic.cpp @@ -104,9 +104,10 @@ G_DEFINE_TYPE( VipsInterpolateBicubic, vips_interpolate_bicubic, /* Fixed-point version, for 8 and 16-bit types. */ -template + +template static void inline -bicubic_int_tab( void *pout, const VipsPel *pin, +bicubic_unsigned_int_tab( void *pout, const VipsPel *pin, const int bands, const int lskip, const int *cx, const int *cy ) { @@ -152,7 +153,73 @@ bicubic_int_tab( void *pout, const VipsPel *pin, const T qua_thr = in[l3_plus_b2]; const T qua_fou = in[l3_plus_b3]; - int bicubic = bicubic_int( + int bicubic = bicubic_unsigned_int( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + if( bicubic < 0 ) + bicubic = 0; + else if( bicubic > max_value ) + bicubic = max_value; + + out[z] = bicubic; + + in += 1; + } +} + +template +static void inline +bicubic_signed_int_tab( void *pout, const VipsPel *pin, + const int bands, const int lskip, + const int *cx, const int *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + const int l1_plus_b1 = l1 + b1; + const int l1_plus_b2 = l1 + b2; + const int l1_plus_b3 = l1 + b3; + const int l2_plus_b1 = l2 + b1; + const int l2_plus_b2 = l2 + b2; + const int l2_plus_b3 = l2 + b3; + const int l3_plus_b1 = l3 + b1; + const int l3_plus_b2 = l3 + b2; + const int l3_plus_b3 = l3 + b3; + + for( int z = 0; z < bands; z++ ) { + const T uno_one = in[0]; + const T uno_two = in[b1]; + const T uno_thr = in[b2]; + const T uno_fou = in[b3]; + + const T dos_one = in[l1]; + const T dos_two = in[l1_plus_b1]; + const T dos_thr = in[l1_plus_b2]; + const T dos_fou = in[l1_plus_b3]; + + const T tre_one = in[l2]; + const T tre_two = in[l2_plus_b1]; + const T tre_thr = in[l2_plus_b2]; + const T tre_fou = in[l2_plus_b3]; + + const T qua_one = in[l3]; + const T qua_two = in[l3_plus_b1]; + const T qua_thr = in[l3_plus_b2]; + const T qua_fou = in[l3_plus_b3]; + + int bicubic = bicubic_signed_int( uno_one, uno_two, uno_thr, uno_fou, dos_one, dos_two, dos_thr, dos_fou, tre_one, tre_two, tre_thr, tre_fou, @@ -350,7 +417,7 @@ vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate, switch( in->im->BandFmt ) { case VIPS_FORMAT_UCHAR: - bicubic_int_tab( + bicubic_unsigned_int_tab( out, p, bands, lskip, cxi, cyi ); @@ -371,19 +438,19 @@ vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate, break; case VIPS_FORMAT_CHAR: - bicubic_int_tab( + bicubic_signed_int_tab( out, p, bands, lskip, cxi, cyi ); break; case VIPS_FORMAT_USHORT: - bicubic_int_tab( + bicubic_unsigned_int_tab( out, p, bands, lskip, cxi, cyi ); break; case VIPS_FORMAT_SHORT: - bicubic_int_tab( + bicubic_signed_int_tab( out, p, bands, lskip, cxi, cyi ); break; diff --git a/libvips/resample/templates.h b/libvips/resample/templates.h index 7cb0e99a..4ac9027c 100644 --- a/libvips/resample/templates.h +++ b/libvips/resample/templates.h @@ -169,49 +169,103 @@ bilinear_nosign( * Bicubic (Catmull-Rom) interpolation templates: */ +static int inline +unsigned_fixed_round( int v ) +{ + const int round_by = VIPS_INTERPOLATE_SCALE >> 1; + + return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT ); +} + /* Fixed-point integer bicubic, used for 8 and 16-bit types. */ template static int inline -bicubic_int( +bicubic_unsigned_int( const T uno_one, const T uno_two, const T uno_thr, const T uno_fou, const T dos_one, const T dos_two, const T dos_thr, const T dos_fou, const T tre_one, const T tre_two, const T tre_thr, const T tre_fou, const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, const int* restrict cx, const int* restrict cy ) { - const int r0 = - (cx[0] * uno_one + - cx[1] * uno_two + - cx[2] * uno_thr + - cx[3] * uno_fou + - (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT; + const int r0 = unsigned_fixed_round( + cx[0] * uno_one + + cx[1] * uno_two + + cx[2] * uno_thr + + cx[3] * uno_fou ); - const int r1 = - (cx[0] * dos_one + - cx[1] * dos_two + - cx[2] * dos_thr + - cx[3] * dos_fou + - (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT; + const int r1 = unsigned_fixed_round( + cx[0] * dos_one + + cx[1] * dos_two + + cx[2] * dos_thr + + cx[3] * dos_fou ); - const int r2 = - (cx[0] * tre_one + - cx[1] * tre_two + - cx[2] * tre_thr + - cx[3] * tre_fou + - (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT; + const int r2 = unsigned_fixed_round( + cx[0] * tre_one + + cx[1] * tre_two + + cx[2] * tre_thr + + cx[3] * tre_fou ); - const int r3 = - (cx[0] * qua_one + - cx[1] * qua_two + - cx[2] * qua_thr + - cx[3] * qua_fou + - (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT; + const int r3 = unsigned_fixed_round( + cx[0] * qua_one + + cx[1] * qua_two + + cx[2] * qua_thr + + cx[3] * qua_fou ); - return( (cy[0] * r0 + - cy[1] * r1 + - cy[2] * r2 + - cy[3] * r3 + - (VIPS_INTERPOLATE_SCALE >> 1)) >> VIPS_INTERPOLATE_SHIFT ); + return( unsigned_fixed_round( + cy[0] * r0 + + cy[1] * r1 + + cy[2] * r2 + + cy[3] * r3 ) ); +} + +static int inline +signed_fixed_round( int v ) +{ + const int sign_of_v = 2 * (v > 0) - 1; + const int round_by = sign_of_v * (VIPS_INTERPOLATE_SCALE >> 1); + + return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT ); +} + +/* Fixed-point integer bicubic, used for 8 and 16-bit types. + */ +template static int inline +bicubic_signed_int( + const T uno_one, const T uno_two, const T uno_thr, const T uno_fou, + const T dos_one, const T dos_two, const T dos_thr, const T dos_fou, + const T tre_one, const T tre_two, const T tre_thr, const T tre_fou, + const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, + const int* restrict cx, const int* restrict cy ) +{ + const int r0 = signed_fixed_round( + cx[0] * uno_one + + cx[1] * uno_two + + cx[2] * uno_thr + + cx[3] * uno_fou ); + + const int r1 = signed_fixed_round( + cx[0] * dos_one + + cx[1] * dos_two + + cx[2] * dos_thr + + cx[3] * dos_fou ); + + const int r2 = signed_fixed_round( + cx[0] * tre_one + + cx[1] * tre_two + + cx[2] * tre_thr + + cx[3] * tre_fou ); + + const int r3 = signed_fixed_round( + cx[0] * qua_one + + cx[1] * qua_two + + cx[2] * qua_thr + + cx[3] * qua_fou ); + + return( signed_fixed_round( + cy[0] * r0 + + cy[1] * r1 + + cy[2] * r2 + + cy[3] * r3 ) ); } /* Floating-point bicubic, used for int/float/double types. From 78a50f6ec5416145ff9ef3b4185b5a2346c19226 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 8 Aug 2014 17:34:30 +0100 Subject: [PATCH 11/17] more docs stuff --- doc/reference/binding.xml | 2 +- doc/reference/extending.xml | 16 +++++++++++++++- libvips/iofuncs/object.c | 13 +++++-------- libvips/iofuncs/operation.c | 25 +++++++++++-------------- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/doc/reference/binding.xml b/doc/reference/binding.xml index 37e19810..ffe5c4af 100644 --- a/doc/reference/binding.xml +++ b/doc/reference/binding.xml @@ -17,7 +17,7 @@ Binding and gobject-introspection - Stuff about goi. + Write this section once the vips8 Python binding is done. diff --git a/doc/reference/extending.xml b/doc/reference/extending.xml index 9ab727e6..faba973d 100644 --- a/doc/reference/extending.xml +++ b/doc/reference/extending.xml @@ -17,8 +17,22 @@ Adding operations to VIPS - all about subclassing. + All about subclassing. Copy-paste from the blog post about extending + vips8. + + +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/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index d9b2f3d9..59ccb348 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -131,16 +131,13 @@ * 3. #VipsObject::postclose. This runs right at the end. The object * pointer is still valid, but nothing else is. * - * ## The #VipsOperation cache + * ## #VipsArgument * - * Because all #VipsObject are immutable, they can be cached. The cache is - * very simple to use: instead of calling vips_object_build(), instead call - * vips_cache_operation_build(). This function calculates a hash from the - * operations's input arguments and looks it up in table of all recent - * operations. If there's a hit, the new operation is unreffed, the old - * operation reffed, and the old operation returned in place of the new one. + * libvips has a simple mechanism for automating at least some aspects of + * %GObject properties. You add a set of macros to your _class_init() which + * describe the arguments, and set the get and set functions to the vips ones. * - * The cache size is controlled with vips_cache_set_max() and friends. + * See extending for a complete example. * * ## The #VipsObject reference counting convention * diff --git a/libvips/iofuncs/operation.c b/libvips/iofuncs/operation.c index 2806490f..3f124396 100644 --- a/libvips/iofuncs/operation.c +++ b/libvips/iofuncs/operation.c @@ -60,8 +60,7 @@ * on #VipsObject to provide the introspection and command-line interface to * libvips. * - * It also maintains a cache of recent operations. You can tune the cache - * behaviour in various ways, see vips_cache_set_max() and friends. + * It also maintains a cache of recent operations. See below. * * vips_call(), vips_call_split() and vips_call_split_option_string() are used * by vips to implement the C API. They can execute any #VipsOperation, @@ -86,18 +85,6 @@ * } * ]| * - * 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. See - * binding for an introduction to libvips - * introspection. - * * Use vips_call_argv() to run any vips operation from a command-line style * argc/argv array. This is the thing used by the vips main program to * implement the vips command-line interface. @@ -157,6 +144,16 @@ * (or #VipsImage, in this case) where when @parent is freed, all non-NULL * #VipsObject in the array are also unreffed. * + * ## The #VipsOperation cache + * + * Because all #VipsObject are immutable, they can be cached. The cache is + * very simple to use: instead of calling vips_object_build(), instead call + * vips_cache_operation_build(). This function calculates a hash from the + * operations's input arguments and looks it up in table of all recent + * operations. If there's a hit, the new operation is unreffed, the old + * operation reffed, and the old operation returned in place of the new one. + * + * The cache size is controlled with vips_cache_set_max() and friends. */ /** From 2e8217338b7ae56dacc3d10cf19013355fa60ff2 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 9 Aug 2014 10:25:34 +0100 Subject: [PATCH 12/17] docs work --- TODO | 4 +++- libvips/iofuncs/buffer.c | 17 +++++++++++++++-- libvips/iofuncs/init.c | 15 +++++++++------ libvips/iofuncs/type.c | 10 ++++++++++ libvips/iofuncs/vips.c | 15 +++++++++++++++ 5 files changed, 52 insertions(+), 9 deletions(-) diff --git a/TODO b/TODO index dd08db4b..ca19f927 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,9 @@ - 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 @@ -13,7 +15,7 @@ need better vipsobject docs - gtk-doc now used markdown, phew, use this to expand some sections + gtk-doc now uses markdown, phew, use this to expand some sections diff --git a/libvips/iofuncs/buffer.c b/libvips/iofuncs/buffer.c index 4067bb5f..d5a6fd1a 100644 --- a/libvips/iofuncs/buffer.c +++ b/libvips/iofuncs/buffer.c @@ -584,9 +584,22 @@ vips__buffer_init_cb( VipsBufferThread *buffer_thread ) { /* This is a mem leak, not catastrophic. */ + + /* Threads (including the main thread) must call + * vips_thread_shutdown() before exiting. Check that they have. + * + * We can't save automatically, because the shutdown order is + * important. We must free all memory before saving the thread + * profile, for example. + * + * We can't do the freeing in this callback since GPrivate has already + * stopped working. + */ + vips_warn( "VipsBuffer", - _( "vips_thread_shutdown() not called for thread %p" ), - g_thread_self() ); + _( "vips_thread_shutdown() not called for thread %p, see %s" ), + g_thread_self(), + "https://github.com/jcupitt/ruby-vips/issues/55" ); } /* Init the buffer cache system. diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index 3cf907e9..9c10910a 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -127,6 +127,9 @@ vips_get_argv0( void ) * it may not be able to get hold of @argv0 and VIPS may therefore be unable * to find its data files. It is much better to call this function yourself. * + * vips_init() is a macro, since it tries to check binary compatibility + * between the caller and the library. + * * vips_init() does approximately the following: * * @@ -157,14 +160,14 @@ vips_get_argv0( void ) * Example: * * |[ - * int main( int argc, char **argv ) + * int main (int argc, char **argv) * { - * if( vips_init( argv[0] ) ) - * vips_error_exit( "unable to start VIPS" ); + * if (vips_init (argv[0])) + * vips_error_exit ("unable to start VIPS"); * - * vips_shutdown(); + * vips_shutdown (); * - * return( 0 ); + * return 0; * } * ]| * @@ -174,7 +177,7 @@ vips_get_argv0( void ) * Returns: 0 on success, -1 otherwise */ -/* vips_init() is actually a macro which checks library and application +/* vips_init() is a macro which checks library and application * compatibility before calling vips__init(). */ diff --git a/libvips/iofuncs/type.c b/libvips/iofuncs/type.c index 589707e4..1e9c6f34 100644 --- a/libvips/iofuncs/type.c +++ b/libvips/iofuncs/type.c @@ -55,6 +55,16 @@ #include #include +/** + * SECTION: type + * @short_description: basic types + * @stability: Stable + * @see_also: operation + * @include: vips/vips.h + * + * A selection of GType defintions used by VIPS. + */ + /* A very simple boxed type for testing. Just an int. */ diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c index 1bd52c65..ed80b20f 100644 --- a/libvips/iofuncs/vips.c +++ b/libvips/iofuncs/vips.c @@ -88,6 +88,21 @@ #include #include +/** + * SECTION: vips + * @short_description: startup, shutdown, version + * @stability: Stable + * @see_also: operation + * @include: vips/vips.h + * + * Start VIPS up, shut VIPS down, get version information, relocation. + * + * VIPS is a relocatable package, meaning you can move the directory tree you + * compiled it to at runtime and it will still be able to find all data files. + * This is required for OS X and Windows, but slightly unusual in the Unix + * world. See vips_init() and vips_guess_prefix(). + */ + /* Try to make an O_BINARY ... sometimes need the leading '_'. */ #ifdef BINARY_OPEN From 296eb8b54ec28e06976a274e5de86d33c0060a1b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 9 Aug 2014 17:14:49 +0100 Subject: [PATCH 13/17] 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 14/17] 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 15/17] 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 16/17] 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 17/17] 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().