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
7 This program is FREE software under GPL licence v2.
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.
18 #include "descratch.h"
22 REGISTER_PLUGIN(DeScratchMain)
24 DeScratchMain::DeScratchMain(PluginServer *server)
25 : PluginVClient(server)
33 DeScratchMain::~DeScratchMain()
43 const char* DeScratchMain::plugin_title() { return N_("DeScratch"); }
44 int DeScratchMain::is_realtime() { return 1; }
46 void DeScratchConfig::reset()
66 DeScratchConfig::DeScratchConfig()
71 DeScratchConfig::~DeScratchConfig()
75 int DeScratchConfig::equivalent(DeScratchConfig &that)
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 &&
90 ffade == that.ffade &&
91 border == that.border &&
92 edge_only == that.edge_only;
94 void DeScratchConfig::copy_from(DeScratchConfig &that)
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;
110 border = that.border;
111 edge_only = that.edge_only;
114 void DeScratchConfig::interpolate(DeScratchConfig &prev, DeScratchConfig &next,
115 int64_t prev_frame, int64_t next_frame, int64_t current_frame)
120 LOAD_CONFIGURATION_MACRO(DeScratchMain, DeScratchConfig)
122 void DeScratchMain::save_data(KeyFrame *keyframe)
125 // cause data to be stored directly in text
126 output.set_shared_output(keyframe->xbuf);
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);
146 output.tag.set_title("/DESCRATCH");
148 output.append_newline();
149 output.terminate_string();
152 void DeScratchMain::read_data(KeyFrame *keyframe)
155 input.set_shared_input(keyframe->xbuf);
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);
181 void DeScratchMain::set_extrems_plane(int width, int comp, int thresh)
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])) ) {
199 for( int i=1; i<r; ++i ) ip[i] = ip[-i] = SD_EXTREM;
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])) ) {
216 for( int i=1; i<r; ++i ) ip[i] = ip[-i] = SD_EXTREM;
224 void DeScratchMain::close_gaps()
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;
238 void DeScratchMain::test_scratches()
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.);
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; }
259 ctr = nctr + src_w; // new center for next row test
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; }
272 ctr = nctr + src_w; // new center for next row test
278 void DeScratchMain::mark_scratches_plane()
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;
292 *dp = config.threshold > 0 ? grn_yuv[comp] : red_yuv[comp];
293 else if( *ip & SD_REJECT )
300 void DeScratchMain::remove_scratches_plane(int comp)
302 int bpp = 3, w1 = src_w-1;
303 int border = config.border;
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;
309 for( int y=0; y<src_h; ++y ) {
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;
318 int ctr = (left + right) / 2; // scratch center
319 int r = (right - left + border) / 2 + 1;
321 int ls = ctr - r, rs = ctr + r; // scratch edges
322 int lt = ls - border, rt = rs + border; // border edges
324 if( rs > w1 ) rs = w1;
326 if( rt > w1 ) rt = w1;
327 ls *= bpp; rs *= bpp;
328 lt *= bpp; rt *= bpp;
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);
338 if( !border ) continue;
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);
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);
362 void DeScratchMain::blur(int scale)
364 int th = (src_h / scale) & ~1;
365 if( tmpy&& (tmpy->get_w() != src_w || tmpy->get_h() != th) ) {
366 delete tmpy; tmpy= 0;
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;
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);
379 void DeScratchMain::copy(int comp)
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;
389 void DeScratchMain::pass(int comp, int thresh)
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);
398 void DeScratchMain::plane_pass(int comp, int mode)
400 int threshold = config.threshold;
401 if( comp != 0 ) threshold /= 2; // fakey UV scaling
404 pass(comp, threshold);
406 case MODE_HIGH: // fall thru
407 threshold = -threshold;
408 case MODE_LOW: // fall thru
409 pass(comp, threshold);
414 void DeScratchMain::plane_proc(int comp, int mode)
416 if( mode == MODE_NONE ) return;
417 remove_scratches_plane(comp);
420 int DeScratchMain::process_realtime(VFrame *input, VFrame *output)
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);
431 if( src && (src->get_w() != src_w || src->get_h() != src_h) ) {
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) ) {
439 if( !dst ) dst = new VFrame(src_w, src_h, BC_YUV888);
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);
452 plane_proc(0, config.mode_y);
453 plane_proc(1, config.mode_u);
454 plane_proc(2, config.mode_v);
457 mark_scratches_plane();
458 output->transfer_from(dst);
463 void DeScratchMain::update_gui()
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();
473 NEW_WINDOW_MACRO(DeScratchMain, DeScratchWindow)
476 DeScratchWindow::DeScratchWindow(DeScratchMain *plugin)
477 : PluginClientWindow(plugin, xS(512), yS(270), xS(512), yS(270), 0)
479 this->plugin = plugin;
482 DeScratchWindow::~DeScratchWindow()
486 void DeScratchWindow::create_objects()
488 int xs10 = xS(10), xs15 = xS(15), xs16 = xS(16);
489 int ys10 = yS(10), ys15 = yS(15);
490 int x = xs10, y = ys10;
491 plugin->load_configuration();
492 DeScratchConfig &config = plugin->config;
495 add_tool(title = new BC_Title(x, y, _("DeScratch:")));
497 int w1 = DeScratchReset::calculate_w(this, _("Reset"));
498 add_tool(reset = new DeScratchReset(this, get_w()-w1-xs15, y));
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;
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;
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;
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;
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;
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;
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));
572 void DeScratchWindow::update_gui()
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);
594 DeScratchModeItem::DeScratchModeItem(DeScratchMode *popup, int type, const char *text)
601 DeScratchModeItem::~DeScratchModeItem()
605 int DeScratchModeItem::handle_event()
608 return popup->handle_event();
611 DeScratchMode::DeScratchMode(DeScratchWindow *win, int x, int y, int *value)
612 : BC_PopupMenu(x, y, xS(100), "", 1)
618 DeScratchMode::~DeScratchMode()
622 void DeScratchMode::create_objects()
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")));
631 int DeScratchMode::handle_event()
633 win->plugin->send_configure_change();
637 void DeScratchMode::update(int v)
639 set_value(*value = v);
642 void DeScratchMode::set_value(int v)
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());
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)
654 this->output = output;
657 DeScratchISlider::~DeScratchISlider()
661 int DeScratchISlider::handle_event()
663 *output = get_value();
664 win->plugin->send_configure_change();
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)
673 this->output = output;
676 DeScratchFSlider::~DeScratchFSlider()
680 int DeScratchFSlider::handle_event()
682 *output = get_value();
683 win->plugin->send_configure_change();
687 DeScratchMark::DeScratchMark(DeScratchWindow *win, int x, int y)
688 : BC_CheckBox(x, y, &win->plugin->config.mark, _("Mark"))
693 DeScratchMark::~DeScratchMark()
697 int DeScratchMark::handle_event()
699 int ret = BC_CheckBox::handle_event();
700 win->plugin->send_configure_change();
704 DeScratchEdgeOnly::DeScratchEdgeOnly(DeScratchWindow *win, int x, int y)
705 : BC_CheckBox(x, y, &win->plugin->config.edge_only, _("Edge"))
710 DeScratchEdgeOnly::~DeScratchEdgeOnly()
714 int DeScratchEdgeOnly::handle_event()
716 int ret = BC_CheckBox::handle_event();
717 win->plugin->send_configure_change();
721 DeScratchReset::DeScratchReset(DeScratchWindow *win, int x, int y)
722 : BC_GenericButton(x, y, _("Reset"))
727 int DeScratchReset::handle_event()
729 win->plugin->config.reset();
731 win->plugin->send_configure_change();