diff --git a/ChangeLog b/ChangeLog index 127c6b51..5c0da2a3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,7 +4,7 @@ it's cheap so caching doesn't help anyway - auto rshift down to 8 bits on save, if necessary - im_gaussnoise(), im_copy_file(), im_grid(), im_scale(), im_scaleps(), - im_wrap(), im_rotquad(), im_zoom() redone as classes + im_wrap(), im_rotquad(), im_zoom(), im_subsample() redone as classes - add --angle option to dzsave 14/5/13 started 7.32.4 diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index ad8ef209..838f8104 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -31,7 +31,7 @@ libconversion_la_SOURCES = \ grid.c \ scale.c \ wrap.c \ - im_subsample.c \ + subsample.c \ im_system.c \ im_system_image.c \ im_text.c \ diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index c2ec23a7..b3cf8128 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -130,6 +130,7 @@ vips_conversion_operation_init( void ) extern GType vips_scale_get_type( void ); extern GType vips_wrap_get_type( void ); extern GType vips_zoom_get_type( void ); + extern GType vips_subsample_get_type( void ); vips_copy_get_type(); vips_tile_cache_get_type(); @@ -157,6 +158,7 @@ vips_conversion_operation_init( void ) vips_scale_get_type(); vips_wrap_get_type(); vips_zoom_get_type(); + vips_subsample_get_type(); } /* The common part of most binary conversion diff --git a/libvips/conversion/im_subsample.c b/libvips/conversion/im_subsample.c deleted file mode 100644 index 247cc1be..00000000 --- a/libvips/conversion/im_subsample.c +++ /dev/null @@ -1,254 +0,0 @@ -/* im_subsample - * - * 3/7/95 JC - * - adapted from im_shrink() - * 3/8/02 JC - * - fall back to im_copy() for x/y factors == 1 - * 21/4/08 - * - don't fall back to pixel-wise shrinks for smalltile, it kills - * performance, just bring IM_MAX_WIDTH down instead - * 1/2/10 - * - gtkdoc - */ - -/* - - 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 - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include - -#include - -/* Maximum width of input we ask for. - */ -#define IM_MAX_WIDTH (100) - -/* Our main parameter struct. - */ -typedef struct { - int xshrink; /* Subsample factors */ - int yshrink; -} SubsampleInfo; - -/* Subsample a REGION. We fetch in IM_MAX_WIDTH pixel-wide strips, left-to-right - * across the input. - */ -static int -line_shrink_gen( REGION *or, void *seq, void *a, void *b ) -{ - REGION *ir = (REGION *) seq; - IMAGE *in = (IMAGE *) a; - SubsampleInfo *st = (SubsampleInfo *) b; - Rect *r = &or->valid; - - int le = r->left; - int ri = IM_RECT_RIGHT( r ); - int to = r->top; - int bo = IM_RECT_BOTTOM(r); - - int ps = IM_IMAGE_SIZEOF_PEL( in ); - - int owidth = IM_MAX_WIDTH / st->xshrink; - - Rect s; - int x, y; - int z, k; - - /* Loop down the region. - */ - for( y = to; y < bo; y++ ) { - VipsPel *q = IM_REGION_ADDR( or, le, y ); - VipsPel *p; - - /* Loop across the region, in owidth sized pieces. - */ - for( x = le; x < ri; x += owidth ) { - /* How many pixels do we make this time? - */ - int ow = IM_MIN( owidth, ri - x ); - - /* Ask for this many from input ... can save a - * little here! - */ - int iw = ow * st->xshrink - (st->xshrink - 1); - - /* Ask for input. - */ - s.left = x * st->xshrink; - s.top = y * st->yshrink; - s.width = iw; - s.height = 1; - if( im_prepare( ir, &s ) ) - return( -1 ); - - /* Append new pels to output. - */ - p = IM_REGION_ADDR( ir, s.left, s.top ); - for( z = 0; z < ow; z++ ) { - for( k = 0; k < ps; k++ ) - q[k] = p[k]; - - q += ps; - p += ps * st->xshrink; - } - } - } - - return( 0 ); -} - -/* Fetch one pixel at a time ... good for very large shrinks. - */ -static int -point_shrink_gen( REGION *or, void *seq, void *a, void *b ) -{ - REGION *ir = (REGION *) seq; - IMAGE *in = (IMAGE *) a; - SubsampleInfo *st = (SubsampleInfo *) b; - Rect *r = &or->valid; - - int le = r->left; - int ri = IM_RECT_RIGHT( r ); - int to = r->top; - int bo = IM_RECT_BOTTOM(r); - - int ps = IM_IMAGE_SIZEOF_PEL( in ); - - Rect s; - int x, y; - int k; - - /* Loop down the region. - */ - for( y = to; y < bo; y++ ) { - VipsPel *q = IM_REGION_ADDR( or, le, y ); - VipsPel *p; - - /* Loop across the region, in owidth sized pieces. - */ - for( x = le; x < ri; x++ ) { - /* Ask for input. - */ - s.left = x * st->xshrink; - s.top = y * st->yshrink; - s.width = 1; - s.height = 1; - if( im_prepare( ir, &s ) ) - return( -1 ); - - /* Append new pels to output. - */ - p = IM_REGION_ADDR( ir, s.left, s.top ); - for( k = 0; k < ps; k++ ) - q[k] = p[k]; - q += ps; - } - } - - return( 0 ); -} - -/** - * im_subsample: - * @in: input image - * @out: output image - * @xshrink: horizontal shrink factor - * @yshrink: vertical shrink factor - * - * Subsample an image by an integer fraction. This is fast nearest-neighbour - * shrink. - * - * See also: im_shrink(), im_affinei(), im_zoom(). - * - * Returns: 0 on success, -1 on error. - */ -int -im_subsample( IMAGE *in, IMAGE *out, int xshrink, int yshrink ) -{ - SubsampleInfo *st; - - /* Check parameters. - */ - if( xshrink < 1 || yshrink < 1 ) { - im_error( "im_subsample", - "%s", _( "factors should both be >= 1" ) ); - return( -1 ); - } - if( xshrink == 1 && yshrink == 1 ) - return( im_copy( in, out ) ); - if( im_piocheck( in, out ) || - im_check_coding_known( "im_subsample", in ) ) - return( -1 ); - - /* Prepare output. Note: we round the output width down! - */ - if( im_cp_desc( out, in ) ) - return( -1 ); - out->Xsize = in->Xsize / xshrink; - out->Ysize = in->Ysize / yshrink; - out->Xres = in->Xres / xshrink; - out->Yres = in->Yres / yshrink; - if( out->Xsize <= 0 || out->Ysize <= 0 ) { - im_error( "im_subsample", - "%s", _( "image has shrunk to nothing" ) ); - return( -1 ); - } - - /* Build and attach state struct. - */ - if( !(st = IM_NEW( out, SubsampleInfo )) ) - return( -1 ); - st->xshrink = xshrink; - st->yshrink = yshrink; - - /* Set demand hints. We want THINSTRIP, as we will be demanding a - * large area of input for each output line. - */ - if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) - return( -1 ); - - /* Generate! If this is a very large shrink, then it's - * probably faster to do it a pixel at a time. - */ - if( xshrink > 10 ) { - if( im_generate( out, - im_start_one, point_shrink_gen, im_stop_one, in, st ) ) - return( -1 ); - } - else { - if( im_generate( out, - im_start_one, line_shrink_gen, im_stop_one, in, st ) ) - return( -1 ); - } - - return( 0 ); -} diff --git a/libvips/conversion/subsample.c b/libvips/conversion/subsample.c new file mode 100644 index 00000000..51cd708c --- /dev/null +++ b/libvips/conversion/subsample.c @@ -0,0 +1,322 @@ +/* subsample + * + * 3/7/95 JC + * - adapted from im_shrink() + * 3/8/02 JC + * - fall back to im_copy() for x/y factors == 1 + * 21/4/08 + * - don't fall back to pixel-wise shrinks for smalltile, it kills + * performance, just bring VIPS_MAX_WIDTH down instead + * 1/2/10 + * - gtkdoc + * 1/6/13 + * - redo as a class + */ + +/* + + 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 + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +#include "conversion.h" + +typedef struct _VipsSubsample { + VipsConversion parent_instance; + + /* The input image. + */ + VipsImage *in; + + int xfac; /* Subsample factors */ + int yfac; + +} VipsSubsample; + +typedef VipsConversionClass VipsSubsampleClass; + +G_DEFINE_TYPE( VipsSubsample, vips_subsample, VIPS_TYPE_CONVERSION ); + +/* Maximum width of input we ask for. + */ +#define VIPS_MAX_WIDTH (100) + +/* Subsample a VipsRegion. We fetch in VIPS_MAX_WIDTH pixel-wide strips, + * left-to-right across the input. + */ +static int +vips_subsample_line_gen( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRegion *ir = (VipsRegion *) seq; + VipsSubsample *subsample = (VipsSubsample *) b; + VipsImage *in = (VipsImage *) a; + VipsRect *r = &or->valid; + int le = r->left; + int ri = VIPS_RECT_RIGHT( r ); + int to = r->top; + int bo = VIPS_RECT_BOTTOM( r ); + int ps = VIPS_IMAGE_SIZEOF_PEL( in ); + int owidth = VIPS_MAX_WIDTH / subsample->xfac; + + VipsRect s; + int x, y; + int z, k; + + /* Loop down the region. + */ + for( y = to; y < bo; y++ ) { + VipsPel *q = VIPS_REGION_ADDR( or, le, y ); + VipsPel *p; + + /* Loop across the region, in owidth sized pieces. + */ + for( x = le; x < ri; x += owidth ) { + /* How many pixels do we make this time? + */ + int ow = VIPS_MIN( owidth, ri - x ); + + /* Ask for this many from input ... can save a + * little here! + */ + int iw = ow * subsample->xfac - (subsample->xfac - 1); + + /* Ask for input. + */ + s.left = x * subsample->xfac; + s.top = y * subsample->yfac; + s.width = iw; + s.height = 1; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + /* Append new pels to output. + */ + p = VIPS_REGION_ADDR( ir, s.left, s.top ); + for( z = 0; z < ow; z++ ) { + for( k = 0; k < ps; k++ ) + q[k] = p[k]; + + q += ps; + p += ps * subsample->xfac; + } + } + } + + return( 0 ); +} + +/* Fetch one pixel at a time ... good for very large shrinks. + */ +static int +vips_subsample_point_gen( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRegion *ir = (VipsRegion *) seq; + VipsSubsample *subsample = (VipsSubsample *) b; + VipsImage *in = (VipsImage *) a; + VipsRect *r = &or->valid; + int le = r->left; + int ri = VIPS_RECT_RIGHT( r ); + int to = r->top; + int bo = VIPS_RECT_BOTTOM(r); + int ps = VIPS_IMAGE_SIZEOF_PEL( in ); + + VipsRect s; + int x, y; + int k; + + /* Loop down the region. + */ + for( y = to; y < bo; y++ ) { + VipsPel *q = VIPS_REGION_ADDR( or, le, y ); + VipsPel *p; + + /* Loop across the region, in owidth sized pieces. + */ + for( x = le; x < ri; x++ ) { + /* Ask for input. + */ + s.left = x * subsample->xfac; + s.top = y * subsample->yfac; + s.width = 1; + s.height = 1; + if( vips_region_prepare( ir, &s ) ) + return( -1 ); + + /* Append new pels to output. + */ + p = VIPS_REGION_ADDR( ir, s.left, s.top ); + for( k = 0; k < ps; k++ ) + q[k] = p[k]; + q += ps; + } + } + + return( 0 ); +} + +static int +vips_subsample_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConversion *conversion = VIPS_CONVERSION( object ); + VipsSubsample *subsample = (VipsSubsample *) object; + + VipsGenerateFn subsample_fn; + + if( VIPS_OBJECT_CLASS( vips_subsample_parent_class )->build( object ) ) + return( -1 ); + + g_assert( subsample->xfac > 0 ); + g_assert( subsample->yfac > 0 ); + + if( subsample->xfac == 1 && + subsample->yfac == 1 ) + return( vips_image_write( subsample->in, conversion->out ) ); + if( vips_image_pio_input( subsample->in ) || + vips_check_coding_known( class->nickname, subsample->in ) ) + return( -1 ); + + /* Prepare output. Note: we round the output width down! + */ + if( vips_image_copy_fields( conversion->out, subsample->in ) ) + return( -1 ); + conversion->out->Xsize = subsample->in->Xsize / subsample->xfac; + conversion->out->Ysize = subsample->in->Ysize / subsample->yfac; + conversion->out->Xres = subsample->in->Xres / subsample->xfac; + conversion->out->Yres = subsample->in->Yres / subsample->yfac; + if( conversion->out->Xsize <= 0 || + conversion->out->Ysize <= 0 ) { + vips_error( class->nickname, + "%s", _( "image has shrunk to nothing" ) ); + return( -1 ); + } + + /* Set demand hints. We want THINSTRIP, as we will be demanding a + * large area of input for each output line. + */ + vips_demand_hint( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, subsample->in, NULL ); + + /* Generate! If this is a very large shrink, then it's + * probably faster to do it a pixel at a time. + */ + if( subsample->xfac > 10 ) + subsample_fn = vips_subsample_point_gen; + else + subsample_fn = vips_subsample_line_gen; + if( vips_image_generate( conversion->out, + vips_start_one, subsample_fn, vips_stop_one, + subsample->in, subsample ) ) + return( -1 ); + + return( 0 ); +} + +/* xy range we sanity check on ... just to stop crazy numbers from divide by 0 + * etc. causing g_assert() failures later. + */ +#define RANGE (100000000) + +static void +vips_subsample_class_init( VipsSubsampleClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "subsample"; + vobject_class->description = _( "subsample an image" ); + vobject_class->build = vips_subsample_build; + + operation_class->flags = VIPS_OPERATION_SEQUENTIAL; + + VIPS_ARG_IMAGE( class, "input", 0, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsSubsample, in ) ); + + VIPS_ARG_INT( class, "xfac", 2, + _( "Xfac" ), + _( "Horizontal subsample factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsSubsample, xfac ), + 1, RANGE, 1 ); + + VIPS_ARG_INT( class, "yfac", 3, + _( "Yfac" ), + _( "Vertical subsample factor" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsSubsample, yfac ), + 1, RANGE, 1 ); + +} + +static void +vips_subsample_init( VipsSubsample *subsample ) +{ +} + +/** + * vips_subsample: + * @in: input image + * @out: output image + * @xfac: horizontal shrink factor + * @yfac: vertical shrink factor + * @...: %NULL-terminated list of optional named arguments + * + * Subsample an image by an integer fraction. This is fast, nearest-neighbour + * shrink. + * + * See also: vips_affine(), vips_shrink(), vips_zoom(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_subsample( VipsImage *in, VipsImage **out, int xfac, int yfac, ... ) +{ + va_list ap; + int result; + + va_start( ap, yfac ); + result = vips_call_split( "subsample", ap, in, out, xfac, yfac ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 04fac133..bf6ed317 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -1581,6 +1581,22 @@ im_zoom( VipsImage *in, VipsImage *out, int xfac, int yfac ) return( 0 ); } +int +im_subsample( VipsImage *in, VipsImage *out, int xfac, int yfac ) +{ + VipsImage *t; + + if( vips_subsample( in, &t, xfac, yfac, NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); + return( -1 ); + } + g_object_unref( t ); + + return( 0 ); +} + static int vips__math( VipsImage *in, VipsImage *out, VipsOperationMath math ) { diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index 92d963b0..61afbc4a 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -196,6 +196,8 @@ int vips_wrap( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_zoom( VipsImage *in, VipsImage **out, int xfac, int yfac, ... ) __attribute__((sentinel)); +int vips_subsample( VipsImage *in, VipsImage **out, int xfac, int yfac, ... ) + __attribute__((sentinel)); int vips_cast( VipsImage *in, VipsImage **out, VipsBandFormat format, ... ) __attribute__((sentinel)); @@ -269,8 +271,6 @@ int im_text( VipsImage *out, const char *text, const char *font, int im_insertset( VipsImage *main, VipsImage *sub, VipsImage *out, int n, int *x, int *y ); -int im_subsample( VipsImage *in, VipsImage *out, int xshrink, int yshrink ); - int im_system( VipsImage *im, const char *cmd, char **out ); VipsImage *im_system_image( VipsImage *im, const char *in_format, const char *out_format, const char *cmd_format, diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 9d6d7707..b6354ef9 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -716,6 +716,7 @@ int im_grid( VipsImage *in, VipsImage *out, int tile_height, int across, int dow int im_scale( VipsImage *in, VipsImage *out ); int im_scaleps( VipsImage *in, VipsImage *out ); int im_zoom( VipsImage *in, VipsImage *out, int xfac, int yfac ); +int im_subsample( VipsImage *in, VipsImage *out, int xshrink, int yshrink ); int im_c2amph( VipsImage *in, VipsImage *out ); int im_c2rect( VipsImage *in, VipsImage *out ); diff --git a/po/POTFILES.in b/po/POTFILES.in index 3631a332..b3c09c75 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -54,14 +54,14 @@ libvips/colour/dECMC.c libvips/colour/scRGB2sRGB.c libvips/conversion/flip.c libvips/conversion/bandmean.c -libvips/conversion/im_zoom.c +libvips/conversion/zoom.c libvips/conversion/im_system.c libvips/conversion/gaussnoise.c libvips/conversion/bandary.c libvips/conversion/cast.c libvips/conversion/conversion.c libvips/conversion/flatten.c -libvips/conversion/im_subsample.c +libvips/conversion/subsample.c libvips/conversion/grid.c libvips/conversion/extract.c libvips/conversion/bandjoin.c