first attempt
seems to all work, test it a bit more see https://github.com/jcupitt/libvips/issues/491
This commit is contained in:
parent
91e31cb830
commit
1f403a4add
@ -37,6 +37,8 @@
|
||||
- support --strip for pngsave
|
||||
- add svgz support [Felix Bünemann]
|
||||
- rename boostrap.sh -> autogen.sh to help snapcraft
|
||||
- resize/reduce/shrink now round output size to nearest rather than rounding
|
||||
down, thanks ioquatix
|
||||
|
||||
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
|
||||
|
11
TODO
11
TODO
@ -1,3 +1,14 @@
|
||||
- strange:
|
||||
|
||||
$ vips similarity --scale 0.33 k2.jpg x.v
|
||||
$ vipsheader k2.jpg
|
||||
k2.jpg: 1450x2048 uchar, 3 bands, srgb, jpegload
|
||||
$ vipsheader x.v
|
||||
x.v: 478x676 uchar, 3 bands, srgb, jpegload
|
||||
|
||||
1450 * 0.33 = 478.5 ... this was rounded down
|
||||
2048 * 0.33 = 675.84 ... this was rounded up
|
||||
|
||||
- add APPROX convsep test?
|
||||
|
||||
- add more webp tests to py suite
|
||||
|
@ -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));
|
||||
|
@ -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
|
||||
|
@ -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++ )
|
||||
@ -503,7 +505,7 @@ vips_reduceh_build( VipsObject *object )
|
||||
* fractional part), we just see the integer part here.
|
||||
*/
|
||||
resample->out->Xsize = VIPS_RINT(
|
||||
(in->Xsize - reduceh->n_point + 1) / reduceh->xshrink );
|
||||
(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 +545,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 +559,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 +580,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 +601,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
|
||||
@ -780,7 +782,7 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in )
|
||||
* fractional part), we just see the integer part here.
|
||||
*/
|
||||
resample->out->Ysize = VIPS_RINT(
|
||||
(in->Ysize - reducev->n_point + 1) / reducev->yshrink );
|
||||
(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 +818,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 +878,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 +913,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 +934,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,14 @@ 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 rint() from the size of the original image.
|
||||
*
|
||||
* 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_RINT(
|
||||
resample->in->Xsize / shrink->hshrink );
|
||||
if( resample->out->Xsize <= 0 ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "image has shrunk to nothing" ) );
|
||||
@ -307,11 +321,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 +348,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 +365,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,14 @@ 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 rint() from the size of the original image.
|
||||
*
|
||||
* 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_RINT(
|
||||
resample->in->Ysize / shrink->vshrink );
|
||||
if( resample->out->Ysize <= 0 ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "image has shrunk to nothing" ) );
|
||||
@ -411,11 +425,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 +452,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 +469,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 );
|
||||
|
@ -167,19 +167,19 @@ class TestResample(unittest.TestCase):
|
||||
def test_resize(self):
|
||||
im = Vips.Image.new_from_file("images/IMG_4618.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))
|
||||
|
||||
def test_shrink(self):
|
||||
im = Vips.Image.new_from_file("images/IMG_4618.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