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