2 DeScratch - Scratches Removing Filter
3 Plugin for Avisynth 2.5
4 Copyright (c)2003-2016 Alexander G. Balakhnin aka Fizick
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, 512, 270, 512, 270, 0)
479 this->plugin = plugin;
482 DeScratchWindow::~DeScratchWindow()
486 void DeScratchWindow::create_objects()
489 plugin->load_configuration();
490 DeScratchConfig &config = plugin->config;
493 add_tool(title = new BC_Title(x, y, _("DeScratch:")));
495 int w1 = DeScratchReset::calculate_w(this, _("Reset"));
496 add_tool(reset = new DeScratchReset(this, get_w()-w1-15, y));
498 y += title->get_h() + 15;
499 int x1 = x, x2 = get_w()/2;
500 add_tool(title = new BC_Title(x1=x, y, _("threshold:")));
501 x1 += title->get_w()+16;
502 add_tool(threshold = new DeScratchISlider(this, x1, y, x2-x1-10, 0,64, &config.threshold));
503 add_tool(title = new BC_Title(x1=x2, y, _("asymmetry:")));
504 x1 += title->get_w()+16;
505 add_tool(asymmetry = new DeScratchFSlider(this, x1, y, get_w()-x1-15, 0,100., &config.asymmetry));
506 y += threshold->get_h() + 10;
508 add_tool(title = new BC_Title(x1=x, y, _("Mode:")));
509 x1 += title->get_w()+16;
510 add_tool(title = new BC_Title(x1, y, _("y:")));
511 w1 = title->get_w()+16;
512 add_tool(y_mode = new DeScratchMode(this, (x1+=w1), y, &config.mode_y));
513 y_mode->create_objects(); x1 += y_mode->get_w()+16;
514 add_tool(title = new BC_Title(x1, y, _("u:")));
515 add_tool(u_mode = new DeScratchMode(this, (x1+=w1), y, &config.mode_u));
516 u_mode->create_objects(); x1 += u_mode->get_w()+16;
517 add_tool(title = new BC_Title(x1, y, _("v:")));
518 add_tool(v_mode = new DeScratchMode(this, (x1+=w1), y, &config.mode_v));
519 v_mode->create_objects();
520 y += y_mode->get_h() + 10;
522 add_tool(title = new BC_Title(x1=x, y, _("width:")));
523 w1 = title->get_w()+16; x1 += w1;
524 add_tool(title = new BC_Title(x1, y, _("min:")));
525 x1 += title->get_w()+16;
526 add_tool(min_width = new DeScratchISlider(this, x1, y, x2-x1-10, 1,16, &config.min_width));
527 add_tool(title = new BC_Title(x1=x2, y, _("max:")));
528 x1 += title->get_w()+16;
529 add_tool(max_width = new DeScratchISlider(this, x1, y, get_w()-x1-15, 1,16, &config.max_width));
530 y += min_width->get_h() + 10;
532 add_tool(title = new BC_Title(x1=x, y, _("len:")));
533 w1 = title->get_w()+16; x1 += w1;
534 add_tool(title = new BC_Title(x1, y, _("min:")));
535 x1 += title->get_w()+16;
536 add_tool(min_len = new DeScratchFSlider(this, x1, y, x2-x1-10, 0.0,100.0, &config.min_len));
537 add_tool(title = new BC_Title(x1=x2, y, _("max:")));
538 x1 += title->get_w()+16;
539 add_tool(max_len = new DeScratchFSlider(this, x1, y, get_w()-x1-15, 0.0,100.0, &config.max_len));
540 y += min_len->get_h() + 10;
542 add_tool(title = new BC_Title(x1=x, y, _("len:")));
543 w1 = title->get_w()+16; x1 += w1;
544 add_tool(title = new BC_Title(x1, y, _("blur:")));
545 x1 += title->get_w()+16;
546 add_tool(blur_len = new DeScratchISlider(this, x1, y, x2-x1-10, 0,8, &config.blur_len));
547 add_tool(title = new BC_Title(x1=x2, y, _("gap:")));
548 x1 += title->get_w()+16;
549 add_tool(gap_len = new DeScratchFSlider(this, x1, y, get_w()-x1-15, 0.0,100.0, &config.gap_len));
550 y += blur_len->get_h() + 10;
552 add_tool(title = new BC_Title(x1=x, y, _("max angle:")));
553 w1 = title->get_w()+16; x1 += w1;
554 add_tool(max_angle = new DeScratchFSlider(this, x1, y, x2-x1-10, 0.0,15.0, &config.max_angle));
555 add_tool(title = new BC_Title(x1=x2, y, _("fade:")));
556 x1 += title->get_w()+16;
557 add_tool(ffade = new DeScratchFSlider(this, x1, y, get_w()-x1-15, 0.0,100.0, &config.ffade));
558 y += max_angle->get_h() + 10;
560 add_tool(title = new BC_Title(x1=x, y, _("border:")));
561 x1 += title->get_w()+16;
562 add_tool(border = new DeScratchISlider(this, x1, y, x2-x1-10, 0,16, &config.border));
563 add_tool(mark = new DeScratchMark(this, x1=x2, y));
564 x1 += mark->get_w() + 10;
565 add_tool(edge_only = new DeScratchEdgeOnly(this, x1, y));
570 void DeScratchWindow::update_gui()
572 DeScratchConfig &config = plugin->config;
573 threshold->update(config.threshold);
574 asymmetry->update(config.asymmetry);
575 min_width->update(config.min_width);
576 max_width->update(config.max_width);
577 min_len->update(config.min_len);
578 max_len->update(config.max_len);
579 max_angle->update(config.max_angle);
580 blur_len->update(config.blur_len);
581 gap_len->update(config.gap_len);
582 y_mode->update(config.mode_y);
583 u_mode->update(config.mode_u);
584 v_mode->update(config.mode_v);
585 mark->update(config.mark);
586 ffade->update(config.ffade);
587 border->update(config.border);
588 edge_only->update(config.edge_only);
592 DeScratchModeItem::DeScratchModeItem(DeScratchMode *popup, int type, const char *text)
599 DeScratchModeItem::~DeScratchModeItem()
603 int DeScratchModeItem::handle_event()
606 return popup->handle_event();
609 DeScratchMode::DeScratchMode(DeScratchWindow *win, int x, int y, int *value)
610 : BC_PopupMenu(x, y, 64, "", 1)
616 DeScratchMode::~DeScratchMode()
620 void DeScratchMode::create_objects()
622 add_item(new DeScratchModeItem(this, MODE_NONE, _("None")));
623 add_item(new DeScratchModeItem(this, MODE_LOW, _("Low")));
624 add_item(new DeScratchModeItem(this, MODE_HIGH, _("High")));
625 add_item(new DeScratchModeItem(this, MODE_ALL, _("All")));
629 int DeScratchMode::handle_event()
631 win->plugin->send_configure_change();
635 void DeScratchMode::update(int v)
637 set_value(*value = v);
640 void DeScratchMode::set_value(int v)
642 int i = total_items();
643 while( --i >= 0 && ((DeScratchModeItem*)get_item(i))->type != v );
644 if( i >= 0 ) set_text(get_item(i)->get_text());
647 DeScratchISlider::DeScratchISlider(DeScratchWindow *win,
648 int x, int y, int w, int min, int max, int *output)
649 : BC_ISlider(x, y, 0, w, w, min, max, *output)
652 this->output = output;
655 DeScratchISlider::~DeScratchISlider()
659 int DeScratchISlider::handle_event()
661 *output = get_value();
662 win->plugin->send_configure_change();
666 DeScratchFSlider::DeScratchFSlider(DeScratchWindow *win,
667 int x, int y, int w, float min, float max, float *output)
668 : BC_FSlider(x, y, 0, w, w, min, max, *output)
671 this->output = output;
674 DeScratchFSlider::~DeScratchFSlider()
678 int DeScratchFSlider::handle_event()
680 *output = get_value();
681 win->plugin->send_configure_change();
685 DeScratchMark::DeScratchMark(DeScratchWindow *win, int x, int y)
686 : BC_CheckBox(x, y, &win->plugin->config.mark, _("Mark"))
691 DeScratchMark::~DeScratchMark()
695 int DeScratchMark::handle_event()
697 int ret = BC_CheckBox::handle_event();
698 win->plugin->send_configure_change();
702 DeScratchEdgeOnly::DeScratchEdgeOnly(DeScratchWindow *win, int x, int y)
703 : BC_CheckBox(x, y, &win->plugin->config.edge_only, _("Edge"))
708 DeScratchEdgeOnly::~DeScratchEdgeOnly()
712 int DeScratchEdgeOnly::handle_event()
714 int ret = BC_CheckBox::handle_event();
715 win->plugin->send_configure_change();
719 DeScratchReset::DeScratchReset(DeScratchWindow *win, int x, int y)
720 : BC_GenericButton(x, y, _("Reset"))
725 int DeScratchReset::handle_event()
727 win->plugin->config.reset();
729 win->plugin->send_configure_change();