Merge branch 'xml-cleanup' of git://git.pipapo.org/cinelerra/ichthyo into ct
[cinelerra_cv/ct.git] / plugins / radialblur / radialblur.C
blobb4c87ada41c79b07059788ac940f066cc4f3cf40
1 #include <math.h>
2 #include <stdint.h>
3 #include <string.h>
6 #include "affine.h"
7 #include "bcdisplayinfo.h"
8 #include "clip.h"
9 #include "bchash.h"
10 #include "filexml.h"
11 #include "keyframe.h"
12 #include "language.h"
13 #include "loadbalance.h"
14 #include "picon_png.h"
15 #include "pluginvclient.h"
16 #include "vframe.h"
19 class RadialBlurMain;
20 class RadialBlurEngine;
26 class RadialBlurConfig
28 public:
29         RadialBlurConfig();
31         int equivalent(RadialBlurConfig &that);
32         void copy_from(RadialBlurConfig &that);
33         void interpolate(RadialBlurConfig &prev, 
34                 RadialBlurConfig &next, 
35                 long prev_frame, 
36                 long next_frame, 
37                 long current_frame);
39         int x;
40         int y;
41         int steps;
42         int angle;
43         int r;
44         int g;
45         int b;
46         int a;
51 class RadialBlurSize : public BC_ISlider
53 public:
54         RadialBlurSize(RadialBlurMain *plugin, 
55                 int x, 
56                 int y, 
57                 int *output,
58                 int min,
59                 int max);
60         int handle_event();
61         RadialBlurMain *plugin;
62         int *output;
65 class RadialBlurToggle : public BC_CheckBox
67 public:
68         RadialBlurToggle(RadialBlurMain *plugin, 
69                 int x, 
70                 int y, 
71                 int *output,
72                 char *string);
73         int handle_event();
74         RadialBlurMain *plugin;
75         int *output;
78 class RadialBlurWindow : public BC_Window
80 public:
81         RadialBlurWindow(RadialBlurMain *plugin, int x, int y);
82         ~RadialBlurWindow();
84         int create_objects();
85         int close_event();
87         RadialBlurSize *x, *y, *steps, *angle;
88         RadialBlurToggle *r, *g, *b, *a;
89         RadialBlurMain *plugin;
94 PLUGIN_THREAD_HEADER(RadialBlurMain, RadialBlurThread, RadialBlurWindow)
97 class RadialBlurMain : public PluginVClient
99 public:
100         RadialBlurMain(PluginServer *server);
101         ~RadialBlurMain();
103         int process_buffer(VFrame *frame,
104                 int64_t start_position,
105                 double frame_rate);
106         int is_realtime();
107         int load_defaults();
108         int save_defaults();
109         void save_data(KeyFrame *keyframe);
110         void read_data(KeyFrame *keyframe);
111         void update_gui();
112         int handle_opengl();
114         PLUGIN_CLASS_MEMBERS(RadialBlurConfig, RadialBlurThread)
116         VFrame *input, *output, *temp;
117         RadialBlurEngine *engine;
118 // Rotate engine only used for OpenGL
119         AffineEngine *rotate;
122 class RadialBlurPackage : public LoadPackage
124 public:
125         RadialBlurPackage();
126         int y1, y2;
129 class RadialBlurUnit : public LoadClient
131 public:
132         RadialBlurUnit(RadialBlurEngine *server, RadialBlurMain *plugin);
133         void process_package(LoadPackage *package);
134         RadialBlurEngine *server;
135         RadialBlurMain *plugin;
138 class RadialBlurEngine : public LoadServer
140 public:
141         RadialBlurEngine(RadialBlurMain *plugin, 
142                 int total_clients, 
143                 int total_packages);
144         void init_packages();
145         LoadClient* new_client();
146         LoadPackage* new_package();
147         RadialBlurMain *plugin;
168 REGISTER_PLUGIN(RadialBlurMain)
172 RadialBlurConfig::RadialBlurConfig()
174         x = 50;
175         y = 50;
176         steps = 10;
177         angle = 33;
178         r = 1;
179         g = 1;
180         b = 1;
181         a = 1;
184 int RadialBlurConfig::equivalent(RadialBlurConfig &that)
186         return 
187                 angle == that.angle &&
188                 x == that.x &&
189                 y == that.y &&
190                 steps == that.steps &&
191                 r == that.r &&
192                 g == that.g &&
193                 b == that.b &&
194                 a == that.a;
197 void RadialBlurConfig::copy_from(RadialBlurConfig &that)
199         x = that.x;
200         y = that.y;
201         angle = that.angle;
202         steps = that.steps;
203         r = that.r;
204         g = that.g;
205         b = that.b;
206         a = that.a;
209 void RadialBlurConfig::interpolate(RadialBlurConfig &prev, 
210         RadialBlurConfig &next, 
211         long prev_frame, 
212         long next_frame, 
213         long current_frame)
215         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
216         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
217         this->x = (int)(prev.x * prev_scale + next.x * next_scale + 0.5);
218         this->y = (int)(prev.y * prev_scale + next.y * next_scale + 0.5);
219         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
220         this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale + 0.5);
221         r = prev.r;
222         g = prev.g;
223         b = prev.b;
224         a = prev.a;
235 PLUGIN_THREAD_OBJECT(RadialBlurMain, RadialBlurThread, RadialBlurWindow)
239 RadialBlurWindow::RadialBlurWindow(RadialBlurMain *plugin, int x, int y)
240  : BC_Window(plugin->gui_string, 
241         x,
242         y,
243         230, 
244         340, 
245         230, 
246         340, 
247         0, 
248         1)
250         this->plugin = plugin; 
253 RadialBlurWindow::~RadialBlurWindow()
257 int RadialBlurWindow::create_objects()
259         int x = 10, y = 10;
261         add_subwindow(new BC_Title(x, y, _("X:")));
262         y += 20;
263         add_subwindow(this->x = new RadialBlurSize(plugin, x, y, &plugin->config.x, 0, 100));
264         y += 30;
265         add_subwindow(new BC_Title(x, y, _("Y:")));
266         y += 20;
267         add_subwindow(this->y = new RadialBlurSize(plugin, x, y, &plugin->config.y, 0, 100));
268         y += 30;
269         add_subwindow(new BC_Title(x, y, _("Angle:")));
270         y += 20;
271         add_subwindow(angle = new RadialBlurSize(plugin, x, y, &plugin->config.angle, 0, 360));
272         y += 30;
273         add_subwindow(new BC_Title(x, y, _("Steps:")));
274         y += 20;
275         add_subwindow(steps = new RadialBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
276         y += 30;
277         add_subwindow(r = new RadialBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
278         y += 30;
279         add_subwindow(g = new RadialBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
280         y += 30;
281         add_subwindow(b = new RadialBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
282         y += 30;
283         add_subwindow(a = new RadialBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
284         y += 30;
286         show_window();
287         flush();
288         return 0;
291 WINDOW_CLOSE_EVENT(RadialBlurWindow)
301 RadialBlurToggle::RadialBlurToggle(RadialBlurMain *plugin, 
302         int x, 
303         int y, 
304         int *output, 
305         char *string)
306  : BC_CheckBox(x, y, *output, string)
308         this->plugin = plugin;
309         this->output = output;
312 int RadialBlurToggle::handle_event()
314         *output = get_value();
315         plugin->send_configure_change();
316         return 1;
325 RadialBlurSize::RadialBlurSize(RadialBlurMain *plugin, 
326         int x, 
327         int y, 
328         int *output,
329         int min,
330         int max)
331  : BC_ISlider(x, y, 0, 200, 200, min, max, *output)
333         this->plugin = plugin;
334         this->output = output;
336 int RadialBlurSize::handle_event()
338         *output = get_value();
339         plugin->send_configure_change();
340         return 1;
352 RadialBlurMain::RadialBlurMain(PluginServer *server)
353  : PluginVClient(server)
355         PLUGIN_CONSTRUCTOR_MACRO
356         engine = 0;
357         temp = 0;
358         rotate = 0;
361 RadialBlurMain::~RadialBlurMain()
363         PLUGIN_DESTRUCTOR_MACRO
364         if(engine) delete engine;
365         if(temp) delete temp;
366         delete rotate;
369 char* RadialBlurMain::plugin_title() { return N_("Radial Blur"); }
370 int RadialBlurMain::is_realtime() { return 1; }
373 NEW_PICON_MACRO(RadialBlurMain)
375 SHOW_GUI_MACRO(RadialBlurMain, RadialBlurThread)
377 SET_STRING_MACRO(RadialBlurMain)
379 RAISE_WINDOW_MACRO(RadialBlurMain)
381 LOAD_CONFIGURATION_MACRO(RadialBlurMain, RadialBlurConfig)
383 int RadialBlurMain::process_buffer(VFrame *frame,
384                                                         int64_t start_position,
385                                                         double frame_rate)
387         load_configuration();
390         read_frame(frame,
391                 0,
392                 get_source_position(),
393                 get_framerate(),
394 //              0);
395                 get_use_opengl());
397         if(get_use_opengl()) return run_opengl();
399         if(!engine) engine = new RadialBlurEngine(this,
400                 get_project_smp() + 1,
401                 get_project_smp() + 1);
403         this->input = frame;
404         this->output = frame;
407         if(!temp) temp = new VFrame(0,
408                 frame->get_w(),
409                 frame->get_h(),
410                 frame->get_color_model());
411         temp->copy_from(frame);
412         this->input = temp;
414         engine->process_packages();
415         return 0;
419 void RadialBlurMain::update_gui()
421         if(thread)
422         {
423                 load_configuration();
424                 thread->window->lock_window();
425                 thread->window->x->update(config.x);
426                 thread->window->y->update(config.y);
427                 thread->window->angle->update(config.angle);
428                 thread->window->steps->update(config.steps);
429                 thread->window->r->update(config.r);
430                 thread->window->g->update(config.g);
431                 thread->window->b->update(config.b);
432                 thread->window->a->update(config.a);
433                 thread->window->unlock_window();
434         }
438 int RadialBlurMain::load_defaults()
440         char directory[1024], string[1024];
441 // set the default directory
442         sprintf(directory, "%sradialblur.rc", BCASTDIR);
444 // load the defaults
445         defaults = new BC_Hash(directory);
446         defaults->load();
448         config.x = defaults->get("X", config.x);
449         config.y = defaults->get("Y", config.y);
450         config.angle = defaults->get("ANGLE", config.angle);
451         config.steps = defaults->get("STEPS", config.steps);
452         config.r = defaults->get("R", config.r);
453         config.g = defaults->get("G", config.g);
454         config.b = defaults->get("B", config.b);
455         config.a = defaults->get("A", config.a);
456         return 0;
460 int RadialBlurMain::save_defaults()
462         defaults->update("X", config.x);
463         defaults->update("Y", config.y);
464         defaults->update("ANGLE", config.angle);
465         defaults->update("STEPS", config.steps);
466         defaults->update("R", config.r);
467         defaults->update("G", config.g);
468         defaults->update("B", config.b);
469         defaults->update("A", config.a);
470         defaults->save();
471         return 0;
476 void RadialBlurMain::save_data(KeyFrame *keyframe)
478         FileXML output;
480 // cause data to be stored directly in text
481         output.set_shared_string(keyframe->data, MESSAGESIZE);
482         output.tag.set_title("RADIALBLUR");
484         output.tag.set_property("X", config.x);
485         output.tag.set_property("Y", config.y);
486         output.tag.set_property("ANGLE", config.angle);
487         output.tag.set_property("STEPS", config.steps);
488         output.tag.set_property("R", config.r);
489         output.tag.set_property("G", config.g);
490         output.tag.set_property("B", config.b);
491         output.tag.set_property("A", config.a);
492         output.append_tag();
493         output.tag.set_title("/RADIALBLUR");
494         output.append_tag();
495         output.terminate_string();
498 void RadialBlurMain::read_data(KeyFrame *keyframe)
500         FileXML input;
502         input.set_shared_string(keyframe->data, strlen(keyframe->data));
504         int result = 0;
506         while(!result)
507         {
508                 result = input.read_tag();
510                 if(!result)
511                 {
512                         if(input.tag.title_is("RADIALBLUR"))
513                         {
514                                 config.x = input.tag.get_property("X", config.x);
515                                 config.y = input.tag.get_property("Y", config.y);
516                                 config.angle = input.tag.get_property("ANGLE", config.angle);
517                                 config.steps = input.tag.get_property("STEPS", config.steps);
518                                 config.r = input.tag.get_property("R", config.r);
519                                 config.g = input.tag.get_property("G", config.g);
520                                 config.b = input.tag.get_property("B", config.b);
521                                 config.a = input.tag.get_property("A", config.a);
522                         }
523                 }
524         }
527 int RadialBlurMain::handle_opengl()
529 #ifdef HAVE_GL
530         get_output()->to_texture();
531         get_output()->enable_opengl();
532         get_output()->init_screen();
533         get_output()->bind_texture(0);
536         int is_yuv = cmodel_is_yuv(get_output()->get_color_model());
537         glClearColor(0.0, 0.0, 0.0, 0.0);
538         glClear(GL_COLOR_BUFFER_BIT);
540 // Draw unselected channels
541         glEnable(GL_BLEND);
542         glBlendFunc(GL_ONE, GL_ONE);
543         glDrawBuffer(GL_BACK);
544         if(!config.r || !config.g || !config.b || !config.a)
545         {
546                 glColor4f(config.r ? 0 : 1, 
547                         config.g ? 0 : 1, 
548                         config.b ? 0 : 1, 
549                         config.a ? 0 : 1);
550                 get_output()->draw_texture();
551         }
552         glAccum(GL_LOAD, 1.0);
555 // Blur selected channels
556         float fraction = 1.0 / config.steps;
557         for(int i = 0; i < config.steps; i++)
558         {
559                 get_output()->set_opengl_state(VFrame::TEXTURE);
560                 glClear(GL_COLOR_BUFFER_BIT);
561                 glColor4f(config.r ? 1 : 0, 
562                         config.g ? 1 : 0, 
563                         config.b ? 1 : 0, 
564                         config.a ? 1 : 0);
566                 float w = get_output()->get_w();
567                 float h = get_output()->get_h();
571                 double current_angle = (double)config.angle *
572                         i / 
573                         config.steps - 
574                         (double)config.angle / 2;
576                 if(!rotate) rotate = new AffineEngine(PluginClient::smp + 1, 
577                         PluginClient::smp + 1);
578                 rotate->set_pivot((int)(config.x * w / 100),
579                         (int)(config.y * h / 100));
580                 rotate->set_opengl(1);
581                 rotate->rotate(get_output(),
582                         get_output(),
583                         current_angle);
585                 glAccum(GL_ACCUM, fraction);
586                 glEnable(GL_TEXTURE_2D);
587                 glColor4f(config.r ? 1 : 0, 
588                         config.g ? 1 : 0, 
589                         config.b ? 1 : 0, 
590                         config.a ? 1 : 0);
591         }
594         glDisable(GL_BLEND);
595         glReadBuffer(GL_BACK);
596         glDisable(GL_TEXTURE_2D);
597         glAccum(GL_RETURN, 1.0);
599         glColor4f(1, 1, 1, 1);
600         get_output()->set_opengl_state(VFrame::SCREEN);
601 #endif
614 RadialBlurPackage::RadialBlurPackage()
615  : LoadPackage()
620 RadialBlurUnit::RadialBlurUnit(RadialBlurEngine *server, 
621         RadialBlurMain *plugin)
622  : LoadClient(server)
624         this->plugin = plugin;
625         this->server = server;
629 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP_TYPE, MAX, DO_YUV) \
630 { \
631         int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
632         TYPE **in_rows = (TYPE**)plugin->input->get_rows(); \
633         TYPE **out_rows = (TYPE**)plugin->output->get_rows(); \
634         int steps = plugin->config.steps; \
635         double step = (double)plugin->config.angle / 360 * 2 * M_PI / steps; \
637         for(int i = pkg->y1, out_y = pkg->y1 - center_y; \
638                 i < pkg->y2; \
639                 i++, out_y++) \
640         { \
641                 TYPE *out_row = out_rows[i]; \
642                 TYPE *in_row = in_rows[i]; \
643                 int y_square = out_y * out_y; \
645                 for(int j = 0, out_x = -center_x; j < w; j++, out_x++) \
646                 { \
647                         double offset = 0; \
648                         TEMP_TYPE accum_r = 0; \
649                         TEMP_TYPE accum_g = 0; \
650                         TEMP_TYPE accum_b = 0; \
651                         TEMP_TYPE accum_a = 0; \
653 /* Output coordinate to polar */ \
654                         double magnitude = sqrt(y_square + out_x * out_x); \
655                         double angle; \
656                         if(out_y < 0) \
657                                 angle = atan((double)out_x / out_y) + M_PI; \
658                         else \
659                         if(out_y > 0) \
660                                 angle = atan((double)out_x / out_y); \
661                         else \
662                         if(out_x > 0) \
663                                 angle = M_PI / 2; \
664                         else \
665                                 angle = M_PI * 1.5; \
667 /* Overlay all steps on this pixel*/ \
668                         angle -= (double)plugin->config.angle / 360 * M_PI; \
669                         for(int k = 0; k < steps; k++, angle += step) \
670                         { \
671 /* Polar to input coordinate */ \
672                                 int in_x = (int)(magnitude * sin(angle)) + center_x; \
673                                 int in_y = (int)(magnitude * cos(angle)) + center_y; \
675 /* Accumulate input coordinate */ \
676                                 if(in_x >= 0 && in_x < w && in_y >= 0 && in_y < h) \
677                                 { \
678                                         accum_r += in_rows[in_y][in_x * COMPONENTS]; \
679                                         if(DO_YUV) \
680                                         { \
681                                                 accum_g += (int)in_rows[in_y][in_x * COMPONENTS + 1]; \
682                                                 accum_b += (int)in_rows[in_y][in_x * COMPONENTS + 2]; \
683                                         } \
684                                         else \
685                                         { \
686                                                 accum_g += in_rows[in_y][in_x * COMPONENTS + 1]; \
687                                                 accum_b += in_rows[in_y][in_x * COMPONENTS + 2]; \
688                                         } \
689                                         if(COMPONENTS == 4) \
690                                                 accum_a += in_rows[in_y][in_x * COMPONENTS + 3]; \
691                                 } \
692                                 else \
693                                 { \
694                                         accum_g += chroma_offset; \
695                                         accum_b += chroma_offset; \
696                                 } \
697                         } \
699 /* Accumulation to output */ \
700                         if(do_r) \
701                         { \
702                                 *out_row++ = (accum_r * fraction) / 0x10000; \
703                                 in_row++; \
704                         } \
705                         else \
706                         { \
707                                 *out_row++ = *in_row++; \
708                         } \
710                         if(do_g) \
711                         { \
712                                 if(DO_YUV) \
713                                         *out_row++ = ((accum_g * fraction) / 0x10000); \
714                                 else \
715                                         *out_row++ = (accum_g * fraction) / 0x10000; \
716                                 in_row++; \
717                         } \
718                         else \
719                         { \
720                                 *out_row++ = *in_row++; \
721                         } \
723                         if(do_b) \
724                         { \
725                                 if(DO_YUV) \
726                                         *out_row++ = (accum_b * fraction) / 0x10000; \
727                                 else \
728                                         *out_row++ = (accum_b * fraction) / 0x10000; \
729                                 in_row++; \
730                         } \
731                         else \
732                         { \
733                                 *out_row++ = *in_row++; \
734                         } \
736                         if(COMPONENTS == 4) \
737                         { \
738                                 if(do_a) \
739                                 { \
740                                         *out_row++ = (accum_a * fraction) / 0x10000; \
741                                         in_row++; \
742                                 } \
743                                 else \
744                                 { \
745                                         *out_row++ = *in_row++; \
746                                 } \
747                         } \
748                 } \
749         } \
752 void RadialBlurUnit::process_package(LoadPackage *package)
754         RadialBlurPackage *pkg = (RadialBlurPackage*)package;
755         int h = plugin->output->get_h();
756         int w = plugin->output->get_w();
757         int do_r = plugin->config.r;
758         int do_g = plugin->config.g;
759         int do_b = plugin->config.b;
760         int do_a = plugin->config.a;
761         int fraction = 0x10000 / plugin->config.steps;
762         int center_x = plugin->config.x * w / 100;
763         int center_y = plugin->config.y * h / 100;
765         switch(plugin->input->get_color_model())
766         {
767                 case BC_RGB888:
768                         BLEND_LAYER(3, uint8_t, int, 0xff, 0)
769                         break;
770                 case BC_RGBA8888:
771                         BLEND_LAYER(4, uint8_t, int, 0xff, 0)
772                         break;
773                 case BC_RGB_FLOAT:
774                         BLEND_LAYER(3, float, float, 1, 0)
775                         break;
776                 case BC_RGBA_FLOAT:
777                         BLEND_LAYER(4, float, float, 1, 0)
778                         break;
779                 case BC_RGB161616:
780                         BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
781                         break;
782                 case BC_RGBA16161616:
783                         BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
784                         break;
785                 case BC_YUV888:
786                         BLEND_LAYER(3, uint8_t, int, 0xff, 1)
787                         break;
788                 case BC_YUVA8888:
789                         BLEND_LAYER(4, uint8_t, int, 0xff, 1)
790                         break;
791                 case BC_YUV161616:
792                         BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
793                         break;
794                 case BC_YUVA16161616:
795                         BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
796                         break;
797         }
805 RadialBlurEngine::RadialBlurEngine(RadialBlurMain *plugin, 
806         int total_clients, 
807         int total_packages)
808  : LoadServer(total_clients, total_packages)
809 // : LoadServer(1, 1)
811         this->plugin = plugin;
814 void RadialBlurEngine::init_packages()
816         for(int i = 0; i < get_total_packages(); i++)
817         {
818                 RadialBlurPackage *package = (RadialBlurPackage*)get_package(i);
819                 package->y1 = plugin->output->get_h() * i / get_total_packages();
820                 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
821         }
824 LoadClient* RadialBlurEngine::new_client()
826         return new RadialBlurUnit(this, plugin);
829 LoadPackage* RadialBlurEngine::new_package()
831         return new RadialBlurPackage;