diff --git a/ChangeLog b/ChangeLog index 72160452..19902d8e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +19/10/13 started 7.37.0 +- redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask(), im_dilate(), + im_erode(), im_rank_image(), im_compass(), im_linedet(), im_gradient(), + im_convsep(), im_convsep_f() as classes +- vips_init() now does some ABI compat checking, though this change requires + an ABI break +- add "interlace" option to vips_jpegsave() +- remove vips_image_copy_fields() and vips_demand_hint() and add + vips_image_pipeline() to do both jobs +- vipsthumbnail allows non-square bounding boxes, thanks seth +- add vips_matrixprint() + 18/10/13 started 7.36.3 - fix compiler warnings in ubuntu 13.10 - reverse similarity rotation direction to match the convention used diff --git a/TODO b/TODO index b1a8e71f..0a1fb5d4 100644 --- a/TODO +++ b/TODO @@ -1,20 +1,9 @@ -Next version -============ - -- make vips_init() into a macro, check sizes of important structs against size - at library compile time - - add a new API call like vips__get_sizeof_image() which returns - sizeof(VipsImage) from library compile time, compare that to the application - compile-time value - - vips_init() macro then passes control on to vips__init() for real startup - - breaks binary API sadly - - do conv and morph quickly as simple wrappers over the vips7 operations +- add vips_gaussian_blur() with approx / int / float precision, maybe + vips_resize() as well? + - do much fancier profiling with timing on all locks saved in memory and dumped on exit @@ -23,16 +12,6 @@ Next version - use g_log() instead of vips_info() -- do we always call copy_fields and demand_hint with ALL input images? what - about the operators in conversion? - - could we add something to check that the two calls agree on the image lists? - I think they should - -- support multiscan jpeg write? see - - https://github.com/jcupitt/ruby-vips/issues/41 - - object construction is threadsafe, but class construction is not https://github.com/jcupitt/libvips/issues/64 @@ -157,6 +136,18 @@ convolution wait for vipsobject for this +- we have aconv and aconvsep + + test timing, make sure it;s worth having a separate aconvsep version + + if it is, make im_aconvsep an optimisation: call im_aconvsep_raw() from + vips_conv() if mask width or height == 1 and prec == APPROX + + now we can get rid of im_aconvsep() since it's just vips_convsep() with prec + set to approx + + aconv needs some more work, get it going at least with gaussian + arithmetic ========== diff --git a/configure.ac b/configure.ac index faa981a4..f5c2bdcd 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # also update the version number in the m4 macros below -AC_INIT([vips], [7.36.3], [vipsip@jiscmail.ac.uk]) +AC_INIT([vips], [7.37.0], [vipsip@jiscmail.ac.uk]) # required for gobject-introspection AC_PREREQ(2.62) @@ -16,8 +16,8 @@ AC_CONFIG_MACRO_DIR([m4]) # user-visible library versioning m4_define([vips_major_version], [7]) -m4_define([vips_minor_version], [36]) -m4_define([vips_micro_version], [3]) +m4_define([vips_minor_version], [37]) +m4_define([vips_micro_version], [0]) m4_define([vips_version], [vips_major_version.vips_minor_version.vips_micro_version]) @@ -36,9 +36,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=35 -LIBRARY_REVISION=3 -LIBRARY_AGE=0 +LIBRARY_CURRENT=36 +LIBRARY_REVISION=0 +LIBRARY_AGE=1 # patched into include/vips/version.h AC_SUBST(VIPS_VERSION) diff --git a/libvips/arithmetic/arithmetic.c b/libvips/arithmetic/arithmetic.c index 59fe05aa..c4fb82e5 100644 --- a/libvips/arithmetic/arithmetic.c +++ b/libvips/arithmetic/arithmetic.c @@ -545,11 +545,9 @@ vips_arithmetic_build( VipsObject *object ) */ arithmetic->ready = size; - if( vips_image_copy_fields_array( arithmetic->out, - arithmetic->ready ) ) + if( vips_image_pipeline_array( arithmetic->out, + VIPS_DEMAND_STYLE_THINSTRIP, arithmetic->ready ) ) return( -1 ); - vips_demand_hint_array( arithmetic->out, - VIPS_DEMAND_STYLE_THINSTRIP, arithmetic->ready ); arithmetic->out->Bands = arithmetic->ready[0]->Bands; arithmetic->out->BandFmt = diff --git a/libvips/arithmetic/hist_find.c b/libvips/arithmetic/hist_find.c index 175adcb5..5c98ee26 100644 --- a/libvips/arithmetic/hist_find.c +++ b/libvips/arithmetic/hist_find.c @@ -150,7 +150,8 @@ vips_hist_find_build( VipsObject *object ) /* Make the output image. */ - if( vips_image_copy_fields( hist_find->out, statistic->ready ) ) + if( vips_image_pipelinev( hist_find->out, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) ) return( -1 ); vips_image_init_fields( hist_find->out, hist_find->hist->mx + 1, 1, hist_find->hist->bands, diff --git a/libvips/arithmetic/hist_find_indexed.c b/libvips/arithmetic/hist_find_indexed.c index 7ce56fea..e2632a42 100644 --- a/libvips/arithmetic/hist_find_indexed.c +++ b/libvips/arithmetic/hist_find_indexed.c @@ -163,7 +163,8 @@ vips_hist_find_indexed_build( VipsObject *object ) VIPS_UNREF( indexed->hist->reg ); - if( vips_image_copy_fieldsv( indexed->out, + if( vips_image_pipelinev( indexed->out, + VIPS_DEMAND_STYLE_ANY, statistic->ready, indexed->index_ready, NULL ) ) return( -1 ); vips_image_init_fields( indexed->out, diff --git a/libvips/arithmetic/hist_find_ndim.c b/libvips/arithmetic/hist_find_ndim.c index f170599d..85db4a98 100644 --- a/libvips/arithmetic/hist_find_ndim.c +++ b/libvips/arithmetic/hist_find_ndim.c @@ -154,7 +154,8 @@ vips_hist_find_ndim_build( VipsObject *object ) build( object ) ) return( -1 ); - if( vips_image_copy_fields( ndim->out, statistic->ready ) ) + if( vips_image_pipelinev( ndim->out, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) ) return( -1 ); vips_image_init_fields( ndim->out, ndim->bins, diff --git a/libvips/arithmetic/profile.c b/libvips/arithmetic/profile.c index 11cc7f53..7777b60c 100644 --- a/libvips/arithmetic/profile.c +++ b/libvips/arithmetic/profile.c @@ -136,8 +136,10 @@ vips_profile_build( VipsObject *object ) /* Make the output image. */ - if( vips_image_copy_fields( profile->columns, statistic->ready ) || - vips_image_copy_fields( profile->rows, statistic->ready ) ) + if( vips_image_pipelinev( profile->columns, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) || + vips_image_pipelinev( profile->rows, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) ) return( -1 ); profile->columns->Ysize = 1; profile->columns->BandFmt = VIPS_FORMAT_INT; diff --git a/libvips/arithmetic/project.c b/libvips/arithmetic/project.c index c2159179..6b61686a 100644 --- a/libvips/arithmetic/project.c +++ b/libvips/arithmetic/project.c @@ -141,8 +141,10 @@ vips_project_build( VipsObject *object ) /* Make the output image. */ - if( vips_image_copy_fields( project->columns, statistic->ready ) || - vips_image_copy_fields( project->rows, statistic->ready ) ) + if( vips_image_pipelinev( project->columns, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) || + vips_image_pipelinev( project->rows, + VIPS_DEMAND_STYLE_ANY, statistic->ready, NULL ) ) return( -1 ); project->columns->Ysize = 1; project->columns->BandFmt = diff --git a/libvips/colour/colour.c b/libvips/colour/colour.c index 3d31e2bb..eb70d8e1 100644 --- a/libvips/colour/colour.c +++ b/libvips/colour/colour.c @@ -320,10 +320,9 @@ vips_colour_build( VipsObject *object ) */ g_assert( !colour->in[colour->n] ); - if( vips_image_copy_fields_array( colour->out, colour->in ) ) + if( vips_image_pipeline_array( colour->out, + VIPS_DEMAND_STYLE_THINSTRIP, colour->in ) ) return( -1 ); - vips_demand_hint_array( colour->out, - VIPS_DEMAND_STYLE_THINSTRIP, colour->in ); colour->out->Coding = colour->coding; colour->out->Type = colour->interpretation; colour->out->BandFmt = colour->format; diff --git a/libvips/conversion/Makefile.am b/libvips/conversion/Makefile.am index f0ab2221..8e3cb0af 100644 --- a/libvips/conversion/Makefile.am +++ b/libvips/conversion/Makefile.am @@ -17,12 +17,14 @@ libconversion_la_SOURCES = \ replicate.c \ cast.c \ bandjoin.c \ + bandrank.c \ recomb.c \ bandmean.c \ bandbool.c \ bandary.h \ bandary.c \ rot.c \ + rot45.c \ ifthenelse.c \ falsecolour.c \ msb.c \ diff --git a/libvips/conversion/bandary.c b/libvips/conversion/bandary.c index 5e4e4f0b..99594aa0 100644 --- a/libvips/conversion/bandary.c +++ b/libvips/conversion/bandary.c @@ -147,10 +147,9 @@ vips_bandary_build( VipsObject *object ) return( -1 ); bandary->ready = size; - if( vips_image_copy_fields_array( conversion->out, bandary->ready ) ) + if( vips_image_pipeline_array( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, bandary->ready ) ) return( -1 ); - vips_demand_hint_array( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, bandary->ready ); conversion->out->Bands = bandary->out_bands; diff --git a/libvips/conversion/bandrank.c b/libvips/conversion/bandrank.c new file mode 100644 index 00000000..14d55136 --- /dev/null +++ b/libvips/conversion/bandrank.c @@ -0,0 +1,322 @@ +/* Sort a set of images, pixelwise, and pick out the index at each point. + * + * 19/8/03 + * - from im_maxvalue(), via im_gbandrank() + * 10/11/10 + * - gtkdoc + * - cleanups + * - any mix of formats and bands + * 23/10/13 + * - redo as a class, from bandrank.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 VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "bandary.h" + +typedef struct _VipsBandrank { + VipsBandary parent_instance; + + /* The input images. + */ + VipsArea *in; + int index; /* Pick out this one */ +} VipsBandrank; + +typedef VipsBandaryClass VipsBandrankClass; + +G_DEFINE_TYPE( VipsBandrank, vips_bandrank, VIPS_TYPE_BANDARY ); + +/* Special-case max and min (rather common). + */ +#define FIND_MAX( TYPE ) { \ + for( x = 0; x < sz; x++ ) { \ + TYPE top = ((TYPE *) p[0])[x]; \ + \ + for( i = 1; i < bandary->n; i++ ) { \ + TYPE v = ((TYPE *) p[i])[x]; \ + \ + if( v > top ) \ + top = v; \ + } \ + \ + ((TYPE *) q)[x] = top; \ + } \ +} + +#define FIND_MIN( TYPE ) { \ + for( x = 0; x < sz; x++ ) { \ + TYPE bot = ((TYPE *) p[0])[x]; \ + \ + for( i = 1; i < bandary->n; i++ ) { \ + TYPE v = ((TYPE *) p[i])[x]; \ + \ + if( v < bot ) \ + bot = v; \ + } \ + \ + ((TYPE *) q)[x] = bot; \ + } \ +} + +#define FIND_RANK( TYPE ) { \ + TYPE *sort = (TYPE *) sort_buffer; \ + \ + for( x = 0; x < sz; x++ ) { \ + for( i = 0; i < bandary->n; i++ ) { \ + TYPE v = ((TYPE *) p[i])[x]; \ + \ + /* Search for element >v. + */\ + for( j = 0; j < i; j++ ) \ + if( sort[j] > v ) \ + break; \ + \ + /* Move remaining elements down. + */ \ + for( k = i; k > j; k-- ) \ + sort[k] = sort[k - 1]; \ + \ + /* Insert this element. + */ \ + sort[j] = v; \ + } \ + \ + ((TYPE *) q)[x] = sort[bandrank->index]; \ + } \ +} + +#define SWITCH( OPERATION ) \ + switch( in[0]->BandFmt ) { \ + case VIPS_FORMAT_UCHAR: OPERATION( unsigned char ); break; \ + case VIPS_FORMAT_CHAR: OPERATION( signed char ); break; \ + case VIPS_FORMAT_USHORT: OPERATION( unsigned short ); break; \ + case VIPS_FORMAT_SHORT: OPERATION( signed short ); break; \ + case VIPS_FORMAT_UINT: OPERATION( unsigned int ); break; \ + case VIPS_FORMAT_INT: OPERATION( signed int ); break; \ + case VIPS_FORMAT_FLOAT: OPERATION( float ); break; \ + case VIPS_FORMAT_DOUBLE: OPERATION( double ); break; \ + \ + default: \ + g_assert( 0 ); \ + } + +/* Sort input band elements in the stack. Needs to be big enough for + * sizeof(band-element) * number-of-images. + */ +#define SORT_BUFFER (1024) + +static void +vips_bandrank_buffer( VipsBandary *bandary, VipsPel *q, VipsPel **p, int width ) +{ + VipsBandrank *bandrank = (VipsBandrank *) bandary; + VipsImage **in = bandary->ready; + int sz = width * in[0]->Bands; + + int i, j, k; + int x; + VipsPel sort_buffer[SORT_BUFFER]; + + /* Special-case max and min. + */ + if( bandrank->index == 0 ) + SWITCH( FIND_MIN ) + else if( bandrank->index == bandary->n - 1 ) + SWITCH( FIND_MAX ) + else + SWITCH( FIND_RANK ) +} + +static int +vips_bandrank_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsBandary *bandary = (VipsBandary *) object; + VipsBandrank *bandrank = (VipsBandrank *) object; + + if( bandrank->in ) { + VipsImage **band = (VipsImage **) + vips_object_local_array( object, bandrank->in->n ); + + if( bandrank->in->n == 1 ) + return( vips_bandary_copy( bandary ) ); + + /* We need to keep one band element for every input image + * on the stack. + */ + if( sizeof( double ) * bandrank->in->n > SORT_BUFFER ) { + vips_error( class->nickname, + "%s", _( "too many input images" ) ); + return( -1 ); + } + + if( vips__bandalike_vec( class->nickname, + bandrank->in->data, band, bandrank->in->n, 0 ) ) + return( -1 ); + + bandary->in = band; + bandary->n = bandrank->in->n; + bandary->out_bands = band[0]->Bands; + + if( bandrank->index == -1 ) + bandrank->index = bandary->n / 2; + } + + if( VIPS_OBJECT_CLASS( vips_bandrank_parent_class )->build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_bandrank_class_init( VipsBandrankClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + VipsBandaryClass *bandary_class = VIPS_BANDARY_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_bandrank_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "bandrank"; + vobject_class->description = _( "band-wise rank of a set of images" ); + vobject_class->build = vips_bandrank_build; + + bandary_class->process_line = vips_bandrank_buffer; + + VIPS_ARG_BOXED( class, "in", 0, + _( "Input" ), + _( "Array of input images" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsBandrank, in ), + VIPS_TYPE_ARRAY_IMAGE ); + + VIPS_ARG_INT( class, "index", 0, + _( "Index" ), + _( "Select this band element from sorted list" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsBandrank, index ), + -1, 1000000, -1 ); + +} + +static void +vips_bandrank_init( VipsBandrank *bandrank ) +{ + /* -1 means median. + */ + bandrank->index = -1; +} + +static int +vips_bandrankv( VipsImage **in, VipsImage **out, int n, va_list ap ) +{ + VipsArea *area; + VipsImage **array; + int i; + int result; + + area = vips_area_new_array_object( n ); + array = (VipsImage **) area->data; + for( i = 0; i < n; i++ ) { + array[i] = in[i]; + g_object_ref( array[i] ); + } + + result = vips_call_split( "bandrank", ap, area, out ); + + vips_area_unref( area ); + + return( result ); +} + +/** + * vips_bandrank: + * @in: array of input images + * @out: output image + * @n: number of input images + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @index: pick this index from list of sorted values + * + * Sorts the images @in band-element-wise, then outputs an + * image in which each band element is selected from the sorted list by the + * @index parameter. For example, if @index + * is zero, then each output band element will be the minimum of all the + * corresponding input band element. + * + * By default, @index is -1, meaning pick the median value. + * + * It works for any uncoded, non-complex image type. Images are cast up to the + * smallest common-format. + * + * Any image can have either 1 band or n bands, where n is the same for all + * the non-1-band images. Single band images are then effectively copied to + * make n-band images. + * + * Smaller input images are expanded by adding black pixels. + * + * See also: vips_rank(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_bandrank( VipsImage **in, VipsImage **out, int n, ... ) +{ + va_list ap; + int result; + + va_start( ap, n ); + result = vips_bandrankv( in, out, n, ap ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/conversion/cast.c b/libvips/conversion/cast.c index 38df919a..be048d0c 100644 --- a/libvips/conversion/cast.c +++ b/libvips/conversion/cast.c @@ -441,10 +441,9 @@ vips_cast_build( VipsObject *object ) vips_image_pio_input( cast->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, cast->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, cast->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, cast->in, NULL ); conversion->out->BandFmt = cast->format; diff --git a/libvips/conversion/conversion.c b/libvips/conversion/conversion.c index 4b00a8eb..56582973 100644 --- a/libvips/conversion/conversion.c +++ b/libvips/conversion/conversion.c @@ -210,8 +210,10 @@ vips_conversion_operation_init( void ) extern GType vips_replicate_get_type( void ); extern GType vips_cast_get_type( void ); extern GType vips_bandjoin_get_type( void ); + extern GType vips_bandrank_get_type( void ); extern GType vips_black_get_type( void ); extern GType vips_rot_get_type( void ); + extern GType vips_rot45_get_type( void ); extern GType vips_ifthenelse_get_type( void ); extern GType vips_recomb_get_type( void ); extern GType vips_bandmean_get_type( void ); @@ -245,8 +247,10 @@ vips_conversion_operation_init( void ) vips_replicate_get_type(); vips_cast_get_type(); vips_bandjoin_get_type(); + vips_bandrank_get_type(); vips_black_get_type(); vips_rot_get_type(); + vips_rot45_get_type(); vips_ifthenelse_get_type(); vips_recomb_get_type(); vips_bandmean_get_type(); diff --git a/libvips/conversion/copy.c b/libvips/conversion/copy.c index 2b3981a9..e2753a2b 100644 --- a/libvips/conversion/copy.c +++ b/libvips/conversion/copy.c @@ -250,10 +250,9 @@ vips_copy_build( VipsObject *object ) if( vips_image_pio_input( copy->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, copy->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, copy->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, copy->in, NULL ); /* Use props to adjust header fields. */ diff --git a/libvips/conversion/embed.c b/libvips/conversion/embed.c index 8f33e248..46a2d557 100644 --- a/libvips/conversion/embed.c +++ b/libvips/conversion/embed.c @@ -426,14 +426,12 @@ vips_embed_build( VipsObject *object ) case VIPS_EXTEND_WHITE: case VIPS_EXTEND_BACKGROUND: case VIPS_EXTEND_COPY: - if( vips_image_copy_fields( conversion->out, embed->in ) ) - return( -1 ); - /* embed is used in many places. We don't really care about * geometry, so use ANY to avoid disturbing all pipelines. */ - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_ANY, embed->in, NULL ); + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_ANY, embed->in, NULL ) ) + return( -1 ); conversion->out->Xsize = embed->width; conversion->out->Ysize = embed->height; diff --git a/libvips/conversion/extract.c b/libvips/conversion/extract.c index 5f3a00bf..8f674170 100644 --- a/libvips/conversion/extract.c +++ b/libvips/conversion/extract.c @@ -155,10 +155,9 @@ vips_extract_area_build( VipsObject *object ) vips_check_coding_known( class->nickname, extract->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, extract->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, extract->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, extract->in, NULL ); conversion->out->Xsize = extract->width; conversion->out->Ysize = extract->height; diff --git a/libvips/conversion/flatten.c b/libvips/conversion/flatten.c index 352e3734..889844a9 100644 --- a/libvips/conversion/flatten.c +++ b/libvips/conversion/flatten.c @@ -316,10 +316,9 @@ vips_flatten_build( VipsObject *object ) vips_image_pio_input( flatten->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, flatten->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, flatten->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, flatten->in, NULL ); conversion->out->Bands -= 1; diff --git a/libvips/conversion/flip.c b/libvips/conversion/flip.c index 824e1f8f..ed70a3c6 100644 --- a/libvips/conversion/flip.c +++ b/libvips/conversion/flip.c @@ -198,10 +198,9 @@ vips_flip_build( VipsObject *object ) if( vips_image_pio_input( flip->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, flip->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, flip->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, flip->in, NULL ); if( flip->direction == VIPS_DIRECTION_HORIZONTAL ) { generate_fn = vips_flip_horizontal_gen; diff --git a/libvips/conversion/grid.c b/libvips/conversion/grid.c index de5d45ef..be39dfd5 100644 --- a/libvips/conversion/grid.c +++ b/libvips/conversion/grid.c @@ -166,12 +166,11 @@ vips_grid_build( VipsObject *object ) return( -1 ); } - if( vips_image_copy_fields( conversion->out, grid->in ) ) - return( -1 ); /* We can render small tiles with pointer copies. */ - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_SMALLTILE, grid->in, NULL ); + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_SMALLTILE, grid->in, NULL ) ) + return( -1 ); conversion->out->Xsize = grid->in->Xsize * grid->across; conversion->out->Ysize = grid->tile_height * grid->down; diff --git a/libvips/conversion/ifthenelse.c b/libvips/conversion/ifthenelse.c index 450cfd3a..7c088e77 100644 --- a/libvips/conversion/ifthenelse.c +++ b/libvips/conversion/ifthenelse.c @@ -442,10 +442,9 @@ vips_ifthenelse_build( VipsObject *object ) if( vips__formatalike_vec( size, format, 2 ) ) return( -1 ); - if( vips_image_copy_fields_array( conversion->out, format ) ) + if( vips_image_pipeline_array( conversion->out, + VIPS_DEMAND_STYLE_SMALLTILE, format ) ) return( -1 ); - vips_demand_hint_array( conversion->out, - VIPS_DEMAND_STYLE_SMALLTILE, format ); if( vips_image_generate( conversion->out, vips_start_many, generate_fn, vips_stop_many, diff --git a/libvips/conversion/insert.c b/libvips/conversion/insert.c index 4b1fbbf9..f0c3a15a 100644 --- a/libvips/conversion/insert.c +++ b/libvips/conversion/insert.c @@ -284,10 +284,9 @@ vips_insert_build( VipsObject *object ) insert->main_processed, insert->sub_processed, NULL )) ) return( -1 ); - if( vips_image_copy_fields_array( conversion->out, arry ) ) + if( vips_image_pipeline_array( conversion->out, + VIPS_DEMAND_STYLE_ANY, arry ) ) return( -1 ); - vips_demand_hint_array( conversion->out, - VIPS_DEMAND_STYLE_ANY, arry ); /* Calculate geometry. */ diff --git a/libvips/conversion/msb.c b/libvips/conversion/msb.c index cefb366b..9a526e6f 100644 --- a/libvips/conversion/msb.c +++ b/libvips/conversion/msb.c @@ -201,10 +201,9 @@ vips_msb_build( VipsObject *object ) msb->in->BandFmt == VIPS_FORMAT_UCHAR ) return( vips_image_write( msb->in, conversion->out ) ); - if( vips_image_copy_fields( conversion->out, msb->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, msb->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, msb->in, NULL ); if( msb->band != -1 ) conversion->out->Bands = 1; diff --git a/libvips/conversion/recomb.c b/libvips/conversion/recomb.c index 5339b080..38bc2867 100644 --- a/libvips/conversion/recomb.c +++ b/libvips/conversion/recomb.c @@ -164,10 +164,9 @@ vips_recomb_build( VipsObject *object ) return( -1 ); recomb->coeff = t[0]; - if( vips_image_copy_fields( conversion->out, recomb->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, recomb->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, recomb->in, NULL ); conversion->out->Bands = recomb->m->Ysize; if( vips_bandfmt_isint( recomb->in->BandFmt ) ) diff --git a/libvips/conversion/replicate.c b/libvips/conversion/replicate.c index 8d0ec6ff..ee8e9d27 100644 --- a/libvips/conversion/replicate.c +++ b/libvips/conversion/replicate.c @@ -162,10 +162,9 @@ vips_replicate_build( VipsObject *object ) if( vips_image_pio_input( replicate->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, replicate->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_SMALLTILE, replicate->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_SMALLTILE, replicate->in, NULL ); conversion->out->Xsize *= replicate->across; conversion->out->Ysize *= replicate->down; diff --git a/libvips/conversion/rot.c b/libvips/conversion/rot.c index 2288e563..8c9c9e73 100644 --- a/libvips/conversion/rot.c +++ b/libvips/conversion/rot.c @@ -1,4 +1,4 @@ -/* im_rot90 +/* rotate by 0/90/180/270 degrees * * Copyright: 1991, N. Dessipris * Written on: 28/10/91 @@ -294,13 +294,16 @@ vips_rot_build( VipsObject *object ) if( vips_image_pio_input( rot->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, rot->in ) ) + hint = rot->angle == VIPS_ANGLE_180 ? + VIPS_DEMAND_STYLE_THINSTRIP : + VIPS_DEMAND_STYLE_SMALLTILE; + + if( vips_image_pipelinev( conversion->out, hint, rot->in, NULL ) ) return( -1 ); switch( rot->angle ) { case VIPS_ANGLE_90: generate_fn = vips_rot90_gen; - hint = VIPS_DEMAND_STYLE_SMALLTILE; conversion->out->Xsize = rot->in->Ysize; conversion->out->Ysize = rot->in->Xsize; conversion->out->Xoffset = rot->in->Ysize; @@ -309,14 +312,12 @@ vips_rot_build( VipsObject *object ) case VIPS_ANGLE_180: generate_fn = vips_rot180_gen; - hint = VIPS_DEMAND_STYLE_THINSTRIP; conversion->out->Xoffset = rot->in->Xsize; conversion->out->Yoffset = rot->in->Ysize; break; case VIPS_ANGLE_270: generate_fn = vips_rot270_gen; - hint = VIPS_DEMAND_STYLE_SMALLTILE; conversion->out->Xsize = rot->in->Ysize; conversion->out->Ysize = rot->in->Xsize; conversion->out->Xoffset = 0; @@ -331,8 +332,6 @@ vips_rot_build( VipsObject *object ) return( 0 ); } - vips_demand_hint( conversion->out, hint, rot->in, NULL ); - if( vips_image_generate( conversion->out, vips_start_one, generate_fn, vips_stop_one, rot->in, rot ) ) diff --git a/libvips/conversion/rot45.c b/libvips/conversion/rot45.c new file mode 100644 index 00000000..b6cfe2c4 --- /dev/null +++ b/libvips/conversion/rot45.c @@ -0,0 +1,319 @@ +/* 'lossless' 45 degree rotate ... odd-sized square images only + * + * Author: N. Dessipris (Copyright, N. Dessipris 1991) + * Written on: 08/05/1991 + * Modified on: 28/05/1991 + * 12/10/95 JC + * - small revisions, needs rewriting really + * 7/8/96 JC + * - absolutely foul desp code revised + * - many bugs and mem leaks fixed + * 1/3/99 JC + * - oops, fns were not preserving scale and offset + * 1/12/10 + * - allow any size mask for the 90 degree rot45ates by using im_rot4590(). + * 12/10/13 + * - rewritten 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 + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "pconversion.h" + +typedef struct _VipsRot45 { + VipsConversion parent_instance; + + /* The input image. + */ + VipsImage *in; + + /* Rotate by ... + */ + VipsAngle45 angle; + +} VipsRot45; + +typedef VipsConversionClass VipsRot45Class; + +G_DEFINE_TYPE( VipsRot45, vips_rot45, VIPS_TYPE_CONVERSION ); + +#define COPY( Q, P ) { \ + VipsPel *q = (Q); \ + VipsPel *p = (P); \ + int b;\ + \ + for( b = 0; b < ps; b++ )\ + q[b] = p[b];\ +} + +#define ASSIGN( Xout, Yout, Xin, Yin ) \ + COPY( VIPS_IMAGE_ADDR( out, Xout, Yout ), \ + VIPS_IMAGE_ADDR( in, Xin, Yin ) ) + +#define POINT_TO_TEMP( q, Xin, Yin ) \ + COPY( q, VIPS_IMAGE_ADDR( in, Xin, Yin ) ) + +#define TEMP_TO_POINT( Xout, Yout, p ) \ + COPY( VIPS_IMAGE_ADDR( out, Xout, Yout ), p ) + +/* This can work inplace, ie. in == out is allowed. + */ +static void +vips_rot45_rot45( VipsImage *out, VipsImage *in ) +{ + size_t ps = VIPS_IMAGE_SIZEOF_PEL( in ); + VipsPel *temp = VIPS_ARRAY( in, ps, VipsPel ); + int size = in->Xsize; + int size_2 = size / 2; + + int x, y; + + g_assert( in->Xsize == in->Ysize ); + g_assert( out->Xsize == out->Ysize ); + g_assert( in->Xsize == out->Xsize ); + g_assert( in->Xsize % 2 == 1 ); + + /* Split the square into 8 triangles. Loop over the top-left one, + * reflect to index the others. + * + * 1 1 2 2 3 + * 8 1 2 3 3 + * 8 8 x 4 4 + * 7 7 6 5 4 + * 7 6 6 5 5 + * + * do the centre separately. + */ + + for( y = 0; y < size_2; y++ ) + for( x = y; x < size_2; x++ ) { + /* Save 1, it goes into 2 at the end. + */ + POINT_TO_TEMP( temp, x, y ); + + /* Fill 1 from 8. + */ + ASSIGN( x, y, + y, size_2 - (x - y) ); + + /* 8 from 7. + */ + ASSIGN( y, size_2 - (x - y), + y, (size - 1) - x ); + + /* 7 from 6. + */ + ASSIGN( y, (size - 1) - x, + size_2 - (x - y), (size - 1) - y ); + + /* 6 from 5. + */ + ASSIGN( size_2 - (x - y), (size - 1) - y, + (size - 1) - x, (size - 1) - y ); + + /* 5 from 4. + */ + ASSIGN( (size - 1) - x, (size - 1) - y, + (size - 1) - y, (x - y) + size_2 ); + + /* 4 from 3. + */ + ASSIGN( (size - 1) - y, (x - y) + size_2, + (size - 1) - y, x ); + + /* 3 from 2. + */ + ASSIGN( (size - 1) - y, x, + (x - y) + size_2, y ); + + /* 2 from saved 1. + */ + TEMP_TO_POINT( (x - y) + size_2, y, temp ); + } + + /* Centre. + */ + ASSIGN( size_2, size_2, size_2, size_2 ); +} + +static int +vips_rot45_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConversion *conversion = VIPS_CONVERSION( object ); + VipsRot45 *rot45 = (VipsRot45 *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 1 ); + + VipsImage *from; + + if( VIPS_OBJECT_CLASS( vips_rot45_parent_class )->build( object ) ) + return( -1 ); + + if( vips_check_oddsquare( class->nickname, rot45->in ) ) + return( -1 ); + + if( rot45->angle == VIPS_ANGLE45_0 ) + return( vips_image_write( rot45->in, conversion->out ) ); + + if( vips_image_wio_input( rot45->in ) ) + return( -1 ); + + t[0] = vips_image_new_buffer(); + if( vips_image_pipelinev( t[0], + VIPS_DEMAND_STYLE_ANY, rot45->in, NULL ) ) + return( -1 ); + if( vips_image_write_prepare( t[0] ) ) + return( -1 ); + + from = rot45->in; + + switch( rot45->angle ) { + case VIPS_ANGLE45_315: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_270: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_225: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_180: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_135: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_90: + vips_rot45_rot45( t[0], from ); + from = t[0]; + + case VIPS_ANGLE45_45: + vips_rot45_rot45( t[0], from ); + from = t[0]; + break; + + default: + g_assert( 0 ); + + /* Keep -Wall happy. + */ + return( 0 ); + } + + if( vips_image_write( t[0], conversion->out ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_rot45_class_init( VipsRot45Class *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + VIPS_DEBUG_MSG( "vips_rot45_class_init\n" ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "rot45"; + vobject_class->description = _( "rotate an image" ); + vobject_class->build = vips_rot45_build; + + VIPS_ARG_IMAGE( class, "in", 1, + _( "Input" ), + _( "Input image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsRot45, in ) ); + + VIPS_ARG_ENUM( class, "angle", 6, + _( "Angle" ), + _( "Angle to rotate image" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsRot45, angle ), + VIPS_TYPE_ANGLE45, VIPS_ANGLE45_45 ); +} + +static void +vips_rot45_init( VipsRot45 *rot45 ) +{ + rot45->angle = VIPS_ANGLE45_45; +} + +/** + * vips_rot45: + * @in: input image + * @out: output image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @angle: rotation angle + * + * Rotate @in by a multiple of 45 degrees. Odd-length sides and square images + * only. + * + * See also: vips_rot(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_rot45( VipsImage *in, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "rot45", ap, in, out ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/conversion/sequential.c b/libvips/conversion/sequential.c index d5b473f5..f64c031e 100644 --- a/libvips/conversion/sequential.c +++ b/libvips/conversion/sequential.c @@ -273,10 +273,9 @@ vips_sequential_build( VipsObject *object ) vips_object_local( object, t ); - if( vips_image_copy_fields( conversion->out, t ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, t, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, t, NULL ); if( vips_image_generate( conversion->out, vips_start_one, vips_sequential_generate, vips_stop_one, t, sequential ) ) diff --git a/libvips/conversion/subsample.c b/libvips/conversion/subsample.c index 45f58643..d4ace719 100644 --- a/libvips/conversion/subsample.c +++ b/libvips/conversion/subsample.c @@ -209,10 +209,14 @@ vips_subsample_build( VipsObject *object ) vips_check_coding_known( class->nickname, subsample->in ) ) return( -1 ); + /* Set demand hints. We want THINSTRIP, as we will be demanding a + * large area of input for each output line. + */ + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, subsample->in, NULL ) ) + 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; @@ -224,12 +228,6 @@ vips_subsample_build( VipsObject *object ) 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. */ diff --git a/libvips/conversion/tilecache.c b/libvips/conversion/tilecache.c index 52c63c2b..058d94dc 100644 --- a/libvips/conversion/tilecache.c +++ b/libvips/conversion/tilecache.c @@ -740,10 +740,9 @@ vips_tile_cache_build( VipsObject *object ) if( vips_image_pio_input( block_cache->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, block_cache->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_SMALLTILE, block_cache->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_SMALLTILE, block_cache->in, NULL ); if( vips_image_generate( conversion->out, vips_start_one, vips_tile_cache_gen, vips_stop_one, @@ -926,10 +925,9 @@ vips_line_cache_build( VipsObject *object ) if( vips_image_pio_input( block_cache->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, block_cache->in ) ) + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_THINSTRIP, block_cache->in, NULL ) ) return( -1 ); - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_THINSTRIP, block_cache->in, NULL ); if( vips_image_generate( conversion->out, vips_start_one, vips_line_cache_gen, vips_stop_one, diff --git a/libvips/conversion/zoom.c b/libvips/conversion/zoom.c index 0e7553f5..c41d4e05 100644 --- a/libvips/conversion/zoom.c +++ b/libvips/conversion/zoom.c @@ -349,13 +349,12 @@ vips_zoom_build( VipsObject *object ) vips_check_coding_known( class->nickname, zoom->in ) ) return( -1 ); - if( vips_image_copy_fields( conversion->out, zoom->in ) ) - return( -1 ); /* Set demand hints. THINSTRIP will prevent us from using * vips_zoom_paint_whole() much ... so go for FATSTRIP. */ - vips_demand_hint( conversion->out, - VIPS_DEMAND_STYLE_FATSTRIP, zoom->in, NULL ); + if( vips_image_pipelinev( conversion->out, + VIPS_DEMAND_STYLE_FATSTRIP, zoom->in, NULL ) ) + return( -1 ); conversion->out->Xsize = zoom->in->Xsize * zoom->xfac; conversion->out->Ysize = zoom->in->Ysize * zoom->yfac; diff --git a/libvips/convolution/Makefile.am b/libvips/convolution/Makefile.am index 30f185c5..a156a44f 100644 --- a/libvips/convolution/Makefile.am +++ b/libvips/convolution/Makefile.am @@ -4,9 +4,10 @@ libconvolution_la_SOURCES = \ convolution.c \ pconvolution.h \ conv.c \ + convsep.c \ + compass.c \ + morph.c \ convol_dispatch.c \ - im_addgnoise.c \ - im_compass.c \ im_aconv.c \ im_aconvsep.c \ im_conv.c \ diff --git a/libvips/convolution/compass.c b/libvips/convolution/compass.c new file mode 100644 index 00000000..5ff9c522 --- /dev/null +++ b/libvips/convolution/compass.c @@ -0,0 +1,214 @@ +/* repeatedly convolve with a rotating mask + * + * 23/10/13 + * - from vips_conv() + */ + +/* + + 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 "pconvolution.h" + +typedef struct { + VipsConvolution parent_instance; + + int times; + VipsAngle45 angle; + VipsCombine combine; + VipsPrecision precision; + int layers; + int cluster; +} VipsCompass; + +typedef VipsConvolutionClass VipsCompassClass; + +G_DEFINE_TYPE( VipsCompass, vips_compass, VIPS_TYPE_CONVOLUTION ); + +static int +vips_compass_build( VipsObject *object ) +{ + VipsConvolution *convolution = (VipsConvolution *) object; + VipsCompass *compass = (VipsCompass *) object; + VipsImage **masks; + VipsImage *mask; + VipsImage **images; + int i; + VipsImage **abs; + VipsImage **combine; + VipsImage *x; + + g_object_set( compass, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_compass_parent_class )->build( object ) ) + return( -1 ); + + masks = (VipsImage **) + vips_object_local_array( object, compass->times ); + images = (VipsImage **) + vips_object_local_array( object, compass->times ); + abs = (VipsImage **) + vips_object_local_array( object, compass->times ); + combine = (VipsImage **) + vips_object_local_array( object, compass->times ); + + mask = convolution->M; + for( i = 0; i < compass->times; i++ ) { + if( vips_conv( convolution->in, &images[i], mask, + "precision", compass->precision, + "layers", compass->layers, + "cluster", compass->cluster, + NULL ) ) + return( -1 ); + if( vips_rot45( mask, &masks[i], + "angle", compass->angle, + NULL ) ) + return( -1 ); + + mask = masks[i]; + } + + for( i = 0; i < compass->times; i++ ) + if( vips_abs( images[i], &abs[i], NULL ) ) + return( -1 ); + + switch( compass->combine ) { + case VIPS_COMBINE_MAX: + if( vips_bandrank( abs, &combine[0], compass->times, + "index", compass->times - 1, + NULL ) ) + return( -1 ); + x = combine[0]; + break; + + case VIPS_COMBINE_SUM: + x = abs[0]; + for( i = 1; i < compass->times; i++ ) { + if( vips_add( x, abs[i], &combine[i], NULL ) ) + return( -1 ); + x = combine[i]; + } + break; + + default: + g_assert( 0 ); + } + + if( vips_image_write( x, convolution->out ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_compass_class_init( VipsCompassClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "compass"; + object_class->description = _( "convolution operation" ); + object_class->build = vips_compass_build; + + VIPS_ARG_INT( class, "times", 101, + _( "Times" ), + _( "Rotate and convolve this many times" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, times ), + 1, 1000, 2 ); + + VIPS_ARG_ENUM( class, "angle", 103, + _( "Angle" ), + _( "Rotate mask by this much between convolutions" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, angle ), + VIPS_TYPE_ANGLE45, VIPS_ANGLE45_90 ); + + VIPS_ARG_ENUM( class, "combine", 104, + _( "Combine" ), + _( "Combine convolution results like this" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, combine ), + VIPS_TYPE_COMBINE, VIPS_COMBINE_MAX ); + + VIPS_ARG_ENUM( class, "precision", 203, + _( "Precision" ), + _( "Convolve with this precision" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, precision ), + VIPS_TYPE_PRECISION, VIPS_PRECISION_INTEGER ); + + VIPS_ARG_INT( class, "layers", 204, + _( "Layers" ), + _( "Use this many layers in approximation" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, layers ), + 1, 1000, 5 ); + + VIPS_ARG_INT( class, "cluster", 205, + _( "Cluster" ), + _( "Cluster lines closer than this in approximation" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsCompass, cluster ), + 1, 100, 1 ); + +} + +static void +vips_compass_init( VipsCompass *compass ) +{ + compass->times = 2; + compass->angle = VIPS_ANGLE45_90; + compass->combine = VIPS_COMBINE_MAX; + compass->precision = VIPS_PRECISION_INTEGER; + compass->layers = 5; + compass->cluster = 1; +} + +int +vips_compass( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) +{ + va_list ap; + int result; + + va_start( ap, mask ); + result = vips_call_split( "compass", ap, in, out, mask ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/convolution/conv.c b/libvips/convolution/conv.c index 759b91b8..441cb550 100644 --- a/libvips/convolution/conv.c +++ b/libvips/convolution/conv.c @@ -62,6 +62,7 @@ G_DEFINE_TYPE( VipsConv, vips_conv, VIPS_TYPE_CONVOLUTION ); static int vips_conv_build( VipsObject *object ) { + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsConvolution *convolution = (VipsConvolution *) object; VipsConv *conv = (VipsConv *) object; @@ -73,27 +74,32 @@ vips_conv_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_conv_parent_class )->build( object ) ) return( -1 ); + /* + printf( "vips_conv_build: convolving with:\n" ); + vips_matrixprint( convolution->M, NULL ); + */ + + if( !(imsk = im_vips2imask( convolution->M, class->nickname )) || + !im_local_imask( convolution->out, imsk ) ) + return( -1 ); + if( !(dmsk = im_vips2mask( convolution->M, class->nickname )) || + !im_local_dmask( convolution->out, dmsk ) ) + return( -1 ); switch( conv->precision ) { case VIPS_PRECISION_INTEGER: - if( !(imsk = im_vips2imask( convolution->M, "im_stats" )) || - !im_local_imask( convolution->out, imsk ) || - im_conv( convolution->in, convolution->out, imsk ) ) + if( im_conv( convolution->in, convolution->out, imsk ) ) return( -1 ); break; case VIPS_PRECISION_FLOAT: - if( !(dmsk = im_vips2mask( convolution->M, "im_stats" )) || - !im_local_dmask( convolution->out, dmsk ) || - im_conv_f( convolution->in, convolution->out, dmsk ) ) + if( im_conv_f( convolution->in, convolution->out, dmsk ) ) return( -1 ); break; case VIPS_PRECISION_APPROXIMATE: - if( !(dmsk = im_vips2mask( convolution->M, "im_stats" )) || - !im_local_dmask( convolution->out, dmsk ) || - im_aconv( convolution->in, convolution->out, dmsk, - conv->layers, conv->cluster ) ) + if( im_aconv( convolution->in, convolution->out, dmsk, + conv->layers, conv->cluster ) ) return( -1 ); break; diff --git a/libvips/convolution/convolution.c b/libvips/convolution/convolution.c index 824e9b80..12abd08f 100644 --- a/libvips/convolution/convolution.c +++ b/libvips/convolution/convolution.c @@ -78,8 +78,8 @@ G_DEFINE_ABSTRACT_TYPE( VipsConvolution, vips_convolution, static int vips_convolution_build( VipsObject *object ) { - VipsConvolution *convolution = VIPS_CONVOLUTION( object ); VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConvolution *convolution = VIPS_CONVOLUTION( object ); VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); #ifdef DEBUG @@ -127,7 +127,7 @@ vips_convolution_class_init( VipsConvolutionClass *class ) VIPS_ARGUMENT_REQUIRED_OUTPUT, G_STRUCT_OFFSET( VipsConvolution, out ) ); - VIPS_ARG_IMAGE( class, "mask", 102, + VIPS_ARG_IMAGE( class, "mask", 20, _( "Mask" ), _( "Input matrix image" ), VIPS_ARGUMENT_REQUIRED_INPUT, @@ -147,6 +147,12 @@ void vips_convolution_operation_init( void ) { extern int vips_conv_get_type( void ); + extern int vips_morph_get_type( void ); + extern int vips_compass_get_type( void ); + extern int vips_convsep_get_type( void ); vips_conv_get_type(); + vips_morph_get_type(); + vips_compass_get_type(); + vips_convsep_get_type(); } diff --git a/libvips/convolution/convsep.c b/libvips/convolution/convsep.c new file mode 100644 index 00000000..b942f67b --- /dev/null +++ b/libvips/convolution/convsep.c @@ -0,0 +1,173 @@ +/* convolve twice, rotating the mask + * + * 23/10/13 + * - from vips_convsep() + */ + +/* + + 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 "pconvolution.h" + +typedef struct { + VipsConvolution parent_instance; + + VipsPrecision precision; + int layers; + int cluster; +} VipsConvsep; + +typedef VipsConvolutionClass VipsConvsepClass; + +G_DEFINE_TYPE( VipsConvsep, vips_convsep, VIPS_TYPE_CONVOLUTION ); + +static int +vips_convsep_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConvolution *convolution = (VipsConvolution *) object; + VipsConvsep *convsep = (VipsConvsep *) object; + VipsImage **t = (VipsImage **) + vips_object_local_array( object, 3 ); + + g_object_set( convsep, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_convsep_parent_class )->build( object ) ) + return( -1 ); + + if( vips_check_separable( class->nickname, convolution->M ) ) + return( -1 ); + + if( vips_rot( convolution->M, &t[0], VIPS_ANGLE_90, NULL ) || + vips_conv( convolution->in, &t[1], convolution->M, + "precision", convsep->precision, + "layers", convsep->layers, + "cluster", convsep->cluster, + NULL ) || + vips_conv( t[1], &t[2], t[0], + "precision", convsep->precision, + "layers", convsep->layers, + "cluster", convsep->cluster, + NULL ) ) + return( -1 ); + + if( vips_image_write( t[2], convolution->out ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_convsep_class_init( VipsConvsepClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "convsep"; + object_class->description = _( "convolution operation" ); + object_class->build = vips_convsep_build; + + VIPS_ARG_ENUM( class, "precision", 203, + _( "Precision" ), + _( "Convolve with this precision" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsConvsep, precision ), + VIPS_TYPE_PRECISION, VIPS_PRECISION_INTEGER ); + + VIPS_ARG_INT( class, "layers", 204, + _( "Layers" ), + _( "Use this many layers in approximation" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsConvsep, layers ), + 1, 1000, 5 ); + + VIPS_ARG_INT( class, "cluster", 205, + _( "Cluster" ), + _( "Cluster lines closer than this in approximation" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsConvsep, cluster ), + 1, 100, 1 ); + +} + +static void +vips_convsep_init( VipsConvsep *convsep ) +{ + convsep->precision = VIPS_PRECISION_INTEGER; + convsep->layers = 5; + convsep->cluster = 1; +} + +/** + * vips_convsep: + * @in: input image + * @out: output image + * @mask: convolution mask + * + * Optional arguments: + * + * @precision: calculation accuracy + * @layers: number of layers for approximation + * @cluster: cluster lines closer than this distance + * + * Perform a separable convolution of @in with @mask. + * See vips_conv() for a detailed description. + * + * The mask must be 1xn or nx1 elements. + * + * The image is convolved twice: once with @mask and then again with @mask + * rotated by 90 degrees. This is much faster for certain types of mask + * (gaussian blur, for example) than doing a full 2D convolution. + * + * See also: vips_conv(), vips_gaussmat(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_convsep( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) +{ + va_list ap; + int result; + + va_start( ap, mask ); + result = vips_call_split( "convsep", ap, in, out, mask ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/convolution/im_addgnoise.c b/libvips/convolution/im_addgnoise.c deleted file mode 100644 index 428eb2d9..00000000 --- a/libvips/convolution/im_addgnoise.c +++ /dev/null @@ -1,83 +0,0 @@ -/* im_addgnoise - * - * Copyright 1990, N. Dessipris. - * - * File written on 2/12/1986 - * Author : N. Dessipris - * Updated : 22/01/1991 - * 1/2/95 JC - * - junked! - * - now uses partial im_gaussnoise() and partial im_add() to do the - * same job - & 1/5/01 JC - * - oops, failed for not-1-band images - * - * 2008-01-28 tcv: - * - now works (was broken) - * - no limit on bands - * 4/2/10 - * - no need to bandup here now, im_add() does that - * - 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 - -/** - * im_addgnoise: - * @in: input image - * @out: output image - * @sigma: standard deviation of noise - * - * Add gaussian noise with mean 0 and variance sigma to @in. - * The noise is generated by averaging 12 random numbers, - * see page 78, PIETGEN, 1989. - * - * See also: im_gaussnoise(). - * - * Returns: 0 on success, -1 on error - */ -int -im_addgnoise( IMAGE *in, IMAGE *out, double sigma ) -{ - IMAGE *t; - - if( !(t = im_open_local( out, "im_addgnoise", "p" )) || - im_gaussnoise( t, in->Xsize, in->Ysize, 0, sigma ) || - im_add( in, t, out ) ) - return( -1 ); - - return( 0 ); -} diff --git a/libvips/convolution/im_compass.c b/libvips/convolution/im_compass.c deleted file mode 100644 index 5963cfa7..00000000 --- a/libvips/convolution/im_compass.c +++ /dev/null @@ -1,158 +0,0 @@ -/* im_compass - * - * Author: N. Dessipris (Copyright, N. Dessipris 1991) - * Written on: 08/05/1991 - * Modified on: - * 11/3/01 JC - * - rewritten, calling im_conv() and im_maxvalue() - * 3/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 - -/** - * im_compass: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * @in is convolved 8 times with @mask, each time @mask is rotated by 45 - * degrees. Each output pixel is the largest absolute value of the 8 - * convolutions. - * - * See also: im_lindetect(), im_gradient(), im_conv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_compass( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *filtered[8]; - IMAGE *absed[8]; - int i; - - if( im_open_local_array( out, filtered, 8, "im_compass:1", "p" ) || - im_open_local_array( out, absed, 8, "im_compass:2", "p" ) ) - return( -1 ); - - for( i = 0; i < 8; i++ ) { - if( im_conv( in, filtered[i], mask ) || - !(mask = im_local_imask( out, - im_rotate_imask45( mask, mask->filename ) )) ) - return( -1 ); - } - - for( i = 0; i < 8; i++ ) - if( im_abs( filtered[i], absed[i] ) ) - return( -1 ); - - return( im_maxvalue( absed, out, 8 ) ); -} - -/** - * im_lindetect: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * @in is convolved four times with @mask, each time @mask is rotated by 45 - * degrees. Each output pixel is the largest absolute value of the four - * convolutions. - * - * See also: im_compass(), im_gradient(), im_conv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_lindetect( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *filtered[4]; - IMAGE *absed[4]; - int i; - - if( im_open_local_array( out, filtered, 4, "im_lindetect:1", "p" ) || - im_open_local_array( out, absed, 4, "im_lindetect:2", "p" ) ) - return( -1 ); - - for( i = 0; i < 4; i++ ) { - if( im_conv( in, filtered[i], mask ) || - !(mask = im_local_imask( out, - im_rotate_imask45( mask, mask->filename ) )) ) - return( -1 ); - } - - for( i = 0; i < 4; i++ ) - if( im_abs( filtered[i], absed[i] ) ) - return( -1 ); - - return( im_maxvalue( absed, out, 4 ) ); -} - -/** - * im_gradient: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * @in is convolved with @mask and with @mask after a 90 degree rotation. The - * result is the sum of the absolute value of the two convolutions. - * - * See also: im_lindetect(), im_gradient(), im_conv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *t[4]; - INTMASK *rmask; - - if( im_open_local_array( out, t, 4, "im_gradient", "p" ) ) - return( -1 ); - - if( !(rmask = im_local_imask( out, - im_rotate_imask90( mask, mask->filename ) )) ) - return( -1 ); - - if( im_conv( in, t[0], mask ) || - im_conv( in, t[1], rmask ) || - im_abs( t[0], t[2] ) || - im_abs( t[1], t[3] ) || - im_add( t[2], t[3], out ) ) - return( -1 ); - - return( 0 ); -} diff --git a/libvips/convolution/im_conv.c b/libvips/convolution/im_conv.c index efa8136e..a2a8ea82 100644 --- a/libvips/convolution/im_conv.c +++ b/libvips/convolution/im_conv.c @@ -1075,28 +1075,6 @@ im_conv_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) return( 0 ); } -/** - * im_conv: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * Convolve @in with @mask using integer arithmetic. The output image - * always has the same #VipsBandFmt as the input image. - * - * Each output pixel is - * calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale - * and offset are part of @mask. For integer @in, the division by scale - * includes round-to-nearest. - * - * Convolutions on unsigned 8-bit images are calculated with the - * processor's vector unit, - * if possible. Disable this with --vips-novector or IM_NOVECTOR. - * - * See also: im_conv_f(), im_convsep(), im_create_imaskv(). - * - * Returns: 0 on success, -1 on error - */ int im_conv( IMAGE *in, IMAGE *out, INTMASK *mask ) { @@ -1114,72 +1092,3 @@ im_conv( IMAGE *in, IMAGE *out, INTMASK *mask ) return( 0 ); } - -int -im_convsep_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *t; - INTMASK *rmask; - - if( mask->xsize != 1 && mask->ysize != 1 ) { - im_error( "im_convsep", - "%s", _( "expect 1xN or Nx1 input mask" ) ); - return( -1 ); - } - - if( !(t = im_open_local( out, "im_convsep", "p" )) || - !(rmask = (INTMASK *) im_local( out, - (im_construct_fn) im_dup_imask, - (im_callback_fn) im_free_imask, mask, mask->filename, NULL )) ) - return( -1 ); - - rmask->xsize = mask->ysize; - rmask->ysize = mask->xsize; - rmask->offset = 0.; - - if( im_conv_raw( in, t, rmask ) || - im_conv_raw( t, out, mask ) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_convsep: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * Perform a separable convolution of @in with @mask using integer arithmetic. - * See im_conv() for a detailed description. - * - * The mask must be 1xn or nx1 elements. - * The output image - * always has the same #VipsBandFmt as the input image. - * - * The image is convolved twice: once with @mask and then again with @mask - * rotated by 90 degrees. This is much faster for certain types of mask - * (gaussian blur, for example) than doing a full 2D convolution. - * - * See also: im_convsep_f(), im_conv(), im_create_imaskv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask ) -{ - IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" ); - int n_mask = mask->xsize * mask->ysize; - - if( !t1 || - im_embed( in, t1, 1, n_mask / 2, n_mask / 2, - in->Xsize + n_mask - 1, - in->Ysize + n_mask - 1 ) || - im_convsep_raw( t1, out, mask ) ) - return( -1 ); - - out->Xoffset = 0; - out->Yoffset = 0; - - return( 0 ); -} diff --git a/libvips/convolution/im_conv_f.c b/libvips/convolution/im_conv_f.c index a4ee23ab..d4fefad2 100644 --- a/libvips/convolution/im_conv_f.c +++ b/libvips/convolution/im_conv_f.c @@ -392,77 +392,3 @@ im_conv_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) return( 0 ); } - -int -im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) -{ - IMAGE *t; - DOUBLEMASK *rmask; - - if( mask->xsize != 1 && mask->ysize != 1 ) { - im_error( "im_convsep_f", - "%s", _( "expect 1xN or Nx1 input mask" ) ); - return( -1 ); - } - - if( !(t = im_open_local( out, "im_convsep_f", "p" )) || - !(rmask = (DOUBLEMASK *) im_local( out, - (im_construct_fn) im_dup_dmask, - (im_callback_fn) im_free_dmask, mask, mask->filename, NULL )) ) - return( -1 ); - - rmask->xsize = mask->ysize; - rmask->ysize = mask->xsize; - rmask->offset = 0.; - - if( im_conv_f_raw( in, t, rmask ) || - im_conv_f_raw( t, out, mask ) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_convsep_f: - * @in: input image - * @out: output image - * @mask: convolution mask - * - * Perform a separable convolution of @in with @mask using floating-point - * arithmetic. - * - * The mask must be 1xn or nx1 elements. - * The output image - * is always %IM_BANDFMT_FLOAT unless @in is %IM_BANDFMT_DOUBLE, in which case - * @out is also %IM_BANDFMT_DOUBLE. - * - * The image is convolved twice: once with @mask and then again with @mask - * rotated by 90 degrees. This is much faster for certain types of mask - * (gaussian blur, for example) than doing a full 2D convolution. - * - * Each output pixel is - * calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale - * and offset are part of @mask. - * - * See also: im_convsep(), im_conv(), im_create_dmaskv(). - * - * Returns: 0 on success, -1 on error - */ -int -im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) -{ - IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" ); - int size = mask->xsize * mask->ysize; - - if( !t1 || - im_embed( in, t1, 1, size / 2, size / 2, - in->Xsize + size - 1, - in->Ysize + size - 1 ) || - im_convsep_f_raw( t1, out, mask ) ) - return( -1 ); - - out->Xoffset = 0; - out->Yoffset = 0; - - return( 0 ); -} diff --git a/libvips/convolution/morph.c b/libvips/convolution/morph.c new file mode 100644 index 00000000..6b699ff0 --- /dev/null +++ b/libvips/convolution/morph.c @@ -0,0 +1,179 @@ +/* morphology + * + * 23/10/13 + * - from vips_conv() + */ + +/* + + 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 + + */ + +/* This is a simple wrapper over the old vips7 functions. At some point we + * should rewrite this as a pure vips8 class and redo the vips7 functions as + * wrappers over this. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#include "pconvolution.h" + +typedef struct { + VipsConvolution parent_instance; + + VipsOperationMorphology morph; + +} VipsMorph; + +typedef VipsConvolutionClass VipsMorphClass; + +G_DEFINE_TYPE( VipsMorph, vips_morph, VIPS_TYPE_CONVOLUTION ); + +static int +vips_morph_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsConvolution *convolution = (VipsConvolution *) object; + VipsMorph *morph = (VipsMorph *) object; + + INTMASK *imsk; + + g_object_set( morph, "out", vips_image_new(), NULL ); + + if( VIPS_OBJECT_CLASS( vips_morph_parent_class )->build( object ) ) + return( -1 ); + + if( !(imsk = im_vips2imask( convolution->M, class->nickname )) || + !im_local_imask( convolution->out, imsk ) ) + return( -1 ); + + switch( morph->morph ) { + case VIPS_OPERATION_MORPHOLOGY_DILATE: + if( im_dilate( convolution->in, convolution->out, imsk ) ) + return( -1 ); + break; + + case VIPS_OPERATION_MORPHOLOGY_ERODE: + if( im_erode( convolution->in, convolution->out, imsk ) ) + return( -1 ); + break; + + default: + g_assert( 0 ); + } + + return( 0 ); +} + +static void +vips_morph_class_init( VipsMorphClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "morph"; + object_class->description = _( "convolution operation" ); + object_class->build = vips_morph_build; + + VIPS_ARG_ENUM( class, "morph", 103, + _( "Morphology" ), + _( "Morphological operation to perform" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsMorph, morph ), + VIPS_TYPE_OPERATION_MORPHOLOGY, + VIPS_OPERATION_MORPHOLOGY_ERODE ); + +} + +static void +vips_morph_init( VipsMorph *morph ) +{ + morph->morph = VIPS_OPERATION_MORPHOLOGY_ERODE; +} + +/** + * vips_morph: + * @in: input image + * @out: output image + * @mask: morphology with this mask + * @morph: operation to perform + * @...: %NULL-terminated list of optional named arguments + * + * Performs a morphological operation on @in using @mask as a + * structuring element. + * + * The image should have 0 (black) for no object and 255 + * (non-zero) for an object. Note that this is the reverse of the usual + * convention for these operations, but more convenient when combined with the + * boolean operators. The output image is the same + * size as the input image: edge pxels are made by expanding the input image + * as necessary. + * + * Mask coefficients can be either 0 (for object) or 255 (for background) + * or 128 (for do not care). The origin of the mask is at location + * (m.xsize / 2, m.ysize / 2), integer division. All algorithms have been + * based on the book "Fundamentals of Digital Image Processing" by A. Jain, + * pp 384-388, Prentice-Hall, 1989. + * + * For #VIPS_OPERATION_MOPHOLOGY_ERODE, + * the whole mask must match for the output pixel to be + * set, that is, the result is the logical AND of the selected input pixels. + * + * For #VIPS_OPERATION_MOPHOLOGY_DILATE, + * the output pixel is set if any part of the mask + * matches, that is, the result is the logical OR of the selected input pixels. + * + * See the boolean operations vips_andimage(), vips_orimage() and + * vips_eorimage() + * for analogues of the usual set difference and set union operations. + * + * Operations are performed using the processor's vector unit, + * if possible. Disable this with --vips-novector or IM_NOVECTOR. + * + * Returns: 0 on success, -1 on error + */ +int +vips_morph( VipsImage *in, VipsImage **out, VipsImage *mask, + VipsOperationMorphology morph, ... ) +{ + va_list ap; + int result; + + va_start( ap, morph ); + result = vips_call_split( "morph", ap, in, out, mask, morph ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/create/Makefile.am b/libvips/create/Makefile.am index 60812e38..59c2cf1b 100644 --- a/libvips/create/Makefile.am +++ b/libvips/create/Makefile.am @@ -3,6 +3,8 @@ noinst_LTLIBRARIES = libcreate.la libcreate_la_SOURCES = \ create.c \ pcreate.h \ + gaussmat.c \ + logmat.c \ buildlut.c \ invertlut.c \ tonelut.c \ diff --git a/libvips/create/black.c b/libvips/create/black.c index 0081a2cf..9c4ad4a1 100644 --- a/libvips/create/black.c +++ b/libvips/create/black.c @@ -100,7 +100,7 @@ vips_black_build( VipsObject *object ) black->bands == 1 ? VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_MULTIBAND, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, diff --git a/libvips/create/create.c b/libvips/create/create.c index 61b6e234..5130e92a 100644 --- a/libvips/create/create.c +++ b/libvips/create/create.c @@ -111,6 +111,8 @@ void vips_create_operation_init( void ) { extern GType vips_black_get_type( void ); + extern GType vips_gaussmat_get_type( void ); + extern GType vips_logmat_get_type( void ); extern GType vips_gaussnoise_get_type( void ); #ifdef HAVE_PANGOFT2 extern GType vips_text_get_type( void ); @@ -126,6 +128,8 @@ vips_create_operation_init( void ) extern GType vips_identity_get_type( void ); vips_black_get_type(); + vips_gaussmat_get_type(); + vips_logmat_get_type(); vips_gaussnoise_get_type(); #ifdef HAVE_PANGOFT2 vips_text_get_type(); diff --git a/libvips/create/gaussmat.c b/libvips/create/gaussmat.c new file mode 100644 index 00000000..e1f624cf --- /dev/null +++ b/libvips/create/gaussmat.c @@ -0,0 +1,242 @@ +/* generate gaussian images + * + * Written on: 30/11/1989 by Nicos + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + * 18/3/09 + * - bumped max mask size *40 + * - added _sep variant + * 30/3/09 + * - set scale in _sep variant, why not + * 21/10/10 + * - gtkdoc + * 20/10/13 + * - redone 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 + + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "pcreate.h" + +typedef struct _VipsGaussmat { + VipsCreate parent_instance; + + double sigma; + double min_ampl; + + gboolean separable; + gboolean integer; + +} VipsGaussmat; + +typedef struct _VipsGaussmatClass { + VipsCreateClass parent_class; + +} VipsGaussmatClass; + +G_DEFINE_TYPE( VipsGaussmat, vips_gaussmat, VIPS_TYPE_CREATE ); + +static int +vips_gaussmat_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsCreate *create = VIPS_CREATE( object ); + VipsGaussmat *gaussmat = (VipsGaussmat *) object; + double sig2 = 2. * gaussmat->sigma * gaussmat->sigma; + int max_x = 8 * gaussmat->sigma > 5000 ? 5000 : 8 * gaussmat->sigma ; + + int x, y; + int width, height; + double sum; + + if( VIPS_OBJECT_CLASS( vips_gaussmat_parent_class )->build( object ) ) + return( -1 ); + + /* Find the size of the mask. Limit the mask size to 10k x 10k for + * sanity. + */ + for( x = 0; x < max_x; x++ ) { + double v = exp( - ((double)(x * x)) / sig2 ); + + if( v < gaussmat->min_ampl ) + break; + } + if( x == max_x ) { + vips_error( class->nickname, "%s", _( "mask too large" ) ); + return( -1 ); + } + width = x * 2 + 1; + height = gaussmat->separable ? 1 : width; + + vips_image_init_fields( create->out, + width, height, 1, + VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, + 1.0, 1.0 ); + vips_image_pipelinev( create->out, + VIPS_DEMAND_STYLE_ANY, NULL ); + if( vips_image_write_prepare( create->out ) ) + return( -1 ); + + sum = 0.0; + for( y = 0; y < height; y++ ) { + for( x = 0; x < width; x++ ) { + int xo = x - width / 2; + int yo = y - height / 2; + double distance = xo * xo + yo * yo; + double v = exp( -distance / sig2 ); + + if( gaussmat->integer ) + v = VIPS_RINT( 20 * v ); + + *VIPS_MATRIX( create->out, x, y ) = v; + sum += v; + } + } + + vips_image_set_double( create->out, "scale", sum ); + vips_image_set_double( create->out, "offset", 0.0 ); + + return( 0 ); +} + +static void +vips_gaussmat_class_init( VipsGaussmatClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "gaussmat"; + vobject_class->description = _( "make a gaussian image" ); + vobject_class->build = vips_gaussmat_build; + + VIPS_ARG_DOUBLE( class, "sigma", 2, + _( "Radius" ), + _( "Radius of Gaussian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsGaussmat, sigma ), + 0.000001, 10000.0, 1.0 ); + + VIPS_ARG_DOUBLE( class, "min_ampl", 3, + _( "Width" ), + _( "Minimum amplitude of Gaussian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsGaussmat, min_ampl ), + 0.000001, 10000.0, 0.1 ); + + VIPS_ARG_BOOL( class, "separable", 4, + _( "Separable" ), + _( "Generate separable Gaussian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsGaussmat, separable ), + FALSE ); + + VIPS_ARG_BOOL( class, "integer", 5, + _( "Integer" ), + _( "Generate integer Gaussian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsGaussmat, integer ), + FALSE ); + +} + +static void +vips_gaussmat_init( VipsGaussmat *gaussmat ) +{ + gaussmat->sigma = 1; + gaussmat->min_ampl = 0.1; +} + +/** + * vips_gaussmat: + * @sigma: standard deviation of mask + * @min_ampl: minimum amplitude + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @separable: generate a separable gaussian + * @integer: generate an integer gaussian + * + * Creates a circularly symmetric Gaussian image of radius + * @sigma. The size of the mask is determined by the variable @min_ampl; + * if for instance the value .1 is entered this means that the produced mask + * is clipped at values less than 10 percent of the maximum amplitude. + * + * The program uses the following equation: + * + * H(r) = exp( -(r * r) / (2 * @sigma * @sigma) ) + * + * The generated image has odd size and its maximum value is normalised to + * 1.0, unless @integer is set. + * + * If @separable is set, only the centre horizontal is generated. This is + * useful for separable convolutions. + * + * If @integer is set, an integer gaussian is generated. This is useful for + * integer convolutions. + * + * "scale" is set to the sum of all the mask elements. + * + * See also: im_log_dmask(), vips_conv(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_gaussmat( VipsImage **out, double sigma, double min_ampl, ... ) +{ + va_list ap; + int result; + + va_start( ap, min_ampl ); + result = vips_call_split( "gaussmat", ap, out, sigma, min_ampl ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/create/gaussnoise.c b/libvips/create/gaussnoise.c index 035063a9..ccbae21c 100644 --- a/libvips/create/gaussnoise.c +++ b/libvips/create/gaussnoise.c @@ -131,7 +131,7 @@ vips_gaussnoise_build( VipsObject *object ) gaussnoise->width, gaussnoise->height, 1, VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, diff --git a/libvips/create/identity.c b/libvips/create/identity.c index d30d72b5..d596bd12 100644 --- a/libvips/create/identity.c +++ b/libvips/create/identity.c @@ -116,7 +116,7 @@ vips_identity_build( VipsObject *object ) VIPS_CODING_NONE, VIPS_INTERPRETATION_HISTOGRAM, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, diff --git a/libvips/create/logmat.c b/libvips/create/logmat.c new file mode 100644 index 00000000..536599bb --- /dev/null +++ b/libvips/create/logmat.c @@ -0,0 +1,273 @@ +/* laplacian of logmatian + * + * Written on: 30/11/1989 + * Updated on: 6/12/1991 + * 7/8/96 JC + * - ansified, mem leaks plugged + * 20/11/98 JC + * - mask too large check added + * 26/3/02 JC + * - ahem, was broken since '96, thanks matt + * 16/7/03 JC + * - makes mask out to zero, not out to minimum, thanks again matt + * 22/10/10 + * - gtkdoc + * 20/10/13 + * - redone as a class from logmat.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 VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include "pcreate.h" + +typedef struct _VipsLogmat { + VipsCreate parent_instance; + + double sigma; + double min_ampl; + + gboolean separable; + gboolean integer; + +} VipsLogmat; + +typedef struct _VipsLogmatClass { + VipsCreateClass parent_class; + +} VipsLogmatClass; + +G_DEFINE_TYPE( VipsLogmat, vips_logmat, VIPS_TYPE_CREATE ); + +static int +vips_logmat_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsCreate *create = VIPS_CREATE( object ); + VipsLogmat *logmat = (VipsLogmat *) object; + double sig2 = logmat->sigma * logmat->sigma; + + double last; + int x, y; + int width, height; + double sum; + + if( VIPS_OBJECT_CLASS( vips_logmat_parent_class )->build( object ) ) + return( -1 ); + + /* Find the size of the mask. We want to eval the mask out to the + * flat zero part, ie. beyond the minimum and to the point where it + * comes back up towards zero. + */ + last = 0.0; + for( x = 0; x < 5000; x++ ) { + const double distance = x * x; + double val; + + /* Handbook of Pattern Recognition and image processing + * by Young and Fu AP 1986 pp 220-221 + * temp = (1.0 / (2.0 * IM_PI * sig4)) * + (2.0 - (distance / sig2)) * + exp( (-1.0) * distance / (2.0 * sig2) ) + + .. use 0.5 to normalise + */ + val = 0.5 * + (2.0 - (distance / sig2)) * + exp( -distance / (2.0 * sig2) ); + + /* Stop when change in value (ie. difference from the last + * point) is positive (ie. we are going up) and absolute value + * is less than the min. + */ + if( val - last >= 0 && + fabs( val ) < logmat->min_ampl ) + break; + + last = val; + } + if( x == 5000 ) { + vips_error( class->nickname, "%s", _( "mask too large" ) ); + return( -1 ); + } + + width = x * 2 + 1; + height = logmat->separable ? 1 : width; + + vips_image_init_fields( create->out, + width, height, 1, + VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, + 1.0, 1.0 ); + vips_image_pipelinev( create->out, + VIPS_DEMAND_STYLE_ANY, NULL ); + if( vips_image_write_prepare( create->out ) ) + return( -1 ); + + sum = 0.0; + for( y = 0; y < height; y++ ) { + for( x = 0; x < width; x++ ) { + int xo = x - width / 2; + int yo = y - height / 2; + double distance = xo * xo + yo * yo; + double v = 0.5 * + (2.0 - (distance / sig2)) * + exp( -distance / (2.0 * sig2) ); + + if( logmat->integer ) + v = VIPS_RINT( 20 * v ); + + *VIPS_MATRIX( create->out, x, y ) = v; + sum += v; + } + } + + vips_image_set_double( create->out, "scale", sum ); + vips_image_set_double( create->out, "offset", 0.0 ); + + return( 0 ); +} + +static void +vips_logmat_class_init( VipsLogmatClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "logmat"; + vobject_class->description = _( "make a laplacian of gaussian image" ); + vobject_class->build = vips_logmat_build; + + VIPS_ARG_DOUBLE( class, "sigma", 2, + _( "Radius" ), + _( "Radius of Logmatian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsLogmat, sigma ), + 0.000001, 10000.0, 1.0 ); + + VIPS_ARG_DOUBLE( class, "min_ampl", 3, + _( "Width" ), + _( "Minimum amplitude of Logmatian" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsLogmat, min_ampl ), + 0.000001, 10000.0, 0.1 ); + + VIPS_ARG_BOOL( class, "separable", 4, + _( "Separable" ), + _( "Generate separable Logmatian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsLogmat, separable ), + FALSE ); + + VIPS_ARG_BOOL( class, "integer", 5, + _( "Integer" ), + _( "Generate integer Logmatian" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsLogmat, integer ), + FALSE ); + +} + +static void +vips_logmat_init( VipsLogmat *logmat ) +{ + logmat->sigma = 1; + logmat->min_ampl = 0.1; +} + +/** + * vips_logmat: + * @sigma: standard deviation of mask + * @min_ampl: minimum amplitude + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @separable: generate a separable logmatian + * @integer: generate an integer logmatian + * + * Creates a circularly symmetric Laplacian of Gaussian mask + * of radius + * @sigma. The size of the mask is determined by the variable @min_ampl; + * if for instance the value .1 is entered this means that the produced mask + * is clipped at values within 10 persent of zero, and where the change + * between mask elements is less than 10%. + * + * The program uses the following equation: (from Handbook of Pattern + * Recognition and image processing by Young and Fu, AP 1986 pages 220-221): + * + * H(r) = (1 / (2 * M_PI * s4)) * + * (2 - (r2 / s2)) * + * exp(-r2 / (2 * s2)) + * + * where s2 = @sigma * @sigma, s4 = s2 * s2, r2 = r * r. + * + * The generated mask has odd size and its maximum value is normalised to + * 1.0, unless @integer is set. + * + * If @separable is set, only the centre horizontal is generated. This is + * useful for separable convolutions. + * + * If @integer is set, an integer logmatian is generated. This is useful for + * integer convolutions. + * + * "scale" is set to the sum of all the mask elements. + * + * See also: vips_gauss(), vips_conv(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_logmat( VipsImage **out, double sigma, double min_ampl, ... ) +{ + va_list ap; + int result; + + va_start( ap, min_ampl ); + result = vips_call_split( "logmat", ap, out, sigma, min_ampl ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/create/point.c b/libvips/create/point.c index 0e03e515..b26c6cb4 100644 --- a/libvips/create/point.c +++ b/libvips/create/point.c @@ -103,7 +103,7 @@ vips_point_build( VipsObject *object ) point->width, point->height, 1, VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( t[0], + vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( t[0], NULL, vips_point_gen, NULL, point, NULL ) ) diff --git a/libvips/create/text.c b/libvips/create/text.c index 6677fdda..bb53fb91 100644 --- a/libvips/create/text.c +++ b/libvips/create/text.c @@ -233,7 +233,7 @@ vips_text_build( VipsObject *object ) text->bitmap.width, text->bitmap.rows, 1, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); for( y = 0; y < text->bitmap.rows; y++ ) diff --git a/libvips/create/xyz.c b/libvips/create/xyz.c index b50fad65..d4a9940e 100644 --- a/libvips/create/xyz.c +++ b/libvips/create/xyz.c @@ -163,7 +163,7 @@ vips_xyz_build( VipsObject *object ) VIPS_FORMAT_UINT, VIPS_CODING_NONE, VIPS_INTERPRETATION_MULTIBAND, 1.0, 1.0 ); - vips_demand_hint( create->out, + vips_image_pipelinev( create->out, VIPS_DEMAND_STYLE_ANY, NULL ); if( vips_image_generate( create->out, diff --git a/libvips/deprecated/Makefile.am b/libvips/deprecated/Makefile.am index c61a6f82..d0110ab6 100644 --- a/libvips/deprecated/Makefile.am +++ b/libvips/deprecated/Makefile.am @@ -45,8 +45,6 @@ libdeprecated_la_SOURCES = \ im_mask2vips.c \ im_vips2mask.c \ rotmask.c \ - im_gaussmasks.c \ - im_logmasks.c \ rw_mask.c \ im_matcat.c \ im_matinv.c \ diff --git a/libvips/deprecated/im_gaussmasks.c b/libvips/deprecated/im_gaussmasks.c deleted file mode 100644 index 0d1dc13d..00000000 --- a/libvips/deprecated/im_gaussmasks.c +++ /dev/null @@ -1,280 +0,0 @@ -/* generate gaussian masks - * - * Written on: 30/11/1989 by Nicos - * Updated on: 6/12/1991 - * 7/8/96 JC - * - ansified, mem leaks plugged - * 20/11/98 JC - * - mask too large check added - * 18/3/09 - * - bumped max mask size *40 - * - added _sep variant - * 30/3/09 - * - set scale in _sep variant, why not - * 21/10/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 - -#define IM_MAXMASK 5000 - -/** - * im_gauss_dmask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_dmask() creates a circularly symmetric Gaussian mask of radius - * @sigma. The size of the mask is determined by the variable @min_ampl; - * if for instance the value .1 is entered this means that the produced mask - * is clipped at values less than 10 percent of the maximum amplitude. - * - * The program uses the following equation: - * - * H(r) = exp( -(r * r) / (2 * sigma * sigma) ) - * - * The generated mask has odd size and its maximum value is normalised to - * 1.0. - * - * See also: im_gauss_imask(), im_gauss_imask_sep(), im_log_dmask(), im_conv(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -DOUBLEMASK * -im_gauss_dmask( const char *filename, double sigma, double min_ampl ) -{ - int x, y, k; - double distance; - double temp; - double *pt1, *pt2, *pt3, *pt4; - int max_x; - int xm, ym; - int xm2, ym2; /* xm2 = xm/2 */ - int offset; - double *cf, *cfs, *mc; - DOUBLEMASK *m; - double sig2, sum; /* sig2 = 2. * sigma * sigma */ - - /* Find the size of the mask depending on the entered data - */ - sig2 = 2. * sigma * sigma; - max_x = 8 * sigma > IM_MAXMASK ? IM_MAXMASK : 8 * sigma ; - for( x = 0; x < max_x; x++ ) { - temp = exp( - ((double)(x * x))/sig2 ); - if( temp < min_ampl ) - break; - } - if( x == max_x ) { - im_error( "im_gauss_dmask", "%s", _( "mask too large" ) ); - return( NULL ); - } - - xm2 = x; - ym2 = x; - xm = xm2 * 2 + 1; - ym = ym2 * 2 + 1; - - if( !(cfs = IM_ARRAY( NULL, (xm2+1)*(ym2+1), double )) ) - return( NULL ); - - for( k = 0, y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++, k++ ) { - distance = x*x + y*y; - cfs[k] = exp( -distance / sig2 ); - } - } - -#ifdef PIM_RINT - for( k = 0, y = 0; y <= ymask_2; y++ ) { - for( x = 0; x <= xmask_2; x++, k++ ) - fprintf(stderr, "%3.2f ", cfs[k] ); - fprintf(stderr, "\n"); - } -#endif - - if( !(m = im_create_dmask( filename, xm, ym )) ) { - im_free( cfs ); - return( NULL ); - } - - /* copy the 1/4 cfs into the m - */ - cf = cfs; - offset = xm2 * (xm + 1); - mc = m->coeff + offset; - for( y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++ ) { - pt1 = mc + (y * xm) + x; - pt2 = mc - (y * xm) + x; - pt3 = mc + (y * xm) - x; - pt4 = mc - (y * xm) - x; - - *pt1 = cf[x]; - *pt2 = cf[x]; - *pt3 = cf[x]; - *pt4 = cf[x]; - } - - cf += (xm2 + 1); - } - im_free( cfs ); - - sum = 0.0; - for( k = 0, y = 0; y < m->ysize; y++ ) - for( x = 0; x < m->xsize; x++, k++ ) - sum += m->coeff[k]; - m->scale = sum; - m->offset = 0.0; - -#ifdef PIM_RINT - im_print_dmask( m ); -#endif - return( m ); -} - -/** - * im_gauss_dmask_sep: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_dmask_sep() works exactly as im_gauss_dmask(), but returns only - * the central line of the mask. This is useful with im_convsepf(). - * - * See also: im_gauss_dmask(), im_convsepf(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -DOUBLEMASK * -im_gauss_dmask_sep( const char *filename, double sigma, double min_ampl ) -{ - DOUBLEMASK *im; - DOUBLEMASK *im2; - int i; - double sum; - - if( !(im = im_gauss_dmask( filename, sigma, min_ampl )) ) - return( NULL ); - if( !(im2 = im_create_dmask( filename, im->xsize, 1 )) ) { - im_free_dmask( im ); - return( NULL ); - } - - sum = 0; - for( i = 0; i < im->xsize; i++ ) { - im2->coeff[i] = im->coeff[i + im->xsize * (im->ysize / 2)]; - sum += im2->coeff[i]; - } - im2->scale = sum; - - im_free_dmask( im ); - - return( im2 ); -} - -/** - * im_gauss_imask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_imask() works exactly as im_gauss_dmask(), but the returned mask - * is scaled so that it's maximum value it set to 100. - * - * See also: im_gauss_dmask(), im_gauss_imask_sep(), im_conv(), im_convsep(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -INTMASK * -im_gauss_imask( const char *filename, double sigma, double min_ampl ) -{ - DOUBLEMASK *dm; - INTMASK *im; - - if( !(dm = im_gauss_dmask( filename, sigma, min_ampl )) ) - return( NULL ); - - if( !(im = im_scale_dmask( dm, dm->filename )) ) { - im_free_dmask( dm ); - return( NULL ); - } - im_free_dmask( dm ); - - return( im ) ; -} - -/** - * im_gauss_imask_sep: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_gauss_imask_sep() works exactly as im_gauss_imask(), but returns only - * the central line of the mask. This is useful with im_convsep(). - * - * See also: im_gauss_dmask(), im_convsep(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -INTMASK * -im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) -{ - INTMASK *im; - INTMASK *im2; - int i; - int sum; - - if( !(im = im_gauss_imask( filename, sigma, min_ampl )) ) - return( NULL ); - if( !(im2 = im_create_imask( filename, im->xsize, 1 )) ) { - im_free_imask( im ); - return( NULL ); - } - - sum = 0; - for( i = 0; i < im->xsize; i++ ) { - im2->coeff[i] = im->coeff[i + im->xsize * (im->ysize / 2)]; - sum += im2->coeff[i]; - } - im2->scale = sum; - - im_free_imask( im ); - - return( im2 ); -} diff --git a/libvips/deprecated/im_logmasks.c b/libvips/deprecated/im_logmasks.c deleted file mode 100644 index a044d43c..00000000 --- a/libvips/deprecated/im_logmasks.c +++ /dev/null @@ -1,238 +0,0 @@ -/* laplacian of gaussian - * - * Written on: 30/11/1989 - * Updated on: 6/12/1991 - * 7/8/96 JC - * - ansified, mem leaks plugged - * 20/11/98 JC - * - mask too large check added - * 26/3/02 JC - * - ahem, was broken since '96, thanks matt - * 16/7/03 JC - * - makes mask out to zero, not out to minimum, thanks again matt - * 22/10/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 - - */ - -/* -#define PIM_RINT 1 - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include - -#include -#include - -#define IM_MAXMASK 256 - -/** - * im_log_dmask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_log_dmask() creates a circularly symmetric Laplacian of Gaussian mask - * of radius - * @sigma. The size of the mask is determined by the variable @min_ampl; - * if for instance the value .1 is entered this means that the produced mask - * is clipped at values within 10 persent of zero, and where the change - * between mask elements is less than 10%. - * - * The program uses the following equation: (from Handbook of Pattern - * Recognition and image processing by Young and Fu, AP 1986 pages 220-221): - * - * H(r) = (1 / (2 * M_PI * s4)) * - * (2 - (r2 / s2)) * - * exp(-r2 / (2 * s2)) - * - * where s2 = sigma * sigma, s4 = s2 * s2, r2 = r * r. - * - * The generated mask has odd size and its maximum value is normalised to 1.0. - * - * See also: im_log_imask(), im_gauss_dmask(), im_conv(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -DOUBLEMASK * -im_log_dmask( const char *filename, double sigma, double min_ampl ) -{ - const double sig2 = sigma * sigma; - - double last; - int x, y, k; - - double *pt1, *pt2, *pt3, *pt4; - int xm, ym; - int xm2, ym2; /* xm2 = xm/2 */ - int offset; - double *cf, *cfs, *mc; - DOUBLEMASK *m; - double sum; - - /* Find the size of the mask depending on the entered data. We want to - * eval the mask out to the flat zero part, ie. beyond the minimum and - * to the point where it comes back up towards zero. - */ - last = 0.0; - for( x = 0; x < IM_MAXMASK; x++ ) { - const double distance = x * x; - double val; - - /* Handbook of Pattern Recognition and image processing - * by Young and Fu AP 1986 pp 220-221 - * temp = (1.0 / (2.0 * IM_PI * sig4)) * - (2.0 - (distance / sig2)) * - exp( (-1.0) * distance / (2.0 * sig2) ) - - .. use 0.5 to normalise - */ - val = 0.5 * - (2.0 - (distance / sig2)) * - exp( -distance / (2.0 * sig2) ); - - /* Stop when change in value (ie. difference from the last - * point) is positive (ie. we are going up) and absolute value - * is less than the min. - */ - if( val - last >= 0 && - fabs( val ) < min_ampl ) - break; - - last = val; - } - if( x == IM_MAXMASK ) { - im_error( "im_log_dmask", "%s", _( "mask too large" ) ); - return( NULL ); - } - - xm2 = x; - ym2 = x; - xm = xm2 * 2 + 1; - ym = ym2 * 2 + 1; - - if( !(cfs = IM_ARRAY( NULL, (xm2 + 1) * (ym2 + 1), double )) ) - return( NULL ); - - /* Make 1/4 of the mask. - */ - for( k = 0, y = 0; y <= ym2; y++ ) - for( x = 0; x <= xm2; x++, k++ ) { - const double distance = x * x + y * y; - - cfs[k] = 0.5 * - (2.0 - (distance / sig2)) * - exp( -distance / (2.0 * sig2) ); - } - -#ifdef PIM_RINT - for( k = 0, y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++, k++ ) - fprintf( stderr, "%3.2f ", cfs[k] ); - fprintf( stderr, "\n" ); - } -#endif - - if( !(m = im_create_dmask( filename, xm, ym )) ) { - im_free( cfs ); - return( NULL ); - } - - /* Copy the 1/4 cfs into the m - */ - cf = cfs; - offset = xm2 * (xm + 1); - mc = m->coeff + offset; - for( y = 0; y <= ym2; y++ ) { - for( x = 0; x <= xm2; x++ ) { - pt1 = mc + (y * xm) + x; - pt2 = mc - (y * xm) + x; - pt3 = mc + (y * xm) - x; - pt4 = mc - (y * xm) - x; - - *pt1 = cf[x]; - *pt2 = cf[x]; - *pt3 = cf[x]; - *pt4 = cf[x]; - } - - cf += (xm2 + 1); - } - im_free( cfs ); - - sum = 0.0; - for( k = 0, y = 0; y < m->ysize; y++ ) - for( x = 0; x < m->xsize; x++, k++ ) - sum += m->coeff[k]; - m->scale = sum; - m->offset = 0.0; - -#ifdef PIM_RINT - im_print_dmask( m ); -#endif - - return( m ); -} - -/** - * im_log_imask: - * @filename: the returned mask has this set as the filename - * @sigma: standard deviation of mask - * @min_ampl: minimum amplitude - * - * im_log_imask() works exactly as im_log_dmask(), but the returned mask - * is scaled so that it's maximum value it set to 100. - * - * See also: im_log_dmask(), im_gauss_imask(), im_conv(). - * - * Returns: the calculated mask on success, or NULL on error. - */ -INTMASK * -im_log_imask( const char *filename, double sigma, double min_ampl ) -{ - DOUBLEMASK *dm; - INTMASK *im; - - if( !(dm = im_log_dmask( filename, sigma, min_ampl )) ) - return( NULL ); - - if( !(im = im_scale_dmask( dm, dm->filename )) ) { - im_free_dmask( dm ); - return( NULL ); - } - im_free_dmask( dm ); - - return( im ) ; -} diff --git a/libvips/deprecated/im_mask2vips.c b/libvips/deprecated/im_mask2vips.c index f4c9e45d..1cf0142d 100644 --- a/libvips/deprecated/im_mask2vips.c +++ b/libvips/deprecated/im_mask2vips.c @@ -101,3 +101,17 @@ im_mask2vips( DOUBLEMASK *in, IMAGE *out ) return( 0 ); } +int +im_imask2vips( INTMASK *in, IMAGE *out ) +{ + DOUBLEMASK *d; + int result; + + if( !(d = im_imask2dmask( in, in->filename )) ) + return( -1 ); + result = im_mask2vips( d, out ); + im_free_dmask( d ); + + return( result ); +} + diff --git a/libvips/deprecated/lazy.c b/libvips/deprecated/lazy.c index 0980d169..0985e527 100644 --- a/libvips/deprecated/lazy.c +++ b/libvips/deprecated/lazy.c @@ -221,7 +221,7 @@ vips_image_open_lazy( VipsImage *image, /* Then 'start' creates the real image and 'gen' paints 'image' * with pixels from the real image on demand. */ - vips_demand_hint( image, image->dhint, NULL ); + vips_image_pipelinev( image, image->dhint, NULL ); if( vips_image_generate( image, open_lazy_start, open_lazy_generate, vips_stop_one, lazy, NULL ) ) diff --git a/libvips/deprecated/rotmask.c b/libvips/deprecated/rotmask.c index 4cdfc6ae..3863eebd 100644 --- a/libvips/deprecated/rotmask.c +++ b/libvips/deprecated/rotmask.c @@ -56,166 +56,6 @@ #include -/* Creates the offsets to rotate by 45 degrees an odd size square mask - */ -int * -im_offsets45( int size ) -{ - int temp; - int x, y; - int size2 = size * size; - int size_2 = size / 2; - int *pnt, *cpnt1, *cpnt2; - - if( size%2 == 0 ) { - im_error( "im_offsets45", "%s", _( "size not odd" ) ); - return( NULL ); - } - if( !(pnt = IM_ARRAY( NULL, size2, int )) ) - return( NULL ); - - /* point at the beginning and end of the buffer - */ - cpnt1 = pnt; cpnt2 = pnt + size2 - 1; - - for( y = 0; y < size_2; y++ ) { - temp = (size_2 + y) * size; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - - for( x = 0; x < y; x++ ) { - temp -= (size-1); - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < size_2 - y; x++ ) { - temp -= size; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < size_2 - y; x++ ) { - temp++; - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - - for( x = 0; x < y; x++ ) { - temp -= ( size - 1 ); - *cpnt1++ = temp; - *cpnt2-- = size2 - 1 - temp; - } - } - - /* the diagonal now - */ - temp = size * (size - 1); - cpnt1 = pnt + size_2 * size; - for( x = 0; x < size; x++ ) { - *cpnt1++ = temp; - temp -= (size-1); - } - -#ifdef PIM_RINT - temp = 0; - for( y = 0; y < size; y++ ) { - for( x = 0; x < size; x++ ) { - fprintf( stderr, "%4d", *(pnt+temp) ); - temp++; - } - fprintf(stderr, "\n"); - } - fprintf(stderr, "\n"); -#endif - - return( pnt ); -} - -/** - * im_rotate_dmask45: - * @in: input matrix - * @filename: name for output matrix - * - * Returns a mask which is the argument mask rotated by 45 degrees. - * Pass the filename to set for the output. - * - * See also: im_rotate_dmask90(). - * - * Returns: the result matrix on success, or %NULL on error. - */ -DOUBLEMASK * -im_rotate_dmask45( DOUBLEMASK *in, const char *filename ) -{ - DOUBLEMASK *out; - int size = in->xsize * in->ysize; - int *offsets; - int i; - - if( in->xsize != in->ysize || (in->xsize % 2) == 0 ) { - im_error( "im_rotate_dmask45", "%s", - _( "mask should be square of odd size" ) ); - return( NULL ); - } - if( !(offsets = im_offsets45( in->xsize )) ) - return( NULL ); - if( !(out = im_create_dmask( filename, in->xsize, in->ysize )) ) { - im_free( offsets ); - return( NULL ); - } - out->scale = in->scale; - out->offset = in->offset; - - for( i = 0; i < size; i++ ) - out->coeff[i] = in->coeff[offsets[i]]; - - im_free( offsets ); - - return( out ); -} - -/** - * im_rotate_imask45: - * @in: input matrix - * @filename: name for output matrix - * - * Returns a mask which is the argument mask rotated by 45 degrees. - * Pass the filename to set for the output. - * - * See also: im_rotate_imask90(). - * - * Returns: the result matrix on success, or %NULL on error. - */ -INTMASK * -im_rotate_imask45( INTMASK *in, const char *filename ) -{ - INTMASK *out; - int size = in->xsize * in->ysize; - int *offsets; - int i; - - if( in->xsize != in->ysize || (in->xsize % 2) == 0 ) { - im_error( "im_rotate_imask45", "%s", - _( "mask should be square of odd size" ) ); - return( NULL ); - } - if( !(offsets = im_offsets45( in->xsize )) ) - return( NULL ); - if( !(out = im_create_imask( filename, in->xsize, in->ysize )) ) { - im_free( offsets ); - return( NULL ); - } - out->scale = in->scale; - out->offset = in->offset; - - for( i = 0; i < size; i++ ) - out->coeff[i] = in->coeff[offsets[i]]; - - im_free( offsets ); - - return( out ); -} - /* The type of the vips operations we support. */ typedef int (*vips_fn)( IMAGE *in, IMAGE *out ); @@ -273,38 +113,42 @@ vapplydmask( DOUBLEMASK *in, const char *name, vips_fn fn ) return( out ); } -/** - * im_rotate_imask90: - * @in: input matrix - * @filename: name for output matrix - * - * Returns a mask which is the argument mask rotated by 90 degrees. - * Pass the filename to set for the output. - * - * See also: im_rotate_imask45(). - * - * Returns: the result matrix on success, or %NULL on error. - */ INTMASK * im_rotate_imask90( INTMASK *in, const char *filename ) { return( vapplyimask( in, filename, im_rot90 ) ); } -/** - * im_rotate_dmask90: - * @in: input matrix - * @filename: name for output matrix - * - * Returns a mask which is the argument mask rotated by 90 degrees. - * Pass the filename to set for the output. - * - * See also: im_rotate_dmask45(). - * - * Returns: the result matrix on success, or %NULL on error. - */ DOUBLEMASK * im_rotate_dmask90( DOUBLEMASK *in, const char *filename ) { return( vapplydmask( in, filename, im_rot90 ) ); } + +static int +im_rot45( IMAGE *in, IMAGE *out ) +{ + VipsImage *t; + + if( vips_rot45( in, &t, NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); + return( -1 ); + } + g_object_unref( t ); + + return( 0 ); +} + +INTMASK * +im_rotate_imask45( INTMASK *in, const char *filename ) +{ + return( vapplyimask( in, filename, im_rot45 ) ); +} + +DOUBLEMASK * +im_rotate_dmask45( DOUBLEMASK *in, const char *filename ) +{ + return( vapplydmask( in, filename, im_rot45 ) ); +} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index 046ae5ef..dba66fab 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -50,6 +50,12 @@ #include #include +int +im_init_world( const char *argv0 ) +{ + return( vips_init( argv0 ) ); +} + VipsImage * im_open( const char *filename, const char *mode ) { @@ -480,7 +486,7 @@ im_wrapmany( IMAGE **in, IMAGE *out, im_wrapmany_fn fn, void *a, void *b ) if( vips_image_pio_input( in[i] ) ) return( -1 ); } - vips_demand_hint_array( out, VIPS_DEMAND_STYLE_THINSTRIP, in ); + vips_image_pipeline_array( out, VIPS_DEMAND_STYLE_THINSTRIP, in ); /* Generate! */ @@ -953,7 +959,35 @@ im_demand_hint (IMAGE * im, VipsDemandStyle hint, ...) return (-1); } - return (im_demand_hint_array (im, hint, ar)); + vips__demand_hint_array (im, hint, ar); + + return (0); +} + +int +im_cp_descv (IMAGE * im, ...) +{ + va_list ap; + int i; + IMAGE *ar[MAX_IMAGES]; + + va_start (ap, im); + for (i = 0; i < MAX_IMAGES && (ar[i] = va_arg (ap, IMAGE *)); i++) + ; + va_end (ap); + if (i == MAX_IMAGES) + { + im_error ("im_cp_descv", "%s", _("too many images")); + return (-1); + } + + return (vips__image_copy_fields_array (im, ar)); +} + +int +im_cp_desc(IMAGE *out, IMAGE *in ) +{ + return( im_cp_descv( out, in, NULL)); } int @@ -1356,6 +1390,30 @@ im_gbandjoin( VipsImage **in, VipsImage *out, int n ) return( 0 ); } +int +im_rank_image( VipsImage **in, VipsImage *out, int n, int index ) +{ + VipsImage *t; + + if( vips_bandrank( in, &t, n, + "index", index, + NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); + return( -1 ); + } + g_object_unref( t ); + + return( 0 ); +} + +int +im_maxvalue( IMAGE **in, IMAGE *out, int n ) +{ + return( im_rank_image( in, out, n, n - 1 ) ); +} + int im_invert( IMAGE *in, IMAGE *out ) { @@ -1894,6 +1952,119 @@ im_stats( VipsImage *in ) return( msk ); } +DOUBLEMASK * +im_gauss_dmask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_gaussmat( &t, sigma, min_ampl, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +DOUBLEMASK * +im_gauss_dmask_sep( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_gaussmat( &t, sigma, min_ampl, + "separable", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +INTMASK * +im_gauss_imask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + INTMASK *msk; + + if( vips_gaussmat( &t, sigma, min_ampl, + "integer", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2imask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +INTMASK * +im_gauss_imask_sep( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + INTMASK *msk; + + if( vips_gaussmat( &t, sigma, min_ampl, + "integer", TRUE, + "separable", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2imask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +INTMASK * +im_log_imask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + INTMASK *msk; + + if( vips_logmat( &t, sigma, min_ampl, + "integer", TRUE, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2imask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + +DOUBLEMASK * +im_log_dmask( const char *filename, double sigma, double min_ampl ) +{ + VipsImage *t; + DOUBLEMASK *msk; + + if( vips_logmat( &t, sigma, min_ampl, + NULL ) ) + return( NULL ); + if( !(msk = im_vips2mask( t, filename )) ) { + g_object_unref( t ); + return( NULL ); + } + g_object_unref( t ); + + return( msk ); +} + int im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *recomb ) { @@ -1917,6 +2088,154 @@ im_recomb( IMAGE *in, IMAGE *out, DOUBLEMASK *recomb ) return( 0 ); } +int +im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_compass( in, &t2, t1, + "times", 8, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + +int +im_lindetect( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_compass( in, &t2, t1, + "times", 4, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + +int +im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_compass( in, &t2, t1, + "times", 2, + "angle", VIPS_ANGLE45_90, + "combine", VIPS_COMBINE_SUM, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + +int +im_convsep_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + im_error( "im_convsep_raw", "no compat function" ); + return( -1 ); +} + +int +im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_imask2vips( mask, t1 ) ) + return( -1 ); + if( vips_convsep( in, &t2, t1, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + +int +im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + im_error( "im_convsep_raw", "no compat function" ); + return( -1 ); +} + +int +im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask ) +{ + VipsImage *t1, *t2; + + if( !(t1 = vips_image_new()) || + im_mask2vips( mask, t1 ) ) + return( -1 ); + if( vips_convsep( in, &t2, t1, + "precision", VIPS_PRECISION_FLOAT, + NULL ) ) { + g_object_unref( t1 ); + return( -1 ); + } + g_object_unref( t1 ); + if( vips_image_write( t2, out ) ) { + g_object_unref( t2 ); + return( -1 ); + } + g_object_unref( t2 ); + + return( 0 ); +} + +int +im_addgnoise( IMAGE *in, IMAGE *out, double sigma ) +{ + IMAGE *t; + + if( !(t = im_open_local( out, "im_addgnoise", "p" )) || + im_gaussnoise( t, in->Xsize, in->Ysize, 0, sigma ) || + im_add( in, t, out ) ) + return( -1 ); + + return( 0 ); +} + static int vips__round( VipsImage *in, VipsImage *out, VipsOperationRound round ) { diff --git a/libvips/foreign/csv.c b/libvips/foreign/csv.c index 78689740..75a85c6f 100644 --- a/libvips/foreign/csv.c +++ b/libvips/foreign/csv.c @@ -280,11 +280,11 @@ read_csv( FILE *fp, VipsImage *out, fsetpos( fp, &pos ); } + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); vips_image_init_fields( out, columns, lines, 1, VIPS_FORMAT_DOUBLE, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Just reading the header? We are done. */ @@ -663,12 +663,11 @@ vips__matrix_body( char *whitemap, VipsImage *out, FILE *fp ) } VipsImage * -vips__matrix_read( const char *filename ) +vips__matrix_read_file( FILE *fp ) { char whitemap[256]; int i; char *p; - FILE *fp; int width; int height; double scale; @@ -680,13 +679,9 @@ vips__matrix_read( const char *filename ) for( p = WHITESPACE; *p; p++ ) whitemap[(int) *p] = 1; - if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) - return( NULL ); if( vips__matrix_header( whitemap, fp, - &width, &height, &scale, &offset ) ) { - fclose( fp ); + &width, &height, &scale, &offset ) ) return( NULL ); - } if( !(out = vips_image_new_matrix( width, height )) ) return( NULL ); @@ -695,28 +690,35 @@ vips__matrix_read( const char *filename ) if( vips__matrix_body( whitemap, out, fp ) ) { g_object_unref( out ); - fclose( fp ); return( NULL ); } - fclose( fp ); return( out ); } +VipsImage * +vips__matrix_read( const char *filename ) +{ + FILE *fp; + VipsImage *out; + + if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) + return( NULL ); + out = vips__matrix_read_file( fp ); + fclose( fp ); + + return( out ); +} + int -vips__matrix_write( VipsImage *in, const char *filename ) +vips__matrix_write_file( VipsImage *in, FILE *fp ) { VipsImage *mask; - FILE *fp; int x, y; if( vips_check_matrix( "vips2mask", in, &mask ) ) return( -1 ); - if( !(fp = vips__file_open_write( filename, TRUE )) ) { - g_object_unref( mask ); - return( -1 ); - } fprintf( fp, "%d %d ", mask->Xsize, mask->Ysize ); if( vips_image_get_typeof( mask, "scale" ) && vips_image_get_typeof( mask, "offset" ) ) @@ -733,10 +735,23 @@ vips__matrix_write( VipsImage *in, const char *filename ) } g_object_unref( mask ); - fclose( fp ); return( 0 ); } +int +vips__matrix_write( VipsImage *in, const char *filename ) +{ + FILE *fp; + int result; + + if( !(fp = vips__file_open_write( filename, TRUE )) ) + return( -1 ); + result = vips__matrix_write_file( in, fp ); + fclose( fp ); + + return( result ); +} + const char *vips__foreign_matrix_suffs[] = { ".mat", NULL }; diff --git a/libvips/foreign/csv.h b/libvips/foreign/csv.h index 7f868c3c..15d1e090 100644 --- a/libvips/foreign/csv.h +++ b/libvips/foreign/csv.h @@ -48,8 +48,10 @@ int vips__csv_write( VipsImage *in, const char *filename, int vips__matrix_read_header( const char *filename, int *width, int *height, double *scale, double *offset ); int vips__matrix_ismatrix( const char *filename ); +VipsImage *vips__matrix_read_file( FILE *fp ); VipsImage *vips__matrix_read( const char *filename ); int vips__matrix_write( VipsImage *in, const char *filename ); +int vips__matrix_write_file( VipsImage *in, FILE *fp ); extern const char *vips__foreign_matrix_suffs[]; diff --git a/libvips/foreign/dzsave.c b/libvips/foreign/dzsave.c index f070527c..877846c8 100644 --- a/libvips/foreign/dzsave.c +++ b/libvips/foreign/dzsave.c @@ -263,7 +263,8 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, * easy. */ layer->image = vips_image_new(); - if( vips_image_copy_fields( layer->image, save->ready ) ) { + if( vips_image_pipelinev( layer->image, + VIPS_DEMAND_STYLE_ANY, save->ready, NULL ) ) { layer_free( layer ); return( NULL ); } diff --git a/libvips/foreign/fits.c b/libvips/foreign/fits.c index db0ebdef..ac0e4a9c 100644 --- a/libvips/foreign/fits.c +++ b/libvips/foreign/fits.c @@ -296,11 +296,11 @@ vips_fits_get_header( VipsFits *fits, VipsImage *out ) else type = VIPS_INTERPRETATION_MULTIBAND; + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); vips_image_init_fields( out, width, height, bands, format, VIPS_CODING_NONE, type, 1.0, 1.0 ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Read all keys into meta. */ diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 10be5040..830b6240 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -776,7 +776,7 @@ vips_foreign_load_start( VipsImage *out, void *a, void *b ) /* We have to tell vips that out depends on real. We've set * the demand hint below, but not given an input there. */ - vips_demand_hint( load->out, load->out->dhint, + vips_image_pipelinev( load->out, load->out->dhint, load->real, NULL ); } @@ -874,7 +874,7 @@ vips_foreign_load_build( VipsObject *object ) /* ->header() should set the dhint. It'll default to the safe * SMALLTILE if header() did not set it. */ - vips_demand_hint( load->out, load->out->dhint, NULL ); + vips_image_pipelinev( load->out, load->out->dhint, NULL ); /* Then 'start' creates the real image and 'gen' fetches * pixels for @out from @real on demand. @@ -1611,6 +1611,7 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_csv_get_type( void ); extern GType vips_foreign_load_matrix_get_type( void ); extern GType vips_foreign_save_matrix_get_type( void ); + extern GType vips_foreign_print_matrix_get_type( void ); extern GType vips_foreign_load_fits_get_type( void ); extern GType vips_foreign_save_fits_get_type( void ); extern GType vips_foreign_load_analyze_get_type( void ); @@ -1643,6 +1644,7 @@ vips_foreign_operation_init( void ) vips_foreign_save_csv_get_type(); vips_foreign_load_matrix_get_type(); vips_foreign_save_matrix_get_type(); + vips_foreign_print_matrix_get_type(); vips_foreign_load_analyze_get_type(); vips_foreign_load_raw_get_type(); vips_foreign_save_raw_get_type(); @@ -1988,6 +1990,7 @@ vips_jpegload( const char *filename, VipsImage **out, ... ) * @Q: quality factor * @profile: attach this ICC profile * @optimize_coding: compute optimal Huffman coding tables + * @interlace: write an interlaced (progressive) jpeg * * Write a VIPS image to a file as JPEG. * @@ -2012,6 +2015,13 @@ vips_jpegload( const char *filename, VipsImage **out, ... ) * IPCT as @VIPS_META_IPCT_NAME ("ipct-data") and XMP as VIPS_META_XMP_NAME * ("xmp-data") are coded and attached. * + * If @optimize_coding is set, the Huffman tables are optimised. This is + * sllightly slower and produces slightly smaller files. + * + * If @interlace is set, the jpeg files will be interlaced (progressive jpeg, + * in jpg parlance). These files may be better for display over a slow network + * conection, but need much more memory to encode and decode. + * * See also: vips_jpegsave_buffer(), vips_image_write_file(). * * Returns: 0 on success, -1 on error. @@ -2041,6 +2051,7 @@ vips_jpegsave( VipsImage *in, const char *filename, ... ) * @Q: JPEG quality factor * @profile: attach this ICC profile * @optimize_coding: compute optimal Huffman coding tables + * @interlace: write an interlaced (progressive) jpeg * * As vips_jpegsave(), but save to a memory buffer. * diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 90ef0c5f..a7e2a234 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -764,7 +764,7 @@ read_jpeg_header( ReadJpeg *jpeg, VipsImage *out ) interpretation, xres, yres ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Interlaced jpegs need lots of memory to read, so our caller needs * to know. diff --git a/libvips/foreign/jpegsave.c b/libvips/foreign/jpegsave.c index e14c6086..ec21af6d 100644 --- a/libvips/foreign/jpegsave.c +++ b/libvips/foreign/jpegsave.c @@ -83,6 +83,10 @@ typedef struct _VipsForeignSaveJpeg { */ gboolean optimize_coding; + /* Generate an interlaced (progressive, in jpg terminology) file. + */ + gboolean interlace; + } VipsForeignSaveJpeg; typedef VipsForeignSaveClass VipsForeignSaveJpegClass; @@ -135,6 +139,13 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_coding ), FALSE ); + + VIPS_ARG_BOOL( class, "interlace", 13, + _( "interlace" ), + _( "Generate an interlaced (progressive) jpeg" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveJpeg, interlace ), + FALSE ); } static void @@ -169,7 +180,8 @@ vips_foreign_save_jpeg_file_build( VipsObject *object ) return( -1 ); if( vips__jpeg_write_file( save->ready, file->filename, - jpeg->Q, jpeg->profile, jpeg->optimize_coding ) ) + jpeg->Q, jpeg->profile, jpeg->optimize_coding, + jpeg->interlace ) ) return( -1 ); return( 0 ); @@ -234,7 +246,8 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object ) return( -1 ); if( vips__jpeg_write_buffer( save->ready, - &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding ) ) + &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding, + jpeg->interlace ) ) return( -1 ); area = vips_area_new_blob( (VipsCallbackFn) vips_free, obuf, olen ); @@ -295,7 +308,8 @@ vips_foreign_save_jpeg_mime_build( VipsObject *object ) return( -1 ); if( vips__jpeg_write_buffer( save->ready, - &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding ) ) + &obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding, + jpeg->interlace ) ) return( -1 ); printf( "Content-length: %zd\r\n", olen ); diff --git a/libvips/foreign/magick2vips.c b/libvips/foreign/magick2vips.c index 75e0e27a..f64d35f8 100644 --- a/libvips/foreign/magick2vips.c +++ b/libvips/foreign/magick2vips.c @@ -337,7 +337,7 @@ parse_header( Read *read ) */ im->Coding = VIPS_CODING_NONE; - vips_demand_hint( im, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); + vips_image_pipelinev( im, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Three ways to loop over attributes / properties :-( */ diff --git a/libvips/foreign/matrixsave.c b/libvips/foreign/matrixsave.c index dd36e045..d3e1934b 100644 --- a/libvips/foreign/matrixsave.c +++ b/libvips/foreign/matrixsave.c @@ -155,3 +155,75 @@ vips_matrixsave( VipsImage *in, const char *filename, ... ) return( result ); } + +typedef struct _VipsForeignPrintMatrix { + VipsForeignSave parent_object; + +} VipsForeignPrintMatrix; + +typedef VipsForeignSaveClass VipsForeignPrintMatrixClass; + +G_DEFINE_TYPE( VipsForeignPrintMatrix, vips_foreign_print_matrix, + VIPS_TYPE_FOREIGN_SAVE ); + +static int +vips_foreign_print_matrix_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + + if( VIPS_OBJECT_CLASS( vips_foreign_print_matrix_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__matrix_write_file( save->ready, stdout ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_print_matrix_class_init( VipsForeignPrintMatrixClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; + + object_class->nickname = "matrixprint"; + object_class->description = _( "print matrix" ); + object_class->build = vips_foreign_print_matrix_build; + + foreign_class->suffs = vips__foreign_matrix_suffs; + + save_class->saveable = VIPS_SAVEABLE_MONO; + save_class->format_table = bandfmt_matrix; +} + +static void +vips_foreign_print_matrix_init( VipsForeignPrintMatrix *matrix ) +{ +} + +/** + * vips_matrixprint: + * @in: image to print + * @...: %NULL-terminated list of optional named arguments + * + * Print @in to %stdout in matrix format. See vips_matrixload() for a + * description of the format. + * + * See also: vips_matrixload(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_matrixprint( VipsImage *in, ... ) +{ + va_list ap; + int result; + + va_start( ap, in ); + result = vips_call_split( "matrixprint", ap, in ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/foreign/openexr2vips.c b/libvips/foreign/openexr2vips.c index bef7f1e3..c6a13f53 100644 --- a/libvips/foreign/openexr2vips.c +++ b/libvips/foreign/openexr2vips.c @@ -212,9 +212,9 @@ read_header( Read *read, VipsImage *out ) VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); if( read->tiles ) - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); else - vips_demand_hint( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL ); } int diff --git a/libvips/foreign/openslide2vips.c b/libvips/foreign/openslide2vips.c index a2a7760b..59ccd931 100644 --- a/libvips/foreign/openslide2vips.c +++ b/libvips/foreign/openslide2vips.c @@ -220,7 +220,7 @@ readslide_new( const char *filename, VipsImage *out, associated, &w, &h ); vips_image_set_string( out, "slide-associated-image", associated ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); } else { char buf[256]; @@ -231,7 +231,7 @@ readslide_new( const char *filename, VipsImage *out, rslide->downsample = openslide_get_level_downsample( rslide->osr, level ); vips_image_set_int( out, "slide-level", level ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Try to get tile width/height. An undocumented, experimental * feature. diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index 8367fc25..bd139538 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -1236,7 +1236,7 @@ read_tilewise( ReadTiff *rtiff, VipsImage *out ) * the cache we are quite happy serving that if anything downstream * would like it. */ - vips_demand_hint( raw, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( raw, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); if( vips_image_generate( raw, tiff_seq_start, tiff_fill_region, tiff_seq_stop, @@ -1413,7 +1413,7 @@ read_stripwise( ReadTiff *rtiff, VipsImage *out ) if( parse_header( rtiff, t[0] ) ) return( -1 ); - vips_demand_hint( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL ); if( !tfget32( rtiff->tiff, TIFFTAG_ROWSPERSTRIP, &rtiff->rows_per_strip ) ) diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index 104f1079..fc47385b 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -841,7 +841,7 @@ write_jpeg_block( REGION *region, Rect *area, void *a ) */ static int write_vips( Write *write, int qfac, const char *profile, - gboolean optimize_coding ) + gboolean optimize_coding, gboolean progressive ) { VipsImage *in; J_COLOR_SPACE space; @@ -898,6 +898,11 @@ write_vips( Write *write, int qfac, const char *profile, */ write->cinfo.optimize_coding = optimize_coding; + /* Enable progressive write. + */ + if( progressive ) + jpeg_simple_progression( &write->cinfo ); + /* Build compress tables. */ jpeg_start_compress( &write->cinfo, TRUE ); @@ -941,7 +946,7 @@ write_vips( Write *write, int qfac, const char *profile, int vips__jpeg_write_file( VipsImage *in, const char *filename, int Q, const char *profile, - gboolean optimize_coding ) + gboolean optimize_coding, gboolean progressive ) { Write *write; @@ -971,7 +976,7 @@ vips__jpeg_write_file( VipsImage *in, /* Convert! */ - if( write_vips( write, Q, profile, optimize_coding ) ) { + if( write_vips( write, Q, profile, optimize_coding, progressive ) ) { write_destroy( write ); return( -1 ); } @@ -1218,7 +1223,7 @@ buf_dest( j_compress_ptr cinfo, void **obuf, size_t *olen ) int vips__jpeg_write_buffer( VipsImage *in, void **obuf, size_t *olen, int Q, const char *profile, - gboolean optimize_coding ) + gboolean optimize_coding, gboolean progressive ) { Write *write; @@ -1247,7 +1252,7 @@ vips__jpeg_write_buffer( VipsImage *in, /* Convert! */ - if( write_vips( write, Q, profile, optimize_coding ) ) { + if( write_vips( write, Q, profile, optimize_coding, progressive ) ) { write_destroy( write ); return( -1 ); diff --git a/libvips/foreign/vipsjpeg.h b/libvips/foreign/vipsjpeg.h index b4337812..246b51ad 100644 --- a/libvips/foreign/vipsjpeg.h +++ b/libvips/foreign/vipsjpeg.h @@ -39,10 +39,10 @@ extern const char *vips__jpeg_suffs[]; int vips__jpeg_write_file( VipsImage *in, const char *filename, int Q, const char *profile, - gboolean optimize_coding ); + gboolean optimize_coding, gboolean progressive ); int vips__jpeg_write_buffer( VipsImage *in, void **obuf, size_t *olen, int Q, const char *profile, - gboolean optimize_coding ); + gboolean optimize_coding, gboolean progressive ); int vips__isjpeg( const char *filename ); int vips__jpeg_read_file( const char *name, VipsImage *out, diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index 02a87e38..5062741a 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -351,7 +351,7 @@ png2vips_header( Read *read, VipsImage *out ) /* Sequential mode needs thinstrip to work with things like * vips_shrink(). */ - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); /* Fetch the ICC profile. @name is useless, something like "icc" or * "ICC Profile" etc. Ignore it. diff --git a/libvips/foreign/webp2vips.c b/libvips/foreign/webp2vips.c index 4996e5c2..031ebad5 100644 --- a/libvips/foreign/webp2vips.c +++ b/libvips/foreign/webp2vips.c @@ -153,7 +153,7 @@ read_header( Read *read, VipsImage *out ) VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); + vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); return( 0 ); } diff --git a/libvips/histogram/hist_local.c b/libvips/histogram/hist_local.c index 70456091..41365738 100644 --- a/libvips/histogram/hist_local.c +++ b/libvips/histogram/hist_local.c @@ -256,16 +256,14 @@ vips_hist_local_build( VipsObject *object ) g_object_set( object, "out", vips_image_new(), NULL ); - if( vips_image_copy_fields( local->out, in ) ) - return( -1 ); - local->out->Xsize -= local->width - 1; - local->out->Ysize -= local->height - 1; - /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause * too many recalculations on overlaps. */ - vips_demand_hint( local->out, - VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ); + if( vips_image_pipelinev( local->out, + VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) ) + return( -1 ); + local->out->Xsize -= local->width - 1; + local->out->Ysize -= local->height - 1; if( vips_image_generate( local->out, vips_hist_local_start, diff --git a/libvips/histogram/histogram.c b/libvips/histogram/histogram.c index a2c024ef..48c1c6b3 100644 --- a/libvips/histogram/histogram.c +++ b/libvips/histogram/histogram.c @@ -157,10 +157,9 @@ vips_histogram_build( VipsObject *object ) */ histogram->ready = size; - if( vips_image_copy_fields_array( histogram->out, histogram->ready ) ) + if( vips_image_pipeline_array( histogram->out, + VIPS_DEMAND_STYLE_THINSTRIP, histogram->ready ) ) return( -1 ); - vips_demand_hint_array( histogram->out, - VIPS_DEMAND_STYLE_THINSTRIP, histogram->ready ); histogram->out->Xsize = VIPS_IMAGE_N_PELS( histogram->ready[0] ); histogram->out->Ysize = 1; diff --git a/libvips/histogram/maplut.c b/libvips/histogram/maplut.c index d6df93fb..f3366c9e 100644 --- a/libvips/histogram/maplut.c +++ b/libvips/histogram/maplut.c @@ -564,10 +564,9 @@ vips_maplut_build( VipsObject *object ) vips_image_pio_input( in ) ) return( -1 ); - if( vips_image_copy_fieldsv( maplut->out, in, lut, NULL ) ) + if( vips_image_pipelinev( maplut->out, + VIPS_DEMAND_STYLE_THINSTRIP, in, lut, NULL ) ) return( -1 ); - vips_demand_hint( maplut->out, VIPS_DEMAND_STYLE_THINSTRIP, - in, lut, NULL ); maplut->out->BandFmt = lut->BandFmt; /* Output has same number of bands as LUT, unless LUT has 1 band, in diff --git a/libvips/histogram/stdif.c b/libvips/histogram/stdif.c index b64052c8..59e0fe95 100644 --- a/libvips/histogram/stdif.c +++ b/libvips/histogram/stdif.c @@ -249,16 +249,14 @@ vips_stdif_build( VipsObject *object ) g_object_set( object, "out", vips_image_new(), NULL ); - if( vips_image_copy_fields( stdif->out, in ) ) - return( -1 ); - stdif->out->Xsize -= stdif->width - 1; - stdif->out->Ysize -= stdif->height - 1; - /* Set demand hints. FATSTRIP is good for us, as THINSTRIP will cause * too many recalculations on overlaps. */ - vips_demand_hint( stdif->out, - VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ); + if( vips_image_pipelinev( stdif->out, + VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ) ) + return( -1 ); + stdif->out->Xsize -= stdif->width - 1; + stdif->out->Ysize -= stdif->height - 1; if( vips_image_generate( stdif->out, vips_start_one, diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am index 415f0cb4..17e219f7 100644 --- a/libvips/include/vips/Makefile.am +++ b/libvips/include/vips/Makefile.am @@ -65,6 +65,7 @@ vips_scan_headers = \ ${top_srcdir}/libvips/include/vips/colour.h \ ${top_srcdir}/libvips/include/vips/operation.h \ ${top_srcdir}/libvips/include/vips/convolution.h \ + ${top_srcdir}/libvips/include/vips/morphology.h \ ${top_srcdir}/libvips/include/vips/object.h enumtypes.h: $(vips_scan_headers) Makefile diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index f2c891f6..611f8846 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -69,6 +69,18 @@ typedef enum { VIPS_ANGLE_LAST } VipsAngle; +typedef enum { + VIPS_ANGLE45_0, + VIPS_ANGLE45_45, + VIPS_ANGLE45_90, + VIPS_ANGLE45_135, + VIPS_ANGLE45_180, + VIPS_ANGLE45_225, + VIPS_ANGLE45_270, + VIPS_ANGLE45_315, + VIPS_ANGLE45_LAST +} VipsAngle45; + int vips_copy( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_tilecache( VipsImage *in, VipsImage **out, ... ) @@ -107,6 +119,8 @@ int vips_wrap( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_rot( VipsImage *in, VipsImage **out, VipsAngle angle, ... ) __attribute__((sentinel)); +int vips_rot45( 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, ... ) @@ -143,7 +157,7 @@ int vips_bandjoin( VipsImage **in, VipsImage **out, int n, ... ) __attribute__((sentinel)); int vips_bandjoin2( VipsImage *in1, VipsImage *in2, VipsImage **out, ... ) __attribute__((sentinel)); -int vips_bandmean( VipsImage *in, VipsImage **out, ... ) +int vips_bandrank( VipsImage **in, VipsImage **out, int n, ... ) __attribute__((sentinel)); int vips_bandbool( VipsImage *in, VipsImage **out, @@ -155,6 +169,8 @@ int vips_bandor( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_bandeor( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_bandmean( VipsImage *in, VipsImage **out, ... ) + __attribute__((sentinel)); int vips_recomb( VipsImage *in, VipsImage **out, VipsImage *m, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/convolution.h b/libvips/include/vips/convolution.h index 5e16909a..3ce122f8 100644 --- a/libvips/include/vips/convolution.h +++ b/libvips/include/vips/convolution.h @@ -41,31 +41,48 @@ extern "C" { typedef enum { VIPS_PRECISION_INTEGER, VIPS_PRECISION_FLOAT, - VIPS_PRECISION_APPROXIMATE + VIPS_PRECISION_APPROXIMATE, + VIPS_PRECISION_LAST } VipsPrecision; +typedef enum { + VIPS_COMBINE_MAX, + VIPS_COMBINE_SUM, + VIPS_COMBINE_LAST +} VipsCombine; + +/** + * VipsOperationMorphology: + * @VIPS_OPERATION_MORPHOLOGY_ERODE: true if all set + * @VIPS_OPERATION_MORPHOLOGY_DILATE: true if one set + * + * More like hit-miss, really. + * + * See also: vips_morph(). + */ + +typedef enum { + VIPS_OPERATION_MORPHOLOGY_ERODE, + VIPS_OPERATION_MORPHOLOGY_DILATE, + VIPS_OPERATION_MORPHOLOGY_LAST +} VipsOperationMorphology; + int vips_conv( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) __attribute__((sentinel)); +int vips_compass( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) + __attribute__((sentinel)); +int vips_convsep( VipsImage *in, VipsImage **out, VipsImage *mask, ... ) + __attribute__((sentinel)); + +int vips_morph( VipsImage *in, VipsImage **out, VipsImage *mask, + VipsOperationMorphology morph, ... ) + __attribute__((sentinel)); void vips_convolution_operation_init( void ); - -int im_aconvsep( VipsImage *in, VipsImage *out, - DOUBLEMASK *mask, int n_layers ); -int im_aconv( VipsImage *in, VipsImage *out, - DOUBLEMASK *mask, int n_layers, int cluster ); -int im_conv( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_conv_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); -int im_convsep( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_convsep_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); - -int im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_gradient( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_lindetect( VipsImage *in, VipsImage *out, INTMASK *mask ); - int im_sharpen( VipsImage *in, VipsImage *out, int mask_size, double x1, double y2, double y3, @@ -80,8 +97,6 @@ int im_gradcor( VipsImage *in, VipsImage *ref, VipsImage *out ); int im_contrast_surface( VipsImage *in, VipsImage *out, int half_win_size, int spacing ); -int im_addgnoise( VipsImage *in, VipsImage *out, double sigma ); - #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/include/vips/create.h b/libvips/include/vips/create.h index abaa7167..ddcf9c66 100644 --- a/libvips/include/vips/create.h +++ b/libvips/include/vips/create.h @@ -47,6 +47,10 @@ int vips_xyz( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); int vips_grey( VipsImage **out, int width, int height, ... ) __attribute__((sentinel)); +int vips_gaussmat( VipsImage **out, double sigma, double min_ampl, ... ) + __attribute__((sentinel)); +int vips_logmat( VipsImage **out, double sigma, double min_ampl, ... ) + __attribute__((sentinel)); int vips_text( VipsImage **out, const char *text, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index 0655a2ae..4c1fa2f0 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -47,6 +47,8 @@ GType vips_align_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_ALIGN (vips_align_get_type()) GType vips_angle_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_ANGLE (vips_angle_get_type()) +GType vips_angle45_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_ANGLE45 (vips_angle45_get_type()) /* enumerations from "../../../libvips/include/vips/util.h" */ GType vips_token_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_TOKEN (vips_token_get_type()) @@ -72,6 +74,10 @@ GType vips_operation_flags_get_type (void) G_GNUC_CONST; /* enumerations from "../../../libvips/include/vips/convolution.h" */ GType vips_precision_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_PRECISION (vips_precision_get_type()) +GType vips_combine_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_COMBINE (vips_combine_get_type()) +GType vips_operation_morphology_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_OPERATION_MORPHOLOGY (vips_operation_morphology_get_type()) /* enumerations from "../../../libvips/include/vips/object.h" */ GType vips_argument_flags_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_ARGUMENT_FLAGS (vips_argument_flags_get_type()) diff --git a/libvips/include/vips/error.h b/libvips/include/vips/error.h index 6873547f..9bfdf139 100644 --- a/libvips/include/vips/error.h +++ b/libvips/include/vips/error.h @@ -83,10 +83,12 @@ int vips_check_8or16( const char *domain, VipsImage *im ); int vips_check_u8or16orf( const char *domain, VipsImage *im ); int vips_check_format_same( const char *domain, VipsImage *im1, VipsImage *im2 ); int vips_check_size_same( const char *domain, VipsImage *im1, VipsImage *im2 ); +int vips_check_oddsquare( const char *domain, VipsImage *im ); int vips_check_vector_length( const char *domain, int n, int len ); int vips_check_vector( const char *domain, int n, VipsImage *im ); int vips_check_hist( const char *domain, VipsImage *im ); int vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out ); +int vips_check_separable( const char *domain, VipsImage *im ); int vips_check_imask( const char *domain, INTMASK *mask ); int vips_check_dmask( const char *domain, DOUBLEMASK *mask ); diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index b3b1e79d..80dc7d32 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -421,6 +421,8 @@ int vips_matrixload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_matrixsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_matrixprint( VipsImage *in, ... ) + __attribute__((sentinel)); int vips_magickload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/generate.h b/libvips/include/vips/generate.h index 4e26d833..f7af6884 100644 --- a/libvips/include/vips/generate.h +++ b/libvips/include/vips/generate.h @@ -73,9 +73,9 @@ int vips_image_generate( VipsImage *im, void *a, void *b ); -void vips_demand_hint_array( VipsImage *image, +int vips_image_pipeline_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ); -void vips_demand_hint( VipsImage *image, VipsDemandStyle hint, ... ) +int vips_image_pipelinev( VipsImage *image, VipsDemandStyle hint, ... ) __attribute__((sentinel)); #ifdef __cplusplus diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index 95da4126..f27f205d 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -118,11 +118,6 @@ void vips_image_init_fields( VipsImage *image, VipsInterpretation interpretation, double xres, double yres ); -int vips_image_copy_fields_array( VipsImage *out, VipsImage *in[] ); -int vips_image_copy_fieldsv( VipsImage *out, VipsImage *in1, ... ) - __attribute__((sentinel)); -int vips_image_copy_fields( VipsImage *out, VipsImage *in ); - void vips_image_set( VipsImage *image, const char *field, GValue *value ); int vips_image_get( const VipsImage *image, const char *field, GValue *value_copy ); diff --git a/libvips/include/vips/mask.h b/libvips/include/vips/mask.h index 8ce14e19..624dc7c0 100644 --- a/libvips/include/vips/mask.h +++ b/libvips/include/vips/mask.h @@ -4,6 +4,9 @@ * - from proto.h */ +/* All deprecated. + */ + /* This file is part of VIPS. diff --git a/libvips/include/vips/morphology.h b/libvips/include/vips/morphology.h index 1e383097..b546bff0 100644 --- a/libvips/include/vips/morphology.h +++ b/libvips/include/vips/morphology.h @@ -31,19 +31,18 @@ */ -#ifndef IM_MORPHOLOGY_H -#define IM_MORPHOLOGY_H +#ifndef VIPS_MORPHOLOGY_H +#define VIPS_MORPHOLOGY_H #ifdef __cplusplus extern "C" { #endif /*__cplusplus*/ -int im_dilate( VipsImage *in, VipsImage *out, INTMASK *mask ); -int im_erode( VipsImage *in, VipsImage *out, INTMASK *mask ); + + + int im_rank( VipsImage *in, VipsImage *out, int width, int height, int index ); -int im_rank_image( VipsImage **in, VipsImage *out, int n, int index ); -int im_maxvalue( VipsImage **in, VipsImage *out, int n ); int im_cntlines( VipsImage *im, double *nolines, int flag ); int im_zerox( VipsImage *in, VipsImage *out, int sign ); @@ -53,4 +52,4 @@ int im_label_regions( VipsImage *test, VipsImage *mask, int *segments ); } #endif /*__cplusplus*/ -#endif /*IM_MORPHOLOGY_H*/ +#endif /*VIPS_MORPHOLOGY_H*/ diff --git a/libvips/include/vips/private.h b/libvips/include/vips/private.h index 03479055..7b270002 100644 --- a/libvips/include/vips/private.h +++ b/libvips/include/vips/private.h @@ -56,6 +56,11 @@ extern "C" { */ #define VIPS_SIZEOF_HEADER (64) +/* Startup plus ABI check. + */ +int vips__init( const char *argv0 ); +size_t vips__get_sizeof_vipsobject( void ); + /* What we track for each mmap window. Have a list of these on an openin * VipsImage. */ @@ -165,6 +170,11 @@ VipsArgumentInstance *vips__argument_get_instance( VipsArgument *vips__argument_table_lookup( VipsArgumentTable *table, GParamSpec *pspec); +void vips__demand_hint_array( struct _VipsImage *image, + int hint, struct _VipsImage **in ); +int vips__image_copy_fields_array( struct _VipsImage *out, + struct _VipsImage *in[] ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/include/vips/vips.h b/libvips/include/vips/vips.h index 85078776..ca777e0f 100644 --- a/libvips/include/vips/vips.h +++ b/libvips/include/vips/vips.h @@ -149,8 +149,20 @@ extern "C" { #include #include +#define vips_init( ARGV0 ) \ + (sizeof( VipsObject ) != vips__get_sizeof_vipsobject() ? ( \ + vips_info( "vips_init", "%s", _( "ABI mismatch" ) ), \ + vips_info( "vips_init", \ + _( "library has sizeof(VipsObject) == %zd" ), \ + vips__get_sizeof_vipsobject() ), \ + vips_info( "vips_init", \ + _( "application has sizeof(VipsObject) == %zd" ), \ + sizeof( VipsObject() ) ), \ + vips_error( "vips_init", "%s", _( "ABI mismatch" ) ), \ + -1 ) : \ + vips__init( ARGV0 )) + const char *vips_get_argv0( void ); -int vips_init( const char *argv0 ); void vips_check_init( void ); void vips_shutdown( void ); GOptionGroup *vips_get_option_group( void ); diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 32c3d88d..ba476e4c 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -203,15 +203,17 @@ extern "C" { #define im_get_argv0 vips_get_argv0 #define im_version_string vips_version_string #define im_version vips_version -#define im_init_world vips_init #define im_get_option_group vips_get_option_group #define im_guess_prefix vips_guess_prefix #define im_guess_libdir vips_guess_libdir #define im__global_lock vips__global_lock -#define im_cp_desc vips_image_copy_fields -#define im_cp_descv vips_image_copy_fieldsv -#define im_cp_desc_array vips_image_copy_fields_array +int im_cp_desc(IMAGE *out, IMAGE *in ); +int im_cp_descv (IMAGE * im, ...); +#define im_cp_desc_array(I, A) vips__image_copy_fields_array(I, A) +int im_demand_hint (IMAGE * im, VipsDemandStyle hint, ...); +#define im_demand_hint_array( A, B, C ) (vips__demand_hint_array( A, B, C ), 0) + #define im_image vips_image_new_from_memory #define im_binfile vips_image_new_from_file_raw #define im__open_temp vips_image_new_temp_file @@ -325,9 +327,6 @@ VipsDemandStyle im_char2dhint( const char *str ); #define im_rect_dup vips_rect_dup #define im_rect_normalise vips_rect_normalise -int im_demand_hint (IMAGE * im, VipsDemandStyle hint, ...); -#define im_demand_hint_array( A, B, C ) (vips_demand_hint_array( A, B, C ), 0) - #define im_start_one vips_start_one #define im_stop_one vips_stop_one #define im_start_many vips_start_many @@ -586,6 +585,8 @@ size_t im_ref_string_get_length( const GValue *value ); #define im_concurrency_set vips_concurrency_set #define im_concurrency_get vips_concurrency_get +int im_init_world( const char *argv0 ); + int im_add( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_subtract( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_multiply( VipsImage *in1, VipsImage *in2, VipsImage *out ); @@ -711,6 +712,8 @@ int im_rotquad( VipsImage *in, VipsImage *out ); int im_clip2fmt( VipsImage *in, VipsImage *out, VipsBandFormat fmt ); int im_bandjoin( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_gbandjoin( VipsImage **in, VipsImage *out, int n ); +int im_rank_image( VipsImage **in, VipsImage *out, int n, int index ); +int im_maxvalue( VipsImage **in, VipsImage *out, int n ); int im_grid( VipsImage *in, VipsImage *out, int tile_height, int across, int down ); int im_scale( VipsImage *in, VipsImage *out ); int im_scaleps( VipsImage *in, VipsImage *out ); @@ -768,6 +771,7 @@ int im_blend( VipsImage *c, VipsImage *a, VipsImage *b, VipsImage *out ); DOUBLEMASK *im_vips2mask( VipsImage *in, const char *filename ); INTMASK *im_vips2imask( IMAGE *in, const char *filename ); int im_mask2vips( DOUBLEMASK *in, VipsImage *out ); +int im_imask2vips( INTMASK *in, VipsImage *out ); int im_bandmean( VipsImage *in, VipsImage *out ); int im_recomb( VipsImage *in, VipsImage *out, DOUBLEMASK *recomb ); @@ -894,6 +898,26 @@ int im_tone_map( VipsImage *in, VipsImage *out, VipsImage *lut ); */ #define vips_class_map_concrete_all vips_class_map_all +int im_dilate( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_erode( VipsImage *in, VipsImage *out, INTMASK *mask ); + +int im_aconv( VipsImage *in, VipsImage *out, + DOUBLEMASK *mask, int n_layers, int cluster ); +int im_conv( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_conv_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); + +int im_aconvsep( VipsImage *in, VipsImage *out, + DOUBLEMASK *mask, int n_layers ); + +int im_convsep( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_convsep_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask ); + +int im_compass( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_gradient( VipsImage *in, VipsImage *out, INTMASK *mask ); +int im_lindetect( VipsImage *in, VipsImage *out, INTMASK *mask ); + +int im_addgnoise( VipsImage *in, VipsImage *out, double sigma ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index d4f4ff12..62dfc644 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -50,6 +50,7 @@ vips_scan_headers = \ ${top_srcdir}/libvips/include/vips/colour.h \ ${top_srcdir}/libvips/include/vips/operation.h \ ${top_srcdir}/libvips/include/vips/convolution.h \ + ${top_srcdir}/libvips/include/vips/morphology.h \ ${top_srcdir}/libvips/include/vips/object.h enumtypes.c: $(vips_scan_headers) Makefile diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index 1ec1cca0..75f8bdc8 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -223,6 +223,30 @@ vips_angle_get_type( void ) return( etype ); } +GType +vips_angle45_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_ANGLE45_0, "VIPS_ANGLE45_0", "0"}, + {VIPS_ANGLE45_45, "VIPS_ANGLE45_45", "45"}, + {VIPS_ANGLE45_90, "VIPS_ANGLE45_90", "90"}, + {VIPS_ANGLE45_135, "VIPS_ANGLE45_135", "135"}, + {VIPS_ANGLE45_180, "VIPS_ANGLE45_180", "180"}, + {VIPS_ANGLE45_225, "VIPS_ANGLE45_225", "225"}, + {VIPS_ANGLE45_270, "VIPS_ANGLE45_270", "270"}, + {VIPS_ANGLE45_315, "VIPS_ANGLE45_315", "315"}, + {VIPS_ANGLE45_LAST, "VIPS_ANGLE45_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsAngle45", values ); + } + + return( etype ); +} /* enumerations from "../../libvips/include/vips/arithmetic.h" */ GType vips_operation_math_get_type( void ) @@ -601,6 +625,7 @@ vips_precision_get_type( void ) {VIPS_PRECISION_INTEGER, "VIPS_PRECISION_INTEGER", "integer"}, {VIPS_PRECISION_FLOAT, "VIPS_PRECISION_FLOAT", "float"}, {VIPS_PRECISION_APPROXIMATE, "VIPS_PRECISION_APPROXIMATE", "approximate"}, + {VIPS_PRECISION_LAST, "VIPS_PRECISION_LAST", "last"}, {0, NULL, NULL} }; @@ -609,6 +634,42 @@ vips_precision_get_type( void ) return( etype ); } +GType +vips_combine_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_COMBINE_MAX, "VIPS_COMBINE_MAX", "max"}, + {VIPS_COMBINE_SUM, "VIPS_COMBINE_SUM", "sum"}, + {VIPS_COMBINE_LAST, "VIPS_COMBINE_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsCombine", values ); + } + + return( etype ); +} +GType +vips_operation_morphology_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_OPERATION_MORPHOLOGY_ERODE, "VIPS_OPERATION_MORPHOLOGY_ERODE", "erode"}, + {VIPS_OPERATION_MORPHOLOGY_DILATE, "VIPS_OPERATION_MORPHOLOGY_DILATE", "dilate"}, + {VIPS_OPERATION_MORPHOLOGY_LAST, "VIPS_OPERATION_MORPHOLOGY_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsOperationMorphology", values ); + } + + return( etype ); +} /* enumerations from "../../libvips/include/vips/object.h" */ GType vips_argument_flags_get_type( void ) diff --git a/libvips/iofuncs/error.c b/libvips/iofuncs/error.c index cdfebe3b..28cc8d99 100644 --- a/libvips/iofuncs/error.c +++ b/libvips/iofuncs/error.c @@ -1012,6 +1012,32 @@ vips_check_size_same( const char *domain, VipsImage *im1, VipsImage *im2 ) return( 0 ); } +/** + * vips_check_oddsquare: + * @domain: the originating domain for the error message + * @im: image to check + * + * Check that the image is square and that the sides are odd. + * If not, set an error message + * and return non-zero. + * + * See also: vips_error(). + * + * Returns: 0 if OK, -1 otherwise. + */ +int +vips_check_oddsquare( const char *domain, VipsImage *im ) +{ + if( im->Xsize != im->Ysize || + im->Xsize % 2 == 0 ) { + vips_error( domain, + "%s", _( "images must be odd and square" ) ); + return( -1 ); + } + + return( 0 ); +} + /** * vips_check_bands_same: * @domain: the originating domain for the error message @@ -1240,6 +1266,33 @@ vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out ) return( 0 ); } +/** + * vips_check_separable: + * @domain: the originating domain for the error message + * @im: image to check + * + * Separable matrix images must have width or height 1. + * Return 0 if the image will pass, or -1 and + * set an error message otherwise. + * + * See also: vips_error(). + * + * Returns: 0 if OK, -1 otherwise. + */ +int +vips_check_separable( const char *domain, VipsImage *im ) +{ + if( im->Xsize != 1 && + im->Ysize != 1 ) { + vips_error( domain, + "%s", _( "separable matrix images must have " + "width or height 1" ) ); + return( -1 ); + } + + return( 0 ); +} + /** * vips_check_imask: (skip) * @domain: the originating domain for the error message diff --git a/libvips/iofuncs/generate.c b/libvips/iofuncs/generate.c index 174671fe..f23dccc1 100644 --- a/libvips/iofuncs/generate.c +++ b/libvips/iofuncs/generate.c @@ -274,26 +274,12 @@ vips__link_map( VipsImage *image, gboolean upstream, return( result ); } -/** - * vips_demand_hint_array: - * @image: image to set hint for - * @hint: hint for this image - * @in: array of input images to this operation - * - * Operations can set demand hints, that is, hints to the VIPS IO system about - * the type of region geometry this operation works best with. For example, - * operations which transform coordinates will usually work best with - * %VIPS_DEMAND_STYLE_SMALLTILE, operations which work on local windows of - * pixels will like %VIPS_DEMAND_STYLE_FATSTRIP. - * - * VIPS uses the list of input images to build the tree of operations it needs - * for the cache invalidation system. You have to call this function, or its - * varargs friend vips_demand_hint(). - * - * See also: vips_demand_hint(), vips_image_generate(). +/* We have to have this as a separate entry point so we can support the old + * vips7 API. */ void -vips_demand_hint_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ) +vips__demand_hint_array( VipsImage *image, + VipsDemandStyle hint, VipsImage **in ) { int i, len, nany; VipsDemandStyle set_hint; @@ -327,7 +313,7 @@ vips_demand_hint_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ) image->dhint = set_hint; #ifdef DEBUG - printf( "vips_demand_hint_array: set dhint for \"%s\" to %s\n", + printf( "vips_image_pipeline_array: set dhint for \"%s\" to %s\n", image->filename, vips_enum_nick( VIPS_TYPE_DEMAND_STYLE, image->dhint ) ); printf( "\toperation requested %s\n", @@ -354,17 +340,62 @@ vips_demand_hint_array( VipsImage *image, VipsDemandStyle hint, VipsImage **in ) } /** - * vips_demand_hint: - * @image: image to set hint for - * @hint: hint for this image - * @Varargs: %NULL-terminated list of input images to this operation + * vips_image_pipeline_array: + * @image: output image + * @hint: demand hint for @image + * @in: %NULL-terminated array of input images * - * Build an array and call vips_demand_hint_array(). + * Add an image to a pipeline. @image depends on all of the images in @in, + * @image prefers to supply pixels according to @hint. * - * See also: vips_demand_hint(), vips_image_generate(). + * Operations can set demand hints, that is, hints to the VIPS IO system about + * the type of region geometry this operation works best with. For example, + * operations which transform coordinates will usually work best with + * %VIPS_DEMAND_STYLE_SMALLTILE, operations which work on local windows of + * pixels will like %VIPS_DEMAND_STYLE_FATSTRIP. + * + * Header fields in @image are set from the fields in @in, with lower-numbered + * images in @in taking priority. + * For example, if @in[0] and @in[1] both have an item + * called "icc-profile", it's the profile attached to @in[0] that will end up + * on @image. + * Image history is completely copied from all @in. @image will have the history + * of all the input images. + * The array of input images can be empty, meaning @image is at the start of a + * pipeline. + * + * VIPS uses the list of input images to build the tree of operations it needs + * for the cache invalidation system. + * + * See also: vips_image_pipelinev(), vips_image_generate(). + * + * Returns: 0 on success, -1 on error. */ -void -vips_demand_hint( VipsImage *image, VipsDemandStyle hint, ... ) +int +vips_image_pipeline_array( VipsImage *image, + VipsDemandStyle hint, VipsImage **in ) +{ + vips__demand_hint_array( image, hint, in ); + + if( in[0] && + vips__image_copy_fields_array( image, in ) ) + return( -1 ); + + return( 0 ); +} + +/** + * vips_image_pipelinev: + * @image: output image of pipeline + * @hint: hint for this image + * @...: %NULL-terminated list of input images + * + * Build an array and call vips_image_pipeline_array(). + * + * See also: vips_image_generate(). + */ +int +vips_image_pipelinev( VipsImage *image, VipsDemandStyle hint, ... ) { va_list ap; int i; @@ -376,14 +407,14 @@ vips_demand_hint( VipsImage *image, VipsDemandStyle hint, ... ) ; va_end( ap ); if( i == MAX_IMAGES ) { - vips_warn( "vips_demand_hint", "%s", _( "too many images" ) ); + vips_warn( "vips_image_pipeline", "%s", _( "too many images" ) ); /* Make sure we have a sentinel there. */ ar[i - 1] = NULL; } - vips_demand_hint_array( image, hint, ar ); + return( vips_image_pipeline_array( image, hint, ar ) ); } /** diff --git a/libvips/iofuncs/header.c b/libvips/iofuncs/header.c index f430bd53..d3ebbb4c 100644 --- a/libvips/iofuncs/header.c +++ b/libvips/iofuncs/header.c @@ -604,13 +604,13 @@ vips_image_get_data( VipsImage *image ) * @yres: vertical resolution, pixels per millimetre * * A convenience function to set the header fields after creating an image. - * Normally you copy the fields from one of your input images with - * vips_image_copy_fields() and then make + * Normally you copy the fields from your input images with + * vips_image_pipelinev() and then make * any adjustments you need, but if you are creating an image from scratch, * for example im_black() or im_jpeg2vips(), you do need to set all the * fields yourself. * - * See also: vips_image_copy_fields(). + * See also: vips_image_pipelinev(). */ void vips_image_init_fields( VipsImage *image, @@ -655,7 +655,7 @@ meta_cp_field( VipsMeta *meta, VipsImage *dst ) return( NULL ); } -/* Copy meta on to dst. Called from vips_cp_desc(). +/* Copy meta on to dst. */ static int meta_cp( VipsImage *dst, const VipsImage *src ) @@ -671,31 +671,11 @@ meta_cp( VipsImage *dst, const VipsImage *src ) return( 0 ); } -/** - * vips_image_copy_fields_array: - * @out: image to copy to - * @in: %NULL-terminated array of images to copy from - * - * Copy fields from all the input images to the output image. There must be at - * least one input image. - * - * The first input image is used to set the main fields of @out (@width, - * @coding and so on). - * - * Metadata from all the images is merged on to @out, with lower-numbered items - * overriding higher. So for example, if @in[0] and @in[1] both have an item - * called "icc-profile", it's the profile attached to @in[0] that will end up - * on @out. - * - * Image history is completely copied from all @in. @out will have the history - * of all the input images. - * - * See also: vips_image_copy_fieldsv(), vips_image_copy_fields(). - * - * Returns: 0 on success, -1 on error. +/* We have to have this as a separate entry point so we can support the old + * vips7 API. */ int -vips_image_copy_fields_array( VipsImage *out, VipsImage *in[] ) +vips__image_copy_fields_array( VipsImage *out, VipsImage *in[] ) { int i; int ni; @@ -740,63 +720,6 @@ vips_image_copy_fields_array( VipsImage *out, VipsImage *in[] ) return( 0 ); } -/* Max number of images we can handle. - */ -#define MAX_IMAGES (1000) - -/** - * vips_image_copy_fieldsv: - * @out: image to copy to - * @in1: first image to copy from - * @Varargs: %NULL-terminated list of images to copy from - * - * Copy fields from all the input images to the output image. A convenience - * function over vips_image_copy_fields_array(). - * - * See also: vips_image_copy_fields_array(), vips_image_copy_fields(). - * - * Returns: 0 on success, -1 on error. - */ -int -vips_image_copy_fieldsv( VipsImage *out, VipsImage *in1, ... ) -{ - va_list ap; - int i; - VipsImage *in[MAX_IMAGES]; - - in[0] = in1; - va_start( ap, in1 ); - for( i = 1; i < MAX_IMAGES && - (in[i] = va_arg( ap, VipsImage * )); i++ ) - ; - va_end( ap ); - if( i == MAX_IMAGES ) { - vips_error( "vips_image_copy_fieldsv", - "%s", _( "too many images" ) ); - return( -1 ); - } - - return( vips_image_copy_fields_array( out, in ) ); -} - -/** - * vips_image_copy_fields: - * @out: image to copy to - * @in: image to copy from - * - * Copy fields from @in to @out. A convenience - * function over vips_image_copy_fields_array(). - * - * See also: vips_image_copy_fields_array(), vips_image_copy_fieldsv(). - * - * Returns: 0 on success, -1 on error. - */ -int -vips_image_copy_fields( VipsImage *out, VipsImage *in ) -{ - return( vips_image_copy_fieldsv( out, in, NULL ) ); -} - /** * vips_image_set: * @image: image to set the metadata on diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 386415b1..22ac7927 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -105,7 +105,7 @@ * @VIPS_DEMAND_STYLE_THINSTRIP: demand in thin (typically 1 pixel high) strips * @VIPS_DEMAND_STYLE_ANY: demand geometry does not matter * - * See vips_demand_hint(). Operations can hint to the VIPS image IO system about + * See vips_image_pipelinev(). Operations can hint to the VIPS image IO system about * the kind of demand geometry they prefer. * * These demand styles are given below in order of increasing @@ -134,7 +134,7 @@ * file (even indirectly) so any demand style is OK. It's used for things like * im_black() where the pixels are calculated. * - * See also: vips_demand_hint(). + * See also: vips_image_pipelinev(). */ /** @@ -1939,10 +1939,9 @@ int vips_image_write( VipsImage *image, VipsImage *out ) { if( vips_image_pio_input( image ) || - vips_image_copy_fields( out, image ) ) + vips_image_pipelinev( out, + VIPS_DEMAND_STYLE_THINSTRIP, image, NULL ) ) return( -1 ); - vips_demand_hint( out, - VIPS_DEMAND_STYLE_THINSTRIP, image, NULL ); /* We generate from @image partially, so we need to keep it about as * long as @out is about. @@ -2358,7 +2357,7 @@ int vips__image_wio_output( VipsImage *image ) { #ifdef DEBUG_IO - printf( "vips_image_wio_output: WIO output for %s\n", + printf( "vips__image_wio_output: WIO output for %s\n", image->filename ); #endif/*DEBUG_IO*/ @@ -2367,7 +2366,7 @@ vips__image_wio_output( VipsImage *image ) /* Make sure nothing is attached. */ if( image->generate_fn ) { - vips_error( "vips_image_wio_output", + vips_error( "vips__image_wio_output", "%s", _( "image already written" ) ); return( -1 ); } @@ -2391,7 +2390,7 @@ vips__image_wio_output( VipsImage *image ) break; default: - vips_error( "vips_image_wio_output", + vips_error( "vips__image_wio_output", "%s", _( "image not writeable" ) ); return( -1 ); } diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index eac7745e..74390550 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -140,7 +140,7 @@ vips_get_argv0( void ) * * * - * loads any plugins from $libdir/vips-x.y, where x and y are the + * loads any plugins from $libdir/vips-x.y/, where x and y are the * major and minor version numbers for this VIPS. * * @@ -165,8 +165,13 @@ vips_get_argv0( void ) * * Returns: 0 on success, -1 otherwise */ + +/* vips_init() is actually a macro which checks library and application + * compatibility before calling vips__init(). + */ + int -vips_init( const char *argv0 ) +vips__init( const char *argv0 ) { extern GType vips_system_get_type( void ); @@ -299,6 +304,20 @@ vips_init( const char *argv0 ) return( 0 ); } +/* Return the sizeof() various important data structures. These are checked + * against the headers used to build our caller by vips_init(). + * + * We allow direct access to members of VipsImage and VipsRegion (mostly for + * reasons of history), so any change to a superclass of either of these + * objects will break our ABI. + */ + +size_t +vips__get_sizeof_vipsobject( void ) +{ + return( sizeof( VipsObject ) ); +} + /* Call this before vips stuff that uses stuff we need to have inited. */ void diff --git a/libvips/iofuncs/sinkscreen.c b/libvips/iofuncs/sinkscreen.c index 4e91f9ef..358fbadd 100644 --- a/libvips/iofuncs/sinkscreen.c +++ b/libvips/iofuncs/sinkscreen.c @@ -1067,14 +1067,14 @@ vips_sink_screen( VipsImage *in, VipsImage *out, VipsImage *mask, } if( vips_image_pio_input( in ) || - vips_image_copy_fields( out, in ) ) + vips_image_pipelinev( out, + VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) return( -1 ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ); if( mask ) { - if( vips_image_copy_fields( mask, in ) ) + if( vips_image_pipelinev( mask, + VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ) ) return( -1 ); - vips_demand_hint( mask, VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ); mask->Bands = 1; mask->BandFmt = VIPS_FORMAT_UCHAR; diff --git a/libvips/morphology/Makefile.am b/libvips/morphology/Makefile.am index e4b7e75b..eb75e9c5 100644 --- a/libvips/morphology/Makefile.am +++ b/libvips/morphology/Makefile.am @@ -2,9 +2,8 @@ noinst_LTLIBRARIES = libmorphology.la libmorphology_la_SOURCES = \ im_cntlines.c \ - morphology.c\ + hitmiss.c\ im_rank.c \ - im_rank_image.c \ im_zerox.c \ morph_dispatch.c \ im_label_regions.c diff --git a/libvips/morphology/morphology.c b/libvips/morphology/hitmiss.c similarity index 85% rename from libvips/morphology/morphology.c rename to libvips/morphology/hitmiss.c index 9882e6fc..0abbe60e 100644 --- a/libvips/morphology/morphology.c +++ b/libvips/morphology/hitmiss.c @@ -766,39 +766,6 @@ im_erode_raw( IMAGE *in, IMAGE *out, INTMASK *mask ) return( morphology( in, out, mask, ERODE ) ); } -/** - * im_dilate: - * @in: input image - * @out: output image - * @mask: mask - * - * im_dilate() performs a morphological dilate operation on @in using @mask as a - * structuring element. The output pixel is set if any part of the mask - * matches, that is, the result is the logical OR of the selected input pixels. - * - * The image should have 0 (black) for no object and 255 - * (non-zero) for an object. Note that this is the reverse of the usual - * convention for these operations, but more convenient when combined with the - * boolean operators im_andimage() and friends. The output image is the same - * size as the input image: edge pxels are made by expanding the input image - * as necessary in the manner of im_conv(). - * - * Mask coefficients can be either 0 (for object) or 255 (for background) - * or 128 (for do not care). The origin of the mask is at location - * (m.xsize / 2, m.ysize / 2), integer division. All algorithms have been - * based on the book "Fundamentals of Digital Image Processing" by A. Jain, - * pp 384-388, Prentice-Hall, 1989. - * - * See the boolean operations im_andimage(), im_orimage() and im_eorimage() - * for analogues of the usual set difference and set union operations. - * - * Operations are performed using the processor's vector unit, - * if possible. Disable this with --vips-novector or IM_NOVECTOR. - * - * See also: im_erode(). - * - * Returns: 0 on success, -1 on error - */ int im_dilate( IMAGE *in, IMAGE *out, INTMASK *mask ) { @@ -817,39 +784,6 @@ im_dilate( IMAGE *in, IMAGE *out, INTMASK *mask ) return( 0 ); } -/** - * im_erode: - * @in: input image - * @out: output image - * @mask: mask - * - * im_erode() performs a morphological erode operation on @in using @mask as a - * structuring element. The whole mask must match for the output pixel to be - * set, that is, the result is the logical AND of the selected input pixels. - * - * The image should have 0 (black) for no object and 255 - * (non-zero) for an object. Note that this is the reverse of the usual - * convention for these operations, but more convenient when combined with the - * boolean operators im_andimage() and friends. The output image is the same - * size as the input image: edge pxels are made by expanding the input image - * as necessary in the manner of im_conv(). - * - * Mask coefficients can be either 0 (for object) or 255 (for background) - * or 128 (for do not care). The origin of the mask is at location - * (m.xsize / 2, m.ysize / 2), integer division. All algorithms have been - * based on the book "Fundamentals of Digital Image Processing" by A. Jain, - * pp 384-388, Prentice-Hall, 1989. - * - * See the boolean operations im_andimage(), im_orimage() and im_eorimage() - * for analogues of the usual set difference and set union operations. - * - * Operations are performed using the processor's vector unit, - * if possible. Disable this with --vips-novector or IM_NOVECTOR. - * - * See also: im_dilate(). - * - * Returns: 0 on success, -1 on error - */ int im_erode( IMAGE *in, IMAGE *out, INTMASK *mask ) { diff --git a/libvips/morphology/im_rank_image.c b/libvips/morphology/im_rank_image.c deleted file mode 100644 index 47f7b412..00000000 --- a/libvips/morphology/im_rank_image.c +++ /dev/null @@ -1,356 +0,0 @@ -/* Sort a set of images, pixelwise, and pick out the index at each point. - * - * 19/8/03 - * - from im_maxvalue(), via im_gbandjoin() - * 10/11/10 - * - gtkdoc - * - cleanups - * - any mix of formats and bands - */ - -/* - - 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 -#include - -/* Parameters. - */ -typedef struct Rank { - IMAGE **in; /* Array of input images, NULL-terminated */ - IMAGE *out; - int n; /* Number of input images */ - int index; /* Pick out this one */ -} Rank; - -/* Make a Rank struct. - */ -static Rank * -rank_new( IMAGE **in, IMAGE *out, int n, int index ) -{ - Rank *rank; - IMAGE **t; - - if( !(rank = IM_NEW( out, Rank )) ) - return( NULL ); - - rank->n = n; - rank->index = index; - rank->out = out; - if( !(t = IM_ARRAY( out, n, IMAGE * )) || - !(rank->in = IM_ARRAY( out, n + 1, IMAGE * )) ) - return( NULL ); - - /* Cast inputs up to a common format, common bands. - */ - if( im_open_local_array( out, t, n, "im_rank_image", "p" ) || - im_open_local_array( out, rank->in, n, "im_rank_image", "p" ) || - im__bandalike_vec( "im_rank_image", in, t, n ) || - im__formatalike_vec( t, rank->in, n ) ) - return( NULL ); - rank->in[n] = NULL; - - return( rank ); -} - -/* Our sequence value. - */ -typedef struct { - Rank *rank; - - REGION **ir; /* Input regions */ - VipsPel **pts; /* Per-input region data pointer */ - VipsPel *sort; /* Sort pixels here */ -} RankSequence; - -/* Free a sequence value. - */ -static int -rank_stop( void *vseq, void *a, void *b ) -{ - RankSequence *seq = (RankSequence *) vseq; - Rank *rank = (Rank *) b; - - if( seq->ir ) { - int i; - - for( i = 0; i < rank->n; i++ ) - IM_FREEF( im_region_free, seq->ir[i] ); - } - - return( 0 ); -} - -/* Make a sequence value. - */ -static void * -rank_start( IMAGE *out, void *a, void *b ) -{ - IMAGE **in = (IMAGE **) a; - Rank *rank = (Rank *) b; - - RankSequence *seq; - int i; - - if( !(seq = IM_NEW( out, RankSequence )) ) - return( NULL ); - - /* Init! - */ - seq->rank = rank; - seq->ir = NULL; - seq->pts = NULL; - - /* Attach regions and arrays. - */ - seq->ir = IM_ARRAY( out, rank->n + 1, REGION * ); - seq->pts = IM_ARRAY( out, rank->n + 1, VipsPel * ); - seq->sort = IM_ARRAY( out, - rank->n * IM_IMAGE_SIZEOF_ELEMENT( in[0] ), VipsPel ); - if( !seq->ir || !seq->pts || !seq->sort ) { - rank_stop( seq, in, rank ); - return( NULL ); - } - - for( i = 0; i < rank->n; i++ ) - if( !(seq->ir[i] = im_region_create( in[i] )) ) { - rank_stop( seq, in, rank ); - return( NULL ); - } - seq->ir[i] = NULL; - - return( (void *) seq ); -} - -/* Special-case max and min (rather common). - */ -#define FIND_IM_MAX( TYPE ) { \ - for( x = 0; x < sz; x++ ) { \ - TYPE top = ((TYPE *) seq->pts[0])[x]; \ - \ - for( i = 1; i < rank->n; i++ ) { \ - TYPE v = ((TYPE *) seq->pts[i])[x]; \ - \ - if( v > top ) \ - top = v; \ - } \ - \ - ((TYPE *) q)[x] = top; \ - } \ -} - -#define FIND_IM_MIN( TYPE ) { \ - for( x = 0; x < sz; x++ ) { \ - TYPE bot = ((TYPE *) seq->pts[0])[x]; \ - \ - for( i = 1; i < rank->n; i++ ) { \ - TYPE v = ((TYPE *) seq->pts[i])[x]; \ - \ - if( v < bot ) \ - bot = v; \ - } \ - \ - ((TYPE *) q)[x] = bot; \ - } \ -} - -/* Inner loop for sorting. - */ -#define FIND_IM_RANK( TYPE ) { \ - TYPE *sort = (TYPE *) seq->sort; \ - \ - for( x = 0; x < sz; x++ ) { \ - for( i = 0; i < rank->n; i++ ) { \ - TYPE v = ((TYPE *) seq->pts[i])[x]; \ - \ - /* Search for element >v. - */\ - for( j = 0; j < i; j++ ) \ - if( sort[j] > v ) \ - break; \ - \ - /* Move remaining elements down. - */ \ - for( k = i; k > j; k-- ) \ - sort[k] = sort[k - 1]; \ - \ - /* Insert this element. - */ \ - sort[j] = v; \ - } \ - \ - ((TYPE *) q)[x] = sort[rank->index]; \ - } \ -} - -#define SWITCH( OPERATION ) \ - switch( rank->out->BandFmt ) { \ - case IM_BANDFMT_UCHAR: OPERATION( unsigned char ); break; \ - case IM_BANDFMT_CHAR: OPERATION( signed char ); break; \ - case IM_BANDFMT_USHORT: OPERATION( unsigned short ); break; \ - case IM_BANDFMT_SHORT: OPERATION( signed short ); break; \ - case IM_BANDFMT_UINT: OPERATION( unsigned int ); break; \ - case IM_BANDFMT_INT: OPERATION( signed int ); break; \ - case IM_BANDFMT_FLOAT: OPERATION( float ); break; \ - case IM_BANDFMT_DOUBLE: OPERATION( double ); break; \ - \ - default: \ - assert( 0 ); \ - } - -static int -rank_gen( REGION *or, void *vseq, void *a, void *b ) -{ - RankSequence *seq = (RankSequence *) vseq; - Rank *rank = (Rank *) b; - Rect *r = &or->valid; - int le = r->left; - int to = r->top; - int bo = IM_RECT_BOTTOM(r); - int sz = IM_REGION_N_ELEMENTS( or ); - - int x, y, i, j, k; - - /* Prepare each input area. - */ - for( i = 0; i < rank->n; i++ ) - if( im_prepare( seq->ir[i], r ) ) - return( -1 ); - - /* Loop over output! - */ - for( y = to; y < bo; y++ ) { - VipsPel *q = IM_REGION_ADDR( or, le, y ); - - for( i = 0; i < rank->n; i++ ) - seq->pts[i] = IM_REGION_ADDR( seq->ir[i], le, y ); - - /* Special-case max and min. - */ - if( rank->index == 0 ) - SWITCH( FIND_IM_MIN ) - else if( rank->index == rank->n - 1 ) - SWITCH( FIND_IM_MAX ) - else - SWITCH( FIND_IM_RANK ) - } - - return( 0 ); -} - -/** - * im_rank_image: - * @in: input image array - * @out: output image - * @n: number of input images - * @index: select pixel - * - * im_rank_image() sorts the images @in pixel-wise, then outputs an - * image in which each pixel is selected from the sorted list by the - * @index parameter. For example, if @index - * is zero, then each output pixel will be the minimum of all the - * corresponding input pixels. - * - * It works for any uncoded, non-complex image type. Images are cast up to the - * smallest common-format. - * - * Any image can have either 1 band or n bands, where n is the same for all - * the non-1-band images. Single band images are then effectively copied to - * make n-band images. - * - * See also: im_rank(), im_maxvalue(). - * - * Returns: 0 on success, -1 on error - */ -int -im_rank_image( IMAGE **in, IMAGE *out, int n, int index ) -{ - int i; - Rank *rank; - - if( n < 1 ) { - im_error( "im_rank_image", "%s", _( "zero input images!" ) ); - return( -1 ); - } - if( index < 0 || index > n - 1 ) { - im_error( "im_rank_image", - _( "index should be in range 0 - %d" ), n - 1 ); - return( -1 ); - } - if( im_poutcheck( out ) ) - return( -1 ); - for( i = 0; i < n; i++ ) - if( im_pincheck( in[i] ) || - im_check_uncoded( "im_rank_image", in[i] ) || - im_check_noncomplex( "im_rank_image", in[i] ) || - im_check_size_same( "im_rank_image", in[i], in[0] ) ) - return( -1 ); - - if( !(rank = rank_new( in, out, n, index )) || - im_cp_desc_array( out, rank->in ) || - im_demand_hint_array( out, IM_THINSTRIP, rank->in ) || - im_generate( out, - rank_start, rank_gen, rank_stop, rank->in, rank ) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_maxvalue: - * @in: input image array - * @out: output image - * @n: number of input images - * - * im_maxvalue() is a convenience function over im_rank_image(). It sorts the - * input images pixel-wise, then outputs an image - * in which each pixel is the maximum of all the corresponding input images. - * It works for any uncoded, non-complex image type. Images are cast up to the - * smallest common-format. - * - * Any image can have either 1 band or n bands, where n is the same for all - * the non-1-band images. Single band images are then effectively copied to - * make n-band images. - * - * See also: im_rank_image(). - * - * Returns: 0 on success, -1 on error - */ -int -im_maxvalue( IMAGE **in, IMAGE *out, int n ) -{ - return( im_rank_image( in, out, n, n - 1 ) ); -} diff --git a/libvips/resample/affine.c b/libvips/resample/affine.c index 76789ba6..6d07d1d2 100644 --- a/libvips/resample/affine.c +++ b/libvips/resample/affine.c @@ -381,6 +381,7 @@ vips_affine_build( VipsObject *object ) VipsImage *in; gboolean repack; + VipsDemandStyle hint; int window_size; int window_offset; double edge; @@ -479,21 +480,19 @@ vips_affine_build( VipsObject *object ) return( -1 ); in = t[1]; - if( vips_image_copy_fields( resample->out, in ) ) - return( -1 ); - - resample->out->Xsize = affine->trn.oarea.width; - resample->out->Ysize = affine->trn.oarea.height; - /* Normally SMALLTILE ... except if this is a size up/down affine. */ if( affine->trn.b == 0.0 && affine->trn.c == 0.0 ) - vips_demand_hint( resample->out, - VIPS_DEMAND_STYLE_FATSTRIP, in, NULL ); + hint = VIPS_DEMAND_STYLE_FATSTRIP; else - vips_demand_hint( resample->out, - VIPS_DEMAND_STYLE_SMALLTILE, in, NULL ); + hint = VIPS_DEMAND_STYLE_SMALLTILE; + + if( vips_image_pipelinev( resample->out, hint, in, NULL ) ) + return( -1 ); + + resample->out->Xsize = affine->trn.oarea.width; + resample->out->Ysize = affine->trn.oarea.height; /* Generate! */ diff --git a/libvips/resample/quadratic.c b/libvips/resample/quadratic.c index 5ab0158b..5fedd545 100644 --- a/libvips/resample/quadratic.c +++ b/libvips/resample/quadratic.c @@ -251,7 +251,11 @@ vips_quadratic_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_quadratic_parent_class )->build( object ) ) return( -1 ); - if( vips_image_copy_fields( resample->out, resample->in ) ) + /* We have the whole of the input in memory, so we can generate any + * output. + */ + if( vips_image_pipelinev( resample->out, + VIPS_DEMAND_STYLE_ANY, resample->in, NULL ) ) return( -1 ); in = resample->in; @@ -315,12 +319,6 @@ vips_quadratic_build( VipsObject *object ) if( vips_image_wio_input( in ) ) return( -1 ); - /* We have the whole of the input in memory, so we can generate any - * output. - */ - vips_demand_hint( resample->out, - VIPS_DEMAND_STYLE_ANY, resample->in, NULL ); - if( vips_image_generate( resample->out, vips_start_one, vips_quadratic_gen, vips_stop_one, in, quadratic ) ) diff --git a/libvips/resample/shrink.c b/libvips/resample/shrink.c index cff50c7c..14ee61a1 100644 --- a/libvips/resample/shrink.c +++ b/libvips/resample/shrink.c @@ -336,15 +336,13 @@ vips_shrink_build( VipsObject *object ) shrink->yshrink == 1.0 ) return( vips_image_write( resample->in, resample->out ) ); - if( vips_image_copy_fields( resample->out, resample->in ) ) - return( -1 ); - /* 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. */ - vips_demand_hint( resample->out, - VIPS_DEMAND_STYLE_THINSTRIP, resample->in, NULL ); + if( vips_image_pipelinev( resample->out, + VIPS_DEMAND_STYLE_THINSTRIP, resample->in, NULL ) ) + return( -1 ); /* Size output. Note: we round the output width down! * diff --git a/man/header.1 b/man/header.1 index 254dfc3e..4c8404e7 100644 --- a/man/header.1 +++ b/man/header.1 @@ -17,6 +17,12 @@ the VIPS extension block: the XML defining the image metadata. You can alter this, then reattach with .B edvips(1). +.TP +.B -a, --all +Show all fields. Normally +.B header +just shows a one-line summary. + .SH EXAMPLES $ header -f Xsize ~/pics/*.v 1024 diff --git a/man/vips.1 b/man/vips.1 index 7f393174..4186470b 100644 --- a/man/vips.1 +++ b/man/vips.1 @@ -57,15 +57,15 @@ Run a vips8 operation. Operation options must follow the operation name. Get a "usage" message for an operation $ vips add - VipsAdd (add), add two images - add left right out + usage: + add left right out + add two images where: - left :: VipsImage (input) - right :: VipsImage (input) - out :: VipsImage (output) - optional arguments: - imtest :: VipsImage (input) - booltest :: gboolean (input) + left - Left-hand image argument, input VipsImage + right - Right-hand image argument, input VipsImage + out - Output image, output VipsImage + operation flags: sequential-unbuffered + add: too few arguments .SH RETURN VALUE returns 0 on success and non-zero on error. diff --git a/man/vipsthumbnail.1 b/man/vipsthumbnail.1 index abf67d1c..4dfee8bd 100644 --- a/man/vipsthumbnail.1 +++ b/man/vipsthumbnail.1 @@ -40,7 +40,8 @@ Set the output thumbnail size to .B N x .B N -pixels. The image is shrunk so that it just fits within this area, Images +pixels. You can use MxN to specify a rectangular bounding box. +The image is shrunk so that it just fits within this area, images which are smaller than this are expanded. .TP @@ -55,7 +56,7 @@ The default value is meaning JPEG output, with .B tn_ prepended. You can add format options too, for example -.B tn_%s.jpg:20 +.B tn_%s.jpg[Q=20] will write JPEG images with Q set to 20. .TP diff --git a/tools/Makefile.am b/tools/Makefile.am index 5031b47f..90ae9a41 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -26,10 +26,10 @@ bin_SCRIPTS = \ batch_image_convert \ batch_rubber_sheet \ batch_crop \ - vips-7.36 + vips-7.37 EXTRA_DIST = \ - vips-7.36 \ + vips-7.37 \ light_correct.in \ shrink_width.in \ batch_image_convert.in \ diff --git a/tools/vips-7.36 b/tools/vips-7.37 similarity index 100% rename from tools/vips-7.36 rename to tools/vips-7.37 diff --git a/tools/vipsthumbnail.c b/tools/vipsthumbnail.c index 4c618705..9b589558 100644 --- a/tools/vipsthumbnail.c +++ b/tools/vipsthumbnail.c @@ -53,7 +53,9 @@ #include -static int thumbnail_size = 128; +static char *thumbnail_size = "128"; +static int thumbnail_width = 128; +static int thumbnail_height = 128; static char *output_format = "tn_%s.jpg"; static char *interpolator = "bilinear"; static char *export_profile = NULL; @@ -68,8 +70,8 @@ static gboolean nodelete_profile = FALSE; static GOptionEntry options[] = { { "size", 's', 0, - G_OPTION_ARG_INT, &thumbnail_size, - N_( "set thumbnail size to SIZE" ), + G_OPTION_ARG_STRING, &thumbnail_size, + N_( "shrink to SIZE or to WIDTHxHEIGHT" ), N_( "SIZE" ) }, { "output", 'o', 0, G_OPTION_ARG_STRING, &output_format, @@ -115,13 +117,14 @@ static GOptionEntry options[] = { static int calculate_shrink( int width, int height, double *residual ) { - /* We shrink to make the largest dimension equal to size. + /* Calculate the horizontal and vertical shrink we'd need to fit the + * image to the bounding box, and pick the biggest. */ - int dimension = IM_MAX( width, height ); + double horizontal = (double) width / thumbnail_width; + double vertical = (double) height / thumbnail_height; + double factor = VIPS_MAX( horizontal, vertical ); - double factor = dimension / (double) thumbnail_size; - - /* If the shrink factor is <=1.0, we need to zoom rather than shrink. + /* If the shrink factor is <= 1.0, we need to zoom rather than shrink. * Just set the factor to 1 in this case. */ double factor2 = factor < 1.0 ? 1.0 : factor; @@ -130,14 +133,15 @@ calculate_shrink( int width, int height, double *residual ) */ int shrink = floor( factor2 ); - /* Size after int shrink. - */ - int isize = floor( dimension / shrink ); + if( residual ) { + /* Width after int shrink. + */ + int iwidth = width / shrink; - /* Therefore residual scale factor is. - */ - if( residual ) - *residual = thumbnail_size / (double) isize; + /* Therefore residual scale factor is. + */ + *residual = (width / factor) / iwidth; + } return( shrink ); } @@ -555,6 +559,15 @@ main( int argc, char **argv ) g_option_context_free( context ); + if( sscanf( thumbnail_size, "%d x %d", + &thumbnail_width, &thumbnail_height ) != 2 ) { + if( sscanf( thumbnail_size, "%d", &thumbnail_width ) != 1 ) + vips_error_exit( "unable to parse size \"%s\" -- " + "use eg. 128 or 200x300", thumbnail_size ); + + thumbnail_height = thumbnail_width; + } + for( i = 1; i < argc; i++ ) { /* Hang resources for this processing off this. */