c5e1ac2c9bf29e58ba2a9d258b7a1152e3575f1d
[goodguy/history.git] / cinelerra-5.0 / 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         260, 
235         120, 
236         260, 
237         120, 
238         0)
239 {
240         this->plugin = plugin; 
241 }
242
243 MotionBlurWindow::~MotionBlurWindow()
244 {
245 }
246
247 void MotionBlurWindow::create_objects()
248 {
249         int x = 10, y = 10;
250
251         add_subwindow(new BC_Title(x, y, _("Length:")));
252         y += 20;
253         add_subwindow(radius = new MotionBlurSize(plugin, x, y, &plugin->config.radius, 0, 100));
254         y += 30;
255         add_subwindow(new BC_Title(x, y, _("Steps:")));
256         y += 20;
257         add_subwindow(steps = new MotionBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
258
259         show_window();
260         flush();
261 }
262
263
264
265
266
267 MotionBlurSize::MotionBlurSize(MotionBlurMain *plugin, 
268         int x, 
269         int y, 
270         int *output,
271         int min,
272         int max)
273  : BC_ISlider(x, y, 0, 240, 240, min, max, *output)
274 {
275         this->plugin = plugin;
276         this->output = output;
277 }
278 int MotionBlurSize::handle_event()
279 {
280         *output = get_value();
281         plugin->send_configure_change();
282         return 1;
283 }
284
285
286
287
288
289
290
291
292
293
294 MotionBlurMain::MotionBlurMain(PluginServer *server)
295  : PluginVClient(server)
296 {
297         
298         engine = 0;
299         scale_x_table = 0;
300         scale_y_table = 0;
301         table_entries = 0;
302         accum = 0;
303         temp = 0;
304 }
305
306 MotionBlurMain::~MotionBlurMain()
307 {
308         
309         if(engine) delete engine;
310         delete_tables();
311         if(accum) delete [] accum;
312         if(temp) delete temp;
313 }
314
315 const char* MotionBlurMain::plugin_title() { return N_("Motion Blur"); }
316 int MotionBlurMain::is_realtime() { return 1; }
317
318
319 NEW_WINDOW_MACRO(MotionBlurMain, MotionBlurWindow)
320
321 LOAD_CONFIGURATION_MACRO(MotionBlurMain, MotionBlurConfig)
322
323 void MotionBlurMain::delete_tables()
324 {
325         if(scale_x_table)
326         {
327                 for(int i = 0; i < table_entries; i++)
328                         delete [] scale_x_table[i];
329                 delete [] scale_x_table;
330         }
331
332         if(scale_y_table)
333         {
334                 for(int i = 0; i < table_entries; i++)
335                         delete [] scale_y_table[i];
336                 delete [] scale_y_table;
337         }
338         scale_x_table = 0;
339         scale_y_table = 0;
340         table_entries = 0;
341 }
342
343 int MotionBlurMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
344 {
345         float xa,ya,za,xb,yb,zb,xd,yd,zd;
346         if (get_source_position() == 0) 
347                 get_camera(&xa, &ya, &za, get_source_position());
348         else
349                 get_camera(&xa, &ya, &za, get_source_position()-1);
350         get_camera(&xb, &yb, &zb, get_source_position());
351
352         xd = xb - xa;
353         yd = yb - ya;
354         zd = zb - za;
355         
356         //printf("Camera automation deltas: %.2f %.2f %.2f\n", xd, yd, zd);
357         load_configuration();
358
359 //printf("MotionBlurMain::process_realtime 1 %d\n", config.radius);
360         if(!engine) engine = new MotionBlurEngine(this,
361                 get_project_smp() + 1,
362                 get_project_smp() + 1);
363         if(!accum) accum = new unsigned char[input_ptr->get_w() * 
364                 input_ptr->get_h() *
365                 BC_CModels::components(input_ptr->get_color_model()) *
366                 MAX(sizeof(int), sizeof(float))];
367
368         this->input = input_ptr;
369         this->output = output_ptr;
370
371
372         if(input_ptr->get_rows()[0] == output_ptr->get_rows()[0])
373         {
374                 if(!temp) temp = new VFrame(0,
375                         -1,
376                         input_ptr->get_w(),
377                         input_ptr->get_h(),
378                         input_ptr->get_color_model(),
379                         -1);
380                 temp->copy_from(input_ptr);
381                 this->input = temp;
382         }
383
384 // Generate tables here.  The same table is used by many packages to render
385 // each horizontal stripe.  Need to cover the entire output range in  each
386 // table to avoid green borders
387         float w = input->get_w();
388         float h = input->get_h();
389         int x_offset;
390         int y_offset;
391
392         float fradius = config.radius * 0.5;
393         float zradius = (float)(zd * fradius / 4 + 1);
394         float center_x = w/2;
395         float center_y = h/2;
396
397         float min_w, min_h;
398         //float max_w, max_h;
399         float min_x1, min_y1, min_x2, min_y2;
400         float max_x1, max_y1, max_x2, max_y2;
401
402         //int steps = config.steps ? config.steps : 1;
403
404         x_offset = (int)(xd * fradius);
405         y_offset = (int)(yd * fradius);
406
407     min_w = w * zradius;
408     min_h = h * zradius;
409     //max_w = w;
410     //max_h = h;
411     min_x1 = center_x - min_w / 2;
412         min_y1 = center_y - min_h / 2;
413         min_x2 = center_x + min_w / 2;
414         min_y2 = center_y + min_h / 2;
415         max_x1 = 0;
416         max_y1 = 0;
417         max_x2 = w;
418         max_y2 = h;
419                 
420         delete_tables();
421         scale_x_table = new int*[config.steps];
422         scale_y_table = new int*[config.steps];
423         table_entries = config.steps;
424
425         for(int i = 0; i < config.steps; i++)
426         {
427                 float fraction = (float)(i - config.steps / 2) / config.steps;
428                 float inv_fraction = 1.0 - fraction;
429                 
430                 int x = (int)(fraction * x_offset);
431                 int y = (int)(fraction * y_offset);
432                 float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
433                 float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
434                 float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
435                 float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
436                 float out_w = out_x2 - out_x1;
437                 float out_h = out_y2 - out_y1;
438                 if(out_w < 0) out_w = 0;
439                 if(out_h < 0) out_h = 0;
440                 float scale_x = (float)w / out_w;
441                 float scale_y = (float)h / out_h;
442
443                 int *x_table;
444                 int *y_table;
445                 scale_y_table[i] = y_table = new int[(int)(h + 1)];
446                 scale_x_table[i] = x_table = new int[(int)(w + 1)];
447                         
448                 for(int j = 0; j < h; j++)
449                 {
450                         y_table[j] = (int)((j - out_y1) * scale_y) + y;
451                 }
452                 for(int j = 0; j < w; j++)
453                 {
454                         x_table[j] = (int)((j - out_x1) * scale_x) + x;
455                 }
456         }
457
458         bzero(accum, 
459                 input_ptr->get_w() * 
460                 input_ptr->get_h() * 
461                 BC_CModels::components(input_ptr->get_color_model()) * 
462                 MAX(sizeof(int), sizeof(float)));
463         engine->process_packages();
464         return 0;
465 }
466
467
468 void MotionBlurMain::update_gui()
469 {
470         if(thread)
471         {
472                 load_configuration();
473                 thread->window->lock_window();
474                 ((MotionBlurWindow*)thread->window)->radius->update(config.radius);
475                 ((MotionBlurWindow*)thread->window)->steps->update(config.steps);
476                 thread->window->unlock_window();
477         }
478 }
479
480
481
482
483
484 void MotionBlurMain::save_data(KeyFrame *keyframe)
485 {
486         FileXML output;
487
488 // cause data to be stored directly in text
489         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
490         output.tag.set_title("MOTIONBLUR");
491
492         output.tag.set_property("RADIUS", config.radius);
493         output.tag.set_property("STEPS", config.steps);
494         output.append_tag();
495         output.terminate_string();
496 }
497
498 void MotionBlurMain::read_data(KeyFrame *keyframe)
499 {
500         FileXML input;
501
502         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
503
504         int result = 0;
505
506         while(!result)
507         {
508                 result = input.read_tag();
509
510                 if(!result)
511                 {
512                         if(input.tag.title_is("MOTIONBLUR"))
513                         {
514                                 config.radius = input.tag.get_property("RADIUS", config.radius);
515                                 config.steps = input.tag.get_property("STEPS", config.steps);
516                         }
517                 }
518         }
519 }
520
521
522
523
524
525
526 MotionBlurPackage::MotionBlurPackage()
527  : LoadPackage()
528 {
529 }
530
531
532
533
534 MotionBlurUnit::MotionBlurUnit(MotionBlurEngine *server, 
535         MotionBlurMain *plugin)
536  : LoadClient(server)
537 {
538         this->plugin = plugin;
539         this->server = server;
540 }
541
542
543 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP, MAX, DO_YUV) \
544 { \
545         const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
546         for(int j = pkg->y1; j < pkg->y2; j++) \
547         { \
548                 TEMP *out_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
549                 int in_y = y_table[j]; \
550  \
551 /* Blend image */ \
552                 if(in_y >= 0 && in_y < h) \
553                 { \
554                         TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
555                         for(int k = 0; k < w; k++) \
556                         { \
557                                 int in_x = x_table[k]; \
558 /* Blend pixel */ \
559                                 if(in_x >= 0 && in_x < w) \
560                                 { \
561                                         int in_offset = in_x * COMPONENTS; \
562                                         *out_row++ += in_row[in_offset]; \
563                                         if(DO_YUV) \
564                                         { \
565                                                 *out_row++ += in_row[in_offset + 1]; \
566                                                 *out_row++ += in_row[in_offset + 2]; \
567                                         } \
568                                         else \
569                                         { \
570                                                 *out_row++ += in_row[in_offset + 1]; \
571                                                 *out_row++ += in_row[in_offset + 2]; \
572                                         } \
573                                         if(COMPONENTS == 4) \
574                                                 *out_row++ += in_row[in_offset + 3]; \
575                                 } \
576 /* Blend nothing */ \
577                                 else \
578                                 { \
579                                         out_row++; \
580                                         if(DO_YUV) \
581                                         { \
582                                                 *out_row++ += chroma_offset; \
583                                                 *out_row++ += chroma_offset; \
584                                         } \
585                                         else \
586                                         { \
587                                                 out_row += 2; \
588                                         } \
589                                         if(COMPONENTS == 4) out_row++; \
590                                 } \
591                         } \
592                 } \
593                 else \
594                 if(DO_YUV) \
595                 { \
596                         for(int k = 0; k < w; k++) \
597                         { \
598                                 out_row++; \
599                                 *out_row++ += chroma_offset; \
600                                 *out_row++ += chroma_offset; \
601                                 if(COMPONENTS == 4) out_row++; \
602                         } \
603                 } \
604         } \
605  \
606 /* Copy to output */ \
607         if(i == plugin->config.steps - 1) \
608         { \
609                 for(int j = pkg->y1; j < pkg->y2; j++) \
610                 { \
611                         TEMP *in_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
612                         TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
613                         TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
614                         for(int k = 0; k < w; k++) \
615                         { \
616                                 *out_row++ = (*in_row++ * fraction) / 0x10000; \
617                                 in_backup++; \
618  \
619                                 if(DO_YUV) \
620                                 { \
621                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
622                                         in_backup++; \
623  \
624                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
625                                         in_backup++; \
626                                 } \
627                                 else \
628                                 { \
629                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
630                                         in_backup++; \
631                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
632                                         in_backup++; \
633                                 } \
634  \
635                                 if(COMPONENTS == 4) \
636                                 { \
637                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
638                                         in_backup++; \
639                                 } \
640                         } \
641                 } \
642         } \
643 }
644
645 void MotionBlurUnit::process_package(LoadPackage *package)
646 {
647         MotionBlurPackage *pkg = (MotionBlurPackage*)package;
648         int h = plugin->output->get_h();
649         int w = plugin->output->get_w();
650
651         int fraction = 0x10000 / plugin->config.steps;
652         for(int i = 0; i < plugin->config.steps; i++)
653         {
654                 int *x_table = plugin->scale_x_table[i];
655                 int *y_table = plugin->scale_y_table[i];
656
657                 switch(plugin->input->get_color_model())
658                 {
659                         case BC_RGB_FLOAT:
660                                 BLEND_LAYER(3, float, float, 1, 0)
661                                 break;
662                         case BC_RGB888:
663                                 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
664                                 break;
665                         case BC_RGBA_FLOAT:
666                                 BLEND_LAYER(4, float, float, 1, 0)
667                                 break;
668                         case BC_RGBA8888:
669                                 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
670                                 break;
671                         case BC_RGB161616:
672                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
673                                 break;
674                         case BC_RGBA16161616:
675                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
676                                 break;
677                         case BC_YUV888:
678                                 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
679                                 break;
680                         case BC_YUVA8888:
681                                 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
682                                 break;
683                         case BC_YUV161616:
684                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
685                                 break;
686                         case BC_YUVA16161616:
687                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
688                                 break;
689                 }
690         }
691 }
692
693
694
695
696
697
698 MotionBlurEngine::MotionBlurEngine(MotionBlurMain *plugin, 
699         int total_clients, 
700         int total_packages)
701  : LoadServer(total_clients, total_packages)
702 {
703         this->plugin = plugin;
704 }
705
706 void MotionBlurEngine::init_packages()
707 {
708         for(int i = 0; i < get_total_packages(); i++)
709         {
710                 MotionBlurPackage *package = (MotionBlurPackage*)get_package(i);
711                 package->y1 = plugin->output->get_h() * i / get_total_packages();
712                 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
713         }
714 }
715
716 LoadClient* MotionBlurEngine::new_client()
717 {
718         return new MotionBlurUnit(this, plugin);
719 }
720
721 LoadPackage* MotionBlurEngine::new_package()
722 {
723         return new MotionBlurPackage;
724 }
725
726
727
728
729