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