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]
- rename boostrap.sh -> autogen.sh to help snapcraft
- 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
- fix performance regression in 8.3.2, thanks Lovell

View File

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

41
TODO
View File

@ -1,40 +1,17 @@
- not sure about utf8 error messages
- 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
- not sure about utf8 error messages on win
- 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?

View File

@ -37,9 +37,9 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date`
# binary interface changes backwards compatible?: increment age
# binary interface changes not backwards compatible?: reset age to 0
LIBRARY_CURRENT=46
LIBRARY_REVISION=3
LIBRARY_AGE=4
LIBRARY_CURRENT=47
LIBRARY_REVISION=1
LIBRARY_AGE=5
# patched into include/vips/version.h
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 );
VImage add( 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 fitssave( char * filename , VOption *options = 0 );
VImage mapim( VImage index , VOption *options = 0 );
VImage shrink( double xshrink , double yshrink , VOption *options = 0 );
VImage shrinkh( int xshrink , VOption *options = 0 );
VImage shrinkv( int yshrink , VOption *options = 0 );
VImage reduceh( double xshrink , VOption *options = 0 );
VImage reducev( double yshrink , VOption *options = 0 );
VImage reduce( double xshrink , double yshrink , VOption *options = 0 );
VImage shrink( double hshrink , double vshrink , VOption *options = 0 );
VImage shrinkh( int hshrink , VOption *options = 0 );
VImage shrinkv( int vshrink , VOption *options = 0 );
VImage reduceh( double hshrink , VOption *options = 0 );
VImage reducev( double vshrink , VOption *options = 0 );
VImage reduce( double hshrink , double vshrink , VOption *options = 0 );
VImage quadratic( VImage coeff , VOption *options = 0 );
VImage affine( std::vector<double> matrix , 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 );
double hist_entropy( 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 convsep( VImage mask , VOption *options = 0 );
VImage convasep( VImage mask , VOption *options = 0 );
VImage fastcor( VImage ref , VOption *options = 0 );
VImage spcor( VImage ref , 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 )
{
call( "system" ,
@ -1849,7 +1853,7 @@ VImage VImage::mapim( VImage index , VOption *options )
return( out );
}
VImage VImage::shrink( double xshrink , double yshrink , VOption *options )
VImage VImage::shrink( double hshrink , double vshrink , VOption *options )
{
VImage out;
@ -1857,13 +1861,13 @@ VImage VImage::shrink( double xshrink , double yshrink , VOption *options )
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "xshrink", xshrink ) ->
set( "yshrink", yshrink ) );
set( "hshrink", hshrink ) ->
set( "vshrink", vshrink ) );
return( out );
}
VImage VImage::shrinkh( int xshrink , VOption *options )
VImage VImage::shrinkh( int hshrink , VOption *options )
{
VImage out;
@ -1871,12 +1875,12 @@ VImage VImage::shrinkh( int xshrink , VOption *options )
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "xshrink", xshrink ) );
set( "hshrink", hshrink ) );
return( out );
}
VImage VImage::shrinkv( int yshrink , VOption *options )
VImage VImage::shrinkv( int vshrink , VOption *options )
{
VImage out;
@ -1884,12 +1888,12 @@ VImage VImage::shrinkv( int yshrink , VOption *options )
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "yshrink", yshrink ) );
set( "vshrink", vshrink ) );
return( out );
}
VImage VImage::reduceh( double xshrink , VOption *options )
VImage VImage::reduceh( double hshrink , VOption *options )
{
VImage out;
@ -1897,12 +1901,12 @@ VImage VImage::reduceh( double xshrink , VOption *options )
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "xshrink", xshrink ) );
set( "hshrink", hshrink ) );
return( out );
}
VImage VImage::reducev( double yshrink , VOption *options )
VImage VImage::reducev( double vshrink , VOption *options )
{
VImage out;
@ -1910,12 +1914,12 @@ VImage VImage::reducev( double yshrink , VOption *options )
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "yshrink", yshrink ) );
set( "vshrink", vshrink ) );
return( out );
}
VImage VImage::reduce( double xshrink , double yshrink , VOption *options )
VImage VImage::reduce( double hshrink , double vshrink , VOption *options )
{
VImage out;
@ -1923,8 +1927,8 @@ VImage VImage::reduce( double xshrink , double yshrink , VOption *options )
(options ? options : VImage::option()) ->
set( "in", *this ) ->
set( "out", &out ) ->
set( "xshrink", xshrink ) ->
set( "yshrink", yshrink ) );
set( "hshrink", hshrink ) ->
set( "vshrink", vshrink ) );
return( out );
}
@ -2509,6 +2513,45 @@ VImage VImage::conv( VImage mask , VOption *options )
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 out;
@ -2535,6 +2578,19 @@ VImage VImage::convsep( VImage mask , VOption *options )
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 out;

View File

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

View File

@ -1265,7 +1265,11 @@ vips_icc_ac2rc( VipsImage *in, VipsImage **out, const char *profile_filename )
*
* If @embedded is set, the input profile is taken from the input image
* 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
* @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
* metadata, if present. If there is no embedded profile,
* @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
* @input_profile. If @input_profile is not supplied, the
* 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
* second half of this operation in isolation.
*

View File

@ -50,14 +50,6 @@
#include "pconversion.h"
/* Round N down to P boundary.
*/
#define ROUND_DOWN( N, P ) ((N) - ((N) % P))
/* Round N up to P boundary.
*/
#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) ))
typedef struct _VipsArrayjoin {
VipsConversion parent_instance;
@ -179,7 +171,7 @@ vips_arrayjoin_build( VipsObject *object )
/* How many images down the grid?
*/
join->down = ROUND_UP( n, join->across ) / join->across;
join->down = VIPS_ROUND_UP( n, join->across ) / join->across;
/* The output size.
*/

View File

@ -98,14 +98,6 @@ typedef VipsConversionClass VipsZoomClass;
G_DEFINE_TYPE( VipsZoom, vips_zoom, VIPS_TYPE_CONVERSION );
/* Round N down to P boundary.
*/
#define ROUND_DOWN( N, P ) ((N) - ((N) % P))
/* Round N up to P boundary.
*/
#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) ))
/* Paint the part of the region containing only whole pels.
*/
static void
@ -265,10 +257,10 @@ vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
/* Area of input we need. We have to round out, as we may have
* part-pixels all around the edges.
*/
left = ROUND_DOWN( r->left, zoom->xfac );
right = ROUND_UP( ri, zoom->xfac );
top = ROUND_DOWN( r->top, zoom->yfac );
bottom = ROUND_UP( bo, zoom->yfac );
left = VIPS_ROUND_DOWN( r->left, zoom->xfac );
right = VIPS_ROUND_UP( ri, zoom->xfac );
top = VIPS_ROUND_DOWN( r->top, zoom->yfac );
bottom = VIPS_ROUND_UP( bo, zoom->yfac );
width = right - left;
height = bottom - top;
s.left = left / zoom->xfac;
@ -280,10 +272,10 @@ vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
/* Find the part of the output (if any) which uses only whole pels.
*/
left = ROUND_UP( r->left, zoom->xfac );
right = ROUND_DOWN( ri, zoom->xfac );
top = ROUND_UP( r->top, zoom->yfac );
bottom = ROUND_DOWN( bo, zoom->yfac );
left = VIPS_ROUND_UP( r->left, zoom->xfac );
right = VIPS_ROUND_DOWN( ri, zoom->xfac );
top = VIPS_ROUND_UP( r->top, zoom->yfac );
bottom = VIPS_ROUND_DOWN( bo, zoom->yfac );
width = right - left;
height = bottom - top;

View File

@ -71,14 +71,6 @@ typedef struct _VipsPerlinClass {
G_DEFINE_TYPE( VipsPerlin, vips_perlin, VIPS_TYPE_CREATE );
/* Round N down to P boundary.
*/
#define ROUND_DOWN( N, P ) ((N) - ((N) % P))
/* Round N up to P boundary.
*/
#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) ))
/* cos and sin from an angle in 0 - 255.
*/
float vips_perlin_cos[256];
@ -261,9 +253,11 @@ vips_perlin_build( VipsObject *object )
/* Be careful if width is a multiple of cell_size.
*/
perlin->cells_across = ROUND_UP( perlin->width, perlin->cell_size ) /
perlin->cells_across =
VIPS_ROUND_UP( perlin->width, perlin->cell_size ) /
perlin->cell_size;
perlin->cells_down = ROUND_UP( perlin->height, perlin->cell_size ) /
perlin->cells_down =
VIPS_ROUND_UP( perlin->height, perlin->cell_size ) /
perlin->cell_size;
perlin->seed = g_random_double() * 0xffffffffu;

View File

@ -75,14 +75,6 @@ G_DEFINE_TYPE( VipsWorley, vips_worley, VIPS_TYPE_CREATE );
#define MAX_FEATURES (10)
/* Round N down to P boundary.
*/
#define ROUND_DOWN( N, P ) ((N) - ((N) % P))
/* Round N up to P boundary.
*/
#define ROUND_UP( N, P ) (ROUND_DOWN( (N) + (P) - 1, (P) ))
typedef struct _Cell {
/* Cell position, in number of cells. Scale by cell_size to get
* absolute image cods.
@ -294,9 +286,11 @@ vips_worley_build( VipsObject *object )
/* Be careful if width is a multiple of cell_size.
*/
worley->cells_across = ROUND_UP( worley->width, worley->cell_size ) /
worley->cells_across =
VIPS_ROUND_UP( worley->width, worley->cell_size ) /
worley->cell_size;
worley->cells_down = ROUND_UP( worley->height, worley->cell_size ) /
worley->cells_down =
VIPS_ROUND_UP( worley->height, worley->cell_size ) /
worley->cell_size;
worley->seed = g_random_double() * 0xffffffffu;

View File

@ -285,14 +285,6 @@ vips__make_xml_metadata( const char *domain, VipsImage *image )
#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.
*
* We need to be able to do scattered writes to structured files. So while
@ -672,8 +664,10 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above,
layer->width = width;
layer->height = height;
layer->tiles_across = ROUND_UP( width, dz->tile_size ) / dz->tile_size;
layer->tiles_down = ROUND_UP( height, dz->tile_size ) / dz->tile_size;
layer->tiles_across = VIPS_ROUND_UP( width, dz->tile_size ) /
dz->tile_size;
layer->tiles_down = VIPS_ROUND_UP( height, dz->tile_size ) /
dz->tile_size;
layer->real_pixels = *real_pixels;

View File

@ -9,6 +9,8 @@
* - write 1, 2, 3, or 4 bands depending on file contents
* 17/8/16
* - 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 &&
extension &&
extension[0] == 4 &&
extension[1] == 1 ) {
(extension[1] & 0x1) ) {
/* Bytes are 4, 1, delay low, delay high,
* transparency.
* transparency. Bit 1 means transparency
* is being set.
*/
gif->transparency = extension[4];
gif->has_transparency = TRUE;
@ -850,7 +853,7 @@ vips_foreign_load_gif_buffer_init( VipsForeignLoadGifBuffer *buffer )
*
* 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.
*
@ -885,7 +888,7 @@ vips_gifload( const char *filename, VipsImage **out, ... )
*
* 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
* vips_gifload(), but read from a memory buffer.

View File

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

View File

@ -375,14 +375,6 @@ vips__tiff_openin( const char *path )
return( tif );
}
/* Round N down to P boundary.
*/
#define ROUND_DOWN(N,P) ((N) - ((N) % P))
/* Round N up to P boundary.
*/
#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) ))
static Layer *
pyramid_new( Write *write, Layer *above, int width, int height )
{
@ -1029,7 +1021,7 @@ write_new( VipsImage *im, const char *filename,
if( im->Coding == VIPS_CODING_LABQ )
write->tls = write->tilew * 3;
else if( write->onebit )
write->tls = ROUND_UP( write->tilew, 8 ) / 8;
write->tls = VIPS_ROUND_UP( write->tilew, 8 ) / 8;
else
write->tls = VIPS_IMAGE_SIZEOF_PEL( im ) * write->tilew;

View File

@ -48,15 +48,20 @@ typedef enum {
} VipsKernel;
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));
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,
double xshrink, double yshrink, ... );
int vips_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... );
int vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... );
double hshrink, double vshrink, ... )
__attribute__((sentinel));
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, ... )
__attribute__((sentinel));

View File

@ -53,11 +53,14 @@ extern "C" {
#define VIPS_MAX( A, B ) ((A) > (B) ? (A) : (B))
#define VIPS_MIN( A, B ) ((A) < (B) ? (A) : (B))
#define VIPS_ABS( X ) (((X) >= 0) ? (X) : -(X))
#define VIPS_CLIP( A, V, B ) VIPS_MAX( (A), VIPS_MIN( (B), (V) ) )
#define VIPS_FCLIP( A, V, B ) VIPS_FMAX( (A), VIPS_FMIN( (B), (V) ) )
#define VIPS_NUMBER( R ) ((int) (sizeof(R) / sizeof(R[0])))
#define VIPS_ABS( X ) (((X) >= 0) ? (X) : -(X))
/* The built-in isnan and isinf functions provided by gcc 4+ and clang are
* up to 7x faster than their libc equivalent included from <math.h>.
*/
@ -67,6 +70,7 @@ extern "C" {
#define VIPS_FLOOR( V ) __builtin_floor( V )
#define VIPS_CEIL( V ) __builtin_ceil( V )
#define VIPS_RINT( V ) __builtin_rint( V )
#define VIPS_ROUND( V ) __builtin_round( V )
#define VIPS_FABS( V ) __builtin_fabs( V )
#define VIPS_FMAX( A, B ) __builtin_fmax( A, B )
#define VIPS_FMIN( A, B ) __builtin_fmin( A, B )
@ -75,13 +79,25 @@ extern "C" {
#define VIPS_ISINF( V ) isinf( V )
#define VIPS_FLOOR( V ) floor( V )
#define VIPS_CEIL( V ) ceil( V )
#define VIPS_RINT( R ) ((int) ((R) > 0 ? ((R) + 0.5) : ((R) - 0.5)))
#define VIPS_RINT( R ) rint( V )
#define VIPS_ROUND( V ) round( V )
#define VIPS_FABS( V ) VIPS_ABS( V )
#define VIPS_FMAX( A, B ) VIPS_MAX( A, B )
#define VIPS_FMIN( A, B ) VIPS_MIN( A, B )
#endif
#define VIPS_FCLIP( A, V, B ) VIPS_FMAX( (A), VIPS_FMIN( (B), (V) ) )
/* VIPS_RINT() does "bankers rounding", it rounds to the nerarest even integer.
* For things like image geometry, we want strict nearest int.
*
* If you know it's unsigned, _UINT is a little faster.
*/
#define VIPS_ROUND_INT( R ) ((int) ((R) > 0 ? ((R) + 0.5) : ((R) - 0.5)))
#define VIPS_ROUND_UINT( R ) ((int) ((R) + 0.5))
/* Round N down and up to the nearest multiple of P.
*/
#define VIPS_ROUND_DOWN( N, P ) ((N) - ((N) % (P)))
#define VIPS_ROUND_UP( N, P ) (VIPS_ROUND_DOWN( (N) + (P) - 1, (P) ))
#define VIPS_SWAP( TYPE, A, B ) \
G_STMT_START { \

View File

@ -168,13 +168,11 @@ vips__b64_encode( const unsigned char *data, size_t data_length )
int i;
int cursor;
if( data_length == 0 ) {
vips_error( "vips__b64_encode", "%s", _( "too little data" ) );
return( NULL );
}
if( output_data_length > 1024 * 1024 ) {
if( output_data_length > 10 * 1024 * 1024 ) {
/* We shouldn't really be used for large amounts of data, plus
* 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" ) );
return( NULL );
@ -228,9 +226,10 @@ vips__b64_decode( const char *buffer, size_t *data_length )
{
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 *p;

View File

@ -467,8 +467,13 @@ vips_leak( void )
vips_buf_append_size( &buf, vips_tracked_get_mem_highwater() );
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 ) );
#ifdef DEBUG
vips_buffer_dump_all();
#endif /*DEBUG*/

View File

@ -699,7 +699,11 @@ vips_operation_set_valist_required( VipsOperation *operation, va_list ap )
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 );
#ifdef VIPS_DEBUG

View File

@ -922,14 +922,6 @@ vips_threadpool_run( VipsImage *im,
return( result );
}
/* Round N down to P boundary.
*/
#define ROUND_DOWN(N,P) ((N) - ((N) % P))
/* Round N up to P boundary.
*/
#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) ))
/**
* vips_get_tile_size:
* @im: image to guess for
@ -986,7 +978,7 @@ vips_get_tile_size( VipsImage *im,
(1 + nthr / VIPS_MAX( 1, im->Xsize / vips__tile_width )) * 2;
*n_lines = VIPS_MAX( *n_lines, vips__fatstrip_height * nthr * 2 );
*n_lines = VIPS_MAX( *n_lines, vips__thinstrip_height * nthr * 2 );
*n_lines = ROUND_UP( *n_lines, *tile_height );
*n_lines = VIPS_ROUND_UP( *n_lines, *tile_height );
/* We make this assumption in several places.
*/

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_free( b64 );
}
else
/* No error return from transform, but we should set it to
* something.
*/
vips_value_set_save_string( dest_value, "" );
}
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 )) )
vips_value_set_blob( dest_value,
(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

View File

@ -2,6 +2,8 @@
*
* 27/1/16
* - from shrink.c
* 15/8/16
* - rename xshrink -> hshrink for greater consistency
*/
/*
@ -66,8 +68,8 @@
typedef struct _VipsReduce {
VipsResample parent_instance;
double xshrink; /* Shrink factors */
double yshrink;
double hshrink; /* Shrink factors */
double vshrink;
/* 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 ) )
return( -1 );
if( vips_reducev( resample->in, &t[0], reduce->yshrink,
if( vips_reducev( resample->in, &t[0], reduce->vshrink,
"kernel", reduce->kernel,
NULL ) ||
vips_reduceh( t[0], &t[1], reduce->xshrink,
vips_reduceh( t[0], &t[1], reduce->hshrink,
"kernel", reduce->kernel,
NULL ) ||
vips_image_write( t[1], resample->out ) )
@ -120,18 +122,18 @@ vips_reduce_class_init( VipsReduceClass *class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_DOUBLE( class, "xshrink", 8,
_( "Xshrink" ),
VIPS_ARG_DOUBLE( class, "hshrink", 8,
_( "Hshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduce, xshrink ),
G_STRUCT_OFFSET( VipsReduce, hshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_DOUBLE( class, "yshrink", 9,
_( "Yshrink" ),
VIPS_ARG_DOUBLE( class, "vshrink", 9,
_( "Vshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduce, yshrink ),
G_STRUCT_OFFSET( VipsReduce, vshrink ),
1.0, 1000000.0, 1.0 );
VIPS_ARG_ENUM( class, "kernel", 3,
@ -141,6 +143,22 @@ vips_reduce_class_init( VipsReduceClass *class )
G_STRUCT_OFFSET( VipsReduce, kernel ),
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
@ -153,8 +171,8 @@ vips_reduce_init( VipsReduce *reduce )
* vips_reduce:
* @in: input image
* @out: output image
* @xshrink: horizontal shrink
* @yshrink: vertical shrink
* @hshrink: horizontal shrink
* @vshrink: vertical shrink
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
@ -176,13 +194,13 @@ vips_reduce_init( VipsReduce *reduce )
*/
int
vips_reduce( VipsImage *in, VipsImage **out,
double xshrink, double yshrink, ... )
double hshrink, double vshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "reduce", ap, in, out, xshrink, yshrink );
va_start( ap, vshrink );
result = vips_call_split( "reduce", ap, in, out, hshrink, vshrink );
va_end( ap );
return( result );

View File

@ -4,6 +4,8 @@
* - from shrinkh.c
* 10/3/16
* - add other kernels
* 15/8/16
* - rename xshrink as hshrink for consistency
*/
/*
@ -67,7 +69,7 @@
typedef struct _VipsReduceh {
VipsResample parent_instance;
double xshrink; /* Reduce factor */
double hshrink; /* Reduce factor */
/* The thing we use to make the kernel.
*/
@ -276,7 +278,7 @@ reduceh_notab( VipsReduceh *reduceh,
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++ ) {
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 );
#endif /*DEBUG*/
s.left = r->left * reduceh->xshrink;
s.left = r->left * reduceh->hshrink;
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;
if( vips_region_prepare( ir, &s ) )
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 );
X = r->left * reduceh->xshrink;
X = r->left * reduceh->hshrink;
/* 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
@ -411,7 +413,7 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
break;
}
X += reduceh->xshrink;
X += reduceh->hshrink;
q += ps;
}
}
@ -439,19 +441,19 @@ vips_reduceh_build( VipsObject *object )
in = resample->in;
if( reduceh->xshrink < 1 ) {
if( reduceh->hshrink < 1 ) {
vips_error( object_class->nickname,
"%s", _( "reduce factors should be >= 1" ) );
return( -1 );
}
if( reduceh->xshrink == 1 )
if( reduceh->hshrink == 1 )
return( vips_image_write( in, resample->out ) );
/* Build the tables of pre-computed coefficients.
*/
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 );
if( reduceh->n_point > MAX_POINT ) {
vips_error( object_class->nickname,
@ -468,7 +470,7 @@ vips_reduceh_build( VipsObject *object )
return( -1 );
vips_reduce_make_mask( reduceh->matrixf[x],
reduceh->kernel, reduceh->xshrink,
reduceh->kernel, reduceh->hshrink,
(float) x / VIPS_TRANSFORM_SCALE );
for( int i = 0; i < reduceh->n_point; i++ )
@ -496,14 +498,15 @@ vips_reduceh_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
return( -1 );
/* Size output. Note: we round to nearest to hide rounding errors.
/* Size output. We need to always round to nearest, so round(), not
* rint().
*
* Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true reduce factor (including the
* fractional part), we just see the integer part here.
*/
resample->out->Xsize = VIPS_RINT(
(in->Xsize - reduceh->n_point + 1) / reduceh->xshrink );
resample->out->Xsize = VIPS_ROUND_UINT(
(in->Xsize - reduceh->n_point + 1) / reduceh->hshrink );
if( resample->out->Xsize <= 0 ) {
vips_error( object_class->nickname,
"%s", _( "image has shrunk to nothing" ) );
@ -543,11 +546,11 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( reduceh_class, "xshrink", 3,
_( "Xshrink" ),
VIPS_ARG_DOUBLE( reduceh_class, "hshrink", 3,
_( "Hshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReduceh, xshrink ),
G_STRUCT_OFFSET( VipsReduceh, hshrink ),
1, 1000000, 1 );
VIPS_ARG_ENUM( reduceh_class, "kernel", 3,
@ -557,6 +560,15 @@ vips_reduceh_class_init( VipsReducehClass *reduceh_class )
G_STRUCT_OFFSET( VipsReduceh, kernel ),
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
@ -569,7 +581,7 @@ vips_reduceh_init( VipsReduceh *reduceh )
* vips_reduceh:
* @in: input image
* @out: output image
* @xshrink: horizontal reduce
* @hshrink: horizontal reduce
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
@ -590,13 +602,13 @@ vips_reduceh_init( VipsReduceh *reduceh )
* Returns: 0 on success, -1 on error
*/
int
vips_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... )
vips_reduceh( VipsImage *in, VipsImage **out, double hshrink, ... )
{
va_list ap;
int result;
va_start( ap, xshrink );
result = vips_call_split( "reduceh", ap, in, out, xshrink );
va_start( ap, hshrink );
result = vips_call_split( "reduceh", ap, in, out, hshrink );
va_end( ap );
return( result );

View File

@ -11,6 +11,8 @@
* equal to the target scale
* 15/6/16
* - better accuracy with smarter multiplication
* 15/8/16
* - rename yshrink as vshrink for consistency
*/
/*
@ -92,7 +94,7 @@ typedef struct {
typedef struct _VipsReducev {
VipsResample parent_instance;
double yshrink; /* Shrink factor */
double vshrink; /* Shrink factor */
/* The thing we use to make the kernel.
*/
@ -491,7 +493,7 @@ reducev_notab( VipsReducev *reducev,
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++ )
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*/
s.left = r->left;
s.top = r->top * reducev->yshrink;
s.top = r->top * reducev->vshrink;
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 ) )
return( -1 );
@ -532,7 +534,7 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
for( int y = 0; y < r->height; y ++ ) {
VipsPel *q =
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 );
const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
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*/
s.left = r->left;
s.top = r->top * reducev->yshrink;
s.top = r->top * reducev->vshrink;
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 ) )
return( -1 );
@ -651,7 +653,7 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
for( int y = 0; y < r->height; y ++ ) {
VipsPel *q =
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 sy = Y * VIPS_TRANSFORM_SCALE * 2;
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
@ -719,7 +721,7 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in )
return( -1 );
vips_reduce_make_mask( reducev->matrixf[y],
reducev->kernel, reducev->yshrink,
reducev->kernel, reducev->vshrink,
(float) y / VIPS_TRANSFORM_SCALE );
#ifdef DEBUG
@ -773,14 +775,15 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in )
VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) )
return( -1 );
/* Size output. Note: we round to nearest to hide rounding errors.
/* Size output. We need to always round to nearest, so round(), not
* rint().
*
* Don't change xres/yres, leave that to the application layer. For
* example, vipsthumbnail knows the true reduce factor (including the
* fractional part), we just see the integer part here.
*/
resample->out->Ysize = VIPS_RINT(
(in->Ysize - reducev->n_point + 1) / reducev->yshrink );
resample->out->Ysize = VIPS_ROUND_UINT(
(in->Ysize - reducev->n_point + 1) / reducev->vshrink );
if( resample->out->Ysize <= 0 ) {
vips_error( object_class->nickname,
"%s", _( "image has shrunk to nothing" ) );
@ -816,17 +819,17 @@ vips_reducev_build( VipsObject *object )
in = resample->in;
if( reducev->yshrink < 1 ) {
if( reducev->vshrink < 1 ) {
vips_error( object_class->nickname,
"%s", _( "reduce factor should be >= 1" ) );
return( -1 );
}
if( reducev->yshrink == 1 )
if( reducev->vshrink == 1 )
return( vips_image_write( in, resample->out ) );
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 ) {
vips_error( object_class->nickname,
"%s", _( "reduce factor too large" ) );
@ -876,20 +879,29 @@ vips_reducev_class_init( VipsReducevClass *reducev_class )
operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED;
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3,
_( "Xshrink" ),
VIPS_ARG_DOUBLE( reducev_class, "vshrink", 3,
_( "Vshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsReducev, yshrink ),
G_STRUCT_OFFSET( VipsReducev, vshrink ),
1, 1000000, 1 );
VIPS_ARG_ENUM( reducev_class, "kernel", 3,
VIPS_ARG_ENUM( reducev_class, "kernel", 4,
_( "Kernel" ),
_( "Resampling kernel" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsReducev, kernel ),
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
@ -902,7 +914,7 @@ vips_reducev_init( VipsReducev *reducev )
* vips_reducev:
* @in: input image
* @out: output image
* @yshrink: horizontal reduce
* @vshrink: horizontal reduce
* @...: %NULL-terminated list of optional named arguments
*
* Optional arguments:
@ -923,13 +935,13 @@ vips_reducev_init( VipsReducev *reducev )
* Returns: 0 on success, -1 on error
*/
int
vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... )
vips_reducev( VipsImage *in, VipsImage **out, double vshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "reducev", ap, in, out, yshrink );
va_start( ap, vshrink );
result = vips_call_split( "reducev", ap, in, out, vshrink );
va_end( ap );
return( result );

View File

@ -18,6 +18,8 @@
* reduce
* 22/6/16
* - 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 *in;
int target_width;
int target_height;
double hscale;
double vscale;
int int_hshrink;
int int_vshrink;
double hresidual;
double vresidual;
if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) )
return( -1 );
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" ) )
target_height = in->Ysize * resize->vscale;
vscale = resize->vscale;
else
target_height = in->Ysize * resize->scale;
vscale = resize->scale;
int_hshrink = vips_resize_int_shrink( resize, resize->scale );
if( vips_object_argument_isset( object, "vscale" ) )
int_vshrink = vips_resize_int_shrink( resize, resize->vscale );
else
int_vshrink = int_hshrink;
/* The int part of our scale.
*/
int_hshrink = vips_resize_int_shrink( resize, hscale );
int_vshrink = vips_resize_int_shrink( resize, vscale );
if( int_vshrink > 1 ) {
vips_info( class->nickname, "shrinkv by %d", int_vshrink );
if( vips_shrinkv( in, &t[0], int_vshrink, NULL ) )
return( -1 );
in = t[0];
vscale *= int_vshrink;
}
if( int_hshrink > 1 ) {
@ -195,16 +196,9 @@ vips_resize_build( VipsObject *object )
if( vips_shrinkh( in, &t[1], int_hshrink, NULL ) )
return( -1 );
in = t[1];
}
/* Do we need a further size adjustment? It's the difference
* 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;
hscale *= int_hshrink;
}
/* We will get overcomputation on vips_shrink() from the vips_reduce()
* 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,
&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],
"tile_width", in->Xsize,
"tile_height", 10,
@ -253,20 +247,20 @@ vips_resize_build( VipsObject *object )
/* Any residual downsizing.
*/
if( vresidual < 1.0 ) {
if( vscale < 1.0 ) {
vips_info( class->nickname, "residual reducev by %g",
vresidual );
if( vips_reducev( in, &t[2], 1.0 / vresidual,
vscale );
if( vips_reducev( in, &t[2], 1.0 / vscale,
"kernel", resize->kernel,
NULL ) )
return( -1 );
in = t[2];
}
if( hresidual < 1.0 ) {
if( hscale < 1.0 ) {
vips_info( class->nickname, "residual reduceh by %g",
hresidual );
if( vips_reduceh( in, &t[3], 1.0 / hresidual,
hscale );
if( vips_reduceh( in, &t[3], 1.0 / hscale,
"kernel", resize->kernel,
NULL ) )
return( -1 );
@ -275,8 +269,8 @@ vips_resize_build( VipsObject *object )
/* Any upsizing.
*/
if( hresidual > 1.0 ||
vresidual > 1.0 ) {
if( hscale > 1.0 ||
vscale > 1.0 ) {
const char *nickname = vips_resize_interpolate( resize->kernel );
VipsInterpolate *interpolate;
@ -284,21 +278,21 @@ vips_resize_build( VipsObject *object )
return( -1 );
vips_object_local( object, interpolate );
if( hresidual > 1.0 &&
vresidual > 1.0 ) {
if( hscale > 1.0 &&
vscale > 1.0 ) {
vips_info( class->nickname,
"residual scale %g x %g", hresidual, vresidual );
"residual scale %g x %g", hscale, vscale );
if( vips_affine( in, &t[4],
hresidual, 0.0, 0.0, vresidual,
hscale, 0.0, 0.0, vscale,
"interpolate", interpolate,
NULL ) )
return( -1 );
in = t[4];
}
else if( hresidual > 1.0 ) {
else if( hscale > 1.0 ) {
vips_info( class->nickname,
"residual scaleh %g", hresidual );
if( vips_affine( in, &t[4], hresidual, 0.0, 0.0, 1.0,
"residual scale %g", hscale );
if( vips_affine( in, &t[4], hscale, 0.0, 0.0, 1.0,
"interpolate", interpolate,
NULL ) )
return( -1 );
@ -306,8 +300,8 @@ vips_resize_build( VipsObject *object )
}
else {
vips_info( class->nickname,
"residual scalev %g", vresidual );
if( vips_affine( in, &t[4], 1.0, 0.0, 0.0, vresidual,
"residual scale %g", vscale );
if( vips_affine( in, &t[4], 1.0, 0.0, 0.0, vscale,
"interpolate", interpolate,
NULL ) )
return( -1 );

View File

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

View File

@ -4,6 +4,8 @@
* - from shrink.c
* 22/1/16
* - reorganise loops, 30% faster, vectorisable
* 15/8/16
* - rename xshrink -> hshrink for greater consistency
*/
/*
@ -55,7 +57,7 @@
typedef struct _VipsShrinkh {
VipsResample parent_instance;
int xshrink; /* Shrink factor */
int hshrink; /* Shrink factor */
} VipsShrinkh;
@ -79,9 +81,9 @@ G_DEFINE_TYPE( VipsShrinkh, vips_shrinkh, VIPS_TYPE_RESAMPLE );
\
sum = 0; \
x1 = b; \
VIPS_UNROLL( shrink->xshrink, INNER( BANDS ) ); \
q[b] = (sum + shrink->xshrink / 2) / \
shrink->xshrink; \
VIPS_UNROLL( shrink->hshrink, INNER( BANDS ) ); \
q[b] = (sum + shrink->hshrink / 2) / \
shrink->hshrink; \
} \
p += ne; \
q += BANDS; \
@ -100,8 +102,8 @@ G_DEFINE_TYPE( VipsShrinkh, vips_shrinkh, VIPS_TYPE_RESAMPLE );
\
sum = 0.0; \
x1 = b; \
VIPS_UNROLL( shrink->xshrink, INNER( bands ) ); \
q[b] = sum / shrink->xshrink; \
VIPS_UNROLL( shrink->hshrink, INNER( bands ) ); \
q[b] = sum / shrink->hshrink; \
} \
p += ne; \
q += bands; \
@ -118,9 +120,9 @@ vips_shrinkh_gen2( VipsShrinkh *shrink, VipsRegion *or, VipsRegion *ir,
const int bands = resample->in->Bands *
(vips_band_format_iscomplex( resample->in->BandFmt ) ?
2 : 1);
const int ne = shrink->xshrink * bands;
const int ne = shrink->hshrink * bands;
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 x1, b;
@ -200,9 +202,9 @@ vips_shrinkh_gen( VipsRegion *or, void *seq,
for( y = 0; y < r->height; y ++ ) {
VipsRect s;
s.left = r->left * shrink->xshrink;
s.left = r->left * shrink->hshrink;
s.top = r->top + y;
s.width = r->width * shrink->xshrink;
s.width = r->width * shrink->hshrink;
s.height = 1;
#ifdef DEBUG
printf( "shrinkh_gen: requesting line %d\n", s.top );
@ -230,7 +232,7 @@ vips_shrinkh_build( VipsObject *object )
VipsResample *resample = VIPS_RESAMPLE( object );
VipsShrinkh *shrink = (VipsShrinkh *) object;
VipsImage **t = (VipsImage **)
vips_object_local_array( object, 1 );
vips_object_local_array( object, 2 );
VipsImage *in;
@ -239,13 +241,13 @@ vips_shrinkh_build( VipsObject *object )
in = resample->in;
if( shrink->xshrink < 1 ) {
if( shrink->hshrink < 1 ) {
vips_error( class->nickname,
"%s", _( "shrink factors should be >= 1" ) );
return( -1 );
}
if( shrink->xshrink == 1 )
if( shrink->hshrink == 1 )
return( vips_image_write( in, resample->out ) );
/* Unpack for processing.
@ -254,6 +256,17 @@ vips_shrinkh_build( VipsObject *object )
return( -1 );
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
* combine shrink with conv you'll need to use a line cache to maintain
* sequentiality.
@ -262,13 +275,15 @@ vips_shrinkh_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
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
* example, vipsthumbnail knows the true shrink factor (including the
* 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 ) {
vips_error( class->nickname,
"%s", _( "image has shrunk to nothing" ) );
@ -307,11 +322,20 @@ vips_shrinkh_class_init( VipsShrinkhClass *class )
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,
_( "Xshrink" ),
_( "Horizontal shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrinkh, xshrink ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrinkh, hshrink ),
1, 1000000, 1 );
}
@ -325,11 +349,11 @@ vips_shrinkh_init( VipsShrinkh *shrink )
* vips_shrinkh:
* @in: input image
* @out: output image
* @xshrink: horizontal shrink
* @hshrink: horizontal shrink
* @...: %NULL-terminated list of optional named arguments
*
* 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
* convenient way to resize images.
@ -342,13 +366,13 @@ vips_shrinkh_init( VipsShrinkh *shrink )
* Returns: 0 on success, -1 on error
*/
int
vips_shrinkh( VipsImage *in, VipsImage **out, int xshrink, ... )
vips_shrinkh( VipsImage *in, VipsImage **out, int hshrink, ... )
{
va_list ap;
int result;
va_start( ap, xshrink );
result = vips_call_split( "shrinkh", ap, in, out, xshrink );
va_start( ap, hshrink );
result = vips_call_split( "shrinkh", ap, in, out, hshrink );
va_end( ap );
return( result );

View File

@ -41,6 +41,8 @@
* 6/6/13
* - don't chunk horizontally, fixes seq problems with large shrink
* factors
* 15/8/16
* - rename yshrink -> vshrink for greater consistency
*/
/*
@ -93,7 +95,7 @@
typedef struct _VipsShrinkv {
VipsResample parent_instance;
int yshrink;
int vshrink;
size_t sizeof_line_buffer;
} VipsShrinkv;
@ -200,7 +202,7 @@ vips_shrinkv_add_line( VipsShrinkv *shrink, VipsShrinkvSequence *seq,
TYPE * restrict q = (TYPE *) out; \
\
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.
@ -210,7 +212,7 @@ vips_shrinkv_add_line( VipsShrinkv *shrink, VipsShrinkvSequence *seq,
TYPE * restrict q = (TYPE *) out; \
\
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.
@ -287,11 +289,11 @@ vips_shrinkv_gen( VipsRegion *or, void *vseq,
for( y = 0; y < r->height; y++ ) {
memset( seq->sum, 0, shrink->sizeof_line_buffer );
for( y1 = 0; y1 < shrink->yshrink; y1++ ) {
for( y1 = 0; y1 < shrink->vshrink; y1++ ) {
VipsRect s;
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.height = 1;
#ifdef DEBUG
@ -328,7 +330,7 @@ vips_shrinkv_build( VipsObject *object )
VipsResample *resample = VIPS_RESAMPLE( object );
VipsShrinkv *shrink = (VipsShrinkv *) object;
VipsImage **t = (VipsImage **)
vips_object_local_array( object, 1 );
vips_object_local_array( object, 2 );
VipsImage *in;
@ -337,13 +339,13 @@ vips_shrinkv_build( VipsObject *object )
in = resample->in;
if( shrink->yshrink < 1 ) {
if( shrink->vshrink < 1 ) {
vips_error( class->nickname,
"%s", _( "shrink factors should be >= 1" ) );
return( -1 );
}
if( shrink->yshrink == 1 )
if( shrink->vshrink == 1 )
return( vips_image_write( in, resample->out ) );
/* Unpack for processing.
@ -352,6 +354,17 @@ vips_shrinkv_build( VipsObject *object )
return( -1 );
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.
*/
shrink->sizeof_line_buffer =
@ -366,13 +379,15 @@ vips_shrinkv_build( VipsObject *object )
VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) )
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
* example, vipsthumbnail knows the true shrink factor (including the
* 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 ) {
vips_error( class->nickname,
"%s", _( "image has shrunk to nothing" ) );
@ -411,11 +426,20 @@ vips_shrinkv_class_init( VipsShrinkvClass *class )
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,
_( "Yshrink" ),
_( "Vertical shrink factor" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsShrinkv, yshrink ),
VIPS_ARGUMENT_REQUIRED_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsShrinkv, vshrink ),
1, 1000000, 1 );
}
@ -429,11 +453,11 @@ vips_shrinkv_init( VipsShrinkv *shrink )
* vips_shrinkv:
* @in: input image
* @out: output image
* @yshrink: vertical shrink
* @vshrink: vertical shrink
* @...: %NULL-terminated list of optional named arguments
*
* 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
* convenient way to resize images.
@ -446,13 +470,13 @@ vips_shrinkv_init( VipsShrinkv *shrink )
* Returns: 0 on success, -1 on error
*/
int
vips_shrinkv( VipsImage *in, VipsImage **out, int yshrink, ... )
vips_shrinkv( VipsImage *in, VipsImage **out, int vshrink, ... )
{
va_list ap;
int result;
va_start( ap, yshrink );
result = vips_call_split( "shrinkv", ap, in, out, yshrink );
va_start( ap, vshrink );
result = vips_call_split( "shrinkv", ap, in, out, vshrink );
va_end( ap );
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 ) ) );
bottom = VIPS_MAX( y1, VIPS_MAX( y2, VIPS_MAX( y3, y4 ) ) );
out->left = VIPS_RINT( left );
out->top = VIPS_RINT( top );
out->width = VIPS_RINT( right - left );
out->height = VIPS_RINT( bottom - top );
out->left = VIPS_ROUND_INT( left );
out->top = VIPS_ROUND_INT( top );
out->width = VIPS_ROUND_INT( right - left );
out->height = VIPS_ROUND_INT( bottom - top );
}
/* Given an area in the input image, calculate the bounding box for those

View File

@ -168,19 +168,25 @@ class TestResample(unittest.TestCase):
def test_resize(self):
im = Vips.Image.new_from_file("images/йцук.jpg")
im2 = im.resize(0.25)
self.assertEqual(im2.width, im.width // 4)
self.assertEqual(im2.height, im.height // 4)
self.assertEqual(im2.width, round(im.width / 4.0))
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):
im = Vips.Image.new_from_file("images/йцук.jpg")
im2 = im.shrink(4, 4)
self.assertEqual(im2.width, im.width // 4)
self.assertEqual(im2.height, im.height // 4)
self.assertEqual(im2.width, round(im.width / 4.0))
self.assertEqual(im2.height, round(im.height / 4.0))
self.assertTrue(abs(im.avg() - im2.avg()) < 1)
im2 = im.shrink(2.5, 2.5)
self.assertEqual(im2.width, im.width // 2.5)
self.assertEqual(im2.height, im.height // 2.5)
self.assertEqual(im2.width, round(im.width / 2.5))
self.assertEqual(im2.height, round(im.height / 2.5))
self.assertLess(abs(im.avg() - im2.avg()), 1)
def test_similarity(self):

View File

@ -83,6 +83,8 @@
* - no need to guess max-alpha now premultiply does this for us
* 1/8/16
* - use scRGB as the working space in linear mode
* 15/8/16
* - can now remove 0.1 rounding adjustment
*/
#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
* 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 vertical = (double) height / (thumbnail_height + 0.1);
double horizontal = (double) width / thumbnail_width;
double vertical = (double) height / thumbnail_height;
if( crop_image ) {
if( horizontal < vertical )