add BC_SCALE env var for hi def monitors, cleanup theme data
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / motionblur / motionblur.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 <math.h>
23 #include <stdint.h>
24 #include <string.h>
25
26 #include "bcdisplayinfo.h"
27 #include "clip.h"
28 #include "bchash.h"
29 #include "filexml.h"
30 #include "keyframe.h"
31 #include "language.h"
32 #include "loadbalance.h"
33 #include "pluginvclient.h"
34 #include "vframe.h"
35
36
37 class MotionBlurMain;
38 class MotionBlurEngine;
39
40
41
42
43
44 class MotionBlurConfig
45 {
46 public:
47         MotionBlurConfig();
48
49         int equivalent(MotionBlurConfig &that);
50         void copy_from(MotionBlurConfig &that);
51         void interpolate(MotionBlurConfig &prev,
52                 MotionBlurConfig &next,
53                 long prev_frame,
54                 long next_frame,
55                 long current_frame);
56
57         int radius;
58         int steps;
59         int r;
60         int g;
61         int b;
62         int a;
63 };
64
65
66
67 class MotionBlurSize : public BC_ISlider
68 {
69 public:
70         MotionBlurSize(MotionBlurMain *plugin,
71                 int x,
72                 int y,
73                 int *output,
74                 int min,
75                 int max);
76         int handle_event();
77         MotionBlurMain *plugin;
78         int *output;
79 };
80
81
82 class MotionBlurWindow : public PluginClientWindow
83 {
84 public:
85         MotionBlurWindow(MotionBlurMain *plugin);
86         ~MotionBlurWindow();
87
88         void create_objects();
89
90
91         MotionBlurSize *steps, *radius;
92         MotionBlurMain *plugin;
93 };
94
95
96
97
98
99
100 class MotionBlurMain : public PluginVClient
101 {
102 public:
103         MotionBlurMain(PluginServer *server);
104         ~MotionBlurMain();
105
106         int process_realtime(VFrame *input_ptr, VFrame *output_ptr);
107         int is_realtime();
108         void save_data(KeyFrame *keyframe);
109         void read_data(KeyFrame *keyframe);
110         void update_gui();
111
112         PLUGIN_CLASS_MEMBERS(MotionBlurConfig)
113
114         void delete_tables();
115         VFrame *input, *output, *temp;
116         MotionBlurEngine *engine;
117         int **scale_y_table;
118         int **scale_x_table;
119         int table_entries;
120         unsigned char *accum;
121 };
122
123 class MotionBlurPackage : public LoadPackage
124 {
125 public:
126         MotionBlurPackage();
127         int y1, y2;
128 };
129
130 class MotionBlurUnit : public LoadClient
131 {
132 public:
133         MotionBlurUnit(MotionBlurEngine *server, MotionBlurMain *plugin);
134         void process_package(LoadPackage *package);
135         MotionBlurEngine *server;
136         MotionBlurMain *plugin;
137 };
138
139 class MotionBlurEngine : public LoadServer
140 {
141 public:
142         MotionBlurEngine(MotionBlurMain *plugin,
143                 int total_clients,
144                 int total_packages);
145         void init_packages();
146         LoadClient* new_client();
147         LoadPackage* new_package();
148         MotionBlurMain *plugin;
149 };
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169 REGISTER_PLUGIN(MotionBlurMain)
170
171
172
173 MotionBlurConfig::MotionBlurConfig()
174 {
175         radius = 10;
176         steps = 10;
177         r = 1;
178         g = 1;
179         b = 1;
180         a = 1;
181 }
182
183 int MotionBlurConfig::equivalent(MotionBlurConfig &that)
184 {
185         return
186                 radius == that.radius &&
187                 steps == that.steps &&
188                 r == that.r &&
189                 g == that.g &&
190                 b == that.b &&
191                 a == that.a;
192 }
193
194 void MotionBlurConfig::copy_from(MotionBlurConfig &that)
195 {
196         radius = that.radius;
197         steps = that.steps;
198         r = that.r;
199         g = that.g;
200         b = that.b;
201         a = that.a;
202 }
203
204 void MotionBlurConfig::interpolate(MotionBlurConfig &prev,
205         MotionBlurConfig &next,
206         long prev_frame,
207         long next_frame,
208         long current_frame)
209 {
210         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
211         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
212         this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
213         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
214         r = prev.r;
215         g = prev.g;
216         b = prev.b;
217         a = prev.a;
218 }
219
220
221
222
223
224
225
226
227
228
229
230
231
232 MotionBlurWindow::MotionBlurWindow(MotionBlurMain *plugin)
233  : PluginClientWindow(plugin,
234         xS(260),
235         yS(120),
236         xS(260),
237         yS(120),
238         0)
239 {
240         this->plugin = plugin;
241 }
242
243 MotionBlurWindow::~MotionBlurWindow()
244 {
245 }
246
247 void MotionBlurWindow::create_objects()
248 {
249         int xs10 = xS(10);
250         int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30);
251         int x = xs10, y = ys10;
252
253         add_subwindow(new BC_Title(x, y, _("Length:")));
254         y += ys20;
255         add_subwindow(radius = new MotionBlurSize(plugin, x, y, &plugin->config.radius, 0, 100));
256         y += ys30;
257         add_subwindow(new BC_Title(x, y, _("Steps:")));
258         y += ys20;
259         add_subwindow(steps = new MotionBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
260
261         show_window();
262         flush();
263 }
264
265
266
267
268
269 MotionBlurSize::MotionBlurSize(MotionBlurMain *plugin,
270         int x,
271         int y,
272         int *output,
273         int min,
274         int max)
275  : BC_ISlider(x, y, 0, xS(240), yS(240), min, max, *output)
276 {
277         this->plugin = plugin;
278         this->output = output;
279 }
280 int MotionBlurSize::handle_event()
281 {
282         *output = get_value();
283         plugin->send_configure_change();
284         return 1;
285 }
286
287
288
289
290
291
292
293
294
295
296 MotionBlurMain::MotionBlurMain(PluginServer *server)
297  : PluginVClient(server)
298 {
299
300         engine = 0;
301         scale_x_table = 0;
302         scale_y_table = 0;
303         table_entries = 0;
304         accum = 0;
305         temp = 0;
306 }
307
308 MotionBlurMain::~MotionBlurMain()
309 {
310
311         if(engine) delete engine;
312         delete_tables();
313         if(accum) delete [] accum;
314         if(temp) delete temp;
315 }
316
317 const char* MotionBlurMain::plugin_title() { return N_("Motion Blur"); }
318 int MotionBlurMain::is_realtime() { return 1; }
319
320
321 NEW_WINDOW_MACRO(MotionBlurMain, MotionBlurWindow)
322
323 LOAD_CONFIGURATION_MACRO(MotionBlurMain, MotionBlurConfig)
324
325 void MotionBlurMain::delete_tables()
326 {
327         if(scale_x_table)
328         {
329                 for(int i = 0; i < table_entries; i++)
330                         delete [] scale_x_table[i];
331                 delete [] scale_x_table;
332         }
333
334         if(scale_y_table)
335         {
336                 for(int i = 0; i < table_entries; i++)
337                         delete [] scale_y_table[i];
338                 delete [] scale_y_table;
339         }
340         scale_x_table = 0;
341         scale_y_table = 0;
342         table_entries = 0;
343 }
344
345 int MotionBlurMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
346 {
347         float xa,ya,za,xb,yb,zb,xd,yd,zd;
348         if (get_source_position() == 0)
349                 get_camera(&xa, &ya, &za, get_source_position());
350         else
351                 get_camera(&xa, &ya, &za, get_source_position()-1);
352         get_camera(&xb, &yb, &zb, get_source_position());
353
354         xd = xb - xa;
355         yd = yb - ya;
356         zd = zb - za;
357
358         //printf("Camera automation deltas: %.2f %.2f %.2f\n", xd, yd, zd);
359         load_configuration();
360
361 //printf("MotionBlurMain::process_realtime 1 %d\n", config.radius);
362         if(!engine) engine = new MotionBlurEngine(this,
363                 get_project_smp() + 1,
364                 get_project_smp() + 1);
365         if(!accum) accum = new unsigned char[input_ptr->get_w() *
366                 input_ptr->get_h() *
367                 BC_CModels::components(input_ptr->get_color_model()) *
368                 MAX(sizeof(int), sizeof(float))];
369
370         this->input = input_ptr;
371         this->output = output_ptr;
372
373
374         if(input_ptr->get_rows()[0] == output_ptr->get_rows()[0])
375         {
376                 if(!temp) temp = new VFrame(input_ptr->get_w(), input_ptr->get_h(),
377                         input_ptr->get_color_model(), 0);
378                 temp->copy_from(input_ptr);
379                 this->input = temp;
380         }
381
382 // Generate tables here.  The same table is used by many packages to render
383 // each horizontal stripe.  Need to cover the entire output range in  each
384 // table to avoid green borders
385         float w = input->get_w();
386         float h = input->get_h();
387         int x_offset;
388         int y_offset;
389
390         float fradius = config.radius * 0.5;
391         float zradius = (float)(zd * fradius / 4 + 1);
392         float center_x = w/2;
393         float center_y = h/2;
394
395         float min_w, min_h;
396         //float max_w, max_h;
397         float min_x1, min_y1, min_x2, min_y2;
398         float max_x1, max_y1, max_x2, max_y2;
399
400         //int steps = config.steps ? config.steps : 1;
401
402         x_offset = (int)(xd * fradius);
403         y_offset = (int)(yd * fradius);
404
405     min_w = w * zradius;
406     min_h = h * zradius;
407     //max_w = w;
408     //max_h = h;
409     min_x1 = center_x - min_w / 2;
410         min_y1 = center_y - min_h / 2;
411         min_x2 = center_x + min_w / 2;
412         min_y2 = center_y + min_h / 2;
413         max_x1 = 0;
414         max_y1 = 0;
415         max_x2 = w;
416         max_y2 = h;
417
418         delete_tables();
419         scale_x_table = new int*[config.steps];
420         scale_y_table = new int*[config.steps];
421         table_entries = config.steps;
422
423         for(int i = 0; i < config.steps; i++)
424         {
425                 float fraction = (float)(i - config.steps / 2) / config.steps;
426                 float inv_fraction = 1.0 - fraction;
427
428                 int x = (int)(fraction * x_offset);
429                 int y = (int)(fraction * y_offset);
430                 float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
431                 float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
432                 float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
433                 float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
434                 float out_w = out_x2 - out_x1;
435                 float out_h = out_y2 - out_y1;
436                 if(out_w < 0) out_w = 0;
437                 if(out_h < 0) out_h = 0;
438                 float scale_x = (float)w / out_w;
439                 float scale_y = (float)h / out_h;
440
441                 int *x_table;
442                 int *y_table;
443                 scale_y_table[i] = y_table = new int[(int)(h + 1)];
444                 scale_x_table[i] = x_table = new int[(int)(w + 1)];
445
446                 for(int j = 0; j < h; j++)
447                 {
448                         y_table[j] = (int)((j - out_y1) * scale_y) + y;
449                 }
450                 for(int j = 0; j < w; j++)
451                 {
452                         x_table[j] = (int)((j - out_x1) * scale_x) + x;
453                 }
454         }
455
456         bzero(accum,
457                 input_ptr->get_w() *
458                 input_ptr->get_h() *
459                 BC_CModels::components(input_ptr->get_color_model()) *
460                 MAX(sizeof(int), sizeof(float)));
461         engine->process_packages();
462         return 0;
463 }
464
465
466 void MotionBlurMain::update_gui()
467 {
468         if(thread)
469         {
470                 load_configuration();
471                 thread->window->lock_window();
472                 ((MotionBlurWindow*)thread->window)->radius->update(config.radius);
473                 ((MotionBlurWindow*)thread->window)->steps->update(config.steps);
474                 thread->window->unlock_window();
475         }
476 }
477
478
479
480
481
482 void MotionBlurMain::save_data(KeyFrame *keyframe)
483 {
484         FileXML output;
485
486 // cause data to be stored directly in text
487         output.set_shared_output(keyframe->xbuf);
488         output.tag.set_title("MOTIONBLUR");
489
490         output.tag.set_property("RADIUS", config.radius);
491         output.tag.set_property("STEPS", config.steps);
492         output.append_tag();
493         output.tag.set_title("/MOTIONBLUR");
494         output.append_tag();
495         output.append_newline();
496         output.terminate_string();
497 }
498
499 void MotionBlurMain::read_data(KeyFrame *keyframe)
500 {
501         FileXML input;
502
503         input.set_shared_input(keyframe->xbuf);
504
505         int result = 0;
506
507         while(!result)
508         {
509                 result = input.read_tag();
510
511                 if(!result)
512                 {
513                         if(input.tag.title_is("MOTIONBLUR"))
514                         {
515                                 config.radius = input.tag.get_property("RADIUS", config.radius);
516                                 config.steps = input.tag.get_property("STEPS", config.steps);
517                         }
518                 }
519         }
520 }
521
522
523
524
525
526
527 MotionBlurPackage::MotionBlurPackage()
528  : LoadPackage()
529 {
530 }
531
532
533
534
535 MotionBlurUnit::MotionBlurUnit(MotionBlurEngine *server,
536         MotionBlurMain *plugin)
537  : LoadClient(server)
538 {
539         this->plugin = plugin;
540         this->server = server;
541 }
542
543
544 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP, MAX, DO_YUV) \
545 { \
546         const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
547         for(int j = pkg->y1; j < pkg->y2; j++) \
548         { \
549                 TEMP *out_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
550                 int in_y = y_table[j]; \
551  \
552 /* Blend image */ \
553                 if(in_y >= 0 && in_y < h) \
554                 { \
555                         TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
556                         for(int k = 0; k < w; k++) \
557                         { \
558                                 int in_x = x_table[k]; \
559 /* Blend pixel */ \
560                                 if(in_x >= 0 && in_x < w) \
561                                 { \
562                                         int in_offset = in_x * COMPONENTS; \
563                                         *out_row++ += in_row[in_offset]; \
564                                         if(DO_YUV) \
565                                         { \
566                                                 *out_row++ += in_row[in_offset + 1]; \
567                                                 *out_row++ += in_row[in_offset + 2]; \
568                                         } \
569                                         else \
570                                         { \
571                                                 *out_row++ += in_row[in_offset + 1]; \
572                                                 *out_row++ += in_row[in_offset + 2]; \
573                                         } \
574                                         if(COMPONENTS == 4) \
575                                                 *out_row++ += in_row[in_offset + 3]; \
576                                 } \
577 /* Blend nothing */ \
578                                 else \
579                                 { \
580                                         out_row++; \
581                                         if(DO_YUV) \
582                                         { \
583                                                 *out_row++ += chroma_offset; \
584                                                 *out_row++ += chroma_offset; \
585                                         } \
586                                         else \
587                                         { \
588                                                 out_row += 2; \
589                                         } \
590                                         if(COMPONENTS == 4) out_row++; \
591                                 } \
592                         } \
593                 } \
594                 else \
595                 if(DO_YUV) \
596                 { \
597                         for(int k = 0; k < w; k++) \
598                         { \
599                                 out_row++; \
600                                 *out_row++ += chroma_offset; \
601                                 *out_row++ += chroma_offset; \
602                                 if(COMPONENTS == 4) out_row++; \
603                         } \
604                 } \
605         } \
606  \
607 /* Copy to output */ \
608         if(i == plugin->config.steps - 1) \
609         { \
610                 for(int j = pkg->y1; j < pkg->y2; j++) \
611                 { \
612                         TEMP *in_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
613                         TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
614                         TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
615                         for(int k = 0; k < w; k++) \
616                         { \
617                                 *out_row++ = (*in_row++ * fraction) / 0x10000; \
618                                 in_backup++; \
619  \
620                                 if(DO_YUV) \
621                                 { \
622                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
623                                         in_backup++; \
624  \
625                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
626                                         in_backup++; \
627                                 } \
628                                 else \
629                                 { \
630                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
631                                         in_backup++; \
632                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
633                                         in_backup++; \
634                                 } \
635  \
636                                 if(COMPONENTS == 4) \
637                                 { \
638                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
639                                         in_backup++; \
640                                 } \
641                         } \
642                 } \
643         } \
644 }
645
646 void MotionBlurUnit::process_package(LoadPackage *package)
647 {
648         MotionBlurPackage *pkg = (MotionBlurPackage*)package;
649         int h = plugin->output->get_h();
650         int w = plugin->output->get_w();
651
652         int fraction = 0x10000 / plugin->config.steps;
653         for(int i = 0; i < plugin->config.steps; i++)
654         {
655                 int *x_table = plugin->scale_x_table[i];
656                 int *y_table = plugin->scale_y_table[i];
657
658                 switch(plugin->input->get_color_model())
659                 {
660                         case BC_RGB_FLOAT:
661                                 BLEND_LAYER(3, float, float, 1, 0)
662                                 break;
663                         case BC_RGB888:
664                                 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
665                                 break;
666                         case BC_RGBA_FLOAT:
667                                 BLEND_LAYER(4, float, float, 1, 0)
668                                 break;
669                         case BC_RGBA8888:
670                                 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
671                                 break;
672                         case BC_RGB161616:
673                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
674                                 break;
675                         case BC_RGBA16161616:
676                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
677                                 break;
678                         case BC_YUV888:
679                                 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
680                                 break;
681                         case BC_YUVA8888:
682                                 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
683                                 break;
684                         case BC_YUV161616:
685                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
686                                 break;
687                         case BC_YUVA16161616:
688                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
689                                 break;
690                 }
691         }
692 }
693
694
695
696
697
698
699 MotionBlurEngine::MotionBlurEngine(MotionBlurMain *plugin,
700         int total_clients,
701         int total_packages)
702  : LoadServer(total_clients, total_packages)
703 {
704         this->plugin = plugin;
705 }
706
707 void MotionBlurEngine::init_packages()
708 {
709         for(int i = 0; i < get_total_packages(); i++)
710         {
711                 MotionBlurPackage *package = (MotionBlurPackage*)get_package(i);
712                 package->y1 = plugin->output->get_h() * i / get_total_packages();
713                 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
714         }
715 }
716
717 LoadClient* MotionBlurEngine::new_client()
718 {
719         return new MotionBlurUnit(this, plugin);
720 }
721
722 LoadPackage* MotionBlurEngine::new_package()
723 {
724         return new MotionBlurPackage;
725 }
726
727
728
729
730