1 /* math2.c --- 2ary math funcs
2 *
3 * Copyright: 1990, N. Dessipris
4 *
5 * Author: Nicos Dessipris
6 * Written on: 02/05/1990
7 * Modified on:
8 * 10/12/93 JC
9 * - now reports total number of x/0, rather than each one.
10 * 1/2/95 JC
11 * - rewritten for PIO with im_wrapone()
12 * - incorrect complex code removed
13 * - /0 reporting removed for ease of programming
14 * 15/4/97 JC
15 * - return( 0 ) missing, oops!
16 * 6/7/98 JC
17 * - _vec form added
18 * 30/8/09
19 * - gtkdoc
20 * - tiny cleanups
21 * 20/9/09
22 * - im_powtra() adapated to make math2.c
23 * 12/11/11
24 * - redone as a class
25 * 17/7/12
26 * - wopconst was broken
27 * 20/10/21 indus
28 * - add atan2
29 */
30
31 /*
32
33 Copyright (C) 1991-2005 The National Gallery
34
35 This library is free software; you can redistribute it and/or
36 modify it under the terms of the GNU Lesser General Public
37 License as published by the Free Software Foundation; either
38 version 2.1 of the License, or (at your option) any later version.
39
40 This library is distributed in the hope that it will be useful,
41 but WITHOUT ANY WARRANTY; without even the implied warranty of
42 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
43 Lesser General Public License for more details.
44
45 You should have received a copy of the GNU Lesser General Public
46 License along with this library; if not, write to the Free Software
47 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
48 02110-1301 USA
49
50 */
51
52 /*
53
54 These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
55
56 */
57
58 /*
59 #define DEBUG
60 */
61
62 #ifdef HAVE_CONFIG_H
63 #include <config.h>
64 #endif /*HAVE_CONFIG_H*/
65 #include <vips/intl.h>
66
67 #include <stdio.h>
68 #include <stdlib.h>
69 #include <math.h>
70
71 #include <vips/vips.h>
72
73 #include "binary.h"
74 #include "unaryconst.h"
75
76 typedef struct _VipsMath2 {
77 VipsBinary parent_instance;
78
79 VipsOperationMath2 math2;
80
81 } VipsMath2;
82
83 typedef VipsBinaryClass VipsMath2Class;
84
85 G_DEFINE_TYPE( VipsMath2, vips_math2, VIPS_TYPE_BINARY );
86
87 static int
vips_math2_build(VipsObject * object)88 vips_math2_build( VipsObject *object )
89 {
90 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
91 VipsBinary *binary = (VipsBinary *) object;
92
93 if( binary->left &&
94 vips_check_noncomplex( class->nickname, binary->left ) )
95 return( -1 );
96 if( binary->right &&
97 vips_check_noncomplex( class->nickname, binary->right ) )
98 return( -1 );
99
100 if( VIPS_OBJECT_CLASS( vips_math2_parent_class )->build( object ) )
101 return( -1 );
102
103 return( 0 );
104 }
105
106 #define LOOP( IN, OUT, OP ) { \
107 IN * restrict p1 = (IN *) in[0]; \
108 IN * restrict p2 = (IN *) in[1]; \
109 OUT * restrict q = (OUT *) out; \
110 \
111 for( x = 0; x < sz; x++ ) \
112 OP( q[x], p1[x], p2[x] ); \
113 }
114
115 #define SWITCH( L, OP ) \
116 switch( vips_image_get_format( im ) ) { \
117 case VIPS_FORMAT_UCHAR: \
118 L( unsigned char, float, OP ); break; \
119 case VIPS_FORMAT_CHAR: \
120 L( signed char, float, OP ); break; \
121 case VIPS_FORMAT_USHORT: \
122 L( unsigned short, float, OP ); break; \
123 case VIPS_FORMAT_SHORT: \
124 L( signed short, float, OP ); break; \
125 case VIPS_FORMAT_UINT: \
126 L( unsigned int, float, OP ); break; \
127 case VIPS_FORMAT_INT: \
128 L( signed int, float, OP ); break; \
129 case VIPS_FORMAT_FLOAT: \
130 L( float, float, OP ); break; \
131 case VIPS_FORMAT_DOUBLE: \
132 L( double, double, OP ); break;\
133 \
134 default: \
135 g_assert_not_reached(); \
136 }
137
138 #define POW( Y, X, E ) { \
139 double left = (double) (X); \
140 double right = (double) (E); \
141 \
142 /* Special case for **-1 and **0.5, since they are so common. Also \
143 * watch for /0. \
144 */ \
145 (Y) = (left == 0.0) \
146 ? 0.0 \
147 : (right == -1) \
148 ? 1.0 / left \
149 : (right == 0.5) \
150 ? sqrt( left ) \
151 : pow( left, right ); \
152 }
153
154 #define WOP( Y, X, E ) POW( Y, E, X )
155
156 #ifdef HAVE_ATAN2
157 #define ATAN2( Y, L, R ) { \
158 double left = (double) (L); \
159 double right = (double) (R); \
160 \
161 (Y) = VIPS_DEG( atan2( left, right ) ); \
162 if( (Y) < 0.0 ) \
163 (Y) += 360; \
164 }
165 #else
166 #define ATAN2( Y, L, R ) { \
167 double left = (double) (L); \
168 double right = (double) (R); \
169 \
170 (Y) = vips_col_ab2h( left, right ); \
171 }
172 #endif
173
174 static void
vips_math2_buffer(VipsArithmetic * arithmetic,VipsPel * out,VipsPel ** in,int width)175 vips_math2_buffer( VipsArithmetic *arithmetic,
176 VipsPel *out, VipsPel **in, int width )
177 {
178 VipsMath2 *math2 = (VipsMath2 *) arithmetic;
179 VipsImage *im = arithmetic->ready[0];
180 const int sz = width * vips_image_get_bands( im );
181
182 int x;
183
184 switch( math2->math2 ) {
185 case VIPS_OPERATION_MATH2_POW: SWITCH( LOOP, POW ); break;
186 case VIPS_OPERATION_MATH2_WOP: SWITCH( LOOP, WOP ); break;
187 case VIPS_OPERATION_MATH2_ATAN2: SWITCH( LOOP, ATAN2 ); break;
188
189 default:
190 g_assert_not_reached();
191 }
192 }
193
194 /* Save a bit of typing.
195 */
196 #define UC VIPS_FORMAT_UCHAR
197 #define C VIPS_FORMAT_CHAR
198 #define US VIPS_FORMAT_USHORT
199 #define S VIPS_FORMAT_SHORT
200 #define UI VIPS_FORMAT_UINT
201 #define I VIPS_FORMAT_INT
202 #define F VIPS_FORMAT_FLOAT
203 #define X VIPS_FORMAT_COMPLEX
204 #define D VIPS_FORMAT_DOUBLE
205 #define DX VIPS_FORMAT_DPCOMPLEX
206
207 /* Type promotion for math2. Keep in sync with math2_buffer() above.
208 */
209 static int vips_math2_format_table[10] = {
210 /* UC C US S UI I F X D DX */
211 F, F, F, F, F, F, F, X, D, DX
212 };
213
214 static void
vips_math2_class_init(VipsMath2Class * class)215 vips_math2_class_init( VipsMath2Class *class )
216 {
217 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
218 VipsObjectClass *object_class = (VipsObjectClass *) class;
219 VipsArithmeticClass *aclass = VIPS_ARITHMETIC_CLASS( class );
220
221 gobject_class->set_property = vips_object_set_property;
222 gobject_class->get_property = vips_object_get_property;
223
224 object_class->nickname = "math2";
225 object_class->description = _( "binary math operations" );
226 object_class->build = vips_math2_build;
227
228 aclass->process_line = vips_math2_buffer;
229
230 vips_arithmetic_set_format_table( aclass, vips_math2_format_table );
231
232 VIPS_ARG_ENUM( class, "math2", 200,
233 _( "Operation" ),
234 _( "math to perform" ),
235 VIPS_ARGUMENT_REQUIRED_INPUT,
236 G_STRUCT_OFFSET( VipsMath2, math2 ),
237 VIPS_TYPE_OPERATION_MATH2, VIPS_OPERATION_MATH2_POW );
238 }
239
240 static void
vips_math2_init(VipsMath2 * math2)241 vips_math2_init( VipsMath2 *math2 )
242 {
243 }
244
245 static int
vips_math2v(VipsImage * left,VipsImage * right,VipsImage ** out,VipsOperationMath2 math2,va_list ap)246 vips_math2v( VipsImage *left, VipsImage *right, VipsImage **out,
247 VipsOperationMath2 math2, va_list ap )
248 {
249 return( vips_call_split( "math2", ap, left, right, out, math2 ) );
250 }
251
252 /**
253 * vips_math2:
254 * @left: left-hand input #VipsImage
255 * @right: right-hand input #VipsImage
256 * @out: (out): output #VipsImage
257 * @math2: math operation to perform
258 * @...: %NULL-terminated list of optional named arguments
259 *
260 * This operation calculates a 2-ary maths operation on a pair of images
261 * and writes the result to @out. The images may have any
262 * non-complex format. @out is float except in the case that either of @left
263 * or @right are double, in which case @out is also double.
264 *
265 * It detects division by zero, setting those pixels to zero in the output.
266 * Beware: it does this silently!
267 *
268 * If the images differ in size, the smaller image is enlarged to match the
269 * larger by adding zero pixels along the bottom and right.
270 *
271 * If the number of bands differs, one of the images
272 * must have one band. In this case, an n-band image is formed from the
273 * one-band image by joining n copies of the one-band image together, and then
274 * the two n-band images are operated upon.
275 *
276 * The two input images are cast up to the smallest common format (see table
277 * Smallest common format in
278 * <link linkend="libvips-arithmetic">arithmetic</link>), and that format is the
279 * result type.
280 *
281 * See also: vips_math2_const().
282 *
283 * Returns: 0 on success, -1 on error
284 */
285 int
vips_math2(VipsImage * left,VipsImage * right,VipsImage ** out,VipsOperationMath2 math2,...)286 vips_math2( VipsImage *left, VipsImage *right, VipsImage **out,
287 VipsOperationMath2 math2, ... )
288 {
289 va_list ap;
290 int result;
291
292 va_start( ap, math2 );
293 result = vips_math2v( left, right, out, math2, ap );
294 va_end( ap );
295
296 return( result );
297 }
298
299 /**
300 * vips_pow:
301 * @left: left-hand input #VipsImage
302 * @right: right-hand input #VipsImage
303 * @out: (out): output #VipsImage
304 * @...: %NULL-terminated list of optional named arguments
305 *
306 * Perform #VIPS_OPERATION_MATH2_POW on a pair of images. See
307 * vips_math2().
308 *
309 * Returns: 0 on success, -1 on error
310 */
311 int
vips_pow(VipsImage * left,VipsImage * right,VipsImage ** out,...)312 vips_pow( VipsImage *left, VipsImage *right, VipsImage **out, ... )
313 {
314 va_list ap;
315 int result;
316
317 va_start( ap, out );
318 result = vips_math2v( left, right, out, VIPS_OPERATION_MATH2_POW, ap );
319 va_end( ap );
320
321 return( result );
322 }
323
324 /**
325 * vips_wop:
326 * @left: left-hand input #VipsImage
327 * @right: right-hand input #VipsImage
328 * @out: (out): output #VipsImage
329 * @...: %NULL-terminated list of optional named arguments
330 *
331 * Perform #VIPS_OPERATION_MATH2_WOP on a pair of images. See
332 * vips_math2().
333 *
334 * Returns: 0 on success, -1 on error
335 */
336 int
vips_wop(VipsImage * left,VipsImage * right,VipsImage ** out,...)337 vips_wop( VipsImage *left, VipsImage *right, VipsImage **out, ... )
338 {
339 va_list ap;
340 int result;
341
342 va_start( ap, out );
343 result = vips_math2v( left, right, out, VIPS_OPERATION_MATH2_WOP, ap );
344 va_end( ap );
345
346 return( result );
347 }
348
349 /**
350 * vips_atan2:
351 * @left: left-hand input #VipsImage
352 * @right: right-hand input #VipsImage
353 * @out: (out): output #VipsImage
354 * @...: %NULL-terminated list of optional named arguments
355 *
356 * Perform #VIPS_OPERATION_MATH2_ATAN2 on a pair of images. See
357 * vips_math2().
358 *
359 * Returns: 0 on success, -1 on error
360 */
361 int
vips_atan2(VipsImage * left,VipsImage * right,VipsImage ** out,...)362 vips_atan2( VipsImage *left, VipsImage *right, VipsImage **out, ... )
363 {
364 va_list ap;
365 int result;
366
367 va_start( ap, out );
368 result = vips_math2v( left, right, out, VIPS_OPERATION_MATH2_ATAN2, ap );
369 va_end( ap );
370
371 return( result );
372 }
373
374
375 typedef struct _VipsMath2Const {
376 VipsUnaryConst parent_instance;
377
378 VipsOperationMath2 math2;
379
380 } VipsMath2Const;
381
382 typedef VipsUnaryConstClass VipsMath2ConstClass;
383
384 G_DEFINE_TYPE( VipsMath2Const,
385 vips_math2_const, VIPS_TYPE_UNARY_CONST );
386
387 static int
vips_math2_const_build(VipsObject * object)388 vips_math2_const_build( VipsObject *object )
389 {
390 VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
391 VipsUnary *unary = (VipsUnary *) object;
392
393 if( unary->in &&
394 vips_check_noncomplex( class->nickname, unary->in ) )
395 return( -1 );
396
397 if( VIPS_OBJECT_CLASS( vips_math2_const_parent_class )->
398 build( object ) )
399 return( -1 );
400
401 return( 0 );
402 }
403
404 #define LOOPC( IN, OUT, OP ) { \
405 IN * restrict p = (IN *) in[0]; \
406 OUT * restrict q = (OUT *) out; \
407 double * restrict c = uconst->c_double; \
408 \
409 for( i = 0, x = 0; x < width; x++ ) \
410 for( b = 0; b < bands; b++, i++ ) \
411 OP( q[i], p[i], c[b] ); \
412 }
413
414 static void
vips_math2_const_buffer(VipsArithmetic * arithmetic,VipsPel * out,VipsPel ** in,int width)415 vips_math2_const_buffer( VipsArithmetic *arithmetic,
416 VipsPel *out, VipsPel **in, int width )
417 {
418 VipsUnaryConst *uconst = (VipsUnaryConst *) arithmetic;
419 VipsMath2Const *math2 = (VipsMath2Const *) arithmetic;
420 VipsImage *im = arithmetic->ready[0];
421 int bands = im->Bands;
422
423 int i, x, b;
424
425 switch( math2->math2 ) {
426 case VIPS_OPERATION_MATH2_POW:
427 SWITCH( LOOPC, POW );
428 break;
429
430 case VIPS_OPERATION_MATH2_WOP:
431 SWITCH( LOOPC, WOP );
432 break;
433
434 case VIPS_OPERATION_MATH2_ATAN2:
435 SWITCH( LOOPC, ATAN2 );
436 break;
437
438 default:
439 g_assert_not_reached();
440 break;
441 }
442 }
443
444 static void
vips_math2_const_class_init(VipsMath2ConstClass * class)445 vips_math2_const_class_init( VipsMath2ConstClass *class )
446 {
447 GObjectClass *gobject_class = G_OBJECT_CLASS( class );
448 VipsObjectClass *object_class = (VipsObjectClass *) class;
449 VipsArithmeticClass *aclass = VIPS_ARITHMETIC_CLASS( class );
450
451 gobject_class->set_property = vips_object_set_property;
452 gobject_class->get_property = vips_object_get_property;
453
454 object_class->nickname = "math2_const";
455 object_class->description =
456 _( "binary math operations with a constant" );
457 object_class->build = vips_math2_const_build;
458
459 aclass->process_line = vips_math2_const_buffer;
460
461 vips_arithmetic_set_format_table( aclass, vips_math2_format_table );
462
463 VIPS_ARG_ENUM( class, "math2", 200,
464 _( "Operation" ),
465 _( "math to perform" ),
466 VIPS_ARGUMENT_REQUIRED_INPUT,
467 G_STRUCT_OFFSET( VipsMath2Const, math2 ),
468 VIPS_TYPE_OPERATION_MATH2, VIPS_OPERATION_MATH2_POW );
469 }
470
471 static void
vips_math2_const_init(VipsMath2Const * math2_const)472 vips_math2_const_init( VipsMath2Const *math2_const )
473 {
474 }
475
476 static int
vips_math2_constv(VipsImage * in,VipsImage ** out,VipsOperationMath2 math2,const double * c,int n,va_list ap)477 vips_math2_constv( VipsImage *in, VipsImage **out,
478 VipsOperationMath2 math2, const double *c, int n, va_list ap )
479 {
480 VipsArea *area_c;
481 double *array;
482 int result;
483 int i;
484
485 area_c = vips_area_new_array( G_TYPE_DOUBLE, sizeof( double ), n );
486 array = (double *) area_c->data;
487 for( i = 0; i < n; i++ )
488 array[i] = c[i];
489
490 result = vips_call_split( "math2_const", ap, in, out, math2, area_c );
491
492 vips_area_unref( area_c );
493
494 return( result );
495 }
496
497 /**
498 * vips_math2_const: (method)
499 * @in: input image
500 * @out: (out): output image
501 * @math2: math operation to perform
502 * @c: (array length=n): array of constants
503 * @n: number of constants in @c
504 * @...: %NULL-terminated list of optional named arguments
505 *
506 * This operation calculates various 2-ary maths operations on an image and
507 * an array of constants and writes the result to @out.
508 * The image may have any
509 * non-complex format. @out is float except in the case that @in
510 * is double, in which case @out is also double.
511 *
512 * It detects division by zero, setting those pixels to zero in the output.
513 * Beware: it does this silently!
514 *
515 * If the array of constants has just one element, that constant is used for
516 * all image bands. If the array has more than one element and they have
517 * the same number of elements as there are bands in the image, then
518 * one array element is used for each band. If the arrays have more than one
519 * element and the image only has a single band, the result is a many-band
520 * image where each band corresponds to one array element.
521 *
522 * See also: vips_math2(), vips_math().
523 *
524 * Returns: 0 on success, -1 on error
525 */
526 int
vips_math2_const(VipsImage * in,VipsImage ** out,VipsOperationMath2 math2,const double * c,int n,...)527 vips_math2_const( VipsImage *in, VipsImage **out,
528 VipsOperationMath2 math2, const double *c, int n, ... )
529 {
530 va_list ap;
531 int result;
532
533 va_start( ap, n );
534 result = vips_math2_constv( in, out, math2, c, n, ap );
535 va_end( ap );
536
537 return( result );
538 }
539
540 /**
541 * vips_pow_const: (method)
542 * @in: left-hand input #VipsImage
543 * @out: (out): output #VipsImage
544 * @c: (array length=n): array of constants
545 * @n: number of constants in @c
546 * @...: %NULL-terminated list of optional named arguments
547 *
548 * Perform #VIPS_OPERATION_MATH2_POW on an image and a constant. See
549 * vips_math2_const().
550 *
551 * Returns: 0 on success, -1 on error
552 */
553 int
vips_pow_const(VipsImage * in,VipsImage ** out,const double * c,int n,...)554 vips_pow_const( VipsImage *in, VipsImage **out, const double *c, int n, ... )
555 {
556 va_list ap;
557 int result;
558
559 va_start( ap, n );
560 result = vips_math2_constv( in, out,
561 VIPS_OPERATION_MATH2_POW, c, n, ap );
562 va_end( ap );
563
564 return( result );
565 }
566
567 /**
568 * vips_wop_const: (method)
569 * @in: left-hand input #VipsImage
570 * @out: (out): output #VipsImage
571 * @c: (array length=n): array of constants
572 * @n: number of constants in @c
573 * @...: %NULL-terminated list of optional named arguments
574 *
575 * Perform #VIPS_OPERATION_MATH2_WOP on an image and a constant. See
576 * vips_math2_const().
577 *
578 * Returns: 0 on success, -1 on error
579 */
580 int
vips_wop_const(VipsImage * in,VipsImage ** out,const double * c,int n,...)581 vips_wop_const( VipsImage *in, VipsImage **out, const double *c, int n, ... )
582 {
583 va_list ap;
584 int result;
585
586 va_start( ap, n );
587 result = vips_math2_constv( in, out,
588 VIPS_OPERATION_MATH2_WOP, c, n, ap );
589 va_end( ap );
590
591 return( result );
592 }
593
594 /**
595 * vips_atan2_const: (method)
596 * @in: left-hand input #VipsImage
597 * @out: (out): output #VipsImage
598 * @c: (array length=n): array of constants
599 * @n: number of constants in @c
600 * @...: %NULL-terminated list of optional named arguments
601 *
602 * Perform #VIPS_OPERATION_MATH2_ATAN2 on an image and a constant. See
603 * vips_math2_const().
604 *
605 * Returns: 0 on success, -1 on error
606 */
607 int
vips_atan2_const(VipsImage * in,VipsImage ** out,const double * c,int n,...)608 vips_atan2_const( VipsImage *in, VipsImage **out, const double *c, int n, ... )
609 {
610 va_list ap;
611 int result;
612
613 va_start( ap, n );
614 result = vips_math2_constv( in, out,
615 VIPS_OPERATION_MATH2_ATAN2, c, n, ap );
616 va_end( ap );
617
618 return( result );
619 }
620
621 /**
622 * vips_math2_const1: (method)
623 * @in: input image
624 * @out: (out): output image
625 * @math2: math operation to perform
626 * @c: constant
627 * @...: %NULL-terminated list of optional named arguments
628 *
629 * This operation calculates various 2-ary maths operations on an image and
630 * a constant. See vips_math2_const().
631 *
632 * Returns: 0 on success, -1 on error
633 */
634 int
vips_math2_const1(VipsImage * in,VipsImage ** out,VipsOperationMath2 math2,double c,...)635 vips_math2_const1( VipsImage *in, VipsImage **out,
636 VipsOperationMath2 math2, double c, ... )
637 {
638 va_list ap;
639 int result;
640
641 va_start( ap, c );
642 result = vips_math2_constv( in, out, math2, &c, 1, ap );
643 va_end( ap );
644
645 return( result );
646 }
647
648 /**
649 * vips_pow_const1: (method)
650 * @in: left-hand input #VipsImage
651 * @out: (out): output #VipsImage
652 * @c: constant
653 * @...: %NULL-terminated list of optional named arguments
654 *
655 * Perform #VIPS_OPERATION_MATH2_POW on an image and a constant. See
656 * vips_math2_const().
657 *
658 * Returns: 0 on success, -1 on error
659 */
660 int
vips_pow_const1(VipsImage * in,VipsImage ** out,double c,...)661 vips_pow_const1( VipsImage *in, VipsImage **out, double c, ... )
662 {
663 va_list ap;
664 int result;
665
666 va_start( ap, c );
667 result = vips_math2_constv( in, out,
668 VIPS_OPERATION_MATH2_POW, &c, 1, ap );
669 va_end( ap );
670
671 return( result );
672 }
673
674 /**
675 * vips_wop_const1: (method)
676 * @in: left-hand input #VipsImage
677 * @out: (out): output #VipsImage
678 * @c: constant
679 * @...: %NULL-terminated list of optional named arguments
680 *
681 * Perform #VIPS_OPERATION_MATH2_WOP on an image and a constant. See
682 * vips_math2_const().
683 *
684 * Returns: 0 on success, -1 on error
685 */
686 int
vips_wop_const1(VipsImage * in,VipsImage ** out,double c,...)687 vips_wop_const1( VipsImage *in, VipsImage **out, double c, ... )
688 {
689 va_list ap;
690 int result;
691
692 va_start( ap, c );
693 result = vips_math2_constv( in, out,
694 VIPS_OPERATION_MATH2_WOP, &c, 1, ap );
695 va_end( ap );
696
697 return( result );
698 }
699
700 /**
701 * vips_atan2_const1: (method)
702 * @in: left-hand input #VipsImage
703 * @out: (out): output #VipsImage
704 * @c: constant
705 * @...: %NULL-terminated list of optional named arguments
706 *
707 * Perform #VIPS_OPERATION_MATH2_ATAN2 on an image and a constant. See
708 * vips_math2_const().
709 *
710 * Returns: 0 on success, -1 on error
711 */
712 int
vips_atan2_const1(VipsImage * in,VipsImage ** out,double c,...)713 vips_atan2_const1( VipsImage *in, VipsImage **out, double c, ... )
714 {
715 va_list ap;
716 int result;
717
718 va_start( ap, c );
719 result = vips_math2_constv( in, out,
720 VIPS_OPERATION_MATH2_ATAN2, &c, 1, ap );
721 va_end( ap );
722
723 return( result );
724 }
725