tweak ffmpeg mpeg hw probe, update bld prepare, insert tracer source
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / tracer / tracer.C
1 /*
2  * CINELERRA
3  * Copyright (C) 1997-2015 Adam Williams <broadcast at earthling dot net>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include<stdio.h>
22 #include<stdint.h>
23 #include<math.h>
24 #include<string.h>
25 #include<float.h>
26
27 #include "arraylist.h"
28 #include "bccmodels.h"
29 #include "bccolors.h"
30 #include "clip.h"
31 #include "edlsession.h"
32 #include "filexml.h"
33 #include "tracer.h"
34 #include "tracerwindow.h"
35 #include "language.h"
36 #include "vframe.h"
37
38 REGISTER_PLUGIN(Tracer)
39
40 void tracer_pgm(const char *fn,VFrame *vfrm)
41 {
42         FILE *fp = fopen(fn,"w");
43         int w = vfrm->get_w(), h = vfrm->get_h();
44         fprintf(fp,"P5\n%d %d\n255\n",w,h);
45         fwrite(vfrm->get_data(),w,h,fp);
46         fclose(fp);
47 }
48
49 TracerPoint::TracerPoint(float x, float y)
50 {
51         this->x = x;      this->y = y;
52 }
53 TracerPoint::~TracerPoint()
54 {
55 }
56
57 TracerConfig::TracerConfig()
58 {
59         drag = draw = fill = 0;
60         radius = 0;  scale = 1;
61         selected = 0;
62 }
63 TracerConfig::~TracerConfig()
64 {
65 }
66
67 int TracerConfig::equivalent(TracerConfig &that)
68 {
69         if( this->drag != that.drag ) return 0;
70         if( this->draw != that.draw ) return 0;
71         if( this->fill != that.fill ) return 0;
72         if( this->radius != that.radius ) return 0;
73         if( this->scale != that.scale ) return 0;
74         if( this->points.size() != that.points.size() ) return 0;
75         for( int i=0, n=points.size(); i<n; ++i ) {
76                 TracerPoint *ap = this->points[i], *bp = that.points[i];
77                 if( !EQUIV(ap->x, bp->x) ) return 0;
78                 if( !EQUIV(ap->y, bp->y) ) return 0;
79         }
80         return 1;
81 }
82
83 void TracerConfig::copy_from(TracerConfig &that)
84 {
85         this->drag = that.drag;
86         this->draw = that.draw;
87         this->fill = that.fill;
88         this->selected = that.selected;
89         this->radius = that.radius;
90         this->scale = that.scale;
91         points.remove_all_objects();
92         for( int i=0,n=that.points.size(); i<n; ++i ) {
93                 TracerPoint *pt = that.points[i];
94                 add_point(pt->x, pt->y);
95         }
96 }
97
98 void TracerConfig::interpolate(TracerConfig &prev, TracerConfig &next,
99                 long prev_frame, long next_frame, long current_frame)
100 {
101         copy_from(prev);
102 }
103
104 void TracerConfig::limits()
105 {
106 }
107
108 int TracerConfig::add_point(float x, float y)
109 {
110         int i = points.size();
111         points.append(new TracerPoint(x, y));
112         return i;
113 }
114
115 void TracerConfig::del_point(int i)
116 {
117         points.remove_object_number(i);
118 }
119
120
121 Tracer::Tracer(PluginServer *server)
122  : PluginVClient(server)
123 {
124         frm = 0; frm_rows = 0;
125         msk = 0; msk_rows = 0;
126         edg = 0; edg_rows = 0;
127         w = 0;   w1 = w-1;
128         h = 0;   h1 = h-1;
129         color_model = bpp = 0;
130         is_float = is_yuv = 0;
131         comps = comp = 0;
132         ax = 0;  ay = 0;
133         bx = 0;  by = 0;
134         cx = 0;  cy = 0;
135         ex = 0;  ey = 0;
136 }
137
138 Tracer::~Tracer()
139 {
140         delete edg;
141         delete msk;
142 }
143
144 const char* Tracer::plugin_title() { return N_("Tracer"); }
145 int Tracer::is_realtime() { return 1; }
146
147 NEW_WINDOW_MACRO(Tracer, TracerWindow);
148 int Tracer::load_configuration1()
149 {
150         KeyFrame *prev_keyframe = get_prev_keyframe(get_source_position());
151         if( prev_keyframe->position == get_source_position() ) {
152                 read_data(prev_keyframe);
153                 return 1;
154         }
155         return load_configuration();
156 }
157 LOAD_CONFIGURATION_MACRO(Tracer, TracerConfig);
158
159 int Tracer::new_point()
160 {
161         EDLSession *session = get_edlsession();
162         float x = !session ? 0.f : session->output_w / 2.f;
163         float y = !session ? 0.f : session->output_h / 2.f;
164         return config.add_point(x, y);
165 }
166
167 void Tracer::save_data(KeyFrame *keyframe)
168 {
169         FileXML output;
170
171 // cause data to be stored directly in text
172         output.set_shared_output(keyframe->xbuf);
173
174         output.tag.set_title("TRACER");
175         output.tag.set_property("DRAG", config.drag);
176         output.tag.set_property("DRAW", config.draw);
177         output.tag.set_property("FILL", config.fill);
178         output.tag.set_property("RADIUS", config.radius);
179         output.tag.set_property("SCALE", config.scale);
180         output.tag.set_property("SELECTED", config.selected);
181         output.append_tag();
182         output.append_newline();
183         output.tag.set_title("/TRACER");
184         output.append_tag();
185         output.append_newline();
186         for( int i=0, n=config.points.size(); i<n; ++i ) {
187                 TracerPoint *pt = config.points[i];
188                 char point[BCSTRLEN];
189                 sprintf(point,"/POINT_%d",i+1);
190                 output.tag.set_title(point+1);
191                 output.tag.set_property("X", pt->x);
192                 output.tag.set_property("Y", pt->y);
193                 output.append_tag();
194                 output.tag.set_title(point+0);
195                 output.append_tag();
196                 output.append_newline();
197         }
198         output.terminate_string();
199 }
200
201 void Tracer::read_data(KeyFrame *keyframe)
202 {
203         FileXML input;
204         input.set_shared_input(keyframe->xbuf);
205         config.points.remove_all_objects();
206         int result = 0;
207
208         while( !(result=input.read_tag()) ) {
209                 if( input.tag.title_is("TRACER") ) {
210                         config.drag = input.tag.get_property("DRAG", config.drag);
211                         config.draw = input.tag.get_property("DRAW", config.draw);
212                         config.fill = input.tag.get_property("FILL", config.fill);
213                         config.radius = input.tag.get_property("RADIUS", config.radius);
214                         config.scale = input.tag.get_property("SCALE", config.scale);
215                         config.selected = input.tag.get_property("SELECTED", 0);
216                         config.limits();
217                 }
218                 else if( !strncmp(input.tag.get_title(),"POINT_",6) ) {
219                         float x = input.tag.get_property("X", 0.f);
220                         float y = input.tag.get_property("Y", 0.f);
221                         config.add_point(x, y);
222                 }
223         }
224
225         if( !config.points.size() ) new_point();
226 }
227
228 void Tracer::update_gui()
229 {
230         if( !thread ) return;
231         thread->window->lock_window("Tracer::update_gui");
232         TracerWindow *window = (TracerWindow*)thread->window;
233         if( load_configuration1() ) {
234                 window->update_gui();
235                 window->flush();
236         }
237         thread->window->unlock_window();
238 }
239
240 void Tracer::draw_point(TracerPoint *pt)
241 {
242         int d = bmax(w,h) / 200 + 2;
243         int r = d/2+1, x = pt->x, y = pt->y;
244         frm->draw_smooth(x-r,y+0, x-r, y-r, x+0,y-r);
245         frm->draw_smooth(x+0,y-r, x+r, y-r, x+r,y+0);
246         frm->draw_smooth(x+r,y+0, x+r, y+r, x+0,y+r);
247         frm->draw_smooth(x+0,y+r, x-r, y+r, x-r,y+0);
248 }
249
250 void Tracer::draw_points()
251 {
252         for( int i=0, n=config.points.size(); i<n; ++i ) {
253                 TracerPoint *pt = config.points[i];
254                 frm->set_pixel_color(config.selected == i ? GREEN : WHITE);
255                 draw_point(pt);
256         }
257 }
258 void Tracer::draw_edge()
259 {
260         float scale = 1 / 255.0f;
261         int color_model = frm->get_color_model();
262         int bpp = BC_CModels::calculate_pixelsize(color_model);
263         switch( color_model ) {
264         case BC_RGB_FLOAT:
265                 for( int y=0; y<h; ++y ) {
266                         uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
267                         for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
268                                 if( !*ep ) continue;
269                                 float a = *ep * scale;
270                                 float *px = (float *)sp;
271                                 px[0] = px[1] = px[2] = a;
272                         }
273                 }
274                 break;
275         case BC_RGBA_FLOAT:
276                 for( int y=0; y<h; ++y ) {
277                         uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
278                         for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
279                                 if( !*ep ) continue;
280                                 float a = *ep * scale;
281                                 float *px = (float *)sp;
282                                 px[0] = px[1] = px[2] = a;
283                                 px[3] = 1.0f;
284                         }
285                 }
286                 break;
287         case BC_RGB888:
288                 for( int y=0; y<h; ++y ) {
289                         uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
290                         for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
291                                 if( !*ep ) continue;
292                                 float a = *ep * scale;
293                                 uint8_t *px = sp;
294                                 px[0] = px[1] = px[2] = a * 255;
295                         }
296                 }
297                 break;
298         case BC_RGBA8888:
299                 for( int y=0; y<h; ++y ) {
300                         uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
301                         for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
302                                 if( !*ep ) continue;
303                                 float a = *ep * scale;
304                                 uint8_t *px = sp;
305                                 px[0] = px[1] = px[2] = a * 255;
306                                 px[3] = 0xff;
307                         }
308                 }
309                 break;
310         case BC_YUV888:
311                 for( int y=0; y<h; ++y ) {
312                         uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
313                         for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
314                                 if( !*ep ) continue;
315                                 float a = *ep * scale;
316                                 uint8_t *px = sp;
317                                 px[0] = a * 255;
318                                 px[1] = px[2] = 0x80;
319                         }
320                 }
321                 break;
322         case BC_YUVA8888:
323                 for( int y=0; y<h; ++y ) {
324                         uint8_t *sp = frm_rows[y], *ep = edg_rows[y];
325                         for( int x=0; x<w; ++x,++ep,sp+=bpp ) {
326                                 if( !*ep ) continue;
327                                 float a = *ep * scale;
328                                 uint8_t *px = sp;
329                                 px[0] = a * 255;
330                                 px[1] = px[2] = 0x80;
331                                 px[3] = 0xff;
332                         }
333                 }
334                 break;
335         }
336 }
337
338
339 #define PIX_GRADIENT(type, ix, iy) do { \
340         int xi = vx+ix, yi = vy+iy; \
341         if( edg_rows[yi][xi] ) break; \
342         type *px = (type *)(frm_rows[yi] + xi*bpp); \
343         float dv = px[0]-xp[0], v = dv*dv; \
344         for( int c=1; c<comp; ++c ) { \
345                 dv = px[c]-xp[c]; v += dv*dv; \
346         } \
347         v = sqrt(v); \
348         if( vmax < v ) vmax = v; \
349 } while(0)
350 #define ROW_GRADIENT(type, iy) do { \
351         if( vx > 0 ) PIX_GRADIENT(type,-1, iy); \
352         if( iy != 0) PIX_GRADIENT(type, 0, iy); \
353         if( vx < w1) PIX_GRADIENT(type, 1, iy); \
354 } while(0)
355 #define MAX_GRADIENT(type) do { \
356         type *xp = (type *)(frm_rows[vy] + vx*bpp); \
357         if( vy > 0 ) ROW_GRADIENT(type,-1); \
358         ROW_GRADIENT(type, 0); \
359         if( vy < h1 ) ROW_GRADIENT(type, 1); \
360 } while(0)
361
362 #define MAX_PIXEL(type, ix, iy) do { \
363         int vx = cx + ix, vy = cy + iy; \
364         if( edg_rows[vy][vx] ) break; \
365         float vv = FLT_MAX; \
366         int dx = ex-vx, dy = ey-vy; \
367         int rr = dx*dx + dy*dy; \
368         if( rr > dd ) break; \
369         if( rr > 0 ) { \
370                 float r = (float)(ix*dx + iy*dy) / rr; \
371                 float vmax = 0; \
372                 MAX_GRADIENT(type); \
373                 vv = r + vmax; \
374         } \
375         if( maxv < vv ) { \
376                 maxv = vv; \
377                 nx = vx;  ny = vy; \
378         } \
379 } while(0)
380 #define ROW_MAX(type, iy) do { \
381         if( cx >  0 ) MAX_PIXEL(type,-1, iy); \
382         if( iy != 0 ) MAX_PIXEL(type, 0, iy); \
383         if( cx < w1 ) MAX_PIXEL(type, 1, iy); \
384 } while(0)
385
386 int Tracer::step()
387 {
388         int ret = 0;
389         if( !edg_rows[cy][cx] ) {
390                 points.add(cx,cy);
391                 edg_rows[cy][cx] = 0xff;
392         }
393         int dx = ex-cx, dy = ey-cy;
394         int dd = dx*dx + dy*dy;
395         if( !dd ) return ret;
396         int nx = cx, ny = cy;
397         double maxv = -FLT_MAX;
398         if( cy > 0 )  ROW_MAX(uint8_t,-1);
399         ROW_MAX(uint8_t, 0);
400         if( cy < h1 ) ROW_MAX(uint8_t, 1);
401         cx = nx;  cy = ny;
402         return maxv > 0 ? 1 : 0;
403 }
404
405 void Tracer::trace(int i0, int i1)
406 {
407         TracerPoint *pt0 = config.points[i0];
408         TracerPoint *pt1 = config.points[i1];
409         cx = pt0->x;  bclamp(cx, 0, w1);
410         cy = pt0->y;  bclamp(cy, 0, h1);
411         ex = pt1->x;  bclamp(ex, 0, w1);
412         ey = pt1->y;  bclamp(ey, 0, h1);
413         while( step() );
414 }
415
416 int Tracer::smooth()
417 {
418         int &n = points.total, m = 0;
419         if( n < 3 ) return m;
420         TracePoint *ap;
421         TracePoint *bp = &points[0];
422         TracePoint *cp = &points[1];
423         for( ; n>=3; --n ) {
424                 ap = &points[n-1];
425                 if( abs(ap->x-cp->x)<2 && abs(ap->y-cp->y)<2 ) {
426                         edg_rows[bp->y][bp->x] = 0;
427                         ++m;
428                 }
429                 else
430                         break;
431         }
432         if( n < 3 ) return m;
433         ap = &points[0];
434         bp = &points[1];
435         for( int i=2; i<n; ++i ) {
436                 cp = &points[i];
437                 if( abs(ap->x-cp->x)<2 && abs(ap->y-cp->y)<2 &&
438                     ( (bp->x==ap->x || bp->x==cp->x) &&
439                       (bp->y==ap->y || bp->y==cp->y) ) ) {
440                         edg_rows[bp->y][bp->x] = 0;
441                         ++m;
442                 }
443                 else {
444                         ++ap;  ++bp;
445                 }
446                 bp->x = cp->x;  bp->y = cp->y;
447         }
448         n -= m;
449         return m;
450 }
451
452 #if 0
453 int winding2(int x, int y, TracePoints &pts, int n)
454 {
455         int w = 0;
456         int x0 = pts[0].x-x, y0 = pts[0].y-y;
457         for( int x1,y1,i=1; i<n; x0=x1,y0=y1,++i ) { 
458                 x1 = pts[i].x-x;  y1 = pts[i].y-y;
459                 if( y0*y1 < 0 ) {                          // crosses x axis
460                         int xi = x0 - y0*(x1-x0)/(y1-y0);  // x-intercept
461                         if( xi > 0 ) w += y0<0 ? 2 : -2;   // crosses x on plus side
462                 }
463                 else if( !y0 && x0 > 0 ) w += y1>0 ? 1 : -1;
464                 else if( !y1 && x1 > 0 ) w += y0>0 ? 1 : -1;
465         }
466         return w;
467 }
468 #endif
469
470 class FillRegion
471 {
472         class segment { public: int y, lt, rt; };
473         ArrayList<segment> stack;
474
475         void push(int y, int lt, int rt) {
476                 segment &seg = stack.append();
477                 seg.y = y;  seg.lt = lt;  seg.rt = rt;
478         }
479         void pop(int &y, int &lt, int &rt) {
480                 segment &seg = stack.last();
481                 y = seg.y;  lt = seg.lt;  rt = seg.rt;
482                 stack.remove();
483         }
484  
485         int w, h;
486         uint8_t *edg;
487         uint8_t *msk;
488         bool edge_pixel(int i) { return edg[i] > 0; }
489
490 public:
491         void fill(int x, int y);
492         void run();
493
494         FillRegion(VFrame *edg, VFrame *msk);
495 };
496
497 FillRegion::FillRegion(VFrame *edg, VFrame *msk)
498 {
499         this->w = msk->get_w();
500         this->h = msk->get_h();
501         this->msk = (uint8_t*) msk->get_data();
502         this->edg = (uint8_t*) edg->get_data();
503 }
504
505 void FillRegion::fill(int x, int y)
506 {
507         push(y, x, x);
508 }
509
510 void FillRegion::run()
511 {
512         while( stack.size() > 0 ) {
513                 int y, ilt, irt;
514                 pop(y, ilt, irt);
515                 int ofs = y*w + ilt;
516                 for( int x=ilt; x<=irt; ++x,++ofs ) {
517                         if( msk[ofs] ) continue;
518                         msk[ofs] = 0xff;
519                         if( edge_pixel(ofs) ) continue;
520                         int lt = x, rt = x;
521                         int lofs = ofs;
522                         for( int i=lt; --i>=0; lt=i ) {
523                                 if( msk[--lofs] ) break;
524                                 msk[lofs] = 0xff;
525                                 if( edge_pixel(lofs) ) break;
526                         }
527                         int rofs = ofs;
528                         for( int i=rt; ++i< w; rt=i ) {
529                                 if( msk[++rofs] ) break;
530                                 msk[rofs] = 0xff;
531                                 if( edge_pixel(rofs) ) break;
532                         }
533                         if( y+1 <  h ) push(y+1, lt, rt);
534                         if( y-1 >= 0 ) push(y-1, lt, rt);
535                 }
536         }
537 }
538
539 void Tracer::feather(int r, double s)
540 {
541         if( !r || !s ) return;
542         int dir = r<0 ? (r=-r, -1) : 1;
543         if( dir < 0 ) s = 1./s;
544         int rr = r * r;
545         int psf[rr];  // pt spot fn
546         int k = dir>=0 ? 0 : rr-1;
547         for( int i=0; i<rr; ++i,k+=dir )
548                 psf[k] = 255*exp(-s*((double)i/rr));
549         int mx = dir > 0 ? 0xff : 0;
550         for( int i=0,n=points.size(); i<n; ++i ) {
551                 TracePoint *pt = &points[i];
552                 int xs = pt->x-r, xn=pt->x+r;
553                 bclamp(xs, 0, w);
554                 bclamp(xn, 0, w);
555                 int ys = pt->y-r, yn=pt->y+r;
556                 bclamp(ys, 0, h);
557                 bclamp(yn, 0, h);
558                 for( int y=ys ; y<yn; ++y ) {
559                         for( int x=xs; x<xn; ++x ) {
560                                 if( msk_rows[y][x] == mx ) continue;
561                                 int dx = x-pt->x, dy = y-pt->y;
562                                 int dd = dx*dx + dy*dy;
563                                 if( dd >= rr ) continue;
564                                 int pix = msk_rows[y][x], v = psf[dd];
565                                 msk_rows[y][x] = dir >= 0 ? pix+v-(pix*v)/255 : (pix*v)/255;
566                         }
567                 }
568         }
569 }
570
571 void Tracer::draw_mask()
572 {
573         switch( color_model ) {
574         case BC_RGB888:
575                 for( int y=0; y<h; ++y ) {
576                         uint8_t *mp = msk_rows[y];
577                         uint8_t *rp = frm_rows[y];
578                         for( int x=0; x<w; rp+=bpp,++x ) {
579                                 if( !mp[x] ) continue;
580                                 int a = 0xff - mp[x];
581                                 rp[0] = a*rp[0] / 0xff;
582                                 rp[1] = a*rp[1] / 0xff;
583                                 rp[2] = a*rp[2] / 0xff;
584                         }
585                 }
586                 break;
587         case BC_YUV888:
588                 for( int y=0; y<h; ++y ) {
589                         uint8_t *mp = msk_rows[y];
590                         uint8_t *rp = frm_rows[y];
591                         for( int x=0; x<w; rp+=bpp,++x ) {
592                                 if( !mp[x] ) continue;
593                                 int a = 0xff - mp[x];
594                                 rp[0] = a*rp[0] / 0xff;
595                                 rp[1] = a*(rp[1]-0x80)/0xff + 0x80;
596                                 rp[2] = a*(rp[2]-0x80)/0xff + 0x80;
597                         }
598                 }
599                 break;
600         case BC_RGB_FLOAT:
601                 for( int y=0; y<h; ++y ) {
602                         uint8_t *mp = msk_rows[y];
603                         uint8_t *rp = frm_rows[y];
604                         for( int x=0; x<w; rp+=bpp,++x ) {
605                                 if( !mp[x] ) continue;
606                                 float a = 1 - mp[x]/255.f;
607                                 float *fp = (float*)rp;
608                                 fp[0] *= a;  fp[1] *= a;  fp[2] *= a;
609                         }
610                 }
611                 break;
612         case BC_RGBA8888:
613         case BC_YUVA8888:
614                 for( int y=0; y<h; ++y ) {
615                         uint8_t *mp = msk_rows[y];
616                         uint8_t *rp = frm_rows[y];
617                         for( int x=0; x<w; rp+=bpp,++x ) {
618                                 rp[3] = 0xff - mp[x];
619                         }
620                 }
621                 break;
622         case BC_RGBA_FLOAT:
623                 for( int y=0; y<h; ++y ) {
624                         uint8_t *mp = msk_rows[y];
625                         uint8_t *rp = frm_rows[y];
626                         for( int x=0; x<w; rp+=bpp,++x ) {
627                                 float *fp = (float*)rp;
628                                 fp[3] = 1 - mp[x]/255.f;
629                         }
630                 }
631                 break;
632         }
633 }
634
635 int Tracer::process_buffer(VFrame *frame, int64_t start_position, double frame_rate)
636 {
637         int redraw = load_configuration1();
638         frm = frame;
639         frm_rows = frm->get_rows();
640         w = frm->get_w();  w1 = w-1;
641         h = frm->get_h();  h1 = h-1;
642         color_model = frm->get_color_model();
643         bpp = BC_CModels::calculate_pixelsize(color_model);
644         is_float = BC_CModels::is_float(color_model);
645         is_yuv = BC_CModels::is_yuv(color_model);
646         has_alpha = BC_CModels::has_alpha(color_model);
647         comps = BC_CModels::components(color_model);
648         comp = bmin(comps, 3);
649         read_frame(frm, 0, start_position, frame_rate, 0);
650         if( !edg ) redraw = 1;
651         VFrame::get_temp(edg, w, h, BC_GREY8);
652         if( redraw ) {
653                 edg->clear_frame();
654                 edg_rows = edg->get_rows();
655
656                 int n = config.points.size()-1;
657                 points.clear();
658                 for( int i=0; i<n; ++i )
659                         trace(i,i+1);
660                 if( n > 0 )
661                         trace(n,0);
662                 while( smooth() > 0 );
663
664                 if( config.fill && points.size() > 2 ) {
665                         int l = points.size(), l2 = l/2;
666                         TracePoint *pt0 = &points[0], *pt1 = &points[l2];
667                         int cx = (pt0->x+pt1->x)/2, cy = (pt0->y+pt1->y)/2;
668                         VFrame::get_temp(msk, w, h, BC_GREY8);
669                         msk->clear_frame();
670                         msk_rows = msk->get_rows();
671
672                         FillRegion fill_region(edg, msk);
673                         fill_region.fill(cx, cy);
674                         fill_region.run();
675
676                         feather(config.radius, config.scale);
677                 }
678         }
679         if( config.fill )
680                 draw_mask();
681         if( config.draw )
682                 draw_edge();
683         if( config.drag )
684                 draw_points();
685         return 0;
686 }
687