1 /* switch between an array of images
2  *
3  * 28/7/19
4  * 	- from maplut.c
5  */
6 
7 /*
8 
9     This file is part of VIPS.
10 
11     VIPS is free software; you can redistribute it and/or modify
12     it under the terms of the GNU Lesser General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17     but WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19     GNU Lesser General Public License for more details.
20 
21     You should have received a copy of the GNU Lesser General Public License
22     along with this program; if not, write to the Free Software
23     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24     02110-1301  USA
25 
26  */
27 
28 /*
29 
30     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
31 
32  */
33 
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif /*HAVE_CONFIG_H*/
37 #include <vips/intl.h>
38 
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include <vips/vips.h>
44 #include <vips/internal.h>
45 
46 typedef struct _VipsSwitch {
47 	VipsOperation parent_instance;
48 
49 	VipsArrayImage *tests;
50 	VipsImage *out;
51 
52 	int n;
53 
54 } VipsSwitch;
55 
56 typedef VipsOperationClass VipsSwitchClass;
57 
58 G_DEFINE_TYPE( VipsSwitch, vips_switch, VIPS_TYPE_OPERATION );
59 
60 static int
vips_switch_gen(VipsRegion * or,void * seq,void * a,void * b,gboolean * stop)61 vips_switch_gen( VipsRegion *or, void *seq, void *a, void *b,
62 	gboolean *stop )
63 {
64 	VipsRegion **ar = (VipsRegion **) seq;
65 	VipsSwitch *swit = (VipsSwitch *) b;
66 	VipsRect *r = &or->valid;
67 
68 	int x, y, i;
69 	VipsPel * restrict q;
70 	size_t qls;
71 	VipsPel * restrict p[256];
72 	size_t ls[256];
73 
74 	if( vips_reorder_prepare_many( or->im, ar, r ) )
75 		return( -1 );
76 
77 	g_assert( ar[0]->im->BandFmt == VIPS_FORMAT_UCHAR );
78 	g_assert( ar[0]->im->Bands == 1 );
79 
80 	for( i = 0; i < swit->n; i++ ) {
81 		p[i] = VIPS_REGION_ADDR( ar[i], r->left, r->top );
82 		ls[i] = VIPS_REGION_LSKIP( ar[i] );
83 	}
84 
85 	q = VIPS_REGION_ADDR( or, r->left, r->top );
86 	qls = VIPS_REGION_LSKIP( or );
87 	for( y = 0; y < r->height; y++ ) {
88 		for( x = 0; x < r->width; x++ ) {
89 			for( i = 0; i < swit->n; i++ )
90 				if( p[i][x] )
91 					break;
92 
93 			q[x] = i;
94 		}
95 
96 		q += qls;
97 		for( i = 0; i < swit->n; i++ )
98 			p[i] += ls[i];
99 	}
100 
101 	return( 0 );
102 }
103 
104 static int
vips_switch_build(VipsObject * object)105 vips_switch_build( VipsObject *object )
106 {
107 	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
108 	VipsSwitch *swit = (VipsSwitch *) object;
109 
110 	VipsImage **tests;
111 	VipsImage **decode;
112 	VipsImage **format;
113 	VipsImage **band;
114 	VipsImage **size;
115 	int i;
116 
117 	g_object_set( object, "out", vips_image_new(), NULL );
118 
119 	if( VIPS_OBJECT_CLASS( vips_switch_parent_class )->build( object ) )
120 		return( -1 );
121 
122 	/* 255 rather than 256, since we want to reserve +1 as the no
123 	 * match value.
124 	 */
125 	tests = vips_area_get_data( &swit->tests->area,
126 		NULL, &swit->n, NULL, NULL );
127 	if( swit->n > 255 ||
128 		swit->n < 1 ) {
129 		vips_error( class->nickname, "%s", _( "bad number of tests" ) );
130 		return( -1 );
131 	}
132 
133 	decode = (VipsImage **) vips_object_local_array( object, swit->n );
134 	format = (VipsImage **) vips_object_local_array( object, swit->n );
135 	band = (VipsImage **) vips_object_local_array( object, swit->n + 1 );
136 	size = (VipsImage **) vips_object_local_array( object, swit->n + 1 );
137 
138 	/* Decode RAD/LABQ etc.
139 	 */
140 	for( i = 0; i < swit->n; i++ )
141 		if( vips_image_decode( tests[i], &decode[i] ) )
142 			return( -1 );
143 	tests = decode;
144 
145 	/* Must be uchar.
146 	 */
147 	for( i = 0; i < swit->n; i++ )
148 		if( vips_cast_uchar( tests[i], &format[i], NULL ) )
149 			return( -1 );
150 	tests = format;
151 
152 	/* Images must match in size and bands.
153 	 */
154 	if( vips__bandalike_vec( class->nickname, tests, band, swit->n, 1 ) ||
155 		vips__sizealike_vec( band, size, swit->n ) )
156 		return( -1 );
157 	tests = size;
158 
159 	if( tests[0]->Bands > 1 ) {
160 		vips_error( class->nickname,
161 			"%s", _( "test images not 1-band" ) );
162 		return( -1 );
163 	}
164 
165 	if( vips_image_pipeline_array( swit->out,
166 		VIPS_DEMAND_STYLE_THINSTRIP, tests ) )
167 		return( -1 );
168 
169 	if( vips_image_generate( swit->out,
170 		vips_start_many, vips_switch_gen, vips_stop_many,
171 		tests, swit ) )
172 		return( -1 );
173 
174 	return( 0 );
175 }
176 
177 static void
vips_switch_class_init(VipsSwitchClass * class)178 vips_switch_class_init( VipsSwitchClass *class )
179 {
180 	GObjectClass *gobject_class = G_OBJECT_CLASS( class );
181 	VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class );
182 	VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
183 
184 	gobject_class->set_property = vips_object_set_property;
185 	gobject_class->get_property = vips_object_get_property;
186 
187 	object_class->nickname = "switch";
188 	object_class->description =
189 		_( "find the index of the first non-zero pixel in tests" );
190 	object_class->build = vips_switch_build;
191 
192 	operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
193 
194 	VIPS_ARG_BOXED( class, "tests", 1,
195 		_( "Tests" ),
196 		_( "Table of images to test" ),
197 		VIPS_ARGUMENT_REQUIRED_INPUT,
198 		G_STRUCT_OFFSET( VipsSwitch, tests ),
199 		VIPS_TYPE_ARRAY_IMAGE );
200 
201 	VIPS_ARG_IMAGE( class, "out", 2,
202 		_( "Output" ),
203 		_( "Output image" ),
204 		VIPS_ARGUMENT_REQUIRED_OUTPUT,
205 		G_STRUCT_OFFSET( VipsSwitch, out ) );
206 
207 }
208 
209 static void
vips_switch_init(VipsSwitch * swit)210 vips_switch_init( VipsSwitch *swit )
211 {
212 }
213 
214 static int
vips_switchv(VipsImage ** tests,VipsImage ** out,int n,va_list ap)215 vips_switchv( VipsImage **tests, VipsImage **out, int n, va_list ap )
216 {
217 	VipsArrayImage *tests_array;
218 	int result;
219 
220 	tests_array = vips_array_image_new( tests, n );
221 	result = vips_call_split( "switch", ap, tests_array, out );
222 	vips_area_unref( VIPS_AREA( tests_array ) );
223 
224 	return( result );
225 }
226 
227 /**
228  * vips_switch: (method)
229  * @tests: (array length=n): test these images
230  * @out: (out): output index image
231  * @n: number of input images
232  * @...: %NULL-terminated list of optional named arguments
233  *
234  * The @tests images are evaluated and at each point the index of the first
235  * non-zero value is written to @out. If all @tests are false, the value
236  * (@n + 1) is written.
237  *
238  * Images in @tests must have one band. They are expanded to the
239  * bounding box of the set of images in @tests, and that size is used for
240  * @out. @tests can have up to 255 elements.
241  *
242  * Combine with vips_case() to make an efficient multi-way vips_ifthenelse().
243  *
244  * See also: vips_maplut(), vips_case(), vips_ifthenelse().
245  *
246  * Returns: 0 on success, -1 on error
247  */
248 int
vips_switch(VipsImage ** tests,VipsImage ** out,int n,...)249 vips_switch( VipsImage **tests, VipsImage **out, int n, ... )
250 {
251 	va_list ap;
252 	int result;
253 
254 	va_start( ap, n );
255 	result = vips_switchv( tests, out, n, ap );
256 	va_end( ap );
257 
258 	return( result );
259 }
260 
261