1 /* fill.c:  Filling a HF with the fill bucket / pen
2  *
3  * Copyright (C) 2003-2005 Patrice St-Gelais
4  *         patrstg@users.sourceforge.net
5  *         www.oricom.ca/patrice.st-gelais
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 #include <stdlib.h>
22  #include "hf.h"
23  #include "hf_calc.h"
24  #include "fill.h"
25 
26 #define test_fill(value_to_test,v1,v2,select_mode,output_value) ( (value_to_test>=v1) && (value_to_test<v2) && ((select_mode!=SELECT_SUBTRACT) || (output_value>MIN_FILLING_VALUE)) )
27 
28 // Local structure used as a "railing" to check for cycles in the recursive "fill bucket" algorithm
29 // ... because I'm not sure the algorithm is 100% safe
30 // We test only 1024x1024 HF or smaller - 2010-07-14 Not anymore true!
31 // 4 spans are kept for each Y
32 // From-To X are encoded with the direction in an unsigned long int value
33 
34 #define SPAN_LIST_LENGTH 16384
35 #define SPAN_MAX 16384
36 static unsigned long int span_list[SPAN_LIST_LENGTH];
37 gint span_count=0;
38 
fill_pen_free(fill_struct * f)39 void fill_pen_free (fill_struct *f) {
40 	if (f) {
41 		reset_seeds (f);
42 		x_free (f);
43 	}
44 }
45 
fill_pen_new(hf_type default_height,gint default_select_mode,gint default_percent)46 fill_struct *fill_pen_new(hf_type default_height, gint default_select_mode, gint default_percent) {
47 	fill_struct *f;
48 	f =  (fill_struct *) x_malloc(sizeof(fill_struct), "fill_struct");
49 	f->height = default_height;
50 	f->select_mode = default_select_mode;
51 	f->percent = default_percent;
52 	f->seeds_list = NULL;
53 	f->noise_exponent = 1;
54 	return f;
55 }
56 
add_seed(fill_struct * f,gint x,gint y)57 void add_seed (fill_struct *f, gint x, gint y) {
58 	seed_coord *s;
59 //	printf("ADD_SEED: %d, %d\n",x,y);
60 	if (f->select_mode==SELECT_REPLACE)
61 		reset_seeds(f);
62 	s = (seed_coord*) x_malloc (sizeof(seed_coord), "seed_coord");
63 	s->x = x;
64 	s->y = y;
65 	s->select_mode = f->select_mode;
66 	f->seeds_list = g_list_append (f->seeds_list, (gpointer) s);
67 //	printf("List length in ADD_SEED: %d\n",g_list_length(f->seeds_list));
68 //	if (g_list_length(f->seeds_list)>10)
69 //		exit(0);
70 }
71 
reset_seeds(fill_struct * f)72 void reset_seeds (fill_struct *f) {
73 	// Resets the filling seeds list to a NULL list
74 	GList *node;
75 //	printf("RESET_SEEDS; list length: %d\n",g_list_length(f->seeds_list));
76 	for (node = f->seeds_list; node; node = node->next) {
77 		x_free((seed_coord *) node->data);
78 	}
79 	g_list_free(f->seeds_list);
80 	f->seeds_list = NULL;
81 }
82 
encode_span(gint from,gint to,gint direction)83 unsigned long int encode_span (gint from, gint to, gint direction){
84 	unsigned long int code; // We assume it's at least 32 bits long (there could be a portability problem here!)
85 	code = code | (unsigned long int) direction;
86 	code = code | (((unsigned long int) from) << 2);
87 	code = code | (((unsigned long int) from) << 14);
88 	return code;
89 };
90 
write_span(gint y,unsigned long int code)91 gboolean write_span (gint y, unsigned long int code) {
92 	// Returns TRUE if SPAN found, FALSE otherwise
93 	// If the span bucket is full, overwrite one chosen randomly
94 	gint i;
95 	for (i=0; i<4; i++) {
96 		if (span_list[(y*4)+i]==code) {
97 			return TRUE;
98 		}
99 		if (span_list[(y*4)+i])
100 			continue;
101 		span_list[(y*4)+i]=code;
102 		break;
103 	}
104 	if (i==4) { // Bucket full
105 		span_list[(y*4)+rand()%4]=code;
106 //		printf("BUCKET FULL for y=%d\n",y);
107 	}
108 	span_count++;
109 	return FALSE;
110 } ;
111 
fill_span_from_shadow(hf_type * buffer_in,hf_type * buffer_out,gint xmax,gint ymax,hf_type v1,hf_type v2,hf_type filling_value,gint y,gint x1,gint x2,gint direction,gint select_mode)112 void fill_span_from_shadow (hf_type *buffer_in, hf_type *buffer_out, gint xmax, gint ymax,
113 	hf_type v1, hf_type v2, hf_type filling_value,
114 	gint y, gint x1, gint x2, gint direction, gint select_mode) {
115 
116 	gint i, mark1, mark2;
117 	glong value_to_test;
118 	hf_type output_value;
119 	gboolean span_end, filled, to_fill;
120 
121 	filled = FALSE;
122 
123 //	printf("FILLING y = %d, from x = %d to %d, direction = %d; value: %d\n",y, x1, x2, direction, filling_value);
124 
125 	if (write_span(y,encode_span(direction,x1,x2))) {
126 
127 //		printf("FILLING in DOUBLE y = %d, from x = %d to %d, direction = %s; value: %d; mode: %s\n",y, x1, x2,
128 //		((direction==0)?"FILL_UP":((direction==1)?"FILL_DOWN":"FILL_BOTH")), filling_value,
129 //		 ((select_mode==0)?"SELECT_REPLACE":((select_mode==1)?"SELECT_ADD":"SELECT_SUBTRACT")));
130 		return;
131 	}
132 //	Direction: FILL_UP (North), FILL_DOWN (South), FILL_BOTH
133 
134 	if ( direction==FILL_BOTH ) {
135 
136 		if ( (y-1) >= 0 )
137 			fill_span_from_shadow (buffer_in, buffer_out, xmax,ymax, v1,v2,
138 				filling_value, y, x1, x2, FILL_UP, select_mode);
139 		if ( (y+1) <= ymax)
140 			fill_span_from_shadow (buffer_in, buffer_out, xmax,ymax, v1,v2,
141 				filling_value, y, x1, x2, FILL_DOWN, select_mode);
142 		return;
143 	}
144 
145 	// 1. Calculate direction (FILL_UP or FILL_DOWN)
146 
147 	if ( direction==FILL_UP )
148 		y--;
149 	else
150 		y++;
151 
152 	// Test the boundaries in case the function is called
153 	// at the first recursivity level with direction <> FILL_BOTH
154 	if ( (y<0) || (y>=ymax) )
155 		return;
156 
157 	// 2. Process span
158 
159 	// 2.1 Backup original span
160 	mark1 = x1;
161 	mark2 = x2;
162 
163 	// 2.2 For x1, if the filling test is true, extend the span as needed
164 
165 	output_value = *(buffer_out + VECTORIZE(x1,y,xmax));
166 	if ( (buffer_out==buffer_in) || (select_mode==SELECT_SUBTRACT))
167 		value_to_test  = *(buffer_in + VECTORIZE(x1,y,xmax));
168 	else // select_mode==SELECT_ADD / REPLACE || buffer_in != buffer_out
169 		value_to_test = ((glong) *(buffer_in + VECTORIZE(x1,y,xmax))) + ((glong) output_value);
170 
171 	if ( test_fill (value_to_test, v1, v2, select_mode, output_value) ) {
172 
173 		// Filling test is true, we extend the span backwards, filling pixels
174 		for (mark1 = x1-1; mark1>=0; mark1--) {
175 
176 			output_value = *(buffer_out + VECTORIZE(mark1,y,xmax));
177 			if ( (buffer_out==buffer_in) || (select_mode==SELECT_SUBTRACT))
178 				value_to_test  = *(buffer_in + VECTORIZE(mark1,y,xmax));
179 			else // select_mode==SELECT_ADD / REPLACE || buffer_in != buffer_out
180 				value_to_test = ((glong) *(buffer_in + VECTORIZE(mark1,y,xmax))) + ((glong) output_value);
181 
182 			// Stop when the filling test is false
183 			if (! test_fill (value_to_test, v1, v2, select_mode, output_value)){
184 				break;
185 			}
186 			else
187 				*(buffer_out + VECTORIZE(mark1,y,xmax)) = filling_value;
188 		}
189 		mark1++;
190 	}
191 	else {
192 	//	If the filling test is false, narrow the span as needed
193 		while ( mark1<=mark2 ) {
194 			mark1++;
195 
196 			output_value = 	*(buffer_out + VECTORIZE(mark1,y,xmax));
197 			if ( (buffer_out==buffer_in) || (select_mode==SELECT_SUBTRACT))
198 				value_to_test  = *(buffer_in + VECTORIZE(mark1,y,xmax));
199 			else // select_mode==SELECT_ADD / REPLACE || buffer_in != buffer_out
200 				value_to_test = ((glong) *(buffer_in + VECTORIZE(mark1,y,xmax))) + ((glong) output_value);
201 
202 			if ( test_fill (value_to_test, v1, v2, select_mode, output_value) )
203 				break;
204 		}
205 	}
206 
207 	if (mark1>mark2)
208 		return;
209 
210 	// Run from mark1 through mark2, filling pixels and calling recursively fill_from_shadow
211 	// for the current span, each time a discontinuity is encountered
212 
213 	// At this point, the filling test is true for mark1
214 	// If mark1 < x1, then the mark1-x1 range is already filled, including x1
215 
216 	span_end = FALSE;
217 
218 	// 	We don't need to fill pixels before x1,
219  	//	but we need mark1 to specify the shadow for the span on the next y
220 	i = MAX(x1,mark1);
221 
222 	// We need to fill the shadow between x1 and mark1, if mark1<(x1-1)
223 
224 	if (mark1<(x1-1))
225 		fill_span_from_shadow (buffer_in, buffer_out, xmax,ymax, v1,v2, filling_value, y, mark1, x1, ((direction==FILL_UP) ? FILL_DOWN : FILL_UP ), select_mode );
226 
227 	to_fill = TRUE;
228 	while (i<=mark2) {
229 //		count++;
230 //		if (i==384)
231 //		printf("Count: %d; I: %d; Mark1: %d; Mark2: %d\n",count, i, mark1, mark2);
232 		if ( to_fill ) {
233 			*(buffer_out + VECTORIZE(i,y,xmax)) = filling_value;
234 			if (span_end) // begin a new span
235 				mark1 = i;
236 			span_end = FALSE;
237 			filled = TRUE;
238 		}
239 		else {
240 			// The first time the fill condition is false, a span has ended,
241 			// we must call "fill_from_shadow" for this span
242 			// After, we're looking for the beginning of a new span
243 			if ( (!span_end) && (i>x1)) {
244 				fill_span_from_shadow (buffer_in, buffer_out, xmax,ymax, v1, v2, filling_value, y, mark1, i-1, direction, select_mode);
245 				// If the new span boundary exceeds the old one by more than one pixel,
246 				// we start a filling movement on the other direction
247 				if ( (i-1) > x2 )
248 					fill_span_from_shadow (buffer_in, buffer_out, xmax,ymax, v1,v2, filling_value, y, x2+1, i-1,
249 					((direction==FILL_UP) ? FILL_DOWN : FILL_UP ), select_mode );
250 				span_end = TRUE;
251 				filled = TRUE;
252 			}
253 			mark1 = i+1;
254 			if (mark1>=x2)
255 				break;
256 		}
257 		i++;
258 		if (i==xmax)
259 			break;
260 
261 		output_value = *(buffer_out + VECTORIZE(i,y,xmax));
262 		if ( (buffer_out==buffer_in) || (select_mode==SELECT_SUBTRACT))
263 			value_to_test  = *(buffer_in + VECTORIZE(i,y,xmax));
264 		else // select_mode==SELECT_ADD / REPLACE || buffer_in != buffer_out
265 			value_to_test = ((glong) *(buffer_in + VECTORIZE(i,y,xmax))) + ((glong) output_value);
266 
267 		if ( test_fill (value_to_test, v1, v2, select_mode, output_value) ) {
268 			to_fill = TRUE;
269 			if (i>x2) {
270 				mark2++; // We extend the span as necessary
271 				if (mark2==xmax) {
272 					mark2--;
273 					break;
274 				}
275 			}
276 		}
277 		else
278 			to_fill = FALSE;
279 	}
280 
281 	// "Flush" the last span if required
282 
283 	if (filled && ((i>mark2) || (i==xmax-1))) {
284 		fill_span_from_shadow (buffer_in, buffer_out, xmax,ymax, v1,v2,
285 			filling_value, y, mark1, mark2, direction, select_mode);
286 
287 			// If the new span boundary exceeds the old one by more than one pixel,
288 			// we start a filling movement on the other direction
289 		if ( mark2 > x2 )
290 			fill_span_from_shadow (buffer_in, buffer_out, xmax,ymax, v1,v2, filling_value, y, x2+1, mark2, ((direction==FILL_UP) ? FILL_DOWN : FILL_UP ), select_mode );
291 	}
292 }
293 
fill_area(hf_type * buffer_in,hf_type * buffer_out,gint xmax,gint ymax,hf_type filling_value,hf_type range,gint x,gint y)294 void fill_area (hf_type *buffer_in, hf_type *buffer_out, gint xmax, gint ymax,
295 	hf_type filling_value, hf_type range, gint x, gint y) {
296 //	printf("FILL AREA: xmax=%d; ymax=%d, filling_value=%d, range=%d, x=%d, y=%d\n", xmax, ymax, filling_value, range, x, y);
297 	fill_area_with_mode (buffer_in, buffer_out, xmax, ymax, filling_value, range, x, y, FILL_RANGE, SELECT_REPLACE);
298 }
299 
fill_area_with_mode(hf_type * buffer_in,hf_type * buffer_out,gint xmax,gint ymax,hf_type filling_value,hf_type range,gint x,gint y,gint fill_mode,gint select_mode)300 void fill_area_with_mode (hf_type *buffer_in, hf_type *buffer_out, gint xmax, gint ymax,
301 	hf_type filling_value, hf_type range, gint x, gint y, gint fill_mode, gint select_mode) {
302 
303 //	Fill area in buffer_out around filling seed (x,y) in buffer_in with filling_value
304 //	if surrounding pixels value are in a � range
305 //	1.	Determine first span
306 //	2.	Fill first span
307 //	3.	Fill top and bottom spans from shadow
308 
309 //	buffer_in and buffer_out could be the same (ex. when drawing faults)
310 //	They would be different, for instance, when buffer_in is used as
311 //	a select tool
312 
313 //	2005-04-20 Modified to allow filling up to a maximum
314 
315 	gint i, x1, x2;
316 	hf_type v1, v2, value_to_test;	// v1, v2: value to replace (range)
317 
318 	// Initialize our control structure
319 	if (xmax<=SPAN_MAX) {
320 		for (i=0; i<SPAN_LIST_LENGTH; i++)
321 			span_list[i]=0;
322 		span_count=0;
323 	}
324 
325 	// Range & filling_value should be >= 1
326 	range = MAX(range,1);
327 	filling_value = MAX(filling_value,MIN_FILLING_VALUE);
328 
329 	if (fill_mode==FILL_MAX) {
330 		v1 = 0;
331 		v2 = range;
332 	}
333 	else { // fill_mode == FILL_RANGE
334 		v2 = v1 = *(buffer_in + VECTORIZE(x,y,xmax));
335 		if (v1 >= range)
336 			v1 = v1 - range;
337 		else
338 			v1 = 0;
339 		if (range <= (MAX_HF_VALUE - v2))
340 			v2 = v2 + range;
341 		else
342 			v2 = MAX_HF_VALUE;
343 	}
344 
345 	*(buffer_out + VECTORIZE(x,y,xmax)) = filling_value;
346 
347 	// Find the first span
348 	for (x1=x-1; x1>=0; x1--) {
349 		value_to_test = *(buffer_in + VECTORIZE(x1,y,xmax));
350 		if (!test_fill(value_to_test,v1,v2,select_mode,*(buffer_out + VECTORIZE(x1,y,xmax)))) {
351 			break;
352 		}
353 		else
354 			*(buffer_out + VECTORIZE(x1,y,xmax)) = filling_value;
355 	} // end for x1
356 	x1++;
357 
358 	for (x2=x+1; x2<xmax; x2++) {
359 		value_to_test = *(buffer_in + VECTORIZE(x2,y,xmax));
360 		if (!test_fill(value_to_test,v1,v2,select_mode,*(buffer_out + VECTORIZE(x2,y,xmax)))) {
361 			break;
362 		}
363 		else
364 			*(buffer_out + VECTORIZE(x2,y,xmax)) = filling_value;
365 	} // end for x2
366 	x2--;
367 
368 	fill_span_from_shadow (buffer_in, buffer_out, xmax,ymax, v1,v2,
369 		filling_value, y, x1, x2, FILL_BOTH, select_mode);
370 
371 //	printf("LAST COUNT: %d\n",span_count);
372 }
373 
fill_from_selection(hf_type * input,hf_type * selection,hf_type * output,gint maxx,gint maxy,fill_struct * pen)374 gboolean fill_from_selection (hf_type *input, hf_type *selection, hf_type *output, gint maxx, gint maxy, fill_struct *pen) {
375 	gint i;
376 	glong diff;
377 	gdouble h;
378 	if (!selection)
379 		return FALSE;
380 	h = (gdouble) pen->height;
381 	for (i=0; i<(maxx*maxy); i++) {
382 		*(output+i) = *(input+i);
383 		if (*(selection+i)) {
384 			// *selection contains the height to fill up to
385 			// where we need to fill, otherwise 0 or 1
386 			diff = *(selection+i) - *(input+i);
387 			if (pen->noise_exponent>1.0) {
388 				diff = (glong) (h * pow(((gdouble) diff) / h, pen->noise_exponent));
389 			}
390 			if (diff>0) {
391 				diff = (((glong) pen->percent) * diff) / 100;
392 				*(output+i) += diff;
393 			}
394 		}
395 	}
396 	return TRUE;
397 }
398 
select_for_fill(fill_struct * pen,hf_struct_type * hf,gint x,gint y)399 void select_for_fill (fill_struct *pen, hf_struct_type *hf, gint x, gint y) {
400 
401 	// Select an area in the "magic wand" style,
402 	// Then fill it given the "paint bucket" parameters (fill _struct *)
403 
404 	hf_type filling_value, height;
405 
406 	if (!hf->tmp_buf)
407 		hf_backup(hf);
408 
409 	//	We use "select_buf" for storing the selection mask
410 	if (!hf->select_buf)
411 		hf->select_buf = (hf_type *) x_calloc(sizeof(hf_type) * hf->max_x * hf->max_y, 1, "hf_type (hf->select_buf)");
412 
413 	switch (pen->select_mode) {
414 		case SELECT_ADD:
415 			height = filling_value = pen->height;
416 			break;
417 		case SELECT_SUBTRACT:
418 			filling_value = MIN_FILLING_VALUE;
419 			height = pen->height + 1; // pen->height is bounded by MAX_HF_VALUE-1
420 			break;
421 		case SELECT_REPLACE:
422 		default:
423 			height = filling_value = pen->height;
424 			hf_reset_buffer(hf->select_buf, hf->max_x, hf->max_y);
425 	}
426 
427 	// Height must be >= 1 and <= MAX_HF_VALUE-1
428 	height = MIN(MAX_HF_VALUE-1,MAX(1,height));
429 
430 	fill_area_with_mode (hf->tmp_buf, hf->select_buf, hf->max_x, hf->max_y, filling_value, height, x, y, FILL_MAX, pen->select_mode);
431 
432 	// Uncomment the next line for testing
433 //		memcpy (hf->hf_buf, hf->select_buf, hf->max_x*hf->max_y*sizeof(hf_type));
434 
435 }
436 
refresh_select_from_seeds(fill_struct * f,hf_struct_type * hf)437 void refresh_select_from_seeds (fill_struct *f, hf_struct_type *hf) {
438 	// Recalculate the selection to fill, typically after a height change
439 	GList *node;
440 	seed_coord *s;
441 	gint select_bck;
442 	select_bck = f->select_mode;
443 	if (!g_list_length(f->seeds_list))
444 		return;
445 //	printf("REFRESH_SELECT_FROM_SEEDS; list length: %d\n",g_list_length(f->seeds_list));
446 	if (hf->select_buf)
447 		hf_reset_buffer(hf->select_buf, hf->max_x, hf->max_y);
448 	for (node = f->seeds_list; node; node=node->next) {
449 		s = (seed_coord *) node->data;
450 		f->select_mode = s->select_mode;
451 //		printf("SELECT_MODE: %d; x: %d; y: %d\n",s->select_mode, s->x, s->y);
452 		select_for_fill (f, hf, s->x, s->y);
453 	}
454 	f->select_mode = select_bck;
455 }
456