From 8b09c864920485dd0c18e128a2f3dcbc1a1d4134 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 27 Jan 2016 11:12:36 +0000 Subject: [PATCH 01/13] add cubich interp., start reduceh --- libvips/include/vips/resample.h | 5 +- libvips/resample/Makefile.am | 2 +- libvips/resample/bicubich.cpp | 380 ++++++++++++++++++++++++++ libvips/resample/interpolate.c | 2 + libvips/resample/reduceh.c | 372 ++++++++++++++++++++++++++ libvips/resample/resample.c | 8 +- libvips/resample/shrink2.c | 461 -------------------------------- libvips/resample/templates.h | 35 +++ 8 files changed, 799 insertions(+), 466 deletions(-) create mode 100644 libvips/resample/bicubich.cpp create mode 100644 libvips/resample/reduceh.c delete mode 100644 libvips/resample/shrink2.c diff --git a/libvips/include/vips/resample.h b/libvips/include/vips/resample.h index 7f58096d..fb409841 100644 --- a/libvips/include/vips/resample.h +++ b/libvips/include/vips/resample.h @@ -43,8 +43,11 @@ int vips_shrink( VipsImage *in, VipsImage **out, __attribute__((sentinel)); int vips_shrinkh( VipsImage *in, VipsImage **out, int xshrink, ... ); int vips_shrinkv( VipsImage *in, VipsImage **out, int yshrink, ... ); -int vips_shrink2( VipsImage *in, VipsImage **out, + +int vips_reduce( VipsImage *in, VipsImage **out, double xshrink, double yshrink, ... ); +int vips_reduceh( VipsImage *in, VipsImage **out, int xshrink, ... ); +int vips_reducev( VipsImage *in, VipsImage **out, int yshrink, ... ); int vips_similarity( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); diff --git a/libvips/resample/Makefile.am b/libvips/resample/Makefile.am index ec8a775e..81e14990 100644 --- a/libvips/resample/Makefile.am +++ b/libvips/resample/Makefile.am @@ -9,10 +9,10 @@ libresample_la_SOURCES = \ shrink.c \ shrinkh.c \ shrinkv.c \ - shrink2.c \ interpolate.c \ transform.c \ bicubic.cpp \ + bicubich.cpp \ lbb.cpp \ nohalo.cpp \ vsqbs.cpp \ diff --git a/libvips/resample/bicubich.cpp b/libvips/resample/bicubich.cpp new file mode 100644 index 00000000..937ad1d0 --- /dev/null +++ b/libvips/resample/bicubich.cpp @@ -0,0 +1,380 @@ +/* 1D horizontal bicubich (catmull-rom) interpolator + * + * 26/1/16 + * - from bicubich.cpp + */ + +/* + + 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 + + */ + +/* Bicubich (Catmull-Rom) interpolator derived from Nicolas Robidoux's + * original YAFR resampler with permission and thanks. + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "templates.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define VIPS_TYPE_INTERPOLATE_BICUBICH \ + (vips_interpolate_bicubich_get_type()) +#define VIPS_INTERPOLATE_BICUBICH( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_BICUBICH, VipsInterpolateBicubich )) +#define VIPS_INTERPOLATE_BICUBICH_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_BICUBICH, VipsInterpolateBicubichClass)) +#define VIPS_IS_INTERPOLATE_BICUBICH( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_BICUBICH )) +#define VIPS_IS_INTERPOLATE_BICUBICH_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_BICUBICH )) +#define VIPS_INTERPOLATE_BICUBICH_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_BICUBICH, VipsInterpolateBicubichClass )) + +typedef VipsInterpolate VipsInterpolateBicubich; + +typedef VipsInterpolateClass VipsInterpolateBicubichClass; + +/* 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. + */ + +static int vips_bicubich_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; +static double vips_bicubich_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsInterpolateBicubich, vips_interpolate_bicubich, + VIPS_TYPE_INTERPOLATE ); +} + +/* Pointers to write to / read from, number of bands, + * how many bytes to add to move down a line. + */ + +/* T is the type of pixels we are reading and writing. + */ + +/* Fixed-point version, for 8 and 16-bit types. + */ + +template +static void inline +bicubich_unsigned_int_tab( void *pout, const VipsPel *pin, + const int bands, const int *cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + for( int z = 0; z < bands; z++ ) { + const T one = in[0]; + const T two = in[b1]; + const T thr = in[b2]; + const T fou = in[b3]; + + int bicubich = bicubic1d_unsigned_int( + one, two, thr, fou, cx ); + + bicubich = VIPS_CLIP( 0, bicubich, max_value ); + + out[z] = bicubich; + + in += 1; + } +} + +template +static void inline +bicubich_signed_int_tab( void *pout, const VipsPel *pin, + const int bands, const int *cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + for( int z = 0; z < bands; z++ ) { + const T one = in[0]; + const T two = in[b1]; + const T thr = in[b2]; + const T fou = in[b3]; + + int bicubich = bicubic1d_signed_int( + one, two, thr, fou, cx ); + + bicubich = VIPS_CLIP( min_value, bicubich, max_value ); + + out[z] = bicubich; + + in += 1; + } +} + +/* Floating-point version, for int/float types. + */ +template +static void inline +bicubich_float_tab( void *pout, const VipsPel *pin, + const int bands, const double *cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + for( int z = 0; z < bands; z++ ) { + const T one = in[0]; + const T two = in[b1]; + const T thr = in[b2]; + const T fou = in[b3]; + + const T bicubich = bicubic1d_float( + one, two, thr, fou, cx ); + + out[z] = bicubich; + + in += 1; + } +} + +/* Ultra-high-quality version for double images. + */ +template +static void inline +bicubich_notab( void *pout, const VipsPel *pin, + const int bands, double x ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + double cx[4]; + + calculate_coefficients_catmull( x, cx ); + + for( int z = 0; z < bands; z++ ) { + const T one = in[0]; + const T two = in[b1]; + const T thr = in[b2]; + const T fou = in[b3]; + + const T bicubich = bicubic1d_float( + one, two, thr, fou, cx ); + + out[z] = bicubich; + + in += 1; + } +} + +static void +vips_interpolate_bicubich_interpolate( VipsInterpolate *interpolate, + void *out, VipsRegion *in, double x, double y ) +{ + /* Find the mask index. We round-to-nearest, so we need to generate + * indexes in 0 to VIPS_TRANSFORM_SCALE, 2^n + 1 values. We multiply + * by 2 more than we need to, add one, mask, then shift down again to + * get the extra range. + */ + const int sx = x * VIPS_TRANSFORM_SCALE * 2; + + const int six = sx & (VIPS_TRANSFORM_SCALE * 2 - 1); + + const int tx = (six + 1) >> 1; + + /* We know (x, y) are always positive, so we can just (int) them. + */ + const int ix = (int) x; + const int iy = (int) y; + + /* Back one to get the left of the 4x1. + */ + const VipsPel *p = VIPS_REGION_ADDR( in, ix - 1, iy ); + + /* Look up the tables we need. + */ + const int *cxi = vips_bicubich_matrixi[tx]; + const double *cxf = vips_bicubich_matrixf[tx]; + + /* Pel size and line size. + */ + const int bands = in->im->Bands; + + g_assert( ix - 1 >= in->valid.left ); + g_assert( iy >= in->valid.top ); + g_assert( ix + 2 < VIPS_RECT_RIGHT( &in->valid ) ); + g_assert( iy < VIPS_RECT_BOTTOM( &in->valid ) ); + g_assert( iy == y ); + + /* Confirm that absolute_x >= 1, because of window_offset. + */ + g_assert( x >= 1.0 ); + +#ifdef DEBUG + printf( "vips_interpolate_bicubich_interpolate: %g %g\n", x, y ); + printf( "\tleft=%d, top=%d, width=%d, height=%d\n", + ix - 1, iy, 4, 1 ); + printf( "\tmaskx=%d\n", tx ); +#endif /*DEBUG*/ + + switch( in->im->BandFmt ) { + case VIPS_FORMAT_UCHAR: + bicubich_unsigned_int_tab( + out, p, bands, cxi ); + + /* + + Handy for benchmarking + + bicubich_float_tab( + out, p, bands, cxf ); + + bicubich_notab( + out, p, bands, x - ix ); + + */ + + break; + + case VIPS_FORMAT_CHAR: + bicubich_signed_int_tab( + out, p, bands, cxi ); + break; + + case VIPS_FORMAT_USHORT: + bicubich_unsigned_int_tab( + out, p, bands, cxi ); + break; + + case VIPS_FORMAT_SHORT: + bicubich_signed_int_tab( + out, p, bands, cxi ); + break; + + case VIPS_FORMAT_UINT: + bicubich_float_tab( + out, p, bands, cxf ); + break; + + case VIPS_FORMAT_INT: + bicubich_float_tab( + out, p, bands, cxf ); + break; + + case VIPS_FORMAT_FLOAT: + bicubich_float_tab( + out, p, bands, cxf ); + break; + + case VIPS_FORMAT_DOUBLE: + bicubich_notab( + out, p, bands, x - ix ); + break; + + case VIPS_FORMAT_COMPLEX: + bicubich_float_tab( + out, p, bands * 2, cxf ); + break; + + case VIPS_FORMAT_DPCOMPLEX: + bicubich_notab( + out, p, bands * 2, x - ix ); + break; + + default: + break; + } +} + +static void +vips_interpolate_bicubich_class_init( VipsInterpolateBicubichClass *iclass ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass ); + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( iclass ); + + object_class->nickname = "bicubich"; + object_class->description = + _( "horizontal bicubic interpolation (Catmull-Rom)" ); + + interpolate_class->interpolate = vips_interpolate_bicubich_interpolate; + interpolate_class->window_size = 4; + + /* Build the tables of pre-computed coefficients. + */ + for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) { + calculate_coefficients_catmull( + (float) x / VIPS_TRANSFORM_SCALE, + vips_bicubich_matrixf[x] ); + + for( int i = 0; i < 4; i++ ) + vips_bicubich_matrixi[x][i] = + vips_bicubich_matrixf[x][i] * + VIPS_INTERPOLATE_SCALE; + } +} + +static void +vips_interpolate_bicubich_init( VipsInterpolateBicubich *bicubich ) +{ +#ifdef DEBUG + printf( "vips_interpolate_bicubich_init: " ); + vips_object_print( VIPS_OBJECT( bicubich ) ); +#endif /*DEBUG*/ + +} + diff --git a/libvips/resample/interpolate.c b/libvips/resample/interpolate.c index 354c8a94..78124f6a 100644 --- a/libvips/resample/interpolate.c +++ b/libvips/resample/interpolate.c @@ -600,6 +600,7 @@ void vips__interpolate_init( void ) { extern GType vips_interpolate_bicubic_get_type( void ); + extern GType vips_interpolate_bicubich_get_type( void ); extern GType vips_interpolate_lbb_get_type( void ); extern GType vips_interpolate_nohalo_get_type( void ); extern GType vips_interpolate_vsqbs_get_type( void ); @@ -608,6 +609,7 @@ vips__interpolate_init( void ) vips_interpolate_bilinear_get_type(); vips_interpolate_bicubic_get_type(); + vips_interpolate_bicubich_get_type(); vips_interpolate_lbb_get_type(); vips_interpolate_nohalo_get_type(); vips_interpolate_vsqbs_get_type(); diff --git a/libvips/resample/reduceh.c b/libvips/resample/reduceh.c new file mode 100644 index 00000000..aee18902 --- /dev/null +++ b/libvips/resample/reduceh.c @@ -0,0 +1,372 @@ +/* horizontal reduce by a float factor + * + * 30/10/15 + * - from reduceh.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" + +typedef struct _VipsReduceh { + VipsResample parent_instance; + + double xreduce; /* Reduce factor */ + VipsInterpolate *interpolate; + +} VipsReduceh; + +typedef VipsResampleClass VipsReducehClass; + +G_DEFINE_TYPE( VipsReduceh, vips_reduceh, VIPS_TYPE_RESAMPLE ); + +#define INNER( BANDS ) \ + sum += p[x1]; \ + x1 += BANDS; + +/* Integer reduce. + */ +#define IREDUCE( TYPE, BANDS ) { \ + TYPE * restrict p = (TYPE *) in; \ + TYPE * restrict q = (TYPE *) out; \ + \ + for( x = 0; x < width; x++ ) { \ + for( b = 0; b < BANDS; b++ ) { \ + int sum; \ + \ + sum = 0; \ + x1 = b; \ + VIPS_UNROLL( reduce->xreduce, INNER( BANDS ) ); \ + q[b] = (sum + reduce->xreduce / 2) / \ + reduce->xreduce; \ + } \ + p += ne; \ + q += BANDS; \ + } \ +} + +/* Float reduce. + */ +#define FREDUCE( TYPE ) { \ + TYPE * restrict p = (TYPE *) in; \ + TYPE * restrict q = (TYPE *) out; \ + \ + for( x = 0; x < width; x++ ) { \ + for( b = 0; b < bands; b++ ) { \ + double sum; \ + \ + sum = 0.0; \ + x1 = b; \ + VIPS_UNROLL( reduce->xreduce, INNER( bands ) ); \ + q[b] = sum / reduce->xreduce; \ + } \ + p += ne; \ + q += bands; \ + } \ +} + +/* Generate an line of @or. @ir is large enough. + */ +static void +vips_reduceh_gen2( VipsReduceh *reduce, VipsRegion *or, VipsRegion *ir, + int left, int top, int width ) +{ + VipsResample *resample = VIPS_RESAMPLE( reduce ); + const int bands = resample->in->Bands * + (vips_band_format_iscomplex( resample->in->BandFmt ) ? + 2 : 1); + const int ne = reduce->xreduce * bands; + VipsPel *out = VIPS_REGION_ADDR( or, left, top ); + VipsPel *in = VIPS_REGION_ADDR( ir, left * reduce->xreduce, top ); + + int x; + int x1, b; + + switch( resample->in->BandFmt ) { + IREDUCE( unsigned char, bands ); break; + case VIPS_FORMAT_CHAR: + IREDUCE( char, bands ); break; + case VIPS_FORMAT_USHORT: + IREDUCE( unsigned short, bands ); break; + case VIPS_FORMAT_SHORT: + IREDUCE( short, bands ); break; + case VIPS_FORMAT_UINT: + IREDUCE( unsigned int, bands ); break; + case VIPS_FORMAT_INT: + IREDUCE( int, bands ); break; + case VIPS_FORMAT_FLOAT: + FREDUCE( float ); break; + case VIPS_FORMAT_DOUBLE: + FREDUCE( double ); break; + case VIPS_FORMAT_COMPLEX: + FREDUCE( float ); break; + case VIPS_FORMAT_DPCOMPLEX: + FREDUCE( double ); break; + + default: + g_assert_not_reached(); + } +} + +static int +vips_reduceh_gen( VipsRegion *or, void *seq, + void *a, void *b, gboolean *stop ) +{ + VipsReduceh *reduce = (VipsReduceh *) b; + VipsRegion *ir = (VipsRegion *) seq; + VipsRect *r = &or->valid; + + int y; + + /* How do we chunk up the image? We don't want to prepare the whole of + * the input region corresponding to *r since it could be huge. + * + * Request input a line at a time. + */ + +#ifdef DEBUG + printf( "vips_reduceh_gen: generating %d x %d at %d x %d\n", + r->width, r->height, r->left, r->top ); +#endif /*DEBUG*/ + + for( y = 0; y < r->height; y ++ ) { + VipsRect s; + + s.left = r->left * reduce->xreduce; + s.top = r->top + y; + s.width = r->width * reduce->xreduce; + s.height = 1; +#ifdef DEBUG + printf( "reduceh_gen: requesting line %d\n", s.top ); +#endif /*DEBUG*/ + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + VIPS_GATE_START( "vips_reduceh_gen: work" ); + + vips_reduceh_gen2( reduce, or, ir, + r->left, r->top + y, r->width ); + + VIPS_GATE_STOP( "vips_reduceh_gen: work" ); + } + + return( 0 ); +} + +static int +vips_reduceh_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsResample *resample = VIPS_RESAMPLE( object ); + VipsReduceh *reduce = (VipsReduceh *) object; + VipsImage **t = (VipsImage **) + vips_object_local_array( object, 1 ); + + VipsImage *in; + int window_size; + int window_offset; + + if( VIPS_OBJECT_CLASS( vips_reduceh_parent_class )->build( object ) ) + return( -1 ); + + in = resample->in; + + /* We can't use vips_object_argument_isset(), since it may have been + * set to NULL, see vips_similarity(). + */ + if( !reduceh->interpolate ) { + VipsInterpolate *interpolate; + + interpolate = vips_interpolate_new( "cubich" ); + g_object_set( object, + "interpolate", interpolate, + NULL ); + g_object_unref( interpolate ); + + /* coverity gets confused by this, it thinks + * reduceh->interpolate may still be null. Assign ourselves, + * even though we don't need to. + */ + reduceh->interpolate = interpolate; + } + + window_size = vips_interpolate_get_window_size( reduceh->interpolate ); + window_offset = + vips_interpolate_get_window_offset( reduceh->interpolate ); + + if( reduce->xreduce < 1 ) { + vips_error( class->nickname, + "%s", _( "reduce factors should be >= 1" ) ); + return( -1 ); + } + if( reduce->xreduce > 2 ) + vips_warn( class->nickname, + "%s", _( "reduce factor greater than 2" ) ); + + if( reduce->xreduce == 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], + window_offset, 0, + in->Xsize + window_size - 1, in->Ysize, + "extend", VIPS_EXTEND_COPY, + NULL ) ) + return( -1 ); + in = t[1]; + + /* THINSTRIP will work, anything else will break seq mode. If you + * combine reduce with conv you'll need to use a line cache to maintain + * sequentiality. + */ + 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 / reduce->xreduce; + if( resample->out->Xsize <= 0 ) { + vips_error( class->nickname, + "%s", _( "image has shrunk to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "vips_reduceh_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_reduceh_gen, vips_stop_one, + in, reduce ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_reduceh_class_init( VipsReducehClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_reduceh_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "reduceh"; + vobject_class->description = _( "reduce an image horizontally" ); + vobject_class->build = vips_reduceh_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_DOUBLE( class, "xreduce", 3, + _( "Xreduce" ), + _( "Horizontal reduce factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReduceh, xreduce ), + 1, 1000000, 1 ); + + VIPS_ARG_INTERPOLATE( class, "interpolate", 4, + _( "Interpolate" ), + _( "Interpolate pixels with this" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsReduceh, interpolate ) ); + +} + +static void +vips_reduceh_init( VipsReduceh *reduce ) +{ +} + +/** + * vips_reduceh: + * @in: input image + * @out: output image + * @xreduce: horizontal reduce + * @...: %NULL-terminated list of optional named arguments + * + * Reduce @in horizontally by a float factor. The pixels in @out are + * interpolated with a 1D bicubic 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_reduceh( VipsImage *in, VipsImage **out, double xreduce, ... ) +{ + va_list ap; + int result; + + va_start( ap, xreduce ); + result = vips_call_split( "reduceh", ap, in, out, xreduce ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/resample/resample.c b/libvips/resample/resample.c index 09013229..39bcf421 100644 --- a/libvips/resample/resample.c +++ b/libvips/resample/resample.c @@ -111,11 +111,13 @@ vips_resample_init( VipsResample *resample ) void vips_resample_operation_init( void ) { - extern GType vips_shrink_get_type( void ); extern GType vips_mapim_get_type( void ); + extern GType vips_shrink_get_type( void ); extern GType vips_shrinkh_get_type( void ); extern GType vips_shrinkv_get_type( void ); - extern GType vips_shrink2_get_type( void ); + extern GType vips_reduce_get_type( void ); + extern GType vips_reduceh_get_type( void ); + extern GType vips_reducev_get_type( void ); extern GType vips_quadratic_get_type( void ); extern GType vips_affine_get_type( void ); extern GType vips_similarity_get_type( void ); @@ -125,7 +127,7 @@ vips_resample_operation_init( void ) vips_shrink_get_type(); vips_shrinkh_get_type(); vips_shrinkv_get_type(); - vips_shrink2_get_type(); + vips_reduceh_get_type(); vips_quadratic_get_type(); vips_affine_get_type(); vips_similarity_get_type(); diff --git a/libvips/resample/shrink2.c b/libvips/resample/shrink2.c deleted file mode 100644 index 02fbd14c..00000000 --- a/libvips/resample/shrink2.c +++ /dev/null @@ -1,461 +0,0 @@ -/* shrink with a box filter - * - * Copyright: 1990, N. Dessipris. - * - * Authors: Nicos Dessipris and Kirk Martinez - * Written on: 29/04/1991 - * Modified on: 2/11/92, 22/2/93 Kirk Martinez - Xres Yres & cleanup - incredibly inefficient for box filters as LUTs are used instead of + - Needs converting to a smoother filter: eg Gaussian! KM - * 15/7/93 JC - * - rewritten for partial v2 - * - ANSIfied - * - now shrinks any non-complex type - * - no longer cloned from im_convsub() - * - could be much better! see km comments above - * 3/8/93 JC - * - rounding bug fixed - * 11/1/94 JC - * - problems with .000001 and round up/down ignored! Try shrink 3738 - * pixel image by 9.345000000001 - * 7/10/94 JC - * - IM_NEW and IM_ARRAY added - * - more typedef - * 3/7/95 JC - * - IM_CODING_LABQ handling added here - * 20/12/08 - * - fall back to im_copy() for 1/1 shrink - * 2/2/11 - * - gtk-doc - * 10/2/12 - * - shrink in chunks to reduce peak memuse for large shrinks - * - simpler - * 12/6/12 - * - redone as a class - * - warn about non-int shrinks - * - some tuning .. tried an int coordinate path, not worthwhile - * 16/11/12 - * - don't change xres/yres, see comment below - * 8/4/13 - * - oops demand_hint was incorrect, thanks Jan - * 6/6/13 - * - don't chunk horizontally, fixes seq problems with large shrink - * factors - */ - -/* - - 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" - -typedef struct _VipsShrink2 { - VipsResample parent_instance; - - double xshrink; /* Shrink factors */ - double yshrink; - - int mw; /* Size of area we average */ - int mh; - - int np; /* Number of pels we average */ - -} VipsShrink2; - -typedef VipsResampleClass VipsShrink2Class; - -G_DEFINE_TYPE( VipsShrink2, vips_shrink2, VIPS_TYPE_RESAMPLE ); - -/* Our per-sequence parameter struct. Somewhere to sum band elements. - */ -typedef struct { - VipsRegion *ir; - - VipsPel *sum; -} VipsShrink2Sequence; - -/* Free a sequence value. - */ -static int -vips_shrink2_stop( void *vseq, void *a, void *b ) -{ - VipsShrink2Sequence *seq = (VipsShrink2Sequence *) vseq; - - VIPS_FREEF( g_object_unref, seq->ir ); - - return( 0 ); -} - -/* Make a sequence value. - */ -static void * -vips_shrink2_start( VipsImage *out, void *a, void *b ) -{ - VipsImage *in = (VipsImage *) a; - VipsShrink2 *shrink = (VipsShrink2 *) b; - VipsShrink2Sequence *seq; - - if( !(seq = VIPS_NEW( out, VipsShrink2Sequence )) ) - return( NULL ); - - seq->ir = vips_region_new( in ); - if( !(seq->sum = (VipsPel *) VIPS_ARRAY( out, in->Bands, double )) ) { - vips_shrink2_stop( seq, in, shrink ); - return( NULL ); - } - - return( (void *) seq ); -} - -/* Integer shrink. - */ -#define ISHRINK( TYPE ) { \ - int *sum = (int *) seq->sum; \ - TYPE *p = (TYPE *) in; \ - TYPE *q = (TYPE *) out; \ - \ - for( b = 0; b < bands; b++ ) \ - sum[b] = 0; \ - \ - for( y1 = 0; y1 < shrink->mh; y1++ ) { \ - for( i = 0, x1 = 0; x1 < shrink->mw; x1++ ) \ - for( b = 0; b < bands; b++, i++ ) \ - sum[b] += p[i]; \ - \ - p += ls; \ - } \ - \ - for( b = 0; b < bands; b++ ) \ - q[b] = (sum[b] + shrink->np / 2) / shrink->np; \ -} - -/* Float shrink. - */ -#define FSHRINK( TYPE ) { \ - double *sum = (double *) seq->sum; \ - TYPE *p = (TYPE *) in; \ - TYPE *q = (TYPE *) out; \ - \ - for( b = 0; b < bands; b++ ) \ - sum[b] = 0.0; \ - \ - for( y1 = 0; y1 < shrink->mh; y1++ ) { \ - for( i = 0, x1 = 0; x1 < shrink->mw; x1++ ) \ - for( b = 0; b < bands; b++, i++ ) \ - sum[b] += p[i]; \ - \ - p += ls; \ - } \ - \ - for( b = 0; b < bands; b++ ) \ - q[b] = sum[b] / shrink->np; \ -} - -/* Generate an area of @or. @ir is large enough. - */ -static void -vips_shrink2_gen2( VipsShrink2 *shrink, VipsShrink2Sequence *seq, - VipsRegion *or, VipsRegion *ir, - int left, int top, int width, int height ) -{ - VipsResample *resample = VIPS_RESAMPLE( shrink ); - const int bands = resample->in->Bands; - const int sizeof_pixel = VIPS_IMAGE_SIZEOF_PEL( resample->in ); - const int ls = VIPS_REGION_LSKIP( ir ) / - VIPS_IMAGE_SIZEOF_ELEMENT( resample->in ); - - int x, y, i; - int x1, y1, b; - - for( y = 0; y < height; y++ ) { - VipsPel *out = VIPS_REGION_ADDR( or, left, top + y ); - - for( x = 0; x < width; x++ ) { - int ix = (left + x) * shrink->xshrink; - int iy = (top + y) * shrink->yshrink; - VipsPel *in = VIPS_REGION_ADDR( ir, ix, iy ); - - switch( resample->in->BandFmt ) { - case VIPS_FORMAT_UCHAR: - ISHRINK( unsigned char ); break; - case VIPS_FORMAT_CHAR: - ISHRINK( char ); break; - case VIPS_FORMAT_USHORT: - ISHRINK( unsigned short ); break; - case VIPS_FORMAT_SHORT: - ISHRINK( short ); break; - case VIPS_FORMAT_UINT: - ISHRINK( unsigned int ); break; - case VIPS_FORMAT_INT: - ISHRINK( int ); break; - case VIPS_FORMAT_FLOAT: - FSHRINK( float ); break; - case VIPS_FORMAT_DOUBLE: - FSHRINK( double ); break; - - default: - g_assert_not_reached(); - } - - out += sizeof_pixel; - } - } -} - -static int -vips_shrink2_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) -{ - VipsShrink2Sequence *seq = (VipsShrink2Sequence *) vseq; - VipsShrink2 *shrink = (VipsShrink2 *) b; - VipsRegion *ir = seq->ir; - VipsRect *r = &or->valid; - - /* How do we chunk up the image? We don't want to prepare the whole of - * the input region corresponding to *r since it could be huge. - * - * Each pixel of *r will depend on roughly mw x mh - * pixels, so we walk *r in chunks which map to the tile size. - * - * Make sure we can't ask for a zero step. - * - * We don't chunk horizontally. We want "vips shrink x.jpg b.jpg 100 - * 100" to run sequentially. If we chunk horizontally, we will fetch - * 100x100 lines from the top of the image, then 100x100 100 lines - * down, etc. for each thread, then when they've finished, fetch - * 100x100, 100 pixels across from the top of the image. This will - * break sequentiality. - */ - int ystep = shrink->mh > VIPS__TILE_HEIGHT ? - 1 : VIPS__TILE_HEIGHT / shrink->mh; - - int y; - -#ifdef DEBUG - printf( "vips_shrink2_gen: generating %d x %d at %d x %d\n", - r->width, r->height, r->left, r->top ); -#endif /*DEBUG*/ - - for( y = 0; y < r->height; y += ystep ) { - /* Clip the this rect against the demand size. - */ - int height = VIPS_MIN( ystep, r->height - y ); - - VipsRect s; - - s.left = r->left * shrink->xshrink; - s.top = (r->top + y) * shrink->yshrink; - s.width = VIPS_CEIL( r->width * shrink->xshrink ); - s.height = VIPS_CEIL( height * shrink->yshrink ); -#ifdef DEBUG - printf( "shrink_gen: requesting %d x %d at %d x %d\n", - s.width, s.height, s.left, s.top ); -#endif /*DEBUG*/ - if( vips_region_prepare( ir, &s ) ) - return( -1 ); - - VIPS_GATE_START( "vips_shrink2_gen: work" ); - - vips_shrink2_gen2( shrink, seq, - or, ir, - r->left, r->top + y, r->width, height ); - - VIPS_GATE_STOP( "vips_shrink2_gen: work" ); - } - - return( 0 ); -} - -static int -vips_shrink2_build( VipsObject *object ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsResample *resample = VIPS_RESAMPLE( object ); - VipsShrink2 *shrink = (VipsShrink2 *) object; - VipsImage **t = (VipsImage **) - vips_object_local_array( object, 1 ); - - VipsImage *in; - - if( VIPS_OBJECT_CLASS( vips_shrink2_parent_class )->build( object ) ) - return( -1 ); - - shrink->mw = VIPS_CEIL( shrink->xshrink ); - shrink->mh = VIPS_CEIL( shrink->yshrink ); - shrink->np = shrink->mw * shrink->mh; - - in = resample->in; - - if( vips_check_noncomplex( class->nickname, in ) ) - return( -1 ); - - if( shrink->xshrink < 1.0 || - shrink->yshrink < 1.0 ) { - vips_error( class->nickname, - "%s", _( "shrink factors should be >= 1" ) ); - return( -1 ); - } - - if( (int) shrink->xshrink != shrink->xshrink || - (int) shrink->yshrink != shrink->yshrink ) - vips_warn( class->nickname, - "%s", _( "not integer shrink factors, " - "expect poor results" ) ); - - if( shrink->xshrink == 1.0 && - shrink->yshrink == 1.0 ) - return( vips_image_write( in, resample->out ) ); - - /* Unpack for processing. - */ - if( vips_image_decode( in, &t[0] ) ) - return( -1 ); - in = t[0]; - - /* THINSTRIP will work, anything else will break seq mode. If you - * combine shrink with conv you'll need to use a line cache to maintain - * sequentiality. - */ - 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 shrink factor (including the - * fractional part), we just see the integer part here. - */ - resample->out->Xsize = in->Xsize / shrink->xshrink; - resample->out->Ysize = in->Ysize / shrink->yshrink; - if( resample->out->Xsize <= 0 || - resample->out->Ysize <= 0 ) { - vips_error( class->nickname, - "%s", _( "image has shrunk to nothing" ) ); - return( -1 ); - } - -#ifdef DEBUG - printf( "vips_shrink2_build: shrinking %d x %d image to %d x %d\n", - in->Xsize, in->Ysize, - resample->out->Xsize, resample->out->Ysize ); - printf( "vips_shrink2_build: %d x %d block average\n", - shrink->mw, shrink->mh ); -#endif /*DEBUG*/ - - if( vips_image_generate( resample->out, - vips_shrink2_start, vips_shrink2_gen, vips_shrink2_stop, - in, shrink ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_shrink2_class_init( VipsShrink2Class *class ) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS( class ); - VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); - VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); - - VIPS_DEBUG_MSG( "vips_shrink2_class_init\n" ); - - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; - - vobject_class->nickname = "shrink2"; - vobject_class->description = _( "shrink an image" ); - vobject_class->build = vips_shrink2_build; - - operation_class->flags = VIPS_OPERATION_SEQUENTIAL; - - VIPS_ARG_DOUBLE( class, "xshrink", 8, - _( "Xshrink" ), - _( "Horizontal shrink factor" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsShrink2, xshrink ), - 1.0, 1000000, 1 ); - - VIPS_ARG_DOUBLE( class, "yshrink", 9, - _( "Yshrink" ), - _( "Vertical shrink factor" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsShrink2, yshrink ), - 1.0, 1000000, 1 ); - -} - -static void -vips_shrink2_init( VipsShrink2 *shrink ) -{ -} - -/** - * vips_shrink2: - * @in: input image - * @out: output image - * @xshrink: horizontal shrink - * @yshrink: vertical shrink - * @...: %NULL-terminated list of optional named arguments - * - * Shrink @in by a pair of factors with a simple box filter. - * - * This is an old version of vips_shrink() kept around for testing. Use - * vips_shrink() in preference. - * - * See also: vips_resize(), vips_affine(). - * - * Returns: 0 on success, -1 on error - */ -int -vips_shrink2( VipsImage *in, VipsImage **out, - double xshrink, double yshrink, ... ) -{ - va_list ap; - int result; - - va_start( ap, yshrink ); - result = vips_call_split( "shrink2", ap, in, out, xshrink, yshrink ); - va_end( ap ); - - return( result ); -} diff --git a/libvips/resample/templates.h b/libvips/resample/templates.h index f7d7cfaa..b5437721 100644 --- a/libvips/resample/templates.h +++ b/libvips/resample/templates.h @@ -195,6 +195,18 @@ bicubic_unsigned_int( cy[3] * r3 ) ); } +template static int inline +bicubic1d_unsigned_int( + const T one, const T two, const T thr, const T fou, + const int* restrict cx ) +{ + return( unsigned_fixed_round( + cx[0] * one + + cx[1] * two + + cx[2] * thr + + cx[3] * fou ) ); +} + static int inline signed_fixed_round( int v ) { @@ -245,6 +257,18 @@ bicubic_signed_int( cy[3] * r3 ) ); } +template static int inline +bicubic1d_signed_int( + const T one, const T two, const T thr, const T fou, + const int* restrict cx ) +{ + return( signed_fixed_round( + cx[0] * one + + cx[1] * two + + cx[2] * thr + + cx[3] * fou ) ); +} + /* Floating-point bicubic, used for int/float/double types. */ template static T inline @@ -277,6 +301,17 @@ bicubic_float( cx[3] * qua_fou) ); } +template static T inline +bicubic1d_float( + const T one, const T two, const T thr, const T fou, + const double* restrict cx ) +{ + return( cx[0] * one + + cx[1] * two + + cx[2] * thr + + cx[3] * fou ); +} + /* Given an offset in [0,1] (we can have x == 1 when building tables), * calculate c0, c1, c2, c3, the catmull-rom coefficients. This is called * from the interpolator as well as from the table builder. From add75f235f2fdea560a379678c3a3cb6d64f5991 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 27 Jan 2016 12:44:24 +0000 Subject: [PATCH 02/13] version bump --- configure.ac | 6 +++--- doc/gtk-doc.make | 20 ++++++++------------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/configure.ac b/configure.ac index d9f60b69..cfc15321 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # also update the version number in the m4 macros below -AC_INIT([vips], [8.2.2], [vipsip@jiscmail.ac.uk]) +AC_INIT([vips], [8.2.3], [vipsip@jiscmail.ac.uk]) # required for gobject-introspection AC_PREREQ(2.62) @@ -18,7 +18,7 @@ AC_CONFIG_MACRO_DIR([m4]) # user-visible library versioning m4_define([vips_major_version], [8]) m4_define([vips_minor_version], [2]) -m4_define([vips_micro_version], [2]) +m4_define([vips_micro_version], [3]) m4_define([vips_version], [vips_major_version.vips_minor_version.vips_micro_version]) @@ -38,7 +38,7 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date` # binary interface changes not backwards compatible?: reset age to 0 LIBRARY_CURRENT=45 -LIBRARY_REVISION=2 +LIBRARY_REVISION=3 LIBRARY_AGE=3 # patched into include/vips/version.h diff --git a/doc/gtk-doc.make b/doc/gtk-doc.make index 9ccd0b04..e7916563 100644 --- a/doc/gtk-doc.make +++ b/doc/gtk-doc.make @@ -25,7 +25,6 @@ TARGET_DIR=$(HTML_DIR)/$(DOC_MODULE) SETUP_FILES = \ $(content_files) \ - $(expand_content_files) \ $(DOC_MAIN_SGML_FILE) \ $(DOC_MODULE)-sections.txt \ $(DOC_MODULE)-overrides.txt @@ -87,7 +86,7 @@ GTK_DOC_V_SETUP_0=@echo " DOC Preparing build"; setup-build.stamp: -$(GTK_DOC_V_SETUP)if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ - files=`echo $(SETUP_FILES) $(DOC_MODULE).types`; \ + files=`echo $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types`; \ if test "x$$files" != "x" ; then \ for file in $$files ; do \ destdir=`dirname $(abs_builddir)/$$file`; \ @@ -119,7 +118,7 @@ scan-build.stamp: setup-build.stamp $(HFILE_GLOB) $(CFILE_GLOB) $(GTK_DOC_V_INTROSPECT)if grep -l '^..*$$' $(DOC_MODULE).types > /dev/null 2>&1 ; then \ scanobj_options=""; \ gtkdoc-scangobj 2>&1 --help | grep >/dev/null "\-\-verbose"; \ - if test "$$?" = "0"; then \ + if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ scanobj_options="--verbose"; \ fi; \ @@ -163,17 +162,17 @@ GTK_DOC_V_XREF=$(GTK_DOC_V_XREF_$(V)) GTK_DOC_V_XREF_=$(GTK_DOC_V_XREF_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_XREF_0=@echo " DOC Fixing cross-references"; -html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files) +html-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(GTK_DOC_V_HTML)rm -rf html && mkdir html && \ mkhtml_options=""; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-verbose"; \ - if test "$$?" = "0"; then \ + if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ mkhtml_options="$$mkhtml_options --verbose"; \ fi; \ fi; \ gtkdoc-mkhtml 2>&1 --help | grep >/dev/null "\-\-path"; \ - if test "$$?" = "0"; then \ + if test "$(?)" = "0"; then \ mkhtml_options="$$mkhtml_options --path=\"$(abs_srcdir)\""; \ fi; \ cd html && gtkdoc-mkhtml $$mkhtml_options $(MKHTML_OPTIONS) $(DOC_MODULE) ../$(DOC_MAIN_SGML_FILE) @@ -195,11 +194,11 @@ GTK_DOC_V_PDF=$(GTK_DOC_V_PDF_$(V)) GTK_DOC_V_PDF_=$(GTK_DOC_V_PDF_$(AM_DEFAULT_VERBOSITY)) GTK_DOC_V_PDF_0=@echo " DOC Building PDF"; -pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(expand_content_files) +pdf-build.stamp: sgml.stamp $(DOC_MAIN_SGML_FILE) $(content_files) $(GTK_DOC_V_PDF)rm -f $(DOC_MODULE).pdf && \ mkpdf_options=""; \ gtkdoc-mkpdf 2>&1 --help | grep >/dev/null "\-\-verbose"; \ - if test "$$?" = "0"; then \ + if test "$(?)" = "0"; then \ if test "x$(V)" = "x1"; then \ mkpdf_options="$$mkpdf_options --verbose"; \ fi; \ @@ -224,15 +223,12 @@ clean-local: @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-types" ; then \ rm -f $(DOC_MODULE).types; \ fi - @if echo $(SCAN_OPTIONS) | grep -q "\-\-rebuild-sections" ; then \ - rm -f $(DOC_MODULE)-sections.txt; \ - fi distclean-local: @rm -rf xml html $(REPORT_FILES) $(DOC_MODULE).pdf \ $(DOC_MODULE)-decl-list.txt $(DOC_MODULE)-decl.txt @if test "$(abs_srcdir)" != "$(abs_builddir)" ; then \ - rm -f $(SETUP_FILES) $(DOC_MODULE).types; \ + rm -f $(SETUP_FILES) $(expand_content_files) $(DOC_MODULE).types; \ fi maintainer-clean-local: From e4b31dde38aa25c6c1ca9a68fa91e55426a54f91 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 27 Jan 2016 12:44:36 +0000 Subject: [PATCH 03/13] fix labq sparc read byteswap was swapping coded images, or trying to --- ChangeLog | 3 +++ libvips/conversion/byteswap.c | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/ChangeLog b/ChangeLog index 79a20cd4..7a69d216 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +27/1/16 started 8.2.3 +- fix a crash with SPARC byte-order labq vips images + 12/1/16 started 8.2.2 - changes to ease compiling C++ binding with MSVC [Lovell Fuller] - reorder file tests to put slow loaders last diff --git a/libvips/conversion/byteswap.c b/libvips/conversion/byteswap.c index f2124018..1d29d9e2 100644 --- a/libvips/conversion/byteswap.c +++ b/libvips/conversion/byteswap.c @@ -2,6 +2,8 @@ * * 5/6/15 * - from copy.c + * 27/1/16 + * - don't swap coded images */ /* @@ -137,6 +139,8 @@ vips_byteswap_gen( VipsRegion *or, int y; + g_assert( swap ); + if( vips_region_prepare( ir, r ) ) return( -1 ); @@ -159,6 +163,12 @@ vips_byteswap_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_byteswap_parent_class )->build( object ) ) return( -1 ); + /* Lots of images don't need swapping. + */ + if( byteswap->in->Coding != VIPS_CODING_NONE || + !vips_byteswap_swap_fn[byteswap->in->BandFmt] ) + return( vips_image_write( byteswap->in, conversion->out ) ); + if( vips_image_pio_input( byteswap->in ) ) return( -1 ); From 3e78bdb6a99060ae0c31fd60b66f6421253d66f6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 27 Jan 2016 14:34:06 +0000 Subject: [PATCH 04/13] reduceh is working! --- libvips/include/vips/resample.h | 4 +- libvips/resample/Makefile.am | 3 +- libvips/resample/{bicubich.cpp => cubich.cpp} | 120 ++++++------- libvips/resample/interpolate.c | 4 +- libvips/resample/reduceh.c | 155 ++++------------ libvips/resample/templates.h | 168 +++++++----------- 6 files changed, 164 insertions(+), 290 deletions(-) rename libvips/resample/{bicubich.cpp => cubich.cpp} (67%) diff --git a/libvips/include/vips/resample.h b/libvips/include/vips/resample.h index fb409841..749f4fea 100644 --- a/libvips/include/vips/resample.h +++ b/libvips/include/vips/resample.h @@ -46,8 +46,8 @@ int vips_shrinkv( VipsImage *in, VipsImage **out, int yshrink, ... ); int vips_reduce( VipsImage *in, VipsImage **out, double xshrink, double yshrink, ... ); -int vips_reduceh( VipsImage *in, VipsImage **out, int xshrink, ... ); -int vips_reducev( VipsImage *in, VipsImage **out, int yshrink, ... ); +int vips_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... ); +int vips_reducev( VipsImage *in, VipsImage **out, double yshrink, ... ); int vips_similarity( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); diff --git a/libvips/resample/Makefile.am b/libvips/resample/Makefile.am index 81e14990..e0d46487 100644 --- a/libvips/resample/Makefile.am +++ b/libvips/resample/Makefile.am @@ -9,10 +9,11 @@ libresample_la_SOURCES = \ shrink.c \ shrinkh.c \ shrinkv.c \ + reduceh.c \ interpolate.c \ transform.c \ bicubic.cpp \ - bicubich.cpp \ + cubich.cpp \ lbb.cpp \ nohalo.cpp \ vsqbs.cpp \ diff --git a/libvips/resample/bicubich.cpp b/libvips/resample/cubich.cpp similarity index 67% rename from libvips/resample/bicubich.cpp rename to libvips/resample/cubich.cpp index 937ad1d0..4ca38b2a 100644 --- a/libvips/resample/bicubich.cpp +++ b/libvips/resample/cubich.cpp @@ -1,7 +1,7 @@ -/* 1D horizontal bicubich (catmull-rom) interpolator +/* horizontal cubic (catmull-rom) interpolator * * 26/1/16 - * - from bicubich.cpp + * - from bicubic.cpp */ /* @@ -31,7 +31,7 @@ */ -/* Bicubich (Catmull-Rom) interpolator derived from Nicolas Robidoux's +/* Cubic (Catmull-Rom) interpolator derived from Nicolas Robidoux's * original YAFR resampler with permission and thanks. */ @@ -56,38 +56,38 @@ #include #endif /*WITH_DMALLOC*/ -#define VIPS_TYPE_INTERPOLATE_BICUBICH \ - (vips_interpolate_bicubich_get_type()) -#define VIPS_INTERPOLATE_BICUBICH( obj ) \ +#define VIPS_TYPE_INTERPOLATE_CUBICH \ + (vips_interpolate_cubich_get_type()) +#define VIPS_INTERPOLATE_CUBICH( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ - VIPS_TYPE_INTERPOLATE_BICUBICH, VipsInterpolateBicubich )) -#define VIPS_INTERPOLATE_BICUBICH_CLASS( klass ) \ + VIPS_TYPE_INTERPOLATE_CUBICH, VipsInterpolateCubich )) +#define VIPS_INTERPOLATE_CUBICH_CLASS( klass ) \ (G_TYPE_CHECK_CLASS_CAST( (klass), \ - VIPS_TYPE_INTERPOLATE_BICUBICH, VipsInterpolateBicubichClass)) -#define VIPS_IS_INTERPOLATE_BICUBICH( obj ) \ - (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_BICUBICH )) -#define VIPS_IS_INTERPOLATE_BICUBICH_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_BICUBICH )) -#define VIPS_INTERPOLATE_BICUBICH_GET_CLASS( obj ) \ + VIPS_TYPE_INTERPOLATE_CUBICH, VipsInterpolateCubichClass)) +#define VIPS_IS_INTERPOLATE_CUBICH( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_CUBICH )) +#define VIPS_IS_INTERPOLATE_CUBICH_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_CUBICH )) +#define VIPS_INTERPOLATE_CUBICH_GET_CLASS( obj ) \ (G_TYPE_INSTANCE_GET_CLASS( (obj), \ - VIPS_TYPE_INTERPOLATE_BICUBICH, VipsInterpolateBicubichClass )) + VIPS_TYPE_INTERPOLATE_CUBICH, VipsInterpolateCubichClass )) -typedef VipsInterpolate VipsInterpolateBicubich; +typedef VipsInterpolate VipsInterpolateCubich; -typedef VipsInterpolateClass VipsInterpolateBicubichClass; +typedef VipsInterpolateClass VipsInterpolateCubichClass; /* 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. */ -static int vips_bicubich_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; -static double vips_bicubich_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; +static int vips_cubich_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; +static double vips_cubich_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; /* We need C linkage for this. */ extern "C" { -G_DEFINE_TYPE( VipsInterpolateBicubich, vips_interpolate_bicubich, +G_DEFINE_TYPE( VipsInterpolateCubich, vips_interpolate_cubich, VIPS_TYPE_INTERPOLATE ); } @@ -103,7 +103,7 @@ G_DEFINE_TYPE( VipsInterpolateBicubich, vips_interpolate_bicubich, template static void inline -bicubich_unsigned_int_tab( void *pout, const VipsPel *pin, +cubich_unsigned_int_tab( void *pout, const VipsPel *pin, const int bands, const int *cx ) { T* restrict out = (T *) pout; @@ -119,12 +119,12 @@ bicubich_unsigned_int_tab( void *pout, const VipsPel *pin, const T thr = in[b2]; const T fou = in[b3]; - int bicubich = bicubic1d_unsigned_int( + int cubich = cubic_unsigned_int( one, two, thr, fou, cx ); - bicubich = VIPS_CLIP( 0, bicubich, max_value ); + cubich = VIPS_CLIP( 0, cubich, max_value ); - out[z] = bicubich; + out[z] = cubich; in += 1; } @@ -132,7 +132,7 @@ bicubich_unsigned_int_tab( void *pout, const VipsPel *pin, template static void inline -bicubich_signed_int_tab( void *pout, const VipsPel *pin, +cubich_signed_int_tab( void *pout, const VipsPel *pin, const int bands, const int *cx ) { T* restrict out = (T *) pout; @@ -148,12 +148,12 @@ bicubich_signed_int_tab( void *pout, const VipsPel *pin, const T thr = in[b2]; const T fou = in[b3]; - int bicubich = bicubic1d_signed_int( + int cubich = cubic_signed_int( one, two, thr, fou, cx ); - bicubich = VIPS_CLIP( min_value, bicubich, max_value ); + cubich = VIPS_CLIP( min_value, cubich, max_value ); - out[z] = bicubich; + out[z] = cubich; in += 1; } @@ -163,7 +163,7 @@ bicubich_signed_int_tab( void *pout, const VipsPel *pin, */ template static void inline -bicubich_float_tab( void *pout, const VipsPel *pin, +cubich_float_tab( void *pout, const VipsPel *pin, const int bands, const double *cx ) { T* restrict out = (T *) pout; @@ -179,10 +179,10 @@ bicubich_float_tab( void *pout, const VipsPel *pin, const T thr = in[b2]; const T fou = in[b3]; - const T bicubich = bicubic1d_float( + const T cubich = cubic_float( one, two, thr, fou, cx ); - out[z] = bicubich; + out[z] = cubich; in += 1; } @@ -192,7 +192,7 @@ bicubich_float_tab( void *pout, const VipsPel *pin, */ template static void inline -bicubich_notab( void *pout, const VipsPel *pin, +cubich_notab( void *pout, const VipsPel *pin, const int bands, double x ) { T* restrict out = (T *) pout; @@ -212,17 +212,17 @@ bicubich_notab( void *pout, const VipsPel *pin, const T thr = in[b2]; const T fou = in[b3]; - const T bicubich = bicubic1d_float( + const T cubich = cubic_float( one, two, thr, fou, cx ); - out[z] = bicubich; + out[z] = cubich; in += 1; } } static void -vips_interpolate_bicubich_interpolate( VipsInterpolate *interpolate, +vips_interpolate_cubich_interpolate( VipsInterpolate *interpolate, void *out, VipsRegion *in, double x, double y ) { /* Find the mask index. We round-to-nearest, so we need to generate @@ -247,8 +247,8 @@ vips_interpolate_bicubich_interpolate( VipsInterpolate *interpolate, /* Look up the tables we need. */ - const int *cxi = vips_bicubich_matrixi[tx]; - const double *cxf = vips_bicubich_matrixf[tx]; + const int *cxi = vips_cubich_matrixi[tx]; + const double *cxf = vips_cubich_matrixf[tx]; /* Pel size and line size. */ @@ -265,7 +265,7 @@ vips_interpolate_bicubich_interpolate( VipsInterpolate *interpolate, g_assert( x >= 1.0 ); #ifdef DEBUG - printf( "vips_interpolate_bicubich_interpolate: %g %g\n", x, y ); + printf( "vips_interpolate_cubich_interpolate: %g %g\n", x, y ); printf( "\tleft=%d, top=%d, width=%d, height=%d\n", ix - 1, iy, 4, 1 ); printf( "\tmaskx=%d\n", tx ); @@ -273,17 +273,17 @@ vips_interpolate_bicubich_interpolate( VipsInterpolate *interpolate, switch( in->im->BandFmt ) { case VIPS_FORMAT_UCHAR: - bicubich_unsigned_int_tab( + cubich_unsigned_int_tab( out, p, bands, cxi ); /* Handy for benchmarking - bicubich_float_tab( + cubich_float_tab( out, p, bands, cxf ); - bicubich_notab( + cubich_notab( out, p, bands, x - ix ); */ @@ -291,47 +291,47 @@ vips_interpolate_bicubich_interpolate( VipsInterpolate *interpolate, break; case VIPS_FORMAT_CHAR: - bicubich_signed_int_tab( + cubich_signed_int_tab( out, p, bands, cxi ); break; case VIPS_FORMAT_USHORT: - bicubich_unsigned_int_tab( + cubich_unsigned_int_tab( out, p, bands, cxi ); break; case VIPS_FORMAT_SHORT: - bicubich_signed_int_tab( + cubich_signed_int_tab( out, p, bands, cxi ); break; case VIPS_FORMAT_UINT: - bicubich_float_tab( + cubich_float_tab( out, p, bands, cxf ); break; case VIPS_FORMAT_INT: - bicubich_float_tab( + cubich_float_tab( out, p, bands, cxf ); break; case VIPS_FORMAT_FLOAT: - bicubich_float_tab( + cubich_float_tab( out, p, bands, cxf ); break; case VIPS_FORMAT_DOUBLE: - bicubich_notab( + cubich_notab( out, p, bands, x - ix ); break; case VIPS_FORMAT_COMPLEX: - bicubich_float_tab( + cubich_float_tab( out, p, bands * 2, cxf ); break; case VIPS_FORMAT_DPCOMPLEX: - bicubich_notab( + cubich_notab( out, p, bands * 2, x - ix ); break; @@ -341,17 +341,17 @@ vips_interpolate_bicubich_interpolate( VipsInterpolate *interpolate, } static void -vips_interpolate_bicubich_class_init( VipsInterpolateBicubichClass *iclass ) +vips_interpolate_cubich_class_init( VipsInterpolateCubichClass *iclass ) { VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass ); VipsInterpolateClass *interpolate_class = VIPS_INTERPOLATE_CLASS( iclass ); - object_class->nickname = "bicubich"; + object_class->nickname = "cubich"; object_class->description = - _( "horizontal bicubic interpolation (Catmull-Rom)" ); + _( "horizontal cubic interpolation (Catmull-Rom)" ); - interpolate_class->interpolate = vips_interpolate_bicubich_interpolate; + interpolate_class->interpolate = vips_interpolate_cubich_interpolate; interpolate_class->window_size = 4; /* Build the tables of pre-computed coefficients. @@ -359,21 +359,21 @@ vips_interpolate_bicubich_class_init( VipsInterpolateBicubichClass *iclass ) for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) { calculate_coefficients_catmull( (float) x / VIPS_TRANSFORM_SCALE, - vips_bicubich_matrixf[x] ); + vips_cubich_matrixf[x] ); for( int i = 0; i < 4; i++ ) - vips_bicubich_matrixi[x][i] = - vips_bicubich_matrixf[x][i] * + vips_cubich_matrixi[x][i] = + vips_cubich_matrixf[x][i] * VIPS_INTERPOLATE_SCALE; } } static void -vips_interpolate_bicubich_init( VipsInterpolateBicubich *bicubich ) +vips_interpolate_cubich_init( VipsInterpolateCubich *cubich ) { #ifdef DEBUG - printf( "vips_interpolate_bicubich_init: " ); - vips_object_print( VIPS_OBJECT( bicubich ) ); + printf( "vips_interpolate_cubich_init: " ); + vips_object_print( VIPS_OBJECT( cubich ) ); #endif /*DEBUG*/ } diff --git a/libvips/resample/interpolate.c b/libvips/resample/interpolate.c index 78124f6a..bccb0fe8 100644 --- a/libvips/resample/interpolate.c +++ b/libvips/resample/interpolate.c @@ -600,7 +600,7 @@ void vips__interpolate_init( void ) { extern GType vips_interpolate_bicubic_get_type( void ); - extern GType vips_interpolate_bicubich_get_type( void ); + extern GType vips_interpolate_cubich_get_type( void ); extern GType vips_interpolate_lbb_get_type( void ); extern GType vips_interpolate_nohalo_get_type( void ); extern GType vips_interpolate_vsqbs_get_type( void ); @@ -609,7 +609,7 @@ vips__interpolate_init( void ) vips_interpolate_bilinear_get_type(); vips_interpolate_bicubic_get_type(); - vips_interpolate_bicubich_get_type(); + vips_interpolate_cubich_get_type(); vips_interpolate_lbb_get_type(); vips_interpolate_nohalo_get_type(); vips_interpolate_vsqbs_get_type(); diff --git a/libvips/resample/reduceh.c b/libvips/resample/reduceh.c index aee18902..66c7c9bd 100644 --- a/libvips/resample/reduceh.c +++ b/libvips/resample/reduceh.c @@ -62,136 +62,57 @@ typedef VipsResampleClass VipsReducehClass; G_DEFINE_TYPE( VipsReduceh, vips_reduceh, VIPS_TYPE_RESAMPLE ); -#define INNER( BANDS ) \ - sum += p[x1]; \ - x1 += BANDS; - -/* Integer reduce. - */ -#define IREDUCE( TYPE, BANDS ) { \ - TYPE * restrict p = (TYPE *) in; \ - TYPE * restrict q = (TYPE *) out; \ - \ - for( x = 0; x < width; x++ ) { \ - for( b = 0; b < BANDS; b++ ) { \ - int sum; \ - \ - sum = 0; \ - x1 = b; \ - VIPS_UNROLL( reduce->xreduce, INNER( BANDS ) ); \ - q[b] = (sum + reduce->xreduce / 2) / \ - reduce->xreduce; \ - } \ - p += ne; \ - q += BANDS; \ - } \ -} - -/* Float reduce. - */ -#define FREDUCE( TYPE ) { \ - TYPE * restrict p = (TYPE *) in; \ - TYPE * restrict q = (TYPE *) out; \ - \ - for( x = 0; x < width; x++ ) { \ - for( b = 0; b < bands; b++ ) { \ - double sum; \ - \ - sum = 0.0; \ - x1 = b; \ - VIPS_UNROLL( reduce->xreduce, INNER( bands ) ); \ - q[b] = sum / reduce->xreduce; \ - } \ - p += ne; \ - q += bands; \ - } \ -} - -/* Generate an line of @or. @ir is large enough. - */ -static void -vips_reduceh_gen2( VipsReduceh *reduce, VipsRegion *or, VipsRegion *ir, - int left, int top, int width ) -{ - VipsResample *resample = VIPS_RESAMPLE( reduce ); - const int bands = resample->in->Bands * - (vips_band_format_iscomplex( resample->in->BandFmt ) ? - 2 : 1); - const int ne = reduce->xreduce * bands; - VipsPel *out = VIPS_REGION_ADDR( or, left, top ); - VipsPel *in = VIPS_REGION_ADDR( ir, left * reduce->xreduce, top ); - - int x; - int x1, b; - - switch( resample->in->BandFmt ) { - IREDUCE( unsigned char, bands ); break; - case VIPS_FORMAT_CHAR: - IREDUCE( char, bands ); break; - case VIPS_FORMAT_USHORT: - IREDUCE( unsigned short, bands ); break; - case VIPS_FORMAT_SHORT: - IREDUCE( short, bands ); break; - case VIPS_FORMAT_UINT: - IREDUCE( unsigned int, bands ); break; - case VIPS_FORMAT_INT: - IREDUCE( int, bands ); break; - case VIPS_FORMAT_FLOAT: - FREDUCE( float ); break; - case VIPS_FORMAT_DOUBLE: - FREDUCE( double ); break; - case VIPS_FORMAT_COMPLEX: - FREDUCE( float ); break; - case VIPS_FORMAT_DPCOMPLEX: - FREDUCE( double ); break; - - default: - g_assert_not_reached(); - } -} - static int vips_reduceh_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { - VipsReduceh *reduce = (VipsReduceh *) b; + VipsImage *in = (VipsImage *) a; + VipsReduceh *reduceh = (VipsReduceh *) b; + int window_size = + vips_interpolate_get_window_size( reduceh->interpolate ); + int window_offset = + vips_interpolate_get_window_offset( reduceh->interpolate ); + const VipsInterpolateMethod interpolate = + vips_interpolate_get_method( reduceh->interpolate ); + int ps = VIPS_IMAGE_SIZEOF_PEL( in ); VipsRegion *ir = (VipsRegion *) seq; VipsRect *r = &or->valid; + VipsRect s; int y; - /* How do we chunk up the image? We don't want to prepare the whole of - * the input region corresponding to *r since it could be huge. - * - * Request input a line at a time. - */ - #ifdef DEBUG printf( "vips_reduceh_gen: generating %d x %d at %d x %d\n", r->width, r->height, r->left, r->top ); #endif /*DEBUG*/ + s.left = r->left * reduceh->xreduce - window_offset; + s.top = r->top; + s.width = r->width * reduceh->xreduce + window_size - 1; + s.height = r->height; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + VIPS_GATE_START( "vips_reduceh_gen: work" ); + for( y = 0; y < r->height; y ++ ) { - VipsRect s; + VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y ); + double Y = r->top + y; - s.left = r->left * reduce->xreduce; - s.top = r->top + y; - s.width = r->width * reduce->xreduce; - s.height = 1; -#ifdef DEBUG - printf( "reduceh_gen: requesting line %d\n", s.top ); -#endif /*DEBUG*/ - if( vips_region_prepare( ir, &s ) ) - return( -1 ); + int x; - VIPS_GATE_START( "vips_reduceh_gen: work" ); + for( x = 0; x < r->width; x++ ) { + double X = window_offset + + (r->left + x) * reduceh->xreduce; - vips_reduceh_gen2( reduce, or, ir, - r->left, r->top + y, r->width ); + interpolate( reduceh->interpolate, q, ir, X, Y ); - VIPS_GATE_STOP( "vips_reduceh_gen: work" ); + q += ps; + } } + VIPS_GATE_STOP( "vips_reduceh_gen: work" ); + return( 0 ); } @@ -200,7 +121,7 @@ vips_reduceh_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsResample *resample = VIPS_RESAMPLE( object ); - VipsReduceh *reduce = (VipsReduceh *) object; + VipsReduceh *reduceh = (VipsReduceh *) object; VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 ); @@ -236,16 +157,16 @@ vips_reduceh_build( VipsObject *object ) window_offset = vips_interpolate_get_window_offset( reduceh->interpolate ); - if( reduce->xreduce < 1 ) { + if( reduceh->xreduce < 1 ) { vips_error( class->nickname, "%s", _( "reduce factors should be >= 1" ) ); return( -1 ); } - if( reduce->xreduce > 2 ) + if( reduceh->xreduce > 2 ) vips_warn( class->nickname, "%s", _( "reduce factor greater than 2" ) ); - if( reduce->xreduce == 1 ) + if( reduceh->xreduce == 1 ) return( vips_image_write( in, resample->out ) ); /* Unpack for processing. @@ -278,7 +199,7 @@ vips_reduceh_build( VipsObject *object ) * example, vipsthumbnail knows the true reduce factor (including the * fractional part), we just see the integer part here. */ - resample->out->Xsize = in->Xsize / reduce->xreduce; + resample->out->Xsize = (in->Xsize - window_size + 1) / reduceh->xreduce; if( resample->out->Xsize <= 0 ) { vips_error( class->nickname, "%s", _( "image has shrunk to nothing" ) ); @@ -293,7 +214,7 @@ vips_reduceh_build( VipsObject *object ) if( vips_image_generate( resample->out, vips_start_one, vips_reduceh_gen, vips_stop_one, - in, reduce ) ) + in, reduceh ) ) return( -1 ); return( 0 ); @@ -333,7 +254,7 @@ vips_reduceh_class_init( VipsReducehClass *class ) } static void -vips_reduceh_init( VipsReduceh *reduce ) +vips_reduceh_init( VipsReduceh *reduceh ) { } @@ -345,7 +266,7 @@ vips_reduceh_init( VipsReduceh *reduce ) * @...: %NULL-terminated list of optional named arguments * * Reduce @in horizontally by a float factor. The pixels in @out are - * interpolated with a 1D bicubic mask. This operation will not work well for + * 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 diff --git a/libvips/resample/templates.h b/libvips/resample/templates.h index b5437721..efd160ba 100644 --- a/libvips/resample/templates.h +++ b/libvips/resample/templates.h @@ -154,49 +154,8 @@ unsigned_fixed_round( int v ) return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT ); } -/* Fixed-point integer bicubic, used for 8 and 16-bit types. - */ template static int inline -bicubic_unsigned_int( - const T uno_one, const T uno_two, const T uno_thr, const T uno_fou, - const T dos_one, const T dos_two, const T dos_thr, const T dos_fou, - const T tre_one, const T tre_two, const T tre_thr, const T tre_fou, - const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, - const int* restrict cx, const int* restrict cy ) -{ - const int r0 = unsigned_fixed_round( - cx[0] * uno_one + - cx[1] * uno_two + - cx[2] * uno_thr + - cx[3] * uno_fou ); - - const int r1 = unsigned_fixed_round( - cx[0] * dos_one + - cx[1] * dos_two + - cx[2] * dos_thr + - cx[3] * dos_fou ); - - const int r2 = unsigned_fixed_round( - cx[0] * tre_one + - cx[1] * tre_two + - cx[2] * tre_thr + - cx[3] * tre_fou ); - - const int r3 = unsigned_fixed_round( - cx[0] * qua_one + - cx[1] * qua_two + - cx[2] * qua_thr + - cx[3] * qua_fou ); - - return( unsigned_fixed_round( - cy[0] * r0 + - cy[1] * r1 + - cy[2] * r2 + - cy[3] * r3 ) ); -} - -template static int inline -bicubic1d_unsigned_int( +cubic_unsigned_int( const T one, const T two, const T thr, const T fou, const int* restrict cx ) { @@ -207,6 +166,28 @@ bicubic1d_unsigned_int( cx[3] * fou ) ); } +/* Fixed-point integer bicubic, used for 8 and 16-bit types. + */ +template static int inline +bicubic_unsigned_int( + const T uno_one, const T uno_two, const T uno_thr, const T uno_fou, + const T dos_one, const T dos_two, const T dos_thr, const T dos_fou, + const T tre_one, const T tre_two, const T tre_thr, const T tre_fou, + const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, + const int* restrict cx, const int* restrict cy ) +{ + const int r0 = cubic_unsigned_int( + uno_one, uno_two, uno_thr, uno_fou, cx ); + const int r1 = cubic_unsigned_int( + dos_one, dos_two, dos_thr, dos_fou, cx ); + const int r2 = cubic_unsigned_int( + tre_one, tre_two, tre_thr, tre_fou, cx ); + const int r3 = cubic_unsigned_int( + qua_one, qua_two, qua_thr, qua_fou, cx ); + + return( cubic_unsigned_int( r0, r1, r2, r3, cy ) ); +} + static int inline signed_fixed_round( int v ) { @@ -216,6 +197,18 @@ signed_fixed_round( int v ) return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT ); } +template static int inline +cubic_signed_int( + const T one, const T two, const T thr, const T fou, + const int* restrict cx ) +{ + return( signed_fixed_round( + cx[0] * one + + cx[1] * two + + cx[2] * thr + + cx[3] * fou ) ); +} + /* Fixed-point integer bicubic, used for 8 and 16-bit types. */ template static int inline @@ -226,47 +219,27 @@ bicubic_signed_int( const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, const int* restrict cx, const int* restrict cy ) { - const int r0 = signed_fixed_round( - cx[0] * uno_one + - cx[1] * uno_two + - cx[2] * uno_thr + - cx[3] * uno_fou ); + const int r0 = cubic_signed_int( + uno_one, uno_two, uno_thr, uno_fou, cx ); + const int r1 = cubic_signed_int( + dos_one, dos_two, dos_thr, dos_fou, cx ); + const int r2 = cubic_signed_int( + tre_one, tre_two, tre_thr, tre_fou, cx ); + const int r3 = cubic_signed_int( + qua_one, qua_two, qua_thr, qua_fou, cx ); - const int r1 = signed_fixed_round( - cx[0] * dos_one + - cx[1] * dos_two + - cx[2] * dos_thr + - cx[3] * dos_fou ); - - const int r2 = signed_fixed_round( - cx[0] * tre_one + - cx[1] * tre_two + - cx[2] * tre_thr + - cx[3] * tre_fou ); - - const int r3 = signed_fixed_round( - cx[0] * qua_one + - cx[1] * qua_two + - cx[2] * qua_thr + - cx[3] * qua_fou ); - - return( signed_fixed_round( - cy[0] * r0 + - cy[1] * r1 + - cy[2] * r2 + - cy[3] * r3 ) ); + return( cubic_signed_int( r0, r1, r2, r3, cy ) ); } -template static int inline -bicubic1d_signed_int( +template static T inline +cubic_float( const T one, const T two, const T thr, const T fou, - const int* restrict cx ) + const double* restrict cx ) { - return( signed_fixed_round( - cx[0] * one + - cx[1] * two + - cx[2] * thr + - cx[3] * fou ) ); + return( cx[0] * one + + cx[1] * two + + cx[2] * thr + + cx[3] * fou ); } /* Floating-point bicubic, used for int/float/double types. @@ -279,37 +252,16 @@ bicubic_float( const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, const double* restrict cx, const double* restrict cy ) { - return( - cy[0] * (cx[0] * uno_one + - cx[1] * uno_two + - cx[2] * uno_thr + - cx[3] * uno_fou) - + - cy[1] * (cx[0] * dos_one + - cx[1] * dos_two + - cx[2] * dos_thr + - cx[3] * dos_fou) - + - cy[2] * (cx[0] * tre_one + - cx[1] * tre_two + - cx[2] * tre_thr + - cx[3] * tre_fou) - + - cy[3] * (cx[0] * qua_one + - cx[1] * qua_two + - cx[2] * qua_thr + - cx[3] * qua_fou) ); -} + const double r0 = cubic_float( + uno_one, uno_two, uno_thr, uno_fou, cx ); + const double r1 = cubic_float( + dos_one, dos_two, dos_thr, dos_fou, cx ); + const double r2 = cubic_float( + tre_one, tre_two, tre_thr, tre_fou, cx ); + const double r3 = cubic_float( + qua_one, qua_two, qua_thr, qua_fou, cx ); -template static T inline -bicubic1d_float( - const T one, const T two, const T thr, const T fou, - const double* restrict cx ) -{ - return( cx[0] * one + - cx[1] * two + - cx[2] * thr + - cx[3] * fou ); + return( cubic_float( r0, r1, r2, r3, cy ) ); } /* Given an offset in [0,1] (we can have x == 1 when building tables), From 991349c9a70254acc049a185c7655ef2826623fa Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 27 Jan 2016 15:01:54 +0000 Subject: [PATCH 05/13] add cubicv, reducev untested --- libvips/resample/Makefile.am | 2 + libvips/resample/cubich.cpp | 34 +-- libvips/resample/cubicv.cpp | 378 +++++++++++++++++++++++++++++++++ libvips/resample/interpolate.c | 2 + libvips/resample/reduceh.c | 38 ++-- libvips/resample/reducev.c | 296 ++++++++++++++++++++++++++ 6 files changed, 709 insertions(+), 41 deletions(-) create mode 100644 libvips/resample/cubicv.cpp create mode 100644 libvips/resample/reducev.c diff --git a/libvips/resample/Makefile.am b/libvips/resample/Makefile.am index e0d46487..ecf76e60 100644 --- a/libvips/resample/Makefile.am +++ b/libvips/resample/Makefile.am @@ -10,10 +10,12 @@ libresample_la_SOURCES = \ shrinkh.c \ shrinkv.c \ reduceh.c \ + reducev.c \ interpolate.c \ transform.c \ bicubic.cpp \ cubich.cpp \ + cubicv.cpp \ lbb.cpp \ nohalo.cpp \ vsqbs.cpp \ diff --git a/libvips/resample/cubich.cpp b/libvips/resample/cubich.cpp index 4ca38b2a..ade73ab9 100644 --- a/libvips/resample/cubich.cpp +++ b/libvips/resample/cubich.cpp @@ -119,8 +119,7 @@ cubich_unsigned_int_tab( void *pout, const VipsPel *pin, const T thr = in[b2]; const T fou = in[b3]; - int cubich = cubic_unsigned_int( - one, two, thr, fou, cx ); + int cubich = cubic_unsigned_int( one, two, thr, fou, cx ); cubich = VIPS_CLIP( 0, cubich, max_value ); @@ -148,8 +147,7 @@ cubich_signed_int_tab( void *pout, const VipsPel *pin, const T thr = in[b2]; const T fou = in[b3]; - int cubich = cubic_signed_int( - one, two, thr, fou, cx ); + int cubich = cubic_signed_int( one, two, thr, fou, cx ); cubich = VIPS_CLIP( min_value, cubich, max_value ); @@ -179,10 +177,7 @@ cubich_float_tab( void *pout, const VipsPel *pin, const T thr = in[b2]; const T fou = in[b3]; - const T cubich = cubic_float( - one, two, thr, fou, cx ); - - out[z] = cubich; + out[z] = cubic_float( one, two, thr, fou, cx ); in += 1; } @@ -212,10 +207,7 @@ cubich_notab( void *pout, const VipsPel *pin, const T thr = in[b2]; const T fou = in[b3]; - const T cubich = cubic_float( - one, two, thr, fou, cx ); - - out[z] = cubich; + out[z] = cubic_float( one, two, thr, fou, cx ); in += 1; } @@ -306,33 +298,27 @@ vips_interpolate_cubich_interpolate( VipsInterpolate *interpolate, break; case VIPS_FORMAT_UINT: - cubich_float_tab( - out, p, bands, cxf ); + cubich_float_tab( out, p, bands, cxf ); break; case VIPS_FORMAT_INT: - cubich_float_tab( - out, p, bands, cxf ); + cubich_float_tab( out, p, bands, cxf ); break; case VIPS_FORMAT_FLOAT: - cubich_float_tab( - out, p, bands, cxf ); + cubich_float_tab( out, p, bands, cxf ); break; case VIPS_FORMAT_DOUBLE: - cubich_notab( - out, p, bands, x - ix ); + cubich_notab( out, p, bands, x - ix ); break; case VIPS_FORMAT_COMPLEX: - cubich_float_tab( - out, p, bands * 2, cxf ); + cubich_float_tab( out, p, bands * 2, cxf ); break; case VIPS_FORMAT_DPCOMPLEX: - cubich_notab( - out, p, bands * 2, x - ix ); + cubich_notab( out, p, bands * 2, x - ix ); break; default: diff --git a/libvips/resample/cubicv.cpp b/libvips/resample/cubicv.cpp new file mode 100644 index 00000000..63631335 --- /dev/null +++ b/libvips/resample/cubicv.cpp @@ -0,0 +1,378 @@ +/* vertical cubic (catmull-rom) interpolator + * + * 26/1/16 + * - from bicubic.cpp + */ + +/* + + 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 + + */ + +/* Cubic (Catmull-Rom) interpolator derived from Nicolas Robidoux's + * original YAFR resampler with permission and thanks. + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include + +#include "templates.h" + +#ifdef WITH_DMALLOC +#include +#endif /*WITH_DMALLOC*/ + +#define VIPS_TYPE_INTERPOLATE_CUBICV \ + (vips_interpolate_cubicv_get_type()) +#define VIPS_INTERPOLATE_CUBICV( obj ) \ + (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ + VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicv )) +#define VIPS_INTERPOLATE_CUBICV_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_CAST( (klass), \ + VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicvClass)) +#define VIPS_IS_INTERPOLATE_CUBICV( obj ) \ + (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_CUBICV )) +#define VIPS_IS_INTERPOLATE_CUBICV_CLASS( klass ) \ + (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_CUBICV )) +#define VIPS_INTERPOLATE_CUBICV_GET_CLASS( obj ) \ + (G_TYPE_INSTANCE_GET_CLASS( (obj), \ + VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicvClass )) + +typedef VipsInterpolate VipsInterpolateCubicv; + +typedef VipsInterpolateClass VipsInterpolateCubicvClass; + +/* 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. + */ + +/* We could keep a large set of 2d 4x4 matricies, but this actually + * works out slower since for many resizes the thing will no longer + * fit in L1. + */ +static int vips_cubicv_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; +static double vips_cubicv_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsInterpolateCubicv, vips_interpolate_cubicv, + VIPS_TYPE_INTERPOLATE ); +} + +/* Pointers to write to / read from, number of bands, + * how many bytes to add to move down a line. + */ + +/* T is the type of pixels we are reading and writing. + */ + +/* Fixed-point version, for 8 and 16-bit types. + */ + +template +static void inline +cubicv_unsigned_int_tab( void *pout, const VipsPel *pin, + const int bands, const int lskip, + const int *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + for( int z = 0; z < bands; z++ ) { + const T uno = in[0]; + const T dos = in[l1]; + const T tre = in[l2]; + const T qua = in[l3]; + + int cubicv = cubic_unsigned_int( uno, dos, tre, qua, cy ); + + cubicv = VIPS_CLIP( 0, cubicv, max_value ); + + out[z] = cubicv; + + in += 1; + } +} + +template +static void inline +cubicv_signed_int_tab( void *pout, const VipsPel *pin, + const int bands, const int lskip, + const int *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + for( int z = 0; z < bands; z++ ) { + const T uno = in[0]; + const T dos = in[l1]; + const T tre = in[l2]; + const T qua = in[l3]; + + int cubicv = cubic_signed_int( uno, dos, tre, qua, cy ); + + cubicv = VIPS_CLIP( min_value, cubicv, max_value ); + + out[z] = cubicv; + + in += 1; + } +} + +/* Floating-point version, for int/float types. + */ +template +static void inline +cubicv_float_tab( void *pout, const VipsPel *pin, + const int bands, const int lskip, + const double *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + for( int z = 0; z < bands; z++ ) { + const T uno = in[0]; + const T dos = in[l1]; + const T tre = in[l2]; + const T qua = in[l3]; + + out[z] = cubic_float( uno, dos, tre, qua, cy ); + + in += 1; + } +} + +/* Ultra-high-quality version for double images. + */ +template +static void inline +cubicv_notab( void *pout, const VipsPel *pin, + const int bands, const int lskip, + double y ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + double cy[4]; + + calculate_coefficients_catmull( y, cy ); + + for( int z = 0; z < bands; z++ ) { + const T uno = in[0]; + const T dos = in[l1]; + const T tre = in[l2]; + const T qua = in[l3]; + + out[z] = cubic_float( uno, dos, tre, qua, cy ); + + in += 1; + } +} + +static void +vips_interpolate_cubicv_interpolate( VipsInterpolate *interpolate, + void *out, VipsRegion *in, double x, double y ) +{ + /* Find the mask index. We round-to-nearest, so we need to generate + * indexes in 0 to VIPS_TRANSFORM_SCALE, 2^n + 1 values. We multiply + * by 2 more than we need to, add one, mask, then shift down again to + * get the extra range. + */ + const int sy = y * VIPS_TRANSFORM_SCALE * 2; + + const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1); + + const int ty = (siy + 1) >> 1; + + /* We know x/y are always positive, so we can just (int) them. + */ + const int ix = (int) x; + const int iy = (int) y; + + /* Back and up one to get the top-left of the 4x4. + */ + const VipsPel *p = VIPS_REGION_ADDR( in, ix - 1, iy - 1 ); + + /* Look up the tables we need. + */ + const int *cyi = vips_cubicv_matrixi[ty]; + const double *cyf = vips_cubicv_matrixf[ty]; + + /* Pel size and line size. + */ + const int bands = in->im->Bands; + const int lskip = VIPS_REGION_LSKIP( in ); + + g_assert( ix >= in->valid.left ); + g_assert( iy - 1 >= in->valid.top ); + g_assert( ix < VIPS_RECT_RIGHT( &in->valid ) ); + g_assert( iy + 2 < VIPS_RECT_BOTTOM( &in->valid ) ); + g_assert( ix == x ); + + /* Confirm that absolute_y is >= 1, because of window_offset. + */ + g_assert( y >= 1.0 ); + +#ifdef DEBUG + printf( "vips_interpolate_cubicv_interpolate: %g %g\n", x, y ); + printf( "\tleft=%d, top=%d, width=%d, height=%d\n", + ix, iy - 1, 1, 4 ); + printf( "\tmasky=%d\n", ty ); +#endif /*DEBUG*/ + + switch( in->im->BandFmt ) { + case VIPS_FORMAT_UCHAR: + cubicv_unsigned_int_tab( + out, p, bands, lskip, + cyi ); + + /* + + Handy for benchmarking + + cubicv_float_tab( + out, p, bands, lskip, + cyf ); + + cubicv_notab( + out, p, bands, lskip, + y - iy ); + + */ + + break; + + case VIPS_FORMAT_CHAR: + cubicv_signed_int_tab( + out, p, bands, lskip, cyi ); + break; + + case VIPS_FORMAT_USHORT: + cubicv_unsigned_int_tab( + out, p, bands, lskip, cyi ); + break; + + case VIPS_FORMAT_SHORT: + cubicv_signed_int_tab( + out, p, bands, lskip, cyi ); + break; + + case VIPS_FORMAT_UINT: + cubicv_float_tab( out, p, bands, lskip, cyf ); + break; + + case VIPS_FORMAT_INT: + cubicv_float_tab( out, p, bands, lskip, cyf ); + break; + + case VIPS_FORMAT_FLOAT: + cubicv_float_tab( out, p, bands, lskip, cyf ); + break; + + case VIPS_FORMAT_DOUBLE: + cubicv_notab( out, p, bands, lskip, y - iy ); + break; + + case VIPS_FORMAT_COMPLEX: + cubicv_float_tab( out, p, bands * 2, lskip, cyf ); + break; + + case VIPS_FORMAT_DPCOMPLEX: + cubicv_notab( out, p, bands * 2, lskip, y - iy ); + break; + + default: + break; + } +} + +static void +vips_interpolate_cubicv_class_init( VipsInterpolateCubicvClass *iclass ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass ); + VipsInterpolateClass *interpolate_class = + VIPS_INTERPOLATE_CLASS( iclass ); + + object_class->nickname = "cubicv"; + object_class->description = + _( "vertical cubic interpolation (Catmull-Rom)" ); + + interpolate_class->interpolate = vips_interpolate_cubicv_interpolate; + interpolate_class->window_size = 4; + + /* Build the tables of pre-computed coefficients. + */ + for( int y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) { + calculate_coefficients_catmull( + (float) y / VIPS_TRANSFORM_SCALE, + vips_cubicv_matrixf[y] ); + + for( int i = 0; i < 4; i++ ) + vips_cubicv_matrixi[y][i] = + vips_cubicv_matrixf[y][i] * + VIPS_INTERPOLATE_SCALE; + } +} + +static void +vips_interpolate_cubicv_init( VipsInterpolateCubicv *cubicv ) +{ +#ifdef DEBUG + printf( "vips_interpolate_cubicv_init: " ); + vips_object_print( VIPS_OBJECT( cubicv ) ); +#endif /*DEBUG*/ + +} + diff --git a/libvips/resample/interpolate.c b/libvips/resample/interpolate.c index bccb0fe8..781f3af9 100644 --- a/libvips/resample/interpolate.c +++ b/libvips/resample/interpolate.c @@ -601,6 +601,7 @@ vips__interpolate_init( void ) { extern GType vips_interpolate_bicubic_get_type( void ); extern GType vips_interpolate_cubich_get_type( void ); + extern GType vips_interpolate_cubicv_get_type( void ); extern GType vips_interpolate_lbb_get_type( void ); extern GType vips_interpolate_nohalo_get_type( void ); extern GType vips_interpolate_vsqbs_get_type( void ); @@ -610,6 +611,7 @@ vips__interpolate_init( void ) vips_interpolate_bicubic_get_type(); vips_interpolate_cubich_get_type(); + vips_interpolate_cubicv_get_type(); vips_interpolate_lbb_get_type(); vips_interpolate_nohalo_get_type(); vips_interpolate_vsqbs_get_type(); diff --git a/libvips/resample/reduceh.c b/libvips/resample/reduceh.c index 66c7c9bd..7235dd2a 100644 --- a/libvips/resample/reduceh.c +++ b/libvips/resample/reduceh.c @@ -53,7 +53,7 @@ typedef struct _VipsReduceh { VipsResample parent_instance; - double xreduce; /* Reduce factor */ + double xshrink; /* Reduce factor */ VipsInterpolate *interpolate; } VipsReduceh; @@ -86,9 +86,9 @@ vips_reduceh_gen( VipsRegion *or, void *seq, r->width, r->height, r->left, r->top ); #endif /*DEBUG*/ - s.left = r->left * reduceh->xreduce - window_offset; + s.left = r->left * reduceh->xshrink - window_offset; s.top = r->top; - s.width = r->width * reduceh->xreduce + window_size - 1; + s.width = r->width * reduceh->xshrink + window_size - 1; s.height = r->height; if( vips_region_prepare( ir, &s ) ) return( -1 ); @@ -103,7 +103,7 @@ vips_reduceh_gen( VipsRegion *or, void *seq, for( x = 0; x < r->width; x++ ) { double X = window_offset + - (r->left + x) * reduceh->xreduce; + (r->left + x) * reduceh->xshrink; interpolate( reduceh->interpolate, q, ir, X, Y ); @@ -157,16 +157,16 @@ vips_reduceh_build( VipsObject *object ) window_offset = vips_interpolate_get_window_offset( reduceh->interpolate ); - if( reduceh->xreduce < 1 ) { + if( reduceh->xshrink < 1 ) { vips_error( class->nickname, "%s", _( "reduce factors should be >= 1" ) ); return( -1 ); } - if( reduceh->xreduce > 2 ) + if( reduceh->xshrink > 2 ) vips_warn( class->nickname, "%s", _( "reduce factor greater than 2" ) ); - if( reduceh->xreduce == 1 ) + if( reduceh->xshrink == 1 ) return( vips_image_write( in, resample->out ) ); /* Unpack for processing. @@ -199,7 +199,7 @@ vips_reduceh_build( VipsObject *object ) * example, vipsthumbnail knows the true reduce factor (including the * fractional part), we just see the integer part here. */ - resample->out->Xsize = (in->Xsize - window_size + 1) / reduceh->xreduce; + resample->out->Xsize = (in->Xsize - window_size + 1) / reduceh->xshrink; if( resample->out->Xsize <= 0 ) { vips_error( class->nickname, "%s", _( "image has shrunk to nothing" ) ); @@ -233,16 +233,16 @@ vips_reduceh_class_init( VipsReducehClass *class ) gobject_class->get_property = vips_object_get_property; vobject_class->nickname = "reduceh"; - vobject_class->description = _( "reduce an image horizontally" ); + vobject_class->description = _( "shrink an image horizontally" ); vobject_class->build = vips_reduceh_build; operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; - VIPS_ARG_DOUBLE( class, "xreduce", 3, - _( "Xreduce" ), - _( "Horizontal reduce factor" ), + VIPS_ARG_DOUBLE( class, "xshrink", 3, + _( "Xshrink" ), + _( "Horizontal shrink factor" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsReduceh, xreduce ), + G_STRUCT_OFFSET( VipsReduceh, xshrink ), 1, 1000000, 1 ); VIPS_ARG_INTERPOLATE( class, "interpolate", 4, @@ -262,9 +262,13 @@ vips_reduceh_init( VipsReduceh *reduceh ) * vips_reduceh: * @in: input image * @out: output image - * @xreduce: horizontal reduce + * @xshrink: horizontal reduce * @...: %NULL-terminated list of optional named arguments * + * Optional arguments: + * + * @interpolate: interpolate pixels with this, default cubich + * * Reduce @in horizontally 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. @@ -280,13 +284,13 @@ vips_reduceh_init( VipsReduceh *reduceh ) * Returns: 0 on success, -1 on error */ int -vips_reduceh( VipsImage *in, VipsImage **out, double xreduce, ... ) +vips_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... ) { va_list ap; int result; - va_start( ap, xreduce ); - result = vips_call_split( "reduceh", ap, in, out, xreduce ); + va_start( ap, xshrink ); + result = vips_call_split( "reduceh", ap, in, out, xshrink ); va_end( ap ); return( result ); diff --git a/libvips/resample/reducev.c b/libvips/resample/reducev.c new file mode 100644 index 00000000..d3691d33 --- /dev/null +++ b/libvips/resample/reducev.c @@ -0,0 +1,296 @@ +/* horizontal reduce by a float factor + * + * 30/10/15 + * - from reducev.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" + +typedef struct _VipsReducev { + VipsResample parent_instance; + + double yshrink; /* Shrink factor */ + VipsInterpolate *interpolate; + +} VipsReducev; + +typedef VipsResampleClass VipsReducevClass; + +G_DEFINE_TYPE( VipsReducev, vips_reducev, VIPS_TYPE_RESAMPLE ); + +static int +vips_reducev_gen( VipsRegion *or, void *seq, + void *a, void *b, gboolean *stop ) +{ + VipsImage *in = (VipsImage *) a; + VipsReducev *reducev = (VipsReducev *) b; + int window_size = + vips_interpolate_get_window_size( reducev->interpolate ); + int window_offset = + vips_interpolate_get_window_offset( reducev->interpolate ); + const VipsInterpolateMethod interpolate = + vips_interpolate_get_method( reducev->interpolate ); + int ps = VIPS_IMAGE_SIZEOF_PEL( in ); + VipsRegion *ir = (VipsRegion *) seq; + VipsRect *r = &or->valid; + + VipsRect s; + int y; + +#ifdef DEBUG + printf( "vips_reducev_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 * reducev->yshrink - window_offset; + s.width = r->width; + s.height = r->height * reducev->yshrink + window_size - 1; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + VIPS_GATE_START( "vips_reducev_gen: work" ); + + for( y = 0; y < r->height; y ++ ) { + VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y ); + double Y = window_offset + (r->top + y) * reducev->yshrink; + + int x; + + for( x = 0; x < r->width; x++ ) { + double X = r->left + x; + + interpolate( reducev->interpolate, q, ir, X, Y ); + + q += ps; + } + } + + VIPS_GATE_STOP( "vips_reducev_gen: work" ); + + return( 0 ); +} + +static int +vips_reducev_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsResample *resample = VIPS_RESAMPLE( object ); + VipsReducev *reducev = (VipsReducev *) object; + VipsImage **t = (VipsImage **) + vips_object_local_array( object, 1 ); + + VipsImage *in; + int window_size; + int window_offset; + + if( VIPS_OBJECT_CLASS( vips_reducev_parent_class )->build( object ) ) + return( -1 ); + + in = resample->in; + + /* We can't use vips_object_argument_isset(), since it may have been + * set to NULL, see vips_similarity(). + */ + if( !reducev->interpolate ) { + VipsInterpolate *interpolate; + + interpolate = vips_interpolate_new( "cubich" ); + g_object_set( object, + "interpolate", interpolate, + NULL ); + g_object_unref( interpolate ); + + /* coverity gets confused by this, it thinks + * reducev->interpolate may still be null. Assign ourselves, + * even though we don't need to. + */ + reducev->interpolate = interpolate; + } + + window_size = vips_interpolate_get_window_size( reducev->interpolate ); + window_offset = + vips_interpolate_get_window_offset( reducev->interpolate ); + + if( reducev->yshrink < 1 ) { + vips_error( class->nickname, + "%s", _( "reduce factors should be >= 1" ) ); + return( -1 ); + } + if( reducev->yshrink > 2 ) + vips_warn( class->nickname, + "%s", _( "reduce factor greater than 2" ) ); + + if( reducev->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, window_offset, + in->Xsize, in->Ysize + window_size - 1, + "extend", VIPS_EXTEND_COPY, + NULL ) ) + return( -1 ); + in = t[1]; + + /* THINSTRIP will work, anything else will break seq mode. If you + * combine reduce with conv you'll need to use a line cache to maintain + * sequentiality. + */ + 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->Ysize = (in->Ysize - window_size + 1) / reducev->yshrink; + if( resample->out->Ysize <= 0 ) { + vips_error( class->nickname, + "%s", _( "image has shrunk to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "vips_reducev_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_reducev_gen, vips_stop_one, + in, reducev ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_reducev_class_init( VipsReducevClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_reducev_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "reducev"; + vobject_class->description = _( "shrink an image vertically" ); + vobject_class->build = vips_reducev_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_DOUBLE( class, "yshrink", 3, + _( "Xshrink" ), + _( "Vertical shrink factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReducev, yshrink ), + 1, 1000000, 1 ); + + VIPS_ARG_INTERPOLATE( class, "interpolate", 4, + _( "Interpolate" ), + _( "Interpolate pixels with this" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsReducev, interpolate ) ); + +} + +static void +vips_reducev_init( VipsReducev *reducev ) +{ +} + +/** + * vips_reducev: + * @in: input image + * @out: output image + * @yshrink: horizontal reduce + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @interpolate: interpolate pixels with this, default cubicv + * + * 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_reducev( VipsImage *in, VipsImage **out, double yshrink, ... ) +{ + va_list ap; + int result; + + va_start( ap, yshrink ); + result = vips_call_split( "reducev", ap, in, out, yshrink ); + va_end( ap ); + + return( result ); +} From 4b2349323c42ea06fe067ebd98121375a08e94f5 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 27 Jan 2016 21:59:07 +0000 Subject: [PATCH 06/13] all working though not as quick as hoped :( --- libvips/resample/Makefile.am | 1 + libvips/resample/cubich.cpp | 6 ++ libvips/resample/reduce.c | 186 +++++++++++++++++++++++++++++++++++ libvips/resample/reduceh.c | 5 +- libvips/resample/reducev.c | 4 +- libvips/resample/resample.c | 2 + 6 files changed, 199 insertions(+), 5 deletions(-) create mode 100644 libvips/resample/reduce.c diff --git a/libvips/resample/Makefile.am b/libvips/resample/Makefile.am index ecf76e60..fffd03ac 100644 --- a/libvips/resample/Makefile.am +++ b/libvips/resample/Makefile.am @@ -9,6 +9,7 @@ libresample_la_SOURCES = \ shrink.c \ shrinkh.c \ shrinkv.c \ + reduce.c \ reduceh.c \ reducev.c \ interpolate.c \ diff --git a/libvips/resample/cubich.cpp b/libvips/resample/cubich.cpp index ade73ab9..1c00d82d 100644 --- a/libvips/resample/cubich.cpp +++ b/libvips/resample/cubich.cpp @@ -268,6 +268,12 @@ vips_interpolate_cubich_interpolate( VipsInterpolate *interpolate, cubich_unsigned_int_tab( out, p, bands, cxi ); + //cubich_float_tab( + //out, p, bands, cxf ); + + //cubich_notab( + //out, p, bands, x - ix ); + /* Handy for benchmarking diff --git a/libvips/resample/reduce.c b/libvips/resample/reduce.c new file mode 100644 index 00000000..501975d0 --- /dev/null +++ b/libvips/resample/reduce.c @@ -0,0 +1,186 @@ +/* 2D reduce ... call reduceh and reducev + * + * 27/1/16 + * - from shrink.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" + +typedef struct _VipsReduce { + VipsResample parent_instance; + + double xshrink; /* Shrink factors */ + double yshrink; + VipsInterpolate *interpolateh; + VipsInterpolate *interpolatev; + +} VipsReduce; + +typedef VipsResampleClass VipsReduceClass; + +G_DEFINE_TYPE( VipsReduce, vips_reduce, VIPS_TYPE_RESAMPLE ); + +static int +vips_reduce_build( VipsObject *object ) +{ + VipsResample *resample = VIPS_RESAMPLE( object ); + VipsReduce *reduce = (VipsReduce *) object; + VipsImage **t = (VipsImage **) + vips_object_local_array( object, 3 ); + + if( VIPS_OBJECT_CLASS( vips_reduce_parent_class )->build( object ) ) + return( -1 ); + + if( vips_reducev( resample->in, &t[0], reduce->yshrink, + "interpolate", reduce->interpolatev, NULL ) || + vips_reduceh( t[0], &t[1], reduce->xshrink, + "interpolate", reduce->interpolateh, NULL ) || + vips_image_write( t[1], resample->out ) ) + return( -1 ); + + /* + if( vips_reduceh( resample->in, &t[0], reduce->xshrink, + "interpolate", reduce->interpolateh, NULL ) || + vips_reducev( t[0], &t[1], reduce->yshrink, + "interpolate", reduce->interpolatev, NULL ) || + vips_image_write( t[1], resample->out ) ) + return( -1 ); + */ + + return( 0 ); +} + +static void +vips_reduce_class_init( VipsReduceClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_reduce_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "reduce"; + vobject_class->description = _( "reduce an image" ); + vobject_class->build = vips_reduce_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_DOUBLE( class, "xshrink", 8, + _( "Xshrink" ), + _( "Horizontal shrink factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReduce, xshrink ), + 1.0, 1000000.0, 1.0 ); + + VIPS_ARG_DOUBLE( class, "yshrink", 9, + _( "Yshrink" ), + _( "Vertical shrink factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReduce, yshrink ), + 1.0, 1000000.0, 1.0 ); + + VIPS_ARG_INTERPOLATE( class, "interpolateh", 10, + _( "Interpolateh" ), + _( "Interpolate horizontal pixels with this" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsReduce, interpolateh ) ); + + VIPS_ARG_INTERPOLATE( class, "interpolatev", 10, + _( "Interpolatev" ), + _( "Interpolate vertical pixels with this" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsReduce, interpolatev ) ); + +} + +static void +vips_reduce_init( VipsReduce *reduce ) +{ +} + +/** + * vips_reduce: + * @in: input image + * @out: output image + * @xshrink: horizontal shrink + * @shrinke: vertical shrink + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @interpolateh: interpolate horizontally with this, default cubich + * @interpolatev: interpolate vertically with this, default cubicv + * + * Reduce @in by a pair of factors with a pair of 1D interpolators. This iwll + * not work well for shrink factors greater than 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_resize(), vips_affine(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_reduce( VipsImage *in, VipsImage **out, + double xshrink, double yshrink, ... ) +{ + va_list ap; + int result; + + va_start( ap, yshrink ); + result = vips_call_split( "reduce", ap, in, out, xshrink, yshrink ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/resample/reduceh.c b/libvips/resample/reduceh.c index 7235dd2a..00af66e1 100644 --- a/libvips/resample/reduceh.c +++ b/libvips/resample/reduceh.c @@ -97,16 +97,15 @@ vips_reduceh_gen( VipsRegion *or, void *seq, for( y = 0; y < r->height; y ++ ) { VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y ); + double X = window_offset + r->left * reduceh->xshrink; double Y = r->top + y; int x; for( x = 0; x < r->width; x++ ) { - double X = window_offset + - (r->left + x) * reduceh->xshrink; - interpolate( reduceh->interpolate, q, ir, X, Y ); + X += reduceh->xshrink; q += ps; } } diff --git a/libvips/resample/reducev.c b/libvips/resample/reducev.c index d3691d33..c4255dc0 100644 --- a/libvips/resample/reducev.c +++ b/libvips/resample/reducev.c @@ -97,15 +97,15 @@ vips_reducev_gen( VipsRegion *or, void *seq, for( y = 0; y < r->height; y ++ ) { VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y ); + double X = r->left; double Y = window_offset + (r->top + y) * reducev->yshrink; int x; for( x = 0; x < r->width; x++ ) { - double X = r->left + x; - interpolate( reducev->interpolate, q, ir, X, Y ); + X += 1; q += ps; } } diff --git a/libvips/resample/resample.c b/libvips/resample/resample.c index 39bcf421..8114e762 100644 --- a/libvips/resample/resample.c +++ b/libvips/resample/resample.c @@ -128,6 +128,8 @@ vips_resample_operation_init( void ) vips_shrinkh_get_type(); vips_shrinkv_get_type(); vips_reduceh_get_type(); + vips_reducev_get_type(); + vips_reduce_get_type(); vips_quadratic_get_type(); vips_affine_get_type(); vips_similarity_get_type(); From 3bb24f2567340daaca8e990af4f5255d2c59a0ca Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 28 Jan 2016 09:33:32 +0000 Subject: [PATCH 07/13] some tinkering still not very quick --- TODO | 27 +++++++++++++++++++++++++-- libvips/resample/reduce.c | 26 ++++++++++++++++++++++++++ libvips/resample/reduceh.c | 4 ---- libvips/resample/reducev.c | 6 +----- libvips/resample/resize.c | 6 ++++++ 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/TODO b/TODO index dc3d76a2..bdd86ca1 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,3 @@ -- look for FCLIP / FABS use ... VIPS_ABS() on float type - - write a shrinker that does two 1D shrinks, vertical and horizontal, with bicubic interpolation @@ -7,6 +5,31 @@ vips_resize() can use this, for the default path at least + try with affine vs. reduce in vips_resize ... on the laptop we see + + affine + $ time vipsthumbnail wtc.tif -o x.tif -s 7000 + real 0m2.325s + user 0m8.316s + sys 0m0.256s + + reduce + $ time vipsthumbnail wtc.tif -o x.tif -s 7000 + real 0m2.388s + user 0m8.544s + sys 0m0.244s + + argh + + reducev could be faster with the interpolator inlined ... we could pick a + set of masks and just run them along a set of input lines to make an output + line + + reduceh ... not so clear, need to pick new masks every output pixel, might + not make much difference + + + - get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very odd, the interpolation shouldn't change between bands diff --git a/libvips/resample/reduce.c b/libvips/resample/reduce.c index 501975d0..280c7f87 100644 --- a/libvips/resample/reduce.c +++ b/libvips/resample/reduce.c @@ -75,6 +75,32 @@ vips_reduce_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_reduce_parent_class )->build( object ) ) return( -1 ); + /* + if( vips_reduceh( resample->in, &t[0], reduce->xshrink, + "interpolate", reduce->interpolateh, NULL ) || + vips_linecache( t[0], &t[1], + "tile_height", 4, + NULL ) || + vips_reducev( t[1], &t[2], reduce->yshrink, + "interpolate", reduce->interpolatev, NULL ) || + vips_image_write( t[2], resample->out ) ) + return( -1 ); + */ + + /* + if( vips_reducev( resample->in, &t[0], reduce->yshrink, + "interpolate", reduce->interpolatev, + NULL ) || + vips_linecache( t[0], &t[1], + "tile_height", 4, + NULL ) || + vips_reduceh( t[1], &t[2], reduce->xshrink, + "interpolate", reduce->interpolateh, + NULL ) || + vips_image_write( t[2], resample->out ) ) + return( -1 ); + */ + if( vips_reducev( resample->in, &t[0], reduce->yshrink, "interpolate", reduce->interpolatev, NULL ) || vips_reduceh( t[0], &t[1], reduce->xshrink, diff --git a/libvips/resample/reduceh.c b/libvips/resample/reduceh.c index 00af66e1..dafc9a10 100644 --- a/libvips/resample/reduceh.c +++ b/libvips/resample/reduceh.c @@ -184,10 +184,6 @@ vips_reduceh_build( VipsObject *object ) return( -1 ); in = t[1]; - /* THINSTRIP will work, anything else will break seq mode. If you - * combine reduce with conv you'll need to use a line cache to maintain - * sequentiality. - */ if( vips_image_pipelinev( resample->out, VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) return( -1 ); diff --git a/libvips/resample/reducev.c b/libvips/resample/reducev.c index c4255dc0..822def62 100644 --- a/libvips/resample/reducev.c +++ b/libvips/resample/reducev.c @@ -184,12 +184,8 @@ vips_reducev_build( VipsObject *object ) return( -1 ); in = t[1]; - /* THINSTRIP will work, anything else will break seq mode. If you - * combine reduce with conv you'll need to use a line cache to maintain - * sequentiality. - */ if( vips_image_pipelinev( resample->out, - VIPS_DEMAND_STYLE_THINSTRIP, in, NULL ) ) + VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) return( -1 ); /* Size output. Note: we round the output width down! diff --git a/libvips/resample/resize.c b/libvips/resample/resize.c index 06131c9b..36947d30 100644 --- a/libvips/resample/resize.c +++ b/libvips/resample/resize.c @@ -230,12 +230,18 @@ vips_resize_build( VipsObject *object ) vips_info( class->nickname, "%s interpolation", nickname ); } + /* if( vips_affine( in, &t[3], hresidual, 0, 0, vresidual, "interpolate", resize->interpolate, "idx", resize->idx, "idy", resize->idy, NULL ) ) return( -1 ); + */ + + if( vips_reduce( in, &t[3], 1.0 / hresidual, 1.0 / vresidual, NULL ) ) + return( -1 ); + in = t[3]; /* If we are upsampling, don't sharpen. Also don't sharpen if we From dd7b30bd00bb34f75a41f8f3622edd4caf029e70 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 29 Jan 2016 13:16:00 +0000 Subject: [PATCH 08/13] in-line reducev bit quicker --- TODO | 8 + libvips/resample/Makefile.am | 3 +- libvips/resample/cubicv.cpp | 378 ---------------------------- libvips/resample/interpolate.c | 2 - libvips/resample/reduce.c | 17 +- libvips/resample/reducev.c | 292 ---------------------- libvips/resample/reducev.cpp | 437 +++++++++++++++++++++++++++++++++ 7 files changed, 449 insertions(+), 688 deletions(-) delete mode 100644 libvips/resample/cubicv.cpp delete mode 100644 libvips/resample/reducev.c create mode 100644 libvips/resample/reducev.cpp diff --git a/TODO b/TODO index bdd86ca1..555b02bb 100644 --- a/TODO +++ b/TODO @@ -28,6 +28,14 @@ reduceh ... not so clear, need to pick new masks every output pixel, might not make much difference + with in-line reducev + + $ time vipsthumbnail wtc.tif -o x.tif -s 7000 + real 0m1.897s + user 0m6.296s + sys 0m0.304s + + - get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very diff --git a/libvips/resample/Makefile.am b/libvips/resample/Makefile.am index fffd03ac..6911625d 100644 --- a/libvips/resample/Makefile.am +++ b/libvips/resample/Makefile.am @@ -11,12 +11,11 @@ libresample_la_SOURCES = \ shrinkv.c \ reduce.c \ reduceh.c \ - reducev.c \ + reducev.cpp \ interpolate.c \ transform.c \ bicubic.cpp \ cubich.cpp \ - cubicv.cpp \ lbb.cpp \ nohalo.cpp \ vsqbs.cpp \ diff --git a/libvips/resample/cubicv.cpp b/libvips/resample/cubicv.cpp deleted file mode 100644 index 63631335..00000000 --- a/libvips/resample/cubicv.cpp +++ /dev/null @@ -1,378 +0,0 @@ -/* vertical cubic (catmull-rom) interpolator - * - * 26/1/16 - * - from bicubic.cpp - */ - -/* - - 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 - - */ - -/* Cubic (Catmull-Rom) interpolator derived from Nicolas Robidoux's - * original YAFR resampler with permission and thanks. - */ - -/* -#define DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include - -#include -#include - -#include "templates.h" - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -#define VIPS_TYPE_INTERPOLATE_CUBICV \ - (vips_interpolate_cubicv_get_type()) -#define VIPS_INTERPOLATE_CUBICV( obj ) \ - (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ - VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicv )) -#define VIPS_INTERPOLATE_CUBICV_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_CAST( (klass), \ - VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicvClass)) -#define VIPS_IS_INTERPOLATE_CUBICV( obj ) \ - (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_CUBICV )) -#define VIPS_IS_INTERPOLATE_CUBICV_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_CUBICV )) -#define VIPS_INTERPOLATE_CUBICV_GET_CLASS( obj ) \ - (G_TYPE_INSTANCE_GET_CLASS( (obj), \ - VIPS_TYPE_INTERPOLATE_CUBICV, VipsInterpolateCubicvClass )) - -typedef VipsInterpolate VipsInterpolateCubicv; - -typedef VipsInterpolateClass VipsInterpolateCubicvClass; - -/* 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. - */ - -/* We could keep a large set of 2d 4x4 matricies, but this actually - * works out slower since for many resizes the thing will no longer - * fit in L1. - */ -static int vips_cubicv_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; -static double vips_cubicv_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; - -/* We need C linkage for this. - */ -extern "C" { -G_DEFINE_TYPE( VipsInterpolateCubicv, vips_interpolate_cubicv, - VIPS_TYPE_INTERPOLATE ); -} - -/* Pointers to write to / read from, number of bands, - * how many bytes to add to move down a line. - */ - -/* T is the type of pixels we are reading and writing. - */ - -/* Fixed-point version, for 8 and 16-bit types. - */ - -template -static void inline -cubicv_unsigned_int_tab( void *pout, const VipsPel *pin, - const int bands, const int lskip, - const int *cy ) -{ - T* restrict out = (T *) pout; - const T* restrict in = (T *) pin; - - const int l1 = lskip / sizeof( T ); - const int l2 = l1 + l1; - const int l3 = l1 + l2; - - for( int z = 0; z < bands; z++ ) { - const T uno = in[0]; - const T dos = in[l1]; - const T tre = in[l2]; - const T qua = in[l3]; - - int cubicv = cubic_unsigned_int( uno, dos, tre, qua, cy ); - - cubicv = VIPS_CLIP( 0, cubicv, max_value ); - - out[z] = cubicv; - - in += 1; - } -} - -template -static void inline -cubicv_signed_int_tab( void *pout, const VipsPel *pin, - const int bands, const int lskip, - const int *cy ) -{ - T* restrict out = (T *) pout; - const T* restrict in = (T *) pin; - - const int l1 = lskip / sizeof( T ); - const int l2 = l1 + l1; - const int l3 = l1 + l2; - - for( int z = 0; z < bands; z++ ) { - const T uno = in[0]; - const T dos = in[l1]; - const T tre = in[l2]; - const T qua = in[l3]; - - int cubicv = cubic_signed_int( uno, dos, tre, qua, cy ); - - cubicv = VIPS_CLIP( min_value, cubicv, max_value ); - - out[z] = cubicv; - - in += 1; - } -} - -/* Floating-point version, for int/float types. - */ -template -static void inline -cubicv_float_tab( void *pout, const VipsPel *pin, - const int bands, const int lskip, - const double *cy ) -{ - T* restrict out = (T *) pout; - const T* restrict in = (T *) pin; - - const int l1 = lskip / sizeof( T ); - const int l2 = l1 + l1; - const int l3 = l1 + l2; - - for( int z = 0; z < bands; z++ ) { - const T uno = in[0]; - const T dos = in[l1]; - const T tre = in[l2]; - const T qua = in[l3]; - - out[z] = cubic_float( uno, dos, tre, qua, cy ); - - in += 1; - } -} - -/* Ultra-high-quality version for double images. - */ -template -static void inline -cubicv_notab( void *pout, const VipsPel *pin, - const int bands, const int lskip, - double y ) -{ - T* restrict out = (T *) pout; - const T* restrict in = (T *) pin; - - const int l1 = lskip / sizeof( T ); - const int l2 = l1 + l1; - const int l3 = l1 + l2; - - double cy[4]; - - calculate_coefficients_catmull( y, cy ); - - for( int z = 0; z < bands; z++ ) { - const T uno = in[0]; - const T dos = in[l1]; - const T tre = in[l2]; - const T qua = in[l3]; - - out[z] = cubic_float( uno, dos, tre, qua, cy ); - - in += 1; - } -} - -static void -vips_interpolate_cubicv_interpolate( VipsInterpolate *interpolate, - void *out, VipsRegion *in, double x, double y ) -{ - /* Find the mask index. We round-to-nearest, so we need to generate - * indexes in 0 to VIPS_TRANSFORM_SCALE, 2^n + 1 values. We multiply - * by 2 more than we need to, add one, mask, then shift down again to - * get the extra range. - */ - const int sy = y * VIPS_TRANSFORM_SCALE * 2; - - const int siy = sy & (VIPS_TRANSFORM_SCALE * 2 - 1); - - const int ty = (siy + 1) >> 1; - - /* We know x/y are always positive, so we can just (int) them. - */ - const int ix = (int) x; - const int iy = (int) y; - - /* Back and up one to get the top-left of the 4x4. - */ - const VipsPel *p = VIPS_REGION_ADDR( in, ix - 1, iy - 1 ); - - /* Look up the tables we need. - */ - const int *cyi = vips_cubicv_matrixi[ty]; - const double *cyf = vips_cubicv_matrixf[ty]; - - /* Pel size and line size. - */ - const int bands = in->im->Bands; - const int lskip = VIPS_REGION_LSKIP( in ); - - g_assert( ix >= in->valid.left ); - g_assert( iy - 1 >= in->valid.top ); - g_assert( ix < VIPS_RECT_RIGHT( &in->valid ) ); - g_assert( iy + 2 < VIPS_RECT_BOTTOM( &in->valid ) ); - g_assert( ix == x ); - - /* Confirm that absolute_y is >= 1, because of window_offset. - */ - g_assert( y >= 1.0 ); - -#ifdef DEBUG - printf( "vips_interpolate_cubicv_interpolate: %g %g\n", x, y ); - printf( "\tleft=%d, top=%d, width=%d, height=%d\n", - ix, iy - 1, 1, 4 ); - printf( "\tmasky=%d\n", ty ); -#endif /*DEBUG*/ - - switch( in->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: - cubicv_unsigned_int_tab( - out, p, bands, lskip, - cyi ); - - /* - - Handy for benchmarking - - cubicv_float_tab( - out, p, bands, lskip, - cyf ); - - cubicv_notab( - out, p, bands, lskip, - y - iy ); - - */ - - break; - - case VIPS_FORMAT_CHAR: - cubicv_signed_int_tab( - out, p, bands, lskip, cyi ); - break; - - case VIPS_FORMAT_USHORT: - cubicv_unsigned_int_tab( - out, p, bands, lskip, cyi ); - break; - - case VIPS_FORMAT_SHORT: - cubicv_signed_int_tab( - out, p, bands, lskip, cyi ); - break; - - case VIPS_FORMAT_UINT: - cubicv_float_tab( out, p, bands, lskip, cyf ); - break; - - case VIPS_FORMAT_INT: - cubicv_float_tab( out, p, bands, lskip, cyf ); - break; - - case VIPS_FORMAT_FLOAT: - cubicv_float_tab( out, p, bands, lskip, cyf ); - break; - - case VIPS_FORMAT_DOUBLE: - cubicv_notab( out, p, bands, lskip, y - iy ); - break; - - case VIPS_FORMAT_COMPLEX: - cubicv_float_tab( out, p, bands * 2, lskip, cyf ); - break; - - case VIPS_FORMAT_DPCOMPLEX: - cubicv_notab( out, p, bands * 2, lskip, y - iy ); - break; - - default: - break; - } -} - -static void -vips_interpolate_cubicv_class_init( VipsInterpolateCubicvClass *iclass ) -{ - VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass ); - VipsInterpolateClass *interpolate_class = - VIPS_INTERPOLATE_CLASS( iclass ); - - object_class->nickname = "cubicv"; - object_class->description = - _( "vertical cubic interpolation (Catmull-Rom)" ); - - interpolate_class->interpolate = vips_interpolate_cubicv_interpolate; - interpolate_class->window_size = 4; - - /* Build the tables of pre-computed coefficients. - */ - for( int y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) { - calculate_coefficients_catmull( - (float) y / VIPS_TRANSFORM_SCALE, - vips_cubicv_matrixf[y] ); - - for( int i = 0; i < 4; i++ ) - vips_cubicv_matrixi[y][i] = - vips_cubicv_matrixf[y][i] * - VIPS_INTERPOLATE_SCALE; - } -} - -static void -vips_interpolate_cubicv_init( VipsInterpolateCubicv *cubicv ) -{ -#ifdef DEBUG - printf( "vips_interpolate_cubicv_init: " ); - vips_object_print( VIPS_OBJECT( cubicv ) ); -#endif /*DEBUG*/ - -} - diff --git a/libvips/resample/interpolate.c b/libvips/resample/interpolate.c index 781f3af9..bccb0fe8 100644 --- a/libvips/resample/interpolate.c +++ b/libvips/resample/interpolate.c @@ -601,7 +601,6 @@ vips__interpolate_init( void ) { extern GType vips_interpolate_bicubic_get_type( void ); extern GType vips_interpolate_cubich_get_type( void ); - extern GType vips_interpolate_cubicv_get_type( void ); extern GType vips_interpolate_lbb_get_type( void ); extern GType vips_interpolate_nohalo_get_type( void ); extern GType vips_interpolate_vsqbs_get_type( void ); @@ -611,7 +610,6 @@ vips__interpolate_init( void ) vips_interpolate_bicubic_get_type(); vips_interpolate_cubich_get_type(); - vips_interpolate_cubicv_get_type(); vips_interpolate_lbb_get_type(); vips_interpolate_nohalo_get_type(); vips_interpolate_vsqbs_get_type(); diff --git a/libvips/resample/reduce.c b/libvips/resample/reduce.c index 280c7f87..a2a50053 100644 --- a/libvips/resample/reduce.c +++ b/libvips/resample/reduce.c @@ -56,7 +56,6 @@ typedef struct _VipsReduce { double xshrink; /* Shrink factors */ double yshrink; VipsInterpolate *interpolateh; - VipsInterpolate *interpolatev; } VipsReduce; @@ -82,14 +81,13 @@ vips_reduce_build( VipsObject *object ) "tile_height", 4, NULL ) || vips_reducev( t[1], &t[2], reduce->yshrink, - "interpolate", reduce->interpolatev, NULL ) || + NULL ) || vips_image_write( t[2], resample->out ) ) return( -1 ); */ /* if( vips_reducev( resample->in, &t[0], reduce->yshrink, - "interpolate", reduce->interpolatev, NULL ) || vips_linecache( t[0], &t[1], "tile_height", 4, @@ -101,8 +99,7 @@ vips_reduce_build( VipsObject *object ) return( -1 ); */ - if( vips_reducev( resample->in, &t[0], reduce->yshrink, - "interpolate", reduce->interpolatev, NULL ) || + if( vips_reducev( resample->in, &t[0], reduce->yshrink, NULL ) || vips_reduceh( t[0], &t[1], reduce->xshrink, "interpolate", reduce->interpolateh, NULL ) || vips_image_write( t[1], resample->out ) ) @@ -111,8 +108,7 @@ vips_reduce_build( VipsObject *object ) /* if( vips_reduceh( resample->in, &t[0], reduce->xshrink, "interpolate", reduce->interpolateh, NULL ) || - vips_reducev( t[0], &t[1], reduce->yshrink, - "interpolate", reduce->interpolatev, NULL ) || + vips_reducev( t[0], &t[1], reduce->yshrink, NULL ) || vips_image_write( t[1], resample->out ) ) return( -1 ); */ @@ -158,12 +154,6 @@ vips_reduce_class_init( VipsReduceClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsReduce, interpolateh ) ); - VIPS_ARG_INTERPOLATE( class, "interpolatev", 10, - _( "Interpolatev" ), - _( "Interpolate vertical pixels with this" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsReduce, interpolatev ) ); - } static void @@ -182,7 +172,6 @@ vips_reduce_init( VipsReduce *reduce ) * Optional arguments: * * @interpolateh: interpolate horizontally with this, default cubich - * @interpolatev: interpolate vertically with this, default cubicv * * Reduce @in by a pair of factors with a pair of 1D interpolators. This iwll * not work well for shrink factors greater than two. diff --git a/libvips/resample/reducev.c b/libvips/resample/reducev.c deleted file mode 100644 index 822def62..00000000 --- a/libvips/resample/reducev.c +++ /dev/null @@ -1,292 +0,0 @@ -/* horizontal reduce by a float factor - * - * 30/10/15 - * - from reducev.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" - -typedef struct _VipsReducev { - VipsResample parent_instance; - - double yshrink; /* Shrink factor */ - VipsInterpolate *interpolate; - -} VipsReducev; - -typedef VipsResampleClass VipsReducevClass; - -G_DEFINE_TYPE( VipsReducev, vips_reducev, VIPS_TYPE_RESAMPLE ); - -static int -vips_reducev_gen( VipsRegion *or, void *seq, - void *a, void *b, gboolean *stop ) -{ - VipsImage *in = (VipsImage *) a; - VipsReducev *reducev = (VipsReducev *) b; - int window_size = - vips_interpolate_get_window_size( reducev->interpolate ); - int window_offset = - vips_interpolate_get_window_offset( reducev->interpolate ); - const VipsInterpolateMethod interpolate = - vips_interpolate_get_method( reducev->interpolate ); - int ps = VIPS_IMAGE_SIZEOF_PEL( in ); - VipsRegion *ir = (VipsRegion *) seq; - VipsRect *r = &or->valid; - - VipsRect s; - int y; - -#ifdef DEBUG - printf( "vips_reducev_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 * reducev->yshrink - window_offset; - s.width = r->width; - s.height = r->height * reducev->yshrink + window_size - 1; - if( vips_region_prepare( ir, &s ) ) - return( -1 ); - - VIPS_GATE_START( "vips_reducev_gen: work" ); - - for( y = 0; y < r->height; y ++ ) { - VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y ); - double X = r->left; - double Y = window_offset + (r->top + y) * reducev->yshrink; - - int x; - - for( x = 0; x < r->width; x++ ) { - interpolate( reducev->interpolate, q, ir, X, Y ); - - X += 1; - q += ps; - } - } - - VIPS_GATE_STOP( "vips_reducev_gen: work" ); - - return( 0 ); -} - -static int -vips_reducev_build( VipsObject *object ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsResample *resample = VIPS_RESAMPLE( object ); - VipsReducev *reducev = (VipsReducev *) object; - VipsImage **t = (VipsImage **) - vips_object_local_array( object, 1 ); - - VipsImage *in; - int window_size; - int window_offset; - - if( VIPS_OBJECT_CLASS( vips_reducev_parent_class )->build( object ) ) - return( -1 ); - - in = resample->in; - - /* We can't use vips_object_argument_isset(), since it may have been - * set to NULL, see vips_similarity(). - */ - if( !reducev->interpolate ) { - VipsInterpolate *interpolate; - - interpolate = vips_interpolate_new( "cubich" ); - g_object_set( object, - "interpolate", interpolate, - NULL ); - g_object_unref( interpolate ); - - /* coverity gets confused by this, it thinks - * reducev->interpolate may still be null. Assign ourselves, - * even though we don't need to. - */ - reducev->interpolate = interpolate; - } - - window_size = vips_interpolate_get_window_size( reducev->interpolate ); - window_offset = - vips_interpolate_get_window_offset( reducev->interpolate ); - - if( reducev->yshrink < 1 ) { - vips_error( class->nickname, - "%s", _( "reduce factors should be >= 1" ) ); - return( -1 ); - } - if( reducev->yshrink > 2 ) - vips_warn( class->nickname, - "%s", _( "reduce factor greater than 2" ) ); - - if( reducev->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, window_offset, - in->Xsize, in->Ysize + window_size - 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 - window_size + 1) / reducev->yshrink; - if( resample->out->Ysize <= 0 ) { - vips_error( class->nickname, - "%s", _( "image has shrunk to nothing" ) ); - return( -1 ); - } - -#ifdef DEBUG - printf( "vips_reducev_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_reducev_gen, vips_stop_one, - in, reducev ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_reducev_class_init( VipsReducevClass *class ) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS( class ); - VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); - VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); - - VIPS_DEBUG_MSG( "vips_reducev_class_init\n" ); - - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; - - vobject_class->nickname = "reducev"; - vobject_class->description = _( "shrink an image vertically" ); - vobject_class->build = vips_reducev_build; - - operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; - - VIPS_ARG_DOUBLE( class, "yshrink", 3, - _( "Xshrink" ), - _( "Vertical shrink factor" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsReducev, yshrink ), - 1, 1000000, 1 ); - - VIPS_ARG_INTERPOLATE( class, "interpolate", 4, - _( "Interpolate" ), - _( "Interpolate pixels with this" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsReducev, interpolate ) ); - -} - -static void -vips_reducev_init( VipsReducev *reducev ) -{ -} - -/** - * vips_reducev: - * @in: input image - * @out: output image - * @yshrink: horizontal reduce - * @...: %NULL-terminated list of optional named arguments - * - * Optional arguments: - * - * @interpolate: interpolate pixels with this, default cubicv - * - * 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_reducev( VipsImage *in, VipsImage **out, double yshrink, ... ) -{ - va_list ap; - int result; - - va_start( ap, yshrink ); - result = vips_call_split( "reducev", ap, in, out, yshrink ); - va_end( ap ); - - return( result ); -} diff --git a/libvips/resample/reducev.cpp b/libvips/resample/reducev.cpp new file mode 100644 index 00000000..0fd8be66 --- /dev/null +++ b/libvips/resample/reducev.cpp @@ -0,0 +1,437 @@ +/* horizontal reduce by a float factor + * + * 30/10/15 + * - from reducev.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 _VipsReducev { + VipsResample parent_instance; + + double yshrink; /* Shrink factor */ + +} VipsReducev; + +typedef VipsResampleClass VipsReducevClass; + +static int vips_reducev_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; +static double vips_reducev_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsReducev, vips_reducev, VIPS_TYPE_RESAMPLE ); +} + +template +static void inline +reducev_unsigned_int_tab( VipsPel *pout, const VipsPel *pin, + const int ne, const int lskip, + const int *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + for( int z = 0; z < ne; z++ ) { + const T uno = in[0]; + const T dos = in[l1]; + const T tre = in[l2]; + const T qua = in[l3]; + + int cubicv = cubic_unsigned_int( uno, dos, tre, qua, cy ); + + cubicv = VIPS_CLIP( 0, cubicv, max_value ); + + out[z] = cubicv; + + in += 1; + } +} + +template +static void inline +reducev_signed_int_tab( VipsPel *pout, const VipsPel *pin, + const int ne, const int lskip, + const int *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + for( int z = 0; z < ne; z++ ) { + const T uno = in[0]; + const T dos = in[l1]; + const T tre = in[l2]; + const T qua = in[l3]; + + int cubicv = cubic_signed_int( uno, dos, tre, qua, cy ); + + cubicv = VIPS_CLIP( min_value, cubicv, max_value ); + + out[z] = cubicv; + + in += 1; + } +} + +/* Floating-point version, for int/float types. + */ +template +static void inline +reducev_float_tab( VipsPel *pout, const VipsPel *pin, + const int ne, const int lskip, + const double *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + for( int z = 0; z < ne; z++ ) { + const T uno = in[0]; + const T dos = in[l1]; + const T tre = in[l2]; + const T qua = in[l3]; + + out[z] = cubic_float( uno, dos, tre, qua, cy ); + + in += 1; + } +} + +/* Ultra-high-quality version for double images. + */ +template +static void inline +reducev_notab( VipsPel *pout, const VipsPel *pin, + const int ne, const int lskip, + double y ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + double cy[4]; + + calculate_coefficients_catmull( y, cy ); + + for( int z = 0; z < ne; z++ ) { + const T uno = in[0]; + const T dos = in[l1]; + const T tre = in[l2]; + const T qua = in[l3]; + + out[z] = cubic_float( uno, dos, tre, qua, cy ); + + in += 1; + } +} + +static int +vips_reducev_gen( VipsRegion *out_region, void *seq, + void *a, void *b, gboolean *stop ) +{ + VipsImage *in = (VipsImage *) a; + VipsReducev *reducev = (VipsReducev *) 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; + int y; + +#ifdef DEBUG + printf( "vips_reducev_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 * reducev->yshrink; + s.width = r->width; + s.height = r->height * reducev->yshrink + 3; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + VIPS_GATE_START( "vips_reducev_gen: work" ); + + for( 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->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_reducev_matrixi[ty]; + const double *cyf = vips_reducev_matrixf[ty]; + const int lskip = VIPS_REGION_LSKIP( ir ); + + switch( in->BandFmt ) { + case VIPS_FORMAT_UCHAR: + reducev_unsigned_int_tab + ( + q, p, ne, lskip, cyi ); + break; + + case VIPS_FORMAT_CHAR: + reducev_signed_int_tab + ( + q, p, ne, lskip, cyi ); + break; + + case VIPS_FORMAT_USHORT: + reducev_unsigned_int_tab + ( + q, p, ne, lskip, cyi ); + break; + + case VIPS_FORMAT_SHORT: + reducev_signed_int_tab + ( + q, p, ne, lskip, cyi ); + break; + + case VIPS_FORMAT_UINT: + reducev_float_tab( q, p, ne, lskip, cyf ); + break; + + case VIPS_FORMAT_INT: + reducev_float_tab( q, p, ne, lskip, cyf ); + break; + + case VIPS_FORMAT_FLOAT: + case VIPS_FORMAT_COMPLEX: + reducev_float_tab( q, p, ne, lskip, cyf ); + break; + + case VIPS_FORMAT_DPCOMPLEX: + case VIPS_FORMAT_DOUBLE: + reducev_notab( q, p, ne, lskip, Y - (int) Y ); + break; + + default: + g_assert_not_reached(); + break; + } + } + + VIPS_GATE_STOP( "vips_reducev_gen: work" ); + + return( 0 ); +} + +static int +vips_reducev_build( VipsObject *object ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); + VipsResample *resample = VIPS_RESAMPLE( object ); + VipsReducev *reducev = (VipsReducev *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); + + VipsImage *in; + + if( VIPS_OBJECT_CLASS( vips_reducev_parent_class )->build( object ) ) + return( -1 ); + + in = resample->in; + + if( reducev->yshrink < 1 ) { + vips_error( object_class->nickname, + "%s", _( "reduce factors should be >= 1" ) ); + return( -1 ); + } + if( reducev->yshrink > 2 ) + vips_warn( object_class->nickname, + "%s", _( "reduce factor greater than 2" ) ); + + if( reducev->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, 1, + in->Xsize, in->Ysize + 3, + "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 - 3) / reducev->yshrink; + if( resample->out->Ysize <= 0 ) { + vips_error( object_class->nickname, + "%s", _( "image has shrunk to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "vips_reducev_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_reducev_gen, vips_stop_one, + in, reducev ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_reducev_class_init( VipsReducevClass *reducev_class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( reducev_class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( reducev_class ); + VipsOperationClass *operation_class = + VIPS_OPERATION_CLASS( reducev_class ); + + VIPS_DEBUG_MSG( "vips_reducev_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "reducev"; + vobject_class->description = _( "shrink an image vertically" ); + vobject_class->build = vips_reducev_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_DOUBLE( reducev_class, "yshrink", 3, + _( "Xshrink" ), + _( "Vertical shrink factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReducev, yshrink ), + 1, 1000000, 1 ); + + /* Build the tables of pre-computed coefficients. + */ + for( int y = 0; y < VIPS_TRANSFORM_SCALE + 1; y++ ) { + calculate_coefficients_catmull( + (float) y / VIPS_TRANSFORM_SCALE, + vips_reducev_matrixf[y] ); + + for( int i = 0; i < 4; i++ ) + vips_reducev_matrixi[y][i] = + vips_reducev_matrixf[y][i] * + VIPS_INTERPOLATE_SCALE; + } + +} + +static void +vips_reducev_init( VipsReducev *reducev ) +{ +} + +/** + * vips_reducev: + * @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_reducev( VipsImage *in, VipsImage **out, double yshrink, ... ) +{ + va_list ap; + int result; + + va_start( ap, yshrink ); + result = vips_call_split( "reducev", ap, in, out, yshrink ); + va_end( ap ); + + return( result ); +} From fbe010b80a78051fd83d50cdb5cb80e6cf7ff699 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 29 Jan 2016 15:01:54 +0000 Subject: [PATCH 09/13] in-line reduceh as well --- TODO | 14 +- libvips/resample/Makefile.am | 3 +- libvips/resample/cubich.cpp | 372 -------------------------- libvips/resample/interpolate.c | 2 - libvips/resample/reduce.c | 34 +-- libvips/resample/reduceh.c | 292 --------------------- libvips/resample/reduceh.cpp | 461 +++++++++++++++++++++++++++++++++ libvips/resample/reducev.cpp | 78 +++--- 8 files changed, 532 insertions(+), 724 deletions(-) delete mode 100644 libvips/resample/cubich.cpp delete mode 100644 libvips/resample/reduceh.c create mode 100644 libvips/resample/reduceh.cpp diff --git a/TODO b/TODO index 555b02bb..78e39756 100644 --- a/TODO +++ b/TODO @@ -35,8 +35,20 @@ user 0m6.296s sys 0m0.304s + with in-line reduceh as well + + $ time vipsthumbnail wtc.tif -o x.tif -s 7000 + real 0m1.838s + user 0m6.132s + sys 0m0.264s + + more small tweaks + + $ time vipsthumbnail wtc.tif -o x.tif -s 7000 + real 0m1.843s + user 0m6.040s + sys 0m0.252s - - get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very odd, the interpolation shouldn't change between bands diff --git a/libvips/resample/Makefile.am b/libvips/resample/Makefile.am index 6911625d..bae493c0 100644 --- a/libvips/resample/Makefile.am +++ b/libvips/resample/Makefile.am @@ -10,12 +10,11 @@ libresample_la_SOURCES = \ shrinkh.c \ shrinkv.c \ reduce.c \ - reduceh.c \ + reduceh.cpp \ reducev.cpp \ interpolate.c \ transform.c \ bicubic.cpp \ - cubich.cpp \ lbb.cpp \ nohalo.cpp \ vsqbs.cpp \ diff --git a/libvips/resample/cubich.cpp b/libvips/resample/cubich.cpp deleted file mode 100644 index 1c00d82d..00000000 --- a/libvips/resample/cubich.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/* horizontal cubic (catmull-rom) interpolator - * - * 26/1/16 - * - from bicubic.cpp - */ - -/* - - 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 - - */ - -/* Cubic (Catmull-Rom) interpolator derived from Nicolas Robidoux's - * original YAFR resampler with permission and thanks. - */ - -/* -#define DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include - -#include -#include - -#include "templates.h" - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -#define VIPS_TYPE_INTERPOLATE_CUBICH \ - (vips_interpolate_cubich_get_type()) -#define VIPS_INTERPOLATE_CUBICH( obj ) \ - (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ - VIPS_TYPE_INTERPOLATE_CUBICH, VipsInterpolateCubich )) -#define VIPS_INTERPOLATE_CUBICH_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_CAST( (klass), \ - VIPS_TYPE_INTERPOLATE_CUBICH, VipsInterpolateCubichClass)) -#define VIPS_IS_INTERPOLATE_CUBICH( obj ) \ - (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_INTERPOLATE_CUBICH )) -#define VIPS_IS_INTERPOLATE_CUBICH_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_INTERPOLATE_CUBICH )) -#define VIPS_INTERPOLATE_CUBICH_GET_CLASS( obj ) \ - (G_TYPE_INSTANCE_GET_CLASS( (obj), \ - VIPS_TYPE_INTERPOLATE_CUBICH, VipsInterpolateCubichClass )) - -typedef VipsInterpolate VipsInterpolateCubich; - -typedef VipsInterpolateClass VipsInterpolateCubichClass; - -/* 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. - */ - -static int vips_cubich_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; -static double vips_cubich_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; - -/* We need C linkage for this. - */ -extern "C" { -G_DEFINE_TYPE( VipsInterpolateCubich, vips_interpolate_cubich, - VIPS_TYPE_INTERPOLATE ); -} - -/* Pointers to write to / read from, number of bands, - * how many bytes to add to move down a line. - */ - -/* T is the type of pixels we are reading and writing. - */ - -/* Fixed-point version, for 8 and 16-bit types. - */ - -template -static void inline -cubich_unsigned_int_tab( void *pout, const VipsPel *pin, - const int bands, const int *cx ) -{ - T* restrict out = (T *) pout; - const T* restrict in = (T *) pin; - - const int b1 = bands; - const int b2 = b1 + b1; - const int b3 = b1 + b2; - - for( int z = 0; z < bands; z++ ) { - const T one = in[0]; - const T two = in[b1]; - const T thr = in[b2]; - const T fou = in[b3]; - - int cubich = cubic_unsigned_int( one, two, thr, fou, cx ); - - cubich = VIPS_CLIP( 0, cubich, max_value ); - - out[z] = cubich; - - in += 1; - } -} - -template -static void inline -cubich_signed_int_tab( void *pout, const VipsPel *pin, - const int bands, const int *cx ) -{ - T* restrict out = (T *) pout; - const T* restrict in = (T *) pin; - - const int b1 = bands; - const int b2 = b1 + b1; - const int b3 = b1 + b2; - - for( int z = 0; z < bands; z++ ) { - const T one = in[0]; - const T two = in[b1]; - const T thr = in[b2]; - const T fou = in[b3]; - - int cubich = cubic_signed_int( one, two, thr, fou, cx ); - - cubich = VIPS_CLIP( min_value, cubich, max_value ); - - out[z] = cubich; - - in += 1; - } -} - -/* Floating-point version, for int/float types. - */ -template -static void inline -cubich_float_tab( void *pout, const VipsPel *pin, - const int bands, const double *cx ) -{ - T* restrict out = (T *) pout; - const T* restrict in = (T *) pin; - - const int b1 = bands; - const int b2 = b1 + b1; - const int b3 = b1 + b2; - - for( int z = 0; z < bands; z++ ) { - const T one = in[0]; - const T two = in[b1]; - const T thr = in[b2]; - const T fou = in[b3]; - - out[z] = cubic_float( one, two, thr, fou, cx ); - - in += 1; - } -} - -/* Ultra-high-quality version for double images. - */ -template -static void inline -cubich_notab( void *pout, const VipsPel *pin, - const int bands, double x ) -{ - T* restrict out = (T *) pout; - const T* restrict in = (T *) pin; - - const int b1 = bands; - const int b2 = b1 + b1; - const int b3 = b1 + b2; - - double cx[4]; - - calculate_coefficients_catmull( x, cx ); - - for( int z = 0; z < bands; z++ ) { - const T one = in[0]; - const T two = in[b1]; - const T thr = in[b2]; - const T fou = in[b3]; - - out[z] = cubic_float( one, two, thr, fou, cx ); - - in += 1; - } -} - -static void -vips_interpolate_cubich_interpolate( VipsInterpolate *interpolate, - void *out, VipsRegion *in, double x, double y ) -{ - /* Find the mask index. We round-to-nearest, so we need to generate - * indexes in 0 to VIPS_TRANSFORM_SCALE, 2^n + 1 values. We multiply - * by 2 more than we need to, add one, mask, then shift down again to - * get the extra range. - */ - const int sx = x * VIPS_TRANSFORM_SCALE * 2; - - const int six = sx & (VIPS_TRANSFORM_SCALE * 2 - 1); - - const int tx = (six + 1) >> 1; - - /* We know (x, y) are always positive, so we can just (int) them. - */ - const int ix = (int) x; - const int iy = (int) y; - - /* Back one to get the left of the 4x1. - */ - const VipsPel *p = VIPS_REGION_ADDR( in, ix - 1, iy ); - - /* Look up the tables we need. - */ - const int *cxi = vips_cubich_matrixi[tx]; - const double *cxf = vips_cubich_matrixf[tx]; - - /* Pel size and line size. - */ - const int bands = in->im->Bands; - - g_assert( ix - 1 >= in->valid.left ); - g_assert( iy >= in->valid.top ); - g_assert( ix + 2 < VIPS_RECT_RIGHT( &in->valid ) ); - g_assert( iy < VIPS_RECT_BOTTOM( &in->valid ) ); - g_assert( iy == y ); - - /* Confirm that absolute_x >= 1, because of window_offset. - */ - g_assert( x >= 1.0 ); - -#ifdef DEBUG - printf( "vips_interpolate_cubich_interpolate: %g %g\n", x, y ); - printf( "\tleft=%d, top=%d, width=%d, height=%d\n", - ix - 1, iy, 4, 1 ); - printf( "\tmaskx=%d\n", tx ); -#endif /*DEBUG*/ - - switch( in->im->BandFmt ) { - case VIPS_FORMAT_UCHAR: - cubich_unsigned_int_tab( - out, p, bands, cxi ); - - //cubich_float_tab( - //out, p, bands, cxf ); - - //cubich_notab( - //out, p, bands, x - ix ); - - /* - - Handy for benchmarking - - cubich_float_tab( - out, p, bands, cxf ); - - cubich_notab( - out, p, bands, x - ix ); - - */ - - break; - - case VIPS_FORMAT_CHAR: - cubich_signed_int_tab( - out, p, bands, cxi ); - break; - - case VIPS_FORMAT_USHORT: - cubich_unsigned_int_tab( - out, p, bands, cxi ); - break; - - case VIPS_FORMAT_SHORT: - cubich_signed_int_tab( - out, p, bands, cxi ); - break; - - case VIPS_FORMAT_UINT: - cubich_float_tab( out, p, bands, cxf ); - break; - - case VIPS_FORMAT_INT: - cubich_float_tab( out, p, bands, cxf ); - break; - - case VIPS_FORMAT_FLOAT: - cubich_float_tab( out, p, bands, cxf ); - break; - - case VIPS_FORMAT_DOUBLE: - cubich_notab( out, p, bands, x - ix ); - break; - - case VIPS_FORMAT_COMPLEX: - cubich_float_tab( out, p, bands * 2, cxf ); - break; - - case VIPS_FORMAT_DPCOMPLEX: - cubich_notab( out, p, bands * 2, x - ix ); - break; - - default: - break; - } -} - -static void -vips_interpolate_cubich_class_init( VipsInterpolateCubichClass *iclass ) -{ - VipsObjectClass *object_class = VIPS_OBJECT_CLASS( iclass ); - VipsInterpolateClass *interpolate_class = - VIPS_INTERPOLATE_CLASS( iclass ); - - object_class->nickname = "cubich"; - object_class->description = - _( "horizontal cubic interpolation (Catmull-Rom)" ); - - interpolate_class->interpolate = vips_interpolate_cubich_interpolate; - interpolate_class->window_size = 4; - - /* Build the tables of pre-computed coefficients. - */ - for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) { - calculate_coefficients_catmull( - (float) x / VIPS_TRANSFORM_SCALE, - vips_cubich_matrixf[x] ); - - for( int i = 0; i < 4; i++ ) - vips_cubich_matrixi[x][i] = - vips_cubich_matrixf[x][i] * - VIPS_INTERPOLATE_SCALE; - } -} - -static void -vips_interpolate_cubich_init( VipsInterpolateCubich *cubich ) -{ -#ifdef DEBUG - printf( "vips_interpolate_cubich_init: " ); - vips_object_print( VIPS_OBJECT( cubich ) ); -#endif /*DEBUG*/ - -} - diff --git a/libvips/resample/interpolate.c b/libvips/resample/interpolate.c index bccb0fe8..354c8a94 100644 --- a/libvips/resample/interpolate.c +++ b/libvips/resample/interpolate.c @@ -600,7 +600,6 @@ void vips__interpolate_init( void ) { extern GType vips_interpolate_bicubic_get_type( void ); - extern GType vips_interpolate_cubich_get_type( void ); extern GType vips_interpolate_lbb_get_type( void ); extern GType vips_interpolate_nohalo_get_type( void ); extern GType vips_interpolate_vsqbs_get_type( void ); @@ -609,7 +608,6 @@ vips__interpolate_init( void ) vips_interpolate_bilinear_get_type(); vips_interpolate_bicubic_get_type(); - vips_interpolate_cubich_get_type(); vips_interpolate_lbb_get_type(); vips_interpolate_nohalo_get_type(); vips_interpolate_vsqbs_get_type(); diff --git a/libvips/resample/reduce.c b/libvips/resample/reduce.c index a2a50053..ebaa02d7 100644 --- a/libvips/resample/reduce.c +++ b/libvips/resample/reduce.c @@ -55,7 +55,6 @@ typedef struct _VipsReduce { double xshrink; /* Shrink factors */ double yshrink; - VipsInterpolate *interpolateh; } VipsReduce; @@ -75,39 +74,32 @@ vips_reduce_build( VipsObject *object ) return( -1 ); /* - if( vips_reduceh( resample->in, &t[0], reduce->xshrink, - "interpolate", reduce->interpolateh, NULL ) || + if( vips_reduceh( resample->in, &t[0], reduce->xshrink, NULL ) || vips_linecache( t[0], &t[1], "tile_height", 4, NULL ) || - vips_reducev( t[1], &t[2], reduce->yshrink, - NULL ) || + vips_reducev( t[1], &t[2], reduce->yshrink, NULL ) || vips_image_write( t[2], resample->out ) ) return( -1 ); */ /* - if( vips_reducev( resample->in, &t[0], reduce->yshrink, - NULL ) || + if( vips_reducev( resample->in, &t[0], reduce->yshrink, NULL ) || vips_linecache( t[0], &t[1], "tile_height", 4, NULL ) || - vips_reduceh( t[1], &t[2], reduce->xshrink, - "interpolate", reduce->interpolateh, - NULL ) || + vips_reduceh( t[1], &t[2], reduce->xshrink, NULL ) || vips_image_write( t[2], resample->out ) ) return( -1 ); */ if( vips_reducev( resample->in, &t[0], reduce->yshrink, NULL ) || - vips_reduceh( t[0], &t[1], reduce->xshrink, - "interpolate", reduce->interpolateh, NULL ) || + vips_reduceh( t[0], &t[1], reduce->xshrink, NULL ) || vips_image_write( t[1], resample->out ) ) return( -1 ); /* - if( vips_reduceh( resample->in, &t[0], reduce->xshrink, - "interpolate", reduce->interpolateh, NULL ) || + if( vips_reduceh( resample->in, &t[0], reduce->xshrink, NULL ) || vips_reducev( t[0], &t[1], reduce->yshrink, NULL ) || vips_image_write( t[1], resample->out ) ) return( -1 ); @@ -148,12 +140,6 @@ vips_reduce_class_init( VipsReduceClass *class ) G_STRUCT_OFFSET( VipsReduce, yshrink ), 1.0, 1000000.0, 1.0 ); - VIPS_ARG_INTERPOLATE( class, "interpolateh", 10, - _( "Interpolateh" ), - _( "Interpolate horizontal pixels with this" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsReduce, interpolateh ) ); - } static void @@ -169,12 +155,8 @@ vips_reduce_init( VipsReduce *reduce ) * @shrinke: vertical shrink * @...: %NULL-terminated list of optional named arguments * - * Optional arguments: - * - * @interpolateh: interpolate horizontally with this, default cubich - * - * Reduce @in by a pair of factors with a pair of 1D interpolators. This iwll - * not work well for shrink factors greater than two. + * Reduce @in by a pair of factors with a pair of 1D cubic interpolators. This + * will not work well for shrink factors greater than two. * * This is a very low-level operation: see vips_resize() for a more * convenient way to resize images. diff --git a/libvips/resample/reduceh.c b/libvips/resample/reduceh.c deleted file mode 100644 index dafc9a10..00000000 --- a/libvips/resample/reduceh.c +++ /dev/null @@ -1,292 +0,0 @@ -/* horizontal reduce by a float factor - * - * 30/10/15 - * - from reduceh.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" - -typedef struct _VipsReduceh { - VipsResample parent_instance; - - double xshrink; /* Reduce factor */ - VipsInterpolate *interpolate; - -} VipsReduceh; - -typedef VipsResampleClass VipsReducehClass; - -G_DEFINE_TYPE( VipsReduceh, vips_reduceh, VIPS_TYPE_RESAMPLE ); - -static int -vips_reduceh_gen( VipsRegion *or, void *seq, - void *a, void *b, gboolean *stop ) -{ - VipsImage *in = (VipsImage *) a; - VipsReduceh *reduceh = (VipsReduceh *) b; - int window_size = - vips_interpolate_get_window_size( reduceh->interpolate ); - int window_offset = - vips_interpolate_get_window_offset( reduceh->interpolate ); - const VipsInterpolateMethod interpolate = - vips_interpolate_get_method( reduceh->interpolate ); - int ps = VIPS_IMAGE_SIZEOF_PEL( in ); - VipsRegion *ir = (VipsRegion *) seq; - VipsRect *r = &or->valid; - - VipsRect s; - int y; - -#ifdef DEBUG - printf( "vips_reduceh_gen: generating %d x %d at %d x %d\n", - r->width, r->height, r->left, r->top ); -#endif /*DEBUG*/ - - s.left = r->left * reduceh->xshrink - window_offset; - s.top = r->top; - s.width = r->width * reduceh->xshrink + window_size - 1; - s.height = r->height; - if( vips_region_prepare( ir, &s ) ) - return( -1 ); - - VIPS_GATE_START( "vips_reduceh_gen: work" ); - - for( y = 0; y < r->height; y ++ ) { - VipsPel *q = VIPS_REGION_ADDR( or, r->left, r->top + y ); - double X = window_offset + r->left * reduceh->xshrink; - double Y = r->top + y; - - int x; - - for( x = 0; x < r->width; x++ ) { - interpolate( reduceh->interpolate, q, ir, X, Y ); - - X += reduceh->xshrink; - q += ps; - } - } - - VIPS_GATE_STOP( "vips_reduceh_gen: work" ); - - return( 0 ); -} - -static int -vips_reduceh_build( VipsObject *object ) -{ - VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); - VipsResample *resample = VIPS_RESAMPLE( object ); - VipsReduceh *reduceh = (VipsReduceh *) object; - VipsImage **t = (VipsImage **) - vips_object_local_array( object, 1 ); - - VipsImage *in; - int window_size; - int window_offset; - - if( VIPS_OBJECT_CLASS( vips_reduceh_parent_class )->build( object ) ) - return( -1 ); - - in = resample->in; - - /* We can't use vips_object_argument_isset(), since it may have been - * set to NULL, see vips_similarity(). - */ - if( !reduceh->interpolate ) { - VipsInterpolate *interpolate; - - interpolate = vips_interpolate_new( "cubich" ); - g_object_set( object, - "interpolate", interpolate, - NULL ); - g_object_unref( interpolate ); - - /* coverity gets confused by this, it thinks - * reduceh->interpolate may still be null. Assign ourselves, - * even though we don't need to. - */ - reduceh->interpolate = interpolate; - } - - window_size = vips_interpolate_get_window_size( reduceh->interpolate ); - window_offset = - vips_interpolate_get_window_offset( reduceh->interpolate ); - - if( reduceh->xshrink < 1 ) { - vips_error( class->nickname, - "%s", _( "reduce factors should be >= 1" ) ); - return( -1 ); - } - if( reduceh->xshrink > 2 ) - vips_warn( class->nickname, - "%s", _( "reduce factor greater than 2" ) ); - - if( reduceh->xshrink == 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], - window_offset, 0, - in->Xsize + window_size - 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 - window_size + 1) / reduceh->xshrink; - if( resample->out->Xsize <= 0 ) { - vips_error( class->nickname, - "%s", _( "image has shrunk to nothing" ) ); - return( -1 ); - } - -#ifdef DEBUG - printf( "vips_reduceh_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_reduceh_gen, vips_stop_one, - in, reduceh ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_reduceh_class_init( VipsReducehClass *class ) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS( class ); - VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); - VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); - - VIPS_DEBUG_MSG( "vips_reduceh_class_init\n" ); - - gobject_class->set_property = vips_object_set_property; - gobject_class->get_property = vips_object_get_property; - - vobject_class->nickname = "reduceh"; - vobject_class->description = _( "shrink an image horizontally" ); - vobject_class->build = vips_reduceh_build; - - operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; - - VIPS_ARG_DOUBLE( class, "xshrink", 3, - _( "Xshrink" ), - _( "Horizontal shrink factor" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsReduceh, xshrink ), - 1, 1000000, 1 ); - - VIPS_ARG_INTERPOLATE( class, "interpolate", 4, - _( "Interpolate" ), - _( "Interpolate pixels with this" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsReduceh, interpolate ) ); - -} - -static void -vips_reduceh_init( VipsReduceh *reduceh ) -{ -} - -/** - * vips_reduceh: - * @in: input image - * @out: output image - * @xshrink: horizontal reduce - * @...: %NULL-terminated list of optional named arguments - * - * Optional arguments: - * - * @interpolate: interpolate pixels with this, default cubich - * - * Reduce @in horizontally 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_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... ) -{ - va_list ap; - int result; - - va_start( ap, xshrink ); - result = vips_call_split( "reduceh", ap, in, out, xshrink ); - va_end( ap ); - - return( result ); -} diff --git a/libvips/resample/reduceh.cpp b/libvips/resample/reduceh.cpp new file mode 100644 index 00000000..181d4723 --- /dev/null +++ b/libvips/resample/reduceh.cpp @@ -0,0 +1,461 @@ +/* horizontal reduce by a float factor + * + * 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" + +typedef struct _VipsReduceh { + VipsResample parent_instance; + + double xshrink; /* Reduce factor */ + +} VipsReduceh; + +typedef VipsResampleClass VipsReducehClass; + +/* 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. + */ + +static int vips_reduceh_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; +static double vips_reduceh_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; + +/* We need C linkage for this. + */ +extern "C" { +G_DEFINE_TYPE( VipsReduceh, vips_reduceh, VIPS_TYPE_RESAMPLE ); +} + +template +static void inline +reduceh_unsigned_int_tab( VipsPel *pout, const VipsPel *pin, + const int bands, const int *cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const int c0 = cx[0]; + const int c1 = cx[1]; + const int c2 = cx[2]; + const int c3 = cx[3]; + + for( int z = 0; z < bands; z++ ) { + int cubich = unsigned_fixed_round( + c0 * in[0] + + c1 * in[b1] + + c2 * in[b2] + + c3 * in[b3] ); + + cubich = VIPS_CLIP( 0, cubich, max_value ); + + out[z] = cubich; + + in += 1; + } +} + +template +static void inline +reduceh_signed_int_tab( VipsPel *pout, const VipsPel *pin, + const int bands, const int *cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const int c0 = cx[0]; + const int c1 = cx[1]; + const int c2 = cx[2]; + const int c3 = cx[3]; + + for( int z = 0; z < bands; z++ ) { + int cubich = signed_fixed_round( + c0 * in[0] + + c1 * in[b1] + + c2 * in[b2] + + c3 * in[b3] ); + + cubich = VIPS_CLIP( min_value, cubich, max_value ); + + out[z] = cubich; + + in += 1; + } +} + +/* Floating-point version, for int/float types. + */ +template +static void inline +reduceh_float_tab( VipsPel *pout, const VipsPel *pin, + const int bands, const double *cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const double c0 = cx[0]; + const double c1 = cx[1]; + const double c2 = cx[2]; + const double c3 = cx[3]; + + for( int z = 0; z < bands; z++ ) { + out[z] = + c0 * in[0] + + c1 * in[1] + + c2 * in[2] + + c3 * in[3]; + + in += 1; + } +} + +/* Ultra-high-quality version for double images. + */ +template +static void inline +reduceh_notab( VipsPel *pout, const VipsPel *pin, + const int bands, double x ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + double cx[4]; + + calculate_coefficients_catmull( x, cx ); + + for( int z = 0; z < bands; z++ ) { + const T one = in[0]; + const T two = in[b1]; + const T thr = in[b2]; + const T fou = in[b3]; + + out[z] = cubic_float( one, two, thr, fou, cx ); + + in += 1; + } +} + +static int +vips_reduceh_gen( VipsRegion *out_region, void *seq, + void *a, void *b, gboolean *stop ) +{ + VipsImage *in = (VipsImage *) a; + VipsReduceh *reduceh = (VipsReduceh *) 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_reduceh_gen: generating %d x %d at %d x %d\n", + r->width, r->height, r->left, r->top ); +#endif /*DEBUG*/ + + s.left = r->left * reduceh->xshrink; + s.top = r->top; + s.width = r->width * reduceh->xshrink + 3; + s.height = r->height; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + VIPS_GATE_START( "vips_reduceh_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 * reduceh->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 = vips_reduceh_matrixi[tx]; + const double *cxf = vips_reduceh_matrixf[tx]; + + switch( in->BandFmt ) { + case VIPS_FORMAT_UCHAR: + reduceh_unsigned_int_tab + ( + q, p, bands, cxi ); + break; + + case VIPS_FORMAT_CHAR: + reduceh_signed_int_tab + ( + q, p, bands, cxi ); + break; + + case VIPS_FORMAT_USHORT: + reduceh_unsigned_int_tab + ( + q, p, bands, cxi ); + break; + + case VIPS_FORMAT_SHORT: + reduceh_signed_int_tab + ( + q, p, bands, cxi ); + break; + + case VIPS_FORMAT_UINT: + reduceh_float_tab( + q, p, bands, cxf ); + break; + + case VIPS_FORMAT_INT: + reduceh_float_tab( + q, p, bands, cxf ); + break; + + case VIPS_FORMAT_FLOAT: + case VIPS_FORMAT_COMPLEX: + reduceh_float_tab( q, p, bands, cxf ); + break; + + case VIPS_FORMAT_DOUBLE: + case VIPS_FORMAT_DPCOMPLEX: + reduceh_notab( q, p, bands, X - ix ); + break; + + default: + g_assert_not_reached(); + break; + } + + X += reduceh->xshrink; + q += ps; + } + } + + VIPS_GATE_STOP( "vips_reduceh_gen: work" ); + + return( 0 ); +} + +static int +vips_reduceh_build( VipsObject *object ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object ); + VipsResample *resample = VIPS_RESAMPLE( object ); + VipsReduceh *reduceh = (VipsReduceh *) object; + VipsImage **t = (VipsImage **) + vips_object_local_array( object, 2 ); + + VipsImage *in; + + if( VIPS_OBJECT_CLASS( vips_reduceh_parent_class )->build( object ) ) + return( -1 ); + + in = resample->in; + + if( reduceh->xshrink < 1 ) { + vips_error( object_class->nickname, + "%s", _( "reduce factors should be >= 1" ) ); + return( -1 ); + } + if( reduceh->xshrink > 2 ) + vips_warn( object_class->nickname, + "%s", _( "reduce factor greater than 2" ) ); + + if( reduceh->xshrink == 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], + 1, 0, + in->Xsize + 3, 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 - 3) / reduceh->xshrink; + if( resample->out->Xsize <= 0 ) { + vips_error( object_class->nickname, + "%s", _( "image has shrunk to nothing" ) ); + return( -1 ); + } + +#ifdef DEBUG + printf( "vips_reduceh_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_reduceh_gen, vips_stop_one, + in, reduceh ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_reduceh_class_init( VipsReducehClass *reduceh_class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( reduceh_class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( reduceh_class ); + VipsOperationClass *operation_class = + VIPS_OPERATION_CLASS( reduceh_class ); + + VIPS_DEBUG_MSG( "vips_reduceh_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "reduceh"; + vobject_class->description = _( "shrink an image horizontally" ); + vobject_class->build = vips_reduceh_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL_UNBUFFERED; + + VIPS_ARG_DOUBLE( reduceh_class, "xshrink", 3, + _( "Xshrink" ), + _( "Horizontal shrink factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsReduceh, xshrink ), + 1, 1000000, 1 ); + + /* Build the tables of pre-computed coefficients. + */ + for( int x = 0; x < VIPS_TRANSFORM_SCALE + 1; x++ ) { + calculate_coefficients_catmull( + (float) x / VIPS_TRANSFORM_SCALE, + vips_reduceh_matrixf[x] ); + + for( int i = 0; i < 4; i++ ) + vips_reduceh_matrixi[x][i] = + vips_reduceh_matrixf[x][i] * + VIPS_INTERPOLATE_SCALE; + } + +} + +static void +vips_reduceh_init( VipsReduceh *reduceh ) +{ +} + +/** + * vips_reduceh: + * @in: input image + * @out: output image + * @xshrink: horizontal reduce + * @...: %NULL-terminated list of optional named arguments + * + * Reduce @in horizontally 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_reduceh( VipsImage *in, VipsImage **out, double xshrink, ... ) +{ + va_list ap; + int result; + + va_start( ap, xshrink ); + result = vips_call_split( "reduceh", ap, in, out, xshrink ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/resample/reducev.cpp b/libvips/resample/reducev.cpp index 0fd8be66..fb9883ae 100644 --- a/libvips/resample/reducev.cpp +++ b/libvips/resample/reducev.cpp @@ -1,7 +1,7 @@ -/* horizontal reduce by a float factor +/* vertical reduce by a float factor * - * 30/10/15 - * - from reducev.c + * 29/1/16 + * - from shrinkv.c */ /* @@ -60,6 +60,11 @@ typedef struct _VipsReducev { typedef VipsResampleClass VipsReducevClass; +/* 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. + */ + static int vips_reducev_matrixi[VIPS_TRANSFORM_SCALE + 1][4]; static double vips_reducev_matrixf[VIPS_TRANSFORM_SCALE + 1][4]; @@ -82,13 +87,17 @@ reducev_unsigned_int_tab( VipsPel *pout, const VipsPel *pin, const int l2 = l1 + l1; const int l3 = l1 + l2; - for( int z = 0; z < ne; z++ ) { - const T uno = in[0]; - const T dos = in[l1]; - const T tre = in[l2]; - const T qua = in[l3]; + const int c0 = cy[0]; + const int c1 = cy[1]; + const int c2 = cy[2]; + const int c3 = cy[3]; - int cubicv = cubic_unsigned_int( uno, dos, tre, qua, cy ); + for( int z = 0; z < ne; z++ ) { + int cubicv = unsigned_fixed_round( + c0 * in[0] + + c1 * in[l1] + + c2 * in[l2] + + c3 * in[l3] ); cubicv = VIPS_CLIP( 0, cubicv, max_value ); @@ -111,13 +120,17 @@ reducev_signed_int_tab( VipsPel *pout, const VipsPel *pin, const int l2 = l1 + l1; const int l3 = l1 + l2; - for( int z = 0; z < ne; z++ ) { - const T uno = in[0]; - const T dos = in[l1]; - const T tre = in[l2]; - const T qua = in[l3]; + const int c0 = cy[0]; + const int c1 = cy[1]; + const int c2 = cy[2]; + const int c3 = cy[3]; - int cubicv = cubic_signed_int( uno, dos, tre, qua, cy ); + for( int z = 0; z < ne; z++ ) { + int cubicv = signed_fixed_round( + c0 * in[0] + + c1 * in[l1] + + c2 * in[l2] + + c3 * in[l3] ); cubicv = VIPS_CLIP( min_value, cubicv, max_value ); @@ -142,13 +155,17 @@ reducev_float_tab( VipsPel *pout, const VipsPel *pin, const int l2 = l1 + l1; const int l3 = l1 + l2; - for( int z = 0; z < ne; z++ ) { - const T uno = in[0]; - const T dos = in[l1]; - const T tre = in[l2]; - const T qua = in[l3]; + const double c0 = cy[0]; + const double c1 = cy[1]; + const double c2 = cy[2]; + const double c3 = cy[3]; - out[z] = cubic_float( uno, dos, tre, qua, cy ); + for( int z = 0; z < ne; z++ ) { + out[z] = + c0 * in[0] + + c1 * in[l1] + + c2 * in[l2] + + c3 * in[l3]; in += 1; } @@ -173,13 +190,17 @@ reducev_notab( VipsPel *pout, const VipsPel *pin, calculate_coefficients_catmull( y, cy ); - for( int z = 0; z < ne; z++ ) { - const T uno = in[0]; - const T dos = in[l1]; - const T tre = in[l2]; - const T qua = in[l3]; + const double c0 = cy[0]; + const double c1 = cy[1]; + const double c2 = cy[2]; + const double c3 = cy[3]; - out[z] = cubic_float( uno, dos, tre, qua, cy ); + for( int z = 0; z < ne; z++ ) { + out[z] = + c0 * in[0] + + c1 * in[l1] + + c2 * in[l2] + + c3 * in[l3]; in += 1; } @@ -201,7 +222,6 @@ vips_reducev_gen( VipsRegion *out_region, void *seq, int ne = r->width * bands; VipsRect s; - int y; #ifdef DEBUG printf( "vips_reducev_gen: generating %d x %d at %d x %d\n", @@ -217,7 +237,7 @@ vips_reducev_gen( VipsRegion *out_region, void *seq, VIPS_GATE_START( "vips_reducev_gen: work" ); - for( y = 0; y < r->height; y ++ ) { + 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->yshrink; VipsPel *p = VIPS_REGION_ADDR( ir, r->left, (int) Y ); From 67f3ecfd8d67ed26ada3a7a4bfcd7dd2b5de0bd9 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 29 Jan 2016 15:23:00 +0000 Subject: [PATCH 10/13] wire up to resize / similarity still need tests --- ChangeLog | 4 +++ TODO | 7 ++--- configure.ac | 12 ++++----- libvips/resample/reduce.c | 27 -------------------- libvips/resample/resize.c | 30 ++++++++++++++-------- libvips/resample/similarity.c | 48 +++++++++++++++++++++++++---------- 6 files changed, 67 insertions(+), 61 deletions(-) diff --git a/ChangeLog b/ChangeLog index 79a20cd4..9319fe95 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +29/1/16 started 8.3 +- add vips_reduce*() ... a fast path for bicubic downsize +- vips_resize() and vips_similarity use it when they can + 12/1/16 started 8.2.2 - changes to ease compiling C++ binding with MSVC [Lovell Fuller] - reorder file tests to put slow loaders last diff --git a/TODO b/TODO index 78e39756..a3dd5f54 100644 --- a/TODO +++ b/TODO @@ -45,9 +45,10 @@ more small tweaks $ time vipsthumbnail wtc.tif -o x.tif -s 7000 - real 0m1.843s - user 0m6.040s - sys 0m0.252s + real 0m1.818s + user 0m5.956s + sys 0m0.296s + - get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very diff --git a/configure.ac b/configure.ac index d9f60b69..bfce240a 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # also update the version number in the m4 macros below -AC_INIT([vips], [8.2.2], [vipsip@jiscmail.ac.uk]) +AC_INIT([vips], [8.3.0], [vipsip@jiscmail.ac.uk]) # required for gobject-introspection AC_PREREQ(2.62) @@ -17,8 +17,8 @@ AC_CONFIG_MACRO_DIR([m4]) # user-visible library versioning m4_define([vips_major_version], [8]) -m4_define([vips_minor_version], [2]) -m4_define([vips_micro_version], [2]) +m4_define([vips_minor_version], [3]) +m4_define([vips_micro_version], [0]) m4_define([vips_version], [vips_major_version.vips_minor_version.vips_micro_version]) @@ -37,9 +37,9 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date` # binary interface changes backwards compatible?: increment age # binary interface changes not backwards compatible?: reset age to 0 -LIBRARY_CURRENT=45 -LIBRARY_REVISION=2 -LIBRARY_AGE=3 +LIBRARY_CURRENT=46 +LIBRARY_REVISION=0 +LIBRARY_AGE=4 # patched into include/vips/version.h AC_SUBST(VIPS_VERSION) diff --git a/libvips/resample/reduce.c b/libvips/resample/reduce.c index ebaa02d7..8eebfb7c 100644 --- a/libvips/resample/reduce.c +++ b/libvips/resample/reduce.c @@ -73,38 +73,11 @@ vips_reduce_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_reduce_parent_class )->build( object ) ) return( -1 ); - /* - if( vips_reduceh( resample->in, &t[0], reduce->xshrink, NULL ) || - vips_linecache( t[0], &t[1], - "tile_height", 4, - NULL ) || - vips_reducev( t[1], &t[2], reduce->yshrink, NULL ) || - vips_image_write( t[2], resample->out ) ) - return( -1 ); - */ - - /* - if( vips_reducev( resample->in, &t[0], reduce->yshrink, NULL ) || - vips_linecache( t[0], &t[1], - "tile_height", 4, - NULL ) || - vips_reduceh( t[1], &t[2], reduce->xshrink, NULL ) || - vips_image_write( t[2], resample->out ) ) - return( -1 ); - */ - if( vips_reducev( resample->in, &t[0], reduce->yshrink, NULL ) || vips_reduceh( t[0], &t[1], reduce->xshrink, NULL ) || vips_image_write( t[1], resample->out ) ) return( -1 ); - /* - if( vips_reduceh( resample->in, &t[0], reduce->xshrink, NULL ) || - vips_reducev( t[0], &t[1], reduce->yshrink, NULL ) || - vips_image_write( t[1], resample->out ) ) - return( -1 ); - */ - return( 0 ); } diff --git a/libvips/resample/resize.c b/libvips/resample/resize.c index 36947d30..5c4bd393 100644 --- a/libvips/resample/resize.c +++ b/libvips/resample/resize.c @@ -230,18 +230,26 @@ vips_resize_build( VipsObject *object ) vips_info( class->nickname, "%s interpolation", nickname ); } - /* - if( vips_affine( in, &t[3], hresidual, 0, 0, vresidual, - "interpolate", resize->interpolate, - "idx", resize->idx, - "idy", resize->idy, - NULL ) ) - return( -1 ); + /* We have a special path for bicubic, idx/idy == 0. */ - - if( vips_reduce( in, &t[3], 1.0 / hresidual, 1.0 / vresidual, NULL ) ) - return( -1 ); - + if( resize->interpolate && + strcmp( VIPS_OBJECT_GET_CLASS( resize->interpolate )->nickname, + "bicubic" ) == 0 && + resize->idx == 0.0 && + resize->idy == 0.0 ) { + vips_info( class->nickname, "using fast path for residual" ); + if( vips_reduce( in, &t[3], + 1.0 / hresidual, 1.0 / vresidual, NULL ) ) + return( -1 ); + } + else { + if( vips_affine( in, &t[3], hresidual, 0, 0, vresidual, + "interpolate", resize->interpolate, + "idx", resize->idx, + "idy", resize->idy, + NULL ) ) + return( -1 ); + } in = t[3]; /* If we are upsampling, don't sharpen. Also don't sharpen if we diff --git a/libvips/resample/similarity.c b/libvips/resample/similarity.c index 2d298cc4..5c9ea8f3 100644 --- a/libvips/resample/similarity.c +++ b/libvips/resample/similarity.c @@ -47,6 +47,7 @@ #include #include +#include #include @@ -78,24 +79,43 @@ vips_similarity_build( VipsObject *object ) VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 ); - double a, b, c, d; - if( VIPS_OBJECT_CLASS( vips_similarity_parent_class )->build( object ) ) return( -1 ); - a = similarity->scale * cos( VIPS_RAD( similarity->angle ) ); - b = similarity->scale * -sin( VIPS_RAD( similarity->angle ) ); - c = -b; - d = a; + /* Use vips_reduce(), if we can. + */ + if( similarity->interpolate && + strcmp( VIPS_OBJECT_GET_CLASS( similarity->interpolate )-> + nickname, "bicubic" ) == 0 && + similarity->angle == 0.0 && + similarity->idx == 0.0 && + similarity->idy == 0.0 && + similarity->odx == 0.0 && + similarity->ody == 0.0 ) { + if( vips_reduce( resample->in, &t[0], + 1.0 / similarity->scale, + 1.0 / similarity->scale, NULL ) ) + return( -1 ); + } + else { + 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 ) || - vips_image_write( t[0], resample->out ) ) + 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 ) ) return( -1 ); return( 0 ); From 39e6963033cfc950c2dec80d0fe507961f7321a4 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 29 Jan 2016 17:55:54 +0000 Subject: [PATCH 11/13] start tests --- TODO | 47 +++++++++++-------------------------------- test/test_resample.py | 13 ++++++++++++ 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/TODO b/TODO index a3dd5f54..801dcc0d 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,16 @@ -- write a shrinker that does two 1D shrinks, vertical and horizontal, with - bicubic interpolation +- strange, try - should be much quicker than affine + bicubic + $ vips affine babe.jpg x.v "0.909 0 0 0.909" + $ vipsheader x.v + x.v: 931x698 uchar, 3 bands, srgb, jpegload - vips_resize() can use this, for the default path at least + but 1024 * 0.909 == 930.9, shouldn't we be rounding down? I guess we are + rounding up - try with affine vs. reduce in vips_resize ... on the laptop we see + need to fix this if we want to be able to used reduce as a shortcut for + similarity + +- new vips_reduce: affine $ time vipsthumbnail wtc.tif -o x.tif -s 7000 @@ -14,41 +19,13 @@ sys 0m0.256s reduce - $ time vipsthumbnail wtc.tif -o x.tif -s 7000 - real 0m2.388s - user 0m8.544s - sys 0m0.244s - - argh - - reducev could be faster with the interpolator inlined ... we could pick a - set of masks and just run them along a set of input lines to make an output - line - - reduceh ... not so clear, need to pick new masks every output pixel, might - not make much difference - - with in-line reducev - - $ time vipsthumbnail wtc.tif -o x.tif -s 7000 - real 0m1.897s - user 0m6.296s - sys 0m0.304s - - with in-line reduceh as well - - $ time vipsthumbnail wtc.tif -o x.tif -s 7000 - real 0m1.838s - user 0m6.132s - sys 0m0.264s - - more small tweaks - $ time vipsthumbnail wtc.tif -o x.tif -s 7000 real 0m1.818s user 0m5.956s sys 0m0.296s + add tests for vips_reduce(), update c++ binding, check docs and "see also" + lines - get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very diff --git a/test/test_resample.py b/test/test_resample.py index 1b457ff7..3fbbf92b 100755 --- a/test/test_resample.py +++ b/test/test_resample.py @@ -100,6 +100,19 @@ class TestResample(unittest.TestCase): self.assertEqual((x - im).abs().max(), 0) + def test_reduce(self): + im = Vips.Image.new_from_file("images/IMG_4618.jpg") + bicubic = Vips.Interpolate.new("bicubic") + + for fac in [1, 1.1, 1.5, 1.999]: + print("fac =", fac) + r = im.reduce(fac, fac) + a = im.affine([1.0 / fac, 0, 0, 1.0 / fac], interpolate = bicubic) + r.write_to_file("r.v") + a.write_to_file("a.v") + d = (r - a).abs().max() + self.assertLess(d, 0.1) + def test_resize(self): im = Vips.Image.new_from_file("images/IMG_4618.jpg") im2 = im.resize(0.25) From 8cffd136e900847a2c8e8dce5692860c76c78cc6 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 7 Feb 2016 17:41:39 +0000 Subject: [PATCH 12/13] finish reduce and fix up bicubic a bit, it works better on int32 images now --- ChangeLog | 1 + TODO | 24 +--- cplusplus/include/vips/vips-operators.h | 6 +- cplusplus/vips-operators.cpp | 32 +++++- doc/libvips-docs.xml | 2 +- libvips/resample/bicubic.cpp | 139 +++++++++++++++++++++++- libvips/resample/reduceh.cpp | 85 +++++++++++++-- libvips/resample/reducev.cpp | 86 ++++++++++++++- libvips/resample/resample.c | 22 ++++ libvips/resample/similarity.c | 2 + libvips/resample/templates.h | 102 ++++++++++------- test/test_arithmetic.py | 1 - test/test_resample.py | 29 +++-- 13 files changed, 439 insertions(+), 92 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9319fe95..29c9858e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ 29/1/16 started 8.3 - add vips_reduce*() ... a fast path for bicubic downsize - vips_resize() and vips_similarity use it when they can +- bicubic is better on 32-bit int images 12/1/16 started 8.2.2 - changes to ease compiling C++ binding with MSVC [Lovell Fuller] diff --git a/TODO b/TODO index 801dcc0d..22f191e1 100644 --- a/TODO +++ b/TODO @@ -1,15 +1,3 @@ -- strange, try - - $ vips affine babe.jpg x.v "0.909 0 0 0.909" - $ vipsheader x.v - x.v: 931x698 uchar, 3 bands, srgb, jpegload - - but 1024 * 0.909 == 930.9, shouldn't we be rounding down? I guess we are - rounding up - - need to fix this if we want to be able to used reduce as a shortcut for - similarity - - new vips_reduce: affine @@ -20,17 +8,15 @@ reduce $ time vipsthumbnail wtc.tif -o x.tif -s 7000 - real 0m1.818s - user 0m5.956s - sys 0m0.296s - - add tests for vips_reduce(), update c++ binding, check docs and "see also" - lines - + real 0m1.677s + user 0m5.996s + sys 0m0.300s - get some brightly coloured spots with nohalo / vsqbs on wobble.ws ... very odd, the interpolation shouldn't change between bands + probably over/underflow in interpolation ... clipping problems? + - still not happy about float->int mask conversion in im_vips2mask.c - need to follow up on diff --git a/cplusplus/include/vips/vips-operators.h b/cplusplus/include/vips/vips-operators.h index ec4c9b6c..b9eba85b 100644 --- a/cplusplus/include/vips/vips-operators.h +++ b/cplusplus/include/vips/vips-operators.h @@ -1,5 +1,5 @@ // headers for vips operations -// Sat Jan 9 15:06:40 GMT 2016 +// Sun Feb 7 16:27:14 GMT 2016 // this file is generated automatically, do not edit! static void system( char * cmd_format , VOption *options = 0 ); @@ -146,7 +146,9 @@ VImage mapim( VImage index , VOption *options = 0 ); VImage shrink( double xshrink , double yshrink , VOption *options = 0 ); VImage shrinkh( int xshrink , VOption *options = 0 ); VImage shrinkv( int yshrink , VOption *options = 0 ); -VImage shrink2( double xshrink , double yshrink , VOption *options = 0 ); +VImage reduceh( double xshrink , VOption *options = 0 ); +VImage reducev( double yshrink , VOption *options = 0 ); +VImage reduce( double xshrink , double yshrink , VOption *options = 0 ); VImage quadratic( VImage coeff , VOption *options = 0 ); VImage affine( std::vector matrix , VOption *options = 0 ); VImage similarity( VOption *options = 0 ); diff --git a/cplusplus/vips-operators.cpp b/cplusplus/vips-operators.cpp index e6c0ba57..d3cd6667 100644 --- a/cplusplus/vips-operators.cpp +++ b/cplusplus/vips-operators.cpp @@ -1,5 +1,5 @@ // bodies for vips operations -// Sat Jan 9 15:05:58 GMT 2016 +// Sun Feb 7 16:26:59 GMT 2016 // this file is generated automatically, do not edit! void VImage::system( char * cmd_format , VOption *options ) @@ -1783,11 +1783,37 @@ VImage VImage::shrinkv( int yshrink , VOption *options ) return( out ); } -VImage VImage::shrink2( double xshrink , double yshrink , VOption *options ) +VImage VImage::reduceh( double xshrink , VOption *options ) { VImage out; - call( "shrink2" , + call( "reduceh" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "xshrink", xshrink ) ); + + return( out ); +} + +VImage VImage::reducev( double yshrink , VOption *options ) +{ + VImage out; + + call( "reducev" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "out", &out ) -> + set( "yshrink", yshrink ) ); + + return( out ); +} + +VImage VImage::reduce( double xshrink , double yshrink , VOption *options ) +{ + VImage out; + + call( "reduce" , (options ? options : VImage::option()) -> set( "in", *this ) -> set( "out", &out ) -> diff --git a/doc/libvips-docs.xml b/doc/libvips-docs.xml index fe88a909..ed2039e5 100644 --- a/doc/libvips-docs.xml +++ b/doc/libvips-docs.xml @@ -9,7 +9,7 @@ VIPS Reference Manual - For VIPS 8.2.2. + For VIPS 8.3.0. The latest version of this documentation can be found on the VIPS website. diff --git a/libvips/resample/bicubic.cpp b/libvips/resample/bicubic.cpp index 909caeef..1b688be7 100644 --- a/libvips/resample/bicubic.cpp +++ b/libvips/resample/bicubic.cpp @@ -2,6 +2,8 @@ * * 12/8/10 * - revise window_size / window_offset stuff again + * 7/2/16 + * - double intermediate for 32-bit int types */ /* @@ -231,7 +233,7 @@ bicubic_signed_int_tab( void *pout, const VipsPel *pin, } } -/* Floating-point version, for int/float types. +/* Floating-point version. */ template static void inline @@ -294,6 +296,135 @@ bicubic_float_tab( void *pout, const VipsPel *pin, } } +/* uint32 version needs a double intermediate. + */ + +template +static void inline +bicubic_unsigned_int32_tab( void *pout, const VipsPel *pin, + const int bands, const int lskip, + const double *cx, const double *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + const int l1_plus_b1 = l1 + b1; + const int l1_plus_b2 = l1 + b2; + const int l1_plus_b3 = l1 + b3; + const int l2_plus_b1 = l2 + b1; + const int l2_plus_b2 = l2 + b2; + const int l2_plus_b3 = l2 + b3; + const int l3_plus_b1 = l3 + b1; + const int l3_plus_b2 = l3 + b2; + const int l3_plus_b3 = l3 + b3; + + for( int z = 0; z < bands; z++ ) { + const T uno_one = in[0]; + const T uno_two = in[b1]; + const T uno_thr = in[b2]; + const T uno_fou = in[b3]; + + const T dos_one = in[l1]; + const T dos_two = in[l1_plus_b1]; + const T dos_thr = in[l1_plus_b2]; + const T dos_fou = in[l1_plus_b3]; + + const T tre_one = in[l2]; + const T tre_two = in[l2_plus_b1]; + const T tre_thr = in[l2_plus_b2]; + const T tre_fou = in[l2_plus_b3]; + + const T qua_one = in[l3]; + const T qua_two = in[l3_plus_b1]; + const T qua_thr = in[l3_plus_b2]; + const T qua_fou = in[l3_plus_b3]; + + double bicubic = bicubic_float( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + bicubic = VIPS_CLIP( 0, bicubic, max_value ); + + out[z] = bicubic; + + in += 1; + } +} + +template +static void inline +bicubic_signed_int32_tab( void *pout, const VipsPel *pin, + const int bands, const int lskip, + const double *cx, const double *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + const int l1_plus_b1 = l1 + b1; + const int l1_plus_b2 = l1 + b2; + const int l1_plus_b3 = l1 + b3; + const int l2_plus_b1 = l2 + b1; + const int l2_plus_b2 = l2 + b2; + const int l2_plus_b3 = l2 + b3; + const int l3_plus_b1 = l3 + b1; + const int l3_plus_b2 = l3 + b2; + const int l3_plus_b3 = l3 + b3; + + for( int z = 0; z < bands; z++ ) { + const T uno_one = in[0]; + const T uno_two = in[b1]; + const T uno_thr = in[b2]; + const T uno_fou = in[b3]; + + const T dos_one = in[l1]; + const T dos_two = in[l1_plus_b1]; + const T dos_thr = in[l1_plus_b2]; + const T dos_fou = in[l1_plus_b3]; + + const T tre_one = in[l2]; + const T tre_two = in[l2_plus_b1]; + const T tre_thr = in[l2_plus_b2]; + const T tre_fou = in[l2_plus_b3]; + + const T qua_one = in[l3]; + const T qua_two = in[l3_plus_b1]; + const T qua_thr = in[l3_plus_b2]; + const T qua_fou = in[l3_plus_b3]; + + double bicubic = bicubic_float( + uno_one, uno_two, uno_thr, uno_fou, + dos_one, dos_two, dos_thr, dos_fou, + tre_one, tre_two, tre_thr, tre_fou, + qua_one, qua_two, qua_thr, qua_fou, + cx, cy ); + + bicubic = VIPS_CLIP( min_value, bicubic, max_value ); + + out[z] = bicubic; + + in += 1; + } +} + /* Ultra-high-quality version for double images. */ template @@ -461,12 +592,14 @@ vips_interpolate_bicubic_interpolate( VipsInterpolate *interpolate, break; case VIPS_FORMAT_UINT: - bicubic_float_tab( out, p, bands, lskip, + bicubic_unsigned_int32_tab( + out, p, bands, lskip, cxf, cyf ); break; case VIPS_FORMAT_INT: - bicubic_float_tab( out, p, bands, lskip, + bicubic_signed_int32_tab( + out, p, bands, lskip, cxf, cyf ); break; diff --git a/libvips/resample/reduceh.cpp b/libvips/resample/reduceh.cpp index 181d4723..62f807dc 100644 --- a/libvips/resample/reduceh.cpp +++ b/libvips/resample/reduceh.cpp @@ -160,9 +160,76 @@ reduceh_float_tab( VipsPel *pout, const VipsPel *pin, for( int z = 0; z < bands; z++ ) { out[z] = c0 * in[0] + - c1 * in[1] + - c2 * in[2] + - c3 * in[3]; + c1 * in[b1] + + c2 * in[b2] + + c3 * in[b3]; + + in += 1; + } +} + +/* 32-bit output needs a double intermediate. + */ + +template +static void inline +reduceh_unsigned_int32_tab( VipsPel *pout, const VipsPel *pin, + const int bands, const double *cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const double c0 = cx[0]; + const double c1 = cx[1]; + const double c2 = cx[2]; + const double c3 = cx[3]; + + for( int z = 0; z < bands; z++ ) { + double cubich = + c0 * in[0] + + c1 * in[b1] + + c2 * in[b2] + + c3 * in[b3]; + + cubich = VIPS_CLIP( 0, cubich, max_value ); + + out[z] = cubich; + + in += 1; + } +} + +template +static void inline +reduceh_signed_int32_tab( VipsPel *pout, const VipsPel *pin, + const int bands, const double *cx ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int b1 = bands; + const int b2 = b1 + b1; + const int b3 = b1 + b2; + + const double c0 = cx[0]; + const double c1 = cx[1]; + const double c2 = cx[2]; + const double c3 = cx[3]; + + for( int z = 0; z < bands; z++ ) { + double cubich = + c0 * in[0] + + c1 * in[b1] + + c2 * in[b2] + + c3 * in[b3]; + + cubich = VIPS_CLIP( min_value, cubich, max_value ); + + out[z] = cubich; in += 1; } @@ -222,7 +289,7 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq, s.left = r->left * reduceh->xshrink; s.top = r->top; - s.width = r->width * reduceh->xshrink + 3; + s.width = r->width * reduceh->xshrink + 4; s.height = r->height; if( vips_region_prepare( ir, &s ) ) return( -1 ); @@ -271,12 +338,14 @@ vips_reduceh_gen( VipsRegion *out_region, void *seq, break; case VIPS_FORMAT_UINT: - reduceh_float_tab( + reduceh_unsigned_int32_tab + ( q, p, bands, cxf ); break; case VIPS_FORMAT_INT: - reduceh_float_tab( + reduceh_signed_int32_tab + ( q, p, bands, cxf ); break; @@ -326,9 +395,9 @@ vips_reduceh_build( VipsObject *object ) "%s", _( "reduce factors should be >= 1" ) ); return( -1 ); } - if( reduceh->xshrink > 2 ) + if( reduceh->xshrink > 3 ) vips_warn( object_class->nickname, - "%s", _( "reduce factor greater than 2" ) ); + "%s", _( "reduce factor greater than 3" ) ); if( reduceh->xshrink == 1 ) return( vips_image_write( in, resample->out ) ); diff --git a/libvips/resample/reducev.cpp b/libvips/resample/reducev.cpp index fb9883ae..765bac06 100644 --- a/libvips/resample/reducev.cpp +++ b/libvips/resample/reducev.cpp @@ -140,8 +140,9 @@ reducev_signed_int_tab( VipsPel *pout, const VipsPel *pin, } } -/* Floating-point version, for int/float types. +/* Floating-point version. */ + template static void inline reducev_float_tab( VipsPel *pout, const VipsPel *pin, @@ -171,6 +172,75 @@ reducev_float_tab( VipsPel *pout, const VipsPel *pin, } } +/* 32-bit int version needs a double intermediate. + */ + +template +static void inline +reducev_unsigned_int32_tab( VipsPel *pout, const VipsPel *pin, + const int ne, const int lskip, + const double *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + const double c0 = cy[0]; + const double c1 = cy[1]; + const double c2 = cy[2]; + const double c3 = cy[3]; + + for( int z = 0; z < ne; z++ ) { + double cubicv = + c0 * in[0] + + c1 * in[l1] + + c2 * in[l2] + + c3 * in[l3]; + + cubicv = VIPS_CLIP( 0, cubicv, max_value ); + + out[z] = cubicv; + + in += 1; + } +} + +template +static void inline +reducev_signed_int32_tab( VipsPel *pout, const VipsPel *pin, + const int ne, const int lskip, + const double *cy ) +{ + T* restrict out = (T *) pout; + const T* restrict in = (T *) pin; + + const int l1 = lskip / sizeof( T ); + const int l2 = l1 + l1; + const int l3 = l1 + l2; + + const double c0 = cy[0]; + const double c1 = cy[1]; + const double c2 = cy[2]; + const double c3 = cy[3]; + + for( int z = 0; z < ne; z++ ) { + double cubicv = + c0 * in[0] + + c1 * in[l1] + + c2 * in[l2] + + c3 * in[l3]; + + cubicv = VIPS_CLIP( min_value, cubicv, max_value ); + + out[z] = cubicv; + + in += 1; + } +} + /* Ultra-high-quality version for double images. */ template @@ -231,7 +301,7 @@ vips_reducev_gen( VipsRegion *out_region, void *seq, s.left = r->left; s.top = r->top * reducev->yshrink; s.width = r->width; - s.height = r->height * reducev->yshrink + 3; + s.height = r->height * reducev->yshrink + 4; if( vips_region_prepare( ir, &s ) ) return( -1 ); @@ -274,11 +344,15 @@ vips_reducev_gen( VipsRegion *out_region, void *seq, break; case VIPS_FORMAT_UINT: - reducev_float_tab( q, p, ne, lskip, cyf ); + reducev_unsigned_int32_tab + ( + q, p, ne, lskip, cyf ); break; case VIPS_FORMAT_INT: - reducev_float_tab( q, p, ne, lskip, cyf ); + reducev_signed_int32_tab + ( + q, p, ne, lskip, cyf ); break; case VIPS_FORMAT_FLOAT: @@ -322,9 +396,9 @@ vips_reducev_build( VipsObject *object ) "%s", _( "reduce factors should be >= 1" ) ); return( -1 ); } - if( reducev->yshrink > 2 ) + if( reducev->yshrink > 3 ) vips_warn( object_class->nickname, - "%s", _( "reduce factor greater than 2" ) ); + "%s", _( "reduce factor greater than 3" ) ); if( reducev->yshrink == 1 ) return( vips_image_write( in, resample->out ) ); diff --git a/libvips/resample/resample.c b/libvips/resample/resample.c index 8114e762..18b21564 100644 --- a/libvips/resample/resample.c +++ b/libvips/resample/resample.c @@ -52,6 +52,28 @@ #include "presample.h" +/** + * SECTION: resample + * @short_description: resample images in various ways + * @stability: Stable + * @include: vips/vips.h + * + * There are three types of operation in this section. + * + * First, vips_affine() applies an affine transform to an image. This is any + * sort of 2D transform which preserves straight lines; so any combination of + * stretch, sheer, rotate and translate. You supply an interpolator for it to + * use to generate pixels, see vips_interpolate_new(). It will not produce + * good results for very large shrinks. + * + * Next, vips_resize() specialises in the common task of image reduce and + * enlarge. It strings together combinations of vips_shrink(), vips_reduce(), + * vips_affine() and others to implement a general, high-quality image + * resizer. + * + * Finally, vips_mapim() can apply arbitrary 2D image transforms to an image. + */ + G_DEFINE_ABSTRACT_TYPE( VipsResample, vips_resample, VIPS_TYPE_OPERATION ); static int diff --git a/libvips/resample/similarity.c b/libvips/resample/similarity.c index 5c9ea8f3..6aaa306f 100644 --- a/libvips/resample/similarity.c +++ b/libvips/resample/similarity.c @@ -8,6 +8,8 @@ * rest of vips * 13/8/14 * - oops, missing scale from b, thanks Topochicho + * 7/2/16 + * - use vips_reduce(), if we can */ /* diff --git a/libvips/resample/templates.h b/libvips/resample/templates.h index efd160ba..133925fb 100644 --- a/libvips/resample/templates.h +++ b/libvips/resample/templates.h @@ -154,18 +154,6 @@ unsigned_fixed_round( int v ) return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT ); } -template static int inline -cubic_unsigned_int( - const T one, const T two, const T thr, const T fou, - const int* restrict cx ) -{ - return( unsigned_fixed_round( - cx[0] * one + - cx[1] * two + - cx[2] * thr + - cx[3] * fou ) ); -} - /* Fixed-point integer bicubic, used for 8 and 16-bit types. */ template static int inline @@ -176,16 +164,37 @@ bicubic_unsigned_int( const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, const int* restrict cx, const int* restrict cy ) { - const int r0 = cubic_unsigned_int( - uno_one, uno_two, uno_thr, uno_fou, cx ); - const int r1 = cubic_unsigned_int( - dos_one, dos_two, dos_thr, dos_fou, cx ); - const int r2 = cubic_unsigned_int( - tre_one, tre_two, tre_thr, tre_fou, cx ); - const int r3 = cubic_unsigned_int( - qua_one, qua_two, qua_thr, qua_fou, cx ); + const int c0 = cx[0]; + const int c1 = cx[1]; + const int c2 = cx[2]; + const int c3 = cx[3]; - return( cubic_unsigned_int( r0, r1, r2, r3, cy ) ); + const int r0 = unsigned_fixed_round( + c0 * uno_one + + c1 * uno_two + + c2 * uno_thr + + c3 * uno_fou ); + const int r1 = unsigned_fixed_round( + c0 * dos_one + + c1 * dos_two + + c2 * dos_thr + + c3 * dos_fou ); + const int r2 = unsigned_fixed_round( + c0 * tre_one + + c1 * tre_two + + c2 * tre_thr + + c3 * tre_fou ); + const int r3 = unsigned_fixed_round( + c0 * qua_one + + c1 * qua_two + + c2 * qua_thr + + c3 * qua_fou ); + + return( unsigned_fixed_round( + cy[0] * r0 + + cy[1] * r1 + + cy[2] * r2 + + cy[3] * r3 ) ); } static int inline @@ -197,18 +206,6 @@ signed_fixed_round( int v ) return( (v + round_by) >> VIPS_INTERPOLATE_SHIFT ); } -template static int inline -cubic_signed_int( - const T one, const T two, const T thr, const T fou, - const int* restrict cx ) -{ - return( signed_fixed_round( - cx[0] * one + - cx[1] * two + - cx[2] * thr + - cx[3] * fou ) ); -} - /* Fixed-point integer bicubic, used for 8 and 16-bit types. */ template static int inline @@ -219,16 +216,37 @@ bicubic_signed_int( const T qua_one, const T qua_two, const T qua_thr, const T qua_fou, const int* restrict cx, const int* restrict cy ) { - const int r0 = cubic_signed_int( - uno_one, uno_two, uno_thr, uno_fou, cx ); - const int r1 = cubic_signed_int( - dos_one, dos_two, dos_thr, dos_fou, cx ); - const int r2 = cubic_signed_int( - tre_one, tre_two, tre_thr, tre_fou, cx ); - const int r3 = cubic_signed_int( - qua_one, qua_two, qua_thr, qua_fou, cx ); + const int c0 = cx[0]; + const int c1 = cx[1]; + const int c2 = cx[2]; + const int c3 = cx[3]; - return( cubic_signed_int( r0, r1, r2, r3, cy ) ); + const int r0 = signed_fixed_round( + c0 * uno_one + + c1 * uno_two + + c2 * uno_thr + + c3 * uno_fou ); + const int r1 = signed_fixed_round( + c0 * dos_one + + c1 * dos_two + + c2 * dos_thr + + c3 * dos_fou ); + const int r2 = signed_fixed_round( + c0 * tre_one + + c1 * tre_two + + c2 * tre_thr + + c3 * tre_fou ); + const int r3 = signed_fixed_round( + c0 * qua_one + + c1 * qua_two + + c2 * qua_thr + + c3 * qua_fou ); + + return( signed_fixed_round( + cy[0] * r0 + + cy[1] * r1 + + cy[2] * r2 + + cy[3] * r3 ) ); } template static T inline diff --git a/test/test_arithmetic.py b/test/test_arithmetic.py index 53938105..9eb0379c 100755 --- a/test/test_arithmetic.py +++ b/test/test_arithmetic.py @@ -11,7 +11,6 @@ from gi.repository import Vips Vips.leak_set(True) - unsigned_formats = [Vips.BandFormat.UCHAR, Vips.BandFormat.USHORT, Vips.BandFormat.UINT] diff --git a/test/test_resample.py b/test/test_resample.py index 3fbbf92b..4c9c2c21 100755 --- a/test/test_resample.py +++ b/test/test_resample.py @@ -10,6 +10,20 @@ from gi.repository import Vips Vips.leak_set(True) +unsigned_formats = [Vips.BandFormat.UCHAR, + Vips.BandFormat.USHORT, + Vips.BandFormat.UINT] +signed_formats = [Vips.BandFormat.CHAR, + Vips.BandFormat.SHORT, + Vips.BandFormat.INT] +float_formats = [Vips.BandFormat.FLOAT, + Vips.BandFormat.DOUBLE] +complex_formats = [Vips.BandFormat.COMPLEX, + Vips.BandFormat.DPCOMPLEX] +int_formats = unsigned_formats + signed_formats +noncomplex_formats = int_formats + float_formats +all_formats = int_formats + float_formats + complex_formats + # Run a function expecting a complex image on a two-band image def run_cmplx(fn, image): if image.format == Vips.BandFormat.FLOAT: @@ -105,13 +119,14 @@ class TestResample(unittest.TestCase): bicubic = Vips.Interpolate.new("bicubic") for fac in [1, 1.1, 1.5, 1.999]: - print("fac =", fac) - r = im.reduce(fac, fac) - a = im.affine([1.0 / fac, 0, 0, 1.0 / fac], interpolate = bicubic) - r.write_to_file("r.v") - a.write_to_file("a.v") - d = (r - a).abs().max() - self.assertLess(d, 0.1) + for fmt in all_formats: + x = im.cast(fmt) + r = x.reduce(fac, fac) + a = x.affine([1.0 / fac, 0, 0, 1.0 / fac], + interpolate = bicubic, + oarea = [0, 0, x.width / fac, x.height / fac]) + d = (r - a).abs().max() + self.assertLess(d, 5) def test_resize(self): im = Vips.Image.new_from_file("images/IMG_4618.jpg") From cadca29fd187a0573a4b7419ca5e7a03adeb307c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Bu=CC=88nemann?= Date: Tue, 9 Feb 2016 05:25:29 +0100 Subject: [PATCH 13/13] Better doc pruning / support acdir dirlist * Clean doc dir with find instead of moving files to keep to temp dir * Limited support for automake ACDIR dirlists (no wildcard handling) --- bootstrap.sh | 54 ++++++++++++++++++++++++---------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/bootstrap.sh b/bootstrap.sh index 296bf12e..7495781d 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -13,47 +13,43 @@ rm -f swig/vipsCC/*.cxx rm -f swig/vipsCC/VImage.h rm -f swig/vipsCC/VImage.py python/vipsCC/VError.py python/vipsCC/VMask.py python/vipsCC/Display.py rm -f benchmark/temp* -( mkdir poop ; \ - mv doc/libvips-docs.xml.in poop ; \ - mv doc/Makefile.am poop ; \ - mv doc/images poop ; \ - mv doc/*.xml poop ; \ - mv doc/*.py poop ; \ - rm -rf doc/* ; \ - mv poop/* doc ; \ - rmdir poop \ -) +find doc -depth \( \ + -path doc/libvips-docs.xml.in \ + -o -path doc/Makefile.am \ + -o -path 'doc/images/*' \ + -o -name '*.xml' ! -name libvips-docs.xml ! -path 'doc/xml/*' \ + -o -name '*.py' \ +\) -prune -or \( \ + -type f \ + -o -type d -empty \ +\) -delete -# glib-gettextize asks us to copy these files to m4 if they aren't there -# I don't have $ACDIR/isc-posix.m4, how mysterious ACDIR=`aclocal --print-ac-dir` - -# OS X with brew sets ACDIR to -# /usr/local/Cellar/automake/1.13.1/share/aclocal, the staging area, which is -# totally wrong argh -if [ ! -d $ACDIR ]; then - ACDIR=/usr/local/share/aclocal +# OS X with brew has a dirlist in ACDIR that points to several directories +# dirlist supports wildcards, but that would require eval ... which is evil +if [ -e $ACDIR/dirlist ]; then + ACDIR=`cat $ACDIR/dirlist` fi mkdir -p m4 -cp $ACDIR/codeset.m4 m4 -cp $ACDIR/gettext.m4 m4 -cp $ACDIR/glibc21.m4 m4 -cp $ACDIR/iconv.m4 m4 -cp $ACDIR/lcmessage.m4 m4 -cp $ACDIR/progtest.m4 m4 -cp $ACDIR/introspection.m4 m4 +# glib-gettextize asks us to copy these files to m4 if they aren't there: +files="codeset gettext glibc21 iconv isc-posix lcmessage progtest introspection" +for dir in $ACDIR; do + test -d $dir && for file in $files; do + test -e $dir/$file.m4 && cp $dir/$file.m4 m4 + done +done gtkdocize --copy --docdir doc --flavour no-tmpl || exit 1 # some systems need libtoolize, some glibtoolize ... how annoying echo testing for glibtoolize ... -if glibtoolize --version >/dev/null 2>&1; then +if glibtoolize --version >/dev/null 2>&1; then LIBTOOLIZE=glibtoolize - echo using glibtoolize + echo using glibtoolize else LIBTOOLIZE=libtoolize - echo using libtoolize + echo using libtoolize fi test -r aclocal.m4 || touch aclocal.m4 @@ -62,7 +58,7 @@ test -r aclocal.m4 || touch aclocal.m4 glib-gettextize --force --copy > /dev/null test -r aclocal.m4 && chmod u+w aclocal.m4 # intltoolize --copy --force --automake -aclocal +aclocal autoconf autoheader $LIBTOOLIZE --copy --force --automake