b48c4d0e17badfbbd8683703557cdf043461fe9d
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / descratch / descratch.C
1 /*
2 DeScratch - Scratches Removing Filter
3 Plugin for Avisynth 2.5
4 Copyright (c)2003-2016 Alexander G. Balakhnin aka Fizick
5 bag@hotmail.ru  http://avisynth.org.ru
6
7 This program is FREE software under GPL licence v2.
8
9 This plugin removes vertical scratches from digitized films.
10 Reworked for cin5 by GG. 03/2018, from the laws of Fizick's
11 Adapted strategy to mark, test, draw during port.
12 */
13
14
15 #include "clip.h"
16 #include "filexml.h"
17 #include "language.h"
18 #include "descratch.h"
19
20 #include <math.h>
21
22 REGISTER_PLUGIN(DeScratchMain)
23
24 DeScratchMain::DeScratchMain(PluginServer *server)
25  : PluginVClient(server)
26 {
27         inf = 0;  sz_inf = 0;
28         src = 0;  dst = 0;
29         tmpy = 0; blury = 0;
30         overlay_frame = 0;
31 }
32
33 DeScratchMain::~DeScratchMain()
34 {
35         delete [] inf;
36         delete src;
37         delete dst;
38         delete blury;
39         delete tmpy;
40         delete overlay_frame;
41 }
42
43 const char* DeScratchMain::plugin_title() { return N_("DeScratch"); }
44 int DeScratchMain::is_realtime() { return 1; }
45
46 void DeScratchConfig::reset()
47 {
48         threshold = 12;
49         asymmetry = 25;
50         min_width = 1;
51         max_width = 2;
52         min_len = 10;
53         max_len = 100;
54         max_angle = 5;
55         blur_len = 2;
56         gap_len = 0;
57         mode_y = MODE_ALL;
58         mode_u = MODE_NONE;
59         mode_v = MODE_NONE;
60         mark = 0;
61         ffade = 100;
62         border = 2;
63         edge_only = 0;
64 }
65
66 DeScratchConfig::DeScratchConfig()
67 {
68         reset();
69 }
70
71 DeScratchConfig::~DeScratchConfig()
72 {
73 }
74
75 int DeScratchConfig::equivalent(DeScratchConfig &that)
76 {
77         return threshold == that.threshold &&
78                 asymmetry == that.asymmetry &&
79                 min_width == that.min_width &&
80                 max_width == that.max_width &&
81                 min_len == that.min_len &&
82                 max_len == that.max_len &&
83                 max_angle == that.max_angle &&
84                 blur_len == that.blur_len &&
85                 gap_len == that.gap_len &&
86                 mode_y == that.mode_y &&
87                 mode_u == that.mode_u &&
88                 mode_v == that.mode_v &&
89                 mark == that.mark &&
90                 ffade == that.ffade &&
91                 border == that.border &&
92                 edge_only == that.edge_only;
93 }
94 void DeScratchConfig::copy_from(DeScratchConfig &that)
95 {
96         threshold = that.threshold;
97         asymmetry = that.asymmetry;
98         min_width = that.min_width;
99         max_width = that.max_width;
100         min_len = that.min_len;
101         max_len = that.max_len;
102         max_angle = that.max_angle;
103         blur_len = that.blur_len;
104         gap_len = that.gap_len;
105         mode_y = that.mode_y;
106         mode_u = that.mode_u;
107         mode_v = that.mode_v;
108         mark = that.mark;
109         ffade = that.ffade;
110         border = that.border;
111         edge_only = that.edge_only;
112 }
113
114 void DeScratchConfig::interpolate(DeScratchConfig &prev, DeScratchConfig &next,
115                 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
116 {
117         copy_from(prev);
118 }
119
120 LOAD_CONFIGURATION_MACRO(DeScratchMain, DeScratchConfig)
121
122 void DeScratchMain::save_data(KeyFrame *keyframe)
123 {
124         FileXML output;
125 // cause data to be stored directly in text
126         output.set_shared_output(keyframe->xbuf);
127 // Store data
128         output.tag.set_title("DESCRATCH");
129         output.tag.set_property("THRESHOLD", config.threshold);
130         output.tag.set_property("ASYMMETRY", config.asymmetry);
131         output.tag.set_property("MIN_WIDTH", config.min_width);
132         output.tag.set_property("MAX_WIDTH", config.max_width);
133         output.tag.set_property("MIN_LEN", config.min_len);
134         output.tag.set_property("MAX_LEN", config.max_len);
135         output.tag.set_property("MAX_ANGLE", config.max_angle);
136         output.tag.set_property("BLUR_LEN", config.blur_len);
137         output.tag.set_property("GAP_LEN", config.gap_len);
138         output.tag.set_property("MODE_Y", config.mode_y);
139         output.tag.set_property("MODE_U", config.mode_u);
140         output.tag.set_property("MODE_V", config.mode_v);
141         output.tag.set_property("MARK", config.mark);
142         output.tag.set_property("FFADE", config.ffade);
143         output.tag.set_property("BORDER", config.border);
144         output.tag.set_property("EDGE_ONLY", config.edge_only);
145         output.append_tag();
146         output.tag.set_title("/DESCRATCH");
147         output.append_tag();
148         output.append_newline();
149         output.terminate_string();
150 }
151
152 void DeScratchMain::read_data(KeyFrame *keyframe)
153 {
154         FileXML input;
155         input.set_shared_input(keyframe->xbuf);
156
157         int result = 0;
158
159         while( !(result = input.read_tag()) ) {
160                 if(input.tag.title_is("DESCRATCH")) {
161                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
162                         config.asymmetry = input.tag.get_property("ASYMMETRY", config.asymmetry);
163                         config.min_width = input.tag.get_property("MIN_WIDTH", config.min_width);
164                         config.max_width = input.tag.get_property("MAX_WIDTH", config.max_width);
165                         config.min_len   = input.tag.get_property("MIN_LEN", config.min_len);
166                         config.max_len   = input.tag.get_property("MAX_LEN", config.max_len);
167                         config.max_angle = input.tag.get_property("MAX_ANGLE", config.max_angle);
168                         config.blur_len  = input.tag.get_property("BLUR_LEN", config.blur_len);
169                         config.gap_len   = input.tag.get_property("GAP_LEN", config.gap_len);
170                         config.mode_y    = input.tag.get_property("MODE_Y", config.mode_y);
171                         config.mode_u    = input.tag.get_property("MODE_U", config.mode_u);
172                         config.mode_v    = input.tag.get_property("MODE_V", config.mode_v);
173                         config.mark      = input.tag.get_property("MARK", config.mark);
174                         config.ffade     = input.tag.get_property("FFADE", config.ffade);
175                         config.border    = input.tag.get_property("BORDER", config.border);
176                         config.edge_only = input.tag.get_property("EDGE_ONLY", config.edge_only);
177                 }
178         }
179 }
180
181 void DeScratchMain::set_extrems_plane(int width, int comp, int thresh)
182 {
183         uint8_t **rows = blury->get_rows();
184         int r = width, r1 = r+1, wd = src_w - r1;
185         int bpp = 3, dsz = r * bpp;
186         int asym = config.asymmetry * 256 / 100;
187         if( thresh > 0 ) {      // black (low value) scratches
188                 for( int y=0; y<src_h; ++y ) {
189                         uint8_t *ip = inf + y*src_w + r1;
190                         uint8_t *dp = rows[y] + r1*bpp + comp;
191                         for( int x=r1; x<wd; ++x,++ip,dp+=bpp ) {
192                                 if( *ip != SD_NULL ) continue;
193                                 uint8_t *lp = dp-dsz, *rp = dp+dsz;
194                                 if( (lp[0]-*dp) > thresh && (rp[0]-*dp) > thresh &&
195                                     (abs(lp[-bpp]-rp[+bpp]) <= asym) &&
196                                     ((lp[0]-lp[+bpp]) + (rp[0]-rp[-bpp]) >
197                                      (lp[-bpp]-lp[0]) + (rp[bpp]-rp[0])) ) {
198                                         *ip = SD_EXTREM;
199                                         for( int i=1; i<r; ++i ) ip[i] = ip[-i] = SD_EXTREM;
200                                 }
201                         }
202                 }
203         }
204         else {                  // white (high value) scratches
205                 for( int y=0; y<src_h; ++y ) {
206                         uint8_t *ip = inf + y*src_w + r1;
207                         uint8_t *dp = rows[y] + r1*bpp + comp;
208                         for( int x=r1; x<wd; ++x,++ip,dp+=bpp ) {
209                                 if( *ip != SD_NULL ) continue;
210                                 uint8_t *lp = dp-dsz, *rp = dp+dsz;
211                                 if( (lp[0]-*dp) < thresh && (rp[0]-*dp) < thresh &&
212                                     (abs(lp[-bpp]-rp[+bpp]) <= asym) &&
213                                     ((lp[0]-lp[+bpp]) + (rp[0]-rp[-bpp]) <
214                                      (lp[-bpp]-lp[0]) + (rp[bpp]-rp[0])) ) {
215                                         *ip = SD_EXTREM;
216                                         for( int i=1; i<r; ++i ) ip[i] = ip[-i] = SD_EXTREM;
217                                 }
218                         }
219                 }
220         }
221 }
222
223 //
224 void DeScratchMain::close_gaps()
225 {
226         int len = config.gap_len * src_h / 100;
227         for( int y=0; y<src_h; ++y ) {
228                 uint8_t *ip = inf + y*src_w;
229                 for( int x=0; x<src_w; ++x,++ip ) {
230                         if( !(*ip & SD_EXTREM) ) continue;
231                         uint8_t *bp = ip, b = *bp;              // expand to previous lines in range
232                         int i = len < y ? len : y;
233                         while( --i>=0 ) *(bp-=src_w) = b;
234                 }
235         }
236 }
237
238 void DeScratchMain::test_scratches()
239 {
240         int w2 = src_w - 2;
241         int min_len = config.min_len * src_h / 100;
242         int max_len = config.max_len * src_h / 100;
243         int maxwidth = config.max_width;
244         float sin_mxa = sin(config.max_angle * M_PI/180.);
245
246         for( int y=0; y<src_h; ++y ) {
247                 for( int x=2; x<w2; ++x ) {
248                         int ofs = y*src_w + x;                  // first candidate
249                         if( !(inf[ofs] & SD_EXTREM) ) continue;
250                         int ctr = ofs, nctr = ctr;
251                         int hy = src_h - y, len;
252                         for( len=0; len<hy; ++len ) {           // check vertical aspect
253                                 uint8_t *ip = inf + ctr;
254                                 int n = 0;                      // number good points in row
255                                 if( ip[-1] & SD_EXTREM ) { ip[-1] |= SD_TESTED; nctr = ctr-1; ++n; }
256                                 if( ip[+1] & SD_EXTREM ) { ip[+1] |= SD_TESTED; nctr = ctr+1; ++n; }
257                                 if( ip[+0] & SD_EXTREM ) { ip[+0] |= SD_TESTED; nctr = ctr+0; ++n; }
258                                 if( !n ) break;
259                                 ctr = nctr + src_w;             // new center for next row test
260                         }
261                         int v = (!config.edge_only || y == 0 || y+len>=src_h) &&
262                                 abs(nctr%src_w - x) < maxwidth + len*sin_mxa &&
263                                 len >= min_len && len <= max_len ? SD_GOOD : SD_REJECT;
264                         ctr = ofs; nctr = ctr;                  // pass2
265                         for( len=0; len<hy; ++len ) {           // cycle along inf
266                                 uint8_t *ip = inf + ctr;
267                                 int n = 0;                      // number good points in row
268                                 if( ip[-1] & SD_TESTED ) { ip[-1] = v; nctr = ctr-1; ++n; }
269                                 if( ip[+1] & SD_TESTED ) { ip[+1] = v; nctr = ctr+1; ++n; }
270                                 if( ip[+0] & SD_TESTED ) { ip[+0] = v; nctr = ctr+0; ++n; }
271                                 if( !n ) break;
272                                 ctr = nctr + src_w;             // new center for next row test
273                         }
274                 }
275         }
276 }
277
278 void DeScratchMain::mark_scratches_plane()
279 {
280         int bpp = 3, dst_w = dst->get_w(), dst_h = dst->get_h();
281         uint8_t **rows = dst->get_rows();
282         for( int y=0; y<dst_h; ++y ) {
283                 uint8_t *ip = inf + y*src_w;
284                 for( int x=0; x<dst_w; ++x,++ip ) {
285                         if( *ip == SD_NULL ) continue;
286                         static uint8_t grn_yuv[3] = { 0xad, 0x28, 0x19, };
287                         static uint8_t ylw_yuv[3] = { 0xdb, 0x0f, 0x89, };
288                         static uint8_t red_yuv[3] = { 0x40, 0x66, 0xef, };
289                         for( int comp=0; comp<3; ++comp ) {
290                                 uint8_t *dp = rows[y] + comp + x*bpp;
291                                 if( *ip & SD_GOOD )
292                                         *dp = config.threshold > 0 ? grn_yuv[comp] : red_yuv[comp];
293                                 else if( *ip & SD_REJECT )
294                                         *dp = ylw_yuv[comp];
295                         }
296                 }
297         }
298 }
299
300 void DeScratchMain::remove_scratches_plane(int comp)
301 {
302         int bpp = 3, w1 = src_w-1;
303         int border = config.border;
304         uint8_t *ip = inf;
305         uint8_t **dst_rows = dst->get_rows();
306         uint8_t **blur_rows = blury->get_rows();
307         float a = config.ffade / 100, b = 1 - a;
308
309         for( int y=0; y<src_h; ++y ) {
310                 int left = -1;
311                 uint8_t *out = dst_rows[y] + comp;
312                 uint8_t *blur = blur_rows[y] + comp;
313                 for( int x=1; x<w1; ++x ) {
314                         uint8_t *dp = ip + x;
315                         if( !(dp[-1]&SD_GOOD) && (dp[0]&SD_GOOD) ) left = x;
316                         if( left < 0 || !(dp[0]&SD_GOOD) || (dp[1]&SD_GOOD) ) continue;
317                         int right = x;
318                         int ctr = (left + right) / 2;                   // scratch center
319                         int r = (right - left + border) / 2 + 1;
320                         left = 0;
321                         int ls = ctr - r, rs = ctr + r;                 // scratch edges
322                         int lt = ls - border, rt = rs + border;         // border edges
323                         if( ls < 0 ) ls = 0;
324                         if( rs > w1 ) rs = w1;
325                         if( lt < 0 ) lt = 0;
326                         if( rt > w1 ) rt = w1;
327                         ls *= bpp;  rs *= bpp;
328                         lt *= bpp;  rt *= bpp;
329                         if( rs > ls ) {
330                                 float s = 1. / (rs - ls);
331                                 for( int i=ls; (i+=bpp)<rs; ) {         // across the scratch
332                                         int lv = a * blur[ls] + b * out[i];
333                                         int rv = a * blur[rs] + b * out[i];
334                                         int v = s * (lv*(rs-i) + rv*(i-ls));
335                                         out[i] = CLIP(v, 0, 255);
336                                 }
337                         }
338                         if( !border ) continue;
339                         if( ls > lt ) {
340                                 float s = 1. / (ls - lt);
341                                 for( int i=lt; (i+=bpp)<=ls; ) {        // at left border
342                                         int lv = a * out[lt] + b * out[i];
343                                         int rv = a * blur[i] + b * out[i];
344                                         int v = s * (lv*(ls-i) + rv*(i-lt));
345                                         out[i] = CLIP(v, 0, 255);
346                                 }
347                         }
348                         if( rt > rs ) {
349                                 float s = 1. / (rt - rs);
350                                 for( int i=rt; (i-=bpp)>=rs; ) {        // at right border
351                                         int lv = a * blur[i] + b * out[i];
352                                         int rv = a * out[rt] + b * out[i];
353                                         int v = s * (rv*(i-rs) + lv*(rt-i));
354                                         out[i] = CLIP(v, 0, 255);
355                                 }
356                         }
357                 }
358                 ip += src_w;
359         }
360 }
361
362 void DeScratchMain::blur(int scale)
363 {
364         int th = (src_h / scale) & ~1;
365         if( tmpy&& (tmpy->get_w() != src_w || tmpy->get_h() != th) ) {
366                 delete tmpy;  tmpy= 0;
367         }
368         if( !tmpy ) tmpy = new VFrame(src_w, th, BC_YUV888);
369         if( blury && (blury->get_w() != src_w || blury->get_h() != src_h) ) {
370                 delete blury;  blury= 0;
371         }
372         if( !blury ) blury = new VFrame(src_w, src_h, BC_YUV888);
373         overlay_frame->overlay(tmpy, src,
374                 0,0,src_w,src_h, 0,0,src_w,th, 1.f, TRANSFER_NORMAL, LINEAR_LINEAR);
375         overlay_frame->overlay(blury, tmpy,
376                 0,0,src_w,th, 0,0,src_w,src_h, 1.f, TRANSFER_NORMAL, CUBIC_CUBIC);
377 }
378
379 void DeScratchMain::copy(int comp)
380 {
381         uint8_t **src_rows = src->get_rows();
382         uint8_t **dst_rows = dst->get_rows();
383         for( int y=0; y<src_h; ++y ) {
384                 uint8_t *sp = src_rows[y] + comp, *dp = dst_rows[y] + comp;
385                 for( int x=0; x<src_w; ++x,sp+=3,dp+=3 ) *sp = *dp;
386         }
387 }
388
389 void DeScratchMain::pass(int comp, int thresh)
390 {
391 // pass for current plane and current sign
392         int w0 = config.min_width, w1 = config.max_width;
393         if( w1 < w0 ) w1 = w0;
394         for( int iw=w0; iw<=w1; ++iw )
395                 set_extrems_plane(iw, comp, thresh);
396 }
397
398 void DeScratchMain::plane_pass(int comp, int mode)
399 {
400         int threshold = config.threshold;
401         if( comp != 0 ) threshold /= 2;  // fakey UV scaling
402         switch( mode ) {
403         case MODE_ALL:
404                 pass(comp, threshold);
405                 copy(comp);
406         case MODE_HIGH:         // fall thru
407                 threshold = -threshold;
408         case MODE_LOW:          // fall thru
409                 pass(comp, threshold);
410                 break;
411         }
412 }
413
414 void DeScratchMain::plane_proc(int comp, int mode)
415 {
416         if( mode == MODE_NONE ) return;
417         remove_scratches_plane(comp);
418 }
419
420 int DeScratchMain::process_realtime(VFrame *input, VFrame *output)
421 {
422         load_configuration();
423         src_w = input->get_w();
424         src_h = input->get_h();
425         if( src_w >= 2*config.max_width+3 ) {
426                 if( !overlay_frame ) {
427                         int cpus = PluginClient::smp + 1;
428                         if( cpus > 8 ) cpus = 8;
429                         overlay_frame = new OverlayFrame(cpus);
430                 }
431                 if( src && (src->get_w() != src_w || src->get_h() != src_h) ) {
432                         delete src;  src = 0;
433                 }
434                 if( !src ) src = new VFrame(src_w, src_h, BC_YUV888);
435                 src->transfer_from(input);
436                 if( dst && (dst->get_w() != src_w || dst->get_h() != src_h) ) {
437                         delete dst;  dst = 0;
438                 }
439                 if( !dst ) dst = new VFrame(src_w, src_h, BC_YUV888);
440                 dst->copy_from(src);
441                 int sz = src_w * src_h;
442                 if( sz_inf != sz ) {  delete [] inf;  inf = 0; }
443                 if( !inf ) inf = new uint8_t[sz_inf=sz];
444                 blur(config.blur_len + 1);
445                 memset(inf, SD_NULL, sz_inf);
446                 plane_pass(0, config.mode_y);
447                 plane_pass(1, config.mode_u);
448                 plane_pass(2, config.mode_v);
449                 close_gaps();
450                 test_scratches();
451                 if( !config.mark ) {
452                         plane_proc(0, config.mode_y);
453                         plane_proc(1, config.mode_u);
454                         plane_proc(2, config.mode_v);
455                 }
456                 else
457                         mark_scratches_plane();
458                 output->transfer_from(dst);
459         }
460         return 0;
461 }
462
463 void DeScratchMain::update_gui()
464 {
465         if( !thread ) return;
466         DeScratchWindow *window = (DeScratchWindow *)thread->get_window();
467         window->lock_window("DeScratchMain::update_gui");
468         if( load_configuration() )
469                 window->update_gui();
470         window->unlock_window();
471 }
472
473 NEW_WINDOW_MACRO(DeScratchMain, DeScratchWindow)
474
475
476 DeScratchWindow::DeScratchWindow(DeScratchMain *plugin)
477  : PluginClientWindow(plugin, xS(512), yS(270), xS(512), yS(270), 0)
478 {
479         this->plugin = plugin;
480 }
481
482 DeScratchWindow::~DeScratchWindow()
483 {
484 }
485
486 void DeScratchWindow::create_objects()
487 {
488         int xs10 = xS(10), xs15 = xS(15), xs16 = xS(16);
489         int ys10 = yS(10), ys15 = yS(15), ys30 = yS(30);
490         int x = xs10, y = ys10;
491         plugin->load_configuration();
492         DeScratchConfig &config = plugin->config;
493
494         BC_Title *title;
495         add_tool(title = new BC_Title(x, y, _("DeScratch:")));
496
497         int w1 = DeScratchReset::calculate_w(this, _("Reset"));
498         add_tool(reset = new DeScratchReset(this, get_w()-w1-xs15, y));
499
500         y += title->get_h() + ys15;
501         int x1 = x, x2 = get_w()/2;
502         add_tool(title = new BC_Title(x1=x, y, _("threshold:")));
503         x1 += title->get_w()+xs16;
504         add_tool(threshold = new DeScratchISlider(this, x1, y, x2-x1-xs10, 0,64, &config.threshold));
505         add_tool(title = new BC_Title(x1=x2, y, _("asymmetry:")));
506         x1 += title->get_w()+xs16;
507         add_tool(asymmetry = new DeScratchFSlider(this, x1, y, get_w()-x1-xs15, 0,100., &config.asymmetry));
508         y += threshold->get_h() + ys10;
509
510         add_tool(title = new BC_Title(x1=x, y, _("Mode:")));
511         x1 += title->get_w()+xs16;
512         add_tool(title = new BC_Title(x1, y, _("y:")));
513         w1 = title->get_w()+xs16;
514         add_tool(y_mode = new DeScratchMode(this, (x1+=w1), y, &config.mode_y));
515         y_mode->create_objects();  x1 += y_mode->get_w()+xs16;
516         add_tool(title = new BC_Title(x1, y, _("u:")));
517         add_tool(u_mode = new DeScratchMode(this, (x1+=w1), y, &config.mode_u));
518         u_mode->create_objects();  x1 += u_mode->get_w()+xs16;
519         add_tool(title = new BC_Title(x1, y, _("v:")));
520         add_tool(v_mode = new DeScratchMode(this, (x1+=w1), y, &config.mode_v));
521         v_mode->create_objects();
522         y += y_mode->get_h() + ys10;
523
524         add_tool(title = new BC_Title(x1=x, y, _("width:")));
525         w1 = title->get_w()+xs16;  x1 += w1;
526         add_tool(title = new BC_Title(x1, y, _("min:")));
527         x1 += title->get_w()+xs16;
528         add_tool(min_width = new DeScratchISlider(this, x1, y, x2-x1-xs10, 1,16, &config.min_width));
529         add_tool(title = new BC_Title(x1=x2, y, _("max:")));
530         x1 += title->get_w()+xs16;
531         add_tool(max_width = new DeScratchISlider(this, x1, y, get_w()-x1-xs15, 1,16, &config.max_width));
532         y += min_width->get_h() + ys10;
533
534         add_tool(title = new BC_Title(x1=x, y, _("len:")));
535         w1 = title->get_w()+xs16;  x1 += w1;
536         add_tool(title = new BC_Title(x1, y, _("min:")));
537         x1 += title->get_w()+xs16;
538         add_tool(min_len = new DeScratchFSlider(this, x1, y, x2-x1-xs10, 0.0,100.0, &config.min_len));
539         add_tool(title = new BC_Title(x1=x2, y, _("max:")));
540         x1 += title->get_w()+xs16;
541         add_tool(max_len = new DeScratchFSlider(this, x1, y, get_w()-x1-xs15, 0.0,100.0, &config.max_len));
542         y += min_len->get_h() + ys10;
543
544         add_tool(title = new BC_Title(x1=x, y, _("len:")));
545         w1 = title->get_w()+xs16;  x1 += w1;
546         add_tool(title = new BC_Title(x1, y, _("blur:")));
547         x1 += title->get_w()+xs16;
548         add_tool(blur_len = new DeScratchISlider(this, x1, y, x2-x1-xs10, 0,8, &config.blur_len));
549         add_tool(title = new BC_Title(x1=x2, y, _("gap:")));
550         x1 += title->get_w()+xs16;
551         add_tool(gap_len = new DeScratchFSlider(this, x1, y, get_w()-x1-xs15, 0.0,100.0, &config.gap_len));
552         y += blur_len->get_h() + ys10;
553
554         add_tool(title = new BC_Title(x1=x, y, _("max angle:")));
555         w1 = title->get_w()+xs16;  x1 += w1;
556         add_tool(max_angle = new DeScratchFSlider(this, x1, y, x2-x1-xs10, 0.0,15.0, &config.max_angle));
557         add_tool(title = new BC_Title(x1=x2, y, _("fade:")));
558         x1 += title->get_w()+xs16;
559         add_tool(ffade = new DeScratchFSlider(this, x1, y, get_w()-x1-xs15, 0.0,100.0, &config.ffade));
560         y += max_angle->get_h() + ys10;
561
562         add_tool(title = new BC_Title(x1=x, y, _("border:")));
563         x1 += title->get_w()+xs16;
564         add_tool(border = new DeScratchISlider(this, x1, y, x2-x1-xs10, 0,16, &config.border));
565         add_tool(mark = new DeScratchMark(this, x1=x2, y));
566         x1 += mark->get_w() + xs10;
567         add_tool(edge_only = new DeScratchEdgeOnly(this, x1, y));
568
569         show_window();
570 }
571
572 void DeScratchWindow::update_gui()
573 {
574         DeScratchConfig &config = plugin->config;
575         threshold->update(config.threshold);
576         asymmetry->update(config.asymmetry);
577         min_width->update(config.min_width);
578         max_width->update(config.max_width);
579         min_len->update(config.min_len);
580         max_len->update(config.max_len);
581         max_angle->update(config.max_angle);
582         blur_len->update(config.blur_len);
583         gap_len->update(config.gap_len);
584         y_mode->update(config.mode_y);
585         u_mode->update(config.mode_u);
586         v_mode->update(config.mode_v);
587         mark->update(config.mark);
588         ffade->update(config.ffade);
589         border->update(config.border);
590         edge_only->update(config.edge_only);
591 }
592
593
594 DeScratchModeItem::DeScratchModeItem(DeScratchMode *popup, int type, const char *text)
595  : BC_MenuItem(text)
596 {
597         this->popup = popup;
598         this->type = type;
599 }
600
601 DeScratchModeItem::~DeScratchModeItem()
602 {
603 }
604
605 int DeScratchModeItem::handle_event()
606 {
607         popup->update(type);
608         return popup->handle_event();
609 }
610
611 DeScratchMode::DeScratchMode(DeScratchWindow *win, int x, int y, int *value)
612  : BC_PopupMenu(x, y, xS(100), "", 1)
613 {
614         this->win = win;
615         this->value = value;
616 }
617
618 DeScratchMode::~DeScratchMode()
619 {
620 }
621
622 void DeScratchMode::create_objects()
623 {
624         add_item(new DeScratchModeItem(this, MODE_NONE, _("None")));
625         add_item(new DeScratchModeItem(this, MODE_LOW,  _("Low")));
626         add_item(new DeScratchModeItem(this, MODE_HIGH, _("High")));
627         add_item(new DeScratchModeItem(this, MODE_ALL,  _("All")));
628         set_value(*value);
629 }
630
631 int DeScratchMode::handle_event()
632 {
633         win->plugin->send_configure_change();
634         return 1;
635 }
636
637 void DeScratchMode::update(int v)
638 {
639         set_value(*value = v);
640 }
641
642 void DeScratchMode::set_value(int v)
643 {
644         int i = total_items();
645         while( --i >= 0 && ((DeScratchModeItem*)get_item(i))->type != v );
646         if( i >= 0 ) set_text(get_item(i)->get_text());
647 }
648
649 DeScratchISlider::DeScratchISlider(DeScratchWindow *win,
650                 int x, int y, int w, int min, int max, int *output)
651  : BC_ISlider(x, y, 0, w, w, min, max, *output)
652 {
653         this->win = win;
654         this->output = output;
655 }
656
657 DeScratchISlider::~DeScratchISlider()
658 {
659 }
660
661 int DeScratchISlider::handle_event()
662 {
663         *output = get_value();
664         win->plugin->send_configure_change();
665         return 1;
666 }
667
668 DeScratchFSlider::DeScratchFSlider(DeScratchWindow *win,
669                 int x, int y, int w, float min, float max, float *output)
670  : BC_FSlider(x, y, 0, w, w, min, max, *output)
671 {
672         this->win = win;
673         this->output = output;
674 }
675
676 DeScratchFSlider::~DeScratchFSlider()
677 {
678 }
679
680 int DeScratchFSlider::handle_event()
681 {
682         *output = get_value();
683         win->plugin->send_configure_change();
684         return 1;
685 }
686
687 DeScratchMark::DeScratchMark(DeScratchWindow *win, int x, int y)
688  : BC_CheckBox(x, y, &win->plugin->config.mark, _("Mark"))
689 {
690         this->win = win;
691 };
692
693 DeScratchMark::~DeScratchMark()
694 {
695 }
696
697 int DeScratchMark::handle_event()
698 {
699         int ret = BC_CheckBox::handle_event();
700         win->plugin->send_configure_change();
701         return ret;
702 }
703
704 DeScratchEdgeOnly::DeScratchEdgeOnly(DeScratchWindow *win, int x, int y)
705  : BC_CheckBox(x, y, &win->plugin->config.edge_only, _("Edge"))
706 {
707         this->win = win;
708 };
709
710 DeScratchEdgeOnly::~DeScratchEdgeOnly()
711 {
712 }
713
714 int DeScratchEdgeOnly::handle_event()
715 {
716         int ret = BC_CheckBox::handle_event();
717         win->plugin->send_configure_change();
718         return ret;
719 }
720
721 DeScratchReset::DeScratchReset(DeScratchWindow *win, int x, int y)
722  : BC_GenericButton(x, y, _("Reset"))
723 {
724         this->win = win;
725 }
726
727 int DeScratchReset::handle_event()
728 {
729         win->plugin->config.reset();
730         win->update_gui();
731         win->plugin->send_configure_change();
732         return 1;
733 }
734