473 lines
13 KiB
C
473 lines
13 KiB
C
/* vipsinterpolate ... abstract base class for various interpolators
|
|
*
|
|
* J. Cupitt, 15/10/08
|
|
*/
|
|
|
|
/*
|
|
|
|
This file is part of VIPS.
|
|
|
|
VIPS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
*/
|
|
|
|
/*
|
|
#define DEBUG
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <vips/vips.h>
|
|
#include <vips/internal.h>
|
|
|
|
#ifdef WITH_DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif /*WITH_DMALLOC*/
|
|
|
|
/* "fast" floor() ... on my laptop, anyway.
|
|
*/
|
|
#define FLOOR( V ) ((V) >= 0 ? (int)(V) : (int)((V) - 1))
|
|
|
|
G_DEFINE_ABSTRACT_TYPE( VipsInterpolate, vips_interpolate, VIPS_TYPE_OBJECT );
|
|
|
|
#ifdef DEBUG
|
|
static void
|
|
vips_interpolate_finalize( GObject *gobject )
|
|
{
|
|
printf( "vips_interpolate_finalize: " );
|
|
vips_object_print( VIPS_OBJECT( gobject ) );
|
|
|
|
G_OBJECT_CLASS( vips_interpolate_parent_class )->finalize( gobject );
|
|
}
|
|
#endif /*DEBUG*/
|
|
|
|
static int
|
|
vips_interpolate_real_get_window_size( VipsInterpolate *interpolate )
|
|
{
|
|
VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate );
|
|
|
|
g_assert( class->window_size != -1 );
|
|
|
|
return( class->window_size );
|
|
}
|
|
|
|
static void
|
|
vips_interpolate_class_init( VipsInterpolateClass *class )
|
|
{
|
|
#ifdef DEBUG
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
|
#endif /*DEBUG*/
|
|
|
|
#ifdef DEBUG
|
|
gobject_class->finalize = vips_interpolate_finalize;
|
|
#endif /*DEBUG*/
|
|
class->interpolate = NULL;
|
|
class->get_window_size = vips_interpolate_real_get_window_size;
|
|
class->window_size = -1;
|
|
}
|
|
|
|
static void
|
|
vips_interpolate_init( VipsInterpolate *interpolate )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "vips_interpolate_init: " );
|
|
vips_object_print( VIPS_OBJECT( interpolate ) );
|
|
#endif /*DEBUG*/
|
|
}
|
|
|
|
/* Set the point out_x, out_y in REGION out to be the point interpolated at
|
|
* in_x, in_y in REGION in. Don't do this as a signal for speed.
|
|
*/
|
|
void
|
|
vips_interpolate( VipsInterpolate *interpolate,
|
|
PEL *out, REGION *in, double x, double y )
|
|
{
|
|
VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate );
|
|
|
|
g_assert( class->interpolate );
|
|
class->interpolate( interpolate, out, in, x, y );
|
|
}
|
|
|
|
/* As above, but return the function pointer. Use this to cache method
|
|
* dispatch.
|
|
*/
|
|
VipsInterpolateMethod
|
|
vips_interpolate_get_method( VipsInterpolate *interpolate )
|
|
{
|
|
VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate );
|
|
|
|
g_assert( class->interpolate );
|
|
|
|
return( class->interpolate );
|
|
}
|
|
|
|
/* Get this interpolator's required window size.
|
|
*/
|
|
int
|
|
vips_interpolate_get_window_size( VipsInterpolate *interpolate )
|
|
{
|
|
VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate );
|
|
|
|
g_assert( class->get_window_size );
|
|
return( class->get_window_size( interpolate ) );
|
|
}
|
|
|
|
/* VipsInterpolateNearest class
|
|
*/
|
|
|
|
#define VIPS_TYPE_INTERPOLATE_NEAREST (vips_interpolate_nearest_get_type())
|
|
#define VIPS_INTERPOLATE_NEAREST( obj ) \
|
|
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
|
|
VIPS_TYPE_INTERPOLATE_NEAREST, VipsInterpolateNearest ))
|
|
#define VIPS_INTERPOLATE_NEAREST_CLASS( klass ) \
|
|
(G_TYPE_CHECK_CLASS_CAST( (klass), \
|
|
VIPS_TYPE_INTERPOLATE_NEAREST, VipsInterpolateNearestClass))
|
|
#define VIPS_IS_INTERPOLATE_NEAREST( obj ) \
|
|
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_NEAREST ))
|
|
#define VIPS_IS_INTERPOLATE_NEAREST_CLASS( klass ) \
|
|
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_NEAREST ))
|
|
#define VIPS_INTERPOLATE_NEAREST_GET_CLASS( obj ) \
|
|
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
|
VIPS_TYPE_INTERPOLATE_NEAREST, VipsInterpolateNearestClass ))
|
|
|
|
/* No new members.
|
|
*/
|
|
typedef VipsInterpolate VipsInterpolateNearest;
|
|
typedef VipsInterpolateClass VipsInterpolateNearestClass;
|
|
|
|
G_DEFINE_TYPE( VipsInterpolateNearest, vips_interpolate_nearest,
|
|
VIPS_TYPE_INTERPOLATE );
|
|
|
|
static void
|
|
vips_interpolate_nearest_interpolate( VipsInterpolate *interpolate,
|
|
PEL *out, REGION *in, double x, double y )
|
|
{
|
|
/* Pel size and line size.
|
|
*/
|
|
const int ps = IM_IMAGE_SIZEOF_PEL( in->im );
|
|
int z;
|
|
|
|
/* Top left corner we interpolate from.
|
|
*/
|
|
const int xi = FLOOR( x );
|
|
const int yi = FLOOR( y );
|
|
|
|
const PEL *p = (PEL *) IM_REGION_ADDR( in, xi, yi );
|
|
|
|
for( z = 0; z < ps; z++ )
|
|
out[z] = p[z];
|
|
}
|
|
|
|
static void
|
|
vips_interpolate_nearest_class_init( VipsInterpolateNearestClass *class )
|
|
{
|
|
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
|
|
VipsInterpolateClass *interpolate_class =
|
|
VIPS_INTERPOLATE_CLASS( class );
|
|
|
|
object_class->nickname = "nearest";
|
|
object_class->description = _( "Nearest-neighbour interpolation" );
|
|
|
|
interpolate_class->interpolate = vips_interpolate_nearest_interpolate;
|
|
interpolate_class->window_size = 1;
|
|
}
|
|
|
|
static void
|
|
vips_interpolate_nearest_init( VipsInterpolateNearest *nearest )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "vips_interpolate_nearest_init: " );
|
|
vips_object_print( VIPS_OBJECT( nearest ) );
|
|
#endif /*DEBUG*/
|
|
}
|
|
|
|
VipsInterpolate *
|
|
vips_interpolate_nearest_new( void )
|
|
{
|
|
|
|
return( VIPS_INTERPOLATE( vips_object_new(
|
|
VIPS_TYPE_INTERPOLATE_NEAREST, NULL, NULL, NULL ) ) );
|
|
}
|
|
|
|
/* Convenience: return a static nearest you don't need to free.
|
|
*/
|
|
VipsInterpolate *
|
|
vips_interpolate_nearest_static( void )
|
|
{
|
|
static VipsInterpolate *interpolate = NULL;
|
|
|
|
if( !interpolate )
|
|
interpolate = vips_interpolate_nearest_new();
|
|
|
|
return( interpolate );
|
|
}
|
|
|
|
/* VipsInterpolateBilinear class
|
|
*/
|
|
|
|
#define VIPS_TYPE_INTERPOLATE_BILINEAR (vips_interpolate_bilinear_get_type())
|
|
#define VIPS_INTERPOLATE_BILINEAR( obj ) \
|
|
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
|
|
VIPS_TYPE_INTERPOLATE_BILINEAR, VipsInterpolateBilinear ))
|
|
#define VIPS_INTERPOLATE_BILINEAR_CLASS( klass ) \
|
|
(G_TYPE_CHECK_CLASS_CAST( (klass), \
|
|
VIPS_TYPE_INTERPOLATE_BILINEAR, VipsInterpolateBilinearClass))
|
|
#define VIPS_IS_INTERPOLATE_BILINEAR( obj ) \
|
|
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_BILINEAR ))
|
|
#define VIPS_IS_INTERPOLATE_BILINEAR_CLASS( klass ) \
|
|
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_BILINEAR ))
|
|
#define VIPS_INTERPOLATE_BILINEAR_GET_CLASS( obj ) \
|
|
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
|
VIPS_TYPE_INTERPOLATE_BILINEAR, VipsInterpolateBilinearClass ))
|
|
|
|
typedef VipsInterpolate VipsInterpolateBilinear;
|
|
|
|
typedef struct _VipsInterpolateBilinearClass {
|
|
VipsInterpolateClass parent_class;
|
|
|
|
/* Precalculated interpolation matricies. int (used for pel sizes up
|
|
* to short), and float (for all others). We go to scale + 1, so
|
|
* we can round-to-nearest safely. Don't bother with double, since
|
|
* this is an approximation anyway.
|
|
*/
|
|
int matrixi[VIPS_TRANSFORM_SCALE + 1][VIPS_TRANSFORM_SCALE + 1][4];
|
|
float matrixd[VIPS_TRANSFORM_SCALE + 1][VIPS_TRANSFORM_SCALE + 1][4];
|
|
} VipsInterpolateBilinearClass;
|
|
|
|
G_DEFINE_TYPE( VipsInterpolateBilinear, vips_interpolate_bilinear,
|
|
VIPS_TYPE_INTERPOLATE );
|
|
|
|
/* in this class, name vars in the 2x2 grid as eg.
|
|
* p1 p2
|
|
* p3 p4
|
|
*/
|
|
|
|
/* Interpolate a section ... int8/16 types.
|
|
*/
|
|
#define BILINEAR_INT( TYPE ) { \
|
|
TYPE *tq = (TYPE *) out; \
|
|
\
|
|
const int c1 = class->matrixi[xi][yi][0]; \
|
|
const int c2 = class->matrixi[xi][yi][1]; \
|
|
const int c3 = class->matrixi[xi][yi][2]; \
|
|
const int c4 = class->matrixi[xi][yi][3]; \
|
|
\
|
|
const TYPE *tp1 = (TYPE *) p1; \
|
|
const TYPE *tp2 = (TYPE *) p2; \
|
|
const TYPE *tp3 = (TYPE *) p3; \
|
|
const TYPE *tp4 = (TYPE *) p4; \
|
|
\
|
|
for( z = 0; z < b; z++ ) \
|
|
tq[z] = (c1 * tp1[z] + c2 * tp2[z] + \
|
|
c3 * tp3[z] + c4 * tp4[z]) >> VIPS_INTERPOLATE_SHIFT; \
|
|
}
|
|
|
|
/* Interpolate a pel ... int32 and float types.
|
|
*/
|
|
#define BILINEAR_FLOAT( TYPE ) { \
|
|
TYPE *tq = (TYPE *) out; \
|
|
\
|
|
const double c1 = class->matrixd[xi][yi][0]; \
|
|
const double c2 = class->matrixd[xi][yi][1]; \
|
|
const double c3 = class->matrixd[xi][yi][2]; \
|
|
const double c4 = class->matrixd[xi][yi][3]; \
|
|
\
|
|
const TYPE *tp1 = (TYPE *) p1; \
|
|
const TYPE *tp2 = (TYPE *) p2; \
|
|
const TYPE *tp3 = (TYPE *) p3; \
|
|
const TYPE *tp4 = (TYPE *) p4; \
|
|
\
|
|
for( z = 0; z < b; z++ ) \
|
|
tq[z] = c1 * tp1[z] + c2 * tp2[z] + \
|
|
c3 * tp3[z] + c4 * tp4[z]; \
|
|
}
|
|
|
|
/* Expand for band types. with a fixed-point interpolator and a float
|
|
* interpolator.
|
|
*/
|
|
#define SWITCH_INTERPOLATE( FMT, INT, FLOAT ) { \
|
|
switch( (FMT) ) { \
|
|
case IM_BANDFMT_UCHAR: INT( unsigned char ); break; \
|
|
case IM_BANDFMT_CHAR: INT( char ); break; \
|
|
case IM_BANDFMT_USHORT: INT( unsigned short ); break; \
|
|
case IM_BANDFMT_SHORT: INT( short ); break; \
|
|
case IM_BANDFMT_UINT: FLOAT( unsigned int ); break; \
|
|
case IM_BANDFMT_INT: FLOAT( int ); break; \
|
|
case IM_BANDFMT_FLOAT: FLOAT( float ); break; \
|
|
case IM_BANDFMT_DOUBLE: FLOAT( double ); break; \
|
|
default: \
|
|
g_assert( FALSE ); \
|
|
} \
|
|
}
|
|
|
|
static void
|
|
vips_interpolate_bilinear_interpolate( VipsInterpolate *interpolate,
|
|
PEL *out, REGION *in, double x, double y )
|
|
{
|
|
VipsInterpolateBilinearClass *class =
|
|
VIPS_INTERPOLATE_BILINEAR_GET_CLASS( interpolate );
|
|
|
|
/* Pel size and line size.
|
|
*/
|
|
const int ps = IM_IMAGE_SIZEOF_PEL( in->im );
|
|
const int ls = IM_REGION_LSKIP( in );
|
|
const int b = in->im->Bands;
|
|
|
|
/* Now go to scaled int.
|
|
*/
|
|
const double sx = x * VIPS_TRANSFORM_SCALE;
|
|
const double sy = y * VIPS_TRANSFORM_SCALE;
|
|
const int sxi = FLOOR( sx );
|
|
const int syi = FLOOR( sy );
|
|
|
|
/* Get index into interpolation table and unscaled integer
|
|
* position.
|
|
*/
|
|
const int xi = sxi & (VIPS_TRANSFORM_SCALE - 1);
|
|
const int yi = syi & (VIPS_TRANSFORM_SCALE - 1);
|
|
const int x_int = sxi >> VIPS_TRANSFORM_SHIFT;
|
|
const int y_int = syi >> VIPS_TRANSFORM_SHIFT;
|
|
|
|
const PEL *p1 = (PEL *) IM_REGION_ADDR( in, x_int, y_int );
|
|
const PEL *p2 = p1 + ps;
|
|
const PEL *p3 = p1 + ls;
|
|
const PEL *p4 = p3 + ps;
|
|
|
|
int z;
|
|
|
|
SWITCH_INTERPOLATE( in->im->BandFmt,
|
|
BILINEAR_INT, BILINEAR_FLOAT );
|
|
}
|
|
|
|
static void
|
|
vips_interpolate_bilinear_class_init( VipsInterpolateBilinearClass *class )
|
|
{
|
|
VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
|
|
VipsInterpolateClass *interpolate_class =
|
|
(VipsInterpolateClass *) class;
|
|
int x, y;
|
|
|
|
object_class->nickname = "bilinear";
|
|
object_class->description = _( "Bilinear interpolation" );
|
|
|
|
interpolate_class->interpolate = vips_interpolate_bilinear_interpolate;
|
|
interpolate_class->window_size = 2;
|
|
|
|
/* Calculate the interpolation matricies.
|
|
*/
|
|
for( x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ )
|
|
for( y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) {
|
|
double X, Y, Xd, Yd;
|
|
double c1, c2, c3, c4;
|
|
|
|
/* Interpolation errors.
|
|
*/
|
|
X = (double) x / VIPS_TRANSFORM_SCALE;
|
|
Y = (double) y / VIPS_TRANSFORM_SCALE;
|
|
Xd = 1.0 - X;
|
|
Yd = 1.0 - Y;
|
|
|
|
/* Weights.
|
|
*/
|
|
c1 = Xd * Yd;
|
|
c2 = X * Yd;
|
|
c3 = Xd * Y;
|
|
c4 = X * Y;
|
|
|
|
class->matrixd[x][y][0] = c1;
|
|
class->matrixd[x][y][1] = c2;
|
|
class->matrixd[x][y][2] = c3;
|
|
class->matrixd[x][y][3] = c4;
|
|
|
|
class->matrixi[x][y][0] = c1 * VIPS_INTERPOLATE_SCALE;
|
|
class->matrixi[x][y][1] = c2 * VIPS_INTERPOLATE_SCALE;
|
|
class->matrixi[x][y][2] = c3 * VIPS_INTERPOLATE_SCALE;
|
|
class->matrixi[x][y][3] = c4 * VIPS_INTERPOLATE_SCALE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
vips_interpolate_bilinear_init( VipsInterpolateBilinear *bilinear )
|
|
{
|
|
#ifdef DEBUG
|
|
printf( "vips_interpolate_bilinear_init: " );
|
|
vips_object_print( VIPS_OBJECT( bilinear ) );
|
|
#endif /*DEBUG*/
|
|
|
|
}
|
|
|
|
VipsInterpolate *
|
|
vips_interpolate_bilinear_new( void )
|
|
{
|
|
return( VIPS_INTERPOLATE( vips_object_new(
|
|
VIPS_TYPE_INTERPOLATE_BILINEAR, NULL, NULL, NULL ) ) );
|
|
}
|
|
|
|
/* Convenience: return a static bilinear you don't need to free.
|
|
*/
|
|
VipsInterpolate *
|
|
vips_interpolate_bilinear_static( void )
|
|
{
|
|
static VipsInterpolate *interpolate = NULL;
|
|
|
|
if( !interpolate )
|
|
interpolate = vips_interpolate_bilinear_new();
|
|
|
|
return( interpolate );
|
|
}
|
|
|
|
/* Called on startup: register the base vips interpolators.
|
|
*/
|
|
void
|
|
vips__interpolate_init( void )
|
|
{
|
|
extern GType vips_interpolate_bicubic_get_type( void );
|
|
extern GType vips_interpolate_yafrsmooth_get_type( void );
|
|
extern GType vips_interpolate_nohalo1_get_type( void );
|
|
extern GType vips_interpolate_snohalo1_get_type( void );
|
|
|
|
vips_interpolate_nearest_get_type();
|
|
vips_interpolate_bilinear_get_type();
|
|
vips_interpolate_bicubic_get_type();
|
|
vips_interpolate_yafrsmooth_get_type();
|
|
vips_interpolate_nohalo1_get_type();
|
|
vips_interpolate_snohalo1_get_type();
|
|
}
|
|
|
|
/* Make an interpolator from a nickname.
|
|
*/
|
|
VipsInterpolate *
|
|
vips_interpolate_new( const char *nickname )
|
|
{
|
|
GType type;
|
|
|
|
if( !(type = vips_type_find( "VipsInterpolate", nickname )) )
|
|
return( NULL );
|
|
|
|
return( VIPS_INTERPOLATE( vips_object_new( type, NULL, NULL, NULL ) ) );
|
|
}
|