r864: Merge 2.1:
[cinelerra_cv/ct.git] / plugins / motionblur / motionblur.C
blob946086e2bf8fe84947c3375cd51ae02b1f8ef3c4
1 #include <math.h>
2 #include <stdint.h>
3 #include <string.h>
5 #include "bcdisplayinfo.h"
6 #include "clip.h"
7 #include "bchash.h"
8 #include "filexml.h"
9 #include "keyframe.h"
10 #include "language.h"
11 #include "loadbalance.h"
12 #include "picon_png.h"
13 #include "pluginvclient.h"
14 #include "vframe.h"
17 class MotionBlurMain;
18 class MotionBlurEngine;
24 class MotionBlurConfig
26 public:
27         MotionBlurConfig();
29         int equivalent(MotionBlurConfig &that);
30         void copy_from(MotionBlurConfig &that);
31         void interpolate(MotionBlurConfig &prev, 
32                 MotionBlurConfig &next, 
33                 long prev_frame, 
34                 long next_frame, 
35                 long current_frame);
37         int radius;
38         int steps;
39         int r;
40         int g;
41         int b;
42         int a;
47 class MotionBlurSize : public BC_ISlider
49 public:
50         MotionBlurSize(MotionBlurMain *plugin, 
51                 int x, 
52                 int y, 
53                 int *output,
54                 int min,
55                 int max);
56         int handle_event();
57         MotionBlurMain *plugin;
58         int *output;
62 class MotionBlurWindow : public BC_Window
64 public:
65         MotionBlurWindow(MotionBlurMain *plugin, int x, int y);
66         ~MotionBlurWindow();
68         int create_objects();
69         int close_event();
71         MotionBlurSize *steps, *radius;
72         MotionBlurMain *plugin;
77 PLUGIN_THREAD_HEADER(MotionBlurMain, MotionBlurThread, MotionBlurWindow)
80 class MotionBlurMain : public PluginVClient
82 public:
83         MotionBlurMain(PluginServer *server);
84         ~MotionBlurMain();
86         int process_realtime(VFrame *input_ptr, VFrame *output_ptr);
87         int is_realtime();
88         int load_defaults();
89         int save_defaults();
90         void save_data(KeyFrame *keyframe);
91         void read_data(KeyFrame *keyframe);
92         void update_gui();
94         PLUGIN_CLASS_MEMBERS(MotionBlurConfig, MotionBlurThread)
96         void delete_tables();
97         VFrame *input, *output, *temp;
98         MotionBlurEngine *engine;
99         int **scale_y_table;
100         int **scale_x_table;
101         int table_entries;
102         unsigned char *accum;
105 class MotionBlurPackage : public LoadPackage
107 public:
108         MotionBlurPackage();
109         int y1, y2;
112 class MotionBlurUnit : public LoadClient
114 public:
115         MotionBlurUnit(MotionBlurEngine *server, MotionBlurMain *plugin);
116         void process_package(LoadPackage *package);
117         MotionBlurEngine *server;
118         MotionBlurMain *plugin;
121 class MotionBlurEngine : public LoadServer
123 public:
124         MotionBlurEngine(MotionBlurMain *plugin, 
125                 int total_clients, 
126                 int total_packages);
127         void init_packages();
128         LoadClient* new_client();
129         LoadPackage* new_package();
130         MotionBlurMain *plugin;
151 REGISTER_PLUGIN(MotionBlurMain)
155 MotionBlurConfig::MotionBlurConfig()
157         radius = 10;
158         steps = 10;
159         r = 1;
160         g = 1;
161         b = 1;
162         a = 1;
165 int MotionBlurConfig::equivalent(MotionBlurConfig &that)
167         return 
168                 radius == that.radius &&
169                 steps == that.steps &&
170                 r == that.r &&
171                 g == that.g &&
172                 b == that.b &&
173                 a == that.a;
176 void MotionBlurConfig::copy_from(MotionBlurConfig &that)
178         radius = that.radius;
179         steps = that.steps;
180         r = that.r;
181         g = that.g;
182         b = that.b;
183         a = that.a;
186 void MotionBlurConfig::interpolate(MotionBlurConfig &prev, 
187         MotionBlurConfig &next, 
188         long prev_frame, 
189         long next_frame, 
190         long current_frame)
192         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
193         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
194         this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
195         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
196         r = prev.r;
197         g = prev.g;
198         b = prev.b;
199         a = prev.a;
210 PLUGIN_THREAD_OBJECT(MotionBlurMain, MotionBlurThread, MotionBlurWindow)
214 MotionBlurWindow::MotionBlurWindow(MotionBlurMain *plugin, int x, int y)
215  : BC_Window(plugin->gui_string, 
216         x,
217         y,
218         260, 
219         120, 
220         260, 
221         120, 
222         0, 
223         1)
225         this->plugin = plugin; 
228 MotionBlurWindow::~MotionBlurWindow()
232 int MotionBlurWindow::create_objects()
234         int x = 10, y = 10;
236         add_subwindow(new BC_Title(x, y, _("Length:")));
237         y += 20;
238         add_subwindow(radius = new MotionBlurSize(plugin, x, y, &plugin->config.radius, 0, 100));
239         y += 30;
240         add_subwindow(new BC_Title(x, y, _("Steps:")));
241         y += 20;
242         add_subwindow(steps = new MotionBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
244         show_window();
245         flush();
246         return 0;
249 WINDOW_CLOSE_EVENT(MotionBlurWindow)
253 MotionBlurSize::MotionBlurSize(MotionBlurMain *plugin, 
254         int x, 
255         int y, 
256         int *output,
257         int min,
258         int max)
259  : BC_ISlider(x, y, 0, 240, 240, min, max, *output)
261         this->plugin = plugin;
262         this->output = output;
264 int MotionBlurSize::handle_event()
266         *output = get_value();
267         plugin->send_configure_change();
268         return 1;
280 MotionBlurMain::MotionBlurMain(PluginServer *server)
281  : PluginVClient(server)
283         PLUGIN_CONSTRUCTOR_MACRO
284         engine = 0;
285         scale_x_table = 0;
286         scale_y_table = 0;
287         table_entries = 0;
288         accum = 0;
289         temp = 0;
292 MotionBlurMain::~MotionBlurMain()
294         PLUGIN_DESTRUCTOR_MACRO
295         if(engine) delete engine;
296         delete_tables();
297         if(accum) delete [] accum;
298         if(temp) delete temp;
301 char* MotionBlurMain::plugin_title() { return N_("Motion Blur"); }
302 int MotionBlurMain::is_realtime() { return 1; }
305 NEW_PICON_MACRO(MotionBlurMain)
307 SHOW_GUI_MACRO(MotionBlurMain, MotionBlurThread)
309 SET_STRING_MACRO(MotionBlurMain)
311 RAISE_WINDOW_MACRO(MotionBlurMain)
313 LOAD_CONFIGURATION_MACRO(MotionBlurMain, MotionBlurConfig)
315 void MotionBlurMain::delete_tables()
317         if(scale_x_table)
318         {
319                 for(int i = 0; i < table_entries; i++)
320                         delete [] scale_x_table[i];
321                 delete [] scale_x_table;
322         }
324         if(scale_y_table)
325         {
326                 for(int i = 0; i < table_entries; i++)
327                         delete [] scale_y_table[i];
328                 delete [] scale_y_table;
329         }
330         scale_x_table = 0;
331         scale_y_table = 0;
332         table_entries = 0;
335 int MotionBlurMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
337         float xa,ya,za,xb,yb,zb,xd,yd,zd;
338         if (get_source_position() == 0) 
339                 get_camera(&xa, &ya, &za, get_source_position());
340         else
341                 get_camera(&xa, &ya, &za, get_source_position()-1);
342         get_camera(&xb, &yb, &zb, get_source_position());
344         xd = xb - xa;
345         yd = yb - ya;
346         zd = zb - za;
347         
348         //printf("Camera automation deltas: %.2f %.2f %.2f\n", xd, yd, zd);
349         load_configuration();
351 //printf("MotionBlurMain::process_realtime 1 %d\n", config.radius);
352         if(!engine) engine = new MotionBlurEngine(this,
353                 get_project_smp() + 1,
354                 get_project_smp() + 1);
355         if(!accum) accum = new unsigned char[input_ptr->get_w() * 
356                 input_ptr->get_h() *
357                 cmodel_components(input_ptr->get_color_model()) *
358                 MAX(sizeof(int), sizeof(float))];
360         this->input = input_ptr;
361         this->output = output_ptr;
364         if(input_ptr->get_rows()[0] == output_ptr->get_rows()[0])
365         {
366                 if(!temp) temp = new VFrame(0,
367                         input_ptr->get_w(),
368                         input_ptr->get_h(),
369                         input_ptr->get_color_model());
370                 temp->copy_from(input_ptr);
371                 this->input = temp;
372         }
374 // Generate tables here.  The same table is used by many packages to render
375 // each horizontal stripe.  Need to cover the entire output range in  each
376 // table to avoid green borders
377         float w = input->get_w();
378         float h = input->get_h();
379         int x_offset;
380         int y_offset;
382         float fradius = config.radius * 0.5;
383         float zradius = (float)(zd * fradius / 4 + 1);
384         float center_x = w/2;
385         float center_y = h/2;
387         float min_w, min_h;
388         float max_w, max_h;
389         float min_x1, min_y1, min_x2, min_y2;
390         float max_x1, max_y1, max_x2, max_y2;
392         int steps = config.steps ? config.steps : 1;
394         x_offset = (int)(xd * fradius);
395         y_offset = (int)(yd * fradius);
397     min_w = w * zradius;
398     min_h = h * zradius;
399     max_w = w;
400     max_h = h;
401     min_x1 = center_x - min_w / 2;
402         min_y1 = center_y - min_h / 2;
403         min_x2 = center_x + min_w / 2;
404         min_y2 = center_y + min_h / 2;
405         max_x1 = 0;
406         max_y1 = 0;
407         max_x2 = w;
408         max_y2 = h;
409                 
410         delete_tables();
411         scale_x_table = new int*[config.steps];
412         scale_y_table = new int*[config.steps];
413         table_entries = config.steps;
415         for(int i = 0; i < config.steps; i++)
416         {
417                 float fraction = (float)(i - config.steps / 2) / config.steps;
418                 float inv_fraction = 1.0 - fraction;
419                 
420                 int x = (int)(fraction * x_offset);
421                 int y = (int)(fraction * y_offset);
422                 float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
423                 float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
424                 float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
425                 float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
426                 float out_w = out_x2 - out_x1;
427                 float out_h = out_y2 - out_y1;
428                 if(out_w < 0) out_w = 0;
429                 if(out_h < 0) out_h = 0;
430                 float scale_x = (float)w / out_w;
431                 float scale_y = (float)h / out_h;
433                 int *x_table;
434                 int *y_table;
435                 scale_y_table[i] = y_table = new int[(int)(h + 1)];
436                 scale_x_table[i] = x_table = new int[(int)(w + 1)];
437                         
438                 for(int j = 0; j < h; j++)
439                 {
440                         y_table[j] = (int)((j - out_y1) * scale_y) + y;
441                 }
442                 for(int j = 0; j < w; j++)
443                 {
444                         x_table[j] = (int)((j - out_x1) * scale_x) + x;
445                 }
446         }
448         bzero(accum, 
449                 input_ptr->get_w() * 
450                 input_ptr->get_h() * 
451                 cmodel_components(input_ptr->get_color_model()) * 
452                 MAX(sizeof(int), sizeof(float)));
453         engine->process_packages();
454         return 0;
458 void MotionBlurMain::update_gui()
460         if(thread)
461         {
462                 load_configuration();
463                 thread->window->lock_window();
464                 thread->window->radius->update(config.radius);
465                 thread->window->steps->update(config.steps);
466                 thread->window->unlock_window();
467         }
471 int MotionBlurMain::load_defaults()
473         char directory[1024], string[1024];
474 // set the default directory
475         sprintf(directory, "%smotionblur.rc", BCASTDIR);
477 // load the defaults
478         defaults = new BC_Hash(directory);
479         defaults->load();
481         config.radius = defaults->get("RADIUS", config.radius);
482         config.steps = defaults->get("STEPS", config.steps);
483         return 0;
487 int MotionBlurMain::save_defaults()
489         defaults->update("RADIUS", config.radius);
490         defaults->update("STEPS", config.steps);
491         defaults->save();
492         return 0;
497 void MotionBlurMain::save_data(KeyFrame *keyframe)
499         FileXML output;
501 // cause data to be stored directly in text
502         output.set_shared_string(keyframe->data, MESSAGESIZE);
503         output.tag.set_title("MOTIONBLUR");
505         output.tag.set_property("RADIUS", config.radius);
506         output.tag.set_property("STEPS", config.steps);
507         output.append_tag();
508         output.terminate_string();
511 void MotionBlurMain::read_data(KeyFrame *keyframe)
513         FileXML input;
515         input.set_shared_string(keyframe->data, strlen(keyframe->data));
517         int result = 0;
519         while(!result)
520         {
521                 result = input.read_tag();
523                 if(!result)
524                 {
525                         if(input.tag.title_is("MOTIONBLUR"))
526                         {
527                                 config.radius = input.tag.get_property("RADIUS", config.radius);
528                                 config.steps = input.tag.get_property("STEPS", config.steps);
529                         }
530                 }
531         }
539 MotionBlurPackage::MotionBlurPackage()
540  : LoadPackage()
547 MotionBlurUnit::MotionBlurUnit(MotionBlurEngine *server, 
548         MotionBlurMain *plugin)
549  : LoadClient(server)
551         this->plugin = plugin;
552         this->server = server;
556 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP, MAX, DO_YUV) \
557 { \
558         const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
559         for(int j = pkg->y1; j < pkg->y2; j++) \
560         { \
561                 TEMP *out_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
562                 int in_y = y_table[j]; \
564 /* Blend image */ \
565                 if(in_y >= 0 && in_y < h) \
566                 { \
567                         TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
568                         for(int k = 0; k < w; k++) \
569                         { \
570                                 int in_x = x_table[k]; \
571 /* Blend pixel */ \
572                                 if(in_x >= 0 && in_x < w) \
573                                 { \
574                                         int in_offset = in_x * COMPONENTS; \
575                                         *out_row++ += in_row[in_offset]; \
576                                         if(DO_YUV) \
577                                         { \
578                                                 *out_row++ += in_row[in_offset + 1]; \
579                                                 *out_row++ += in_row[in_offset + 2]; \
580                                         } \
581                                         else \
582                                         { \
583                                                 *out_row++ += in_row[in_offset + 1]; \
584                                                 *out_row++ += in_row[in_offset + 2]; \
585                                         } \
586                                         if(COMPONENTS == 4) \
587                                                 *out_row++ += in_row[in_offset + 3]; \
588                                 } \
589 /* Blend nothing */ \
590                                 else \
591                                 { \
592                                         out_row++; \
593                                         if(DO_YUV) \
594                                         { \
595                                                 *out_row++ += chroma_offset; \
596                                                 *out_row++ += chroma_offset; \
597                                         } \
598                                         else \
599                                         { \
600                                                 out_row += 2; \
601                                         } \
602                                         if(COMPONENTS == 4) out_row++; \
603                                 } \
604                         } \
605                 } \
606                 else \
607                 if(DO_YUV) \
608                 { \
609                         for(int k = 0; k < w; k++) \
610                         { \
611                                 out_row++; \
612                                 *out_row++ += chroma_offset; \
613                                 *out_row++ += chroma_offset; \
614                                 if(COMPONENTS == 4) out_row++; \
615                         } \
616                 } \
617         } \
619 /* Copy to output */ \
620         if(i == plugin->config.steps - 1) \
621         { \
622                 for(int j = pkg->y1; j < pkg->y2; j++) \
623                 { \
624                         TEMP *in_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
625                         TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
626                         TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
627                         for(int k = 0; k < w; k++) \
628                         { \
629                                 *out_row++ = (*in_row++ * fraction) / 0x10000; \
630                                 in_backup++; \
632                                 if(DO_YUV) \
633                                 { \
634                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
635                                         in_backup++; \
637                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
638                                         in_backup++; \
639                                 } \
640                                 else \
641                                 { \
642                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
643                                         in_backup++; \
644                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
645                                         in_backup++; \
646                                 } \
648                                 if(COMPONENTS == 4) \
649                                 { \
650                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
651                                         in_backup++; \
652                                 } \
653                         } \
654                 } \
655         } \
658 void MotionBlurUnit::process_package(LoadPackage *package)
660         MotionBlurPackage *pkg = (MotionBlurPackage*)package;
661         int h = plugin->output->get_h();
662         int w = plugin->output->get_w();
664         int fraction = 0x10000 / plugin->config.steps;
665         for(int i = 0; i < plugin->config.steps; i++)
666         {
667                 int *x_table = plugin->scale_x_table[i];
668                 int *y_table = plugin->scale_y_table[i];
670                 switch(plugin->input->get_color_model())
671                 {
672                         case BC_RGB_FLOAT:
673                                 BLEND_LAYER(3, float, float, 1, 0)
674                                 break;
675                         case BC_RGB888:
676                                 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
677                                 break;
678                         case BC_RGBA_FLOAT:
679                                 BLEND_LAYER(4, float, float, 1, 0)
680                                 break;
681                         case BC_RGBA8888:
682                                 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
683                                 break;
684                         case BC_RGB161616:
685                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
686                                 break;
687                         case BC_RGBA16161616:
688                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
689                                 break;
690                         case BC_YUV888:
691                                 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
692                                 break;
693                         case BC_YUVA8888:
694                                 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
695                                 break;
696                         case BC_YUV161616:
697                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
698                                 break;
699                         case BC_YUVA16161616:
700                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
701                                 break;
702                 }
703         }
711 MotionBlurEngine::MotionBlurEngine(MotionBlurMain *plugin, 
712         int total_clients, 
713         int total_packages)
714  : LoadServer(total_clients, total_packages)
716         this->plugin = plugin;
719 void MotionBlurEngine::init_packages()
721         for(int i = 0; i < get_total_packages(); i++)
722         {
723                 MotionBlurPackage *package = (MotionBlurPackage*)get_package(i);
724                 package->y1 = plugin->output->get_h() * i / get_total_packages();
725                 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
726         }
729 LoadClient* MotionBlurEngine::new_client()
731         return new MotionBlurUnit(this, plugin);
734 LoadPackage* MotionBlurEngine::new_package()
736         return new MotionBlurPackage;