From 271d8656e919d5ab7823c5e226e52e0b1740dc9e Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sat, 20 Aug 2016 12:59:41 +0100 Subject: [PATCH] use round() rather than rint() where appropriate rint() rounds to nearest even, rather than nearest ... in some cases, like geometry transforms, we want strict nearest --- ChangeLog | 5 +++-- libvips/colour/Lab2LabQ.c | 6 ++---- libvips/conversion/arrayjoin.c | 10 +--------- libvips/conversion/zoom.c | 24 ++++++++---------------- libvips/create/perlin.c | 14 ++++---------- libvips/create/worley.c | 14 ++++---------- libvips/foreign/dzsave.c | 14 ++++---------- libvips/foreign/vips2tiff.c | 10 +--------- libvips/include/vips/util.h | 22 +++++++++++++++++++--- libvips/iofuncs/threadpool.c | 10 +--------- libvips/resample/reduceh.cpp | 5 +++-- libvips/resample/reducev.cpp | 5 +++-- libvips/resample/shrinkh.c | 5 +++-- libvips/resample/shrinkv.c | 5 +++-- libvips/resample/transform.c | 8 ++++---- 15 files changed, 63 insertions(+), 94 deletions(-) diff --git a/ChangeLog b/ChangeLog index cb2f0767..71963b98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -37,8 +37,9 @@ - support --strip for pngsave - add svgz support [Felix Bünemann] - rename boostrap.sh -> autogen.sh to help snapcraft -- resize/reduce/shrink now round output size to nearest rather than rounding - down, thanks ioquatix +- added VIPS_ROUND as well as VIPS_RINT +- resize/reduce*/shrink*/affine now round output size to nearest rather than + rounding down, thanks ioquatix 30/7/16 started 8.3.3 - fix performance regression in 8.3.2, thanks Lovell diff --git a/libvips/colour/Lab2LabQ.c b/libvips/colour/Lab2LabQ.c index 01d250c4..f8d3df26 100644 --- a/libvips/colour/Lab2LabQ.c +++ b/libvips/colour/Lab2LabQ.c @@ -95,11 +95,9 @@ vips_Lab2LabQ_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width ) int lsbs; int intv; - /* Scale L up to 10 bits. Add 0.5 rather than call VIPS_RINT - * for speed. This will not round negatives correctly! But - * this does not matter, since L is >0. L*=100.0 -> 1023. + /* Scale L up to 10 bits. */ - intv = 10.23 * p[0] + 0.5; /* scale L up to 10 bits */ + intv = VIPS_ROUND_UINT( 10.23 * p[0] ); intv = VIPS_CLIP( 0, intv, 1023 ); lsbs = (intv & 0x3) << 6; /* 00000011 -> 11000000 */ q[0] = intv >> 2; /* drop bot 2 bits and store */ diff --git a/libvips/conversion/arrayjoin.c b/libvips/conversion/arrayjoin.c index a4536a43..35370c7d 100644 --- a/libvips/conversion/arrayjoin.c +++ b/libvips/conversion/arrayjoin.c @@ -50,14 +50,6 @@ #include "pconversion.h" -/* Round N down to P boundary. - */ -#define ROUND_DOWN( N, P ) ((N) - ((N) % P)) - -/* Round N up to P boundary. - */ -#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) )) - typedef struct _VipsArrayjoin { VipsConversion parent_instance; @@ -179,7 +171,7 @@ vips_arrayjoin_build( VipsObject *object ) /* How many images down the grid? */ - join->down = ROUND_UP( n, join->across ) / join->across; + join->down = VIPS_ROUND_UP( n, join->across ) / join->across; /* The output size. */ diff --git a/libvips/conversion/zoom.c b/libvips/conversion/zoom.c index c41d4e05..8421b091 100644 --- a/libvips/conversion/zoom.c +++ b/libvips/conversion/zoom.c @@ -98,14 +98,6 @@ typedef VipsConversionClass VipsZoomClass; G_DEFINE_TYPE( VipsZoom, vips_zoom, VIPS_TYPE_CONVERSION ); -/* Round N down to P boundary. - */ -#define ROUND_DOWN( N, P ) ((N) - ((N) % P)) - -/* Round N up to P boundary. - */ -#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) )) - /* Paint the part of the region containing only whole pels. */ static void @@ -265,10 +257,10 @@ vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) /* Area of input we need. We have to round out, as we may have * part-pixels all around the edges. */ - left = ROUND_DOWN( r->left, zoom->xfac ); - right = ROUND_UP( ri, zoom->xfac ); - top = ROUND_DOWN( r->top, zoom->yfac ); - bottom = ROUND_UP( bo, zoom->yfac ); + left = VIPS_ROUND_DOWN( r->left, zoom->xfac ); + right = VIPS_ROUND_UP( ri, zoom->xfac ); + top = VIPS_ROUND_DOWN( r->top, zoom->yfac ); + bottom = VIPS_ROUND_UP( bo, zoom->yfac ); width = right - left; height = bottom - top; s.left = left / zoom->xfac; @@ -280,10 +272,10 @@ vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) /* Find the part of the output (if any) which uses only whole pels. */ - left = ROUND_UP( r->left, zoom->xfac ); - right = ROUND_DOWN( ri, zoom->xfac ); - top = ROUND_UP( r->top, zoom->yfac ); - bottom = ROUND_DOWN( bo, zoom->yfac ); + left = VIPS_ROUND_UP( r->left, zoom->xfac ); + right = VIPS_ROUND_DOWN( ri, zoom->xfac ); + top = VIPS_ROUND_UP( r->top, zoom->yfac ); + bottom = VIPS_ROUND_DOWN( bo, zoom->yfac ); width = right - left; height = bottom - top; diff --git a/libvips/create/perlin.c b/libvips/create/perlin.c index e006a5a6..7d535794 100644 --- a/libvips/create/perlin.c +++ b/libvips/create/perlin.c @@ -71,14 +71,6 @@ typedef struct _VipsPerlinClass { G_DEFINE_TYPE( VipsPerlin, vips_perlin, VIPS_TYPE_CREATE ); -/* Round N down to P boundary. - */ -#define ROUND_DOWN( N, P ) ((N) - ((N) % P)) - -/* Round N up to P boundary. - */ -#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) )) - /* cos and sin from an angle in 0 - 255. */ float vips_perlin_cos[256]; @@ -261,9 +253,11 @@ vips_perlin_build( VipsObject *object ) /* Be careful if width is a multiple of cell_size. */ - perlin->cells_across = ROUND_UP( perlin->width, perlin->cell_size ) / + perlin->cells_across = + VIPS_ROUND_UP( perlin->width, perlin->cell_size ) / perlin->cell_size; - perlin->cells_down = ROUND_UP( perlin->height, perlin->cell_size ) / + perlin->cells_down = + VIPS_ROUND_UP( perlin->height, perlin->cell_size ) / perlin->cell_size; perlin->seed = g_random_double() * 0xffffffffu; diff --git a/libvips/create/worley.c b/libvips/create/worley.c index 514b7258..bdd30fbd 100644 --- a/libvips/create/worley.c +++ b/libvips/create/worley.c @@ -75,14 +75,6 @@ G_DEFINE_TYPE( VipsWorley, vips_worley, VIPS_TYPE_CREATE ); #define MAX_FEATURES (10) -/* Round N down to P boundary. - */ -#define ROUND_DOWN( N, P ) ((N) - ((N) % P)) - -/* Round N up to P boundary. - */ -#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) )) - typedef struct _Cell { /* Cell position, in number of cells. Scale by cell_size to get * absolute image cods. @@ -294,9 +286,11 @@ vips_worley_build( VipsObject *object ) /* Be careful if width is a multiple of cell_size. */ - worley->cells_across = ROUND_UP( worley->width, worley->cell_size ) / + worley->cells_across = + VIPS_ROUND_UP( worley->width, worley->cell_size ) / worley->cell_size; - worley->cells_down = ROUND_UP( worley->height, worley->cell_size ) / + worley->cells_down = + VIPS_ROUND_UP( worley->height, worley->cell_size ) / worley->cell_size; worley->seed = g_random_double() * 0xffffffffu; diff --git a/libvips/foreign/dzsave.c b/libvips/foreign/dzsave.c index 8370de20..c51a8086 100644 --- a/libvips/foreign/dzsave.c +++ b/libvips/foreign/dzsave.c @@ -285,14 +285,6 @@ vips__make_xml_metadata( const char *domain, VipsImage *image ) #include -/* Round N down to P boundary. - */ -#define ROUND_DOWN( N, P ) ((N) - ((N) % P)) - -/* Round N up to P boundary. - */ -#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) )) - /* Simple wrapper around libgsf. * * We need to be able to do scattered writes to structured files. So while @@ -672,8 +664,10 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, layer->width = width; layer->height = height; - layer->tiles_across = ROUND_UP( width, dz->tile_size ) / dz->tile_size; - layer->tiles_down = ROUND_UP( height, dz->tile_size ) / dz->tile_size; + layer->tiles_across = VIPS_ROUND_UP( width, dz->tile_size ) / + dz->tile_size; + layer->tiles_down = VIPS_ROUND_UP( height, dz->tile_size ) / + dz->tile_size; layer->real_pixels = *real_pixels; diff --git a/libvips/foreign/vips2tiff.c b/libvips/foreign/vips2tiff.c index 6b022ec5..9b882294 100644 --- a/libvips/foreign/vips2tiff.c +++ b/libvips/foreign/vips2tiff.c @@ -322,14 +322,6 @@ tiff_openin( const char *name ) return( tif ); } -/* Round N down to P boundary. - */ -#define ROUND_DOWN(N,P) ((N) - ((N) % P)) - -/* Round N up to P boundary. - */ -#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) )) - static Layer * pyramid_new( Write *write, Layer *above, int width, int height ) { @@ -975,7 +967,7 @@ write_new( VipsImage *im, const char *filename, if( im->Coding == VIPS_CODING_LABQ ) write->tls = write->tilew * 3; else if( write->onebit ) - write->tls = ROUND_UP( write->tilew, 8 ) / 8; + write->tls = VIPS_ROUND_UP( write->tilew, 8 ) / 8; else write->tls = VIPS_IMAGE_SIZEOF_PEL( im ) * write->tilew; diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index 01cd8091..a6cc888e 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -53,11 +53,14 @@ extern "C" { #define VIPS_MAX( A, B ) ((A) > (B) ? (A) : (B)) #define VIPS_MIN( A, B ) ((A) < (B) ? (A) : (B)) -#define VIPS_ABS( X ) (((X) >= 0) ? (X) : -(X)) #define VIPS_CLIP( A, V, B ) VIPS_MAX( (A), VIPS_MIN( (B), (V) ) ) +#define VIPS_FCLIP( A, V, B ) VIPS_FMAX( (A), VIPS_FMIN( (B), (V) ) ) + #define VIPS_NUMBER( R ) ((int) (sizeof(R) / sizeof(R[0]))) +#define VIPS_ABS( X ) (((X) >= 0) ? (X) : -(X)) + /* The built-in isnan and isinf functions provided by gcc 4+ and clang are * up to 7x faster than their libc equivalent included from . */ @@ -67,6 +70,7 @@ extern "C" { #define VIPS_FLOOR( V ) __builtin_floor( V ) #define VIPS_CEIL( V ) __builtin_ceil( V ) #define VIPS_RINT( V ) __builtin_rint( V ) +#define VIPS_ROUND( V ) __builtin_round( V ) #define VIPS_FABS( V ) __builtin_fabs( V ) #define VIPS_FMAX( A, B ) __builtin_fmax( A, B ) #define VIPS_FMIN( A, B ) __builtin_fmin( A, B ) @@ -75,13 +79,25 @@ extern "C" { #define VIPS_ISINF( V ) isinf( V ) #define VIPS_FLOOR( V ) floor( V ) #define VIPS_CEIL( V ) ceil( V ) -#define VIPS_RINT( R ) ((int) ((R) > 0 ? ((R) + 0.5) : ((R) - 0.5))) +#define VIPS_RINT( R ) rint( V ) +#define VIPS_ROUND( V ) round( V ) #define VIPS_FABS( V ) VIPS_ABS( V ) #define VIPS_FMAX( A, B ) VIPS_MAX( A, B ) #define VIPS_FMIN( A, B ) VIPS_MIN( A, B ) #endif -#define VIPS_FCLIP( A, V, B ) VIPS_FMAX( (A), VIPS_FMIN( (B), (V) ) ) +/* VIPS_RINT() does "bankers rounding", it rounds to the nerarest even integer. + * For things like image geometry, we want strict nearest int. + * + * If you know it's unsigned, _UINT is a little faster. + */ +#define VIPS_ROUND_INT( R ) ((int) ((R) > 0 ? ((R) + 0.5) : ((R) - 0.5))) +#define VIPS_ROUND_UINT( R ) ((int) ((R) + 0.5)) + +/* Round N down and up to the nearest multiple of P. + */ +#define VIPS_ROUND_DOWN( N, P ) ((N) - ((N) % (P))) +#define VIPS_ROUND_UP( N, P ) (VIPS_ROUND_DOWN( N, P ) + (P)) #define VIPS_SWAP( TYPE, A, B ) \ G_STMT_START { \ diff --git a/libvips/iofuncs/threadpool.c b/libvips/iofuncs/threadpool.c index c70ec47d..559feee8 100644 --- a/libvips/iofuncs/threadpool.c +++ b/libvips/iofuncs/threadpool.c @@ -922,14 +922,6 @@ vips_threadpool_run( VipsImage *im, return( result ); } -/* Round N down to P boundary. - */ -#define ROUND_DOWN(N,P) ((N) - ((N) % P)) - -/* Round N up to P boundary. - */ -#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) )) - /** * vips_get_tile_size: * @im: image to guess for @@ -986,7 +978,7 @@ vips_get_tile_size( VipsImage *im, (1 + nthr / VIPS_MAX( 1, im->Xsize / vips__tile_width )) * 2; *n_lines = VIPS_MAX( *n_lines, vips__fatstrip_height * nthr * 2 ); *n_lines = VIPS_MAX( *n_lines, vips__thinstrip_height * nthr * 2 ); - *n_lines = ROUND_UP( *n_lines, *tile_height ); + *n_lines = VIPS_ROUND_UP( *n_lines, *tile_height ); /* We make this assumption in several places. */ diff --git a/libvips/resample/reduceh.cpp b/libvips/resample/reduceh.cpp index cf51d7f8..bad39044 100644 --- a/libvips/resample/reduceh.cpp +++ b/libvips/resample/reduceh.cpp @@ -498,13 +498,14 @@ vips_reduceh_build( VipsObject *object ) VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) return( -1 ); - /* Size output. Note: we round to nearest to hide rounding errors. + /* Size output. We need to always round to nearest, so round(), not + * rint(). * * Don't change xres/yres, leave that to the application layer. For * example, vipsthumbnail knows the true reduce factor (including the * fractional part), we just see the integer part here. */ - resample->out->Xsize = VIPS_RINT( + resample->out->Xsize = VIPS_ROUND_UINT( (in->Xsize - reduceh->n_point + 1) / reduceh->hshrink ); if( resample->out->Xsize <= 0 ) { vips_error( object_class->nickname, diff --git a/libvips/resample/reducev.cpp b/libvips/resample/reducev.cpp index c7913781..16ac50e8 100644 --- a/libvips/resample/reducev.cpp +++ b/libvips/resample/reducev.cpp @@ -775,13 +775,14 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in ) VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) ) return( -1 ); - /* Size output. Note: we round to nearest to hide rounding errors. + /* Size output. We need to always round to nearest, so round(), not + * rint(). * * Don't change xres/yres, leave that to the application layer. For * example, vipsthumbnail knows the true reduce factor (including the * fractional part), we just see the integer part here. */ - resample->out->Ysize = VIPS_RINT( + resample->out->Ysize = VIPS_ROUND_UINT( (in->Ysize - reducev->n_point + 1) / reducev->vshrink ); if( resample->out->Ysize <= 0 ) { vips_error( object_class->nickname, diff --git a/libvips/resample/shrinkh.c b/libvips/resample/shrinkh.c index 0a9b66ef..695b039c 100644 --- a/libvips/resample/shrinkh.c +++ b/libvips/resample/shrinkh.c @@ -275,13 +275,14 @@ vips_shrinkh_build( VipsObject *object ) VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) return( -1 ); - /* Size output. We need to rint() from the size of the original image. + /* Size output. We need to always round to nearest, so round(), not + * rint(). * * Don't change xres/yres, leave that to the application layer. For * example, vipsthumbnail knows the true shrink factor (including the * fractional part), we just see the integer part here. */ - resample->out->Xsize = VIPS_RINT( + resample->out->Xsize = VIPS_ROUND_UINT( resample->in->Xsize / shrink->hshrink ); if( resample->out->Xsize <= 0 ) { vips_error( class->nickname, diff --git a/libvips/resample/shrinkv.c b/libvips/resample/shrinkv.c index 001e4842..9e061566 100644 --- a/libvips/resample/shrinkv.c +++ b/libvips/resample/shrinkv.c @@ -379,13 +379,14 @@ vips_shrinkv_build( VipsObject *object ) VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) return( -1 ); - /* Size output. We need to rint() from the size of the original image. + /* Size output. We need to always round to nearest, so round(), not + * rint(). * * Don't change xres/yres, leave that to the application layer. For * example, vipsthumbnail knows the true shrink factor (including the * fractional part), we just see the integer part here. */ - resample->out->Ysize = VIPS_RINT( + resample->out->Ysize = VIPS_ROUND_UINT( resample->in->Ysize / shrink->vshrink ); if( resample->out->Ysize <= 0 ) { vips_error( class->nickname, diff --git a/libvips/resample/transform.c b/libvips/resample/transform.c index 116a6090..30b275ab 100644 --- a/libvips/resample/transform.c +++ b/libvips/resample/transform.c @@ -215,10 +215,10 @@ transform_rect( const VipsTransformation *trn, transform_fn transform, top = VIPS_MIN( y1, VIPS_MIN( y2, VIPS_MIN( y3, y4 ) ) ); bottom = VIPS_MAX( y1, VIPS_MAX( y2, VIPS_MAX( y3, y4 ) ) ); - out->left = VIPS_RINT( left ); - out->top = VIPS_RINT( top ); - out->width = VIPS_RINT( right - left ); - out->height = VIPS_RINT( bottom - top ); + out->left = VIPS_ROUND_INT( left ); + out->top = VIPS_ROUND_INT( top ); + out->width = VIPS_ROUND_INT( right - left ); + out->height = VIPS_ROUND_INT( bottom - top ); } /* Given an area in the input image, calculate the bounding box for those