1 /* @(#) Function which convolves and subsamples VASARI format picture
2  * @(#) with a mask stored in a file argument.
3  * @(#)
4  * @(#) int im_convsub( in, out, mask, xskip, yskip )
5  * @(#) IMAGE *in, *out;
6  * @(#) INTMASK *mask;  details in vips.h
7  * @(#) int xskip, yskip;  is the subsamping factor along both directions
8  * @(#)
9  * @(#) Returns either 0 (sucess) or -1 (fail)
10  * @(#)
11  * @(#) Picture can have any number of channels (max 64).
12  * @(#) It is assummed that the output picture is subsampled on
13  * @(#) both directions by a factor of xskip horizontally and yskip vertically.
14  *
15  * Copyright: 1990, N. Dessipris.
16  *
17  * Author: Nicos Dessipris
18  * Written on: 29/04/1991
19  * Modified on:
20  */
21 
22 /*
23 
24     This file is part of VIPS.
25 
26     VIPS is free software; you can redistribute it and/or modify
27     it under the terms of the GNU Lesser General Public License as published by
28     the Free Software Foundation; either version 2 of the License, or
29     (at your option) any later version.
30 
31     This program is distributed in the hope that it will be useful,
32     but WITHOUT ANY WARRANTY; without even the implied warranty of
33     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
34     GNU Lesser General Public License for more details.
35 
36     You should have received a copy of the GNU Lesser General Public License
37     along with this program; if not, write to the Free Software
38     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
39     02110-1301  USA
40 
41  */
42 
43 /*
44 
45     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
46 
47  */
48 
49 #ifdef HAVE_CONFIG_H
50 #include <config.h>
51 #endif /*HAVE_CONFIG_H*/
52 #include <vips/intl.h>
53 
54 #include <stdio.h>
55 #include <stdlib.h>
56 
57 #include <vips/vips.h>
58 #include <vips/vips7compat.h>
59 #include <vips/internal.h>
60 
61 /* Create multiplication luts for all non zero elements  of the original mask;
62  * which is kept in buffer of length buffersize
63  * cnt is needed for freeing luts
64  */
65 static int
im__create_int_luts(int * buffer,int buffersize,int ** orig_luts,int ** luts,int * cnt)66 im__create_int_luts( int *buffer, int buffersize,
67 	int **orig_luts, int **luts, int *cnt )
68 {
69 	int *pbuffer;
70 	int *buf1, *buf2, *pbuf1, *pbuf2;
71 	int i, j;
72 	int min, max;
73 	int mark; /* used to mark the buffer mark = max+1 */
74 	int counter; /* counts the no of unique elms in mask; returned in cnt*/
75 
76 	buf1 = (int*)calloc( (unsigned)buffersize, sizeof(int) );
77 	buf2 = (int*)calloc( (unsigned)buffersize, sizeof(int) );
78 	if ( ( buf1 == NULL ) || ( buf2 == NULL ) )
79 		{
80 		im_error( "im_create_int_luts", "%s", _( "calloc failed (1)") );
81 		return( -1 );
82 		}
83 
84 	pbuffer = buffer;
85 	pbuf1 = buf1;
86 	/* find max and copy mask to buf1 */
87 	max = *pbuffer;
88 	for ( i=0; i < buffersize; i++ )
89 		{
90 		if ( *pbuffer > max )
91 			max = *pbuffer;
92 		*pbuf1++ = *pbuffer++;
93 		}
94 	mark = max + 1;
95 	pbuf1 = buf1;
96 	pbuf2 = buf2;
97 	counter = 0;
98 /* find a min at a time; put it into buf2 and mark all values of
99  * buf1 equal to found min, to INT_MAX
100  */
101 	for ( i=0; i < buffersize; i++ )
102 		{
103 		min = mark + 1; /* force min to be greater than mark */
104 		pbuf1 = buf1;
105 		/* find a min */
106 		for ( j=0; j < buffersize; j++ )
107 			{
108 			if ( *pbuf1 < min )
109 				min = *pbuf1;
110 			pbuf1++;
111 			}
112 		if ( min == mark )	/* all min are found */
113 			break;
114 		*pbuf2++ = min;
115 		counter++;
116 		pbuf1 = buf1;
117 		for ( j=0; j < buffersize; j++ ) /* mark values equal to min */
118 			{
119 			if ( *pbuf1 == min )
120 				*pbuf1 = mark;
121 			pbuf1++;
122 			}
123 		}
124 /* buf2 should keep now counter unique values of the mask, descending order
125  * Malloc counter luts and initialise them
126  */
127 	pbuf2 = buf2;
128 	for ( i=0; i<counter; i++)
129 		{
130 		orig_luts[i] = (int*)calloc((unsigned)256, sizeof(int));
131 		if (orig_luts[i] == NULL)
132 			{
133 			im_error( "im_create_int_luts", "%s", _( "calloc failed (2)") );
134 			return( -1 );
135 			}
136 		for ( j=0; j<256; j++ )
137 			*(orig_luts[i] + j) = j * (*pbuf2);
138 		pbuf2++;
139 		}
140 
141 	pbuffer = buffer;
142 	for ( i=0; i<buffersize; i++ )
143 		{
144 		j = 0;
145 		while ( 1 )
146 			{
147 			if ( *(buf2 + j) == *pbuffer )
148 				{
149 				luts[i] = orig_luts[j];
150 				break;
151 				}
152 			j++;
153 			}
154 		pbuffer++;
155 		}
156 /* free buf1, buf2 */
157 	free((char*)buf1); free( (char*)buf2);
158 	*cnt = counter;
159 	return(0);
160 }
161 
162 
im_convsub(in,out,m,xskip,yskip)163 int im_convsub( in, out, m, xskip, yskip )
164 IMAGE *in, *out;
165 INTMASK *m;
166 int xskip, yskip;
167 {
168 
169 
170 	int x;		/* horizontal direction */
171 	int y;		/* vertical direction */
172 	int n_clipped = 0;
173 	int p_clipped = 0;
174 	int i, b;
175 	PEL **pnts, **cpnt1s, **cpnt2s;	/* to keep pointers to data */
176 	PEL **pnt, **cpnt1, **cpnt2;	/* to keep pointers to data */
177 	PEL *input, *line, *cpline;
178 	int *pm; /* pointer to mask coefficients */
179 	int count; /* no of non zero elms of the original mask */
180 	int *newm, *pnewm; /* pointer to non zero mask coefficients */
181 	int os; /* size of an input line of data */
182 	int ms; /*  is m->xsize * m->ysize */
183 	int **lut_orig, **lut;
184 	int lutcnt = 0;
185 	int rounding, sum;
186 	int tempsize;
187 
188 /* Check input, output and vars */
189 	if ((xskip < 1)||(yskip < 1))
190                 {
191                 im_error( "im_convsub", "%s", _( "xskip and yskip must be >= 1") );
192                 return(-1);
193                 }
194 	if (im_iocheck(in, out) == -1)
195 		return( -1 );
196 
197 	if ( (in->Coding != IM_CODING_NONE)||
198 	    (in->BandFmt != IM_BANDFMT_UCHAR) )
199 		{
200 		im_error( "im_convsub", "%s", _( "nput should be unsigned char uncoded") );
201 		return(-1);
202 		}
203 
204 /* Prepare output */
205 	if (im_cp_desc(out, in) == -1)
206 		return( -1 );
207 	tempsize = in->Xsize/xskip;
208 	while ( 1 )
209 		{
210 		if ( tempsize * xskip + m->xsize < in->Xsize )
211 			break;
212 		else
213 			tempsize--;
214 		if ( tempsize < 0 )
215 			break;
216 		}
217         out->Xsize = tempsize;
218 	tempsize = in->Ysize/yskip;
219 	while ( 1 )
220 		{
221 		if ( tempsize * yskip + m->ysize < in->Ysize )
222 			break;
223 		else
224 			tempsize--;
225 		if ( tempsize < 0 )
226 			break;
227 		}
228         out->Ysize = tempsize;
229 	if ( ( out->Xsize < 2 )||( out->Ysize < 2 ) ) {
230 		im_error( "im_convsub", "%s", _( "too small output sizes") );
231 		return(-1);
232 	}
233 
234 	if( im_setupout(out) == -1)
235 		return(-1);
236 
237 /* Malloc one line of output data */
238 	os = out->Xsize * out->Bands;
239 	if ( (line=(PEL*)calloc( (unsigned)os, sizeof(char))) == NULL) {
240 		im_error( "im_convsub", "%s", _( "unable to calloc(1)") );
241 		return(-1);
242 	}
243 
244 /* Malloc pointers and put them at correct location */
245 	ms = m->xsize * m->ysize;
246 	count = 0;	/* exclude the non-zero elms */
247 	pm = m->coeff;
248 	for ( i=0; i<ms; i++)
249 		{
250 		if ( *pm++ != 0 )
251 			count++;
252 		}
253 
254 	if (((newm = (int*)calloc((unsigned)count, sizeof(int))) == NULL )||
255 	    ((pnts = (PEL**)calloc((unsigned)count, sizeof(char *))) == NULL)||
256 	    ((cpnt1s=(PEL**)calloc((unsigned)count, sizeof(char *))) == NULL)||
257 	    ((cpnt2s=(PEL**)calloc((unsigned)count, sizeof(char *))) ==NULL ) )
258 		{
259 		im_error( "im_convsub", "%s", _( "unable to calloc(2)") );
260 		return(-1);
261 	}
262 
263 	pnt = pnts;
264 	cpnt1 = cpnt1s;
265 	cpnt2 = cpnt2s;
266 
267 /* copy the non-zero elms of the original mask and set pointers */
268 	i=0;
269 	input = (PEL*)in->data;
270 	pm = m->coeff;
271 	pnewm = newm;
272 	for (y=0; y<m->ysize; y++)
273 		{
274 		for (x=0; x<m->xsize; x++)
275 			{
276 			if ( *pm != 0 )
277 				{
278 				*pnewm++ = *pm;
279 				pnt[i] = (input +(x + y*in->Xsize) * in->Bands);
280 				i++;
281 				}
282 			pm++;
283 			}
284 		}
285 
286 	if ( i != count ) {
287 		im_error( "im_convsub", "%s", _( "impossible state") );
288 		return(-1); }
289 
290 /* Malloc pointers; not all lut_orig are used necessarily */
291         lut_orig = (int**)calloc((unsigned)count, sizeof(int**) );
292         lut = (int**)calloc((unsigned)count, sizeof(int**) );
293         if ( (lut == NULL) || (lut_orig == NULL) ) {
294 		im_error( "im_conv", "%s", _( "unable to calloc(1)") );
295 		return(-1); }
296 
297 /* Create luts; count is needed for freeing pointers. Not all lut_orig are used
298  * if zero elms are detected.
299  */
300         if ( im__create_int_luts(newm, count, lut_orig, lut, &lutcnt ) == -1 )
301 		{
302 		im_error( "im_convsub", "%s", _( "im_create_int_luts failed") );
303                 return(-1);
304 		}
305 
306 	rounding = m->scale/2;
307 
308 /* Output out->Ysize processed lines */
309 	for(y=0; y < out->Ysize; y++)
310 		{
311 		cpline = line;
312 		for (i=0; i<count; i++)
313 			{
314 			cpnt1[i] = pnt[i];
315 				/* skip yskip input lines */
316 			pnt[i] += ( in->Xsize * in->Bands * yskip );
317 			}
318 
319 		/* process out->Xsize points */
320 		for( x = 0; x < out->Xsize; x++ )
321 			{
322 			for (i=0; i<count; i++)
323 				{ /* skip xskip elms */
324 				cpnt2[i] = cpnt1[i];
325 				cpnt1[i] += xskip * in->Bands;
326 				}
327 			for ( b=0; b<out->Bands; b++ )
328 				{
329 				sum = 0;
330 				for (i=0; i<count; i++)
331 					{	/* core of convolution */
332 					sum += *( lut[i] + (*cpnt2[i]) );
333 					cpnt2[i]++;
334 					}
335 				sum = ( (sum+rounding)/m->scale ) + m->offset;
336 
337 				if ( sum < (int)0 )
338 					{ n_clipped++; sum = (int)0; }
339 				else if ( sum > (int)255)
340 					{ p_clipped++; sum = (int)255; }
341 				*cpline++ = (unsigned char)sum;
342 				}
343 			}
344 
345 		/* Output the calculated line */
346 		if ( im_writeline(y, out, (PEL*)line) == -1 )
347 			{
348 			free((char*)line); free((char*)newm);
349 			free((char*)pnts);
350 			free((char*)cpnt1s); free((char*)cpnt2s);
351 			for ( i=0; i<lutcnt; i++)
352 				free( (char*)lut_orig[i] );
353 			free( (char*)lut_orig ); free( (char*)lut );
354 			return(-1);
355 			}
356 		}		/* end of the for (..y..) loop */
357 
358 	if (n_clipped || p_clipped)
359                fprintf(stderr,
360                "im_convsub: %d pels over 255 and %d under 0 clipped\n",
361                 p_clipped, n_clipped);
362 
363 	free((char*)line); free((char*)newm);
364 	free((char*)pnts);
365 	free((char*)cpnt1s); free((char*)cpnt2s);
366 	for ( i=0; i<lutcnt; i++)
367 		free( (char*)lut_orig[i] );
368 	free( (char*)lut_orig ); free( (char*)lut );
369 
370 	return(0);
371 }
372