seems to work
add @centre option for downsampling, see https://github.com/jcupitt/libvips/issues/504
This commit is contained in:
parent
1e9cdef615
commit
d3cafb3a9f
@ -43,6 +43,8 @@
|
||||
rounding down, thanks ioquatix
|
||||
- better support for tile overlaps in google maps mode in dzsave
|
||||
- dzsave puts vips-properties.xml in the main dir for gm and zoomify layouts
|
||||
- resize and reduce have @centre option for centre convention downsampling
|
||||
- vipsthumbnail uses centre convention to better match imagemagick
|
||||
|
||||
19/8/16 started 8.3.4
|
||||
- better transparency handling in gifload, thanks diegocsandrim
|
||||
|
8
TODO
8
TODO
@ -1,11 +1,3 @@
|
||||
- test possible centre hack
|
||||
|
||||
I think we're moving too far ... this is going to shift the mask by 50%, we
|
||||
just want half a pixel
|
||||
|
||||
put the shift into vips_reduce_make_mask() so it gets picked up by the double
|
||||
path too
|
||||
|
||||
- not sure about utf8 error messages on win
|
||||
|
||||
- strange:
|
||||
|
@ -4,6 +4,8 @@
|
||||
* - from shrink.c
|
||||
* 15/8/16
|
||||
* - rename xshrink -> hshrink for greater consistency
|
||||
* 9/9/16
|
||||
* - add @centre option
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -75,6 +77,10 @@ typedef struct _VipsReduce {
|
||||
*/
|
||||
VipsKernel kernel;
|
||||
|
||||
/* Use centre rather than corner sampling convention.
|
||||
*/
|
||||
gboolean centre;
|
||||
|
||||
} VipsReduce;
|
||||
|
||||
typedef VipsResampleClass VipsReduceClass;
|
||||
@ -94,9 +100,11 @@ vips_reduce_build( VipsObject *object )
|
||||
|
||||
if( vips_reducev( resample->in, &t[0], reduce->vshrink,
|
||||
"kernel", reduce->kernel,
|
||||
"centre", reduce->centre,
|
||||
NULL ) ||
|
||||
vips_reduceh( t[0], &t[1], reduce->hshrink,
|
||||
"kernel", reduce->kernel,
|
||||
"centre", reduce->centre,
|
||||
NULL ) ||
|
||||
vips_image_write( t[1], resample->out ) )
|
||||
return( -1 );
|
||||
@ -143,6 +151,13 @@ vips_reduce_class_init( VipsReduceClass *class )
|
||||
G_STRUCT_OFFSET( VipsReduce, kernel ),
|
||||
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "centre", 7,
|
||||
_( "Centre" ),
|
||||
_( "Use centre sampling convention" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsReduce, centre ),
|
||||
FALSE );
|
||||
|
||||
/* The old names .. now use h and v everywhere.
|
||||
*/
|
||||
VIPS_ARG_DOUBLE( class, "xshrink", 8,
|
||||
@ -178,10 +193,14 @@ vips_reduce_init( VipsReduce *reduce )
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @kernel: #VipsKernel to use to interpolate (default: lanczos3)
|
||||
* * @centre: %gboolean use centre rather than corner sampling convention
|
||||
*
|
||||
* Reduce @in by a pair of factors with a pair of 1D kernels. This
|
||||
* will not work well for shrink factors greater than three.
|
||||
*
|
||||
* Set @centre to use centre rather than corner sampling convention. Centre
|
||||
* convention can be useful to match the behaviour of other systems.
|
||||
*
|
||||
* This is a very low-level operation: see vips_resize() for a more
|
||||
* convenient way to resize images.
|
||||
*
|
||||
|
@ -6,6 +6,8 @@
|
||||
* - add other kernels
|
||||
* 15/8/16
|
||||
* - rename xshrink as hshrink for consistency
|
||||
* 9/9/16
|
||||
* - add @centre option
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -321,6 +323,8 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
|
||||
s.top = r->top;
|
||||
s.width = r->width * reduceh->hshrink + reduceh->n_point;
|
||||
s.height = r->height;
|
||||
if( reduceh->centre )
|
||||
s.width += 1;
|
||||
if( vips_region_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
@ -335,6 +339,8 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq,
|
||||
q = VIPS_REGION_ADDR( out_region, r->left, r->top + y );
|
||||
|
||||
X = r->left * reduceh->hshrink;
|
||||
if( reduceh->centre )
|
||||
X += 0.5;
|
||||
|
||||
/* 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
|
||||
@ -439,6 +445,7 @@ vips_reduceh_build( VipsObject *object )
|
||||
vips_object_local_array( object, 2 );
|
||||
|
||||
VipsImage *in;
|
||||
int width;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_reduceh_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
@ -465,8 +472,6 @@ vips_reduceh_build( VipsObject *object )
|
||||
return( -1 );
|
||||
}
|
||||
for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) {
|
||||
float fx;
|
||||
|
||||
reduceh->matrixf[x] =
|
||||
VIPS_ARRAY( object, reduceh->n_point, double );
|
||||
reduceh->matrixi[x] =
|
||||
@ -475,18 +480,9 @@ vips_reduceh_build( VipsObject *object )
|
||||
!reduceh->matrixi[x] )
|
||||
return( -1 );
|
||||
|
||||
/* Calculate our [0, 1] float displacement. For centre
|
||||
* convention we must shift by 0.5 and wrap around.
|
||||
*/
|
||||
fx = (float) x / VIPS_TRANSFORM_SCALE;
|
||||
if( reduceh->centre ) {
|
||||
fx += 0.5;
|
||||
if( fx > 1.0 )
|
||||
fx -= 1.0;
|
||||
}
|
||||
|
||||
vips_reduce_make_mask( reduceh->matrixf[x],
|
||||
reduceh->kernel, reduceh->hshrink, fx );
|
||||
reduceh->kernel, reduceh->hshrink,
|
||||
(float) x / VIPS_TRANSFORM_SCALE );
|
||||
|
||||
for( int i = 0; i < reduceh->n_point; i++ )
|
||||
reduceh->matrixi[x][i] = reduceh->matrixf[x][i] *
|
||||
@ -500,10 +496,15 @@ vips_reduceh_build( VipsObject *object )
|
||||
in = t[0];
|
||||
|
||||
/* Add new pixels around the input so we can interpolate at the edges.
|
||||
* In centre mode, we read 0.5 pixels more to the right, so we must
|
||||
* enlarge a little further.
|
||||
*/
|
||||
width = in->Xsize + reduceh->n_point - 1;
|
||||
if( reduceh->centre )
|
||||
width += 1;
|
||||
if( vips_embed( in, &t[1],
|
||||
reduceh->n_point / 2 - 1, 0,
|
||||
in->Xsize + reduceh->n_point - 1, in->Ysize,
|
||||
width, in->Ysize,
|
||||
"extend", VIPS_EXTEND_COPY,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
@ -521,7 +522,7 @@ vips_reduceh_build( VipsObject *object )
|
||||
* fractional part), we just see the integer part here.
|
||||
*/
|
||||
resample->out->Xsize = VIPS_ROUND_UINT(
|
||||
(in->Xsize - reduceh->n_point + 1) / reduceh->hshrink );
|
||||
resample->in->Xsize / reduceh->hshrink );
|
||||
if( resample->out->Xsize <= 0 ) {
|
||||
vips_error( object_class->nickname,
|
||||
"%s", _( "image has shrunk to nothing" ) );
|
||||
|
@ -13,6 +13,8 @@
|
||||
* - better accuracy with smarter multiplication
|
||||
* 15/8/16
|
||||
* - rename yshrink as vshrink for consistency
|
||||
* 9/9/16
|
||||
* - add @centre option
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -100,6 +102,10 @@ typedef struct _VipsReducev {
|
||||
*/
|
||||
VipsKernel kernel;
|
||||
|
||||
/* Use centre rather than corner sampling convention.
|
||||
*/
|
||||
gboolean centre;
|
||||
|
||||
/* Number of points in kernel.
|
||||
*/
|
||||
int n_point;
|
||||
@ -526,6 +532,8 @@ vips_reducev_gen( VipsRegion *out_region, void *vseq,
|
||||
s.top = r->top * reducev->vshrink;
|
||||
s.width = r->width;
|
||||
s.height = r->height * reducev->vshrink + reducev->n_point;
|
||||
if( reducev->centre )
|
||||
s.height += 1;
|
||||
if( vips_region_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
@ -534,7 +542,8 @@ 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->vshrink;
|
||||
const double Y = (r->top + y) * reducev->vshrink +
|
||||
(reducev->centre ? 0.5 : 0.0);
|
||||
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);
|
||||
@ -636,6 +645,8 @@ vips_reducev_vector_gen( VipsRegion *out_region, void *vseq,
|
||||
s.top = r->top * reducev->vshrink;
|
||||
s.width = r->width;
|
||||
s.height = r->height * reducev->vshrink + reducev->n_point;
|
||||
if( reducev->centre )
|
||||
s.height += 1;
|
||||
if( vips_region_prepare( ir, &s ) )
|
||||
return( -1 );
|
||||
|
||||
@ -653,7 +664,8 @@ 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->vshrink;
|
||||
const double Y = (r->top + y) * reducev->vshrink +
|
||||
(reducev->centre ? 0.5 : 0.0);
|
||||
const int py = (int) Y;
|
||||
const int sy = Y * VIPS_TRANSFORM_SCALE * 2;
|
||||
const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1);
|
||||
@ -783,7 +795,7 @@ vips_reducev_raw( VipsReducev *reducev, VipsImage *in )
|
||||
* fractional part), we just see the integer part here.
|
||||
*/
|
||||
resample->out->Ysize = VIPS_ROUND_UINT(
|
||||
(in->Ysize - reducev->n_point + 1) / reducev->vshrink );
|
||||
resample->in->Ysize / reducev->vshrink );
|
||||
if( resample->out->Ysize <= 0 ) {
|
||||
vips_error( object_class->nickname,
|
||||
"%s", _( "image has shrunk to nothing" ) );
|
||||
@ -813,6 +825,7 @@ vips_reducev_build( VipsObject *object )
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
|
||||
|
||||
VipsImage *in;
|
||||
int height;
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_reducev_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
@ -845,9 +858,12 @@ vips_reducev_build( VipsObject *object )
|
||||
|
||||
/* Add new pixels around the input so we can interpolate at the edges.
|
||||
*/
|
||||
height = in->Ysize + reducev->n_point - 1;
|
||||
if( reducev->centre )
|
||||
height += 1;
|
||||
if( vips_embed( in, &t[1],
|
||||
0, reducev->n_point / 2 - 1,
|
||||
in->Xsize, in->Ysize + reducev->n_point - 1,
|
||||
in->Xsize, height,
|
||||
"extend", VIPS_EXTEND_COPY,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
@ -893,6 +909,13 @@ vips_reducev_class_init( VipsReducevClass *reducev_class )
|
||||
G_STRUCT_OFFSET( VipsReducev, kernel ),
|
||||
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
|
||||
|
||||
VIPS_ARG_BOOL( reducev_class, "centre", 7,
|
||||
_( "Centre" ),
|
||||
_( "Use centre sampling convention" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsReducev, centre ),
|
||||
FALSE );
|
||||
|
||||
/* Old name.
|
||||
*/
|
||||
VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3,
|
||||
@ -920,9 +943,13 @@ vips_reducev_init( VipsReducev *reducev )
|
||||
* Optional arguments:
|
||||
*
|
||||
* * @kernel: #VipsKernel to use to interpolate (default: lanczos3)
|
||||
* * @centre: %gboolean use centre rather than corner sampling convention
|
||||
*
|
||||
* Reduce @in vertically by a float factor. The pixels in @out are
|
||||
* interpolated with a 1D mask.
|
||||
* interpolated with a 1D mask generated by @kernel.
|
||||
*
|
||||
* Set @centre to use centre rather than corner sampling convention. Centre
|
||||
* convention can be useful to match the behaviour of other systems.
|
||||
*
|
||||
* This is a very low-level operation: see vips_resize() for a more
|
||||
* convenient way to resize images.
|
||||
|
@ -20,6 +20,8 @@
|
||||
* - faster and better upsizing
|
||||
* 15/8/16
|
||||
* - more accurate resizing
|
||||
* 9/9/16
|
||||
* - add @centre option
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -78,6 +80,7 @@ typedef struct _VipsResize {
|
||||
double scale;
|
||||
double vscale;
|
||||
VipsKernel kernel;
|
||||
gboolean centre;
|
||||
|
||||
/* Deprecated.
|
||||
*/
|
||||
@ -252,6 +255,7 @@ vips_resize_build( VipsObject *object )
|
||||
vscale );
|
||||
if( vips_reducev( in, &t[2], 1.0 / vscale,
|
||||
"kernel", resize->kernel,
|
||||
"centre", resize->centre,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[2];
|
||||
@ -262,6 +266,7 @@ vips_resize_build( VipsObject *object )
|
||||
hscale );
|
||||
if( vips_reduceh( in, &t[3], 1.0 / hscale,
|
||||
"kernel", resize->kernel,
|
||||
"centre", resize->centre,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
in = t[3];
|
||||
@ -354,6 +359,13 @@ vips_resize_class_init( VipsResizeClass *class )
|
||||
G_STRUCT_OFFSET( VipsResize, kernel ),
|
||||
VIPS_TYPE_KERNEL, VIPS_KERNEL_LANCZOS3 );
|
||||
|
||||
VIPS_ARG_BOOL( class, "centre", 7,
|
||||
_( "Centre" ),
|
||||
_( "Use centre sampling convention" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsResize, centre ),
|
||||
FALSE );
|
||||
|
||||
/* We used to let people set the input offset so you could pick centre
|
||||
* or corner interpolation, but it's not clear this was useful.
|
||||
*/
|
||||
@ -398,6 +410,7 @@ vips_resize_init( VipsResize *resize )
|
||||
*
|
||||
* * @vscale: %gdouble vertical scale factor
|
||||
* * @kernel: #VipsKernel to reduce with
|
||||
* * @centre: %gboolean use centre rather than corner sampling convention
|
||||
*
|
||||
* Resize an image.
|
||||
*
|
||||
@ -410,6 +423,9 @@ vips_resize_init( VipsResize *resize )
|
||||
* vips_resize() normally uses #VIPS_KERNEL_LANCZOS3 for the final reduce, you
|
||||
* can change this with @kernel.
|
||||
*
|
||||
* Set @centre to use centre rather than corner sampling convention. Centre
|
||||
* convention can be useful to match the behaviour of other systems.
|
||||
*
|
||||
* When upsizing (@scale > 1), the operation uses vips_affine() with
|
||||
* a #VipsInterpolate selected depending on @kernel. It will use
|
||||
* #VipsInterpolateBicubic for #VIPS_KERNEL_CUBIC and above.
|
||||
|
@ -450,7 +450,7 @@ thumbnail_shrink( VipsObject *process, VipsImage *in )
|
||||
|
||||
shrink = calculate_shrink( in );
|
||||
|
||||
if( vips_resize( in, &t[4], 1.0 / shrink, NULL ) )
|
||||
if( vips_resize( in, &t[4], 1.0 / shrink, "centre", TRUE, NULL ) )
|
||||
return( NULL );
|
||||
in = t[4];
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user