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.