r105: This commit was manufactured by cvs2svn to create tag
[cinelerra_cv/mob.git] / hvirtual / plugins / linearblur / linearblur.C
blobd052e171cef6b695c2453596336129d048e44825
1 #include <math.h>
2 #include <stdint.h>
3 #include <string.h>
5 #include "bcdisplayinfo.h"
6 #include "clip.h"
7 #include "defaults.h"
8 #include "filexml.h"
9 #include "keyframe.h"
10 #include "loadbalance.h"
11 #include "picon_png.h"
12 #include "pluginvclient.h"
13 #include "vframe.h"
15 #include <libintl.h>
16 #define _(String) gettext(String)
17 #define gettext_noop(String) String
18 #define N_(String) gettext_noop (String)
21 class LinearBlurMain;
22 class LinearBlurEngine;
28 class LinearBlurConfig
30 public:
31         LinearBlurConfig();
33         int equivalent(LinearBlurConfig &that);
34         void copy_from(LinearBlurConfig &that);
35         void interpolate(LinearBlurConfig &prev, 
36                 LinearBlurConfig &next, 
37                 long prev_frame, 
38                 long next_frame, 
39                 long current_frame);
41         int radius;
42         int steps;
43         int angle;
44         int r;
45         int g;
46         int b;
47         int a;
52 class LinearBlurSize : public BC_ISlider
54 public:
55         LinearBlurSize(LinearBlurMain *plugin, 
56                 int x, 
57                 int y, 
58                 int *output,
59                 int min,
60                 int max);
61         int handle_event();
62         LinearBlurMain *plugin;
63         int *output;
66 class LinearBlurToggle : public BC_CheckBox
68 public:
69         LinearBlurToggle(LinearBlurMain *plugin, 
70                 int x, 
71                 int y, 
72                 int *output,
73                 char *string);
74         int handle_event();
75         LinearBlurMain *plugin;
76         int *output;
79 class LinearBlurWindow : public BC_Window
81 public:
82         LinearBlurWindow(LinearBlurMain *plugin, int x, int y);
83         ~LinearBlurWindow();
85         int create_objects();
86         int close_event();
88         LinearBlurSize *angle, *steps, *radius;
89         LinearBlurToggle *r, *g, *b, *a;
90         LinearBlurMain *plugin;
95 PLUGIN_THREAD_HEADER(LinearBlurMain, LinearBlurThread, LinearBlurWindow)
98 class LinearBlurMain : public PluginVClient
100 public:
101         LinearBlurMain(PluginServer *server);
102         ~LinearBlurMain();
104         int process_realtime(VFrame *input_ptr, VFrame *output_ptr);
105         int is_realtime();
106         int load_defaults();
107         int save_defaults();
108         void save_data(KeyFrame *keyframe);
109         void read_data(KeyFrame *keyframe);
110         void update_gui();
112         PLUGIN_CLASS_MEMBERS(LinearBlurConfig, LinearBlurThread)
114         void delete_tables();
115         VFrame *input, *output, *temp;
116         LinearBlurEngine *engine;
117         int **scale_y_table;
118         int **scale_x_table;
119         int table_entries;
120         int need_reconfigure;
121         int *accum;
124 class LinearBlurPackage : public LoadPackage
126 public:
127         LinearBlurPackage();
128         int y1, y2;
131 class LinearBlurUnit : public LoadClient
133 public:
134         LinearBlurUnit(LinearBlurEngine *server, LinearBlurMain *plugin);
135         void process_package(LoadPackage *package);
136         LinearBlurEngine *server;
137         LinearBlurMain *plugin;
140 class LinearBlurEngine : public LoadServer
142 public:
143         LinearBlurEngine(LinearBlurMain *plugin, 
144                 int total_clients, 
145                 int total_packages);
146         void init_packages();
147         LoadClient* new_client();
148         LoadPackage* new_package();
149         LinearBlurMain *plugin;
170 REGISTER_PLUGIN(LinearBlurMain)
174 LinearBlurConfig::LinearBlurConfig()
176         radius = 10;
177         angle = 0;
178         steps = 10;
179         r = 1;
180         g = 1;
181         b = 1;
182         a = 1;
185 int LinearBlurConfig::equivalent(LinearBlurConfig &that)
187         return 
188                 radius == that.radius &&
189                 angle == that.angle &&
190                 steps == that.steps &&
191                 r == that.r &&
192                 g == that.g &&
193                 b == that.b &&
194                 a == that.a;
197 void LinearBlurConfig::copy_from(LinearBlurConfig &that)
199         radius = that.radius;
200         angle = that.angle;
201         steps = that.steps;
202         r = that.r;
203         g = that.g;
204         b = that.b;
205         a = that.a;
208 void LinearBlurConfig::interpolate(LinearBlurConfig &prev, 
209         LinearBlurConfig &next, 
210         long prev_frame, 
211         long next_frame, 
212         long current_frame)
214         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
215         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
216         this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
217         this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale + 0.5);
218         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
219         r = prev.r;
220         g = prev.g;
221         b = prev.b;
222         a = prev.a;
233 PLUGIN_THREAD_OBJECT(LinearBlurMain, LinearBlurThread, LinearBlurWindow)
237 LinearBlurWindow::LinearBlurWindow(LinearBlurMain *plugin, int x, int y)
238  : BC_Window(plugin->gui_string, 
239         x,
240         y,
241         230, 
242         290, 
243         230, 
244         290, 
245         0, 
246         1)
248         this->plugin = plugin; 
251 LinearBlurWindow::~LinearBlurWindow()
255 int LinearBlurWindow::create_objects()
257         int x = 10, y = 10;
259         add_subwindow(new BC_Title(x, y, _("Length:")));
260         y += 20;
261         add_subwindow(radius = new LinearBlurSize(plugin, x, y, &plugin->config.radius, 0, 100));
262         y += 30;
263         add_subwindow(new BC_Title(x, y, _("Angle:")));
264         y += 20;
265         add_subwindow(angle = new LinearBlurSize(plugin, x, y, &plugin->config.angle, -180, 180));
266         y += 30;
267         add_subwindow(new BC_Title(x, y, _("Steps:")));
268         y += 20;
269         add_subwindow(steps = new LinearBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
270         y += 30;
271         add_subwindow(r = new LinearBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
272         y += 30;
273         add_subwindow(g = new LinearBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
274         y += 30;
275         add_subwindow(b = new LinearBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
276         y += 30;
277         add_subwindow(a = new LinearBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
278         y += 30;
280         show_window();
281         flush();
282         return 0;
285 int LinearBlurWindow::close_event()
287 // Set result to 1 to indicate a plugin side close
288         set_done(1);
289         return 1;
301 LinearBlurToggle::LinearBlurToggle(LinearBlurMain *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 LinearBlurToggle::handle_event()
314         *output = get_value();
315         plugin->send_configure_change();
316         return 1;
325 LinearBlurSize::LinearBlurSize(LinearBlurMain *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 LinearBlurSize::handle_event()
338         *output = get_value();
339         plugin->send_configure_change();
340         return 1;
352 LinearBlurMain::LinearBlurMain(PluginServer *server)
353  : PluginVClient(server)
355         PLUGIN_CONSTRUCTOR_MACRO
356         engine = 0;
357         scale_x_table = 0;
358         scale_y_table = 0;
359         table_entries = 0;
360         accum = 0;
361         need_reconfigure = 1;
362         temp = 0;
365 LinearBlurMain::~LinearBlurMain()
367         PLUGIN_DESTRUCTOR_MACRO
368         if(engine) delete engine;
369         delete_tables();
370         if(accum) delete [] accum;
371         if(temp) delete temp;
374 char* LinearBlurMain::plugin_title() { return _("Linear Blur"); }
375 int LinearBlurMain::is_realtime() { return 1; }
378 NEW_PICON_MACRO(LinearBlurMain)
380 SHOW_GUI_MACRO(LinearBlurMain, LinearBlurThread)
382 SET_STRING_MACRO(LinearBlurMain)
384 RAISE_WINDOW_MACRO(LinearBlurMain)
386 LOAD_CONFIGURATION_MACRO(LinearBlurMain, LinearBlurConfig)
388 void LinearBlurMain::delete_tables()
390         if(scale_x_table)
391         {
392                 for(int i = 0; i < table_entries; i++)
393                         delete [] scale_x_table[i];
394                 delete [] scale_x_table;
395         }
397         if(scale_y_table)
398         {
399                 for(int i = 0; i < table_entries; i++)
400                         delete [] scale_y_table[i];
401                 delete [] scale_y_table;
402         }
403         scale_x_table = 0;
404         scale_y_table = 0;
405         table_entries = 0;
408 int LinearBlurMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
410         need_reconfigure |= load_configuration();
412 //printf("LinearBlurMain::process_realtime 1 %d\n", config.radius);
413         if(!engine) engine = new LinearBlurEngine(this,
414                 get_project_smp() + 1,
415                 get_project_smp() + 1);
416         if(!accum) accum = new int[input_ptr->get_w() * 
417                 input_ptr->get_h() *
418                 cmodel_components(input_ptr->get_color_model())];
420         this->input = input_ptr;
421         this->output = output_ptr;
424         if(input_ptr->get_rows()[0] == output_ptr->get_rows()[0])
425         {
426                 if(!temp) temp = new VFrame(0,
427                         input_ptr->get_w(),
428                         input_ptr->get_h(),
429                         input_ptr->get_color_model());
430                 temp->copy_from(input_ptr);
431                 this->input = temp;
432         }
434 // Generate tables here.  The same table is used by many packages to render
435 // each horizontal stripe.  Need to cover the entire output range in  each
436 // table to avoid green borders
437         if(need_reconfigure)
438         {
439                 int w = input->get_w();
440                 int h = input->get_h();
441                 int x_offset;
442                 int y_offset;
443                 int angle = config.angle;
444                 int radius = config.radius * MIN(w, h) / 100;
446                 while(angle < 0) angle += 360;
447                 switch(angle)
448                 {
449                         case 0:
450                         case 360:
451                                 x_offset = radius;
452                                 y_offset = 0;
453                                 break;
454                         case 90:
455                                 x_offset = 0;
456                                 y_offset = radius;
457                                 break;
458                         case 180:
459                                 x_offset = radius;
460                                 y_offset = 0;
461                                 break;
462                         case 270:
463                                 x_offset = 0;
464                                 y_offset = radius;
465                                 break;
466                         default:
467                                 y_offset = (int)(sin((float)config.angle / 360 * 2 * M_PI) * radius);
468                                 x_offset = (int)(cos((float)config.angle / 360 * 2 * M_PI) * radius);
469                                 break;
470                 }
473                 delete_tables();
474                 scale_x_table = new int*[config.steps];
475                 scale_y_table = new int*[config.steps];
476                 table_entries = config.steps;
478 //printf("LinearBlurMain::process_realtime 1 %d %d %d\n", radius, x_offset, y_offset);
480                 for(int i = 0; i < config.steps; i++)
481                 {
482                         float fraction = (float)(i - config.steps / 2) / config.steps;
483                         int x = (int)(fraction * x_offset);
484                         int y = (int)(fraction * y_offset);
486                         int *x_table;
487                         int *y_table;
488                         scale_y_table[i] = y_table = new int[h];
489                         scale_x_table[i] = x_table = new int[w];
491                         for(int j = 0; j < h; j++)
492                         {
493                                 y_table[j] = j + y;
494                         }
495                         for(int j = 0; j < w; j++)
496                         {
497                                 x_table[j] = j + x;
498                         }
499                 }
500                 need_reconfigure = 0;
501         }
503         bzero(accum, input_ptr->get_w() * 
504                 input_ptr->get_h() *
505                 cmodel_components(input_ptr->get_color_model()) *
506                 sizeof(int));
507         engine->process_packages();
508         return 0;
512 void LinearBlurMain::update_gui()
514         if(thread)
515         {
516                 load_configuration();
517                 thread->window->lock_window();
518                 thread->window->radius->update(config.radius);
519                 thread->window->angle->update(config.angle);
520                 thread->window->steps->update(config.steps);
521                 thread->window->r->update(config.r);
522                 thread->window->g->update(config.g);
523                 thread->window->b->update(config.b);
524                 thread->window->a->update(config.a);
525                 thread->window->unlock_window();
526         }
530 int LinearBlurMain::load_defaults()
532         char directory[1024], string[1024];
533 // set the default directory
534         sprintf(directory, "%slinearblur.rc", BCASTDIR);
536 // load the defaults
537         defaults = new Defaults(directory);
538         defaults->load();
540         config.radius = defaults->get("RADIUS", config.radius);
541         config.angle = defaults->get("ANGLE", config.angle);
542         config.steps = defaults->get("STEPS", config.steps);
543         config.r = defaults->get("R", config.r);
544         config.g = defaults->get("G", config.g);
545         config.b = defaults->get("B", config.b);
546         config.a = defaults->get("A", config.a);
547         return 0;
551 int LinearBlurMain::save_defaults()
553         defaults->update("RADIUS", config.radius);
554         defaults->update("ANGLE", config.angle);
555         defaults->update("STEPS", config.steps);
556         defaults->update("R", config.r);
557         defaults->update("G", config.g);
558         defaults->update("B", config.b);
559         defaults->update("A", config.a);
560         defaults->save();
561         return 0;
566 void LinearBlurMain::save_data(KeyFrame *keyframe)
568         FileXML output;
570 // cause data to be stored directly in text
571         output.set_shared_string(keyframe->data, MESSAGESIZE);
572         output.tag.set_title("LINEARBLUR");
574         output.tag.set_property("RADIUS", config.radius);
575         output.tag.set_property("ANGLE", config.angle);
576         output.tag.set_property("STEPS", config.steps);
577         output.tag.set_property("R", config.r);
578         output.tag.set_property("G", config.g);
579         output.tag.set_property("B", config.b);
580         output.tag.set_property("A", config.a);
581         output.append_tag();
582         output.terminate_string();
585 void LinearBlurMain::read_data(KeyFrame *keyframe)
587         FileXML input;
589         input.set_shared_string(keyframe->data, strlen(keyframe->data));
591         int result = 0;
593         while(!result)
594         {
595                 result = input.read_tag();
597                 if(!result)
598                 {
599                         if(input.tag.title_is("LINEARBLUR"))
600                         {
601                                 config.radius = input.tag.get_property("RADIUS", config.radius);
602                                 config.angle = input.tag.get_property("ANGLE", config.angle);
603                                 config.steps = input.tag.get_property("STEPS", config.steps);
604                                 config.r = input.tag.get_property("R", config.r);
605                                 config.g = input.tag.get_property("G", config.g);
606                                 config.b = input.tag.get_property("B", config.b);
607                                 config.a = input.tag.get_property("A", config.a);
608                         }
609                 }
610         }
618 LinearBlurPackage::LinearBlurPackage()
619  : LoadPackage()
626 LinearBlurUnit::LinearBlurUnit(LinearBlurEngine *server, 
627         LinearBlurMain *plugin)
628  : LoadClient(server)
630         this->plugin = plugin;
631         this->server = server;
635 #define BLEND_LAYER(COMPONENTS, TYPE, MAX, DO_YUV) \
636 { \
637         const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
638         for(int j = pkg->y1; j < pkg->y2; j++) \
639         { \
640                 int *out_row = plugin->accum + COMPONENTS * w * j; \
641                 int in_y = y_table[j]; \
643 /* Blend image */ \
644                 if(in_y >= 0 && in_y < h) \
645                 { \
646                         TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
647                         for(int k = 0; k < w; k++) \
648                         { \
649                                 int in_x = x_table[k]; \
650 /* Blend pixel */ \
651                                 if(in_x >= 0 && in_x < w) \
652                                 { \
653                                         int in_offset = in_x * COMPONENTS; \
654                                         *out_row++ += in_row[in_offset]; \
655                                         if(DO_YUV) \
656                                         { \
657                                                 *out_row++ += in_row[in_offset + 1]; \
658                                                 *out_row++ += in_row[in_offset + 2]; \
659                                         } \
660                                         else \
661                                         { \
662                                                 *out_row++ += (int)in_row[in_offset + 1]; \
663                                                 *out_row++ += (int)in_row[in_offset + 2]; \
664                                         } \
665                                         if(COMPONENTS == 4) \
666                                                 *out_row++ += in_row[in_offset + 3]; \
667                                 } \
668 /* Blend nothing */ \
669                                 else \
670                                 { \
671                                         out_row++; \
672                                         if(DO_YUV) \
673                                         { \
674                                                 *out_row++ += chroma_offset; \
675                                                 *out_row++ += chroma_offset; \
676                                         } \
677                                         else \
678                                         { \
679                                                 out_row += 2; \
680                                         } \
681                                         if(COMPONENTS == 4) out_row++; \
682                                 } \
683                         } \
684                 } \
685                 else \
686                 if(DO_YUV) \
687                 { \
688                         for(int k = 0; k < w; k++) \
689                         { \
690                                 out_row++; \
691                                 *out_row++ += chroma_offset; \
692                                 *out_row++ += chroma_offset; \
693                                 if(COMPONENTS == 4) out_row++; \
694                         } \
695                 } \
696         } \
698 /* Copy to output */ \
699         if(i == plugin->config.steps - 1) \
700         { \
701                 for(int j = pkg->y1; j < pkg->y2; j++) \
702                 { \
703                         int *in_row = plugin->accum + COMPONENTS * w * j; \
704                         TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
705                         TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
706                         for(int k = 0; k < w; k++) \
707                         { \
708                                 if(do_r) \
709                                 { \
710                                         *out_row++ = (*in_row++ * fraction) >> 16; \
711                                         in_backup++; \
712                                 } \
713                                 else \
714                                 { \
715                                         *out_row++ = *in_backup++; \
716                                         in_row++; \
717                                 } \
719                                 if(DO_YUV) \
720                                 { \
721                                         if(do_g) \
722                                         { \
723                                                 *out_row++ = ((*in_row++ * fraction) >> 16); \
724                                                 in_backup++; \
725                                         } \
726                                         else \
727                                         { \
728                                                 *out_row++ = *in_backup++; \
729                                                 in_row++; \
730                                         } \
732                                         if(do_b) \
733                                         { \
734                                                 *out_row++ = ((*in_row++ * fraction) >> 16); \
735                                                 in_backup++; \
736                                         } \
737                                         else \
738                                         { \
739                                                 *out_row++ = *in_backup++; \
740                                                 in_row++; \
741                                         } \
742                                 } \
743                                 else \
744                                 { \
745                                         if(do_g) \
746                                         { \
747                                                 *out_row++ = (*in_row++ * fraction) >> 16; \
748                                                 in_backup++; \
749                                         } \
750                                         else \
751                                         { \
752                                                 *out_row++ = *in_backup++; \
753                                                 in_row++; \
754                                         } \
756                                         if(do_b) \
757                                         { \
758                                                 *out_row++ = (*in_row++ * fraction) >> 16; \
759                                                 in_backup++; \
760                                         } \
761                                         else \
762                                         { \
763                                                 *out_row++ = *in_backup++; \
764                                                 in_row++; \
765                                         } \
766                                 } \
768                                 if(COMPONENTS == 4) \
769                                 { \
770                                         if(do_a) \
771                                         { \
772                                                 *out_row++ = (*in_row++ * fraction) >> 16; \
773                                                 in_backup++; \
774                                         } \
775                                         else \
776                                         { \
777                                                 *out_row++ = *in_backup++; \
778                                                 in_row++; \
779                                         } \
780                                 } \
781                         } \
782                 } \
783         } \
786 void LinearBlurUnit::process_package(LoadPackage *package)
788         LinearBlurPackage *pkg = (LinearBlurPackage*)package;
789         int h = plugin->output->get_h();
790         int w = plugin->output->get_w();
792         int fraction = 0x10000 / plugin->config.steps;
793         int do_r = plugin->config.r;
794         int do_g = plugin->config.g;
795         int do_b = plugin->config.b;
796         int do_a = plugin->config.a;
797         for(int i = 0; i < plugin->config.steps; i++)
798         {
799                 int *x_table = plugin->scale_x_table[i];
800                 int *y_table = plugin->scale_y_table[i];
802                 switch(plugin->input->get_color_model())
803                 {
804                         case BC_RGB888:
805                                 BLEND_LAYER(3, uint8_t, 0xff, 0)
806                                 break;
807                         case BC_RGBA8888:
808                                 BLEND_LAYER(4, uint8_t, 0xff, 0)
809                                 break;
810                         case BC_RGB161616:
811                                 BLEND_LAYER(3, uint16_t, 0xffff, 0)
812                                 break;
813                         case BC_RGBA16161616:
814                                 BLEND_LAYER(4, uint16_t, 0xffff, 0)
815                                 break;
816                         case BC_YUV888:
817                                 BLEND_LAYER(3, uint8_t, 0xff, 1)
818                                 break;
819                         case BC_YUVA8888:
820                                 BLEND_LAYER(4, uint8_t, 0xff, 1)
821                                 break;
822                         case BC_YUV161616:
823                                 BLEND_LAYER(3, uint16_t, 0xffff, 1)
824                                 break;
825                         case BC_YUVA16161616:
826                                 BLEND_LAYER(4, uint16_t, 0xffff, 1)
827                                 break;
828                 }
829         }
837 LinearBlurEngine::LinearBlurEngine(LinearBlurMain *plugin, 
838         int total_clients, 
839         int total_packages)
840  : LoadServer(total_clients, total_packages)
842         this->plugin = plugin;
845 void LinearBlurEngine::init_packages()
847         int package_h = (int)((float)plugin->output->get_h() / 
848                         total_packages + 1);
849         int y1 = 0;
850         for(int i = 0; i < total_packages; i++)
851         {
852                 LinearBlurPackage *package = (LinearBlurPackage*)packages[i];
853                 package->y1 = y1;
854                 package->y2 = y1 + package_h;
855                 package->y1 = MIN(plugin->output->get_h(), package->y1);
856                 package->y2 = MIN(plugin->output->get_h(), package->y2);
857                 y1 = package->y2;
858         }
861 LoadClient* LinearBlurEngine::new_client()
863         return new LinearBlurUnit(this, plugin);
866 LoadPackage* LinearBlurEngine::new_package()
868         return new LinearBlurPackage;