37df852f6c501484cd4fb9633b9a87ff482a9ead
[goodguy/history.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         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 _("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.tag.set_title("/MOTIONBLUR");
496         output.append_tag();
497         output.append_newline();
498         output.terminate_string();
499 }
500
501 void MotionBlurMain::read_data(KeyFrame *keyframe)
502 {
503         FileXML input;
504
505         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
506
507         int result = 0;
508
509         while(!result)
510         {
511                 result = input.read_tag();
512
513                 if(!result)
514                 {
515                         if(input.tag.title_is("MOTIONBLUR"))
516                         {
517                                 config.radius = input.tag.get_property("RADIUS", config.radius);
518                                 config.steps = input.tag.get_property("STEPS", config.steps);
519                         }
520                 }
521         }
522 }
523
524
525
526
527
528
529 MotionBlurPackage::MotionBlurPackage()
530  : LoadPackage()
531 {
532 }
533
534
535
536
537 MotionBlurUnit::MotionBlurUnit(MotionBlurEngine *server,
538         MotionBlurMain *plugin)
539  : LoadClient(server)
540 {
541         this->plugin = plugin;
542         this->server = server;
543 }
544
545
546 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP, MAX, DO_YUV) \
547 { \
548         const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
549         for(int j = pkg->y1; j < pkg->y2; j++) \
550         { \
551                 TEMP *out_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
552                 int in_y = y_table[j]; \
553  \
554 /* Blend image */ \
555                 if(in_y >= 0 && in_y < h) \
556                 { \
557                         TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
558                         for(int k = 0; k < w; k++) \
559                         { \
560                                 int in_x = x_table[k]; \
561 /* Blend pixel */ \
562                                 if(in_x >= 0 && in_x < w) \
563                                 { \
564                                         int in_offset = in_x * COMPONENTS; \
565                                         *out_row++ += in_row[in_offset]; \
566                                         if(DO_YUV) \
567                                         { \
568                                                 *out_row++ += in_row[in_offset + 1]; \
569                                                 *out_row++ += in_row[in_offset + 2]; \
570                                         } \
571                                         else \
572                                         { \
573                                                 *out_row++ += in_row[in_offset + 1]; \
574                                                 *out_row++ += in_row[in_offset + 2]; \
575                                         } \
576                                         if(COMPONENTS == 4) \
577                                                 *out_row++ += in_row[in_offset + 3]; \
578                                 } \
579 /* Blend nothing */ \
580                                 else \
581                                 { \
582                                         out_row++; \
583                                         if(DO_YUV) \
584                                         { \
585                                                 *out_row++ += chroma_offset; \
586                                                 *out_row++ += chroma_offset; \
587                                         } \
588                                         else \
589                                         { \
590                                                 out_row += 2; \
591                                         } \
592                                         if(COMPONENTS == 4) out_row++; \
593                                 } \
594                         } \
595                 } \
596                 else \
597                 if(DO_YUV) \
598                 { \
599                         for(int k = 0; k < w; k++) \
600                         { \
601                                 out_row++; \
602                                 *out_row++ += chroma_offset; \
603                                 *out_row++ += chroma_offset; \
604                                 if(COMPONENTS == 4) out_row++; \
605                         } \
606                 } \
607         } \
608  \
609 /* Copy to output */ \
610         if(i == plugin->config.steps - 1) \
611         { \
612                 for(int j = pkg->y1; j < pkg->y2; j++) \
613                 { \
614                         TEMP *in_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
615                         TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
616                         TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
617                         for(int k = 0; k < w; k++) \
618                         { \
619                                 *out_row++ = (*in_row++ * fraction) / 0x10000; \
620                                 in_backup++; \
621  \
622                                 if(DO_YUV) \
623                                 { \
624                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
625                                         in_backup++; \
626  \
627                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
628                                         in_backup++; \
629                                 } \
630                                 else \
631                                 { \
632                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
633                                         in_backup++; \
634                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
635                                         in_backup++; \
636                                 } \
637  \
638                                 if(COMPONENTS == 4) \
639                                 { \
640                                         *out_row++ = (*in_row++ * fraction) / 0x10000; \
641                                         in_backup++; \
642                                 } \
643                         } \
644                 } \
645         } \
646 }
647
648 void MotionBlurUnit::process_package(LoadPackage *package)
649 {
650         MotionBlurPackage *pkg = (MotionBlurPackage*)package;
651         int h = plugin->output->get_h();
652         int w = plugin->output->get_w();
653
654         int fraction = 0x10000 / plugin->config.steps;
655         for(int i = 0; i < plugin->config.steps; i++)
656         {
657                 int *x_table = plugin->scale_x_table[i];
658                 int *y_table = plugin->scale_y_table[i];
659
660                 switch(plugin->input->get_color_model())
661                 {
662                         case BC_RGB_FLOAT:
663                                 BLEND_LAYER(3, float, float, 1, 0)
664                                 break;
665                         case BC_RGB888:
666                                 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
667                                 break;
668                         case BC_RGBA_FLOAT:
669                                 BLEND_LAYER(4, float, float, 1, 0)
670                                 break;
671                         case BC_RGBA8888:
672                                 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
673                                 break;
674                         case BC_RGB161616:
675                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
676                                 break;
677                         case BC_RGBA16161616:
678                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
679                                 break;
680                         case BC_YUV888:
681                                 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
682                                 break;
683                         case BC_YUVA8888:
684                                 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
685                                 break;
686                         case BC_YUV161616:
687                                 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
688                                 break;
689                         case BC_YUVA16161616:
690                                 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
691                                 break;
692                 }
693         }
694 }
695
696
697
698
699
700
701 MotionBlurEngine::MotionBlurEngine(MotionBlurMain *plugin,
702         int total_clients,
703         int total_packages)
704  : LoadServer(total_clients, total_packages)
705 {
706         this->plugin = plugin;
707 }
708
709 void MotionBlurEngine::init_packages()
710 {
711         for(int i = 0; i < get_total_packages(); i++)
712         {
713                 MotionBlurPackage *package = (MotionBlurPackage*)get_package(i);
714                 package->y1 = plugin->output->get_h() * i / get_total_packages();
715                 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
716         }
717 }
718
719 LoadClient* MotionBlurEngine::new_client()
720 {
721         return new MotionBlurUnit(this, plugin);
722 }
723
724 LoadPackage* MotionBlurEngine::new_package()
725 {
726         return new MotionBlurPackage;
727 }
728
729
730
731
732