diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am index 5ada4f8b..33630384 100644 --- a/libvips/include/vips/Makefile.am +++ b/libvips/include/vips/Makefile.am @@ -56,6 +56,7 @@ EXTRA_DIST = version.h.in internal.h enumtemplate # we need absolute filenames here since this list appears in the src dir as # well vips_scan_headers = \ + ${top_srcdir}/libvips/include/vips/resample.h \ ${top_srcdir}/libvips/include/vips/memory.h \ ${top_srcdir}/libvips/include/vips/create.h \ ${top_srcdir}/libvips/include/vips/foreign.h \ diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index 37380972..8fdc127a 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -6,6 +6,9 @@ G_BEGIN_DECLS +/* enumerations from "../../../libvips/include/vips/resample.h" */ +GType vips_kernel_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_KERNEL (vips_kernel_get_type()) /* enumerations from "../../../libvips/include/vips/foreign.h" */ GType vips_foreign_flags_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_FOREIGN_FLAGS (vips_foreign_flags_get_type()) diff --git a/libvips/include/vips/resample.h b/libvips/include/vips/resample.h index 749f4fea..821a9995 100644 --- a/libvips/include/vips/resample.h +++ b/libvips/include/vips/resample.h @@ -38,6 +38,15 @@ extern "C" { #endif /*__cplusplus*/ +typedef enum { + VIPS_KERNEL_NEAREST, + VIPS_KERNEL_LINEAR, + VIPS_KERNEL_CUBIC, + VIPS_KERNEL_LANCZOS2, + VIPS_KERNEL_LANCZOS3, + VIPS_KERNEL_LAST +} VipsKernel; + int vips_shrink( VipsImage *in, VipsImage **out, double xshrink, double yshrink, ... ) __attribute__((sentinel)); diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index d0d32998..eff85d1b 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -54,6 +54,7 @@ AM_CPPFLAGS = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ # we need absolute filenames here since this list appears in the header dir as # well vips_scan_headers = \ + ${top_srcdir}/libvips/include/vips/resample.h \ ${top_srcdir}/libvips/include/vips/memory.h \ ${top_srcdir}/libvips/include/vips/create.h \ ${top_srcdir}/libvips/include/vips/foreign.h \ diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index 8dc6a448..4fb8062f 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -4,6 +4,28 @@ /* auto-generated enums for vips introspection */ #include +/* enumerations from "../../libvips/include/vips/resample.h" */ +GType +vips_kernel_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_KERNEL_NEAREST, "VIPS_KERNEL_NEAREST", "nearest"}, + {VIPS_KERNEL_LINEAR, "VIPS_KERNEL_LINEAR", "linear"}, + {VIPS_KERNEL_CUBIC, "VIPS_KERNEL_CUBIC", "cubic"}, + {VIPS_KERNEL_LANCZOS2, "VIPS_KERNEL_LANCZOS2", "lanczos2"}, + {VIPS_KERNEL_LANCZOS3, "VIPS_KERNEL_LANCZOS3", "lanczos3"}, + {VIPS_KERNEL_LAST, "VIPS_KERNEL_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsKernel", values ); + } + + return( etype ); +} /* enumerations from "../../libvips/include/vips/foreign.h" */ GType vips_foreign_flags_get_type( void ) diff --git a/libvips/resample/Makefile.am b/libvips/resample/Makefile.am index bae493c0..a2d5b5fd 100644 --- a/libvips/resample/Makefile.am +++ b/libvips/resample/Makefile.am @@ -11,7 +11,9 @@ libresample_la_SOURCES = \ shrinkv.c \ reduce.c \ reduceh.cpp \ + reducehl3.cpp \ reducev.cpp \ + reducevl3.cpp \ interpolate.c \ transform.c \ bicubic.cpp \ diff --git a/libvips/resample/presample.h b/libvips/resample/presample.h index fc678930..a8f587f4 100644 --- a/libvips/resample/presample.h +++ b/libvips/resample/presample.h @@ -65,6 +65,9 @@ typedef struct _VipsResampleClass { GType vips_resample_get_type( void ); +int vips_reducehl3_get_points( VipsKernel kernel ); +void vips_reducehl3_make_mask( VipsKernel kernel, double x, double *c ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/resample/reducehl3.cpp b/libvips/resample/reducehl3.cpp new file mode 100644 index 00000000..876e9c7b --- /dev/null +++ b/libvips/resample/reducehl3.cpp @@ -0,0 +1,421 @@ +/* horizontal reduce by a float factor with lanczos3 + * + * 29/1/16 + * - from shrinkh.c + */ + +/* + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "presample.h" +#include "templates.h" + +/** + * VipsKernel: + * @VIPS_KERNEL_NEAREST: nearest-neighbour + * @VIPS_KERNEL_LINEAR: linear interpolation + * @VIPS_KERNEL_CUBIC: cubic interpolation + * @VIPS_KERNEL_LANCZOS2: lanczos2 interpolation + * @VIPS_KERNEL_LANCZOS3: lanczos3 interpolation + * + * 1D resampling kernels. + */ + +/* The max size of the vector we use. + */ +#define MAX_POINTS (6) + +typedef struct _VipsReducehl3 { + VipsResample parent_instance; + + double xshrink; /* Reduce factor */ + + /* The thing we use to make the kernel. + */ + VipsKernel kernel; + + /* Number of points in kernel. + */ + int n_points; + + /* Precalculated interpolation matrices. 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 matrixi[VIPS_TRANSFORM_SCALE + 1][MAX_POINTS]; + double matrixf[VIPS_TRANSFORM_SCALE + 1][MAX_POINTS]; + +} VipsReducehl3; + +typedef VipsResampleClass VipsReducehl3Class; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsReducehl3, vips_reducehl3, VIPS_TYPE_RESAMPLE ); +} + +/* Get n points. + */ +int +vips_reducehl3_get_points( VipsKernel kernel ) +{ + switch( kernel ) { + case VIPS_KERNEL_NEAREST: + return( 1 ); + + case VIPS_KERNEL_LINEAR: + return( 2 ); + + case VIPS_KERNEL_CUBIC: + return( 4 ); + + case VIPS_KERNEL_LANCZOS2: + return( 4 ); + + case VIPS_KERNEL_LANCZOS3: + return( 6 ); + + default: + g_assert_not_reached(); + return( 0 ); + } +} + +/* Calculate a mask. + */ +void +vips_reducehl3_make_mask( VipsKernel kernel, double x, double *c ) +{ + switch( kernel ) { + case VIPS_KERNEL_NEAREST: + c[0] = 1.0; + break; + + case VIPS_KERNEL_LINEAR: + c[0] = x; + c[1] = 1.0 - x; + break; + + case VIPS_KERNEL_CUBIC: + calculate_coefficients_catmull( x, c ); + break; + + case VIPS_KERNEL_LANCZOS2: + calculate_coefficients_lanczos( 2, x, c ); + break; + + case VIPS_KERNEL_LANCZOS3: + calculate_coefficients_lanczos( 3, x, c ); + break; + + default: + g_assert_not_reached(); + break; + } +} + +template +static void inline +reducehl3_unsigned_int_tab( VipsReducehl3 *reducehl3, + VipsPel *pout, const VipsPel *pin, + const int bands, const int * restrict cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + for( int z = 0; z < bands; z++ ) { + int sum; + + sum = 0; + for( int i = 0; i < reducehl3->n_points; i++ ) + sum += cx[i] * in[i * bands]; + + sum = unsigned_fixed_round( sum ); + + sum = VIPS_CLIP( 0, sum, max_value ); + + out[z] = sum; + + in += 1; + } +} + +static int +vips_reducehl3_gen( VipsRegion *out_region, void *seq, + void *a, void *b, gboolean *stop ) +{ + VipsImage *in = (VipsImage *) a; + VipsReducehl3 *reducehl3 = (VipsReducehl3 *) b; + const int ps = VIPS_IMAGE_SIZEOF_PEL( in ); + VipsRegion *ir = (VipsRegion *) seq; + VipsRect *r = &out_region->valid; + + /* Double bands for complex. + */ + const int bands = in->Bands * + (vips_band_format_iscomplex( in->BandFmt ) ? 2 : 1); + + VipsRect s; + +#ifdef DEBUG + printf( "vips_reducehl3_gen: generating %d x %d at %d x %d\n", + r->width, r->height, r->left, r->top ); +#endif /*DEBUG*/ + + s.left = r->left * reducehl3->xshrink; + s.top = r->top; + s.width = r->width * reducehl3->xshrink + reducehl3->n_points; + s.height = r->height; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + VIPS_GATE_START( "vips_reducehl3_gen: work" ); + + for( int y = 0; y < r->height; y ++ ) { + VipsPel *q; + double X; + + q = VIPS_REGION_ADDR( out_region, r->left, r->top + y ); + X = r->left * reducehl3->xshrink; + + for( int x = 0; x < r->width; x++ ) { + int ix = (int) X; + VipsPel *p = VIPS_REGION_ADDR( ir, ix, r->top + y ); + const int sx = X * VIPS_TRANSFORM_SCALE * 2; + const int six = sx & (VIPS_TRANSFORM_SCALE * 2 - 1); + const int tx = (six + 1) >> 1; + const int *cxi = reducehl3->matrixi[tx]; + const double *cxf = reducehl3->matrixf[tx]; + + switch( in->BandFmt ) { + case VIPS_FORMAT_UCHAR: + reducehl3_unsigned_int_tab + ( + reducehl3, + q, p, bands, cxi ); + break; + + default: + g_assert_not_reached(); + break; + } + + X += reducehl3->xshrink; + q += ps; + } + } + + VIPS_GATE_STOP( "vips_reducehl3_gen: work" ); + + return( 0 ); +} + +static int +vips_reducehl3_build( VipsObject *object ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); + VipsResample *resample = VIPS_RESAMPLE( object ); + VipsReducehl3 *reducehl3 = (VipsReducehl3 *) object; + VipsImage **t = (VipsImage **) + vips_object_local_array( object, 2 ); + + VipsImage *in; + + if( VIPS_OBJECT_CLASS( vips_reducehl3_parent_class )->build( object ) ) + return( -1 ); + + in = resample->in; + + if( reducehl3->xshrink < 1 ) { + vips_error( object_class->nickname, + "%s", _( "reduce factors should be >= 1" ) ); + return( -1 ); + } + if( reducehl3->xshrink > 3 ) + vips_warn( object_class->nickname, + "%s", _( "reduce factor greater than 3" ) ); + + if( reducehl3->xshrink == 1 ) + return( vips_image_write( in, resample->out ) ); + + /* Build the tables of pre-computed coefficients. + */ + reducehl3->n_points = vips_reducehl3_get_points( reducehl3->kernel ); + for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) { + vips_reducehl3_make_mask( reducehl3->kernel, + (float) x / VIPS_TRANSFORM_SCALE, + reducehl3->matrixf[x] ); + + for( int i = 0; i < reducehl3->n_points; i++ ) + reducehl3->matrixi[x][i] = reducehl3->matrixf[x][i] * + VIPS_INTERPOLATE_SCALE; + } + + /* Unpack for processing. + */ + if( vips_image_decode( in, &t[0] ) ) + return( -1 ); + in = t[0]; + + /* Add new pixels around the input so we can interpolate at the edges. + */ + if( vips_embed( in, &t[1], + reducehl3->n_points / 2, 0, + in->Xsize + reducehl3->n_points - 1, in->Ysize, + "extend", VIPS_EXTEND_COPY, + NULL ) ) + return( -1 ); + in = t[1]; + + if( vips_image_pipelinev( resample->out, + VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) + return( -1 ); + + /* Size output. Note: we round the output width down! + * + * Don't change xres/yres, leave that to the application layer. For + * example, vipsthumbnail knows the true reduce factor (including the + * fractional part), we just see the integer part here. + */ + resample->out->Xsize = (in->Xsize - reducehl3->n_points + 1) / + reducehl3->xshrink; + if( resample->out->Xsize <= 0 ) { + vips_error( object_class->nickname, + "%s", _( "image has shrunk to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "vips_reducehl3_build: reducing %d x %d image to %d x %d\n", + in->Xsize, in->Ysize, + resample->out->Xsize, resample->out->Ysize ); +#endif /*DEBUG*/ + + if( vips_image_generate( resample->out, + vips_start_one, vips_reducehl3_gen, vips_stop_one, + in, reducehl3 ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_reducehl3_class_init( VipsReducehl3Class *reducehl3_class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( reducehl3_class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( reducehl3_class ); + VipsOperationClass *operation_class = + VIPS_OPERATION_CLASS( reducehl3_class ); + + VIPS_DEBUG_MSG( "vips_reducehl3_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "reducehl3"; + vobject_class->description = _( "shrink an image horizontally" ); + vobject_class->build = vips_reducehl3_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_DOUBLE( reducehl3_class, "xshrink", 3, + _( "Xshrink" ), + _( "Horizontal shrink factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReducehl3, xshrink ), + 1, 1000000, 1 ); + + VIPS_ARG_ENUM( reducehl3_class, "kernel", 3, + _( "Kernel" ), + _( "Resamling kernel" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsReducehl3, kernel ), + VIPS_TYPE_KERNEL, VIPS_KERNEL_CUBIC ); + +} + +static void +vips_reducehl3_init( VipsReducehl3 *reducehl3 ) +{ + reducehl3->kernel = VIPS_KERNEL_CUBIC; +} + +/** + * vips_reducehl3: + * @in: input image + * @out: output image + * @xshrink: horizontal reduce + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @kernel: #VipsKernel to use to interpolate (default: cubic) + * + * Reduce @in horizontally by a float factor. The pixels in @out are + * interpolated with a 1D mask. This operation will not work well for + * a reduction of more than a factor of two. + * + * This is a very low-level operation: see vips_resize() for a more + * convenient way to resize images. + * + * This operation does not change xres or yres. The image resolution needs to + * be updated by the application. + * + * See also: vips_shrink(), vips_resize(), vips_affine(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_reducehl3( VipsImage *in, VipsImage **out, double xshrink, ... ) +{ + va_list ap; + int result; + + va_start( ap, xshrink ); + result = vips_call_split( "reducehl3", ap, in, out, xshrink ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/resample/reducevl3.cpp b/libvips/resample/reducevl3.cpp new file mode 100644 index 00000000..d291167d --- /dev/null +++ b/libvips/resample/reducevl3.cpp @@ -0,0 +1,319 @@ +/* horizontal reduce by a float factor with lanczos3 + * + * 29/1/16 + * - from shrinkv.c + */ + +/* + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "presample.h" +#include "templates.h" + +typedef struct _VipsReducevl3 { + VipsResample parent_instance; + + double yshrink; /* Shrink factor */ + +} VipsReducevl3; + +typedef VipsResampleClass VipsReducevl3Class; + +/* Precalculated interpolation matrices. 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. + */ + +const int n_points = 6; + +static int vips_reducevl3_matrixi[VIPS_TRANSFORM_SCALE + 1][n_points]; +static double vips_reducevl3_matrixf[VIPS_TRANSFORM_SCALE + 1][n_points]; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsReducevl3, vips_reducevl3, VIPS_TYPE_RESAMPLE ); +} + +template +static void inline +reducevl3_unsigned_int_tab( VipsPel *pout, const VipsPel *pin, + const int ne, const int lskip, + const int * restrict cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + + for( int z = 0; z < ne; z++ ) { + int sum; + + sum = 0; + for( int i = 0; i < n_points; i++ ) + sum += cy[i] * in[i * l1]; + + sum = unsigned_fixed_round( sum ); + + sum = VIPS_CLIP( 0, sum, max_value ); + + out[z] = sum; + + in += 1; + } +} + +static int +vips_reducevl3_gen( VipsRegion *out_region, void *seq, + void *a, void *b, gboolean *stop ) +{ + VipsImage *in = (VipsImage *) a; + VipsReducevl3 *reducevl3 = (VipsReducevl3 *) b; + VipsRegion *ir = (VipsRegion *) seq; + VipsRect *r = &out_region->valid; + + /* Double bands for complex. + */ + const int bands = in->Bands * + (vips_band_format_iscomplex( in->BandFmt ) ? 2 : 1); + int ne = r->width * bands; + + VipsRect s; + +#ifdef DEBUG + printf( "vips_reducevl3_gen: generating %d x %d at %d x %d\n", + r->width, r->height, r->left, r->top ); +#endif /*DEBUG*/ + + s.left = r->left; + s.top = r->top * reducevl3->yshrink; + s.width = r->width; + s.height = r->height * reducevl3->yshrink + n_points; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + VIPS_GATE_START( "vips_reducevl3_gen: work" ); + + 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) * reducevl3->yshrink; + 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); + const int ty = (siy + 1) >> 1; + const int *cyi = vips_reducevl3_matrixi[ty]; + const double *cyf = vips_reducevl3_matrixf[ty]; + const int lskip = VIPS_REGION_LSKIP( ir ); + + switch( in->BandFmt ) { + case VIPS_FORMAT_UCHAR: + reducevl3_unsigned_int_tab + ( + q, p, ne, lskip, cyi ); + break; + + default: + g_assert_not_reached(); + break; + } + } + + VIPS_GATE_STOP( "vips_reducevl3_gen: work" ); + + return( 0 ); +} + +static int +vips_reducevl3_build( VipsObject *object ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); + VipsResample *resample = VIPS_RESAMPLE( object ); + VipsReducevl3 *reducevl3 = (VipsReducevl3 *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); + + VipsImage *in; + + if( VIPS_OBJECT_CLASS( vips_reducevl3_parent_class )->build( object ) ) + return( -1 ); + + in = resample->in; + + if( reducevl3->yshrink < 1 ) { + vips_error( object_class->nickname, + "%s", _( "reduce factors should be >= 1" ) ); + return( -1 ); + } + if( reducevl3->yshrink > 3 ) + vips_warn( object_class->nickname, + "%s", _( "reduce factor greater than 3" ) ); + + if( reducevl3->yshrink == 1 ) + return( vips_image_write( in, resample->out ) ); + + /* Unpack for processing. + */ + if( vips_image_decode( in, &t[0] ) ) + return( -1 ); + in = t[0]; + + /* Add new pixels around the input so we can interpolate at the edges. + */ + if( vips_embed( in, &t[1], + 0, n_points / 2, + in->Xsize, in->Ysize + n_points - 1, + "extend", VIPS_EXTEND_COPY, + NULL ) ) + return( -1 ); + in = t[1]; + + if( vips_image_pipelinev( resample->out, + VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) + return( -1 ); + + /* Size output. Note: we round the output width down! + * + * Don't change xres/yres, leave that to the application layer. For + * example, vipsthumbnail knows the true reduce factor (including the + * fractional part), we just see the integer part here. + */ + resample->out->Ysize = (in->Ysize - n_points + 1) / reducevl3->yshrink; + if( resample->out->Ysize <= 0 ) { + vips_error( object_class->nickname, + "%s", _( "image has shrunk to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "vips_reducevl3_build: reducing %d x %d image to %d x %d\n", + in->Xsize, in->Ysize, + resample->out->Xsize, resample->out->Ysize ); +#endif /*DEBUG*/ + + if( vips_image_generate( resample->out, + vips_start_one, vips_reducevl3_gen, vips_stop_one, + in, reducevl3 ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_reducevl3_class_init( VipsReducevl3Class *reducevl3_class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( reducevl3_class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( reducevl3_class ); + VipsOperationClass *operation_class = + VIPS_OPERATION_CLASS( reducevl3_class ); + + VIPS_DEBUG_MSG( "vips_reducevl3_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "reducevl3"; + vobject_class->description = _( "shrink an image vertically" ); + vobject_class->build = vips_reducevl3_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_DOUBLE( reducevl3_class, "yshrink", 3, + _( "Xshrink" ), + _( "Vertical shrink factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReducevl3, yshrink ), + 1, 1000000, 1 ); + + /* Build the tables of pre-computed coefficients. + */ + for( int y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) { + calculate_coefficients_lanczos( 3, + (float) y / VIPS_TRANSFORM_SCALE, + vips_reducevl3_matrixf[y] ); + + for( int i = 0; i < n_points; i++ ) + vips_reducevl3_matrixi[y][i] = + vips_reducevl3_matrixf[y][i] * + VIPS_INTERPOLATE_SCALE; + } + +} + +static void +vips_reducevl3_init( VipsReducevl3 *reducevl3 ) +{ +} + +/** + * vips_reducevl3: + * @in: input image + * @out: output image + * @yshrink: horizontal reduce + * @...: %NULL-terminated list of optional named arguments + * + * Reduce @in vertically by a float factor. The pixels in @out are + * interpolated with a 1D cubic mask. This operation will not work well for + * a reduction of more than a factor of two. + * + * This is a very low-level operation: see vips_resize() for a more + * convenient way to resize images. + * + * This operation does not change xres or yres. The image resolution needs to + * be updated by the application. + * + * See also: vips_shrink(), vips_resize(), vips_affine(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_reducevl3( VipsImage *in, VipsImage **out, double yshrink, ... ) +{ + va_list ap; + int result; + + va_start( ap, yshrink ); + result = vips_call_split( "reducevl3", ap, in, out, yshrink ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/resample/resample.c b/libvips/resample/resample.c index 18b21564..1b45f328 100644 --- a/libvips/resample/resample.c +++ b/libvips/resample/resample.c @@ -139,7 +139,9 @@ vips_resample_operation_init( void ) extern GType vips_shrinkv_get_type( void ); extern GType vips_reduce_get_type( void ); extern GType vips_reduceh_get_type( void ); + extern GType vips_reducehl3_get_type( void ); extern GType vips_reducev_get_type( void ); + extern GType vips_reducevl3_get_type( void ); extern GType vips_quadratic_get_type( void ); extern GType vips_affine_get_type( void ); extern GType vips_similarity_get_type( void ); @@ -150,7 +152,9 @@ vips_resample_operation_init( void ) vips_shrinkh_get_type(); vips_shrinkv_get_type(); vips_reduceh_get_type(); + vips_reducehl3_get_type(); vips_reducev_get_type(); + vips_reducevl3_get_type(); vips_reduce_get_type(); vips_quadratic_get_type(); vips_affine_get_type(); diff --git a/libvips/resample/templates.h b/libvips/resample/templates.h index 3aab774f..e6087236 100644 --- a/libvips/resample/templates.h +++ b/libvips/resample/templates.h @@ -312,18 +312,30 @@ calculate_coefficients_catmull( const double x, double c[4] ) } /* Given an offset in [0,1] (we can have x == 1 when building tables), - * calculate c0 .. c6, the lanczos3 coefficients. This is called + * calculate c0 .. c(a * 2 - 1), the lanczos coefficients. This is called * from the interpolator as well as from the table builder. */ static void inline -calculate_coefficients_lanczos3( const double x, double c[7] ) +calculate_coefficients_lanczos( int a, const double x, double *c ) { int i; - for( i = 0; i < 7; i++ ) { - double xp = (i - 3) + x; + for( i = 0; i < a * 2; i++ ) { + double xp = (i - a) + (1 - x); - c[i] = 3.0 * sin( VIPS_PI * xp ) * sin( VIPS_PI * xp / 3.0 ) / - (VIPS_PI * VIPS_PI * xp * xp); + double l; + + if( xp == 0.0 ) + l = 1.0; + else if( xp < -a ) + l = 0.0; + else if( xp > a ) + l = 0.0; + else + l = (double) a * sin( VIPS_PI * xp ) * + sin( VIPS_PI * xp / (double) a ) / + (VIPS_PI * VIPS_PI * xp * xp); + + c[i] = l; } }