r864: Merge 2.1:
[cinelerra_cv/ct.git] / plugins / radialblur / radialblur.C
blob710fe63e381941857ff416dcf3786d2d72cf31b1
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 RadialBlurMain;
18 class RadialBlurEngine;
24 class RadialBlurConfig
26 public:
27         RadialBlurConfig();
29         int equivalent(RadialBlurConfig &that);
30         void copy_from(RadialBlurConfig &that);
31         void interpolate(RadialBlurConfig &prev, 
32                 RadialBlurConfig &next, 
33                 long prev_frame, 
34                 long next_frame, 
35                 long current_frame);
37         int x;
38         int y;
39         int steps;
40         int angle;
41         int r;
42         int g;
43         int b;
44         int a;
49 class RadialBlurSize : public BC_ISlider
51 public:
52         RadialBlurSize(RadialBlurMain *plugin, 
53                 int x, 
54                 int y, 
55                 int *output,
56                 int min,
57                 int max);
58         int handle_event();
59         RadialBlurMain *plugin;
60         int *output;
63 class RadialBlurToggle : public BC_CheckBox
65 public:
66         RadialBlurToggle(RadialBlurMain *plugin, 
67                 int x, 
68                 int y, 
69                 int *output,
70                 char *string);
71         int handle_event();
72         RadialBlurMain *plugin;
73         int *output;
76 class RadialBlurWindow : public BC_Window
78 public:
79         RadialBlurWindow(RadialBlurMain *plugin, int x, int y);
80         ~RadialBlurWindow();
82         int create_objects();
83         int close_event();
85         RadialBlurSize *x, *y, *steps, *angle;
86         RadialBlurToggle *r, *g, *b, *a;
87         RadialBlurMain *plugin;
92 PLUGIN_THREAD_HEADER(RadialBlurMain, RadialBlurThread, RadialBlurWindow)
95 class RadialBlurMain : public PluginVClient
97 public:
98         RadialBlurMain(PluginServer *server);
99         ~RadialBlurMain();
101         int process_buffer(VFrame *frame,
102                 int64_t start_position,
103                 double frame_rate);
104         int is_realtime();
105         int load_defaults();
106         int save_defaults();
107         void save_data(KeyFrame *keyframe);
108         void read_data(KeyFrame *keyframe);
109         void update_gui();
111         PLUGIN_CLASS_MEMBERS(RadialBlurConfig, RadialBlurThread)
113         VFrame *input, *output, *temp;
114         RadialBlurEngine *engine;
117 class RadialBlurPackage : public LoadPackage
119 public:
120         RadialBlurPackage();
121         int y1, y2;
124 class RadialBlurUnit : public LoadClient
126 public:
127         RadialBlurUnit(RadialBlurEngine *server, RadialBlurMain *plugin);
128         void process_package(LoadPackage *package);
129         RadialBlurEngine *server;
130         RadialBlurMain *plugin;
133 class RadialBlurEngine : public LoadServer
135 public:
136         RadialBlurEngine(RadialBlurMain *plugin, 
137                 int total_clients, 
138                 int total_packages);
139         void init_packages();
140         LoadClient* new_client();
141         LoadPackage* new_package();
142         RadialBlurMain *plugin;
163 REGISTER_PLUGIN(RadialBlurMain)
167 RadialBlurConfig::RadialBlurConfig()
169         x = 50;
170         y = 50;
171         steps = 10;
172         angle = 33;
173         r = 1;
174         g = 1;
175         b = 1;
176         a = 1;
179 int RadialBlurConfig::equivalent(RadialBlurConfig &that)
181         return 
182                 angle == that.angle &&
183                 x == that.x &&
184                 y == that.y &&
185                 steps == that.steps &&
186                 r == that.r &&
187                 g == that.g &&
188                 b == that.b &&
189                 a == that.a;
192 void RadialBlurConfig::copy_from(RadialBlurConfig &that)
194         x = that.x;
195         y = that.y;
196         angle = that.angle;
197         steps = that.steps;
198         r = that.r;
199         g = that.g;
200         b = that.b;
201         a = that.a;
204 void RadialBlurConfig::interpolate(RadialBlurConfig &prev, 
205         RadialBlurConfig &next, 
206         long prev_frame, 
207         long next_frame, 
208         long current_frame)
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->x = (int)(prev.x * prev_scale + next.x * next_scale + 0.5);
213         this->y = (int)(prev.y * prev_scale + next.y * next_scale + 0.5);
214         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
215         this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale + 0.5);
216         r = prev.r;
217         g = prev.g;
218         b = prev.b;
219         a = prev.a;
230 PLUGIN_THREAD_OBJECT(RadialBlurMain, RadialBlurThread, RadialBlurWindow)
234 RadialBlurWindow::RadialBlurWindow(RadialBlurMain *plugin, int x, int y)
235  : BC_Window(plugin->gui_string, 
236         x,
237         y,
238         230, 
239         340, 
240         230, 
241         340, 
242         0, 
243         1)
245         this->plugin = plugin; 
248 RadialBlurWindow::~RadialBlurWindow()
252 int RadialBlurWindow::create_objects()
254         int x = 10, y = 10;
256         add_subwindow(new BC_Title(x, y, _("X:")));
257         y += 20;
258         add_subwindow(this->x = new RadialBlurSize(plugin, x, y, &plugin->config.x, 0, 100));
259         y += 30;
260         add_subwindow(new BC_Title(x, y, _("Y:")));
261         y += 20;
262         add_subwindow(this->y = new RadialBlurSize(plugin, x, y, &plugin->config.y, 0, 100));
263         y += 30;
264         add_subwindow(new BC_Title(x, y, _("Angle:")));
265         y += 20;
266         add_subwindow(angle = new RadialBlurSize(plugin, x, y, &plugin->config.angle, 0, 360));
267         y += 30;
268         add_subwindow(new BC_Title(x, y, _("Steps:")));
269         y += 20;
270         add_subwindow(steps = new RadialBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
271         y += 30;
272         add_subwindow(r = new RadialBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
273         y += 30;
274         add_subwindow(g = new RadialBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
275         y += 30;
276         add_subwindow(b = new RadialBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
277         y += 30;
278         add_subwindow(a = new RadialBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
279         y += 30;
281         show_window();
282         flush();
283         return 0;
286 WINDOW_CLOSE_EVENT(RadialBlurWindow)
296 RadialBlurToggle::RadialBlurToggle(RadialBlurMain *plugin, 
297         int x, 
298         int y, 
299         int *output, 
300         char *string)
301  : BC_CheckBox(x, y, *output, string)
303         this->plugin = plugin;
304         this->output = output;
307 int RadialBlurToggle::handle_event()
309         *output = get_value();
310         plugin->send_configure_change();
311         return 1;
320 RadialBlurSize::RadialBlurSize(RadialBlurMain *plugin, 
321         int x, 
322         int y, 
323         int *output,
324         int min,
325         int max)
326  : BC_ISlider(x, y, 0, 200, 200, min, max, *output)
328         this->plugin = plugin;
329         this->output = output;
331 int RadialBlurSize::handle_event()
333         *output = get_value();
334         plugin->send_configure_change();
335         return 1;
347 RadialBlurMain::RadialBlurMain(PluginServer *server)
348  : PluginVClient(server)
350         PLUGIN_CONSTRUCTOR_MACRO
351         engine = 0;
352         temp = 0;
355 RadialBlurMain::~RadialBlurMain()
357         PLUGIN_DESTRUCTOR_MACRO
358         if(engine) delete engine;
359         if(temp) delete temp;
362 char* RadialBlurMain::plugin_title() { return N_("Radial Blur"); }
363 int RadialBlurMain::is_realtime() { return 1; }
366 NEW_PICON_MACRO(RadialBlurMain)
368 SHOW_GUI_MACRO(RadialBlurMain, RadialBlurThread)
370 SET_STRING_MACRO(RadialBlurMain)
372 RAISE_WINDOW_MACRO(RadialBlurMain)
374 LOAD_CONFIGURATION_MACRO(RadialBlurMain, RadialBlurConfig)
376 int RadialBlurMain::process_buffer(VFrame *frame,
377                                                         int64_t start_position,
378                                                         double frame_rate)
380         load_configuration();
383         read_frame(frame,
384                 0,
385                 get_source_position(),
386                 get_framerate());
388         if(!engine) engine = new RadialBlurEngine(this,
389                 get_project_smp() + 1,
390                 get_project_smp() + 1);
392         this->input = frame;
393         this->output = frame;
396         if(!temp) temp = new VFrame(0,
397                 frame->get_w(),
398                 frame->get_h(),
399                 frame->get_color_model());
400         temp->copy_from(frame);
401         this->input = temp;
403         engine->process_packages();
404         return 0;
408 void RadialBlurMain::update_gui()
410         if(thread)
411         {
412                 load_configuration();
413                 thread->window->lock_window();
414                 thread->window->x->update(config.x);
415                 thread->window->y->update(config.y);
416                 thread->window->angle->update(config.angle);
417                 thread->window->steps->update(config.steps);
418                 thread->window->r->update(config.r);
419                 thread->window->g->update(config.g);
420                 thread->window->b->update(config.b);
421                 thread->window->a->update(config.a);
422                 thread->window->unlock_window();
423         }
427 int RadialBlurMain::load_defaults()
429         char directory[1024], string[1024];
430 // set the default directory
431         sprintf(directory, "%sradialblur.rc", BCASTDIR);
433 // load the defaults
434         defaults = new BC_Hash(directory);
435         defaults->load();
437         config.x = defaults->get("X", config.x);
438         config.y = defaults->get("Y", config.y);
439         config.angle = defaults->get("ANGLE", config.angle);
440         config.steps = defaults->get("STEPS", config.steps);
441         config.r = defaults->get("R", config.r);
442         config.g = defaults->get("G", config.g);
443         config.b = defaults->get("B", config.b);
444         config.a = defaults->get("A", config.a);
445         return 0;
449 int RadialBlurMain::save_defaults()
451         defaults->update("X", config.x);
452         defaults->update("Y", config.y);
453         defaults->update("ANGLE", config.angle);
454         defaults->update("STEPS", config.steps);
455         defaults->update("R", config.r);
456         defaults->update("G", config.g);
457         defaults->update("B", config.b);
458         defaults->update("A", config.a);
459         defaults->save();
460         return 0;
465 void RadialBlurMain::save_data(KeyFrame *keyframe)
467         FileXML output;
469 // cause data to be stored directly in text
470         output.set_shared_string(keyframe->data, MESSAGESIZE);
471         output.tag.set_title("RADIALBLUR");
473         output.tag.set_property("X", config.x);
474         output.tag.set_property("Y", config.y);
475         output.tag.set_property("ANGLE", config.angle);
476         output.tag.set_property("STEPS", config.steps);
477         output.tag.set_property("R", config.r);
478         output.tag.set_property("G", config.g);
479         output.tag.set_property("B", config.b);
480         output.tag.set_property("A", config.a);
481         output.append_tag();
482         output.terminate_string();
485 void RadialBlurMain::read_data(KeyFrame *keyframe)
487         FileXML input;
489         input.set_shared_string(keyframe->data, strlen(keyframe->data));
491         int result = 0;
493         while(!result)
494         {
495                 result = input.read_tag();
497                 if(!result)
498                 {
499                         if(input.tag.title_is("RADIALBLUR"))
500                         {
501                                 config.x = input.tag.get_property("X", config.x);
502                                 config.y = input.tag.get_property("Y", config.y);
503                                 config.angle = input.tag.get_property("ANGLE", config.angle);
504                                 config.steps = input.tag.get_property("STEPS", config.steps);
505                                 config.r = input.tag.get_property("R", config.r);
506                                 config.g = input.tag.get_property("G", config.g);
507                                 config.b = input.tag.get_property("B", config.b);
508                                 config.a = input.tag.get_property("A", config.a);
509                         }
510                 }
511         }
519 RadialBlurPackage::RadialBlurPackage()
520  : LoadPackage()
525 RadialBlurUnit::RadialBlurUnit(RadialBlurEngine *server, 
526         RadialBlurMain *plugin)
527  : LoadClient(server)
529         this->plugin = plugin;
530         this->server = server;
534 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP_TYPE, MAX, DO_YUV) \
535 { \
536         int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
537         TYPE **in_rows = (TYPE**)plugin->input->get_rows(); \
538         TYPE **out_rows = (TYPE**)plugin->output->get_rows(); \
539         int steps = plugin->config.steps; \
540         double step = (double)plugin->config.angle / 360 * 2 * M_PI / steps; \
542         for(int i = pkg->y1, out_y = pkg->y1 - center_y; \
543                 i < pkg->y2; \
544                 i++, out_y++) \
545         { \
546                 TYPE *out_row = out_rows[i]; \
547                 TYPE *in_row = in_rows[i]; \
548                 int y_square = out_y * out_y; \
550                 for(int j = 0, out_x = -center_x; j < w; j++, out_x++) \
551                 { \
552                         double offset = 0; \
553                         TEMP_TYPE accum_r = 0; \
554                         TEMP_TYPE accum_g = 0; \
555                         TEMP_TYPE accum_b = 0; \
556                         TEMP_TYPE accum_a = 0; \
558 /* Output coordinate to polar */ \
559                         double magnitude = sqrt(y_square + out_x * out_x); \
560                         double angle; \
561                         if(out_y < 0) \
562                                 angle = atan((double)out_x / out_y) + M_PI; \
563                         else \
564                         if(out_y > 0) \
565                                 angle = atan((double)out_x / out_y); \
566                         else \
567                         if(out_x > 0) \
568                                 angle = M_PI / 2; \
569                         else \
570                                 angle = M_PI * 1.5; \
572 /* Overlay all steps on this pixel*/ \
573                         angle -= (double)plugin->config.angle / 360 * M_PI; \
574                         for(int k = 0; k < steps; k++, angle += step) \
575                         { \
576 /* Polar to input coordinate */ \
577                                 int in_x = (int)(magnitude * sin(angle)) + center_x; \
578                                 int in_y = (int)(magnitude * cos(angle)) + center_y; \
580 /* Accumulate input coordinate */ \
581                                 if(in_x >= 0 && in_x < w && in_y >= 0 && in_y < h) \
582                                 { \
583                                         accum_r += in_rows[in_y][in_x * COMPONENTS]; \
584                                         if(DO_YUV) \
585                                         { \
586                                                 accum_g += (int)in_rows[in_y][in_x * COMPONENTS + 1]; \
587                                                 accum_b += (int)in_rows[in_y][in_x * COMPONENTS + 2]; \
588                                         } \
589                                         else \
590                                         { \
591                                                 accum_g += in_rows[in_y][in_x * COMPONENTS + 1]; \
592                                                 accum_b += in_rows[in_y][in_x * COMPONENTS + 2]; \
593                                         } \
594                                         if(COMPONENTS == 4) \
595                                                 accum_a += in_rows[in_y][in_x * COMPONENTS + 3]; \
596                                 } \
597                                 else \
598                                 { \
599                                         accum_g += chroma_offset; \
600                                         accum_b += chroma_offset; \
601                                 } \
602                         } \
604 /* Accumulation to output */ \
605                         if(do_r) \
606                         { \
607                                 *out_row++ = (accum_r * fraction) / 0x10000; \
608                                 in_row++; \
609                         } \
610                         else \
611                         { \
612                                 *out_row++ = *in_row++; \
613                         } \
615                         if(do_g) \
616                         { \
617                                 if(DO_YUV) \
618                                         *out_row++ = ((accum_g * fraction) / 0x10000); \
619                                 else \
620                                         *out_row++ = (accum_g * fraction) / 0x10000; \
621                                 in_row++; \
622                         } \
623                         else \
624                         { \
625                                 *out_row++ = *in_row++; \
626                         } \
628                         if(do_b) \
629                         { \
630                                 if(DO_YUV) \
631                                         *out_row++ = (accum_b * fraction) / 0x10000; \
632                                 else \
633                                         *out_row++ = (accum_b * fraction) / 0x10000; \
634                                 in_row++; \
635                         } \
636                         else \
637                         { \
638                                 *out_row++ = *in_row++; \
639                         } \
641                         if(COMPONENTS == 4) \
642                         { \
643                                 if(do_a) \
644                                 { \
645                                         *out_row++ = (accum_a * fraction) / 0x10000; \
646                                         in_row++; \
647                                 } \
648                                 else \
649                                 { \
650                                         *out_row++ = *in_row++; \
651                                 } \
652                         } \
653                 } \
654         } \
657 void RadialBlurUnit::process_package(LoadPackage *package)
659         RadialBlurPackage *pkg = (RadialBlurPackage*)package;
660         int h = plugin->output->get_h();
661         int w = plugin->output->get_w();
662         int do_r = plugin->config.r;
663         int do_g = plugin->config.g;
664         int do_b = plugin->config.b;
665         int do_a = plugin->config.a;
666         int fraction = 0x10000 / plugin->config.steps;
667         int center_x = plugin->config.x * w / 100;
668         int center_y = plugin->config.y * h / 100;
670         switch(plugin->input->get_color_model())
671         {
672                 case BC_RGB888:
673                         BLEND_LAYER(3, uint8_t, int, 0xff, 0)
674                         break;
675                 case BC_RGBA8888:
676                         BLEND_LAYER(4, uint8_t, int, 0xff, 0)
677                         break;
678                 case BC_RGB_FLOAT:
679                         BLEND_LAYER(3, float, float, 1, 0)
680                         break;
681                 case BC_RGBA_FLOAT:
682                         BLEND_LAYER(4, float, float, 1, 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         }
710 RadialBlurEngine::RadialBlurEngine(RadialBlurMain *plugin, 
711         int total_clients, 
712         int total_packages)
713  : LoadServer(total_clients, total_packages)
714 // : LoadServer(1, 1)
716         this->plugin = plugin;
719 void RadialBlurEngine::init_packages()
721         for(int i = 0; i < get_total_packages(); i++)
722         {
723                 RadialBlurPackage *package = (RadialBlurPackage*)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* RadialBlurEngine::new_client()
731         return new RadialBlurUnit(this, plugin);
734 LoadPackage* RadialBlurEngine::new_package()
736         return new RadialBlurPackage;