From b112b828c986c678c32d47cad118cac8504981da Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 19 Oct 2008 20:25:48 +0000 Subject: [PATCH] stuff --- TODO | 12 + include/vips/interpolate.h | 107 ++++++- libsrc/iofuncs/object.c | 8 +- libsrc/mosaicing/interpolate.c | 539 ++++++++++++++++++++++++++++++++- 4 files changed, 656 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index fdae92b4..5f1f35e9 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,17 @@ - update the Portfiles on the mac and on the website from the macports ones +- im_render should use a hash for tile lookup ... or+shift x/y together + +- eval callbacks shouldn't set an errormsg? or maybe it should for debugging + and stuff + +- bilinear should be a true subclass of interpolate ... and put the tables + into the base class + +- add 'name' to vipsobbject, and vips_object_print() ... handy for debugging + + make 'print' into a virtual method so subclasses can add stuff if they want + WONTFIX for 7.16 ================ diff --git a/include/vips/interpolate.h b/include/vips/interpolate.h index 5269efe2..b86dd81d 100644 --- a/include/vips/interpolate.h +++ b/include/vips/interpolate.h @@ -65,17 +65,114 @@ typedef struct _VipsInterpolateClass { void (*interpolate)( VipsInterpolate *, REGION *out, REGION *in, int out_x, int out_y, double in_x, double in_y ); - /* This interpolator needs a window of pixels this big. + /* This interpolator needs a window this many pixels across and down. */ - int window; + int (*get_window_size)( VipsInterpolate * ); + /* Or just set this if you want constant. + */ + int window_size; } VipsInterpolateClass; -VipsInterpolate *vips_interpolate_bilinear_new( void ); +GType vips_interpolate_get_type( void ); +void vips_interpolate( VipsInterpolate *interpolate, REGION *out, REGION *in, + int out_x, int out_y, double in_x, double in_y ); +int vips_interpolate_get_window_size( VipsInterpolate *interpolate ); -/* Convenience: return a static bilinear, so no need to free it. +/* Nearest class starts. + */ + +#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 )) + +typedef struct _VipsInterpolateNearest { + VipsObject parent_object; + +} VipsInterpolateNearest; + +typedef struct _VipsInterpolateNearestClass { + VipsObjectClass parent_class; + +} VipsInterpolateNearestClass; + +VipsInterpolateNearest *vips_interpolate_nearest_new( void ); +GType vips_interpolate_nearest_get_type( void ); + +/* Convenience: return a static fast nearest, so no need to free it. + */ +VipsInterpolate *vips_interpolate_nearest_static( void ); + +/* Bilinear class starts. + */ + +/* How many bits of precision we keep for transformations, ie. how many + * pre-computed matricies we have. + */ +#define VIPS_TRANSFORM_SHIFT (5) +#define VIPS_TRANSFORM_SCALE (1 << VIPS_TRANSFORM_SHIFT) + +/* How many bits of precision we keep for interpolation, ie. where the decimal + * is in the fixed-point tables. + */ +#define VIPS_INTERPOLATE_SHIFT (13) +#define VIPS_INTERPOLATE_SCALE (1 << VIPS_INTERPOLATE_SHIFT) + +#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 struct _VipsInterpolateBilinear { + VipsObject parent_object; + + /* Set this to not use tables ...slightly more accurate. + */ + gboolean slow; +} VipsInterpolateBilinear; + +typedef struct _VipsInterpolateBilinearClass { + VipsObjectClass parent_class; + + /* Precalculated interpolation matricies. int (used for pel sizes up + * to short), and double (for all others). We go to scale + 1, so + * we can round-to-nearest safely. + */ + int matrix_int[VIPS_TRANSFORM_SCALE + 1][2]; + double matrix_double[VIPS_TRANSFORM_SCALE + 1][2]; +} VipsInterpolateBilinearClass; + +GType vips_interpolate_bilinear_get_type( void ); +void vips_interpolate_bilinear_set_slow( VipsInterpolateBilinear *, gboolean ); +VipsInterpolateBilinear *vips_interpolate_bilinear_new( void ); + +/* Convenience: return a static fast bilinear, so no need to free it. + */ +VipsInterpolate *vips_interpolate_bilinear_static( void ); + +/* Yafr class starts. */ -VipsInterpolate *vips_interpolate_bilinear(); #define VIPS_TYPE_INTERPOLATE_YAFR (vips_interpolate_yafr_get_type()) #define VIPS_INTERPOLATE_YAFR( obj ) \ diff --git a/libsrc/iofuncs/object.c b/libsrc/iofuncs/object.c index 3e3d7c95..aacbbac0 100644 --- a/libsrc/iofuncs/object.c +++ b/libsrc/iofuncs/object.c @@ -183,9 +183,9 @@ vips_object_init( VipsObject *vips_object ) GType vips_object_get_type( void ) { - static GType vips_object_type = 0; + static GType type = 0; - if( !vips_object_type ) { + if( !type ) { static const GTypeInfo info = { sizeof( VipsObjectClass ), NULL, /* base_init */ @@ -198,11 +198,11 @@ vips_object_get_type( void ) (GInstanceInitFunc) vips_object_init, }; - vips_object_type = g_type_register_static( G_TYPE_OBJECT, + type = g_type_register_static( G_TYPE_OBJECT, "VipsObject", &info, 0 ); } - return( vips_object_type ); + return( type ); } void diff --git a/libsrc/mosaicing/interpolate.c b/libsrc/mosaicing/interpolate.c index a29763d5..fac701db 100644 --- a/libsrc/mosaicing/interpolate.c +++ b/libsrc/mosaicing/interpolate.c @@ -1,4 +1,4 @@ -/* im_interpolate ... abstract base class for various interpolators +/* vipsinterpolate ... abstract base class for various interpolators * * J. Cupitt, 15/10/08 */ @@ -47,4 +47,541 @@ #include #endif /*WITH_DMALLOC*/ +/* "fast" floor() ... on my laptop, anyway. + */ +#define FLOOR( V ) ((V) >= 0 ? (int)(V) : (int)((V) - 1)) + +static VipsObjectClass *vips_interpolate_parent_class = NULL; +static VipsObjectClass *vips_interpolate_nearest_parent_class = NULL; +static VipsObjectClass *vips_interpolate_bilinear_parent_class = NULL; + +#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*/ + + vips_interpolate_parent_class = g_type_class_peek_parent( class ); + +#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*/ +} + +GType +vips_interpolate_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + static const GTypeInfo info = { + sizeof( VipsObjectClass ), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) vips_interpolate_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof( VipsObject ), + 32, /* n_preallocs */ + (GInstanceInitFunc) vips_interpolate_init, + }; + + type = g_type_register_static( VIPS_TYPE_OBJECT, + "VipsInterpolate", &info, 0 ); + } + + return( type ); +} + +/* 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 ffor speed. + */ +void +vips_interpolate( VipsInterpolate *interpolate, REGION *out, REGION *in, + int out_x, int out_y, double in_x, double in_y ) +{ + VipsInterpolateClass *class = VIPS_INTERPOLATE_GET_CLASS( interpolate ); + + g_assert( class->interpolate ); + class->interpolate( interpolate, out, in, out_x, out_y, in_x, in_y ); +} + +/* 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 + */ + +#ifdef DEBUG +static void +vips_interpolate_nearest_finalize( GObject *gobject ) +{ + printf( "vips_interpolate_nearest_finalize: " ); + vips_object_print( VIPS_OBJECT( gobject ) ); + + G_OBJECT_CLASS( vips_interpolate_nearest_parent_class )-> + finalize( gobject ); +} +#endif /*DEBUG*/ + +static void +vips_interpolate_nearest_interpolate( VipsInterpolate *interpolate, + REGION *out, REGION *in, + int out_x, int out_y, double in_x, double in_y ) +{ + /* Pel size and line size. + */ + const int ps = IM_IMAGE_SIZEOF_PEL( in->im ); + int z; + + PEL *q = (PEL *) IM_REGION_ADDR( out, out_x, out_y ); + + /* Subtract 0.5 to centre the nearest. + */ + const double cx = in_x - 0.5; + const double cy = in_y - 0.5; + + /* Top left corner we interpolate from. + */ + const int xi = FLOOR( cx ); + const int yi = FLOOR( cy ); + + const PEL *p = (PEL *) IM_REGION_ADDR( in, xi, yi ); + + for( z = 0; z < ps; z++ ) + q[z] = p[z]; +} + +static void +vips_interpolate_nearest_class_init( VipsInterpolateNearestClass *class ) +{ +#ifdef DEBUG + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); +#endif /*DEBUG*/ + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( class ); + + vips_interpolate_nearest_parent_class = + g_type_class_peek_parent( class ); + +#ifdef DEBUG + gobject_class->finalize = vips_interpolate_nearest_finalize; +#endif /*DEBUG*/ + 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*/ +} + +GType +vips_interpolate_nearest_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + static const GTypeInfo info = { + sizeof( VipsObjectClass ), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) vips_interpolate_nearest_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof( VipsObject ), + 32, /* n_preallocs */ + (GInstanceInitFunc) vips_interpolate_nearest_init, + }; + + type = g_type_register_static( VIPS_TYPE_INTERPOLATE, + "VipsInterpolateNearest", &info, 0 ); + } + + return( type ); +} + +VipsInterpolateNearest * +vips_interpolate_nearest_new( void ) +{ + return( g_object_new( VIPS_TYPE_INTERPOLATE_NEAREST, 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( vips_interpolate_nearest_new() ); + + return( interpolate ); +} + +/* VipsInterpolateBilinear class + */ + +#ifdef DEBUG +static void +vips_interpolate_bilinear_finalize( GObject *gobject ) +{ + printf( "vips_interpolate_bilinear_finalize: " ); + vips_object_print( VIPS_OBJECT( gobject ) ); + + G_OBJECT_CLASS( vips_interpolate_bilinear_parent_class )-> + finalize( gobject ); +} +#endif /*DEBUG*/ + +/* Interpolate a section ... int8/16 types. + */ +#define BILINEAR_INT( TYPE ) { \ + TYPE *tq = (TYPE *) q; \ + \ + const int m1 = class->matrix_int[xi][0]; \ + const int m2 = class->matrix_int[xi][1]; \ + const int m3 = class->matrix_int[yi][0]; \ + const int m4 = class->matrix_int[yi][1]; \ + \ + const int c1 = (m3 * m1) >> VIPS_INTERPOLATE_SHIFT; \ + const int c2 = (m3 * m2) >> VIPS_INTERPOLATE_SHIFT; \ + const int c3 = (m4 * m1) >> VIPS_INTERPOLATE_SHIFT; \ + const int c4 = (m4 * m2) >> VIPS_INTERPOLATE_SHIFT; \ + \ + /* var points to \ + * p1 (x_int, y_int) \ + * p2 (x_int+1, y_int) \ + * p3 (x_int, y_int+1) \ + * p4 (x_int+1, y_int+1) \ + */ \ + const TYPE *tp1 = (TYPE *) p1; \ + const TYPE *tp2 = (TYPE *) p2; \ + const TYPE *tp3 = (TYPE *) p3; \ + const TYPE *tp4 = (TYPE *) p4; \ + \ + /* Interpolate each band. \ + */ \ + 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 *) q; \ + \ + const double m1 = class->matrix_double[xi][0]; \ + const double m2 = class->matrix_double[xi][1]; \ + const double m3 = class->matrix_double[yi][0]; \ + const double m4 = class->matrix_double[yi][1]; \ + \ + const double c1 = m3 * m1; \ + const double c2 = m3 * m2; \ + const double c3 = m4 * m1; \ + const double c4 = m4 * m2; \ + \ + /* var points to \ + * p1 (x_int, y_int) \ + * p2 (x_int+1, y_int) \ + * p3 (x_int, y_int+1) \ + * p4 (x_int+1, y_int+1) \ + */ \ + const TYPE *tp1 = (TYPE *) p1; \ + const TYPE *tp2 = (TYPE *) p2; \ + const TYPE *tp3 = (TYPE *) p3; \ + const TYPE *tp4 = (TYPE *) p4; \ + \ + /* Interpolate each band. \ + */ \ + for( z = 0; z < b; z++ ) \ + tq[z] = c1 * tp1[z] + c2 * tp2[z] + \ + c3 * tp3[z] + c4 * tp4[z]; \ +} + +/* Interpolate a pel ... don't use the pre-calcuated matricies. + */ +#define BILINEAR_SLOW( TYPE ) { \ + TYPE *tq = (TYPE *) q; \ + \ + /* var points to \ + * p1 (x_int, y_int) \ + * p2 (x_int+1, y_int) \ + * p3 (x_int, y_int+1) \ + * p4 (x_int+1, y_int+1) \ + */ \ + const TYPE *tp1 = (TYPE *) p1; \ + const TYPE *tp2 = (TYPE *) p2; \ + const TYPE *tp3 = (TYPE *) p3; \ + const TYPE *tp4 = (TYPE *) p4; \ + \ + /* Interpolate each band. \ + */ \ + 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, + REGION *out, REGION *in, + int out_x, int out_y, double in_x, double in_y ) +{ + VipsInterpolateBilinear *bilinear = + VIPS_INTERPOLATE_BILINEAR( interpolate ); + 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; + int z; + + PEL *q = (PEL *) IM_REGION_ADDR( out, out_x, out_y ); + + if( bilinear->slow ) { + /* Subtract 0.5 to centre the bilinear. + */ + const double cx = in_x - 0.5; + const double cy = in_y - 0.5; + + /* Top left corner we interpolate from. + */ + const int xi = FLOOR( cx ); + const int yi = FLOOR( cy ); + + /* Fractional part. + */ + const double X = cx - xi; + const double Y = cy - yi; + + /* Residual. + */ + const double Xd = 1.0 - X; + const double Yd = 1.0 - Y; + + /* Weights. + */ + const double c1 = Xd * Yd; + const double c2 = X * Yd; + const double c3 = X * Y; + const double c4 = Xd * Y; + + /* var points to + * p1 (x_int, y_int) + * p2 (x_int+1, y_int) + * p3 (x_int, y_int+1) + * p4 (x_int+1, y_int+1) + */ + const PEL *p1 = (PEL *) IM_REGION_ADDR( in, xi, yi ); + const PEL *p2 = p1 + ps; + const PEL *p3 = p1 + ls; + const PEL *p4 = p1 + ls + ps; + + SWITCH_INTERPOLATE( in->im->BandFmt, + BILINEAR_SLOW, BILINEAR_SLOW ); + } + else { + /* Subtract 0.5 to centre the bilinear. + */ + const double cx = in_x - 0.5; + const double cy = in_y - 0.5; + + /* Now go to scaled int. + */ + const double sx = cx * VIPS_TRANSFORM_SCALE; + const double sy = cy * 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 in_x_int = sxi >> VIPS_TRANSFORM_SHIFT; + const int in_y_int = syi >> VIPS_TRANSFORM_SHIFT; + + /* var points to + * p1 (x_int, y_int) + * p2 (x_int+1, y_int) + * p3 (x_int, y_int+1) + * p4 (x_int+1, y_int+1) + */ + const PEL *p1 = (PEL *) + IM_REGION_ADDR( in, in_x_int, in_y_int ); + const PEL *p2 = p1 + ps; + const PEL *p3 = p1 + ls; + const PEL *p4 = p1 + ls + ps; + + SWITCH_INTERPOLATE( in->im->BandFmt, + BILINEAR_INT, BILINEAR_FLOAT ); + } +} + +static void +vips_interpolate_bilinear_class_init( VipsInterpolateBilinearClass *class ) +{ +#ifdef DEBUG + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); +#endif /*DEBUG*/ + VipsInterpolateClass *interpolate_class = + (VipsInterpolateClass *) class; + int x; + + vips_interpolate_bilinear_parent_class = + g_type_class_peek_parent( class ); + +#ifdef DEBUG + gobject_class->finalize = vips_interpolate_bilinear_finalize; +#endif /*DEBUG*/ + 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++ ) { + const double c1 = (double) x / VIPS_TRANSFORM_SCALE; + const double c2 = 1.0 - c1; + + class->matrix_double[x][0] = c1; + class->matrix_double[x][1] = c2; + + class->matrix_int[x][0] = c1 * VIPS_INTERPOLATE_SCALE; + class->matrix_int[x][1] = c2 * 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*/ + + bilinear->slow = FALSE; +} + +GType +vips_interpolate_bilinear_get_type( void ) +{ + static GType type = 0; + + if( !type ) { + static const GTypeInfo info = { + sizeof( VipsObjectClass ), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) vips_interpolate_bilinear_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof( VipsObject ), + 32, /* n_preallocs */ + (GInstanceInitFunc) vips_interpolate_bilinear_init, + }; + + type = g_type_register_static( VIPS_TYPE_INTERPOLATE, + "VipsInterpolateBilinear", &info, 0 ); + } + + return( type ); +} + +void +vips_interpolate_bilinear_set_slow( VipsInterpolateBilinear *bilinear, + gboolean slow ) +{ + bilinear->slow = slow; +} + +VipsInterpolateBilinear * +vips_interpolate_bilinear_new( void ) +{ + return( g_object_new( VIPS_TYPE_INTERPOLATE_BILINEAR, 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( vips_interpolate_bilinear_new() ); + + return( interpolate ); +}