r794: Motion blur plugin by jstewart@lurhq.com (Joe Stewart)
[cinelerra_cv/mob.git] / plugins / motionblur / motionblur.C
blob6b32e795840c8517e04ba24d9f47fe386568ff4c
1 #include <math.h>
2 #include <stdint.h>
3 #include <string.h>
5 #include "bcdisplayinfo.h"
6 #include "clip.h"
7 #include "defaults.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 zradius = (float)(zd * config.radius / 4 + 1);
383         float center_x = w/2;
384         float center_y = h/2;
386         float min_w, min_h;
387         float max_w, max_h;
388         float min_x1, min_y1, min_x2, min_y2;
389         float max_x1, max_y1, max_x2, max_y2;
391         int steps = config.steps ? config.steps : 1;
393         x_offset = (int)(xd * config.radius);
394         y_offset = (int)(yd * config.radius);
396         min_w = w * zradius;
397         min_h = h * zradius;
398         max_w = w;
399         max_h = h;
400         min_x1 = center_x - min_w / 2;
401         min_y1 = center_y - min_h / 2;
402         min_x2 = center_x + min_w / 2;
403         min_y2 = center_y + min_h / 2;
404         max_x1 = 0;
405         max_y1 = 0;
406         max_x2 = w;
407         max_y2 = h;
408                 
409         delete_tables();
410         scale_x_table = new int*[config.steps];
411         scale_y_table = new int*[config.steps];
412         table_entries = config.steps;
414         for(int i = 0; i < config.steps; i++)
415         {
416                 float fraction = (float)(i - config.steps / 2) / config.steps;
417                 float inv_fraction = 1.0 - fraction;
418                 
419                 int x = (int)(fraction * x_offset);
420                 int y = (int)(fraction * y_offset);
421                 float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
422                 float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
423                 float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
424                 float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
425                 float out_w = out_x2 - out_x1;
426                 float out_h = out_y2 - out_y1;
427                 if(out_w < 0) out_w = 0;
428                 if(out_h < 0) out_h = 0;
429                 float scale_x = (float)w / out_w;
430                 float scale_y = (float)h / out_h;
432                 int *x_table;
433                 int *y_table;
434                 scale_y_table[i] = y_table = new int[(int)(h + 1)];
435                 scale_x_table[i] = x_table = new int[(int)(w + 1)];
436                         
437                 for(int j = 0; j < h; j++)
438                 {
439                         y_table[j] = (int)((j - out_y1) * scale_y) + y;
440                 }
441                 for(int j = 0; j < w; j++)
442                 {
443                         x_table[j] = (int)((j - out_x1) * scale_x) + x;
444                 }
445         }
447         bzero(accum, 
448                 input_ptr->get_w() * 
449                 input_ptr->get_h() * 
450                 cmodel_components(input_ptr->get_color_model()) * 
451                 MAX(sizeof(int), sizeof(float)));
452         engine->process_packages();
453         return 0;
457 void MotionBlurMain::update_gui()
459         if(thread)
460         {
461                 load_configuration();
462                 thread->window->lock_window();
463                 thread->window->radius->update(config.radius);
464                 thread->window->steps->update(config.steps);
465                 thread->window->unlock_window();
466         }
470 int MotionBlurMain::load_defaults()
472         char directory[1024], string[1024];
473 // set the default directory
474         sprintf(directory, "%smotionblur.rc", BCASTDIR);
476 // load the defaults
477         defaults = new Defaults(directory);
478         defaults->load();
480         config.radius = defaults->get("RADIUS", config.radius);
481         config.steps = defaults->get("STEPS", config.steps);
482         return 0;
486 int MotionBlurMain::save_defaults()
488         defaults->update("RADIUS", config.radius);
489         defaults->update("STEPS", config.steps);
490         defaults->save();
491         return 0;
496 void MotionBlurMain::save_data(KeyFrame *keyframe)
498         FileXML output;
500 // cause data to be stored directly in text
501         output.set_shared_string(keyframe->data, MESSAGESIZE);
502         output.tag.set_title("MOTIONBLUR");
504         output.tag.set_property("RADIUS", config.radius);
505         output.tag.set_property("STEPS", config.steps);
506         output.append_tag();
507         output.terminate_string();
510 void MotionBlurMain::read_data(KeyFrame *keyframe)
512         FileXML input;
514         input.set_shared_string(keyframe->data, strlen(keyframe->data));
516         int result = 0;
518         while(!result)
519         {
520                 result = input.read_tag();
522                 if(!result)
523                 {
524                         if(input.tag.title_is("MOTIONBLUR"))
525                         {
526                                 config.radius = input.tag.get_property("RADIUS", config.radius);
527                                 config.steps = input.tag.get_property("STEPS", config.steps);
528                         }
529                 }
530         }
538 MotionBlurPackage::MotionBlurPackage()
539  : LoadPackage()
546 MotionBlurUnit::MotionBlurUnit(MotionBlurEngine *server, 
547         MotionBlurMain *plugin)
548  : LoadClient(server)
550         this->plugin = plugin;
551         this->server = server;
555 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP, MAX, DO_YUV) \
556 { \
557         const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
558         for(int j = pkg->y1; j < pkg->y2; j++) \
559         { \
560                 TEMP *out_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
561                 int in_y = y_table[j]; \
563 /* Blend image */ \
564                 if(in_y >= 0 && in_y < h) \
565                 { \
566                         TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
567                         for(int k = 0; k < w; k++) \
568                         { \
569                                 int in_x = x_table[k]; \
570 /* Blend pixel */ \
571                                 if(in_x >= 0 && in_x < w) \
572                                 { \
573                                         int in_offset = in_x * COMPONENTS; \
574                                         *out_row++ += in_row[in_offset]; \
575                                         if(DO_YUV) \
576                                         { \
577                                                 *out_row++ += in_row[in_offset + 1]; \
578                                                 *out_row++ += in_row[in_offset + 2]; \
579                                         } \
580                                         else \
581                                         { \
582                                                 *out_row++ += in_row[in_offset + 1]; \
583                                                 *out_row++ += in_row[in_offset + 2]; \
584                                         } \
585                                         if(COMPONENTS == 4) \
586                                                 *out_row++ += in_row[in_offset + 3]; \
587                                 } \
588 /* Blend nothing */ \
589                                 else \
590                                 { \
591                                         out_row++; \
592                                         if(DO_YUV) \
593                                         { \
594                                                 *out_row++ += chroma_offset; \
595                                                 *out_row++ += chroma_offset; \
596                                         } \
597                                         else \
598                                         { \
599                                                 out_row += 2; \
600                                         } \
601                                         if(COMPONENTS == 4) out_row++; \
602                                 } \
603                         } \
604                 } \
605                 else \
606                 if(DO_YUV) \
607                 { \
608                         for(int k = 0; k < w; k++) \
609                         { \
610                                 out_row++; \
611                                 *out_row++ += chroma_offset; \
612                                 *out_row++ += chroma_offset; \
613                                 if(COMPONENTS == 4) out_row++; \
614                         } \
615                 } \
616         } \
618 /* Copy to output */ \
619         if(i == plugin->config.steps - 1) \
620         { \
621                 for(int j = pkg->y1; j < pkg->y2; j++) \
622                 { \
623                         TEMP *in_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
624                         TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
625                         TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
626                         for(int k = 0; k < w; k++) \
627                         { \
628                                 *out_row++ = (*in_row++ * fraction) / 0x10000; \
629                                 in_backup++; \
631                                 if(DO_YUV) \
632                                 { \
633                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
634                                         in_backup++; \
636                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
637                                         in_backup++; \
638                                 } \
639                                 else \
640                                 { \
641                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
642                                         in_backup++; \
643                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
644                                         in_backup++; \
645                                 } \
647                                 if(COMPONENTS == 4) \
648                                 { \
649                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
650                                         in_backup++; \
651                                 } \
652                         } \
653                 } \
654         } \
657 void MotionBlurUnit::process_package(LoadPackage *package)
659         MotionBlurPackage *pkg = (MotionBlurPackage*)package;
660         int h = plugin->output->get_h();
661         int w = plugin->output->get_w();
663         int fraction = 0x10000 / plugin->config.steps;
664         for(int i = 0; i < plugin->config.steps; i++)
665         {
666                 int *x_table = plugin->scale_x_table[i];
667                 int *y_table = plugin->scale_y_table[i];
669                 switch(plugin->input->get_color_model())
670                 {
671                         case BC_RGB_FLOAT:
672                                 BLEND_LAYER(3, float, float, 1, 0)
673                                 break;
674                         case BC_RGB888:
675                                 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
676                                 break;
677                         case BC_RGBA_FLOAT:
678                                 BLEND_LAYER(4, float, float, 1, 0)
679                                 break;
680                         case BC_RGBA8888:
681                                 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
682                                 break;
683                         case BC_RGB161616:
684                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
685                                 break;
686                         case BC_RGBA16161616:
687                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
688                                 break;
689                         case BC_YUV888:
690                                 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
691                                 break;
692                         case BC_YUVA8888:
693                                 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
694                                 break;
695                         case BC_YUV161616:
696                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
697                                 break;
698                         case BC_YUVA16161616:
699                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
700                                 break;
701                 }
702         }
710 MotionBlurEngine::MotionBlurEngine(MotionBlurMain *plugin, 
711         int total_clients, 
712         int total_packages)
713  : LoadServer(total_clients, total_packages)
715         this->plugin = plugin;
718 void MotionBlurEngine::init_packages()
720         for(int i = 0; i < total_packages; i++)
721         {
722                 MotionBlurPackage *package = (MotionBlurPackage*)packages[i];
723                 package->y1 = plugin->output->get_h() * i / total_packages;
724                 package->y2 = plugin->output->get_h() * (i + 1) / total_packages;
725         }
728 LoadClient* MotionBlurEngine::new_client()
730         return new MotionBlurUnit(this, plugin);
733 LoadPackage* MotionBlurEngine::new_package()
735         return new MotionBlurPackage;