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