add BC_SCALE env var for hi def monitors, cleanup theme data
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / reframert / reframert.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008-2016 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 "bcdisplayinfo.h"
23 #include "clip.h"
24 #include "bchash.h"
25 #include "filexml.h"
26 #include "reframert.h"
27 #include "guicast.h"
28 #include "language.h"
29 #include "pluginvclient.h"
30 #include "theme.h"
31 #include "transportque.h"
32
33 #include <string.h>
34
35
36
37
38 REGISTER_PLUGIN(ReframeRT);
39
40
41
42 ReframeRTConfig::ReframeRTConfig()
43 {
44         reset();
45 }
46
47 void ReframeRTConfig::reset()
48 {
49         num = 1.0;
50         denom = 1.0;
51         stretch = 0;
52         interp = 0;
53         optic_flow = 1;
54 }
55
56 int ReframeRTConfig::equivalent(ReframeRTConfig &src)
57 {
58         return fabs(num - src.num) < 0.0001 &&
59                 fabs(denom - src.denom) < 0.0001 &&
60                 stretch == src.stretch &&
61                 interp == src.interp;
62 }
63
64 void ReframeRTConfig::copy_from(ReframeRTConfig &src)
65 {
66         this->num = src.num;
67         this->denom = src.denom;
68         this->stretch = src.stretch;
69         this->interp = src.interp;
70 }
71
72 void ReframeRTConfig::interpolate(ReframeRTConfig &prev,
73         ReframeRTConfig &next,
74         int64_t prev_frame,
75         int64_t next_frame,
76         int64_t current_frame)
77 {
78         this->interp = prev.interp;
79         this->stretch = prev.stretch;
80         this->denom = prev.denom;
81
82         if (this->interp && prev_frame != next_frame)
83         {
84                 double next_weight = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
85                 double prev_weight = (double)(next_frame - current_frame) / (next_frame - prev_frame);
86                 double prev_slope = prev.num / prev.denom, next_slope = next.num / next.denom;
87                 // for interpolation, this is (for now) a simple linear slope to the next keyframe.
88                 double scale = prev_slope * prev_weight + next_slope * next_weight;
89                 this->num = this->denom * scale;
90         }
91         else
92         {
93                 this->num = prev.num;
94         }
95 }
96
97 void ReframeRTConfig::boundaries()
98 {
99         if(num < 0.0001) num = 0.0001;
100         if(denom < 0.0001) denom = 0.0001;
101 }
102
103
104
105
106
107
108
109
110 ReframeRTWindow::ReframeRTWindow(ReframeRT *plugin)
111  : PluginClientWindow(plugin, xS(230), yS(235), xS(230), yS(235), 0)
112 {
113         this->plugin = plugin;
114 }
115
116 ReframeRTWindow::~ReframeRTWindow()
117 {
118 }
119
120 void ReframeRTWindow::create_objects()
121 {
122         int x = plugin->get_theme()->window_border;
123         int y = plugin->get_theme()->window_border;
124         BC_Title *title;
125         add_subwindow(title = new BC_Title(x, y, _("Input frames:")));
126         y += title->get_h() + plugin->get_theme()->widget_border;
127         num = new ReframeRTNum(plugin,
128                 this,
129                 x,
130                 y);
131         num->create_objects();
132         num->set_increment(1.0);
133
134         y += num->get_h() + plugin->get_theme()->widget_border;
135         add_subwindow(title = new BC_Title(x, y, _("Output frames:")));
136         y += title->get_h() + plugin->get_theme()->widget_border;
137         denom = new ReframeRTDenom(plugin,
138                 this,
139                 x,
140                 y);
141         denom->create_objects();
142         denom->set_increment(1.0);
143
144
145         y += denom->get_h() + plugin->get_theme()->widget_border;
146         add_subwindow(stretch = new ReframeRTStretch(plugin, this, x, y));
147         y += yS(30);
148         add_subwindow(downsample = new ReframeRTDownsample(plugin, this, x, y));
149         y += yS(30);
150         add_subwindow(interpolate = new ReframeRTInterpolate(plugin, this, x, y));
151         y += yS(40);
152         add_subwindow(reset = new ReframeRTReset(plugin, this, x, y));
153         show_window();
154 }
155
156 void ReframeRTWindow::update()
157 {
158         num->update((float)plugin->config.num);
159         denom->update((float)plugin->config.denom);
160         stretch->update(plugin->config.stretch);
161         downsample->update(!plugin->config.stretch);
162         interpolate->update(plugin->config.interp);
163 }
164
165
166
167
168
169
170 ReframeRTNum::ReframeRTNum(ReframeRT *plugin,
171         ReframeRTWindow *gui,
172         int x,
173         int y)
174  : BC_TumbleTextBox(gui,
175         (float)plugin->config.num,
176         (float)0.0001,
177         (float)1000,
178         x,
179         y,
180         gui->get_w() - plugin->get_theme()->window_border * 3)
181 {
182         this->plugin = plugin;
183 }
184
185 int ReframeRTNum::handle_event()
186 {
187         plugin->config.num = atof(get_text());
188         plugin->config.boundaries();
189         plugin->send_configure_change();
190         return 1;
191 }
192
193
194 ReframeRTDenom::ReframeRTDenom(ReframeRT *plugin,
195         ReframeRTWindow *gui,
196         int x,
197         int y)
198  : BC_TumbleTextBox(gui,
199         (float)plugin->config.denom,
200         (float)-1000,
201         (float)1000,
202         x,
203         y,
204         gui->get_w() - plugin->get_theme()->window_border * 3)
205 {
206         this->plugin = plugin;
207 }
208
209 int ReframeRTDenom::handle_event()
210 {
211         plugin->config.denom = atof(get_text());
212         plugin->config.boundaries();
213         plugin->send_configure_change();
214         return 1;
215 }
216
217
218 ReframeRTStretch::ReframeRTStretch(ReframeRT *plugin,
219         ReframeRTWindow *gui,
220         int x,
221         int y)
222  : BC_Radial(x, y, plugin->config.stretch, _("Stretch"))
223 {
224         this->plugin = plugin;
225         this->gui = gui;
226 }
227
228 int ReframeRTStretch::handle_event()
229 {
230         plugin->config.stretch = get_value();
231         gui->downsample->update(!get_value());
232         plugin->send_configure_change();
233         return 1;
234 }
235
236
237 ReframeRTDownsample::ReframeRTDownsample(ReframeRT *plugin,
238         ReframeRTWindow *gui,
239         int x,
240         int y)
241  : BC_Radial(x, y, !plugin->config.stretch, _("Downsample"))
242 {
243         this->plugin = plugin;
244         this->gui = gui;
245 }
246
247 int ReframeRTDownsample::handle_event()
248 {
249         plugin->config.stretch = !get_value();
250         gui->stretch->update(!get_value());
251         plugin->send_configure_change();
252         return 1;
253 }
254
255 ReframeRTInterpolate::ReframeRTInterpolate(ReframeRT *plugin,
256         ReframeRTWindow *gui,
257         int x,
258         int y)
259  : BC_CheckBox(x, y, 0, _("Interpolate"))
260 {
261         this->plugin = plugin;
262         this->gui = gui;
263 }
264
265 int ReframeRTInterpolate::handle_event()
266 {
267         plugin->config.interp = get_value();
268         gui->interpolate->update(get_value());
269         plugin->send_configure_change();
270         return 1;
271 }
272
273
274 ReframeRTReset::ReframeRTReset(ReframeRT *plugin, ReframeRTWindow *gui, int x, int y)
275  : BC_GenericButton(x, y, _("Reset"))
276 {
277         this->plugin = plugin;
278         this->gui = gui;
279 }
280 ReframeRTReset::~ReframeRTReset()
281 {
282 }
283 int ReframeRTReset::handle_event()
284 {
285         plugin->config.reset();
286         gui->update();
287         plugin->send_configure_change();
288         return 1;
289 }
290
291
292
293 ReframeRT::ReframeRT(PluginServer *server)
294  : PluginVClient(server)
295 {
296 }
297
298 ReframeRT::~ReframeRT()
299 {
300
301 }
302
303 const char* ReframeRT::plugin_title() { return N_("ReframeRT"); }
304 int ReframeRT::is_realtime() { return 1; }
305 int ReframeRT::is_synthesis() { return 1; }
306
307
308 NEW_WINDOW_MACRO(ReframeRT, ReframeRTWindow)
309 LOAD_CONFIGURATION_MACRO(ReframeRT, ReframeRTConfig)
310
311 int ReframeRT::process_buffer(VFrame *frame,
312                 int64_t start_position,
313                 double frame_rate)
314 {
315         int64_t input_frame = get_source_start();
316         ReframeRTConfig prev_config, next_config;
317         KeyFrame *tmp_keyframe, *next_keyframe = get_prev_keyframe(get_source_start());
318         int64_t tmp_position, next_position;
319         int64_t segment_len;
320         double input_rate = frame_rate;
321         int is_current_keyframe;
322
323 // if there are no keyframes, the default keyframe is used, and its position is always 0;
324 // if there are keyframes, the first keyframe can be after the effect start (and it controls settings before it)
325 // so let's calculate using a fake keyframe with the same settings but position == effect start
326         KeyFrame *fake_keyframe = new KeyFrame();
327         fake_keyframe->copy_from(next_keyframe);
328         fake_keyframe->position = local_to_edl(get_source_start());
329         next_keyframe = fake_keyframe;
330
331         // calculate input_frame accounting for all previous keyframes
332         do
333         {
334                 tmp_keyframe = next_keyframe;
335                 next_keyframe = get_next_keyframe(tmp_keyframe->position+1, 0);
336
337                 tmp_position = edl_to_local(tmp_keyframe->position);
338                 next_position = edl_to_local(next_keyframe->position);
339
340                 is_current_keyframe =
341                         next_position > start_position // the next keyframe is after the current position
342                         || next_keyframe->position == tmp_keyframe->position // there are no more keyframes
343                         || !next_keyframe->position; // there are no keyframes at all
344
345                 if (is_current_keyframe)
346                         segment_len = start_position - tmp_position;
347                 else
348                         segment_len = next_position - tmp_position;
349
350                 read_data(next_keyframe);
351                 next_config.copy_from(config);
352                 read_data(tmp_keyframe);
353                 prev_config.copy_from(config);
354                 config.interpolate(prev_config, next_config, tmp_position, next_position, tmp_position + segment_len);
355
356                 // the area under the curve is the number of frames to advance
357                 // as long as interpolate() uses a linear slope we can use geometry to determine this
358                 // if interpolate() changes to use a curve then this needs use (possibly) the definite integral
359                 double prev_scale = prev_config.num / prev_config.denom;
360                 double config_scale = config.num / config.denom;
361                 input_frame += (int64_t)(segment_len * ((prev_scale + config_scale) / 2));
362         } while (!is_current_keyframe);
363
364 // Change rate
365         if (!config.stretch)
366         {
367                 input_rate *= config.num / config.denom;
368
369         }
370
371 // printf("ReframeRT::process_buffer %d %lld %f %lld %f\n",
372 // __LINE__,
373 // start_position,
374 // frame_rate,
375 // input_frame,
376 // input_rate);
377
378         read_frame(frame,
379                 0,
380                 input_frame,
381                 input_rate,
382                 0);
383
384         delete fake_keyframe;
385
386         return 0;
387 }
388
389
390
391 void ReframeRT::save_data(KeyFrame *keyframe)
392 {
393         FileXML output;
394
395 // cause data to be stored directly in text
396         output.set_shared_output(keyframe->xbuf);
397         output.tag.set_title("REFRAMERT");
398 // for backwards compatability, we call num scale
399         output.tag.set_property("SCALE", config.num);
400         output.tag.set_property("DENOM", config.denom);
401         output.tag.set_property("STRETCH", config.stretch);
402         output.tag.set_property("INTERPOLATE", config.interp);
403         output.append_tag();
404         output.tag.set_title("/REFRAMERT");
405         output.append_tag();
406         output.append_newline();
407         output.terminate_string();
408 }
409
410 void ReframeRT::read_data(KeyFrame *keyframe)
411 {
412         FileXML input;
413
414         input.set_shared_input(keyframe->xbuf);
415
416         while(!input.read_tag())
417         {
418                 if(input.tag.title_is("REFRAMERT"))
419                 {
420 // for backwards compatability, we call num scale
421                         config.num = input.tag.get_property("SCALE", config.num);
422                         config.denom = input.tag.get_property("DENOM", config.denom);
423                         config.stretch = input.tag.get_property("STRETCH", config.stretch);
424                         config.interp = input.tag.get_property("INTERPOLATE", config.interp);
425                 }
426         }
427 }
428
429 void ReframeRT::update_gui()
430 {
431         if(thread)
432         {
433                 int changed = load_configuration();
434
435                 if(changed)
436                 {
437                         ReframeRTWindow* window = (ReframeRTWindow*)thread->window;
438                         window->lock_window("ReframeRT::update_gui");
439                         window->num->update((float)config.num);
440                         window->denom->update((float)config.denom);
441                         window->stretch->update(config.stretch);
442                         window->downsample->update(!config.stretch);
443                         window->interpolate->update(config.interp);
444                         window->unlock_window();
445                 }
446         }
447 }
448
449
450
451
452