50da059921f1038cf1ea35aafba5a11c0e18fcae
[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         if( plugin->config.drag && drag->drag_activate() )
283                 eprintf("drag enabled, but compositor already grabbed\n");
284         int x1 = get_w() - BoxBlurReset::calculate_w(this) - 2*margin;
285         add_subwindow(reset = new BoxBlurReset(this, x1, y));
286         y += bmax(title->get_h(), drag->get_h()) + 2*margin;
287
288         add_subwindow(title = new BC_Title(t1, y, _("X:")));
289         box_x = new BoxBlurX(this, t2, y);
290         box_x->create_objects();
291         add_subwindow(title = new BC_Title(t3, y, _("W:")));
292         box_w = new BoxBlurW(this, t4, y);
293         box_w->create_objects();
294         y += bmax(title->get_h(), box_w->get_h()) + margin;
295         add_subwindow(title = new BC_Title(t1, y, _("Y:")));
296         box_y = new BoxBlurY(this, t2, y);
297         box_y->create_objects();
298         add_subwindow(title = new BC_Title(t3, y, _("H:")));
299         box_h = new BoxBlurH(this, t4, y);
300         box_h->create_objects();
301         y += bmax(title->get_h(), box_h->get_h()) + margin;
302
303         y += 2*margin;
304         x1 = xS(70);
305         add_subwindow(title = new BC_Title(x, y, _("Horz:")));
306         add_subwindow(blur_horz = new BoxBlurRadius(this, x1, y, xS(160),
307                 &plugin->config.horz_radius));
308         y += blur_horz->get_h() + margin;
309         add_subwindow(title = new BC_Title(x, y, _("Vert:")));
310         add_subwindow(blur_vert = new BoxBlurRadius(this, x1, y, xS(160),
311                 &plugin->config.vert_radius));
312         y += blur_vert->get_h() + margin;
313         add_subwindow(title = new BC_Title(x, y, _("Power:")));
314         add_subwindow(blur_power = new BoxBlurPower(this, x1, y, xS(160),
315                 &plugin->config.power));
316         y += blur_power->get_h() + margin;
317
318         show_window(1);
319 }
320
321 void BoxBlurWindow::update_gui()
322 {
323         BoxBlurConfig &config = plugin->config;
324         blur_horz->update(config.horz_radius);
325         blur_vert->update(config.vert_radius);
326         blur_power->update(config.power);
327         box_x->update(config.box_x);
328         box_y->update(config.box_y);
329         box_w->update((int64_t)config.box_w);
330         box_h->update((int64_t)config.box_h);
331         drag->drag_x = config.box_x;
332         drag->drag_y = config.box_y;
333         drag->drag_w = config.box_w;
334         drag->drag_h = config.box_h;
335 }
336
337
338 REGISTER_PLUGIN(BoxBlurEffect)
339 NEW_WINDOW_MACRO(BoxBlurEffect, BoxBlurWindow)
340 LOAD_CONFIGURATION_MACRO(BoxBlurEffect, BoxBlurConfig)
341
342
343 BoxBlurEffect::BoxBlurEffect(PluginServer *server)
344  : PluginVClient(server)
345 {
346         box_blur = 0;
347 }
348
349 BoxBlurEffect::~BoxBlurEffect()
350 {
351         delete box_blur;
352 }
353
354 const char* BoxBlurEffect::plugin_title() { return N_("BoxBlur"); }
355 int BoxBlurEffect::is_realtime() { return 1; }
356
357
358 void BoxBlurEffect::save_data(KeyFrame *keyframe)
359 {
360         FileXML output;
361         output.set_shared_output(keyframe->xbuf);
362         output.tag.set_title("BOXBLUR");
363         output.tag.set_property("HORZ_RADIUS", config.horz_radius);
364         output.tag.set_property("VERT_RADIUS", config.vert_radius);
365         output.tag.set_property("POWER", config.power);
366         output.tag.set_property("DRAG", config.drag);
367         output.tag.set_property("BOX_X", config.box_x);
368         output.tag.set_property("BOX_Y", config.box_y);
369         output.tag.set_property("BOX_W", config.box_w);
370         output.tag.set_property("BOX_H", config.box_h);
371         output.append_tag();
372         output.tag.set_title("/BOXBLUR");
373         output.append_tag();
374         output.append_newline();
375         output.terminate_string();
376 }
377
378 void BoxBlurEffect::read_data(KeyFrame *keyframe)
379 {
380         FileXML input;
381         input.set_shared_input(keyframe->xbuf);
382         int result = 0;
383
384         while( !(result = input.read_tag()) ) {
385                 if( input.tag.title_is("BOXBLUR") ) {
386                         config.horz_radius = input.tag.get_property("HORZ_RADIUS", config.horz_radius);
387                         config.vert_radius = input.tag.get_property("VERT_RADIUS", config.vert_radius);
388                         config.power = input.tag.get_property("POWER", config.power);
389                         config.drag = input.tag.get_property("DRAG", config.drag);
390                         config.box_x = input.tag.get_property("BOX_X", config.box_x);
391                         config.box_y = input.tag.get_property("BOX_Y", config.box_y);
392                         config.box_w = input.tag.get_property("BOX_W", config.box_w);
393                         config.box_h = input.tag.get_property("BOX_H", config.box_h);
394                 }
395         }
396 }
397
398 void BoxBlurEffect::draw_boundry()
399 {
400         if( !gui_open() ) return;
401         int box_x = config.box_x, box_y = config.box_y;
402         int box_w = config.box_w ? config.box_w : input->get_w();
403         int box_h = config.box_h ? config.box_h : input->get_h();
404         DragCheckBox::draw_boundary(output, box_x, box_y, box_w, box_h);
405 }
406
407 int BoxBlurEffect::process_realtime(VFrame *input, VFrame *output)
408 {
409         this->input = input;
410         this->output = output;
411         load_configuration();
412         int out_w = output->get_w(), out_h = output->get_h();
413
414         if( !box_blur ) {
415                 int cpus = (out_w * out_h)/0x80000 + 1;
416                 box_blur = new BoxBlur(cpus);
417         }
418         int x = config.box_x, y = config.box_y;
419         int ow = config.box_w ? config.box_w : out_w;
420         int oh = config.box_h ? config.box_h : out_h;
421         if( config.horz_radius ) {
422                 box_blur->hblur(output, input, config.horz_radius, config.power,
423                         -1, x,y, ow, oh);
424                 input = output;
425         }
426         if( config.vert_radius ) {
427                 box_blur->vblur(output, input, config.vert_radius, config.power,
428                         -1, x,y, ow, oh);
429         }
430
431         if( config.drag )
432                 draw_boundry();
433
434         return 1;
435 }
436
437 void BoxBlurEffect::update_gui()
438 {
439         if( !thread ) return;
440         load_configuration();
441         thread->window->lock_window("BoxBlurEffect::update_gui");
442         BoxBlurWindow *gui = (BoxBlurWindow *)thread->window;
443         gui->update_gui();
444         thread->window->unlock_window();
445 }
446
447
448 BoxBlurX::BoxBlurX(BoxBlurWindow *gui, int x, int y)
449  : BC_TumbleTextBox(gui, gui->plugin->config.box_x,
450                 -32767.f, 32767.f, x, y, xS(64))
451 {
452         this->gui = gui;
453         set_precision(1);
454 }
455 int BoxBlurX::handle_event()
456 {
457         gui->plugin->config.box_x = atof(get_text());
458         gui->update_drag();
459         return 1;
460 }
461
462 BoxBlurY::BoxBlurY(BoxBlurWindow *gui, int x, int y)
463  : BC_TumbleTextBox(gui, gui->plugin->config.box_y,
464                 -32767.f, 32767.f, x, y, xS(64))
465 {
466         this->gui = gui;
467         set_precision(1);
468 }
469 int BoxBlurY::handle_event()
470 {
471         gui->plugin->config.box_y = atof(get_text());
472         gui->update_drag();
473         return 1;
474 }
475
476 BoxBlurW::BoxBlurW(BoxBlurWindow *gui, int x, int y)
477  : BC_TumbleTextBox(gui, gui->plugin->config.box_w,
478                 0, 32767, x, y, xS(64))
479 {
480         this->gui = gui;
481 }
482 int BoxBlurW::handle_event()
483 {
484         gui->plugin->config.box_w = atol(get_text());
485         gui->update_drag();
486         return 1;
487 }
488
489 BoxBlurH::BoxBlurH(BoxBlurWindow *gui, int x, int y)
490  : BC_TumbleTextBox(gui, gui->plugin->config.box_h,
491                 0, 32767, x, y, xS(64))
492 {
493         this->gui = gui;
494 }
495 int BoxBlurH::handle_event()
496 {
497         gui->plugin->config.box_h = atol(get_text());
498         gui->update_drag();
499         return 1;
500 }
501
502 BoxBlurDrag::BoxBlurDrag(BoxBlurWindow *gui, BoxBlurEffect *plugin, int x, int y)
503  : DragCheckBox(plugin->server->mwindow, x, y, _("Drag"), &plugin->config.drag,
504                 plugin->config.box_x, plugin->config.box_y,
505                 plugin->config.box_w, plugin->config.box_h)
506 {
507         this->plugin = plugin;
508         this->gui = gui;
509 }
510
511 Track *BoxBlurDrag::get_drag_track()
512 {
513         PluginServer *server = plugin->server;
514         int plugin_id = server->plugin_id;
515         Plugin *plugin = server->edl->tracks->plugin_exists(plugin_id);
516         return !plugin ? 0 : plugin->track;
517 }
518 int64_t BoxBlurDrag::get_drag_position()
519 {
520         return plugin->get_source_position();
521 }
522
523 void BoxBlurDrag::update_gui()
524 {
525         plugin->config.drag = get_value();
526         plugin->config.box_x = drag_x;
527         plugin->config.box_y = drag_y;
528         plugin->config.box_w = drag_w+0.5;
529         plugin->config.box_h = drag_h+0.5;
530         gui->box_x->update((float)plugin->config.box_x);
531         gui->box_y->update((float)plugin->config.box_y);
532         gui->box_w->update((int64_t)plugin->config.box_w);
533         gui->box_h->update((int64_t)plugin->config.box_h);
534         plugin->send_configure_change();
535 }
536
537 int BoxBlurDrag::handle_event()
538 {
539         int ret = DragCheckBox::handle_event();
540         plugin->send_configure_change();
541         return ret;
542 }
543
544 void BoxBlurWindow::update_drag()
545 {
546         drag->drag_x = plugin->config.box_x;
547         drag->drag_y = plugin->config.box_y;
548         drag->drag_w = plugin->config.box_w;
549         drag->drag_h = plugin->config.box_h;
550         plugin->send_configure_change();
551 }
552
553 BoxBlurReset::BoxBlurReset(BoxBlurWindow *gui, int x, int y)
554  : BC_Button(x, y, gui->plugin->get_theme()->get_image_set("reset_button"))
555 {
556         this->gui = gui;
557 }
558
559 int BoxBlurReset::calculate_w(BoxBlurWindow *gui)
560 {
561         VFrame **imgs = gui->plugin->get_theme()->get_image_set("reset_button");
562         return imgs[0]->get_w();
563 }
564
565 int BoxBlurReset::handle_event()
566 {
567         BoxBlurEffect *plugin = gui->plugin;
568         plugin->config.reset();
569         gui->update_gui();
570         gui->update_drag();
571         return 1;
572 }
573