Merge branch 'master' into windows-unicode
This commit is contained in:
commit
57e1423d57
@ -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
|
||||
|
@ -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
41
TODO
@ -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?
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 );
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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 { \
|
||||
|
@ -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;
|
||||
|
@ -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*/
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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 );
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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 )
|
||||
|
Loading…
Reference in New Issue
Block a user