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