Merge branch 'master' into windows-unicode

This commit is contained in:
John Cupitt 2016-09-05 13:12:06 +01:00
commit 57e1423d57
33 changed files with 486 additions and 337 deletions

View File

@ -38,6 +38,12 @@
- 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
- support unicode filenames on Windows - support unicode filenames on Windows
- added VIPS_ROUND as well as VIPS_RINT
- resize/reduce*/shrink*/affine now round output size to nearest rather than
rounding down, thanks ioquatix
19/8/16 started 8.3.4
- better transparency handling in gifload, thanks diegocsandrim
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

@ -92,7 +92,6 @@ Debug build:
Leak check: Leak check:
$ export G_DEBUG=gc-friendly $ export G_DEBUG=gc-friendly
$ export G_SLICE=always-malloc
$ valgrind --suppressions=libvips.supp \ $ valgrind --suppressions=libvips.supp \
--leak-check=yes \ --leak-check=yes \
vips ... > vips-vg.log 2>&1 vips ... > vips-vg.log 2>&1

41
TODO
View File

@ -1,40 +1,17 @@
- not sure about utf8 error messages - not sure about utf8 error messages on win
- done: jpeg, tiff, csv, vips, gif, openslide (needs git master), ppm, png,
pdf, rad, svg, webp
cfitsio: no unicode support on Windows, as far as I can see
libmatio: no unicode support on Windows, as far as I can see
OpenEXR: hates mingw and will not compile
imagemagick seems to like it ought to work, but does not, need to investigate
magick does not seem to be using wopen on win ... see
magick/utility-private.h, fopen_utf8()
we use the fopen() branch, not the _wfopen() one
code is:
#if !defined(MAGICKCORE_WINDOWS_SUPPORT) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__)
return(fopen(..));
#else
return(_wfopen(..));
#endif
we have __MINGW64__ I guess, so we therefore use fopen
how can we build under mingw?
try removing the MINGW stuff
- add unicode filename test - add unicode filename test
- strange:
$ vips similarity --scale 0.33 k2.jpg x.v
$ vipsheader k2.jpg
k2.jpg: 1450x2048 uchar, 3 bands, srgb, jpegload
$ vipsheader x.v
x.v: 478x676 uchar, 3 bands, srgb, jpegload
1450 * 0.33 = 478.5 ... this was rounded down
2048 * 0.33 = 675.84 ... this was rounded up
- add APPROX convsep test? - add APPROX convsep test?

View File

@ -37,9 +37,9 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date`
# binary interface changes backwards compatible?: increment age # binary interface changes backwards compatible?: increment age
# binary interface changes not backwards compatible?: reset age to 0 # binary interface changes not backwards compatible?: reset age to 0
LIBRARY_CURRENT=46 LIBRARY_CURRENT=47
LIBRARY_REVISION=3 LIBRARY_REVISION=1
LIBRARY_AGE=4 LIBRARY_AGE=5
# patched into include/vips/version.h # patched into include/vips/version.h
AC_SUBST(VIPS_VERSION) AC_SUBST(VIPS_VERSION)

View File

@ -1,3 +1,7 @@
// headers for vips operations
// Thu 18 Aug 16:02:54 BST 2016
// this file is generated automatically, do not edit!
static void system( char * cmd_format , VOption *options = 0 ); static void system( char * cmd_format , VOption *options = 0 );
VImage add( VImage right , VOption *options = 0 ); VImage add( VImage right , VOption *options = 0 );
VImage subtract( VImage right , VOption *options = 0 ); VImage subtract( VImage right , VOption *options = 0 );
@ -148,12 +152,12 @@ VipsBlob * webpsave_buffer( VOption *options = 0 );
void tiffsave( char * filename , VOption *options = 0 ); void tiffsave( char * filename , VOption *options = 0 );
void fitssave( char * filename , VOption *options = 0 ); void fitssave( char * filename , VOption *options = 0 );
VImage mapim( VImage index , VOption *options = 0 ); VImage mapim( VImage index , VOption *options = 0 );
VImage shrink( double xshrink , double yshrink , VOption *options = 0 ); VImage shrink( double hshrink , double vshrink , VOption *options = 0 );
VImage shrinkh( int xshrink , VOption *options = 0 ); VImage shrinkh( int hshrink , VOption *options = 0 );
VImage shrinkv( int yshrink , VOption *options = 0 ); VImage shrinkv( int vshrink , VOption *options = 0 );
VImage reduceh( double xshrink , VOption *options = 0 ); VImage reduceh( double hshrink , VOption *options = 0 );
VImage reducev( double yshrink , VOption *options = 0 ); VImage reducev( double vshrink , VOption *options = 0 );
VImage reduce( double xshrink , double yshrink , VOption *options = 0 ); VImage reduce( double hshrink , double vshrink , VOption *options = 0 );
VImage quadratic( VImage coeff , VOption *options = 0 ); VImage quadratic( VImage coeff , VOption *options = 0 );
VImage affine( std::vector<double> matrix , VOption *options = 0 ); VImage affine( std::vector<double> matrix , VOption *options = 0 );
VImage similarity( VOption *options = 0 ); VImage similarity( VOption *options = 0 );
@ -201,8 +205,12 @@ VImage hist_local( int width , int height , VOption *options = 0 );
bool hist_ismonotonic( VOption *options = 0 ); bool hist_ismonotonic( VOption *options = 0 );
double hist_entropy( VOption *options = 0 ); double hist_entropy( VOption *options = 0 );
VImage conv( VImage mask , VOption *options = 0 ); VImage conv( VImage mask , VOption *options = 0 );
VImage conva( VImage mask , VOption *options = 0 );
VImage convf( VImage mask , VOption *options = 0 );
VImage convi( VImage mask , VOption *options = 0 );
VImage compass( VImage mask , VOption *options = 0 ); VImage compass( VImage mask , VOption *options = 0 );
VImage convsep( VImage mask , VOption *options = 0 ); VImage convsep( VImage mask , VOption *options = 0 );
VImage convasep( VImage mask , VOption *options = 0 );
VImage fastcor( VImage ref , VOption *options = 0 ); VImage fastcor( VImage ref , VOption *options = 0 );
VImage spcor( VImage ref , VOption *options = 0 ); VImage spcor( VImage ref , VOption *options = 0 );
VImage sharpen( VOption *options = 0 ); VImage sharpen( VOption *options = 0 );

View File

@ -1,3 +1,7 @@
// bodies for vips operations
// Thu 18 Aug 16:01:57 BST 2016
// this file is generated automatically, do not edit!
void VImage::system( char * cmd_format , VOption *options ) void VImage::system( char * cmd_format , VOption *options )
{ {
call( "system" , call( "system" ,
@ -1849,7 +1853,7 @@ VImage VImage::mapim( VImage index , VOption *options )
return( out ); return( out );
} }
VImage VImage::shrink( double xshrink , double yshrink , VOption *options ) VImage VImage::shrink( double hshrink , double vshrink , VOption *options )
{ {
VImage out; VImage out;
@ -1857,13 +1861,13 @@ VImage VImage::shrink( double xshrink , double yshrink , VOption *options )
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "xshrink", xshrink ) -> set( "hshrink", hshrink ) ->
set( "yshrink", yshrink ) ); set( "vshrink", vshrink ) );
return( out ); return( out );
} }
VImage VImage::shrinkh( int xshrink , VOption *options ) VImage VImage::shrinkh( int hshrink , VOption *options )
{ {
VImage out; VImage out;
@ -1871,12 +1875,12 @@ VImage VImage::shrinkh( int xshrink , VOption *options )
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "xshrink", xshrink ) ); set( "hshrink", hshrink ) );
return( out ); return( out );
} }
VImage VImage::shrinkv( int yshrink , VOption *options ) VImage VImage::shrinkv( int vshrink , VOption *options )
{ {
VImage out; VImage out;
@ -1884,12 +1888,12 @@ VImage VImage::shrinkv( int yshrink , VOption *options )
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "yshrink", yshrink ) ); set( "vshrink", vshrink ) );
return( out ); return( out );
} }
VImage VImage::reduceh( double xshrink , VOption *options ) VImage VImage::reduceh( double hshrink , VOption *options )
{ {
VImage out; VImage out;
@ -1897,12 +1901,12 @@ VImage VImage::reduceh( double xshrink , VOption *options )
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "xshrink", xshrink ) ); set( "hshrink", hshrink ) );
return( out ); return( out );
} }
VImage VImage::reducev( double yshrink , VOption *options ) VImage VImage::reducev( double vshrink , VOption *options )
{ {
VImage out; VImage out;
@ -1910,12 +1914,12 @@ VImage VImage::reducev( double yshrink , VOption *options )
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "yshrink", yshrink ) ); set( "vshrink", vshrink ) );
return( out ); return( out );
} }
VImage VImage::reduce( double xshrink , double yshrink , VOption *options ) VImage VImage::reduce( double hshrink , double vshrink , VOption *options )
{ {
VImage out; VImage out;
@ -1923,8 +1927,8 @@ VImage VImage::reduce( double xshrink , double yshrink , VOption *options )
(options ? options : VImage::option()) -> (options ? options : VImage::option()) ->
set( "in", *this ) -> set( "in", *this ) ->
set( "out", &out ) -> set( "out", &out ) ->
set( "xshrink", xshrink ) -> set( "hshrink", hshrink ) ->
set( "yshrink", yshrink ) ); set( "vshrink", vshrink ) );
return( out ); return( out );
} }
@ -2509,6 +2513,45 @@ VImage VImage::conv( VImage mask , VOption *options )
return( out ); return( out );
} }
VImage VImage::conva( VImage mask , VOption *options )
{
VImage out;
call( "conva" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "mask", mask ) );
return( out );
}
VImage VImage::convf( VImage mask , VOption *options )
{
VImage out;
call( "convf" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "mask", mask ) );
return( out );
}
VImage VImage::convi( VImage mask , VOption *options )
{
VImage out;
call( "convi" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "mask", mask ) );
return( out );
}
VImage VImage::compass( VImage mask , VOption *options ) VImage VImage::compass( VImage mask , VOption *options )
{ {
VImage out; VImage out;
@ -2535,6 +2578,19 @@ VImage VImage::convsep( VImage mask , VOption *options )
return( out ); return( out );
} }
VImage VImage::convasep( VImage mask , VOption *options )
{
VImage out;
call( "convasep" ,
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "mask", mask ) );
return( out );
}
VImage VImage::fastcor( VImage ref , VOption *options ) VImage VImage::fastcor( VImage ref , VOption *options )
{ {
VImage out; VImage out;

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

@ -1266,6 +1266,10 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
* If @embedded is set, the input profile is taken from the input image * If @embedded is set, the input profile is taken from the input image
* metadata. If there is no embedded profile, * metadata. If there is no embedded profile,
* @input_profile_filename is used as a fall-back. * @input_profile_filename is used as a fall-back.
* You can test for the
* presence of an embedded profile with
* vips_image_get_typeof() with #VIPS_META_ICC_NAME as an argument. This will
* return %GType 0 if there is no profile.
* *
* If @embedded is not set, the input profile is taken from * If @embedded is not set, the input profile is taken from
* @input_profile. If @input_profile is not supplied, the * @input_profile. If @input_profile is not supplied, the
@ -1342,11 +1346,18 @@ vips_icc_export( VipsImage *in, VipsImage **out, ... )
* If @embedded is set, the input profile is taken from the input image * If @embedded is set, the input profile is taken from the input image
* metadata, if present. If there is no embedded profile, * metadata, if present. If there is no embedded profile,
* @input_profile is used as a fall-back. * @input_profile is used as a fall-back.
* You can test for the
* presence of an embedded profile with
* vips_image_get_typeof() with #VIPS_META_ICC_NAME as an argument. This will
* return %GType 0 if there is no profile.
* *
* If @embedded is not set, the input profile is taken from * If @embedded is not set, the input profile is taken from
* @input_profile. If @input_profile is not supplied, the * @input_profile. If @input_profile is not supplied, the
* metadata profile, if any, is used as a fall-back. * metadata profile, if any, is used as a fall-back.
* *
* The output image has the output profile attached to the #VIPS_META_ICC_NAME
* field.
*
* Use vips_icc_import() and vips_icc_export() to do either the first or * Use vips_icc_import() and vips_icc_export() to do either the first or
* second half of this operation in isolation. * second half of this operation in isolation.
* *

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

@ -9,6 +9,8 @@
* - write 1, 2, 3, or 4 bands depending on file contents * - write 1, 2, 3, or 4 bands depending on file contents
* 17/8/16 * 17/8/16
* - support unicode on win * - support unicode on win
* 19/8/16
* - better transparency detection, thanks diegocsandrim
*/ */
/* /*
@ -539,9 +541,10 @@ vips_foreign_load_gif_to_memory( VipsForeignLoadGif *gif, VipsImage *out )
if( ext_code == GRAPHICS_EXT_FUNC_CODE && if( ext_code == GRAPHICS_EXT_FUNC_CODE &&
extension && extension &&
extension[0] == 4 && extension[0] == 4 &&
extension[1] == 1 ) { (extension[1] & 0x1) ) {
/* Bytes are 4, 1, delay low, delay high, /* Bytes are 4, 1, delay low, delay high,
* transparency. * transparency. Bit 1 means transparency
* is being set.
*/ */
gif->transparency = extension[4]; gif->transparency = extension[4];
gif->has_transparency = TRUE; gif->has_transparency = TRUE;
@ -850,7 +853,7 @@ vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer )
* *
* Optional arguments: * Optional arguments:
* *
* * @page: %ginit, page (frame) to read * * @page: %gint, page (frame) to read
* *
* Read a GIF file into a VIPS image. Rendering uses the giflib library. * Read a GIF file into a VIPS image. Rendering uses the giflib library.
* *
@ -885,7 +888,7 @@ vips_gifload( const char *filename, VipsImage **out, ... )
* *
* Optional arguments: * Optional arguments:
* *
* * @page: %ginit, page (frame) to read * * @page: %gint, page (frame) to read
* *
* Read a GIF-formatted memory block into a VIPS image. Exactly as * Read a GIF-formatted memory block into a VIPS image. Exactly as
* vips_gifload(), but read from a memory buffer. * vips_gifload(), but read from a memory buffer.

View File

@ -301,7 +301,7 @@ vips_foreign_load_tiff_buffer_init( VipsForeignLoadTiffBuffer *buffer )
* *
* Optional arguments: * Optional arguments:
* *
* * @page: int, load this page * * @page: %gint, load this page
* * @autorotate: %gboolean, use orientation tag to rotate the image * * @autorotate: %gboolean, use orientation tag to rotate the image
* during load * during load
* *

View File

@ -375,14 +375,6 @@ vips__tiff_openin( const char *path )
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 )
{ {
@ -1029,7 +1021,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

@ -48,15 +48,20 @@ typedef enum {
} VipsKernel; } VipsKernel;
int vips_shrink( VipsImage *in, VipsImage **out, int vips_shrink( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... ) double hshrink, double vshrink, ... )
__attribute__((sentinel));
int vips_shrinkh( VipsImage *in, VipsImage **out, int hshrink, ... )
__attribute__((sentinel));
int vips_shrinkv( VipsImage *in, VipsImage **out, int vshrink, ... )
__attribute__((sentinel)); __attribute__((sentinel));
int vips_shrinkh( VipsImage *in, VipsImage **out, int xshrink, ... );
int vips_shrinkv( VipsImage *in, VipsImage **out, int yshrink, ... );
int vips_reduce( VipsImage *in, VipsImage **out, int vips_reduce( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... ); double hshrink, double vshrink, ... )
int vips_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... ); __attribute__((sentinel));
int vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... ); int vips_reduceh( VipsImage *in, VipsImage **out, double hshrink, ... )
__attribute__((sentinel));
int vips_reducev( VipsImage *in, VipsImage **out, double vshrink, ... )
__attribute__((sentinel));
int vips_similarity( VipsImage *in, VipsImage **out, ... ) int vips_similarity( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel)); __attribute__((sentinel));

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) - 1, (P) ))
#define VIPS_SWAP( TYPE, A, B ) \ #define VIPS_SWAP( TYPE, A, B ) \
G_STMT_START { \ G_STMT_START { \

View File

@ -168,13 +168,11 @@ vips__b64_encode( const unsigned char *data, size_t data_length )
int i; int i;
int cursor; int cursor;
if( data_length == 0 ) { if( output_data_length > 10 * 1024 * 1024 ) {
vips_error( "vips__b64_encode", "%s", _( "too little data" ) );
return( NULL );
}
if( output_data_length > 1024 * 1024 ) {
/* We shouldn't really be used for large amounts of data, plus /* We shouldn't really be used for large amounts of data, plus
* we are using int offsets. * we are using int offsets.
*
* A large ICC profile can be 1MB, so allow 10MB of b64.
*/ */
vips_error( "vips__b64_encode", "%s", _( "too much data" ) ); vips_error( "vips__b64_encode", "%s", _( "too much data" ) );
return( NULL ); return( NULL );
@ -228,9 +226,10 @@ vips__b64_decode( const char *buffer, size_t *data_length )
{ {
const size_t buffer_length = strlen( buffer ); const size_t buffer_length = strlen( buffer );
/* Worst case. /* Worst case. Add one, since we don't want to return NULL for an empty
* input string, it would look like an error return.
*/ */
const size_t output_data_length = buffer_length * 3 / 4; const size_t output_data_length = 1 + buffer_length * 3 / 4;
unsigned char *data; unsigned char *data;
unsigned char *p; unsigned char *p;

View File

@ -467,8 +467,13 @@ vips_leak( void )
vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() ); vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() );
vips_buf_appends( &buf, "\n" ); vips_buf_appends( &buf, "\n" );
if( strlen( vips_error_buffer() ) > 0 )
vips_buf_appendf( &buf, "error buffer: %s",
vips_error_buffer() );
fprintf( stderr, "%s", vips_buf_all( &buf ) ); fprintf( stderr, "%s", vips_buf_all( &buf ) );
#ifdef DEBUG #ifdef DEBUG
vips_buffer_dump_all(); vips_buffer_dump_all();
#endif /*DEBUG*/ #endif /*DEBUG*/

View File

@ -699,7 +699,11 @@ vips_operation_set_valist_required( VipsOperation *operation, va_list ap )
g_assert( argument_instance ); g_assert( argument_instance );
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) ) { /* We skip deprecated required args. There will be a new,
* renamed arg in the same place.
*/
if( (argument_class->flags & VIPS_ARGUMENT_REQUIRED) &&
!(argument_class->flags & VIPS_ARGUMENT_DEPRECATED) ) {
VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class, ap ); VIPS_ARGUMENT_COLLECT_SET( pspec, argument_class, ap );
#ifdef VIPS_DEBUG #ifdef VIPS_DEBUG

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

@ -670,6 +670,11 @@ transform_blob_save_string( const GValue *src_value, GValue *dest_value )
vips_value_set_save_string( dest_value, b64 ); vips_value_set_save_string( dest_value, b64 );
vips_free( b64 ); vips_free( b64 );
} }
else
/* No error return from transform, but we should set it to
* something.
*/
vips_value_set_save_string( dest_value, "" );
} }
static void static void
@ -683,6 +688,11 @@ transform_save_string_blob( const GValue *src_value, GValue *dest_value )
if( (blob = vips__b64_decode( b64, &blob_length )) ) if( (blob = vips__b64_decode( b64, &blob_length )) )
vips_value_set_blob( dest_value, vips_value_set_blob( dest_value,
(VipsCallbackFn) vips_free, blob, blob_length ); (VipsCallbackFn) vips_free, blob, blob_length );
else
/* No error return from transform, but we should set it to
* something.
*/
vips_value_set_save_string( dest_value, "" );
} }
GType GType

View File

@ -2,6 +2,8 @@
* *
* 27/1/16 * 27/1/16
* - from shrink.c * - from shrink.c
* 15/8/16
* - rename xshrink -> hshrink for greater consistency
*/ */
/* /*
@ -66,8 +68,8 @@
typedef struct _VipsReduce { typedef struct _VipsReduce {
VipsResample parent_instance; VipsResample parent_instance;
double xshrink; /* Shrink factors */ double hshrink; /* Shrink factors */
double yshrink; double vshrink;
/* The thing we use to make the kernel. /* The thing we use to make the kernel.
*/ */
@ -90,10 +92,10 @@ vips_reduce_build( VipsObject *object )
if( VIPS_OBJECT_CLASS( vips_reduce_parent_class )->build( object ) ) if( VIPS_OBJECT_CLASS( vips_reduce_parent_class )->build( object ) )
return( -1 ); return( -1 );
if( vips_reducev( resample->in, &t[0], reduce->yshrink, if( vips_reducev( resample->in, &t[0], reduce->vshrink,
"kernel", reduce->kernel, "kernel", reduce->kernel,
NULL ) || NULL ) ||
vips_reduceh( t[0], &t[1], reduce->xshrink, vips_reduceh( t[0], &t[1], reduce->hshrink,
"kernel", reduce->kernel, "kernel", reduce->kernel,
NULL ) || NULL ) ||
vips_image_write( t[1], resample->out ) ) vips_image_write( t[1], resample->out ) )
@ -120,18 +122,18 @@ vips_reduce_class_init( VipsReduceClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL; operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_DOUBLE( class, "xshrink", 8, VIPS_ARG_DOUBLE( class, "hshrink", 8,
_( "Xshrink" ), _( "Hshrink" ),
_( "Horizontal shrink factor" ), _( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduce, xshrink ), G_STRUCT_OFFSET( VipsReduce, hshrink ),
1.0, 1000000.0, 1.0 ); 1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "yshrink", 9, VIPS_ARG_DOUBLE( class, "vshrink", 9,
_( "Yshrink" ), _( "Vshrink" ),
_( "Vertical shrink factor" ), _( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduce, yshrink ), G_STRUCT_OFFSET( VipsReduce, vshrink ),
1.0, 1000000.0, 1.0 ); 1.0, 1000000.0, 1.0 );
VIPS_ARG_ENUM( class, "kernel", 3, VIPS_ARG_ENUM( class, "kernel", 3,
@ -141,6 +143,22 @@ vips_reduce_class_init( VipsReduceClass *class )
G_STRUCT_OFFSET( VipsReduce, kernel ), G_STRUCT_OFFSET( VipsReduce, kernel ),
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 ); VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
/* The old names .. now use h and v everywhere.
*/
VIPS_ARG_DOUBLE( class, "xshrink", 8,
_( "Xshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsReduce, hshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "yshrink", 9,
_( "Yshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsReduce, vshrink ),
1.0, 1000000.0, 1.0 );
} }
static void static void
@ -153,8 +171,8 @@ vips_reduce_init( VipsReduce *reduce )
* vips_reduce: * vips_reduce:
* @in: input image * @in: input image
* @out: output image * @out: output image
* @xshrink: horizontal shrink * @hshrink: horizontal shrink
* @yshrink: vertical shrink * @vshrink: vertical shrink
* @...: %NULL-terminated list of optional named arguments * @...: %NULL-terminated list of optional named arguments
* *
* Optional arguments: * Optional arguments:
@ -176,13 +194,13 @@ vips_reduce_init( VipsReduce *reduce )
*/ */
int int
vips_reduce( VipsImage *in, VipsImage **out, vips_reduce( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... ) double hshrink, double vshrink, ... )
{ {
va_list ap; va_list ap;
int result; int result;
va_start( ap, yshrink ); va_start( ap, vshrink );
result = vips_call_split( "reduce", ap, in, out, xshrink, yshrink ); result = vips_call_split( "reduce", ap, in, out, hshrink, vshrink );
va_end( ap ); va_end( ap );
return( result ); return( result );

View File

@ -4,6 +4,8 @@
* - from shrinkh.c * - from shrinkh.c
* 10/3/16 * 10/3/16
* - add other kernels * - add other kernels
* 15/8/16
* - rename xshrink as hshrink for consistency
*/ */
/* /*
@ -67,7 +69,7 @@
typedef struct _VipsReduceh { typedef struct _VipsReduceh {
VipsResample parent_instance; VipsResample parent_instance;
double xshrink; /* Reduce factor */ double hshrink; /* Reduce factor */
/* The thing we use to make the kernel. /* The thing we use to make the kernel.
*/ */
@ -276,7 +278,7 @@ reduceh_notab( VipsReduceh *reduceh,
double cx[MAX_POINT]; double cx[MAX_POINT];
vips_reduce_make_mask( cx, reduceh->kernel, reduceh->xshrink, x ); vips_reduce_make_mask( cx, reduceh->kernel, reduceh->hshrink, x );
for( int z = 0; z < bands; z++ ) { for( int z = 0; z < bands; z++ ) {
out[z] = reduce_sum<T, double>( in, bands, cx, n ); out[z] = reduce_sum<T, double>( in, bands, cx, n );
@ -311,9 +313,9 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
r->width, r->height, r->left, r->top ); r->width, r->height, r->left, r->top );
#endif /*DEBUG*/ #endif /*DEBUG*/
s.left = r->left * reduceh->xshrink; s.left = r->left * reduceh->hshrink;
s.top = r->top; s.top = r->top;
s.width = r->width * reduceh->xshrink + reduceh->n_point; s.width = r->width * reduceh->hshrink + reduceh->n_point;
s.height = r->height; s.height = r->height;
if( vips_region_prepare( ir, &s ) ) if( vips_region_prepare( ir, &s ) )
return( -1 ); return( -1 );
@ -328,7 +330,7 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
q = VIPS_REGION_ADDR( out_region, r->left, r->top + y ); q = VIPS_REGION_ADDR( out_region, r->left, r->top + y );
X = r->left * reduceh->xshrink; X = r->left * reduceh->hshrink;
/* We want p0 to be the start (ie. x == 0) of the input /* We want p0 to be the start (ie. x == 0) of the input
* scanline we are reading from. We can then calculate the p we * scanline we are reading from. We can then calculate the p we
@ -411,7 +413,7 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
break; break;
} }
X += reduceh->xshrink; X += reduceh->hshrink;
q += ps; q += ps;
} }
} }
@ -439,19 +441,19 @@ vips_reduceh_build( VipsObject *object )
in = resample->in; in = resample->in;
if( reduceh->xshrink < 1 ) { if( reduceh->hshrink < 1 ) {
vips_error( object_class->nickname, vips_error( object_class->nickname,
"%s", _( "reduce factors should be >= 1" ) ); "%s", _( "reduce factors should be >= 1" ) );
return( -1 ); return( -1 );
} }
if( reduceh->xshrink == 1 ) if( reduceh->hshrink == 1 )
return( vips_image_write( in, resample->out ) ); return( vips_image_write( in, resample->out ) );
/* Build the tables of pre-computed coefficients. /* Build the tables of pre-computed coefficients.
*/ */
reduceh->n_point = reduceh->n_point =
vips_reduce_get_points( reduceh->kernel, reduceh->xshrink ); vips_reduce_get_points( reduceh->kernel, reduceh->hshrink );
vips_info( object_class->nickname, "%d point mask", reduceh->n_point ); vips_info( object_class->nickname, "%d point mask", reduceh->n_point );
if( reduceh->n_point > MAX_POINT ) { if( reduceh->n_point > MAX_POINT ) {
vips_error( object_class->nickname, vips_error( object_class->nickname,
@ -468,7 +470,7 @@ vips_reduceh_build( VipsObject *object )
return( -1 ); return( -1 );
vips_reduce_make_mask( reduceh->matrixf[x], vips_reduce_make_mask( reduceh->matrixf[x],
reduceh->kernel, reduceh->xshrink, reduceh->kernel, reduceh->hshrink,
(float) x / VIPS_TRANSFORM_SCALE ); (float) x / VIPS_TRANSFORM_SCALE );
for( int i = 0; i < reduceh->n_point; i++ ) for( int i = 0; i < reduceh->n_point; i++ )
@ -496,14 +498,15 @@ 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->xshrink ); (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,
"%s", _( "image has shrunk to nothing" ) ); "%s", _( "image has shrunk to nothing" ) );
@ -543,11 +546,11 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( reduceh_class, "xshrink", 3, VIPS_ARG_DOUBLE( reduceh_class, "hshrink", 3,
_( "Xshrink" ), _( "Hshrink" ),
_( "Horizontal shrink factor" ), _( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduceh, xshrink ), G_STRUCT_OFFSET( VipsReduceh, hshrink ),
1, 1000000, 1 ); 1, 1000000, 1 );
VIPS_ARG_ENUM( reduceh_class, "kernel", 3, VIPS_ARG_ENUM( reduceh_class, "kernel", 3,
@ -557,6 +560,15 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
G_STRUCT_OFFSET( VipsReduceh, kernel ), G_STRUCT_OFFSET( VipsReduceh, kernel ),
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 ); VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
/* Old name.
*/
VIPS_ARG_DOUBLE( reduceh_class, "xshrink", 3,
_( "Xshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsReduceh, hshrink ),
1, 1000000, 1 );
} }
static void static void
@ -569,7 +581,7 @@ vips_reduceh_init( VipsReduceh *reduceh )
* vips_reduceh: * vips_reduceh:
* @in: input image * @in: input image
* @out: output image * @out: output image
* @xshrink: horizontal reduce * @hshrink: horizontal reduce
* @...: %NULL-terminated list of optional named arguments * @...: %NULL-terminated list of optional named arguments
* *
* Optional arguments: * Optional arguments:
@ -590,13 +602,13 @@ vips_reduceh_init( VipsReduceh *reduceh )
* Returns: 0 on success, -1 on error * Returns: 0 on success, -1 on error
*/ */
int int
vips_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... ) vips_reduceh( VipsImage *in, VipsImage **out, double hshrink, ... )
{ {
va_list ap; va_list ap;
int result; int result;
va_start( ap, xshrink ); va_start( ap, hshrink );
result = vips_call_split( "reduceh", ap, in, out, xshrink ); result = vips_call_split( "reduceh", ap, in, out, hshrink );
va_end( ap ); va_end( ap );
return( result ); return( result );

View File

@ -11,6 +11,8 @@
* equal to the target scale * equal to the target scale
* 15/6/16 * 15/6/16
* - better accuracy with smarter multiplication * - better accuracy with smarter multiplication
* 15/8/16
* - rename yshrink as vshrink for consistency
*/ */
/* /*
@ -92,7 +94,7 @@ typedef struct {
typedef struct _VipsReducev { typedef struct _VipsReducev {
VipsResample parent_instance; VipsResample parent_instance;
double yshrink; /* Shrink factor */ double vshrink; /* Shrink factor */
/* The thing we use to make the kernel. /* The thing we use to make the kernel.
*/ */
@ -491,7 +493,7 @@ reducev_notab( VipsReducev *reducev,
double cy[MAX_POINT]; double cy[MAX_POINT];
vips_reduce_make_mask( cy, reducev->kernel, reducev->yshrink, y ); vips_reduce_make_mask( cy, reducev->kernel, reducev->vshrink, y );
for( int z = 0; z < ne; z++ ) for( int z = 0; z < ne; z++ )
out[z] = reduce_sum<T, double>( in + z, l1, cy, n ); out[z] = reduce_sum<T, double>( in + z, l1, cy, n );
@ -521,9 +523,9 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
#endif /*DEBUG*/ #endif /*DEBUG*/
s.left = r->left; s.left = r->left;
s.top = r->top * reducev->yshrink; s.top = r->top * reducev->vshrink;
s.width = r->width; s.width = r->width;
s.height = r->height * reducev->yshrink + reducev->n_point; s.height = r->height * reducev->vshrink + reducev->n_point;
if( vips_region_prepare( ir, &s ) ) if( vips_region_prepare( ir, &s ) )
return( -1 ); return( -1 );
@ -532,7 +534,7 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
for( int y = 0; y < r->height; y ++ ) { for( int y = 0; y < r->height; y ++ ) {
VipsPel *q = VipsPel *q =
VIPS_REGION_ADDR( out_region, r->left, r->top + y ); VIPS_REGION_ADDR( out_region, r->left, r->top + y );
const double Y = (r->top + y) * reducev->yshrink; const double Y = (r->top + y) * reducev->vshrink;
VipsPel *p = VIPS_REGION_ADDR( ir, r->left, (int) Y ); VipsPel *p = VIPS_REGION_ADDR( ir, r->left, (int) Y );
const int sy = Y * VIPS_TRANSFORM_SCALE * 2; const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1); const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
@ -631,9 +633,9 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
#endif /*DEBUG_PIXELS*/ #endif /*DEBUG_PIXELS*/
s.left = r->left; s.left = r->left;
s.top = r->top * reducev->yshrink; s.top = r->top * reducev->vshrink;
s.width = r->width; s.width = r->width;
s.height = r->height * reducev->yshrink + reducev->n_point; s.height = r->height * reducev->vshrink + reducev->n_point;
if( vips_region_prepare( ir, &s ) ) if( vips_region_prepare( ir, &s ) )
return( -1 ); return( -1 );
@ -651,7 +653,7 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
for( int y = 0; y < r->height; y ++ ) { for( int y = 0; y < r->height; y ++ ) {
VipsPel *q = VipsPel *q =
VIPS_REGION_ADDR( out_region, r->left, r->top + y ); VIPS_REGION_ADDR( out_region, r->left, r->top + y );
const double Y = (r->top + y) * reducev->yshrink; const double Y = (r->top + y) * reducev->vshrink;
const int py = (int) Y; const int py = (int) Y;
const int sy = Y * VIPS_TRANSFORM_SCALE * 2; const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1); const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
@ -719,7 +721,7 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in )
return( -1 ); return( -1 );
vips_reduce_make_mask( reducev->matrixf[y], vips_reduce_make_mask( reducev->matrixf[y],
reducev->kernel, reducev->yshrink, reducev->kernel, reducev->vshrink,
(float) y / VIPS_TRANSFORM_SCALE ); (float) y / VIPS_TRANSFORM_SCALE );
#ifdef DEBUG #ifdef DEBUG
@ -773,14 +775,15 @@ 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->yshrink ); (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,
"%s", _( "image has shrunk to nothing" ) ); "%s", _( "image has shrunk to nothing" ) );
@ -816,17 +819,17 @@ vips_reducev_build( VipsObject *object )
in = resample->in; in = resample->in;
if( reducev->yshrink < 1 ) { if( reducev->vshrink < 1 ) {
vips_error( object_class->nickname, vips_error( object_class->nickname,
"%s", _( "reduce factor should be >= 1" ) ); "%s", _( "reduce factor should be >= 1" ) );
return( -1 ); return( -1 );
} }
if( reducev->yshrink == 1 ) if( reducev->vshrink == 1 )
return( vips_image_write( in, resample->out ) ); return( vips_image_write( in, resample->out ) );
reducev->n_point = reducev->n_point =
vips_reduce_get_points( reducev->kernel, reducev->yshrink ); vips_reduce_get_points( reducev->kernel, reducev->vshrink );
if( reducev->n_point > MAX_POINT ) { if( reducev->n_point > MAX_POINT ) {
vips_error( object_class->nickname, vips_error( object_class->nickname,
"%s", _( "reduce factor too large" ) ); "%s", _( "reduce factor too large" ) );
@ -876,20 +879,29 @@ vips_reducev_class_init( VipsReducevClass *reducev_class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3, VIPS_ARG_DOUBLE( reducev_class, "vshrink", 3,
_( "Xshrink" ), _( "Vshrink" ),
_( "Vertical shrink factor" ), _( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReducev, yshrink ), G_STRUCT_OFFSET( VipsReducev, vshrink ),
1, 1000000, 1 ); 1, 1000000, 1 );
VIPS_ARG_ENUM( reducev_class, "kernel", 3, VIPS_ARG_ENUM( reducev_class, "kernel", 4,
_( "Kernel" ), _( "Kernel" ),
_( "Resampling kernel" ), _( "Resampling kernel" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsReducev, kernel ), G_STRUCT_OFFSET( VipsReducev, kernel ),
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 ); VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
/* Old name.
*/
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3,
_( "Yshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsReducev, vshrink ),
1, 1000000, 1 );
} }
static void static void
@ -902,7 +914,7 @@ vips_reducev_init( VipsReducev *reducev )
* vips_reducev: * vips_reducev:
* @in: input image * @in: input image
* @out: output image * @out: output image
* @yshrink: horizontal reduce * @vshrink: horizontal reduce
* @...: %NULL-terminated list of optional named arguments * @...: %NULL-terminated list of optional named arguments
* *
* Optional arguments: * Optional arguments:
@ -923,13 +935,13 @@ vips_reducev_init( VipsReducev *reducev )
* Returns: 0 on success, -1 on error * Returns: 0 on success, -1 on error
*/ */
int int
vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... ) vips_reducev( VipsImage *in, VipsImage **out, double vshrink, ... )
{ {
va_list ap; va_list ap;
int result; int result;
va_start( ap, yshrink ); va_start( ap, vshrink );
result = vips_call_split( "reducev", ap, in, out, yshrink ); result = vips_call_split( "reducev", ap, in, out, vshrink );
va_end( ap ); va_end( ap );
return( result ); return( result );

View File

@ -18,6 +18,8 @@
* reduce * reduce
* 22/6/16 * 22/6/16
* - faster and better upsizing * - faster and better upsizing
* 15/8/16
* - more accurate resizing
*/ */
/* /*
@ -157,37 +159,36 @@ vips_resize_build( VipsObject *object )
VipsImage **t = (VipsImage **) vips_object_local_array( object, 7 ); VipsImage **t = (VipsImage **) vips_object_local_array( object, 7 );
VipsImage *in; VipsImage *in;
int target_width; double hscale;
int target_height; double vscale;
int int_hshrink; int int_hshrink;
int int_vshrink; int int_vshrink;
double hresidual;
double vresidual;
if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) ) if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) )
return( -1 ); return( -1 );
in = resample->in; in = resample->in;
/* The image size we are aiming for. /* Updated below when we do the int part of our shrink.
*/ */
target_width = in->Xsize * resize->scale; hscale = resize->scale;
if( vips_object_argument_isset( object, "vscale" ) ) if( vips_object_argument_isset( object, "vscale" ) )
target_height = in->Ysize * resize->vscale; vscale = resize->vscale;
else else
target_height = in->Ysize * resize->scale; vscale = resize->scale;
int_hshrink = vips_resize_int_shrink( resize, resize->scale ); /* The int part of our scale.
if( vips_object_argument_isset( object, "vscale" ) ) */
int_vshrink = vips_resize_int_shrink( resize, resize->vscale ); int_hshrink = vips_resize_int_shrink( resize, hscale );
else int_vshrink = vips_resize_int_shrink( resize, vscale );
int_vshrink = int_hshrink;
if( int_vshrink > 1 ) { if( int_vshrink > 1 ) {
vips_info( class->nickname, "shrinkv by %d", int_vshrink ); vips_info( class->nickname, "shrinkv by %d", int_vshrink );
if( vips_shrinkv( in, &t[0], int_vshrink, NULL ) ) if( vips_shrinkv( in, &t[0], int_vshrink, NULL ) )
return( -1 ); return( -1 );
in = t[0]; in = t[0];
vscale *= int_vshrink;
} }
if( int_hshrink > 1 ) { if( int_hshrink > 1 ) {
@ -195,16 +196,9 @@ vips_resize_build( VipsObject *object )
if( vips_shrinkh( in, &t[1], int_hshrink, NULL ) ) if( vips_shrinkh( in, &t[1], int_hshrink, NULL ) )
return( -1 ); return( -1 );
in = t[1]; in = t[1];
}
/* Do we need a further size adjustment? It's the difference hscale *= int_hshrink;
* between our target size and the size we have after vips_shrink(). }
*
* This can break the aspect ratio slightly :/ but hopefully no one
* will notice.
*/
hresidual = (double) target_width / in->Xsize;
vresidual = (double) target_height / in->Ysize;
/* We will get overcomputation on vips_shrink() from the vips_reduce() /* We will get overcomputation on vips_shrink() from the vips_reduce()
* coming later, so read into a cache where tiles are scanlines, and * coming later, so read into a cache where tiles are scanlines, and
@ -239,7 +233,7 @@ vips_resize_build( VipsObject *object )
vips_get_tile_size( in, vips_get_tile_size( in,
&tile_width, &tile_height, &n_lines ); &tile_width, &tile_height, &n_lines );
need_lines = 1.2 * n_lines / vresidual; need_lines = 1.2 * n_lines / vscale;
if( vips_tilecache( in, &t[6], if( vips_tilecache( in, &t[6],
"tile_width", in->Xsize, "tile_width", in->Xsize,
"tile_height", 10, "tile_height", 10,
@ -253,20 +247,20 @@ vips_resize_build( VipsObject *object )
/* Any residual downsizing. /* Any residual downsizing.
*/ */
if( vresidual < 1.0 ) { if( vscale < 1.0 ) {
vips_info( class->nickname, "residual reducev by %g", vips_info( class->nickname, "residual reducev by %g",
vresidual ); vscale );
if( vips_reducev( in, &t[2], 1.0 / vresidual, if( vips_reducev( in, &t[2], 1.0 / vscale,
"kernel", resize->kernel, "kernel", resize->kernel,
NULL ) ) NULL ) )
return( -1 ); return( -1 );
in = t[2]; in = t[2];
} }
if( hresidual < 1.0 ) { if( hscale < 1.0 ) {
vips_info( class->nickname, "residual reduceh by %g", vips_info( class->nickname, "residual reduceh by %g",
hresidual ); hscale );
if( vips_reduceh( in, &t[3], 1.0 / hresidual, if( vips_reduceh( in, &t[3], 1.0 / hscale,
"kernel", resize->kernel, "kernel", resize->kernel,
NULL ) ) NULL ) )
return( -1 ); return( -1 );
@ -275,8 +269,8 @@ vips_resize_build( VipsObject *object )
/* Any upsizing. /* Any upsizing.
*/ */
if( hresidual > 1.0 || if( hscale > 1.0 ||
vresidual > 1.0 ) { vscale > 1.0 ) {
const char *nickname = vips_resize_interpolate( resize->kernel ); const char *nickname = vips_resize_interpolate( resize->kernel );
VipsInterpolate *interpolate; VipsInterpolate *interpolate;
@ -284,21 +278,21 @@ vips_resize_build( VipsObject *object )
return( -1 ); return( -1 );
vips_object_local( object, interpolate ); vips_object_local( object, interpolate );
if( hresidual > 1.0 && if( hscale > 1.0 &&
vresidual > 1.0 ) { vscale > 1.0 ) {
vips_info( class->nickname, vips_info( class->nickname,
"residual scale %g x %g", hresidual, vresidual ); "residual scale %g x %g", hscale, vscale );
if( vips_affine( in, &t[4], if( vips_affine( in, &t[4],
hresidual, 0.0, 0.0, vresidual, hscale, 0.0, 0.0, vscale,
"interpolate", interpolate, "interpolate", interpolate,
NULL ) ) NULL ) )
return( -1 ); return( -1 );
in = t[4]; in = t[4];
} }
else if( hresidual > 1.0 ) { else if( hscale > 1.0 ) {
vips_info( class->nickname, vips_info( class->nickname,
"residual scaleh %g", hresidual ); "residual scale %g", hscale );
if( vips_affine( in, &t[4], hresidual, 0.0, 0.0, 1.0, if( vips_affine( in, &t[4], hscale, 0.0, 0.0, 1.0,
"interpolate", interpolate, "interpolate", interpolate,
NULL ) ) NULL ) )
return( -1 ); return( -1 );
@ -306,8 +300,8 @@ vips_resize_build( VipsObject *object )
} }
else { else {
vips_info( class->nickname, vips_info( class->nickname,
"residual scalev %g", vresidual ); "residual scale %g", vscale );
if( vips_affine( in, &t[4], 1.0, 0.0, 0.0, vresidual, if( vips_affine( in, &t[4], 1.0, 0.0, 0.0, vscale,
"interpolate", interpolate, "interpolate", interpolate,
NULL ) ) NULL ) )
return( -1 ); return( -1 );

View File

@ -4,6 +4,9 @@
* - from shrink.c (now renamed as shrink2.c) * - from shrink.c (now renamed as shrink2.c)
* - split to h and v shrinks for a large memory saving * - split to h and v shrinks for a large memory saving
* - now handles complex * - now handles complex
* 15/8/16
* - more accurate resize
* - rename xshrink -> hshrink for greater consistency
*/ */
/* /*
@ -55,8 +58,8 @@
typedef struct _VipsShrink { typedef struct _VipsShrink {
VipsResample parent_instance; VipsResample parent_instance;
double xshrink; /* Shrink factors */ double hshrink; /* Shrink factors */
double yshrink; double vshrink;
} VipsShrink; } VipsShrink;
@ -72,40 +75,34 @@ vips_shrink_build( VipsObject *object )
VipsImage **t = (VipsImage **) VipsImage **t = (VipsImage **)
vips_object_local_array( object, 3 ); vips_object_local_array( object, 3 );
int xshrink_int; int hshrink_int;
int yshrink_int; int vshrink_int;
if( VIPS_OBJECT_CLASS( vips_shrink_parent_class )->build( object ) ) if( VIPS_OBJECT_CLASS( vips_shrink_parent_class )->build( object ) )
return( -1 ); return( -1 );
xshrink_int = (int) shrink->xshrink; hshrink_int = (int) shrink->hshrink;
yshrink_int = (int) shrink->yshrink; vshrink_int = (int) shrink->vshrink;
if( xshrink_int != shrink->xshrink || if( hshrink_int != shrink->hshrink ||
yshrink_int != shrink->yshrink ) { vshrink_int != shrink->vshrink ) {
/* Shrink by int factors, affine to final size. /* Shrink by int factors, affine to final size.
*/ */
int target_width = resample->in->Xsize / shrink->xshrink; double xresidual = hshrink_int / shrink->hshrink;
int target_height = resample->in->Ysize / shrink->yshrink; double yresidual = vshrink_int / shrink->vshrink;
double xresidual; if( vips_shrinkv( resample->in, &t[0], vshrink_int, NULL ) ||
double yresidual; vips_shrinkh( t[0], &t[1], hshrink_int, NULL ) )
if( vips_shrinkv( resample->in, &t[0], yshrink_int, NULL ) ||
vips_shrinkh( t[0], &t[1], xshrink_int, NULL ) )
return( -1 ); return( -1 );
xresidual = (double) target_width / t[1]->Xsize;
yresidual = (double) target_height / t[1]->Ysize;
if( vips_affine( t[1], &t[2], if( vips_affine( t[1], &t[2],
xresidual, 0.0, 0.0, yresidual, NULL ) || xresidual, 0.0, 0.0, yresidual, NULL ) ||
vips_image_write( t[2], resample->out ) ) vips_image_write( t[2], resample->out ) )
return( -1 ); return( -1 );
} }
else { else {
if( vips_shrinkv( resample->in, &t[0], shrink->yshrink, NULL ) || if( vips_shrinkv( resample->in, &t[0], shrink->vshrink, NULL ) ||
vips_shrinkh( t[0], &t[1], shrink->xshrink, NULL ) || vips_shrinkh( t[0], &t[1], shrink->hshrink, NULL ) ||
vips_image_write( t[1], resample->out ) ) vips_image_write( t[1], resample->out ) )
return( -1 ); return( -1 );
} }
@ -131,18 +128,34 @@ vips_shrink_class_init( VipsShrinkClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( class, "vshrink", 9,
_( "Vshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrink, vshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "hshrink", 8,
_( "Hshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrink, hshrink ),
1.0, 1000000.0, 1.0 );
/* The old names .. now use h and v everywhere.
*/
VIPS_ARG_DOUBLE( class, "xshrink", 8, VIPS_ARG_DOUBLE( class, "xshrink", 8,
_( "Xshrink" ), _( "Xshrink" ),
_( "Horizontal shrink factor" ), _( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrink, xshrink ), G_STRUCT_OFFSET( VipsShrink, hshrink ),
1.0, 1000000.0, 1.0 ); 1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "yshrink", 9, VIPS_ARG_DOUBLE( class, "yshrink", 9,
_( "Yshrink" ), _( "Yshrink" ),
_( "Vertical shrink factor" ), _( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrink, yshrink ), G_STRUCT_OFFSET( VipsShrink, vshrink ),
1.0, 1000000.0, 1.0 ); 1.0, 1000000.0, 1.0 );
} }
@ -156,8 +169,8 @@ vips_shrink_init( VipsShrink *shrink )
* vips_shrink: * vips_shrink:
* @in: input image * @in: input image
* @out: output image * @out: output image
* @xshrink: horizontal shrink * @hshrink: horizontal shrink
* @yshrink: vertical shrink * @vshrink: vertical shrink
* @...: %NULL-terminated list of optional named arguments * @...: %NULL-terminated list of optional named arguments
* *
* Shrink @in by a pair of factors with a simple box filter. For non-integer * Shrink @in by a pair of factors with a simple box filter. For non-integer
@ -177,13 +190,13 @@ vips_shrink_init( VipsShrink *shrink )
*/ */
int int
vips_shrink( VipsImage *in, VipsImage **out, vips_shrink( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... ) double hshrink, double vshrink, ... )
{ {
va_list ap; va_list ap;
int result; int result;
va_start( ap, yshrink ); va_start( ap, vshrink );
result = vips_call_split( "shrink", ap, in, out, xshrink, yshrink ); result = vips_call_split( "shrink", ap, in, out, hshrink, vshrink );
va_end( ap ); va_end( ap );
return( result ); return( result );

View File

@ -4,6 +4,8 @@
* - from shrink.c * - from shrink.c
* 22/1/16 * 22/1/16
* - reorganise loops, 30% faster, vectorisable * - reorganise loops, 30% faster, vectorisable
* 15/8/16
* - rename xshrink -> hshrink for greater consistency
*/ */
/* /*
@ -55,7 +57,7 @@
typedef struct _VipsShrinkh { typedef struct _VipsShrinkh {
VipsResample parent_instance; VipsResample parent_instance;
int xshrink; /* Shrink factor */ int hshrink; /* Shrink factor */
} VipsShrinkh; } VipsShrinkh;
@ -79,9 +81,9 @@ G_DEFINE_TYPE( VipsShrinkh, vips_shrinkh, VIPS_TYPE_RESAMPLE );
\ \
sum = 0; \ sum = 0; \
x1 = b; \ x1 = b; \
VIPS_UNROLL( shrink->xshrink, INNER( BANDS ) ); \ VIPS_UNROLL( shrink->hshrink, INNER( BANDS ) ); \
q[b] = (sum + shrink->xshrink / 2) / \ q[b] = (sum + shrink->hshrink / 2) / \
shrink->xshrink; \ shrink->hshrink; \
} \ } \
p += ne; \ p += ne; \
q += BANDS; \ q += BANDS; \
@ -100,8 +102,8 @@ G_DEFINE_TYPE( VipsShrinkh, vips_shrinkh, VIPS_TYPE_RESAMPLE );
\ \
sum = 0.0; \ sum = 0.0; \
x1 = b; \ x1 = b; \
VIPS_UNROLL( shrink->xshrink, INNER( bands ) ); \ VIPS_UNROLL( shrink->hshrink, INNER( bands ) ); \
q[b] = sum / shrink->xshrink; \ q[b] = sum / shrink->hshrink; \
} \ } \
p += ne; \ p += ne; \
q += bands; \ q += bands; \
@ -118,9 +120,9 @@ vips_shrinkh_gen2( VipsShrinkh *shrink, VipsRegion *or, VipsRegion *ir,
const int bands = resample->in->Bands * const int bands = resample->in->Bands *
(vips_band_format_iscomplex( resample->in->BandFmt ) ? (vips_band_format_iscomplex( resample->in->BandFmt ) ?
2 : 1); 2 : 1);
const int ne = shrink->xshrink * bands; const int ne = shrink->hshrink * bands;
VipsPel *out = VIPS_REGION_ADDR( or, left, top ); VipsPel *out = VIPS_REGION_ADDR( or, left, top );
VipsPel *in = VIPS_REGION_ADDR( ir, left * shrink->xshrink, top ); VipsPel *in = VIPS_REGION_ADDR( ir, left * shrink->hshrink, top );
int x; int x;
int x1, b; int x1, b;
@ -200,9 +202,9 @@ vips_shrinkh_gen( VipsRegion *or, void *seq,
for( y = 0; y < r->height; y ++ ) { for( y = 0; y < r->height; y ++ ) {
VipsRect s; VipsRect s;
s.left = r->left * shrink->xshrink; s.left = r->left * shrink->hshrink;
s.top = r->top + y; s.top = r->top + y;
s.width = r->width * shrink->xshrink; s.width = r->width * shrink->hshrink;
s.height = 1; s.height = 1;
#ifdef DEBUG #ifdef DEBUG
printf( "shrinkh_gen: requesting line %d\n", s.top ); printf( "shrinkh_gen: requesting line %d\n", s.top );
@ -230,7 +232,7 @@ vips_shrinkh_build( VipsObject *object )
VipsResample *resample = VIPS_RESAMPLE( object ); VipsResample *resample = VIPS_RESAMPLE( object );
VipsShrinkh *shrink = (VipsShrinkh *) object; VipsShrinkh *shrink = (VipsShrinkh *) object;
VipsImage **t = (VipsImage **) VipsImage **t = (VipsImage **)
vips_object_local_array( object, 1 ); vips_object_local_array( object, 2 );
VipsImage *in; VipsImage *in;
@ -239,13 +241,13 @@ vips_shrinkh_build( VipsObject *object )
in = resample->in; in = resample->in;
if( shrink->xshrink < 1 ) { if( shrink->hshrink < 1 ) {
vips_error( class->nickname, vips_error( class->nickname,
"%s", _( "shrink factors should be >= 1" ) ); "%s", _( "shrink factors should be >= 1" ) );
return( -1 ); return( -1 );
} }
if( shrink->xshrink == 1 ) if( shrink->hshrink == 1 )
return( vips_image_write( in, resample->out ) ); return( vips_image_write( in, resample->out ) );
/* Unpack for processing. /* Unpack for processing.
@ -254,6 +256,17 @@ vips_shrinkh_build( VipsObject *object )
return( -1 ); return( -1 );
in = t[0]; in = t[0];
/* We need new pixels at the right so that we don't have small chunks
* to average down the right edge.
*/
if( vips_embed( in, &t[1],
0, 0,
in->Xsize + shrink->hshrink, in->Ysize,
"extend", VIPS_EXTEND_COPY,
NULL ) )
return( -1 );
in = t[1];
/* THINSTRIP will work, anything else will break seq mode. If you /* THINSTRIP will work, anything else will break seq mode. If you
* combine shrink with conv you'll need to use a line cache to maintain * combine shrink with conv you'll need to use a line cache to maintain
* sequentiality. * sequentiality.
@ -262,13 +275,15 @@ vips_shrinkh_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
return( -1 ); return( -1 );
/* Size output. Note: we round the output width down! /* 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 = in->Xsize / shrink->xshrink; resample->out->Xsize = VIPS_ROUND_UINT(
resample->in->Xsize / shrink->hshrink );
if( resample->out->Xsize <= 0 ) { if( resample->out->Xsize <= 0 ) {
vips_error( class->nickname, vips_error( class->nickname,
"%s", _( "image has shrunk to nothing" ) ); "%s", _( "image has shrunk to nothing" ) );
@ -307,11 +322,20 @@ vips_shrinkh_class_init( VipsShrinkhClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_INT( class, "hshrink", 8,
_( "Hshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrinkh, hshrink ),
1, 1000000, 1 );
/* The old name .. now use h and v everywhere.
*/
VIPS_ARG_INT( class, "xshrink", 8, VIPS_ARG_INT( class, "xshrink", 8,
_( "Xshrink" ), _( "Xshrink" ),
_( "Horizontal shrink factor" ), _( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrinkh, xshrink ), G_STRUCT_OFFSET( VipsShrinkh, hshrink ),
1, 1000000, 1 ); 1, 1000000, 1 );
} }
@ -325,11 +349,11 @@ vips_shrinkh_init( VipsShrinkh *shrink )
* vips_shrinkh: * vips_shrinkh:
* @in: input image * @in: input image
* @out: output image * @out: output image
* @xshrink: horizontal shrink * @hshrink: horizontal shrink
* @...: %NULL-terminated list of optional named arguments * @...: %NULL-terminated list of optional named arguments
* *
* Shrink @in horizontally by an integer factor. Each pixel in the output is * Shrink @in horizontally by an integer factor. Each pixel in the output is
* the average of the corresponding line of @xshrink pixels in the input. * the average of the corresponding line of @hshrink pixels in the input.
* *
* This is a very low-level operation: see vips_resize() for a more * This is a very low-level operation: see vips_resize() for a more
* convenient way to resize images. * convenient way to resize images.
@ -342,13 +366,13 @@ vips_shrinkh_init( VipsShrinkh *shrink )
* Returns: 0 on success, -1 on error * Returns: 0 on success, -1 on error
*/ */
int int
vips_shrinkh( VipsImage *in, VipsImage **out, int xshrink, ... ) vips_shrinkh( VipsImage *in, VipsImage **out, int hshrink, ... )
{ {
va_list ap; va_list ap;
int result; int result;
va_start( ap, xshrink ); va_start( ap, hshrink );
result = vips_call_split( "shrinkh", ap, in, out, xshrink ); result = vips_call_split( "shrinkh", ap, in, out, hshrink );
va_end( ap ); va_end( ap );
return( result ); return( result );

View File

@ -41,6 +41,8 @@
* 6/6/13 * 6/6/13
* - don't chunk horizontally, fixes seq problems with large shrink * - don't chunk horizontally, fixes seq problems with large shrink
* factors * factors
* 15/8/16
* - rename yshrink -> vshrink for greater consistency
*/ */
/* /*
@ -93,7 +95,7 @@
typedef struct _VipsShrinkv { typedef struct _VipsShrinkv {
VipsResample parent_instance; VipsResample parent_instance;
int yshrink; int vshrink;
size_t sizeof_line_buffer; size_t sizeof_line_buffer;
} VipsShrinkv; } VipsShrinkv;
@ -200,7 +202,7 @@ vips_shrinkv_add_line( VipsShrinkv *shrink, VipsShrinkvSequence *seq,
TYPE * restrict q = (TYPE *) out; \ TYPE * restrict q = (TYPE *) out; \
\ \
for( x = 0; x < sz; x++ ) \ for( x = 0; x < sz; x++ ) \
q[x] = (sum[x] + shrink->yshrink / 2) / shrink->yshrink; \ q[x] = (sum[x] + shrink->vshrink / 2) / shrink->vshrink; \
} }
/* Float average. /* Float average.
@ -210,7 +212,7 @@ vips_shrinkv_add_line( VipsShrinkv *shrink, VipsShrinkvSequence *seq,
TYPE * restrict q = (TYPE *) out; \ TYPE * restrict q = (TYPE *) out; \
\ \
for( x = 0; x < sz; x++ ) \ for( x = 0; x < sz; x++ ) \
q[x] = sum[x] / shrink->yshrink; \ q[x] = sum[x] / shrink->vshrink; \
} }
/* Average the line of sums to out. /* Average the line of sums to out.
@ -287,11 +289,11 @@ vips_shrinkv_gen( VipsRegion *or, void *vseq,
for( y = 0; y < r->height; y++ ) { for( y = 0; y < r->height; y++ ) {
memset( seq->sum, 0, shrink->sizeof_line_buffer ); memset( seq->sum, 0, shrink->sizeof_line_buffer );
for( y1 = 0; y1 < shrink->yshrink; y1++ ) { for( y1 = 0; y1 < shrink->vshrink; y1++ ) {
VipsRect s; VipsRect s;
s.left = r->left; s.left = r->left;
s.top = y1 + (y + r->top) * shrink->yshrink; s.top = y1 + (y + r->top) * shrink->vshrink;
s.width = r->width; s.width = r->width;
s.height = 1; s.height = 1;
#ifdef DEBUG #ifdef DEBUG
@ -328,7 +330,7 @@ vips_shrinkv_build( VipsObject *object )
VipsResample *resample = VIPS_RESAMPLE( object ); VipsResample *resample = VIPS_RESAMPLE( object );
VipsShrinkv *shrink = (VipsShrinkv *) object; VipsShrinkv *shrink = (VipsShrinkv *) object;
VipsImage **t = (VipsImage **) VipsImage **t = (VipsImage **)
vips_object_local_array( object, 1 ); vips_object_local_array( object, 2 );
VipsImage *in; VipsImage *in;
@ -337,13 +339,13 @@ vips_shrinkv_build( VipsObject *object )
in = resample->in; in = resample->in;
if( shrink->yshrink < 1 ) { if( shrink->vshrink < 1 ) {
vips_error( class->nickname, vips_error( class->nickname,
"%s", _( "shrink factors should be >= 1" ) ); "%s", _( "shrink factors should be >= 1" ) );
return( -1 ); return( -1 );
} }
if( shrink->yshrink == 1 ) if( shrink->vshrink == 1 )
return( vips_image_write( in, resample->out ) ); return( vips_image_write( in, resample->out ) );
/* Unpack for processing. /* Unpack for processing.
@ -352,6 +354,17 @@ vips_shrinkv_build( VipsObject *object )
return( -1 ); return( -1 );
in = t[0]; in = t[0];
/* We need new pixels along the bottom so that we don't have small chunks
* to average along the bottom edge.
*/
if( vips_embed( in, &t[1],
0, 0,
in->Xsize, in->Ysize + shrink->vshrink,
"extend", VIPS_EXTEND_COPY,
NULL ) )
return( -1 );
in = t[1];
/* We have to keep a line buffer as we sum columns. /* We have to keep a line buffer as we sum columns.
*/ */
shrink->sizeof_line_buffer = shrink->sizeof_line_buffer =
@ -366,13 +379,15 @@ vips_shrinkv_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
return( -1 ); return( -1 );
/* Size output. Note: we round the output width down! /* 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 = in->Ysize / shrink->yshrink; resample->out->Ysize = VIPS_ROUND_UINT(
resample->in->Ysize / shrink->vshrink );
if( resample->out->Ysize <= 0 ) { if( resample->out->Ysize <= 0 ) {
vips_error( class->nickname, vips_error( class->nickname,
"%s", _( "image has shrunk to nothing" ) ); "%s", _( "image has shrunk to nothing" ) );
@ -411,11 +426,20 @@ vips_shrinkv_class_init( VipsShrinkvClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_INT( class, "vshrink", 9,
_( "Vshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrinkv, vshrink ),
1, 1000000, 1 );
/* The old name .. now use h and v everywhere.
*/
VIPS_ARG_INT( class, "yshrink", 9, VIPS_ARG_INT( class, "yshrink", 9,
_( "Yshrink" ), _( "Yshrink" ),
_( "Vertical shrink factor" ), _( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT, VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrinkv, yshrink ), G_STRUCT_OFFSET( VipsShrinkv, vshrink ),
1, 1000000, 1 ); 1, 1000000, 1 );
} }
@ -429,11 +453,11 @@ vips_shrinkv_init( VipsShrinkv *shrink )
* vips_shrinkv: * vips_shrinkv:
* @in: input image * @in: input image
* @out: output image * @out: output image
* @yshrink: vertical shrink * @vshrink: vertical shrink
* @...: %NULL-terminated list of optional named arguments * @...: %NULL-terminated list of optional named arguments
* *
* Shrink @in vertically by an integer factor. Each pixel in the output is * Shrink @in vertically by an integer factor. Each pixel in the output is
* the average of the corresponding column of @yshrink pixels in the input. * the average of the corresponding column of @vshrink pixels in the input.
* *
* This is a very low-level operation: see vips_resize() for a more * This is a very low-level operation: see vips_resize() for a more
* convenient way to resize images. * convenient way to resize images.
@ -446,13 +470,13 @@ vips_shrinkv_init( VipsShrinkv *shrink )
* Returns: 0 on success, -1 on error * Returns: 0 on success, -1 on error
*/ */
int int
vips_shrinkv( VipsImage *in, VipsImage **out, int yshrink, ... ) vips_shrinkv( VipsImage *in, VipsImage **out, int vshrink, ... )
{ {
va_list ap; va_list ap;
int result; int result;
va_start( ap, yshrink ); va_start( ap, vshrink );
result = vips_call_split( "shrinkv", ap, in, out, yshrink ); result = vips_call_split( "shrinkv", ap, in, out, vshrink );
va_end( ap ); va_end( ap );
return( result ); return( result );

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

View File

@ -168,19 +168,25 @@ class TestResample(unittest.TestCase):
def test_resize(self): def test_resize(self):
im = Vips.Image.new_from_file("images/йцук.jpg") im = Vips.Image.new_from_file("images/йцук.jpg")
im2 = im.resize(0.25) im2 = im.resize(0.25)
self.assertEqual(im2.width, im.width // 4) self.assertEqual(im2.width, round(im.width / 4.0))
self.assertEqual(im2.height, im.height // 4) self.assertEqual(im2.height, round(im.height / 4.0))
# test geometry rounding corner case
im = Vips.Image.black(100, 1);
x = im.resize(0.5)
self.assertEqual(x.width, 50)
self.assertEqual(x.height, 1)
def test_shrink(self): def test_shrink(self):
im = Vips.Image.new_from_file("images/йцук.jpg") im = Vips.Image.new_from_file("images/йцук.jpg")
im2 = im.shrink(4, 4) im2 = im.shrink(4, 4)
self.assertEqual(im2.width, im.width // 4) self.assertEqual(im2.width, round(im.width / 4.0))
self.assertEqual(im2.height, im.height // 4) self.assertEqual(im2.height, round(im.height / 4.0))
self.assertTrue(abs(im.avg() - im2.avg()) < 1) self.assertTrue(abs(im.avg() - im2.avg()) < 1)
im2 = im.shrink(2.5, 2.5) im2 = im.shrink(2.5, 2.5)
self.assertEqual(im2.width, im.width // 2.5) self.assertEqual(im2.width, round(im.width / 2.5))
self.assertEqual(im2.height, im.height // 2.5) self.assertEqual(im2.height, round(im.height / 2.5))
self.assertLess(abs(im.avg() - im2.avg()), 1) self.assertLess(abs(im.avg() - im2.avg()), 1)
def test_similarity(self): def test_similarity(self):

View File

@ -83,6 +83,8 @@
* - no need to guess max-alpha now premultiply does this for us * - no need to guess max-alpha now premultiply does this for us
* 1/8/16 * 1/8/16
* - use scRGB as the working space in linear mode * - use scRGB as the working space in linear mode
* 15/8/16
* - can now remove 0.1 rounding adjustment
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -192,12 +194,9 @@ calculate_shrink( VipsImage *im )
* *
* In crop mode, we aim to fill the bounding box, so we must use the * In crop mode, we aim to fill the bounding box, so we must use the
* smaller axis. * smaller axis.
*
* Add a small amount so when vips_resize() later rounds down, we
* don't round below target.
*/ */
double horizontal = (double) width / (thumbnail_width + 0.1); double horizontal = (double) width / thumbnail_width;
double vertical = (double) height / (thumbnail_height + 0.1); double vertical = (double) height / thumbnail_height;
if( crop_image ) { if( crop_image ) {
if( horizontal < vertical ) if( horizontal < vertical )