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
This commit is contained in:
John Cupitt 2016-08-20 12:59:41 +01:00
parent 8503065faa
commit 271d8656e9
15 changed files with 63 additions and 94 deletions

View File

@ -37,8 +37,9 @@
- support --strip for pngsave - support --strip for pngsave
- add svgz support [Felix Bünemann] - add svgz support [Felix Bünemann]
- rename boostrap.sh -> autogen.sh to help snapcraft - rename boostrap.sh -> autogen.sh to help snapcraft
- resize/reduce/shrink now round output size to nearest rather than rounding - added VIPS_ROUND as well as VIPS_RINT
down, thanks ioquatix - resize/reduce*/shrink*/affine now round output size to nearest rather than
rounding down, thanks ioquatix
30/7/16 started 8.3.3 30/7/16 started 8.3.3
- fix performance regression in 8.3.2, thanks Lovell - fix performance regression in 8.3.2, thanks Lovell

View File

@ -95,11 +95,9 @@ vips_Lab2LabQ_line( VipsColour *colour, VipsPel *out, VipsPel **in, int width )
int lsbs; int lsbs;
int intv; int intv;
/* Scale L up to 10 bits. Add 0.5 rather than call VIPS_RINT /* Scale L up to 10 bits.
* for speed. This will not round negatives correctly! But
* this does not matter, since L is >0. L*=100.0 -> 1023.
*/ */
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 ); intv = VIPS_CLIP( 0, intv, 1023 );
lsbs = (intv & 0x3) << 6; /* 00000011 -> 11000000 */ lsbs = (intv & 0x3) << 6; /* 00000011 -> 11000000 */
q[0] = intv >> 2; /* drop bot 2 bits and store */ q[0] = intv >> 2; /* drop bot 2 bits and store */

View File

@ -50,14 +50,6 @@
#include "pconversion.h" #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 { typedef struct _VipsArrayjoin {
VipsConversion parent_instance; VipsConversion parent_instance;
@ -179,7 +171,7 @@ vips_arrayjoin_build( VipsObject *object )
/* How many images down the grid? /* 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. /* The output size.
*/ */

View File

@ -98,14 +98,6 @@ typedef VipsConversionClass VipsZoomClass;
G_DEFINE_TYPE( VipsZoom, vips_zoom, VIPS_TYPE_CONVERSION ); 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. /* Paint the part of the region containing only whole pels.
*/ */
static void 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 /* Area of input we need. We have to round out, as we may have
* part-pixels all around the edges. * part-pixels all around the edges.
*/ */
left = ROUND_DOWN( r->left, zoom->xfac ); left = VIPS_ROUND_DOWN( r->left, zoom->xfac );
right = ROUND_UP( ri, zoom->xfac ); right = VIPS_ROUND_UP( ri, zoom->xfac );
top = ROUND_DOWN( r->top, zoom->yfac ); top = VIPS_ROUND_DOWN( r->top, zoom->yfac );
bottom = ROUND_UP( bo, zoom->yfac ); bottom = VIPS_ROUND_UP( bo, zoom->yfac );
width = right - left; width = right - left;
height = bottom - top; height = bottom - top;
s.left = left / zoom->xfac; 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. /* Find the part of the output (if any) which uses only whole pels.
*/ */
left = ROUND_UP( r->left, zoom->xfac ); left = VIPS_ROUND_UP( r->left, zoom->xfac );
right = ROUND_DOWN( ri, zoom->xfac ); right = VIPS_ROUND_DOWN( ri, zoom->xfac );
top = ROUND_UP( r->top, zoom->yfac ); top = VIPS_ROUND_UP( r->top, zoom->yfac );
bottom = ROUND_DOWN( bo, zoom->yfac ); bottom = VIPS_ROUND_DOWN( bo, zoom->yfac );
width = right - left; width = right - left;
height = bottom - top; height = bottom - top;

View File

@ -71,14 +71,6 @@ typedef struct _VipsPerlinClass {
G_DEFINE_TYPE( VipsPerlin, vips_perlin, VIPS_TYPE_CREATE ); 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. /* cos and sin from an angle in 0 - 255.
*/ */
float vips_perlin_cos[256]; float vips_perlin_cos[256];
@ -261,9 +253,11 @@ vips_perlin_build( VipsObject *object )
/* Be careful if width is a multiple of cell_size. /* 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->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->cell_size;
perlin->seed = g_random_double() * 0xffffffffu; perlin->seed = g_random_double() * 0xffffffffu;

View File

@ -75,14 +75,6 @@ G_DEFINE_TYPE( VipsWorley, vips_worley, VIPS_TYPE_CREATE );
#define MAX_FEATURES (10) #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 { typedef struct _Cell {
/* Cell position, in number of cells. Scale by cell_size to get /* Cell position, in number of cells. Scale by cell_size to get
* absolute image cods. * absolute image cods.
@ -294,9 +286,11 @@ vips_worley_build( VipsObject *object )
/* Be careful if width is a multiple of cell_size. /* 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->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->cell_size;
worley->seed = g_random_double() * 0xffffffffu; worley->seed = g_random_double() * 0xffffffffu;

View File

@ -285,14 +285,6 @@ vips__make_xml_metadata( const char *domain, VipsImage *image )
#include <gsf/gsf.h> #include <gsf/gsf.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) ))
/* Simple wrapper around libgsf. /* Simple wrapper around libgsf.
* *
* We need to be able to do scattered writes to structured files. So while * 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->width = width;
layer->height = height; layer->height = height;
layer->tiles_across = ROUND_UP( width, dz->tile_size ) / dz->tile_size; layer->tiles_across = VIPS_ROUND_UP( width, dz->tile_size ) /
layer->tiles_down = ROUND_UP( height, dz->tile_size ) / dz->tile_size; dz->tile_size;
layer->tiles_down = VIPS_ROUND_UP( height, dz->tile_size ) /
dz->tile_size;
layer->real_pixels = *real_pixels; layer->real_pixels = *real_pixels;

View File

@ -322,14 +322,6 @@ tiff_openin( const char *name )
return( tif ); 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 * static Layer *
pyramid_new( Write *write, Layer *above, int width, int height ) 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 ) if( im->Coding == VIPS_CODING_LABQ )
write->tls = write->tilew * 3; write->tls = write->tilew * 3;
else if( write->onebit ) else if( write->onebit )
write->tls = ROUND_UP( write->tilew, 8 ) / 8; write->tls = VIPS_ROUND_UP( write->tilew, 8 ) / 8;
else else
write->tls = VIPS_IMAGE_SIZEOF_PEL( im ) * write->tilew; write->tls = VIPS_IMAGE_SIZEOF_PEL( im ) * write->tilew;

View File

@ -53,11 +53,14 @@ extern "C" {
#define VIPS_MAX( A, B ) ((A) > (B) ? (A) : (B)) #define VIPS_MAX( A, B ) ((A) > (B) ? (A) : (B))
#define VIPS_MIN( 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_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_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 /* The built-in isnan and isinf functions provided by gcc 4+ and clang are
* up to 7x faster than their libc equivalent included from <math.h>. * up to 7x faster than their libc equivalent included from <math.h>.
*/ */
@ -67,6 +70,7 @@ extern "C" {
#define VIPS_FLOOR( V ) __builtin_floor( V ) #define VIPS_FLOOR( V ) __builtin_floor( V )
#define VIPS_CEIL( V ) __builtin_ceil( V ) #define VIPS_CEIL( V ) __builtin_ceil( V )
#define VIPS_RINT( V ) __builtin_rint( V ) #define VIPS_RINT( V ) __builtin_rint( V )
#define VIPS_ROUND( V ) __builtin_round( V )
#define VIPS_FABS( V ) __builtin_fabs( V ) #define VIPS_FABS( V ) __builtin_fabs( V )
#define VIPS_FMAX( A, B ) __builtin_fmax( A, B ) #define VIPS_FMAX( A, B ) __builtin_fmax( A, B )
#define VIPS_FMIN( A, B ) __builtin_fmin( 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_ISINF( V ) isinf( V )
#define VIPS_FLOOR( V ) floor( V ) #define VIPS_FLOOR( V ) floor( V )
#define VIPS_CEIL( V ) ceil( 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_FABS( V ) VIPS_ABS( V )
#define VIPS_FMAX( A, B ) VIPS_MAX( A, B ) #define VIPS_FMAX( A, B ) VIPS_MAX( A, B )
#define VIPS_FMIN( A, B ) VIPS_MIN( A, B ) #define VIPS_FMIN( A, B ) VIPS_MIN( A, B )
#endif #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 ) \ #define VIPS_SWAP( TYPE, A, B ) \
G_STMT_START { \ G_STMT_START { \

View File

@ -922,14 +922,6 @@ vips_threadpool_run( VipsImage *im,
return( result ); 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: * vips_get_tile_size:
* @im: image to guess for * @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; (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__fatstrip_height * nthr * 2 );
*n_lines = VIPS_MAX( *n_lines, vips__thinstrip_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. /* We make this assumption in several places.
*/ */

View File

@ -498,13 +498,14 @@ vips_reduceh_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
return( -1 ); 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 * Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true reduce factor (including the * example, vipsthumbnail knows the true reduce factor (including the
* fractional part), we just see the integer part here. * 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 ); (in->Xsize - reduceh->n_point + 1) / reduceh->hshrink );
if( resample->out->Xsize <= 0 ) { if( resample->out->Xsize <= 0 ) {
vips_error( object_class->nickname, vips_error( object_class->nickname,

View File

@ -775,13 +775,14 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in )
VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) ) VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) )
return( -1 ); 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 * Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true reduce factor (including the * example, vipsthumbnail knows the true reduce factor (including the
* fractional part), we just see the integer part here. * 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 ); (in->Ysize - reducev->n_point + 1) / reducev->vshrink );
if( resample->out->Ysize <= 0 ) { if( resample->out->Ysize <= 0 ) {
vips_error( object_class->nickname, vips_error( object_class->nickname,

View File

@ -275,13 +275,14 @@ vips_shrinkh_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
return( -1 ); 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 * Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true shrink factor (including the * example, vipsthumbnail knows the true shrink factor (including the
* fractional part), we just see the integer part here. * 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 ); resample->in->Xsize / shrink->hshrink );
if( resample->out->Xsize <= 0 ) { if( resample->out->Xsize <= 0 ) {
vips_error( class->nickname, vips_error( class->nickname,

View File

@ -379,13 +379,14 @@ vips_shrinkv_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
return( -1 ); 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 * Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true shrink factor (including the * example, vipsthumbnail knows the true shrink factor (including the
* fractional part), we just see the integer part here. * 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 ); resample->in->Ysize / shrink->vshrink );
if( resample->out->Ysize <= 0 ) { if( resample->out->Ysize <= 0 ) {
vips_error( class->nickname, vips_error( class->nickname,

View File

@ -215,10 +215,10 @@ transform_rect( const VipsTransformation *trn, transform_fn transform,
top = VIPS_MIN( y1, VIPS_MIN( y2, VIPS_MIN( y3, y4 ) ) ); top = VIPS_MIN( y1, VIPS_MIN( y2, VIPS_MIN( y3, y4 ) ) );
bottom = VIPS_MAX( y1, VIPS_MAX( y2, VIPS_MAX( y3, y4 ) ) ); bottom = VIPS_MAX( y1, VIPS_MAX( y2, VIPS_MAX( y3, y4 ) ) );
out->left = VIPS_RINT( left ); out->left = VIPS_ROUND_INT( left );
out->top = VIPS_RINT( top ); out->top = VIPS_ROUND_INT( top );
out->width = VIPS_RINT( right - left ); out->width = VIPS_ROUND_INT( right - left );
out->height = VIPS_RINT( bottom - top ); out->height = VIPS_ROUND_INT( bottom - top );
} }
/* Given an area in the input image, calculate the bounding box for those /* Given an area in the input image, calculate the bounding box for those