From a31c7d8cafaf2577f652c26daca53e851e4f93a0 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 13 Nov 2011 17:45:27 +0000 Subject: [PATCH] added math2, various 2ary math ops --- ChangeLog | 3 +- TODO | 18 +- libvips/arithmetic/Makefile.am | 2 +- libvips/arithmetic/arithmetic.c | 4 + libvips/arithmetic/math2.c | 427 +++++++++++++++++++++++++++ libvips/arithmetic/power.c | 451 ----------------------------- libvips/deprecated/vips7compat.c | 43 +++ libvips/include/vips/arithmetic.h | 27 +- libvips/include/vips/enumtypes.h | 2 + libvips/include/vips/vips7compat.h | 5 + libvips/iofuncs/enumtypes.c | 18 ++ 11 files changed, 538 insertions(+), 462 deletions(-) create mode 100644 libvips/arithmetic/math2.c delete mode 100644 libvips/arithmetic/power.c diff --git a/ChangeLog b/ChangeLog index aa1416c7..01b7dc98 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,7 +10,8 @@ im_abs(), im_sign(), im_max(), im_maxpos(), im_deviate(), im_divide(), im_multiply(), im_stats(), im_measure(), im_recomb(), im_floor(), im_ceil(), im_rint(), im_equal*(), im_notequal*(), im_less*(), im_lesseq*(), im_more*(), - im_moreeq*(), im_remainder*(), im_and*(), im_or*(), im_eor*(), im_shift*() + im_moreeq*(), im_remainder*(), im_and*(), im_or*(), im_eor*(), im_shift*(), + im_pow*(), im_exp*() redone as classes - added argument priorites to help control arg ordering - generate has a 'stop' param to signal successful early termination diff --git a/TODO b/TODO index be21f132..8e92d6fd 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,21 @@ +- various failures in make check + + + +- move ifthenelse / blend to conversion? or arithmetic? + + + + +- try an area operation, like conv, VipsArea? oops no + + + + - test _O_TEMPORARY thing on Windows -- move power to unary const - - - - avg/dev etc. should uncode images? eg. labq2lab etc. diff --git a/libvips/arithmetic/Makefile.am b/libvips/arithmetic/Makefile.am index 781d1409..96eb7b22 100644 --- a/libvips/arithmetic/Makefile.am +++ b/libvips/arithmetic/Makefile.am @@ -37,7 +37,7 @@ libarithmetic_la_SOURCES = \ add.c \ linear.c \ invert.c \ - power.c \ + math2.c \ round.c INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/arithmetic/arithmetic.c b/libvips/arithmetic/arithmetic.c index ab7758ab..0e733ac9 100644 --- a/libvips/arithmetic/arithmetic.c +++ b/libvips/arithmetic/arithmetic.c @@ -524,6 +524,8 @@ vips_arithmetic_operation_init( void ) extern GType vips_remainder_const_get_type( void ); extern GType vips_boolean_get_type( void ); extern GType vips_boolean_const_get_type( void ); + extern GType vips_math2_get_type( void ); + extern GType vips_math2_const_get_type( void ); vips_add_get_type(); vips_subtract_get_type(); @@ -548,4 +550,6 @@ vips_arithmetic_operation_init( void ) vips_remainder_const_get_type(); vips_boolean_get_type(); vips_boolean_const_get_type(); + vips_math2_get_type(); + vips_math2_const_get_type(); } diff --git a/libvips/arithmetic/math2.c b/libvips/arithmetic/math2.c new file mode 100644 index 00000000..64784276 --- /dev/null +++ b/libvips/arithmetic/math2.c @@ -0,0 +1,427 @@ +/* math2.c --- 2ary math funcs + * + * Copyright: 1990, N. Dessipris + * + * Author: Nicos Dessipris + * Written on: 02/05/1990 + * Modified on: + * 10/12/93 JC + * - now reports total number of x/0, rather than each one. + * 1/2/95 JC + * - rewritten for PIO with im_wrapone() + * - incorrect complex code removed + * - /0 reporting removed for ease of programming + * 15/4/97 JC + * - return( 0 ) missing, oops! + * 6/7/98 JC + * - _vec form added + * 30/8/09 + * - gtkdoc + * - tiny cleanups + * 20/9/09 + * - im_powtra() adapated to make math2.c + * 12/11/11 + * - redone as a class + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "binary.h" +#include "unaryconst.h" + +/** + * VipsMath2: + * @left: left-hand input #VipsImage + * @right: right-hand input #VipsImage + * @out: output #VipsImage + * @math2: math operation to perform + * + * This operation calculates a 2-ary maths operation on a pair of images + * and writes the result to @out. The images may have any + * non-complex format. @out is float except in the case that either of @left + * or @right are double, in which case @out is also double. + * + * It detects division by zero, setting those pixels to zero in the output. + * Beware: it does this silently! + * + * If the images differ in size, the smaller image is enlarged to match the + * larger by adding zero pixels along the bottom and right. + * + * If the number of bands differs, one of the images + * must have one band. In this case, an n-band image is formed from the + * one-band image by joining n copies of the one-band image together, and then + * the two n-band images are operated upon. + * + * The two input images are cast up to the smallest common type (see table + * Smallest common format in + * arithmetic), and that format is the + * result type. + * + * See also: #VipsMath2Const. + */ + +typedef struct _VipsMath2 { + VipsBinary parent_instance; + + VipsOperationMath2 math2; + +} VipsMath2; + +typedef VipsBinaryClass VipsMath2Class; + +G_DEFINE_TYPE( VipsMath2, vips_math2, VIPS_TYPE_BINARY ); + +static int +vips_math2_build( VipsObject *object ) +{ + VipsBinary *binary = (VipsBinary *) object; + + if( binary->left && + vips_check_noncomplex( "VipsMath2", binary->left ) ) + return( -1 ); + if( binary->right && + vips_check_noncomplex( "VipsMath2", binary->right ) ) + return( -1 ); + + if( VIPS_OBJECT_CLASS( vips_math2_parent_class )->build( object ) ) + return( -1 ); + + return( 0 ); +} + +#define LOOP( IN, OUT, OP ) { \ + IN *p1 = (IN *) in[0]; \ + IN *p2 = (IN *) in[1]; \ + OUT *q = (OUT *) out; \ + \ + for( x = 0; x < sz; x++ ) \ + OP( q[x], p1[x], p2[x] ); \ +} + +#define SWITCH( L, OP ) \ + switch( vips_image_get_format( im ) ) { \ + case VIPS_FORMAT_UCHAR: \ + L( unsigned char, float, OP ); break; \ + case VIPS_FORMAT_CHAR: \ + L( signed char, float, OP ); break; \ + case VIPS_FORMAT_USHORT: \ + L( unsigned short, float, OP ); break; \ + case VIPS_FORMAT_SHORT: \ + L( signed short, float, OP ); break; \ + case VIPS_FORMAT_UINT: \ + L( unsigned int, float, OP ); break; \ + case VIPS_FORMAT_INT: \ + L( signed int, float, OP ); break; \ + case VIPS_FORMAT_FLOAT: \ + L( float, float, OP ); break; \ + case VIPS_FORMAT_DOUBLE: \ + L( double, double, OP ); break;\ + \ + default: \ + g_assert( 0 ); \ + } + +#define POW( Y, X, E ) { \ + double left = (double) (X); \ + double right = (double) (E); \ + \ + if( left == 0.0 && right < 0.0 ) \ + /* Division by zero! Difficult to report tho' \ + */ \ + (Y) = 0.0; \ + else \ + (Y) = pow( left, right ); \ +} + +#define WOP( Y, X, E ) POW( Y, E, X ) + +static void +vips_math2_buffer( VipsArithmetic *arithmetic, + PEL *out, PEL **in, int width ) +{ + VipsMath2 *math2 = (VipsMath2 *) arithmetic; + VipsImage *im = arithmetic->ready[0]; + const int sz = width * vips_image_get_bands( im ); + + int x; + + switch( math2->math2 ) { + case VIPS_OPERATION_MATH2_POW: SWITCH( LOOP, POW ); break; + case VIPS_OPERATION_MATH2_WOP: SWITCH( LOOP, WOP ); break; + + default: + g_assert( 0 ); + } +} + +/* Save a bit of typing. + */ +#define UC VIPS_FORMAT_UCHAR +#define C VIPS_FORMAT_CHAR +#define US VIPS_FORMAT_USHORT +#define S VIPS_FORMAT_SHORT +#define UI VIPS_FORMAT_UINT +#define I VIPS_FORMAT_INT +#define F VIPS_FORMAT_FLOAT +#define X VIPS_FORMAT_COMPLEX +#define D VIPS_FORMAT_DOUBLE +#define DX VIPS_FORMAT_DPCOMPLEX + +/* Type promotion for math2. Keep in sync with math2_buffer() above. + */ +static int vips_bandfmt_math2[10] = { +/* UC C US S UI I F X D DX */ + F, F, F, F, F, F, F, X, D, DX +}; + +static void +vips_math2_class_init( VipsMath2Class *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsArithmeticClass *aclass = VIPS_ARITHMETIC_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "math2"; + object_class->description = _( "pow( left, right)" ); + object_class->build = vips_math2_build; + + vips_arithmetic_set_format_table( aclass, vips_bandfmt_math2 ); + + aclass->process_line = vips_math2_buffer; + + VIPS_ARG_ENUM( class, "math2", 200, + _( "Operation" ), + _( "math to perform" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsMath2, math2 ), + VIPS_TYPE_OPERATION_MATH2, VIPS_OPERATION_MATH2_POW ); +} + +static void +vips_math2_init( VipsMath2 *math2 ) +{ +} + +int +vips_math2( VipsImage *left, VipsImage *right, VipsImage **out, + VipsOperationMath2 math2, ... ) +{ + va_list ap; + int result; + + va_start( ap, math2 ); + result = vips_call_split( "math2", ap, left, right, out, math2 ); + va_end( ap ); + + return( result ); +} + +/** + * VipsMath2Const: + * @in: input image + * @out: output image + * @c: array of constants + * @math2: math operation to perform + * + * This operation calculates various 2-ary maths operations on an image and + * an array of constants and writes the result to @out. + * The image may have any + * non-complex format. @out is float except in the case that @in + * is double, in which case @out is also double. + * + * It detects division by zero, setting those pixels to zero in the output. + * Beware: it does this silently! + * + * If the array of constants has just one element, that constant is used for + * all image bands. If the array has more than one element and they have + * the same number of elements as there are bands in the image, then + * one array element is used for each band. If the arrays have more than one + * element and the image only has a single band, the result is a many-band + * image where each band corresponds to one array element. + * + * See also: #VipsMath, #VipsDivide. + */ + + +typedef struct _VipsMath2Const { + VipsUnaryConst parent_instance; + + VipsOperationMath2 math2; + +} VipsMath2Const; + +typedef VipsUnaryConstClass VipsMath2ConstClass; + +G_DEFINE_TYPE( VipsMath2Const, + vips_math2_const, VIPS_TYPE_UNARY_CONST ); + +static int +vips_math2_const_build( VipsObject *object ) +{ + VipsUnary *unary = (VipsUnary *) object; + VipsUnaryConst *uconst = (VipsUnaryConst *) object; + + if( unary->in && + vips_check_noncomplex( "VipsMath2", unary->in ) ) + return( -1 ); + + uconst->const_format = VIPS_FORMAT_DOUBLE; + + if( VIPS_OBJECT_CLASS( vips_math2_const_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +#define LOOPC( IN, OUT, OP ) { \ + IN *p = (IN *) in[0]; \ + OUT *q = (OUT *) out; \ + double *c = (double *) uconst->c_ready; \ + \ + for( i = 0, x = 0; x < width; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + OP( q[i], p[i], c[b] ); \ +} + +static void +vips_math2_const_buffer( VipsArithmetic *arithmetic, + PEL *out, PEL **in, int width ) +{ + VipsUnaryConst *uconst = (VipsUnaryConst *) arithmetic; + VipsMath2Const *math2 = (VipsMath2Const *) arithmetic; + VipsImage *im = arithmetic->ready[0]; + int bands = im->Bands; + + int i, x, b; + + switch( math2->math2 ) { + case VIPS_OPERATION_MATH2_POW: SWITCH( LOOPC, POW ); break; + case VIPS_OPERATION_MATH2_WOP: SWITCH( LOOPC, WOP ); break; + + default: + g_assert( 0 ); + } +} + +static void +vips_math2_const_class_init( VipsMath2ConstClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsArithmeticClass *aclass = VIPS_ARITHMETIC_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "math2_const"; + object_class->description = _( "pow( @in, @c )" ); + object_class->build = vips_math2_const_build; + + vips_arithmetic_set_format_table( aclass, vips_bandfmt_math2 ); + + aclass->process_line = vips_math2_const_buffer; + + VIPS_ARG_ENUM( class, "math2", 200, + _( "Operation" ), + _( "math to perform" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsMath2, math2 ), + VIPS_TYPE_OPERATION_MATH2, VIPS_OPERATION_MATH2_POW ); +} + +static void +vips_math2_const_init( VipsMath2Const *math2_const ) +{ +} + +static int +vips_math2_constv( VipsImage *in, VipsImage **out, + VipsOperationMath2 math2, double *c, int n, va_list ap ) +{ + VipsArea *area_c; + double *array; + int result; + int i; + + area_c = vips_area_new_array( G_TYPE_DOUBLE, sizeof( double ), n ); + array = (double *) area_c->data; + for( i = 0; i < n; i++ ) + array[i] = c[i]; + + result = vips_call_split( "math2_const", ap, in, out, area_c ); + + vips_area_unref( area_c ); + + return( result ); +} + +int +vips_math2_const( VipsImage *in, VipsImage **out, + VipsOperationMath2 math2, double *c, int n, ... ) +{ + va_list ap; + int result; + + va_start( ap, n ); + result = vips_math2_constv( in, out, math2, c, n, ap ); + va_end( ap ); + + return( result ); +} + +int +vips_math2_const1( VipsImage *in, VipsImage **out, + VipsOperationMath2 math2, double c, ... ) +{ + va_list ap; + int result; + + va_start( ap, c ); + result = vips_math2_constv( in, out, math2, &c, 1, ap ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/arithmetic/power.c b/libvips/arithmetic/power.c deleted file mode 100644 index 4f21479d..00000000 --- a/libvips/arithmetic/power.c +++ /dev/null @@ -1,451 +0,0 @@ -/* power.c --- various power functions - * - * Copyright: 1990, N. Dessipris - * - * Author: Nicos Dessipris - * Written on: 02/05/1990 - * Modified on: - * 10/12/93 JC - * - now reports total number of x/0, rather than each one. - * 1/2/95 JC - * - rewritten for PIO with im_wrapone() - * - incorrect complex code removed - * - /0 reporting removed for ease of programming - * 15/4/97 JC - * - return( 0 ) missing, oops! - * 6/7/98 JC - * - _vec form added - * 30/8/09 - * - gtkdoc - * - tiny cleanups - * 20/9/09 - * - im_powtra() adapated to make power.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 - -/* Cast a vector of double to a vector of TYPE, clipping to a range. - */ -#define CAST_CLIP( TYPE, N, X ) { \ - TYPE *tq = (TYPE *) q; \ - \ - for( i = 0; i < n; i++ ) \ - tq[i] = (TYPE) IM_CLIP( N, p[i], X ); \ -} - -/* Cast a vector of double to a vector of TYPE. - */ -#define CAST( TYPE ) { \ - TYPE *tq = (TYPE *) q; \ - \ - for( i = 0; i < n; i++ ) \ - tq[i] = (TYPE) p[i]; \ -} - -/* Cast a vector of double to a complex vector of TYPE. - */ -#define CASTC( TYPE ) { \ - TYPE *tq = (TYPE *) q; \ - \ - for( i = 0; i < n; i++ ) { \ - tq[0] = (TYPE) p[i]; \ - tq[1] = 0; \ - tq += 2; \ - } \ -} - -/* Cast a vector of double to a passed format. - */ -static PEL * -make_pixel( IMAGE *out, VipsBandFmt fmt, int n, double *p ) -{ - PEL *q; - int i; - - if( !(q = IM_ARRAY( out, n * (im_bits_of_fmt( fmt ) >> 3), PEL )) ) - return( NULL ); - - switch( fmt ) { - case IM_BANDFMT_CHAR: - CAST_CLIP( signed char, SCHAR_MIN, SCHAR_MAX ); - break; - - case IM_BANDFMT_UCHAR: - CAST_CLIP( unsigned char, 0, UCHAR_MAX ); - break; - - case IM_BANDFMT_SHORT: - CAST_CLIP( signed short, SCHAR_MIN, SCHAR_MAX ); - break; - - case IM_BANDFMT_USHORT: - CAST_CLIP( unsigned short, 0, USHRT_MAX ); - break; - - case IM_BANDFMT_INT: - CAST_CLIP( signed int, INT_MIN, INT_MAX ); - break; - - case IM_BANDFMT_UINT: - CAST_CLIP( unsigned int, 0, UINT_MAX ); - break; - - case IM_BANDFMT_FLOAT: - CAST( float ); - break; - - case IM_BANDFMT_DOUBLE: - CAST( double ); - break; - - case IM_BANDFMT_COMPLEX: - CASTC( float ); - break; - - case IM_BANDFMT_DPCOMPLEX: - CASTC( double ); - break; - - default: - g_assert( 0 ); - } - - return( q ); -} - -int -im__arith_binary_const( const char *domain, - IMAGE *in, IMAGE *out, - int n, double *c, VipsBandFmt vfmt, - int format_table[10], - im_wrapone_fn fn1, im_wrapone_fn fnn ) -{ - PEL *vector; - - if( im_piocheck( in, out ) || - im_check_vector( domain, n, in ) || - im_check_uncoded( domain, in ) ) - return( -1 ); - if( im_cp_desc( out, in ) ) - return( -1 ); - out->BandFmt = format_table[in->BandFmt]; - - /* Some operations need the vector in the input type (eg. - * im_equal_vec() where the output type is always uchar and is useless - * for comparisons), some need it in the output type (eg. - * im_andimage_vec() where we want to get the double to an int so we - * can do bitwise-and without having to cast for each pixel), some - * need a fixed type (eg. im_powtra_vec(), where we want to keep it as - * double). - * - * Therefore pass in the desired vector type as a param. - */ - if( !(vector = make_pixel( out, vfmt, n, c )) ) - return( -1 ); - - /* Band-up the input image if we have a >1 vector and - * a 1-band image. - */ - if( n > 1 && out->Bands == 1 ) { - IMAGE *t; - - if( !(t = im_open_local( out, domain, "p" )) || - im__bandup( domain, in, t, n ) ) - return( -1 ); - - in = t; - } - - if( n == 1 ) { - if( im_wrapone( in, out, fn1, vector, in ) ) - return( -1 ); - } - else { - if( im_wrapone( in, out, fnn, vector, in ) ) - return( -1 ); - } - - return( 0 ); -} - -/* Operator with a single constant. - */ -#define CONST1( IN, OUT, FUN ) { \ - OUT *tq = (OUT *) q; \ - IN *tp = (IN *) p; \ - \ - for( i = 0; i < ne; i++ ) \ - FUN( tq[i], tp[i], c ); \ -} - -/* Operator with a single constant on a buffer. - */ -#define CONST1_BUFFER( FUN ) \ -static void \ -FUN ## 1_buffer( PEL *p, PEL *q, int n, double *tc, IMAGE *im ) \ -{ \ - /* Complex just doubles the size. \ - */ \ - const int ne = n * im->Bands * \ - (vips_bandfmt_iscomplex( im->BandFmt ) ? 2 : 1); \ - const double c = tc[0]; \ - \ - int i; \ - \ - switch( im->BandFmt ) { \ - case IM_BANDFMT_CHAR: \ - CONST1( signed char, float, FUN ); break; \ - case IM_BANDFMT_UCHAR: \ - CONST1( unsigned char, float, FUN ); break; \ - case IM_BANDFMT_SHORT: \ - CONST1( signed short, float, FUN ); break; \ - case IM_BANDFMT_USHORT: \ - CONST1( unsigned short, float, FUN ); break; \ - case IM_BANDFMT_INT: \ - CONST1( signed int, float, FUN ); break; \ - case IM_BANDFMT_UINT: \ - CONST1( unsigned int, float, FUN ); break; \ - case IM_BANDFMT_FLOAT: \ - CONST1( float, float, FUN ); break; \ - case IM_BANDFMT_COMPLEX: \ - CONST1( float, float, FUN ); break; \ - case IM_BANDFMT_DOUBLE: \ - CONST1( double, double, FUN ); break; \ - case IM_BANDFMT_DPCOMPLEX: \ - CONST1( double, double, FUN ); break; \ - \ - default: \ - g_assert( 0 ); \ - } \ -} - -/* Operator with one constant per band. - */ -#define CONSTN( IN, OUT, FUN ) { \ - OUT *tq = (OUT *) q; \ - IN *tp = (IN *) p; \ - \ - for( i = 0, x = 0; x < n; x++ ) \ - for( b = 0; b < bands; b++, i++ ) \ - FUN( tq[i], tp[i], tc[b] ); \ -} - -/* Operator with one constant per band on a buffer. - */ -#define CONSTN_BUFFER( FUN ) \ -static void \ -FUN ## n_buffer( PEL *p, PEL *q, int n, double *tc, IMAGE *im ) \ -{ \ - const int bands = im->Bands; \ - \ - int i, x, b; \ - \ - switch( im->BandFmt ) { \ - case IM_BANDFMT_CHAR: \ - CONSTN( signed char, float, FUN ); break; \ - case IM_BANDFMT_UCHAR: \ - CONSTN( unsigned char, float, FUN ); break; \ - case IM_BANDFMT_SHORT: \ - CONSTN( signed short, float, FUN ); break; \ - case IM_BANDFMT_USHORT: \ - CONSTN( unsigned short, float, FUN ); break; \ - case IM_BANDFMT_INT: \ - CONSTN( signed int, float, FUN ); break; \ - case IM_BANDFMT_UINT: \ - CONSTN( unsigned int, float, FUN ); break; \ - case IM_BANDFMT_FLOAT: \ - CONSTN( float, float, FUN ); break; \ - case IM_BANDFMT_COMPLEX: \ - CONSTN( float, float, FUN ); break; \ - case IM_BANDFMT_DOUBLE: \ - CONSTN( double, double, FUN ); break; \ - case IM_BANDFMT_DPCOMPLEX: \ - CONSTN( double, double, FUN ); break; \ - \ - default: \ - g_assert( 0 ); \ - } \ -} - -#define POW( Y, X, E ) { \ - double x = (double) (X); \ - double e = (double) (E); \ - \ - if( x == 0.0 && e < 0.0 ) \ - /* Division by zero! Difficult to report tho' \ - */ \ - (Y) = 0.0; \ - else \ - (Y) = pow( x, e ); \ -} - -CONST1_BUFFER( POW ) - -CONSTN_BUFFER( POW ) - -/* Save a bit of typing. - */ -#define F IM_BANDFMT_FLOAT -#define X IM_BANDFMT_COMPLEX -#define D IM_BANDFMT_DOUBLE -#define DX IM_BANDFMT_DPCOMPLEX - -/* Type conversions for boolean. - */ -static int bandfmt_power[10] = { -/* UC C US S UI I F X D DX */ - F, F, F, F, F, F, F, X, D, DX, -}; - -/** - * im_powtra_vec: - * @in: input #IMAGE - * @out: output #IMAGE - * @n: number of elements in array - * @e: array of constants - * - * im_powtra_vec() transforms element x of input to - * pow(x, @b) in output. - * It detects division by zero, setting those pixels to zero in the output. - * Beware: it does this silently! - * - * If the array of constants has one element, that constant is used for each - * image band. If the array has more than one element, it must have the same - * number of elements as there are bands in the image, and one array element - * is used for each band. - * - * See also: im_logtra(), im_expntra_vec() - * - * Returns: 0 on success, -1 on error - */ -int -im_powtra_vec( IMAGE *in, IMAGE *out, int n, double *c ) -{ - if( im_check_noncomplex( "im_powtra_vec", in ) ) - return( -1 ); - - return( im__arith_binary_const( "im_powtra_vec", - in, out, n, c, IM_BANDFMT_DOUBLE, - bandfmt_power, - (im_wrapone_fn) POW1_buffer, - (im_wrapone_fn) POWn_buffer ) ); -} - -/** - * im_powtra: - * @in: input #IMAGE - * @out: output #IMAGE - * @e: exponent - * - * im_powtra() transforms element x of input to - * pow(x, @e) in output. - * It detects division by zero, setting those pixels to zero in the output. - * Beware: it does this silently! - * - * See also: im_logtra(), im_powntra_vec() - * - * Returns: 0 on success, -1 on error - */ -int -im_powtra( IMAGE *in, IMAGE *out, double e ) -{ - return( im_powtra_vec( in, out, 1, &e ) ); -} - -/* Converse of POW() above. - */ -#define POWC( Y, X, E ) POW( Y, E, X ) - -CONST1_BUFFER( POWC ) - -CONSTN_BUFFER( POWC ) - -/** - * im_expntra_vec: - * @in: input #IMAGE - * @out: output #IMAGE - * @n: number of elements in array - * @e: array of constants - * - * im_expntra_vec() transforms element x of input to - * pow(@b, x) in output. - * It detects division by zero, setting those pixels to zero in the output. - * Beware: it does this silently! - * - * If the array of constants has one element, that constant is used for each - * image band. If the array has more than one element, it must have the same - * number of elements as there are bands in the image, and one array element - * is used for each band. - * - * See also: im_logtra(), im_powtra() - * - * Returns: 0 on success, -1 on error - */ -int -im_expntra_vec( IMAGE *in, IMAGE *out, int n, double *c ) -{ - if( im_check_noncomplex( "im_expntra_vec", in ) ) - return( -1 ); - - return( im__arith_binary_const( "im_expntra_vec", - in, out, n, c, IM_BANDFMT_DOUBLE, - bandfmt_power, - (im_wrapone_fn) POWC1_buffer, - (im_wrapone_fn) POWCn_buffer ) ); -} - -/** - * im_expntra: - * @in: input #IMAGE - * @out: output #IMAGE - * @e: base - * - * im_expntra() transforms element x of input to - * pow(@e, x) in output. - * It detects division by zero, setting those pixels to zero in the output. - * Beware: it does this silently! - * - * See also: im_logtra(), im_powtra() - * - * Returns: 0 on success, -1 on error - */ -int -im_expntra( IMAGE *in, IMAGE *out, double e ) -{ - return( im_expntra_vec( in, out, 1, &e ) ); -} diff --git a/libvips/deprecated/vips7compat.c b/libvips/deprecated/vips7compat.c index f294c24e..da1b115f 100644 --- a/libvips/deprecated/vips7compat.c +++ b/libvips/deprecated/vips7compat.c @@ -1999,3 +1999,46 @@ im_shiftright( IMAGE *in, IMAGE *out, int n ) return( im_shiftright_vec( in, out, 1, &c ) ); } + +static int +vips__math2_vec( IMAGE *in, IMAGE *out, + VipsOperationMath2 math2, double *c, int n ) +{ + VipsImage *t; + + if( vips_math2_const( in, &t, math2, c, n, + NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); + return( -1 ); + } + g_object_unref( t ); + + return( 0 ); +} + +int +im_powtra_vec( VipsImage *in, VipsImage *out, int n, double *c ) +{ + return( vips__math2_vec( in, out, VIPS_OPERATION_MATH2_POW, c, n ) ); +} + +int +im_powtra( IMAGE *in, IMAGE *out, double c ) +{ + return( im_powtra_vec( in, out, 1, &c ) ); +} + +int +im_expntra_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + return( vips__math2_vec( in, out, VIPS_OPERATION_MATH2_WOP, c, n ) ); +} + +int +im_expntra( IMAGE *in, IMAGE *out, double c ) +{ + return( im_expntra_vec( in, out, 1, &c ) ); +} + diff --git a/libvips/include/vips/arithmetic.h b/libvips/include/vips/arithmetic.h index 8c5d8517..ebfb1bbf 100644 --- a/libvips/include/vips/arithmetic.h +++ b/libvips/include/vips/arithmetic.h @@ -66,6 +66,19 @@ typedef enum { VIPS_OPERATION_MATH_LAST } VipsOperationMath; +/** + * VipsOperationMath2: + * @VIPS_OPERATION_MATH_POW: pow( left, right ) + * @VIPS_OPERATION_MATH_WOP: pow( right, left ) + * + * See also: vips_math(). + */ +typedef enum { + VIPS_OPERATION_MATH2_POW, + VIPS_OPERATION_MATH2_WOP, + VIPS_OPERATION_MATH2_LAST +} VipsOperationMath2; + /** * VipsOperationRound: * @VIPS_OPERATION_ROUND_NEAREST: round to nearest @@ -180,6 +193,15 @@ int vips_boolean( VipsImage *left, VipsImage *right, VipsImage **out, int vips_boolean_const( VipsImage *in, VipsImage **out, VipsOperationBoolean boolean, double *c, int n, ... ) __attribute__((sentinel)); +int vips_math2( VipsImage *left, VipsImage *right, VipsImage **out, + VipsOperationMath2 math2, ... ) + __attribute__((sentinel)); +int vips_math2_const( VipsImage *in, VipsImage **out, + VipsOperationMath2 math2, double *c, int n, ... ) + __attribute__((sentinel)); +int vips_math2_const1( VipsImage *in, VipsImage **out, + VipsOperationMath2 math2, double c, ... ) + __attribute__((sentinel)); @@ -195,11 +217,6 @@ int im_point( VipsImage *im, VipsInterpolate *interpolate, int im_point_bilinear( VipsImage *im, double x, double y, int band, double *out ); -int im_powtra( VipsImage *in, VipsImage *out, double e ); -int im_powtra_vec( VipsImage *in, VipsImage *out, int n, double *e ); -int im_expntra( VipsImage *in, VipsImage *out, double e ); -int im_expntra_vec( VipsImage *in, VipsImage *out, int n, double *e ); - int im_cross_phase( VipsImage *a, VipsImage *b, VipsImage *out ); #ifdef __cplusplus diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index 136bdcaf..3a0b8985 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -9,6 +9,8 @@ G_BEGIN_DECLS /* enumerations from "../../../libvips/include/vips/arithmetic.h" */ GType vips_operation_math_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_OPERATION_MATH (vips_operation_math_get_type()) +GType vips_operation_math2_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_OPERATION_MATH2 (vips_operation_math2_get_type()) GType vips_operation_round_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_OPERATION_ROUND (vips_operation_round_get_type()) GType vips_operation_relational_get_type (void) G_GNUC_CONST; diff --git a/libvips/include/vips/vips7compat.h b/libvips/include/vips/vips7compat.h index 264eae41..4c6ee18b 100644 --- a/libvips/include/vips/vips7compat.h +++ b/libvips/include/vips/vips7compat.h @@ -583,6 +583,11 @@ int im_remainder( VipsImage *in1, VipsImage *in2, VipsImage *out ); int im_remainder_vec( VipsImage *in, VipsImage *out, int n, double *c ); int im_remainderconst( VipsImage *in, VipsImage *out, double c ); +int im_powtra( VipsImage *in, VipsImage *out, double e ); +int im_powtra_vec( VipsImage *in, VipsImage *out, int n, double *e ); +int im_expntra( VipsImage *in, VipsImage *out, double e ); +int im_expntra_vec( VipsImage *in, VipsImage *out, int n, double *e ); + int im_equal_vec( VipsImage *in, VipsImage *out, int n, double *c ); int im_notequal_vec( VipsImage *in, VipsImage *out, int n, double *c ); int im_less_vec( VipsImage *in, VipsImage *out, int n, double *c ); diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index 9aa49df0..b4c17584 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -111,6 +111,24 @@ vips_operation_math_get_type( void ) return( etype ); } GType +vips_operation_math2_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_OPERATION_MATH2_POW, "VIPS_OPERATION_MATH2_POW", "pow"}, + {VIPS_OPERATION_MATH2_WOP, "VIPS_OPERATION_MATH2_WOP", "wop"}, + {VIPS_OPERATION_MATH2_LAST, "VIPS_OPERATION_MATH2_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsOperationMath2", values ); + } + + return( etype ); +} +GType vips_operation_round_get_type( void ) { static GType etype = 0;