diff --git a/ChangeLog b/ChangeLog index 561cb76f..45060af8 100644 --- a/ChangeLog +++ b/ChangeLog @@ -43,6 +43,8 @@ - added im_shift*_vec() - renamed im_eor_vec() as im_eorimage_vec() for consistency, also and, or - renamed im_eorconst() as im_eorimage_const() for consistency, also and, or +- relational revised: smaller, more general, faster +- im_blend() allows many-band conditional, 1-band then/else 25/3/09 started 7.18.0 - revised version numbers diff --git a/TODO b/TODO index 66d3932c..39bcf72c 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,16 @@ -- relational next +- test im_blend, including new n-band cond + n-band a/b mode + +- do the same to im_ifthenelse + +- should we bandup then/else too? could have + + 4 band cond + 1 band then + 4 band else + + I guess + + - we have tools/ and contrib/ argh diff --git a/doc/reference/libvips-docs.sgml.in b/doc/reference/libvips-docs.sgml.in index 9906a391..c4c5e06f 100644 --- a/doc/reference/libvips-docs.sgml.in +++ b/doc/reference/libvips-docs.sgml.in @@ -17,30 +17,37 @@ API by section + + + + + + + + + API by section (no gtkdoc comments yet) - - - - + Object Hierarchy + API Index diff --git a/libvips/boolean/boolean.c b/libvips/boolean/boolean.c index a826d554..cf36b0a2 100644 --- a/libvips/boolean/boolean.c +++ b/libvips/boolean/boolean.c @@ -303,13 +303,12 @@ CONSTN_BUFFER( AND, & ) /** * im_andimage_vec: - * @in1: input #IMAGE 1 - * @in2: input #IMAGE 2 + * @in: input #IMAGE 1 * @out: output #IMAGE * @n: array length * @c: array of constants * - * This operation calculates @in1 & @c (bitwise and of image pixels with array + * This operation calculates @in & @c (bitwise and of image pixels with array * @c) and writes the result to @out. * * See also: im_andimage(), im_orimage_vec(). @@ -328,12 +327,11 @@ im_andimage_vec( IMAGE *in, IMAGE *out, int n, double *c ) /** * im_andimageconst: - * @in1: input #IMAGE 1 - * @in2: input #IMAGE 2 + * @in: input #IMAGE 1 * @out: output #IMAGE * @c: constant * - * This operation calculates @in1 & @c (bitwise and of image pixels with + * This operation calculates @in & @c (bitwise and of image pixels with * constant * @c) and writes the result to @out. * @@ -353,13 +351,12 @@ CONSTN_BUFFER( OR, | ) /** * im_orimage_vec: - * @in1: input #IMAGE 1 - * @in2: input #IMAGE 2 + * @in: input #IMAGE 1 * @out: output #IMAGE * @n: array length * @c: array of constants * - * This operation calculates @in1 | @c (bitwise or of image pixels with array + * This operation calculates @in | @c (bitwise or of image pixels with array * @c) and writes the result to @out. * * See also: im_andimage(), im_orimageconst(). @@ -378,12 +375,11 @@ im_orimage_vec( IMAGE *in, IMAGE *out, int n, double *c ) /** * im_orimageconst: - * @in1: input #IMAGE 1 - * @in2: input #IMAGE 2 + * @in: input #IMAGE 1 * @out: output #IMAGE * @c: constant * - * This operation calculates @in1 | @c (bitwise or of image pixels with + * This operation calculates @in | @c (bitwise or of image pixels with * constant * @c) and writes the result to @out. * @@ -403,13 +399,12 @@ CONSTN_BUFFER( EOR, ^ ) /** * im_eorimage_vec: - * @in1: input #IMAGE 1 - * @in2: input #IMAGE 2 + * @in: input #IMAGE 1 * @out: output #IMAGE * @n: array length * @c: array of constants * - * This operation calculates @in1 ^ @c (bitwise exclusive-or of image pixels + * This operation calculates @in ^ @c (bitwise exclusive-or of image pixels * with array * @c) and writes the result to @out. * @@ -429,12 +424,11 @@ im_eorimage_vec( IMAGE *in, IMAGE *out, int n, double *c ) /** * im_eorimageconst: - * @in1: input #IMAGE 1 - * @in2: input #IMAGE 2 + * @in: input #IMAGE 1 * @out: output #IMAGE * @c: constant * - * This operation calculates @in1 ^ @c (bitwise exclusive-or of image pixels + * This operation calculates @in ^ @c (bitwise exclusive-or of image pixels * with * constant * @c) and writes the result to @out. @@ -460,7 +454,7 @@ CONSTN_BUFFER( SHIFTL, << ) * @n: array length * @c: array of constants * - * This operation calculates @in1 << @c (left-shift by @c bits + * This operation calculates @in << @c (left-shift by @c bits * with array * @c) and writes the result to @out. * @@ -484,7 +478,7 @@ im_shiftleft_vec( IMAGE *in, IMAGE *out, int n, double *c ) * @out: output #IMAGE * @n: constant * - * This operation calculates @in1 << @n (left-shift by @n bits) + * This operation calculates @in << @n (left-shift by @n bits) * and writes the result to @out. * * See also: im_andimage(), im_orimageconst(). @@ -510,7 +504,7 @@ CONSTN_BUFFER( SHIFTR, >> ) * @n: array length * @c: array of constants * - * This operation calculates @in1 << @c (right-shift by @c bits + * This operation calculates @in << @c (right-shift by @c bits * with array * @c) and writes the result to @out. * @@ -534,7 +528,7 @@ im_shiftright_vec( IMAGE *in, IMAGE *out, int n, double *c ) * @out: output #IMAGE * @n: constant * - * This operation calculates @in1 >> @n (right-shift by @n bits) + * This operation calculates @in >> @n (right-shift by @n bits) * and writes the result to @out. * * See also: im_andimage(), im_orimageconst(). diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am index 35da35d1..769133a8 100644 --- a/libvips/include/vips/Makefile.am +++ b/libvips/include/vips/Makefile.am @@ -1,5 +1,6 @@ pkginclude_HEADERS = \ arithmetic.h \ + relational.h \ colour.h \ boolean.h \ debug.h \ diff --git a/libvips/include/vips/relational.h b/libvips/include/vips/relational.h new file mode 100644 index 00000000..b97f2220 --- /dev/null +++ b/libvips/include/vips/relational.h @@ -0,0 +1,66 @@ +/* relational.h + * + * 23/9/09 + * - from proto.h + */ + +/* + + 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 + + */ + +#ifndef IM_RELATIONAL_H +#define IM_RELATIONAL_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +int im_equal( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_notequal( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_less( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_lesseq( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_more( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_moreeq( IMAGE *in1, IMAGE *in2, IMAGE *out ); +int im_equal_vec( IMAGE *in, IMAGE *out, int n, double *c ); +int im_notequal_vec( IMAGE *in, IMAGE *out, int n, double *c ); +int im_less_vec( IMAGE *in, IMAGE *out, int n, double *c ); +int im_lesseq_vec( IMAGE *in, IMAGE *out, int n, double *c ); +int im_more_vec( IMAGE *in, IMAGE *out, int n, double *c ); +int im_moreeq_vec( IMAGE *in, IMAGE *out, int n, double *c ); +int im_equalconst( IMAGE *in, IMAGE *out, double c ); +int im_notequalconst( IMAGE *in, IMAGE *out, double c ); +int im_lessconst( IMAGE *in, IMAGE *out, double c ); +int im_lesseqconst( IMAGE *in, IMAGE *out, double c ); +int im_moreconst( IMAGE *in, IMAGE *out, double c ); +int im_moreeqconst( IMAGE *in, IMAGE *out, double c ); + +int im_ifthenelse( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out ); +int im_blend( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*IM_RELATIONAL_H*/ diff --git a/libvips/iofuncs/predicate.c b/libvips/iofuncs/predicate.c index 1ed2ed2a..8c0a1dc7 100644 --- a/libvips/iofuncs/predicate.c +++ b/libvips/iofuncs/predicate.c @@ -377,7 +377,7 @@ im_check_bands_1orn( const char *domain, IMAGE *im1, IMAGE *im2 ) (im1->Bands != 1 && im2->Bands != 1) ) { im_error( domain, "%s", _( "images must have the same number of bands, " - "or one muct be single-band" ) ); + "or one must be single-band" ) ); return( -1 ); } diff --git a/libvips/relational/im_blend.c b/libvips/relational/im_blend.c index b7639fb7..ef46aca3 100644 --- a/libvips/relational/im_blend.c +++ b/libvips/relational/im_blend.c @@ -1,22 +1,14 @@ -/* @(#) Two images as input: must match in size and type. Build an output - * @(#) image blending pixels together according to a conditional image. - * @(#) - * @(#) The conditional image can have n bands or 1 band. If n bands, then we - * @(#) choose from the two source images an element at a time. If 1 band, - * @(#) then choose from the source images a pixel at a time. - * @(#) - * @(#) int - * @(#) im_blend( c, a, b, out ) - * @(#) IMAGE *c, *a, *b; - * @(#) IMAGE *out; - * @(#) - * @(#) Returns either 0 (success) or -1 (fail). +/* im_blend.c --- blend images with a condition image * * Modified: * 15/4/05 * - from im_ifthenelse() * 8/7/05 * - oops, broken for some combinations of band differences (thanks Joe) + * 23/9/09 + * - gtkdoc comments + * - use im_check*() + * - allow many-band conditional and single-band a/b */ /* @@ -50,9 +42,8 @@ #endif /*HAVE_CONFIG_H*/ #include -#include - #include +#include #ifdef WITH_DMALLOC #include @@ -173,7 +164,7 @@ blend1_buffer( PEL *qp, PEL *c, PEL *ap, PEL *bp, int width, IMAGE *im ) cblend1( double ); break; default: - assert( 0 ); + g_assert( 0 ); } } @@ -209,7 +200,7 @@ blendn_buffer( PEL *qp, PEL *c, PEL *ap, PEL *bp, int width, IMAGE *im ) cblendn( double ); break; default: - assert( 0 ); + g_assert( 0 ); } } @@ -289,78 +280,102 @@ blend_gen( REGION *or, void *seq, void *client1, void *client2 ) return( 0 ); } +/** + * im_blend: + * @c: condition #IMAGE + * @a: then #IMAGE + * @b: else #IMAGE + * @out: output #IMAGE + * + * This operation scans the condition image @c (which must be unsigned char) + * and uses it to blend pixels from either the then image @a or the else + * image @b. 255 means @a only, 0 means @b only, and intermediate values are a + * mixture. + * + * The conditional image @c can have either 1 band, in which case entire pels + * come either from @a or @b, or n bands, where n is the number of bands in + * both @a and @b, in which case individual band elements are chosen from + * @a and @b. Finally, @c may have n bands while @a and @b are single band. In + * this case, @a and @b are copied n times to make n band images and those are + * operated upon. + * + * Images @a and @b must match exactly in size, bands and format. + * + * See also: im_ifthenelse(), im_equal(). + * + * Returns: 0 on success, -1 on error + */ int im_blend( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out ) { - IMAGE **in; - /* If a and b are both LABPACK, repack agan after the blend. */ - if( a->Coding == IM_CODING_LABQ || b->Coding == IM_CODING_LABQ ) { - IMAGE *t[3]; - int repack = a->Coding == IM_CODING_LABQ && - b->Coding == IM_CODING_LABQ; + const int repack = a->Coding == IM_CODING_LABQ && + b->Coding == IM_CODING_LABQ; - if( im_open_local_array( out, t, 3, "relational-1", "p" ) ) + IMAGE *t[5]; + IMAGE **in; + + /* Unpack LABPACK as a courtesy. + */ + if( im_open_local_array( out, t, 5, "im_blend", "p" ) ) + return( -1 ); + if( a->Coding == IM_CODING_LABQ ) { + if( im_LabQ2Lab( a, t[0] ) ) return( -1 ); - - if( a->Coding == IM_CODING_LABQ ) { - if( im_LabQ2Lab( a, t[0] ) ) - return( -1 ); - a = t[0]; - } - - if( b->Coding == IM_CODING_LABQ ) { - if( im_LabQ2Lab( b, t[1] ) ) - return( -1 ); - b = t[1]; - } - - if( repack ) - return( im_blend( c, a, b, t[2] ) || - im_Lab2LabQ( t[2], out ) ); - else - return( im_blend( c, a, b, out ) ); + a = t[0]; + } + if( b->Coding == IM_CODING_LABQ ) { + if( im_LabQ2Lab( b, t[1] ) ) + return( -1 ); + b = t[1]; } /* Check args. */ - if( a->Coding != IM_CODING_NONE || b->Coding != IM_CODING_NONE || - c->Coding != IM_CODING_NONE ) { - im_error( "im_blend", "%s", _( "images not uncoded" ) ); - return( -1 ); - } - if( a->BandFmt != b->BandFmt || - a->Bands != b->Bands ) { - im_error( "im_blend", - "%s", _( "size and format of then and else " - "must match" ) ); - return( -1 ); - } - if( c->BandFmt != IM_BANDFMT_UCHAR ) { - im_error( "im_blend", - "%s", _( "conditional image must be uchar" ) ); - return( -1 ); - } - if( c->Bands != 1 && c->Bands != a->Bands ) { - im_error( "im_blend", - "%s", _( "conditional image must be one band or " - "same as then and else images" ) ); - return( -1 ); - } - if( im_piocheck( c, out ) || im_pincheck( a ) || im_pincheck( b ) ) + if( im_check_uncoded( "im_blend", c ) || + im_check_uncoded( "im_blend", a ) || + im_check_uncoded( "im_blend", b ) || + im_check_uchar( "im_blend", c ) || + im_check_format( "im_blend", a, b ) || + im_check_bands( "im_blend", a, b ) || + im_check_bands_1orn( "im_blend", c, a ) || + im_piocheck( c, out ) || + im_pincheck( a ) || + im_pincheck( b ) ) return( -1 ); if( im_demand_hint( out, IM_THINSTRIP, a, b, c, NULL ) ) return( -1 ); /* Make output image. */ - if( im_cp_descv( out, a, b, c, NULL ) || - !(in = im_allocate_input_array( out, c, a, b, NULL )) || - im_generate( out, - im_start_many, blend_gen, im_stop_many, - in, NULL ) ) + if( im_cp_descv( out, a, b, c, NULL ) ) return( -1 ); + out->Bands = IM_MAX( c->Bands, a->Bands ); + + /* Force a/b bands up to the same as out. + */ + if( im_open_local_array( out, t, 2, "im_blend", "p" ) ) + return( -1 ); + if( im__bandup( a, t[2], out->Bands ) || + im__bandup( a, t[3], out->Bands ) ) + return( -1 ); + a = t[2]; + b = t[3]; + + if( !(in = im_allocate_input_array( out, c, a, b, NULL )) || + im_generate( t[4], + im_start_many, blend_gen, im_stop_many, in, NULL ) ) + return( -1 ); + + if( repack ) { + if( im_Lab2LabQ( t[4], out ) ) + return( -1 ); + } + else { + if( im_copy( t[4], out ) ) + return( -1 ); + } return( 0 ); } diff --git a/libvips/relational/im_ifthenelse.c b/libvips/relational/im_ifthenelse.c index 9eb01cb4..0c6c566a 100644 --- a/libvips/relational/im_ifthenelse.c +++ b/libvips/relational/im_ifthenelse.c @@ -1,17 +1,4 @@ -/* @(#) Two images as input: must match in size and type. Build an output - * @(#) image choosing pixels from the left if a conditional image is - * @(#) non-zero and from the right otherwise. - * @(#) - * @(#) The conditional image can have n bands or 1 band. If n bands, then we - * @(#) choose from the two source images an element at a time. If 1 band, - * @(#) then choose from the source images a pixel at a time. - * @(#) - * @(#) int - * @(#) im_ifthenelse( c, a, b, out ) - * @(#) IMAGE *c, *a, *b; - * @(#) IMAGE *out; - * @(#) - * @(#) Returns either 0 (success) or -1 (fail). +/* im_ifthenelse.c --- use a condition image to join two images together * * Modified: * 9/2/95 JC @@ -22,6 +9,8 @@ * - now just evals left/right if all zero/all one * 7/10/06 * - set THINSTRIP + * 23/9/09 + * - gtkdoc comment */ /* @@ -154,9 +143,27 @@ ifthenelse_gen( REGION *or, void *seq, void *client1, void *client2 ) return( 0 ); } -/* if-then-else. Use IM_BANDFMT_UCHAR image to choose between two other images. - * Either: all same number of bands, or choice image is one band, others are - * n band. +/** + * im_ifthenelse: + * @c: condition #IMAGE + * @a: then #IMAGE + * @b: else #IMAGE + * @out: output #IMAGE + * + * This operation scans the condition image @c (which must be unsigned char) + * and uses it to select pixels from either the then image @a or the else + * image @b. Non-zero means @a, 0 means @b. + * + * The conditional image @c can have either 1 band, in which case entire pels + * come either from @a or @b, or n bands, where n is the number of bands in + * both @a and @b, in which case individual band elements are chosen from + * @a and @b. + * + * Images @a and @b must match exactly in size, bands and format. + * + * See also: im_blend(), im_equal(). + * + * Returns: 0 on success, -1 on error */ int im_ifthenelse( IMAGE *c, IMAGE *a, IMAGE *b, IMAGE *out ) diff --git a/libvips/relational/relational.c b/libvips/relational/relational.c index 7341e8ca..68405a2d 100644 --- a/libvips/relational/relational.c +++ b/libvips/relational/relational.c @@ -1,33 +1,4 @@ -/* @(#) Relational operations on VASARI images. All return a unsigned - * @(#) char image with the same number of bands as the input images. 255 - * @(#) for true, 0 for false. All work with mixed images types: eg. - * @(#) comparing float and byte. - * @(#) - * @(#) int im_equal( a, b, out ) int im_notequal( a, b, out ) - * @(#) IMAGE *a, *b, *out; IMAGE *a, *b, *out; - * @(#) - * @(#) - * @(#) int im_equalconst( a, out, c ) int im_notequalconst( a, out, c ) - * @(#) IMAGE *a, *out; IMAGE *a, *out; - * @(#) double c; double c; - * @(#) - * @(#) int im_less( a, b, out ) int im_lessconst( a, out, c ) - * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; - * @(#) double c; - * @(#) - * @(#) int im_more( a, b, out ) int im_moreconst( a, out, c ) - * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; - * @(#) double c; - * @(#) - * @(#) int im_lesseq( a, b, out ) int im_lesseqconst( a, out, c ) - * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; - * @(#) double c; - * @(#) - * @(#) int im_moreeq( a, b, out ) int im_moreeqconst( a, out, c ) - * @(#) IMAGE *a, *b, *out; IMAGE *a, *out; - * @(#) double c; - * @(#) - * @(#) Returns either 0 (success) or -1 (fail). +/* relational.c --- various relational operation * * Modified: * 26/7/93 JC @@ -47,6 +18,10 @@ * - all except _vec forms now work on complex * 31/7/03 JC * - oops, relational_format was broken for some combinations + * 23/9/09 + * - gtkdoc + * - use new im__arith_binary*() functions + * - more meta-programming */ /* @@ -81,658 +56,674 @@ #include #include -#include #include #include +#include #ifdef WITH_DMALLOC #include #endif /*WITH_DMALLOC*/ -/* Save a bit of typing. - */ #define UC IM_BANDFMT_UCHAR -#define C IM_BANDFMT_CHAR -#define US IM_BANDFMT_USHORT -#define S IM_BANDFMT_SHORT -#define UI IM_BANDFMT_UINT -#define I IM_BANDFMT_INT -#define F IM_BANDFMT_FLOAT -#define M IM_BANDFMT_COMPLEX -#define D IM_BANDFMT_DOUBLE -#define DM IM_BANDFMT_DPCOMPLEX -/* Type conversions for relational operators. For two input types, give the - * smallest common type, that is, the smallest type which can completely - * express the range of each. +/* Type conversions for relational: everything goes to uchar. */ -static int relational_format[10][10] = { - /* UC C US S UI I F M D DM */ -/* UC */ { UC, S, US, S, UI, I, F, M, D, DM }, -/* C */ { S, C, I, S, D, I, F, M, D, DM }, -/* US */ { US, I, US, I, UI, I, F, M, D, DM }, -/* S */ { S, S, I, S, D, I, F, M, D, DM }, -/* UI */ { UI, D, UI, D, UI, D, F, M, D, DM }, -/* I */ { I, I, I, I, D, I, F, M, D, DM }, -/* F */ { F, F, F, F, F, F, F, M, D, DM }, -/* M */ { M, M, M, M, M, M, M, M, DM, DM }, -/* D */ { D, D, D, D, D, D, D, DM, D, DM }, -/* DM */ { DM, DM, DM, DM, DM, DM, DM, DM, DM, DM } +static int bandfmt_relational[10] = { +/* UC C US S UI I F X D DX */ + UC, UC, UC, UC, UC, UC, UC, UC, UC, UC, }; -/* Check input images, cast both up to the smallest common type, and invoke - * the process function. - */ -static int -relational_process( char *name, IMAGE **in, IMAGE *out, - im_wrapmany_fn fn, void *b ) -{ - int i, n; - - /* Count args. - */ - for( n = 0; in[n]; n++ ) { - if( in[n]->Coding != IM_CODING_NONE ) { - im_error( name, "%s", _( "uncoded images only" ) ); - return( -1 ); - } - } - - /* Check sizes match. We don't need to check xsize/ysize, as wrapmany - * does this for us. - */ - for( i = 1; i < n; i++ ) - if( in[0]->Bands != in[i]->Bands ) { - im_error( name, "%s", - _( "images differ in numbers of bands" ) ); - return( -1 ); - } - - /* Prepare the output image. - */ - if( im_cp_desc_array( out, in ) ) - return( -1 ); - out->BandFmt = IM_BANDFMT_UCHAR; - out->Bbits = IM_BBITS_BYTE; - - /* For binary ops, cast inputs up to a common format. - */ - if( n == 2 ) { - int fmt = relational_format[in[0]->BandFmt][in[1]->BandFmt]; - IMAGE *t[3]; - - if( im_open_local_array( out, t, 2, "relational-1", "p" ) ) - return( -1 ); - t[2] = NULL; - - for( i = 0; i < n; i++ ) - if( im_clip2fmt( in[i], t[i], fmt ) ) - return( -1 ); - - if( im_wrapmany( t, out, fn, t[0], b ) ) - return( -1 ); - } - else - if( im_wrapmany( in, out, fn, in[0], b ) ) - return( -1 ); - - return( 0 ); +#define RBINARY( IN, FUN ) { \ + IN *tp1 = (IN *) p[0]; \ + IN *tp2 = (IN *) p[1]; \ + \ + for( i = 0; i < ne; i++ ) \ + FUN( q[i], tp1[i], tp2[i] ); \ } -/* Switch over bandfmt, calling a complexd and a non-complex processor. - */ -#define SWITCH( T, P_REAL, P_COMPLEX ) \ - switch( T ) {\ - case IM_BANDFMT_UCHAR: \ - P_REAL( unsigned char ); \ - break; \ - case IM_BANDFMT_CHAR: \ - P_REAL( char ); \ - break; \ - case IM_BANDFMT_USHORT: \ - P_REAL( unsigned short ); \ - break; \ - case IM_BANDFMT_SHORT: \ - P_REAL( short ); \ - break; \ - case IM_BANDFMT_UINT: \ - P_REAL( unsigned int ); \ - break; \ - case IM_BANDFMT_INT: \ - P_REAL( int ); \ - break; \ - case IM_BANDFMT_FLOAT: \ - P_REAL( float ); \ - break; \ - case IM_BANDFMT_DOUBLE: \ - P_REAL( double ); \ - break; \ - case IM_BANDFMT_COMPLEX: \ - P_COMPLEX( float ); \ - break; \ - case IM_BANDFMT_DPCOMPLEX: \ - P_COMPLEX( double ); \ - break; \ - default:\ - error_exit( "relational: internal error" );\ - } - -static void -equal_buffer( PEL **p, PEL *q, int n, IMAGE *a ) -{ - int ne = n * a->Bands; - PEL *p1 = p[0]; - PEL *p2 = p[1]; - int x; - -#define EQUAL_REAL( TYPE ) { \ - TYPE *i = (TYPE *) p1; \ - TYPE *j = (TYPE *) p2; \ - \ - for( x = 0; x < ne; x++ ) \ - if( i[x] == j[x] ) \ - q[x] = 255; \ - else \ - q[x] = 0; \ -} - -#define EQUAL_COMPLEX( TYPE ) { \ - TYPE *i = (TYPE *) p1; \ - TYPE *j = (TYPE *) p2; \ - \ - for( x = 0; x < ne; x++ ) { \ - if( i[0] == j[0] && i[1] == j[1] ) \ - q[x] = 255; \ - else \ - q[x] = 0; \ +#define CBINARY( IN, FUN ) { \ + IN *tp1 = (IN *) p[0]; \ + IN *tp2 = (IN *) p[1]; \ + \ + for( i = 0; i < ne; i++ ) { \ + FUN( q[i], tp1, tp2 ); \ \ - i += 2; \ - j += 2; \ + tp1 += 2; \ + tp2 += 2; \ } \ } - SWITCH( a->BandFmt, EQUAL_REAL, EQUAL_COMPLEX ); +#define BINARY_BUFFER( NAME, RFUN, CFUN ) \ +static void \ +NAME ## _buffer( PEL **p, PEL *q, int n, IMAGE *im ) \ +{ \ + const int ne = n * im->Bands; \ + \ + int i; \ + \ + switch( im->BandFmt ) { \ + case IM_BANDFMT_CHAR: RBINARY( signed char, RFUN ); break; \ + case IM_BANDFMT_UCHAR: RBINARY( unsigned char, RFUN ); break; \ + case IM_BANDFMT_SHORT: RBINARY( signed short, RFUN ); break; \ + case IM_BANDFMT_USHORT: RBINARY( unsigned short, RFUN ); break; \ + case IM_BANDFMT_INT: RBINARY( signed int, RFUN ); break; \ + case IM_BANDFMT_UINT: RBINARY( unsigned int, RFUN ); break; \ + case IM_BANDFMT_FLOAT: RBINARY( float, RFUN ); break; \ + case IM_BANDFMT_COMPLEX: CBINARY( float, CFUN ); break; \ + case IM_BANDFMT_DOUBLE: RBINARY( double, RFUN ); break; \ + case IM_BANDFMT_DPCOMPLEX: CBINARY( double, CFUN ); break; \ + \ + default: \ + g_assert( 0 ); \ + } \ } +#define EQUAL_REAL( Q, A, B ) { \ + if( (A) == (B) ) \ + Q = 255; \ + else \ + Q = 0; \ +} + +#define EQUAL_COMPLEX( Q, A, B ) { \ + if( (A)[0] == (B)[0] && (A)[1] == (B)[1] ) \ + Q = 255; \ + else \ + Q = 0; \ +} + +BINARY_BUFFER( EQUAL, EQUAL_REAL, EQUAL_COMPLEX ) + +/** + * im_equal: + * @in1: input #IMAGE 1 + * @in2: input #IMAGE 2 + * @out: output #IMAGE + * + * This operation calculates @in1 == @in2 (image element equals image element) + * and writes the result to @out. + * + * See also: im_notequal(). + * + * Returns: 0 on success, -1 on error + */ int im_equal( IMAGE *in1, IMAGE *in2, IMAGE *out ) { - IMAGE *invec[3]; - - invec[0] = in1; invec[1] = in2; invec[2] = NULL; - if( relational_process( "im_equal", invec, out, - (im_wrapmany_fn) equal_buffer, NULL ) ) - return( -1 ); - - return( 0 ); + return( im__arith_binary( "im_equal", + in1, in2, out, + bandfmt_relational, + (im_wrapmany_fn) EQUAL_buffer, NULL ) ); } -static void -notequal_buffer( PEL **p, PEL *q, int n, IMAGE *a ) -{ - int ne = n * a->Bands; - PEL *p1 = p[0]; - PEL *p2 = p[1]; - int x; - -#define NOTEQUAL_REAL( TYPE ) { \ - TYPE *i = (TYPE *) p1; \ - TYPE *j = (TYPE *) p2; \ - \ - for( x = 0; x < ne; x++ ) \ - if( i[x] != j[x] ) \ - q[x] = 255; \ - else \ - q[x] = 0; \ +#define NOTEQUAL_REAL( Q, A, B ) { \ + if( (A) != (B) ) \ + Q = 255; \ + else \ + Q = 0; \ } -#define NOTEQUAL_COMPLEX( TYPE ) { \ - TYPE *i = (TYPE *) p1; \ - TYPE *j = (TYPE *) p2; \ - \ - for( x = 0; x < ne; x++ ) { \ - if( i[0] != j[0] || i[1] != j[1] ) \ - q[x] = 255; \ - else \ - q[x] = 0; \ - \ - i += 2; \ - j += 2; \ - } \ +#define NOTEQUAL_COMPLEX( Q, A, B ) { \ + if( (A)[0] != (B)[0] || (A)[1] != (B)[1] ) \ + Q = 255; \ + else \ + Q = 0; \ } - SWITCH( a->BandFmt, NOTEQUAL_REAL, NOTEQUAL_COMPLEX ); -} +BINARY_BUFFER( NOTEQUAL, NOTEQUAL_REAL, NOTEQUAL_COMPLEX ) +/** + * im_notequal: + * @in1: input #IMAGE 1 + * @in2: input #IMAGE 2 + * @out: output #IMAGE + * + * This operation calculates @in1 != @in2 (image element does not equal image + * element) and writes the result to @out. + * + * See also: im_notequal(). + * + * Returns: 0 on success, -1 on error + */ int im_notequal( IMAGE *in1, IMAGE *in2, IMAGE *out ) { - IMAGE *invec[3]; - - invec[0] = in1; invec[1] = in2; invec[2] = NULL; - if( relational_process( "im_equal", invec, out, - (im_wrapmany_fn) notequal_buffer, NULL ) ) - return( -1 ); - - return( 0 ); + return( im__arith_binary( "im_notequal", + in1, in2, out, + bandfmt_relational, + (im_wrapmany_fn) NOTEQUAL_buffer, NULL ) ); } -/* strdup a vector of doubles. +#define LESS_REAL( Q, A, B ) { \ + if( (A) < (B) ) \ + Q = 255; \ + else \ + Q = 0; \ +} + +#define LESS_COMPLEX( Q, A, B ) { \ + double m1 = (A)[0] * (A)[0] + (A)[1] * (A)[1]; \ + double m2 = (B)[0] * (B)[0] + (B)[1] * (B)[1]; \ + \ + if( m1 < m2 ) \ + Q = 255; \ + else \ + Q = 0; \ +} + +BINARY_BUFFER( LESS, LESS_REAL, LESS_COMPLEX ) + +/** + * im_less: + * @in1: input #IMAGE 1 + * @in2: input #IMAGE 2 + * @out: output #IMAGE + * + * This operation calculates @in1 < @in2 (image element is less than image + * element) and writes the result to @out. + * + * See also: im_more(). + * + * Returns: 0 on success, -1 on error */ -static double * -numdup( IMAGE *out, int n, double *c ) -{ - double *p = IM_ARRAY( out, n, double ); - int i; - - if( !p ) - return( NULL ); - - for( i = 0; i < n; i++ ) - p[i] = c[i]; - - return( p ); -} - -static void -equalvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) -{ - int x, b, i; - -#define EQUALVEC_REAL( TYPE ) { \ - TYPE *p = (TYPE *) in[0]; \ - \ - for( i = 0, x = 0; x < n; x++ ) \ - for( b = 0; b < a->Bands; b++, i++ ) \ - if( p[i] == c[b] ) \ - out[i] = 255; \ - else \ - out[i] = 0; \ -} - -/* Sanity failure! - */ -#define EQUALVEC_COMPLEX( TYPE ) assert( 0 ); - - SWITCH( a->BandFmt, EQUALVEC_REAL, EQUALVEC_COMPLEX ); -} - -int -im_equal_vec( IMAGE *in, IMAGE *out, int n, double *c ) -{ - IMAGE *invec[2]; - double *p; - - if( n != in->Bands ) { - im_error( "im_equal_vec", "%s", _( "vec size does not match bands" ) ); - return( -1 ); - } - if( im_iscomplex( in ) ) { - im_error( "im_equal_vec", "%s", _( "not implemented for complex" ) ); - return( -1 ); - } - - invec[0] = in; invec[1] = NULL; - if( !(p = numdup( out, n, c )) || - relational_process( "im_equal_vec", invec, out, - (im_wrapmany_fn) equalvec_buffer, (void *) p ) ) - return( -1 ); - - return( 0 ); -} - -static double * -mkvec( IMAGE *in, IMAGE *out, double c ) -{ - double *v; - int i; - - if( !(v = IM_ARRAY( out, in->Bands, double )) ) - return( NULL ); - for( i = 0; i < in->Bands; i++ ) - v[i] = c; - - return( v ); -} - -int -im_equalconst( IMAGE *in, IMAGE *out, double c ) -{ - double *v; - - return( !(v = mkvec( in, out, c )) || - im_equal_vec( in, out, in->Bands, v ) ); -} - -static void -notequalvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) -{ - int x, b, i; - -#define NOTEQUALVEC_REAL( TYPE ) { \ - TYPE *p = (TYPE *) in[0]; \ - \ - for( i = 0, x = 0; x < n; x++ ) \ - for( b = 0; b < a->Bands; b++, i++ ) \ - if( p[i] != c[b] ) \ - out[i] = 255; \ - else \ - out[i] = 0; \ -} - -#define NOTEQUALVEC_COMPLEX( TYPE ) assert( 0 ); - - SWITCH( a->BandFmt, NOTEQUALVEC_REAL, NOTEQUALVEC_COMPLEX ); -} - -int -im_notequal_vec( IMAGE *in, IMAGE *out, int n, double *c ) -{ - IMAGE *invec[2]; - double *p; - - if( n != in->Bands ) { - im_error( "im_notequal_vec", "%s", _( "vec size does not match bands" ) ); - return( -1 ); - } - if( im_iscomplex( in ) ) { - im_error( "im_notequal_vec", "%s", _( "not implemented for complex" ) ); - return( -1 ); - } - - invec[0] = in; invec[1] = NULL; - if( !(p = numdup( out, n, c )) || - relational_process( "im_notequal_vec", invec, out, - (im_wrapmany_fn) notequalvec_buffer, (void *) p ) ) - return( -1 ); - - return( 0 ); -} - -int -im_notequalconst( IMAGE *in, IMAGE *out, double c ) -{ - double *v; - - return( !(v = mkvec( in, out, c )) || - im_notequal_vec( in, out, in->Bands, v ) ); -} - -static void -less_buffer( PEL **p, PEL *q, int n, IMAGE *a, IMAGE *b ) -{ - int ne = n * a->Bands; - PEL *p1 = p[0]; - PEL *p2 = p[1]; - int x; - -#define LESS_REAL( TYPE ) { \ - TYPE *i = (TYPE *) p1; \ - TYPE *j = (TYPE *) p2; \ - \ - for( x = 0; x < ne; x++ ) \ - if( i[x] < j[x] ) \ - q[x] = 255; \ - else \ - q[x] = 0; \ -} - -/* Take the mod and compare that. - */ -#define LESS_COMPLEX( TYPE ) { \ - TYPE *i = (TYPE *) p1; \ - TYPE *j = (TYPE *) p2; \ - \ - for( x = 0; x < ne; x++ ) { \ - double m1 = sqrt( i[0] * i[0] + i[1] * i[1] ); \ - double m2 = sqrt( j[0] * j[0] + j[1] * j[1] ); \ - \ - if( m1 < m2 ) \ - q[x] = 255; \ - else \ - q[x] = 0; \ - \ - i += 2; \ - j += 2; \ - } \ -} - - SWITCH( a->BandFmt, LESS_REAL, LESS_COMPLEX ); -} - int im_less( IMAGE *in1, IMAGE *in2, IMAGE *out ) { - IMAGE *invec[3]; - - invec[0] = in1; invec[1] = in2; invec[2] = NULL; - if( relational_process( "im_less", invec, out, - (im_wrapmany_fn) less_buffer, NULL ) ) - return( -1 ); - - return( 0 ); + return( im__arith_binary( "im_less", + in1, in2, out, + bandfmt_relational, + (im_wrapmany_fn) LESS_buffer, NULL ) ); } -static void -lessvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) -{ - int x, b, i; +#define LESSEQ_REAL( Q, A, B ) { \ + if( (A) <= (B) ) \ + Q = 255; \ + else \ + Q = 0; \ +} -#define LESSVEC_REAL( TYPE ) { \ - TYPE *p = (TYPE *) in[0]; \ +#define LESSEQ_COMPLEX( Q, A, B ) { \ + double m1 = (A)[0] * (A)[0] + (A)[1] * (A)[1]; \ + double m2 = (B)[0] * (B)[0] + (B)[1] * (B)[1]; \ \ - for( i = 0, x = 0; x < n; x++ ) \ - for( b = 0; b < a->Bands; b++, i++ ) \ - if( p[i] < c[b] ) \ - out[i] = 255; \ - else \ - out[i] = 0; \ + if( m1 <= m2 ) \ + Q = 255; \ + else \ + Q = 0; \ } -#define LESSVEC_COMPLEX( TYPE ) assert( 0 ); +BINARY_BUFFER( LESSEQ, LESSEQ_REAL, LESSEQ_COMPLEX ) - SWITCH( a->BandFmt, LESSVEC_REAL, LESSVEC_COMPLEX ); -} - -int -im_less_vec( IMAGE *in, IMAGE *out, int n, double *c ) -{ - IMAGE *invec[2]; - double *p; - - if( n != in->Bands ) { - im_error( "im_less_vec", "%s", _( "vec size does not match bands" ) ); - return( -1 ); - } - if( im_iscomplex( in ) ) { - im_error( "im_less_vec", "%s", _( "not implemented for complex" ) ); - return( -1 ); - } - - invec[0] = in; invec[1] = NULL; - if( !(p = numdup( out, n, c )) || - relational_process( "im_less_vec", invec, out, - (im_wrapmany_fn) lessvec_buffer, (void *) p ) ) - return( -1 ); - - return( 0 ); -} - -int -im_lessconst( IMAGE *in, IMAGE *out, double c ) -{ - double *v; - - return( !(v = mkvec( in, out, c )) || - im_less_vec( in, out, in->Bands, v ) ); -} - -static void -lesseq_buffer( PEL **p, PEL *q, int n, IMAGE *a, IMAGE *b ) -{ - int ne = n * a->Bands; - PEL *p1 = p[0]; - PEL *p2 = p[1]; - int x; - -#define LESSEQ_REAL( TYPE ) { \ - TYPE *i = (TYPE *) p1; \ - TYPE *j = (TYPE *) p2; \ - \ - for( x = 0; x < ne; x++ ) \ - if( i[x] <= j[x] ) \ - q[x] = 255; \ - else \ - q[x] = 0; \ -} - -/* Take the mod and compare that. +/** + * im_lesseq: + * @in1: input #IMAGE 1 + * @in2: input #IMAGE 2 + * @out: output #IMAGE + * + * This operation calculates @in1 <= @in2 (image element is less than or equal + * to image elemment) and writes the result to @out. + * + * See also: im_more(). + * + * Returns: 0 on success, -1 on error */ -#define LESSEQ_COMPLEX( TYPE ) { \ - TYPE *i = (TYPE *) p1; \ - TYPE *j = (TYPE *) p2; \ - \ - for( x = 0; x < ne; x++ ) { \ - double m1 = sqrt( i[0] * i[0] + i[1] * i[1] ); \ - double m2 = sqrt( j[0] * j[0] + j[1] * j[1] ); \ - \ - if( m1 <= m2 ) \ - q[x] = 255; \ - else \ - q[x] = 0; \ - \ - i += 2; \ - j += 2; \ - } \ -} - - SWITCH( a->BandFmt, LESSEQ_REAL, LESSEQ_COMPLEX ); -} - int im_lesseq( IMAGE *in1, IMAGE *in2, IMAGE *out ) { - IMAGE *invec[3]; - - invec[0] = in1; invec[1] = in2; invec[2] = NULL; - if( relational_process( "im_lesseq", invec, out, - (im_wrapmany_fn) lesseq_buffer, NULL ) ) - return( -1 ); - - return( 0 ); + return( im__arith_binary( "im_lesseq", + in1, in2, out, + bandfmt_relational, + (im_wrapmany_fn) LESSEQ_buffer, NULL ) ); } -static void -lesseqvec_buffer( PEL **in, PEL *out, int n, IMAGE *a, double *c ) -{ - int x, b, i; - -#define LESSEQVEC_REAL( TYPE ) { \ - TYPE *p = (TYPE *) in[0]; \ - \ - for( i = 0, x = 0; x < n; x++ ) \ - for( b = 0; b < a->Bands; b++, i++ ) \ - if( p[i] <= c[b] ) \ - out[i] = 255; \ - else \ - out[i] = 0; \ -} - -#define LESSEQVEC_COMPLEX( TYPE ) assert( 0 ); - - SWITCH( a->BandFmt, LESSEQVEC_REAL, LESSEQVEC_COMPLEX ); -} - -int -im_lesseq_vec( IMAGE *in, IMAGE *out, int n, double *c ) -{ - IMAGE *invec[2]; - double *p; - - if( n != in->Bands ) { - im_error( "im_lesseq_vec", "%s", _( "vec size does not match bands" ) ); - return( -1 ); - } - if( im_iscomplex( in ) ) { - im_error( "im_lesseq_vec", "%s", _( "not implemented for complex" ) ); - return( -1 ); - } - - invec[0] = in; invec[1] = NULL; - if( !(p = numdup( out, n, c )) || - relational_process( "im_lesseq_vec", invec, out, - (im_wrapmany_fn) lesseqvec_buffer, (void *) p ) ) - return( -1 ); - - return( 0 ); -} - -int -im_lesseqconst( IMAGE *in, IMAGE *out, double c ) -{ - double *v; - - return( !(v = mkvec( in, out, c )) || - im_lesseq_vec( in, out, in->Bands, v ) ); -} - -int +/** + * im_more: + * @in1: input #IMAGE 1 + * @in2: input #IMAGE 2 + * @out: output #IMAGE + * + * This operation calculates @in1 > @in2 (image element is greater than + * image elemment) and writes the result to @out. + * + * See also: im_less(). + * + * Returns: 0 on success, -1 on error + */ +int im_more( IMAGE *in1, IMAGE *in2, IMAGE *out ) { return( im_less( in2, in1, out ) ); } -int -im_more_vec( IMAGE *in, IMAGE *out, int n, double *c ) -{ - IMAGE *t; - - /* Same as not (lesseq x). - */ - if( !(t = im_open_local( out, "im_more_vec-1", "p" )) || - im_lesseq_vec( in, t, n, c ) || - im_eorimageconst( t, out, 255 ) ) - return( -1 ); - - return( 0 ); -} - -int -im_moreconst( IMAGE *in, IMAGE *out, double c ) -{ - double *v; - - return( !(v = mkvec( in, out, c )) || - im_more_vec( in, out, in->Bands, v ) ); -} - +/** + * im_moreeq: + * @in1: input #IMAGE 1 + * @in2: input #IMAGE 2 + * @out: output #IMAGE + * + * This operation calculates @in1 >= @in2 (image element is greater than or + * equal to image element) and writes the result to @out. + * + * See also: im_more(). + * + * Returns: 0 on success, -1 on error + */ int im_moreeq( IMAGE *in1, IMAGE *in2, IMAGE *out ) { return( im_lesseq( in2, in1, out ) ); } +#define RCONST1( IN, FUN ) { \ + IN *tp = (IN *) p; \ + IN tc = *((IN *) vector); \ + \ + for( i = 0; i < ne; i++ ) \ + FUN( q[i], tp[i], tc ); \ +} + +#define CCONST1( IN, FUN ) { \ + IN *tp = (IN *) p; \ + IN *tc = ((IN *) vector); \ + \ + for( i = 0; i < ne; i++ ) { \ + FUN( q[i], tp, tc ); \ + \ + tp += 2; \ + } \ +} + +#define CONST1_BUFFER( NAME, RFUN, CFUN ) \ +static void \ +NAME ## 1_buffer( PEL *p, PEL *q, int n, PEL *vector, IMAGE *im ) \ +{ \ + const int ne = n * im->Bands; \ + \ + int i; \ + \ + switch( im->BandFmt ) { \ + case IM_BANDFMT_CHAR: RCONST1( signed char, RFUN ); break; \ + case IM_BANDFMT_UCHAR: RCONST1( unsigned char, RFUN ); break; \ + case IM_BANDFMT_SHORT: RCONST1( signed short, RFUN ); break; \ + case IM_BANDFMT_USHORT: RCONST1( unsigned short, RFUN ); break; \ + case IM_BANDFMT_INT: RCONST1( signed int, RFUN ); break; \ + case IM_BANDFMT_UINT: RCONST1( unsigned int, RFUN ); break; \ + case IM_BANDFMT_FLOAT: RCONST1( float, RFUN ); break; \ + case IM_BANDFMT_COMPLEX: CCONST1( float, CFUN ); break; \ + case IM_BANDFMT_DOUBLE: RCONST1( double, RFUN ); break; \ + case IM_BANDFMT_DPCOMPLEX: CCONST1( double, CFUN ); break; \ + \ + default: \ + g_assert( 0 ); \ + } \ +} + +#define RCONSTN( IN, FUN ) { \ + IN *tp = (IN *) p; \ + IN *tc = (IN *) vector; \ + \ + for( i = 0, x = 0; x < n; x++ ) \ + for( b = 0; b < bands; b++, i++ ) \ + FUN( q[i], tp[i], tc[b] ); \ +} + +#define CCONSTN( IN, FUN ) { \ + IN *tp = (IN *) p; \ + \ + for( i = 0, x = 0; x < n; x++ ) { \ + IN *tc = ((IN *) vector); \ + \ + for( b = 0; b < bands; b++, i++ ) { \ + FUN( q[i], tp, tc ); \ + \ + tp += 2; \ + tc += 2; \ + } \ + } \ +} + +#define CONSTN_BUFFER( NAME, RFUN, CFUN ) \ +static void \ +NAME ## n_buffer( PEL *p, PEL *q, int n, PEL *vector, IMAGE *im ) \ +{ \ + const int bands = im->Bands; \ + \ + int i, x, b; \ + \ + switch( im->BandFmt ) { \ + case IM_BANDFMT_CHAR: RCONSTN( signed char, RFUN ); break; \ + case IM_BANDFMT_UCHAR: RCONSTN( unsigned char, RFUN ); break; \ + case IM_BANDFMT_SHORT: RCONSTN( signed short, RFUN ); break; \ + case IM_BANDFMT_USHORT: RCONSTN( unsigned short, RFUN ); break; \ + case IM_BANDFMT_INT: RCONSTN( signed int, RFUN ); break; \ + case IM_BANDFMT_UINT: RCONSTN( unsigned int, RFUN ); break; \ + case IM_BANDFMT_FLOAT: RCONSTN( float, RFUN ); break; \ + case IM_BANDFMT_COMPLEX: CCONSTN( float, CFUN ); break; \ + case IM_BANDFMT_DOUBLE: RCONSTN( double, RFUN ); break; \ + case IM_BANDFMT_DPCOMPLEX: CCONSTN( double, CFUN ); break; \ + \ + default: \ + g_assert( 0 ); \ + } \ +} + +CONST1_BUFFER( EQUAL, EQUAL_REAL, EQUAL_COMPLEX ) + +CONSTN_BUFFER( EQUAL, EQUAL_REAL, EQUAL_COMPLEX ) + +/** + * im_equal_vec: + * @in: input #IMAGE + * @out: output #IMAGE + * @n: array length + * @c: array of constants + * + * This operation calculates @in == @c (image element equals constant array + * @c) and writes the result to @out. + * + * See also: im_equal(), im_equalconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_equal_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + return( im__arith_binary_const( "im_equal", + in, out, n, c, + bandfmt_relational, + (im_wrapone_fn) EQUAL1_buffer, + (im_wrapone_fn) EQUALn_buffer ) ); +} + +CONST1_BUFFER( NOTEQUAL, NOTEQUAL_REAL, NOTEQUAL_COMPLEX ) + +CONSTN_BUFFER( NOTEQUAL, NOTEQUAL_REAL, NOTEQUAL_COMPLEX ) + +/** + * im_notequal_vec: + * @in: input #IMAGE + * @out: output #IMAGE + * @n: array length + * @c: array of constants + * + * This operation calculates @in != @c (image element is not equal to constant + * array @c) and writes the result to @out. + * + * See also: im_equal(), im_equal_vec(). + * + * Returns: 0 on success, -1 on error + */ +int +im_notequal_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + return( im__arith_binary_const( "im_notequal", + in, out, n, c, + bandfmt_relational, + (im_wrapone_fn) NOTEQUAL1_buffer, + (im_wrapone_fn) NOTEQUALn_buffer ) ); +} + +CONST1_BUFFER( LESS, LESS_REAL, LESS_COMPLEX ) + +CONSTN_BUFFER( LESS, LESS_REAL, LESS_COMPLEX ) + +/** + * im_less_vec: + * @in: input #IMAGE + * @out: output #IMAGE + * @n: array length + * @c: array of constants + * + * This operation calculates @in < @c (image element is less than constant + * array @c) and writes the result to @out. + * + * See also: im_less(), im_lessconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_less_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + return( im__arith_binary_const( "im_less", + in, out, n, c, + bandfmt_relational, + (im_wrapone_fn) LESS1_buffer, + (im_wrapone_fn) LESSn_buffer ) ); +} + +CONST1_BUFFER( LESSEQ, LESSEQ_REAL, LESSEQ_COMPLEX ) + +CONSTN_BUFFER( LESSEQ, LESSEQ_REAL, LESSEQ_COMPLEX ) + +/** + * im_lesseq_vec: + * @in: input #IMAGE + * @out: output #IMAGE + * @n: array length + * @c: array of constants + * + * This operation calculates @in <= @c (image element is less than or equal to + * constant array @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_lesseq_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + return( im__arith_binary_const( "im_lesseq", + in, out, n, c, + bandfmt_relational, + (im_wrapone_fn) LESSEQ1_buffer, + (im_wrapone_fn) LESSEQn_buffer ) ); +} + +#define MORE_REAL( Q, A, B ) { \ + if( (A) >= (B) ) \ + Q = 255; \ + else \ + Q = 0; \ +} + +#define MORE_COMPLEX( Q, A, B ) { \ + double m1 = (A)[0] * (A)[0] + (A)[1] * (A)[1]; \ + double m2 = (B)[0] * (B)[0] + (B)[1] * (B)[1]; \ + \ + if( m1 >= m2 ) \ + Q = 255; \ + else \ + Q = 0; \ +} + +CONST1_BUFFER( MORE, MORE_REAL, MORE_COMPLEX ) + +CONSTN_BUFFER( MORE, MORE_REAL, MORE_COMPLEX ) + +/** + * im_more_vec: + * @in: input #IMAGE + * @out: output #IMAGE + * @n: array length + * @c: array of constants + * + * This operation calculates @in > @c (image element is greater than + * constant array @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_more_vec( IMAGE *in, IMAGE *out, int n, double *c ) +{ + return( im__arith_binary_const( "im_more", + in, out, n, c, + bandfmt_relational, + (im_wrapone_fn) MORE1_buffer, + (im_wrapone_fn) MOREn_buffer ) ); +} + +#define MOREEQ_REAL( Q, A, B ) { \ + if( (A) >= (B) ) \ + Q = 255; \ + else \ + Q = 0; \ +} + +#define MOREEQ_COMPLEX( Q, A, B ) { \ + double m1 = (A)[0] * (A)[0] + (A)[1] * (A)[1]; \ + double m2 = (B)[0] * (B)[0] + (B)[1] * (B)[1]; \ + \ + if( m1 >= m2 ) \ + Q = 255; \ + else \ + Q = 0; \ +} + +CONST1_BUFFER( MOREEQ, MOREEQ_REAL, MOREEQ_COMPLEX ) + +CONSTN_BUFFER( MOREEQ, MOREEQ_REAL, MOREEQ_COMPLEX ) + +/** + * im_moreeq_vec: + * @in: input #IMAGE + * @out: output #IMAGE + * @n: array length + * @c: array of constants + * + * This operation calculates @in >= @c (image element is greater than or + * equal to + * constant array @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ int im_moreeq_vec( IMAGE *in, IMAGE *out, int n, double *c ) { - IMAGE *t; - - /* Same as not (less x). - */ - if( !(t = im_open_local( out, "im_moreeq_vec-1", "p" )) || - im_less_vec( in, t, n, c ) || - im_eorimageconst( t, out, 255 ) ) - return( -1 ); - - return( 0 ); + return( im__arith_binary_const( "im_moreeq", + in, out, n, c, + bandfmt_relational, + (im_wrapone_fn) MOREEQ1_buffer, + (im_wrapone_fn) MOREEQn_buffer ) ); } +/** + * im_equalconst: + * @in: input #IMAGE + * @out: output #IMAGE + * @c: constant + * + * This operation calculates @in == @c (image element is + * equal to constant @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ int -im_moreeqconst( IMAGE *in, IMAGE *out, double c ) -{ - double *v; - - return( !(v = mkvec( in, out, c )) || - im_moreeq_vec( in, out, in->Bands, v ) ); +im_equalconst( IMAGE *in, IMAGE *out, double c ) +{ + return( im_equal_vec( in, out, 1, &c ) ); } + +/** + * im_notequalconst: + * @in: input #IMAGE + * @out: output #IMAGE + * @c: constant + * + * This operation calculates @in != @c (image element is not equal to + * constant @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_notequalconst( IMAGE *in, IMAGE *out, double c ) +{ + return( im_notequal_vec( in, out, 1, &c ) ); +} + +/** + * im_lessconst: + * @in: input #IMAGE + * @out: output #IMAGE + * @c: constant + * + * This operation calculates @in < @c (image element is less than + * constant @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_lessconst( IMAGE *in, IMAGE *out, double c ) +{ + return( im_less_vec( in, out, 1, &c ) ); +} + +/** + * im_lesseqconst: + * @in: input #IMAGE + * @out: output #IMAGE + * @c: constant + * + * This operation calculates @in = @c (image element is less than + * or equal to + * constant @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_lesseqconst( IMAGE *in, IMAGE *out, double c ) +{ + return( im_lesseq_vec( in, out, 1, &c ) ); +} + +/** + * im_moreconst: + * @in: input #IMAGE + * @out: output #IMAGE + * @c: constant + * + * This operation calculates @in = @c (image element is more than + * constant @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_moreconst( IMAGE *in, IMAGE *out, double c ) +{ + return( im_more_vec( in, out, 1, &c ) ); +} + +/** + * im_moreeqconst: + * @in: input #IMAGE + * @out: output #IMAGE + * @c: constant + * + * This operation calculates @in = @c (image element is more than + * or equal to + * constant @c) and writes the result to @out. + * + * See also: im_lesseq(), im_lesseqconst(). + * + * Returns: 0 on success, -1 on error + */ +int +im_moreeqconst( IMAGE *in, IMAGE *out, double c ) +{ + return( im_moreeq_vec( in, out, 1, &c ) ); +} + diff --git a/libvips/relational/relational_dispatch.c b/libvips/relational/relational_dispatch.c index b57c5b86..0a84f60d 100644 --- a/libvips/relational/relational_dispatch.c +++ b/libvips/relational/relational_dispatch.c @@ -42,6 +42,37 @@ #include #endif /*WITH_DMALLOC*/ +/** + * SECTION: relational + * @short_description: relational comparisons between pairs of images and + * images and constants + * @see_also: arithmetic + * @stability: Stable + * @include: vips/vips.h + * + * These operations perform comparison operations, such as equals, on + * every pixel in an image or pair of images. + * All will work with + * images of any type or any mixture of types of any size and of any number + * of bands. + * + * For binary operations, 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. + * + * In the same way, for operations that take an array constant, such as + * im_equal_vec(), you can mix single-element arrays or single-band images + * freely. + * + * The output type is always unsigned char, + * with 255 for every band element for which the condition + * is true, and 0 for every other element. + * For complex images, the operations calculate and compare the modulus. + * + * For binary operations on pairs of images, the images must match in size. + */ + /* Two images in, one out. */ static im_arg_desc two_in_one_out[] = {