fix jaggies on the edge of affine output
and add a "background" param
This commit is contained in:
parent
ef9b23b95a
commit
fcec6d639b
@ -35,6 +35,8 @@
|
|||||||
- add vips_fill_nearest() ... fill pixels with nearest colour
|
- add vips_fill_nearest() ... fill pixels with nearest colour
|
||||||
- add VIPS_COMBINE_MIN, a new combining mode for vips_compass()
|
- add VIPS_COMBINE_MIN, a new combining mode for vips_compass()
|
||||||
- vips_hist_find_indexed() now has a @combine parameter
|
- vips_hist_find_indexed() now has a @combine parameter
|
||||||
|
- vips_affine() and vips_similarity() have a "background" parameter
|
||||||
|
- fix nasty jaggies on the edge of affine output
|
||||||
|
|
||||||
29/8/17 started 8.5.9
|
29/8/17 started 8.5.9
|
||||||
- make --fail stop jpeg read on any libjpeg warning, thanks @mceachen
|
- make --fail stop jpeg read on any libjpeg warning, thanks @mceachen
|
||||||
|
@ -162,7 +162,7 @@ void vips__region_no_ownership( struct _VipsRegion *reg );
|
|||||||
|
|
||||||
typedef int (*VipsRegionFillFn)( struct _VipsRegion *, void * );
|
typedef int (*VipsRegionFillFn)( struct _VipsRegion *, void * );
|
||||||
int vips_region_fill( struct _VipsRegion *reg,
|
int vips_region_fill( struct _VipsRegion *reg,
|
||||||
VipsRect *r, VipsRegionFillFn fn, void *a );
|
const VipsRect *r, VipsRegionFillFn fn, void *a );
|
||||||
|
|
||||||
int vips__image_wio_output( struct _VipsImage *image );
|
int vips__image_wio_output( struct _VipsImage *image );
|
||||||
int vips__image_pio_output( struct _VipsImage *image );
|
int vips__image_pio_output( struct _VipsImage *image );
|
||||||
@ -185,7 +185,7 @@ void vips_region_dump_all( void );
|
|||||||
*/
|
*/
|
||||||
int vips__init( const char *argv0 );
|
int vips__init( const char *argv0 );
|
||||||
size_t vips__get_sizeof_vipsobject( void );
|
size_t vips__get_sizeof_vipsobject( void );
|
||||||
int vips_region_prepare_many( struct _VipsRegion **reg, VipsRect *r );
|
int vips_region_prepare_many( struct _VipsRegion **reg, const VipsRect *r );
|
||||||
|
|
||||||
/* Handy for debugging.
|
/* Handy for debugging.
|
||||||
*/
|
*/
|
||||||
|
@ -104,23 +104,25 @@ GType vips_region_get_type(void);
|
|||||||
|
|
||||||
VipsRegion *vips_region_new( VipsImage *image );
|
VipsRegion *vips_region_new( VipsImage *image );
|
||||||
|
|
||||||
int vips_region_buffer( VipsRegion *reg, VipsRect *r );
|
int vips_region_buffer( VipsRegion *reg, const VipsRect *r );
|
||||||
int vips_region_image( VipsRegion *reg, VipsRect *r );
|
int vips_region_image( VipsRegion *reg, const VipsRect *r );
|
||||||
int vips_region_region( VipsRegion *reg, VipsRegion *dest,
|
int vips_region_region( VipsRegion *reg, VipsRegion *dest,
|
||||||
VipsRect *r, int x, int y );
|
const VipsRect *r, int x, int y );
|
||||||
int vips_region_equalsregion( VipsRegion *reg1, VipsRegion *reg2 );
|
int vips_region_equalsregion( VipsRegion *reg1, VipsRegion *reg2 );
|
||||||
int vips_region_position( VipsRegion *reg, int x, int y );
|
int vips_region_position( VipsRegion *reg, int x, int y );
|
||||||
|
|
||||||
void vips_region_paint( VipsRegion *reg, VipsRect *r, int value );
|
void vips_region_paint( VipsRegion *reg, const VipsRect *r, int value );
|
||||||
void vips_region_paint_pel( VipsRegion *reg, VipsRect *r, VipsPel *ink );
|
void vips_region_paint_pel( VipsRegion *reg,
|
||||||
|
const VipsRect *r, const VipsPel *ink );
|
||||||
void vips_region_black( VipsRegion *reg );
|
void vips_region_black( VipsRegion *reg );
|
||||||
void vips_region_copy( VipsRegion *reg, VipsRegion *dest,
|
void vips_region_copy( VipsRegion *reg, VipsRegion *dest,
|
||||||
VipsRect *r, int x, int y );
|
const VipsRect *r, int x, int y );
|
||||||
int vips_region_shrink( VipsRegion *from, VipsRegion *to, VipsRect *target );
|
int vips_region_shrink( VipsRegion *from,
|
||||||
|
VipsRegion *to, const VipsRect *target );
|
||||||
|
|
||||||
int vips_region_prepare( VipsRegion *reg, VipsRect *r );
|
int vips_region_prepare( VipsRegion *reg, const VipsRect *r );
|
||||||
int vips_region_prepare_to( VipsRegion *reg,
|
int vips_region_prepare_to( VipsRegion *reg,
|
||||||
VipsRegion *dest, VipsRect *r, int x, int y );
|
VipsRegion *dest, const VipsRect *r, int x, int y );
|
||||||
|
|
||||||
void vips_region_invalidate( VipsRegion *reg );
|
void vips_region_invalidate( VipsRegion *reg );
|
||||||
|
|
||||||
|
@ -563,7 +563,7 @@ vips_region_new( VipsImage *image )
|
|||||||
* Returns: 0 on success, or -1 for error.
|
* Returns: 0 on success, or -1 for error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vips_region_buffer( VipsRegion *reg, VipsRect *r )
|
vips_region_buffer( VipsRegion *reg, const VipsRect *r )
|
||||||
{
|
{
|
||||||
VipsImage *im = reg->im;
|
VipsImage *im = reg->im;
|
||||||
|
|
||||||
@ -626,14 +626,14 @@ vips_region_buffer( VipsRegion *reg, VipsRect *r )
|
|||||||
* @reg: region to operate upon
|
* @reg: region to operate upon
|
||||||
* @r: #VipsRect of pixels you need to be able to address
|
* @r: #VipsRect of pixels you need to be able to address
|
||||||
*
|
*
|
||||||
* The region is transformed so that at least @r pixels are available to be read from
|
* The region is transformed so that at least @r pixels are available to be
|
||||||
* image. The image needs to be a memory buffer or represent a file
|
* read from the image. The image needs to be a memory buffer or represent a
|
||||||
* on disc that has been mapped or can be mapped.
|
* file on disc that has been mapped or can be mapped.
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, or -1 for error.
|
* Returns: 0 on success, or -1 for error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vips_region_image( VipsRegion *reg, VipsRect *r )
|
vips_region_image( VipsRegion *reg, const VipsRect *r )
|
||||||
{
|
{
|
||||||
VipsImage *image = reg->im;
|
VipsImage *image = reg->im;
|
||||||
|
|
||||||
@ -725,16 +725,15 @@ vips_region_image( VipsRegion *reg, VipsRect *r )
|
|||||||
* Performs all clipping necessary to ensure that @reg->valid is indeed
|
* Performs all clipping necessary to ensure that @reg->valid is indeed
|
||||||
* valid.
|
* valid.
|
||||||
*
|
*
|
||||||
* If the region we attach to is moved or destroyed, we can be left with dangling
|
* If the region we attach to is moved or destroyed, we can be left with
|
||||||
* pointers! If the region we attach to is on another image, the two images
|
* dangling pointers! If the region we attach to is on another image, the
|
||||||
* must have
|
* two images must have the same sizeof( pel ).
|
||||||
* the same sizeof( pel ).
|
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, or -1 for error.
|
* Returns: 0 on success, or -1 for error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vips_region_region( VipsRegion *reg,
|
vips_region_region( VipsRegion *reg,
|
||||||
VipsRegion *dest, VipsRect *r, int x, int y )
|
VipsRegion *dest, const VipsRect *r, int x, int y )
|
||||||
{
|
{
|
||||||
VipsRect image;
|
VipsRect image;
|
||||||
VipsRect wanted;
|
VipsRect wanted;
|
||||||
@ -885,7 +884,8 @@ vips_region_position( VipsRegion *reg, int x, int y )
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vips_region_fill( VipsRegion *reg, VipsRect *r, VipsRegionFillFn fn, void *a )
|
vips_region_fill( VipsRegion *reg,
|
||||||
|
const VipsRect *r, VipsRegionFillFn fn, void *a )
|
||||||
{
|
{
|
||||||
g_assert( reg->im->dtype == VIPS_IMAGE_PARTIAL );
|
g_assert( reg->im->dtype == VIPS_IMAGE_PARTIAL );
|
||||||
g_assert( reg->im->generate_fn );
|
g_assert( reg->im->generate_fn );
|
||||||
@ -947,7 +947,7 @@ vips_region_fill( VipsRegion *reg, VipsRect *r, VipsRegionFillFn fn, void *a )
|
|||||||
* See also: vips_region_black().
|
* See also: vips_region_black().
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
vips_region_paint( VipsRegion *reg, VipsRect *r, int value )
|
vips_region_paint( VipsRegion *reg, const VipsRect *r, int value )
|
||||||
{
|
{
|
||||||
VipsRect clipped;
|
VipsRect clipped;
|
||||||
|
|
||||||
@ -1011,7 +1011,7 @@ vips_region_paint( VipsRegion *reg, VipsRect *r, int value )
|
|||||||
* See also: vips_region_paint().
|
* See also: vips_region_paint().
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
vips_region_paint_pel( VipsRegion *reg, VipsRect *r, VipsPel *ink )
|
vips_region_paint_pel( VipsRegion *reg, const VipsRect *r, const VipsPel *ink )
|
||||||
{
|
{
|
||||||
VipsRect ovl;
|
VipsRect ovl;
|
||||||
|
|
||||||
@ -1076,7 +1076,8 @@ vips_region_black( VipsRegion *reg )
|
|||||||
* See also: vips_region_paint().
|
* See also: vips_region_paint().
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
vips_region_copy( VipsRegion *reg, VipsRegion *dest, VipsRect *r, int x, int y )
|
vips_region_copy( VipsRegion *reg,
|
||||||
|
VipsRegion *dest, const VipsRect *r, int x, int y )
|
||||||
{
|
{
|
||||||
int z;
|
int z;
|
||||||
int len = VIPS_IMAGE_SIZEOF_PEL( reg->im ) * r->width;
|
int len = VIPS_IMAGE_SIZEOF_PEL( reg->im ) * r->width;
|
||||||
@ -1135,7 +1136,8 @@ vips_region_copy( VipsRegion *reg, VipsRegion *dest, VipsRect *r, int x, int y )
|
|||||||
* VIPS_CODING_LABQ only.
|
* VIPS_CODING_LABQ only.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
vips_region_shrink_labpack( VipsRegion *from, VipsRegion *to, VipsRect *target )
|
vips_region_shrink_labpack( VipsRegion *from,
|
||||||
|
VipsRegion *to, const VipsRect *target )
|
||||||
{
|
{
|
||||||
int ls = VIPS_REGION_LSKIP( from );
|
int ls = VIPS_REGION_LSKIP( from );
|
||||||
|
|
||||||
@ -1212,7 +1214,8 @@ vips_region_shrink_labpack( VipsRegion *from, VipsRegion *to, VipsRect *target )
|
|||||||
/* Generate area @target in @to using pixels in @from. Non-complex.
|
/* Generate area @target in @to using pixels in @from. Non-complex.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
vips_region_shrink_uncoded( VipsRegion *from, VipsRegion *to, VipsRect *target )
|
vips_region_shrink_uncoded( VipsRegion *from,
|
||||||
|
VipsRegion *to, const VipsRect *target )
|
||||||
{
|
{
|
||||||
int ls = VIPS_REGION_LSKIP( from );
|
int ls = VIPS_REGION_LSKIP( from );
|
||||||
int ps = VIPS_IMAGE_SIZEOF_PEL( from->im );
|
int ps = VIPS_IMAGE_SIZEOF_PEL( from->im );
|
||||||
@ -1295,7 +1298,8 @@ vips_region_shrink_uncoded( VipsRegion *from, VipsRegion *to, VipsRect *target )
|
|||||||
* last band as alpha.
|
* last band as alpha.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
vips_region_shrink_alpha( VipsRegion *from, VipsRegion *to, VipsRect *target )
|
vips_region_shrink_alpha( VipsRegion *from,
|
||||||
|
VipsRegion *to, const VipsRect *target )
|
||||||
{
|
{
|
||||||
int ls = VIPS_REGION_LSKIP( from );
|
int ls = VIPS_REGION_LSKIP( from );
|
||||||
int nb = from->im->Bands;
|
int nb = from->im->Bands;
|
||||||
@ -1347,7 +1351,7 @@ vips_region_shrink_alpha( VipsRegion *from, VipsRegion *to, VipsRect *target )
|
|||||||
* See also: vips_region_copy().
|
* See also: vips_region_copy().
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vips_region_shrink( VipsRegion *from, VipsRegion *to, VipsRect *target )
|
vips_region_shrink( VipsRegion *from, VipsRegion *to, const VipsRect *target )
|
||||||
{
|
{
|
||||||
VipsImage *image = from->im;
|
VipsImage *image = from->im;
|
||||||
|
|
||||||
@ -1419,7 +1423,7 @@ vips_region_generate( VipsRegion *reg )
|
|||||||
* Returns: 0 on success, or -1 on error.
|
* Returns: 0 on success, or -1 on error.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vips_region_prepare( VipsRegion *reg, VipsRect *r )
|
vips_region_prepare( VipsRegion *reg, const VipsRect *r )
|
||||||
{
|
{
|
||||||
VipsImage *im = reg->im;
|
VipsImage *im = reg->im;
|
||||||
|
|
||||||
@ -1492,7 +1496,7 @@ vips_region_prepare( VipsRegion *reg, VipsRect *r )
|
|||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
vips_region_prepare_to_generate( VipsRegion *reg,
|
vips_region_prepare_to_generate( VipsRegion *reg,
|
||||||
VipsRegion *dest, VipsRect *r, int x, int y )
|
VipsRegion *dest, const VipsRect *r, int x, int y )
|
||||||
{
|
{
|
||||||
VipsImage *im = reg->im;
|
VipsImage *im = reg->im;
|
||||||
VipsPel *p;
|
VipsPel *p;
|
||||||
@ -1551,7 +1555,7 @@ vips_region_prepare_to_generate( VipsRegion *reg,
|
|||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vips_region_prepare_to( VipsRegion *reg,
|
vips_region_prepare_to( VipsRegion *reg,
|
||||||
VipsRegion *dest, VipsRect *r, int x, int y )
|
VipsRegion *dest, const VipsRect *r, int x, int y )
|
||||||
{
|
{
|
||||||
VipsImage *im = reg->im;
|
VipsImage *im = reg->im;
|
||||||
VipsRect image;
|
VipsRect image;
|
||||||
@ -1686,7 +1690,7 @@ vips_region_prepare_to( VipsRegion *reg,
|
|||||||
/* Don't use this, use vips_reorder_prepare_many() instead.
|
/* Don't use this, use vips_reorder_prepare_many() instead.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
vips_region_prepare_many( VipsRegion **reg, VipsRect *r )
|
vips_region_prepare_many( VipsRegion **reg, const VipsRect *r )
|
||||||
{
|
{
|
||||||
for( ; *reg; ++reg )
|
for( ; *reg; ++reg )
|
||||||
if( vips_region_prepare( *reg, r ) )
|
if( vips_region_prepare( *reg, r ) )
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/* affine transform with a supplied interpolator.
|
/* affine transform with a supplied interpolator.
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* Copyright N. Dessipris
|
* Copyright N. Dessipris
|
||||||
* Written on: 01/11/1991
|
* Written on: 01/11/1991
|
||||||
* Modified on: 12/3/92 JC
|
* Modified on: 12/3/92 JC
|
||||||
@ -84,6 +83,9 @@
|
|||||||
* 1/8/14
|
* 1/8/14
|
||||||
* - revise transform ... again
|
* - revise transform ... again
|
||||||
* - see new stress test in nip2/test/extras
|
* - see new stress test in nip2/test/extras
|
||||||
|
* 7/11/17
|
||||||
|
* - add "background" parameter
|
||||||
|
* - better clipping means we have no jaggies on edges
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -116,6 +118,7 @@
|
|||||||
/*
|
/*
|
||||||
#define DEBUG_VERBOSE
|
#define DEBUG_VERBOSE
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
|
#define VIPS_DEBUG
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
@ -149,6 +152,14 @@ typedef struct _VipsAffine {
|
|||||||
|
|
||||||
VipsTransformation trn;
|
VipsTransformation trn;
|
||||||
|
|
||||||
|
/* Background colour.
|
||||||
|
*/
|
||||||
|
VipsArrayDouble *background;
|
||||||
|
|
||||||
|
/* The [double] converted to the input image format.
|
||||||
|
*/
|
||||||
|
VipsPel *ink;
|
||||||
|
|
||||||
} VipsAffine;
|
} VipsAffine;
|
||||||
|
|
||||||
typedef VipsResampleClass VipsAffineClass;
|
typedef VipsResampleClass VipsAffineClass;
|
||||||
@ -275,7 +286,7 @@ vips_affine_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
|||||||
#endif /*DEBUG_VERBOSE*/
|
#endif /*DEBUG_VERBOSE*/
|
||||||
|
|
||||||
if( vips_rect_isempty( &clipped ) ) {
|
if( vips_rect_isempty( &clipped ) ) {
|
||||||
vips_region_black( or );
|
vips_region_paint_pel( or, r, affine->ink );
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
if( vips_region_prepare( ir, &clipped ) )
|
if( vips_region_prepare( ir, &clipped ) )
|
||||||
@ -336,9 +347,9 @@ vips_affine_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
|||||||
/* Clip against iarea.
|
/* Clip against iarea.
|
||||||
*/
|
*/
|
||||||
if( fx >= ile &&
|
if( fx >= ile &&
|
||||||
fx < iri &&
|
fx <= iri &&
|
||||||
fy >= ito &&
|
fy >= ito &&
|
||||||
fy < ibo ) {
|
fy <= ibo ) {
|
||||||
/* Verify that we can read the whole stencil.
|
/* Verify that we can read the whole stencil.
|
||||||
* With DEBUG on this will range-check.
|
* With DEBUG on this will range-check.
|
||||||
*/
|
*/
|
||||||
@ -355,8 +366,10 @@ vips_affine_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
|||||||
q, ir, ix, iy );
|
q, ir, ix, iy );
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
/* Out of range: paint the background.
|
||||||
|
*/
|
||||||
for( z = 0; z < ps; z++ )
|
for( z = 0; z < ps; z++ )
|
||||||
q[z] = 0;
|
q[z] = affine->ink[z];
|
||||||
}
|
}
|
||||||
|
|
||||||
ix += ddx;
|
ix += ddx;
|
||||||
@ -478,16 +491,36 @@ vips_affine_build( VipsObject *object )
|
|||||||
return( -1 );
|
return( -1 );
|
||||||
in = t[0];
|
in = t[0];
|
||||||
|
|
||||||
|
/* Convert the background to the image's format.
|
||||||
|
*/
|
||||||
|
if( !(affine->ink = vips__vector_to_ink( class->nickname,
|
||||||
|
resample->out,
|
||||||
|
VIPS_AREA( affine->background )->data, NULL,
|
||||||
|
VIPS_AREA( affine->background )->n )) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
/* Add new pixels around the input so we can interpolate at the edges.
|
/* Add new pixels around the input so we can interpolate at the edges.
|
||||||
|
*
|
||||||
|
* We add the interpolate stencil, plus one extra pixel on all the
|
||||||
|
* edges. This means when we clip in generate (above) we can be sure
|
||||||
|
* we clip outside the real pixels and don't get jaggies on edges.
|
||||||
*/
|
*/
|
||||||
if( vips_embed( in, &t[2],
|
if( vips_embed( in, &t[2],
|
||||||
window_offset, window_offset,
|
window_offset + 1, window_offset + 1,
|
||||||
in->Xsize + window_size - 1, in->Ysize + window_size - 1,
|
in->Xsize + window_size - 1 + 2,
|
||||||
"extend", VIPS_EXTEND_COPY,
|
in->Ysize + window_size - 1 + 2,
|
||||||
|
"extend", VIPS_EXTEND_BACKGROUND,
|
||||||
|
"background", affine->background,
|
||||||
NULL ) )
|
NULL ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
in = t[2];
|
in = t[2];
|
||||||
|
|
||||||
|
/* We've added a one-pixel border to the input: displace the transform
|
||||||
|
* to compensate.
|
||||||
|
*/
|
||||||
|
affine->trn.idx -= 1;
|
||||||
|
affine->trn.idy -= 1;
|
||||||
|
|
||||||
/* Normally SMALLTILE ... except if this is strictly a size
|
/* Normally SMALLTILE ... except if this is strictly a size
|
||||||
* up/down affine.
|
* up/down affine.
|
||||||
*/
|
*/
|
||||||
@ -591,11 +624,19 @@ vips_affine_class_init( VipsAffineClass *class )
|
|||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsAffine, idy ),
|
G_STRUCT_OFFSET( VipsAffine, idy ),
|
||||||
-10000000, 10000000, 0 );
|
-10000000, 10000000, 0 );
|
||||||
|
|
||||||
|
VIPS_ARG_BOXED( class, "background", 2,
|
||||||
|
_( "Background" ),
|
||||||
|
_( "Background value" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsAffine, background ),
|
||||||
|
VIPS_TYPE_ARRAY_DOUBLE );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vips_affine_init( VipsAffine *affine )
|
vips_affine_init( VipsAffine *affine )
|
||||||
{
|
{
|
||||||
|
affine->background = vips_array_double_newv( 1, 0.0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -616,6 +657,7 @@ vips_affine_init( VipsAffine *affine )
|
|||||||
* * @idy: %gdouble, input vertical offset
|
* * @idy: %gdouble, input vertical offset
|
||||||
* * @odx: %gdouble, output horizontal offset
|
* * @odx: %gdouble, output horizontal offset
|
||||||
* * @ody: %gdouble, output vertical offset
|
* * @ody: %gdouble, output vertical offset
|
||||||
|
* * @background: #VipsArrayDouble colour for new pixels
|
||||||
*
|
*
|
||||||
* This operator performs an affine transform on an image using @interpolate.
|
* This operator performs an affine transform on an image using @interpolate.
|
||||||
*
|
*
|
||||||
@ -633,6 +675,8 @@ vips_affine_init( VipsAffine *affine )
|
|||||||
* By default @oarea is just large enough to cover the whole of the
|
* By default @oarea is just large enough to cover the whole of the
|
||||||
* transformed input image.
|
* transformed input image.
|
||||||
*
|
*
|
||||||
|
* New pixels are filled with @background. This defaults to zero (black).
|
||||||
|
*
|
||||||
* @interpolate defaults to bilinear.
|
* @interpolate defaults to bilinear.
|
||||||
*
|
*
|
||||||
* @idx, @idy, @odx, @ody default to zero.
|
* @idx, @idy, @odx, @ody default to zero.
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
* - oops, missing scale from b, thanks Topochicho
|
* - oops, missing scale from b, thanks Topochicho
|
||||||
* 7/2/16
|
* 7/2/16
|
||||||
* - use vips_reduce(), if we can
|
* - use vips_reduce(), if we can
|
||||||
|
* 17/11/17
|
||||||
|
* ` - add optional "background" param
|
||||||
|
* ` - don't use vips_reduce() since it has no "background" param
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -65,6 +68,7 @@ typedef struct _VipsSimilarity {
|
|||||||
double ody;
|
double ody;
|
||||||
double idx;
|
double idx;
|
||||||
double idy;
|
double idy;
|
||||||
|
VipsArrayDouble *background;
|
||||||
|
|
||||||
} VipsSimilarity;
|
} VipsSimilarity;
|
||||||
|
|
||||||
@ -72,19 +76,6 @@ typedef VipsResampleClass VipsSimilarityClass;
|
|||||||
|
|
||||||
G_DEFINE_TYPE( VipsSimilarity, vips_similarity, VIPS_TYPE_RESAMPLE );
|
G_DEFINE_TYPE( VipsSimilarity, vips_similarity, VIPS_TYPE_RESAMPLE );
|
||||||
|
|
||||||
/* Map interpolator names to vips kernels.
|
|
||||||
*/
|
|
||||||
typedef struct _VipsInterpolateKernel {
|
|
||||||
const char *nickname;
|
|
||||||
VipsKernel kernel;
|
|
||||||
} VipsInterpolateKernel;
|
|
||||||
|
|
||||||
static VipsInterpolateKernel vips_similarity_kernel[] = {
|
|
||||||
{ "bicubic", VIPS_KERNEL_CUBIC },
|
|
||||||
{ "bilinear", VIPS_KERNEL_LINEAR },
|
|
||||||
{ "nearest", VIPS_KERNEL_NEAREST }
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vips_similarity_build( VipsObject *object )
|
vips_similarity_build( VipsObject *object )
|
||||||
{
|
{
|
||||||
@ -92,61 +83,23 @@ vips_similarity_build( VipsObject *object )
|
|||||||
VipsSimilarity *similarity = (VipsSimilarity *) object;
|
VipsSimilarity *similarity = (VipsSimilarity *) object;
|
||||||
VipsImage **t = (VipsImage **)
|
VipsImage **t = (VipsImage **)
|
||||||
vips_object_local_array( object, 4 );
|
vips_object_local_array( object, 4 );
|
||||||
|
double a = similarity->scale * cos( VIPS_RAD( similarity->angle ) );
|
||||||
gboolean handled;
|
double b = similarity->scale * -sin( VIPS_RAD( similarity->angle ) );
|
||||||
|
double c = -b;
|
||||||
|
double d = a;
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_similarity_parent_class )->build( object ) )
|
if( VIPS_OBJECT_CLASS( vips_similarity_parent_class )->build( object ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
handled = FALSE;
|
if( vips_affine( resample->in, &t[0], a, b, c, d,
|
||||||
|
"interpolate", similarity->interpolate,
|
||||||
/* Use vips_reduce(), if we can.
|
"odx", similarity->odx,
|
||||||
*/
|
"ody", similarity->ody,
|
||||||
if( similarity->interpolate &&
|
"idx", similarity->idx,
|
||||||
similarity->angle == 0.0 &&
|
"idy", similarity->idy,
|
||||||
similarity->idx == 0.0 &&
|
"background", similarity->background,
|
||||||
similarity->idy == 0.0 &&
|
NULL ) )
|
||||||
similarity->odx == 0.0 &&
|
return( -1 );
|
||||||
similarity->ody == 0.0 ) {
|
|
||||||
const char *nickname = VIPS_OBJECT_GET_CLASS(
|
|
||||||
similarity->interpolate )->nickname;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for( i = 0; i < VIPS_NUMBER( vips_similarity_kernel ); i++ ) {
|
|
||||||
VipsInterpolateKernel *ik = &vips_similarity_kernel[i];
|
|
||||||
|
|
||||||
if( strcmp( nickname, ik->nickname ) == 0 ) {
|
|
||||||
if( vips_reduce( resample->in, &t[0],
|
|
||||||
1.0 / similarity->scale,
|
|
||||||
1.0 / similarity->scale,
|
|
||||||
"kernel", ik->kernel,
|
|
||||||
NULL ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
handled = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( !handled ) {
|
|
||||||
double a = similarity->scale *
|
|
||||||
cos( VIPS_RAD( similarity->angle ) );
|
|
||||||
double b = similarity->scale *
|
|
||||||
-sin( VIPS_RAD( similarity->angle ) );
|
|
||||||
double c = -b;
|
|
||||||
double d = a;
|
|
||||||
|
|
||||||
if( vips_affine( resample->in, &t[0], a, b, c, d,
|
|
||||||
"interpolate", similarity->interpolate,
|
|
||||||
"odx", similarity->odx,
|
|
||||||
"ody", similarity->ody,
|
|
||||||
"idx", similarity->idx,
|
|
||||||
"idy", similarity->idy,
|
|
||||||
NULL ) )
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( vips_image_write( t[0], resample->out ) )
|
if( vips_image_write( t[0], resample->out ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
@ -214,6 +167,13 @@ vips_similarity_class_init( VipsSimilarityClass *class )
|
|||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsSimilarity, idy ),
|
G_STRUCT_OFFSET( VipsSimilarity, idy ),
|
||||||
-10000000, 10000000, 0 );
|
-10000000, 10000000, 0 );
|
||||||
|
|
||||||
|
VIPS_ARG_BOXED( class, "background", 2,
|
||||||
|
_( "Background" ),
|
||||||
|
_( "Background value" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsSimilarity, background ),
|
||||||
|
VIPS_TYPE_ARRAY_DOUBLE );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -226,6 +186,7 @@ vips_similarity_init( VipsSimilarity *similarity )
|
|||||||
similarity->ody = 0;
|
similarity->ody = 0;
|
||||||
similarity->idx = 0;
|
similarity->idx = 0;
|
||||||
similarity->idy = 0;
|
similarity->idy = 0;
|
||||||
|
similarity->background = vips_array_double_newv( 1, 0.0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -243,6 +204,8 @@ vips_similarity_init( VipsSimilarity *similarity )
|
|||||||
* * @idy: %gdouble, input vertical offset
|
* * @idy: %gdouble, input vertical offset
|
||||||
* * @odx: %gdouble, output horizontal offset
|
* * @odx: %gdouble, output horizontal offset
|
||||||
* * @ody: %gdouble, output vertical offset
|
* * @ody: %gdouble, output vertical offset
|
||||||
|
* * @ody: %gdouble, output vertical offset
|
||||||
|
* * @background: #VipsArrayDouble colour for new pixels
|
||||||
*
|
*
|
||||||
* This operator calls vips_affine() for you, calculating the matrix for the
|
* This operator calls vips_affine() for you, calculating the matrix for the
|
||||||
* affine transform from @scale and @angle. Other parameters are passed on to
|
* affine transform from @scale and @angle. Other parameters are passed on to
|
||||||
|
Loading…
Reference in New Issue
Block a user