r125: This commit was manufactured by cvs2svn to create tag 'r1_1_7-last'.
[cinelerra_cv/mob.git] / hvirtual / plugins / whirl / whirl.C
blob6931feed8624857e530651c5f16f4eb052b63927
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "defaults.h"
4 #include "filexml.h"
5 #include "guicast.h"
6 #include "keyframe.h"
7 #include "loadbalance.h"
8 #include "picon_png.h"
9 #include "pluginvclient.h"
10 #include "vframe.h"
12 #include <libintl.h>
13 #define _(String) gettext(String)
14 #define gettext_noop(String) String
15 #define N_(String) gettext_noop (String)
19 #include <stdint.h>
20 #include <string.h>
22 class WhirlEffect;
23 class WhirlWindow;
24 class WhirlEngine;
26 #define MAXRADIUS 100
27 #define MAXPINCH 100
34 class WhirlConfig
36 public:
37         WhirlConfig();
38         
39         void copy_from(WhirlConfig &src);
40         int equivalent(WhirlConfig &src);
41         void interpolate(WhirlConfig &prev, 
42                 WhirlConfig &next, 
43                 long prev_frame, 
44                 long next_frame, 
45                 long current_frame);
46         
47         float angle;
48         float pinch;
49         float radius;
53 class WhirlAngle : public BC_FSlider
55 public:
56         WhirlAngle(WhirlEffect *plugin, int x, int y);
57         int handle_event();
58         WhirlEffect *plugin;
63 class WhirlPinch : public BC_FSlider
65 public:
66         WhirlPinch(WhirlEffect *plugin, int x, int y);
67         int handle_event();
68         WhirlEffect *plugin;
73 class WhirlRadius : public BC_FSlider
75 public:
76         WhirlRadius(WhirlEffect *plugin, int x, int y);
77         int handle_event();
78         WhirlEffect *plugin;
81 class WhirlWindow : public BC_Window
83 public:
84         WhirlWindow(WhirlEffect *plugin, int x, int y);
85         void create_objects();
86         int close_event();
87         WhirlEffect *plugin;
88         WhirlRadius *radius;
89         WhirlPinch *pinch;
90         WhirlAngle *angle;
94 PLUGIN_THREAD_HEADER(WhirlEffect, WhirlThread, WhirlWindow)
97 class WhirlPackage : public LoadPackage
99 public:
100         WhirlPackage();
101         int row1, row2;
104 class WhirlUnit : public LoadClient
106 public:
107         WhirlUnit(WhirlEffect *plugin, WhirlEngine *server);
108         void process_package(LoadPackage *package);
109         WhirlEngine *server;
110         WhirlEffect *plugin;
111         
115 class WhirlEngine : public LoadServer
117 public:
118         WhirlEngine(WhirlEffect *plugin, int cpus);
119         void init_packages();
120         LoadClient* new_client();
121         LoadPackage* new_package();
122         WhirlEffect *plugin;
127 class WhirlEffect : public PluginVClient
129 public:
130         WhirlEffect(PluginServer *server);
131         ~WhirlEffect();
133         int process_realtime(VFrame *input, VFrame *output);
134         int is_realtime();
135         char* plugin_title();
136         VFrame* new_picon();
137         int show_gui();
138         void raise_window();
139         void update_gui();
140         int set_string();
141         int load_configuration();
142         int load_defaults();
143         int save_defaults();
144         void save_data(KeyFrame *keyframe);
145         void read_data(KeyFrame *keyframe);
147         WhirlEngine *engine;
148         VFrame *temp_frame;
149         VFrame *input, *output;
150         WhirlConfig config;
151         Defaults *defaults;
152         WhirlThread *thread;
153         int need_reconfigure;
159 PLUGIN_THREAD_OBJECT(WhirlEffect, WhirlThread, WhirlWindow)
162 REGISTER_PLUGIN(WhirlEffect)
179 WhirlConfig::WhirlConfig()
181         angle = 0.0;
182         pinch = 0.0;
183         radius = 0.0;
186 void WhirlConfig::copy_from(WhirlConfig &src)
188         this->angle = src.angle;
189         this->pinch = src.pinch;
190         this->radius = src.radius;
193 int WhirlConfig::equivalent(WhirlConfig &src)
195         return EQUIV(this->angle, src.angle) &&
196                 EQUIV(this->pinch, src.pinch) &&
197                 EQUIV(this->radius, src.radius);
200 void WhirlConfig::interpolate(WhirlConfig &prev, 
201         WhirlConfig &next, 
202         long prev_frame, 
203         long next_frame, 
204         long current_frame)
206         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
207         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
209         this->angle = prev.angle * prev_scale + next.angle * next_scale;
210         this->pinch = prev.pinch * prev_scale + next.pinch * next_scale;
211         this->radius = prev.radius * prev_scale + next.radius * next_scale;
223 WhirlWindow::WhirlWindow(WhirlEffect *plugin, int x, int y)
224  : BC_Window(plugin->gui_string, 
225         x, 
226         y, 
227         220, 
228         200, 
229         220, 
230         200, 
231         0, 
232         0,
233         1)
235         this->plugin = plugin;
240 void WhirlWindow::create_objects()
242         int x = 10, y = 10;
243         add_subwindow(new BC_Title(x, y, _("Radius")));
244         y += 20;
245         add_subwindow(radius = new WhirlRadius(plugin, x, y));
246         y += 40;
247         add_subwindow(new BC_Title(x, y, _("Pinch")));
248         y += 20;
249         add_subwindow(pinch = new WhirlPinch(plugin, x, y));
250         y += 40;
251         add_subwindow(new BC_Title(x, y, _("Angle")));
252         y += 20;
253         add_subwindow(angle = new WhirlAngle(plugin, x, y));
255         show_window();
256         flush();
259 int WhirlWindow::close_event()
261         set_done(1);
262         return 1;
277 WhirlAngle::WhirlAngle(WhirlEffect *plugin, int x, int y)
278  : BC_FSlider(x, 
279                 y, 
280                 0,
281                 200,
282                 200, 
283                 (float)0, 
284                 (float)360,
285                 plugin->config.angle)
287         this->plugin = plugin;
289 int WhirlAngle::handle_event()
291         plugin->config.angle = get_value();
292         plugin->send_configure_change();
293         return 1;
299 WhirlPinch::WhirlPinch(WhirlEffect *plugin, int x, int y)
300  : BC_FSlider(x, 
301                 y, 
302                 0,
303                 200,
304                 200, 
305                 (float)0, 
306                 (float)MAXPINCH, 
307                 plugin->config.pinch)
309         this->plugin = plugin;
311 int WhirlPinch::handle_event()
313         plugin->config.pinch = get_value();
314         plugin->send_configure_change();
315         return 1;
321 WhirlRadius::WhirlRadius(WhirlEffect *plugin, int x, int y)
322  : BC_FSlider(x, 
323                 y, 
324                 0,
325                 200,
326                 200, 
327                 (float)0, 
328                 (float)MAXRADIUS, 
329                 plugin->config.radius)
331         this->plugin = plugin;
333 int WhirlRadius::handle_event()
335         plugin->config.radius = get_value();
336         plugin->send_configure_change();
337         return 1;
350 WhirlEffect::WhirlEffect(PluginServer *server)
351  : PluginVClient(server)
353         need_reconfigure = 1;
354         engine = 0;
355         temp_frame = 0;
356         PLUGIN_CONSTRUCTOR_MACRO
359 WhirlEffect::~WhirlEffect()
361         PLUGIN_DESTRUCTOR_MACRO
362         if(engine) delete engine;
363         if(temp_frame) delete temp_frame;
370 int WhirlEffect::is_realtime()
372         return 1;
375 char* WhirlEffect::plugin_title()
377         return _("Whirl");
380 NEW_PICON_MACRO(WhirlEffect)
382 SHOW_GUI_MACRO(WhirlEffect, WhirlThread)
384 RAISE_WINDOW_MACRO(WhirlEffect)
386 SET_STRING_MACRO(WhirlEffect)
388 void WhirlEffect::update_gui()
390         if(thread)
391         {
392                 load_configuration();
393                 thread->window->lock_window();
394                 thread->window->angle->update(config.angle);
395                 thread->window->pinch->update(config.pinch);
396                 thread->window->radius->update(config.radius);
397                 thread->window->unlock_window();
398         }
401 LOAD_CONFIGURATION_MACRO(WhirlEffect, WhirlConfig)
406 int WhirlEffect::load_defaults()
408         char directory[1024], string[1024];
409 // set the default directory
410         sprintf(directory, "%swhirl.rc", BCASTDIR);
412 // load the defaults
413         defaults = new Defaults(directory);
414         defaults->load();
416         config.angle = defaults->get("ANGLE", config.angle);
417         config.pinch = defaults->get("PINCH", config.pinch);
418         config.radius = defaults->get("RADIUS", config.radius);
421 int WhirlEffect::save_defaults()
423         defaults->update("ANGLE", config.angle);
424         defaults->update("PINCH", config.pinch);
425         defaults->update("RADIUS", config.radius);
426         defaults->save();
429 void WhirlEffect::save_data(KeyFrame *keyframe)
431         FileXML output;
433 // cause data to be stored directly in text
434         output.set_shared_string(keyframe->data, MESSAGESIZE);
436         output.tag.set_title("WHIRL");
437         output.tag.set_property("ANGLE", config.angle);
438         output.tag.set_property("PINCH", config.pinch);
439         output.tag.set_property("RADIUS", config.radius);
440         output.append_tag();
441         output.terminate_string();
442 // data is now in *text
445 void WhirlEffect::read_data(KeyFrame *keyframe)
447         FileXML input;
449         input.set_shared_string(keyframe->data, strlen(keyframe->data));
451         int result = 0;
453         while(!result)
454         {
455                 result = input.read_tag();
457                 if(!result)
458                 {
459                         if(input.tag.title_is("WHIRL"))
460                         {
461                                 config.angle = input.tag.get_property("ANGLE", config.angle);
462                                 config.pinch = input.tag.get_property("PINCH", config.pinch);
463                                 config.radius = input.tag.get_property("RADIUS", config.radius);
464                         }
465                 }
466         }
469 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
471         need_reconfigure |= load_configuration();
472         this->input = input;
473         this->output = output;
475         if(EQUIV(config.angle, 0) || EQUIV(config.radius, 0))
476         {
477                 if(input->get_rows()[0] != output->get_rows()[0])
478                         output->copy_from(input);
479         }
480         else
481         {
482                 if(input->get_rows()[0] == output->get_rows()[0])
483                 {
484                         if(!temp_frame) temp_frame = new VFrame(0,
485                                 input->get_w(),
486                                 input->get_h(),
487                                 input->get_color_model());
488                         temp_frame->copy_from(input);
489                         this->input = temp_frame;
490 //printf("WhirlEffect::process_realtime 1\n");
491                 }
493                 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
494                 
495                 engine->process_packages();
496         }
506 WhirlPackage::WhirlPackage()
507  : LoadPackage()
513 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
514  : LoadClient(server)
516         this->plugin = plugin;
521 static int calc_undistorted_coords(double cen_x,
522                         double cen_y,
523                         double scale_x,
524                         double scale_y,
525                         double radius,
526                         double radius2,
527                         double radius3,
528                         double pinch,
529                         double wx,
530                         double wy,
531                         double &whirl,
532                         double &x,
533                         double &y)
535         double dx, dy;
536         double d, factor;
537         double dist;
538         double ang, sina, cosa;
539         int inside;
541 /* Distances to center, scaled */
543         dx = (wx - cen_x) * scale_x;
544         dy = (wy - cen_y) * scale_y;
546 /* Distance^2 to center of *circle* (scaled ellipse) */
548         d = dx * dx + dy * dy;
550 /*  If we are inside circle, then distort.
551  *  Else, just return the same position
552  */
554         inside = (d < radius2);
556         if(inside)
557     {
558         dist = sqrt(d / radius3) / radius;
560 /* Pinch */
562         factor = pow(sin(M_PI / 2 * dist), -pinch);
564         dx *= factor;
565         dy *= factor;
567 /* Whirl */
569         factor = 1.0 - dist;
571         ang = whirl * factor * factor;
573         sina = sin(ang);
574         cosa = cos(ang);
576         x = (cosa * dx - sina * dy) / scale_x + cen_x;
577         y = (sina * dx + cosa * dy) / scale_y + cen_y;
578     }
580         return inside;
585 #define GET_PIXEL(components, x, y, input_rows) \
586         input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
593 static float bilinear(double x, double y, double *values)
595         double m0, m1;
596         x = fmod(x, 1.0);
597         y = fmod(y, 1.0);
599         if(x < 0.0) x += 1.0;
600         if(y < 0.0) y += 1.0;
602         m0 = (double)values[0] + x * ((double)values[1] - values[0]);
603         m1 = (double)values[2] + x * ((double)values[3] - values[2]);
604         return m0 + y * (m1 - m0);
611 #define WHIRL_MACRO(type, max, components) \
612 { \
613         type **input_rows = (type**)plugin->input->get_rows(); \
614         double values[components]; \
615         for(int row = pkg->row1; row <= (pkg->row2 + pkg->row1) / 2; row++) \
616         { \
617                 type *top_row = (type*)plugin->output->get_rows()[row]; \
618                 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
619                 type *top_p = top_row; \
620                 type *bot_p = bot_row + components * w - components; \
621                  \
622                 for(int col = 0; col < w; col++) \
623                 { \
624                         if(calc_undistorted_coords(cen_x, \
625                                 cen_y, \
626                                 scale_x, \
627                                 scale_y, \
628                                 radius, \
629                                 radius2, \
630                                 radius3, \
631                                 pinch, \
632                                 col, \
633                                 row, \
634                                 whirl, \
635                                 cx, \
636                                 cy)) \
637                         { \
638 /* Inside distortion area */ \
639 /* Do top */ \
640                                 if(cx >= 0.0) \
641                                         ix = (int)cx; \
642                                 else \
643                                         ix = -((int)-cx + 1); \
645                                 if(cy >= 0.0) \
646                                         iy = (int)cy; \
647                                 else \
648                                         iy = -((int)-cy + 1); \
650                                 type *pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
651                                 type *pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
652                                 type *pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
653                                 type *pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
655                                 values[0] = pixel1[0]; \
656                                 values[1] = pixel2[0]; \
657                                 values[2] = pixel3[0]; \
658                                 values[3] = pixel4[0]; \
659                                 top_p[0] = (type)bilinear(cx, cy, values); \
661                                 values[0] = pixel1[1]; \
662                                 values[1] = pixel2[1]; \
663                                 values[2] = pixel3[1]; \
664                                 values[3] = pixel4[1]; \
665                                 top_p[1] = (type)bilinear(cx, cy, values); \
667                                 values[0] = pixel1[2]; \
668                                 values[1] = pixel2[2]; \
669                                 values[2] = pixel3[2]; \
670                                 values[3] = pixel4[2]; \
671                                 top_p[2] = (type)bilinear(cx, cy, values); \
673                                 if(components == 4) \
674                                 { \
675                                         values[0] = pixel1[3]; \
676                                         values[1] = pixel2[3]; \
677                                         values[2] = pixel3[3]; \
678                                         values[3] = pixel4[3]; \
679                                         top_p[3] = (type)bilinear(cx, cy, values); \
680                                 } \
682                                 top_p += components; \
684 /* Do bottom */ \
685                         cx = cen_x + (cen_x - cx); \
686                         cy = cen_y + (cen_y - cy); \
688                         if(cx >= 0.0) \
689                                         ix = (int)cx; \
690                         else \
691                                         ix = -((int)-cx + 1); \
693                         if(cy >= 0.0) \
694                                         iy = (int)cy; \
695                         else \
696                                         iy = -((int)-cy + 1); \
698                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
699                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
700                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
701                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
705                                 values[0] = pixel1[0]; \
706                                 values[1] = pixel2[0]; \
707                                 values[2] = pixel3[0]; \
708                                 values[3] = pixel4[0]; \
709                                 bot_p[0] = (type)bilinear(cx, cy, values); \
711                                 values[0] = pixel1[1]; \
712                                 values[1] = pixel2[1]; \
713                                 values[2] = pixel3[1]; \
714                                 values[3] = pixel4[1]; \
715                                 bot_p[1] = (type)bilinear(cx, cy, values); \
717                                 values[0] = pixel1[2]; \
718                                 values[1] = pixel2[2]; \
719                                 values[2] = pixel3[2]; \
720                                 values[3] = pixel4[2]; \
721                                 bot_p[2] = (type)bilinear(cx, cy, values); \
723                                 if(components == 4) \
724                                 { \
725                                         values[0] = pixel1[3]; \
726                                         values[1] = pixel2[3]; \
727                                         values[2] = pixel3[3]; \
728                                         values[3] = pixel4[3]; \
729                                         bot_p[3] = (type)bilinear(cx, cy, values); \
730                                 } \
732                                 bot_p -= components; \
735                         } \
736                         else \
737                         { \
738 /* Outside distortion area */ \
739 /* Do top */ \
740                                 top_p[0] = input_rows[row][components * col + 0]; \
741                                 top_p[1] = input_rows[row][components * col + 1]; \
742                                 top_p[2] = input_rows[row][components * col + 2]; \
743                                 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
746                                 top_p += components; \
748 /* Do bottom */ \
749                                 int bot_offset = w * components - col * components - components; \
750                                 int bot_row = h - 1 - row; \
751                                 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
752                                 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
753                                 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
754                                 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
755                                 bot_p -= components; \
756                         } \
757                 } \
758         } \
761 void WhirlUnit::process_package(LoadPackage *package)
763         WhirlPackage *pkg = (WhirlPackage*)package;
764         int w = plugin->input->get_w();
765         int h = plugin->input->get_h();
766         double whirl = plugin->config.angle * M_PI / 180;
767         double pinch = plugin->config.pinch / MAXPINCH;
768         double cx, cy;
769     int ix, iy;
770         double cen_x = (double)(w - 1) / 2.0;
771         double cen_y = (double)(h - 1) / 2.0;
772         double radius = MAX(w, h);
773         double radius3 = plugin->config.radius / MAXRADIUS;
774         double radius2 = radius * radius * radius3;
775         double scale_x;
776         double scale_y;
779 //printf("WhirlUnit::process_package 1 %f %f %f\n", 
780 //      plugin->config.angle, plugin->config.pinch, plugin->config.radius);
781         if(w < h)
782         {
783         scale_x = (double)h / w;
784         scale_y = 1.0;
785         }
786         else
787         if(w > h)
788         {
789         scale_x = 1.0;
790         scale_y = (double)w / h;
791         }
792         else
793         {
794         scale_x = 1.0;
795         scale_y = 1.0;
796         }
800         switch(plugin->input->get_color_model())
801         {
802                 case BC_RGB888:
803                 case BC_YUV888:
804                         WHIRL_MACRO(unsigned char, 0xff, 3);
805                         break;
806                 case BC_RGBA8888:
807                 case BC_YUVA8888:
808                         WHIRL_MACRO(unsigned char, 0xff, 4);
809                         break;
810                 case BC_RGB161616:
811                 case BC_YUV161616:
812                         WHIRL_MACRO(uint16_t, 0xffff, 3);
813                         break;
814                 case BC_RGBA16161616:
815                 case BC_YUVA16161616:
816                         WHIRL_MACRO(uint16_t, 0xffff, 4);
817                         break;
818                 
819         }
828 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
829  : LoadServer(cpus, cpus)
831         this->plugin = plugin;
833 void WhirlEngine::init_packages()
835         int increment = plugin->input->get_h() / LoadServer::total_packages;
836         int y = 0;
837         for(int i = 0; i < LoadServer::total_packages; i++)
838         {
839                 WhirlPackage *pkg = (WhirlPackage*)packages[i];
840                 pkg->row1 = y;
841                 pkg->row2 = y + increment;
842                 if(i == LoadServer::total_packages - 1) pkg->row2 = plugin->input->get_h();
843                 y += increment;
844         }
845         
848 LoadClient* WhirlEngine::new_client()
850         return new WhirlUnit(plugin, this);
853 LoadPackage* WhirlEngine::new_package()
855         return new WhirlPackage;