ec8bf7673f0f5d3920ceb55cbe6162978cfb81be
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / boxblur / boxblur.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 1997-2020 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "guicast.h"
23 #include "boxblur.h"
24 #include "dragcheckbox.h"
25 #include "edl.h"
26 #include "filexml.h"
27 #include "language.h"
28 #include "mainerror.h"
29 #include "plugin.h"
30 #include "pluginserver.h"
31 #include "pluginvclient.h"
32 #include "track.h"
33 #include "tracks.h"
34 #include "theme.h"
35
36 #include <stdint.h>
37 #include <string.h>
38
39 class BoxBlurConfig;
40 class BoxBlurRadius;
41 class BoxBlurPower;
42 class BoxBlurDrag;
43 class BoxBlurReset;
44 class BoxBlurX;
45 class BoxBlurY;
46 class BoxBlurW;
47 class BoxBlurH;
48 class BoxBlurWindow;
49 class BoxBlurEffect;
50
51
52 class BoxBlurConfig
53 {
54 public:
55         BoxBlurConfig();
56         void copy_from(BoxBlurConfig &that);
57         int equivalent(BoxBlurConfig &that);
58         void interpolate(BoxBlurConfig &prev, BoxBlurConfig &next,
59                 int64_t prev_frame, int64_t next_frame, int64_t current_frame);
60         void reset();
61
62         int horz_radius, vert_radius, power;
63         float box_x, box_y;
64         int box_w, box_h;
65         int drag;
66 };
67
68
69 class BoxBlurRadius : public BC_ISlider
70 {
71 public:
72         BoxBlurRadius(BoxBlurWindow *gui, int x, int y, int w, int *radius);
73         int handle_event();
74
75         BoxBlurWindow *gui;
76         int *radius;
77 };
78
79 class BoxBlurPower : public BC_ISlider
80 {
81 public:
82         BoxBlurPower(BoxBlurWindow *gui, int x, int y, int w, int *power);
83         int handle_event();
84
85         BoxBlurWindow *gui;
86         int *power;
87 };
88
89 class BoxBlurX : public BC_TumbleTextBox
90 {
91 public:
92         BoxBlurX(BoxBlurWindow *gui, int x, int y);
93         int handle_event();
94         BoxBlurWindow *gui;
95 };
96 class BoxBlurY : public BC_TumbleTextBox
97 {
98 public:
99         BoxBlurY(BoxBlurWindow *gui, int x, int y);
100         int handle_event();
101         BoxBlurWindow *gui;
102 };
103 class BoxBlurW : public BC_TumbleTextBox
104 {
105 public:
106         BoxBlurW(BoxBlurWindow *gui, int x, int y);
107         int handle_event();
108         BoxBlurWindow *gui;
109 };
110 class BoxBlurH : public BC_TumbleTextBox
111 {
112 public:
113         BoxBlurH(BoxBlurWindow *gui, int x, int y);
114         int handle_event();
115         BoxBlurWindow *gui;
116 };
117
118 class BoxBlurDrag : public DragCheckBox
119 {
120 public:
121         BoxBlurDrag(BoxBlurWindow *gui, BoxBlurEffect *plugin, int x, int y);
122         int handle_event();
123         void update_gui();
124         Track *get_drag_track();
125         int64_t get_drag_position();
126
127         BoxBlurWindow *gui;
128         BoxBlurEffect *plugin;
129 };
130
131 class BoxBlurReset : public BC_Button
132 {
133 public:
134         BoxBlurReset(BoxBlurWindow *gui, int x, int y);
135         int handle_event();
136         static int calculate_w(BoxBlurWindow *gui);
137
138         BoxBlurWindow *gui;
139 };
140
141 class BoxBlurWindow : public PluginClientWindow
142 {
143 public:
144         BoxBlurWindow(BoxBlurEffect *plugin);
145         ~BoxBlurWindow();
146         void create_objects();
147         void update_gui();
148         void update_drag();
149
150         BoxBlurEffect *plugin;
151         BoxBlurReset *reset;
152         BoxBlurRadius *blur_horz;
153         BoxBlurRadius *blur_vert;
154         BoxBlurPower *blur_power;
155         BoxBlurDrag *drag;
156         BoxBlurX *box_x;
157         BoxBlurY *box_y;
158         BoxBlurW *box_w;
159         BoxBlurH *box_h;
160 };
161
162
163 class BoxBlurEffect : public PluginVClient
164 {
165 public:
166         BoxBlurEffect(PluginServer *server);
167         ~BoxBlurEffect();
168
169         PLUGIN_CLASS_MEMBERS(BoxBlurConfig)
170         int process_realtime(VFrame *input, VFrame *output);
171         void update_gui();
172         int is_realtime();
173         void save_data(KeyFrame *keyframe);
174         void read_data(KeyFrame *keyframe);
175         void draw_boundry();
176
177         VFrame *input, *output;
178         BoxBlur *box_blur;
179 };
180
181
182 void BoxBlurConfig::reset()
183 {
184         horz_radius = 2;
185         vert_radius = 2;
186         power = 2;
187         drag = 0;
188         box_x = box_y = 0.0;
189         box_w = box_h = 0;
190 }
191
192 BoxBlurConfig::BoxBlurConfig()
193 {
194         reset();
195 }
196
197 void BoxBlurConfig::copy_from(BoxBlurConfig &that)
198 {
199         horz_radius = that.horz_radius;
200         vert_radius = that.vert_radius;
201         power = that.power;
202         drag = that.drag;
203         box_x = that.box_x;  box_y = that.box_y;
204         box_w = that.box_w;  box_h = that.box_h;
205 }
206
207 int BoxBlurConfig::equivalent(BoxBlurConfig &that)
208 {
209         return horz_radius == that.horz_radius &&
210                 vert_radius == that.vert_radius &&
211                 power == that.power && // drag == that.drag &&
212                 EQUIV(box_x, that.box_x) && EQUIV(box_y, that.box_y) &&
213                 box_w == that.box_w && box_h == that.box_h;
214 }
215
216 void BoxBlurConfig::interpolate(BoxBlurConfig &prev, BoxBlurConfig &next,
217         int64_t prev_frame, int64_t next_frame, int64_t current_frame)
218 {
219         double u = (double)(next_frame - current_frame) / (next_frame - prev_frame);
220         double v = 1. - u;
221         this->horz_radius = u*prev.horz_radius + v*next.horz_radius;
222         this->vert_radius = u*prev.vert_radius + v*next.vert_radius;
223         this->power = u*prev.power + v*next.power;
224         this->drag = prev.drag;
225         this->box_x = u*prev.box_x + v*next.box_x;
226         this->box_y = u*prev.box_y + v*next.box_y;
227         this->box_w = u*prev.box_w + v*next.box_w;
228         this->box_h = u*prev.box_h + v*next.box_h;
229 }
230
231
232 BoxBlurRadius::BoxBlurRadius(BoxBlurWindow *gui, int x, int y, int w, int *radius)
233  : BC_ISlider(x, y, 0, w, w, 0, 100, *radius)
234 {
235         this->gui = gui;
236         this->radius = radius;
237 }
238 int BoxBlurRadius::handle_event()
239 {
240         *radius = get_value();
241         gui->plugin->send_configure_change();
242         return 1;
243 }
244
245 BoxBlurPower::BoxBlurPower(BoxBlurWindow *gui, int x, int y, int w, int *power)
246  : BC_ISlider(x, y, 0, w, w, 1, 10, *power)
247 {
248         this->gui = gui;
249         this->power = power;
250 }
251 int BoxBlurPower::handle_event()
252 {
253         *power = get_value();
254         gui->plugin->send_configure_change();
255         return 1;
256 }
257
258 BoxBlurWindow::BoxBlurWindow(BoxBlurEffect *plugin)
259  : PluginClientWindow(plugin, xS(250), yS(185), xS(250), yS(185), 0)
260 {
261         this->plugin = plugin;
262         box_x = 0;  box_y = 0;
263         box_w = 0;  box_h = 0;
264 }
265
266 BoxBlurWindow::~BoxBlurWindow()
267 {
268         delete box_x;
269         delete box_y;
270         delete box_w;
271         delete box_h;
272 }
273
274 void BoxBlurWindow::create_objects()
275 {
276         int x = xS(10), y = yS(10);
277         int t1 = x, t2 = t1+xS(24), t3 = get_w()/2, t4 = t3 + xS(24);
278         int margin = plugin->get_theme()->widget_border;
279         BC_Title *title;
280         add_subwindow(title = new BC_Title(x, y, _("Box Blur"), MEDIUMFONT_3D));
281         add_subwindow(drag = new BoxBlurDrag(this, plugin, t3, y));
282         drag->create_objects();
283         if( plugin->config.drag && drag->drag_activate() )
284                 eprintf("drag enabled, but compositor already grabbed\n");
285         int x1 = get_w() - BoxBlurReset::calculate_w(this) - 2*margin;
286         add_subwindow(reset = new BoxBlurReset(this, x1, y));
287         y += bmax(title->get_h(), drag->get_h()) + 2*margin;
288
289         add_subwindow(title = new BC_Title(t1, y, _("X:")));
290         box_x = new BoxBlurX(this, t2, y);
291         box_x->create_objects();
292         add_subwindow(title = new BC_Title(t3, y, _("W:")));
293         box_w = new BoxBlurW(this, t4, y);
294         box_w->create_objects();
295         y += bmax(title->get_h(), box_w->get_h()) + margin;
296         add_subwindow(title = new BC_Title(t1, y, _("Y:")));
297         box_y = new BoxBlurY(this, t2, y);
298         box_y->create_objects();
299         add_subwindow(title = new BC_Title(t3, y, _("H:")));
300         box_h = new BoxBlurH(this, t4, y);
301         box_h->create_objects();
302         y += bmax(title->get_h(), box_h->get_h()) + margin;
303
304         y += 2*margin;
305         x1 = xS(70);
306         add_subwindow(title = new BC_Title(x, y, _("Horz:")));
307         add_subwindow(blur_horz = new BoxBlurRadius(this, x1, y, xS(160),
308                 &plugin->config.horz_radius));
309         y += blur_horz->get_h() + margin;
310         add_subwindow(title = new BC_Title(x, y, _("Vert:")));
311         add_subwindow(blur_vert = new BoxBlurRadius(this, x1, y, xS(160),
312                 &plugin->config.vert_radius));
313         y += blur_vert->get_h() + margin;
314         add_subwindow(title = new BC_Title(x, y, _("Power:")));
315         add_subwindow(blur_power = new BoxBlurPower(this, x1, y, xS(160),
316                 &plugin->config.power));
317         y += blur_power->get_h() + margin;
318
319         show_window(1);
320 }
321
322 void BoxBlurWindow::update_gui()
323 {
324         BoxBlurConfig &config = plugin->config;
325         blur_horz->update(config.horz_radius);
326         blur_vert->update(config.vert_radius);
327         blur_power->update(config.power);
328         box_x->update(config.box_x);
329         box_y->update(config.box_y);
330         box_w->update((int64_t)config.box_w);
331         box_h->update((int64_t)config.box_h);
332         drag->drag_x = config.box_x;
333         drag->drag_y = config.box_y;
334         drag->drag_w = config.box_w;
335         drag->drag_h = config.box_h;
336 }
337
338
339 REGISTER_PLUGIN(BoxBlurEffect)
340 NEW_WINDOW_MACRO(BoxBlurEffect, BoxBlurWindow)
341 LOAD_CONFIGURATION_MACRO(BoxBlurEffect, BoxBlurConfig)
342
343
344 BoxBlurEffect::BoxBlurEffect(PluginServer *server)
345  : PluginVClient(server)
346 {
347         box_blur = 0;
348 }
349
350 BoxBlurEffect::~BoxBlurEffect()
351 {
352         delete box_blur;
353 }
354
355 const char* BoxBlurEffect::plugin_title() { return N_("BoxBlur"); }
356 int BoxBlurEffect::is_realtime() { return 1; }
357
358
359 void BoxBlurEffect::save_data(KeyFrame *keyframe)
360 {
361         FileXML output;
362         output.set_shared_output(keyframe->xbuf);
363         output.tag.set_title("BOXBLUR");
364         output.tag.set_property("HORZ_RADIUS", config.horz_radius);
365         output.tag.set_property("VERT_RADIUS", config.vert_radius);
366         output.tag.set_property("POWER", config.power);
367         output.tag.set_property("DRAG", config.drag);
368         output.tag.set_property("BOX_X", config.box_x);
369         output.tag.set_property("BOX_Y", config.box_y);
370         output.tag.set_property("BOX_W", config.box_w);
371         output.tag.set_property("BOX_H", config.box_h);
372         output.append_tag();
373         output.tag.set_title("/BOXBLUR");
374         output.append_tag();
375         output.append_newline();
376         output.terminate_string();
377 }
378
379 void BoxBlurEffect::read_data(KeyFrame *keyframe)
380 {
381         FileXML input;
382         input.set_shared_input(keyframe->xbuf);
383         int result = 0;
384
385         while( !(result = input.read_tag()) ) {
386                 if( input.tag.title_is("BOXBLUR") ) {
387                         config.horz_radius = input.tag.get_property("HORZ_RADIUS", config.horz_radius);
388                         config.vert_radius = input.tag.get_property("VERT_RADIUS", config.vert_radius);
389                         config.power = input.tag.get_property("POWER", config.power);
390                         config.drag = input.tag.get_property("DRAG", config.drag);
391                         config.box_x = input.tag.get_property("BOX_X", config.box_x);
392                         config.box_y = input.tag.get_property("BOX_Y", config.box_y);
393                         config.box_w = input.tag.get_property("BOX_W", config.box_w);
394                         config.box_h = input.tag.get_property("BOX_H", config.box_h);
395                 }
396         }
397 }
398
399 void BoxBlurEffect::draw_boundry()
400 {
401         if( !gui_open() ) return;
402         int box_x = config.box_x, box_y = config.box_y;
403         int box_w = config.box_w ? config.box_w : input->get_w();
404         int box_h = config.box_h ? config.box_h : input->get_h();
405         DragCheckBox::draw_boundary(output, box_x, box_y, box_w, box_h);
406 }
407
408 int BoxBlurEffect::process_realtime(VFrame *input, VFrame *output)
409 {
410         this->input = input;
411         this->output = output;
412         load_configuration();
413         int out_w = output->get_w(), out_h = output->get_h();
414
415         if( !box_blur ) {
416                 int cpus = (out_w * out_h)/0x80000 + 1;
417                 box_blur = new BoxBlur(cpus);
418         }
419         int x = config.box_x, y = config.box_y;
420         int ow = config.box_w ? config.box_w : out_w;
421         int oh = config.box_h ? config.box_h : out_h;
422         if( config.horz_radius ) {
423                 box_blur->hblur(output, input, config.horz_radius, config.power,
424                         -1, x,y, ow, oh);
425                 input = output;
426         }
427         if( config.vert_radius ) {
428                 box_blur->vblur(output, input, config.vert_radius, config.power,
429                         -1, x,y, ow, oh);
430         }
431
432         if( config.drag )
433                 draw_boundry();
434
435         return 1;
436 }
437
438 void BoxBlurEffect::update_gui()
439 {
440         if( !thread ) return;
441         load_configuration();
442         thread->window->lock_window("BoxBlurEffect::update_gui");
443         BoxBlurWindow *gui = (BoxBlurWindow *)thread->window;
444         gui->update_gui();
445         thread->window->unlock_window();
446 }
447
448
449 BoxBlurX::BoxBlurX(BoxBlurWindow *gui, int x, int y)
450  : BC_TumbleTextBox(gui, gui->plugin->config.box_x,
451                 -32767.f, 32767.f, x, y, xS(64))
452 {
453         this->gui = gui;
454         set_precision(1);
455 }
456 int BoxBlurX::handle_event()
457 {
458         gui->plugin->config.box_x = atof(get_text());
459         gui->update_drag();
460         return 1;
461 }
462
463 BoxBlurY::BoxBlurY(BoxBlurWindow *gui, int x, int y)
464  : BC_TumbleTextBox(gui, gui->plugin->config.box_y,
465                 -32767.f, 32767.f, x, y, xS(64))
466 {
467         this->gui = gui;
468         set_precision(1);
469 }
470 int BoxBlurY::handle_event()
471 {
472         gui->plugin->config.box_y = atof(get_text());
473         gui->update_drag();
474         return 1;
475 }
476
477 BoxBlurW::BoxBlurW(BoxBlurWindow *gui, int x, int y)
478  : BC_TumbleTextBox(gui, gui->plugin->config.box_w,
479                 0, 32767, x, y, xS(64))
480 {
481         this->gui = gui;
482 }
483 int BoxBlurW::handle_event()
484 {
485         gui->plugin->config.box_w = atol(get_text());
486         gui->update_drag();
487         return 1;
488 }
489
490 BoxBlurH::BoxBlurH(BoxBlurWindow *gui, int x, int y)
491  : BC_TumbleTextBox(gui, gui->plugin->config.box_h,
492                 0, 32767, x, y, xS(64))
493 {
494         this->gui = gui;
495 }
496 int BoxBlurH::handle_event()
497 {
498         gui->plugin->config.box_h = atol(get_text());
499         gui->update_drag();
500         return 1;
501 }
502
503 BoxBlurDrag::BoxBlurDrag(BoxBlurWindow *gui, BoxBlurEffect *plugin, int x, int y)
504  : DragCheckBox(plugin->server->mwindow, x, y, _("Drag"), &plugin->config.drag,
505                 plugin->config.box_x, plugin->config.box_y,
506                 plugin->config.box_w, plugin->config.box_h)
507 {
508         this->plugin = plugin;
509         this->gui = gui;
510 }
511
512 Track *BoxBlurDrag::get_drag_track()
513 {
514         PluginServer *server = plugin->server;
515         int plugin_id = server->plugin_id;
516         Plugin *plugin = server->edl->tracks->plugin_exists(plugin_id);
517         return !plugin ? 0 : plugin->track;
518 }
519 int64_t BoxBlurDrag::get_drag_position()
520 {
521         return plugin->get_source_position();
522 }
523
524 void BoxBlurDrag::update_gui()
525 {
526         plugin->config.drag = get_value();
527         plugin->config.box_x = drag_x;
528         plugin->config.box_y = drag_y;
529         plugin->config.box_w = drag_w+0.5;
530         plugin->config.box_h = drag_h+0.5;
531         gui->box_x->update((float)plugin->config.box_x);
532         gui->box_y->update((float)plugin->config.box_y);
533         gui->box_w->update((int64_t)plugin->config.box_w);
534         gui->box_h->update((int64_t)plugin->config.box_h);
535         plugin->send_configure_change();
536 }
537
538 int BoxBlurDrag::handle_event()
539 {
540         int ret = DragCheckBox::handle_event();
541         plugin->send_configure_change();
542         return ret;
543 }
544
545 void BoxBlurWindow::update_drag()
546 {
547         drag->drag_x = plugin->config.box_x;
548         drag->drag_y = plugin->config.box_y;
549         drag->drag_w = plugin->config.box_w;
550         drag->drag_h = plugin->config.box_h;
551         plugin->send_configure_change();
552 }
553
554 BoxBlurReset::BoxBlurReset(BoxBlurWindow *gui, int x, int y)
555  : BC_Button(x, y, gui->plugin->get_theme()->get_image_set("reset_button"))
556 {
557         this->gui = gui;
558 }
559
560 int BoxBlurReset::calculate_w(BoxBlurWindow *gui)
561 {
562         VFrame **imgs = gui->plugin->get_theme()->get_image_set("reset_button");
563         return imgs[0]->get_w();
564 }
565
566 int BoxBlurReset::handle_event()
567 {
568         BoxBlurEffect *plugin = gui->plugin;
569         plugin->config.reset();
570         gui->update_gui();
571         gui->update_drag();
572         return 1;
573 }
574