r864: Merge 2.1:
[cinelerra_cv.git] / plugins / whirl / whirl.C
blob62a95be4d0afd1374b8be01f562e43840245c819
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "bchash.h"
4 #include "filexml.h"
5 #include "guicast.h"
6 #include "keyframe.h"
7 #include "language.h"
8 #include "loadbalance.h"
9 #include "picon_png.h"
10 #include "pluginvclient.h"
11 #include "vframe.h"
15 #include <stdint.h>
16 #include <string.h>
18 class WhirlEffect;
19 class WhirlWindow;
20 class WhirlEngine;
22 #define MAXRADIUS 100
23 #define MAXPINCH 100
30 class WhirlConfig
32 public:
33         WhirlConfig();
34         
35         void copy_from(WhirlConfig &src);
36         int equivalent(WhirlConfig &src);
37         void interpolate(WhirlConfig &prev, 
38                 WhirlConfig &next, 
39                 long prev_frame, 
40                 long next_frame, 
41                 long current_frame);
42         
43         float angle;
44         float pinch;
45         float radius;
49 class WhirlAngle : public BC_FSlider
51 public:
52         WhirlAngle(WhirlEffect *plugin, int x, int y);
53         int handle_event();
54         WhirlEffect *plugin;
59 class WhirlPinch : public BC_FSlider
61 public:
62         WhirlPinch(WhirlEffect *plugin, int x, int y);
63         int handle_event();
64         WhirlEffect *plugin;
69 class WhirlRadius : public BC_FSlider
71 public:
72         WhirlRadius(WhirlEffect *plugin, int x, int y);
73         int handle_event();
74         WhirlEffect *plugin;
77 class WhirlWindow : public BC_Window
79 public:
80         WhirlWindow(WhirlEffect *plugin, int x, int y);
81         void create_objects();
82         int close_event();
83         WhirlEffect *plugin;
84         WhirlRadius *radius;
85         WhirlPinch *pinch;
86         WhirlAngle *angle;
90 PLUGIN_THREAD_HEADER(WhirlEffect, WhirlThread, WhirlWindow)
93 class WhirlPackage : public LoadPackage
95 public:
96         WhirlPackage();
97         int row1, row2;
100 class WhirlUnit : public LoadClient
102 public:
103         WhirlUnit(WhirlEffect *plugin, WhirlEngine *server);
104         void process_package(LoadPackage *package);
105         WhirlEngine *server;
106         WhirlEffect *plugin;
107         
111 class WhirlEngine : public LoadServer
113 public:
114         WhirlEngine(WhirlEffect *plugin, int cpus);
115         void init_packages();
116         LoadClient* new_client();
117         LoadPackage* new_package();
118         WhirlEffect *plugin;
123 class WhirlEffect : public PluginVClient
125 public:
126         WhirlEffect(PluginServer *server);
127         ~WhirlEffect();
129         int process_realtime(VFrame *input, VFrame *output);
130         int is_realtime();
131         char* plugin_title();
132         VFrame* new_picon();
133         int show_gui();
134         void raise_window();
135         void update_gui();
136         int set_string();
137         int load_configuration();
138         int load_defaults();
139         int save_defaults();
140         void save_data(KeyFrame *keyframe);
141         void read_data(KeyFrame *keyframe);
143         WhirlEngine *engine;
144         VFrame *temp_frame;
145         VFrame *input, *output;
146         WhirlConfig config;
147         BC_Hash *defaults;
148         WhirlThread *thread;
149         int need_reconfigure;
155 PLUGIN_THREAD_OBJECT(WhirlEffect, WhirlThread, WhirlWindow)
158 REGISTER_PLUGIN(WhirlEffect)
175 WhirlConfig::WhirlConfig()
177         angle = 0.0;
178         pinch = 0.0;
179         radius = 0.0;
182 void WhirlConfig::copy_from(WhirlConfig &src)
184         this->angle = src.angle;
185         this->pinch = src.pinch;
186         this->radius = src.radius;
189 int WhirlConfig::equivalent(WhirlConfig &src)
191         return EQUIV(this->angle, src.angle) &&
192                 EQUIV(this->pinch, src.pinch) &&
193                 EQUIV(this->radius, src.radius);
196 void WhirlConfig::interpolate(WhirlConfig &prev, 
197         WhirlConfig &next, 
198         long prev_frame, 
199         long next_frame, 
200         long current_frame)
202         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
203         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
205         this->angle = prev.angle * prev_scale + next.angle * next_scale;
206         this->pinch = prev.pinch * prev_scale + next.pinch * next_scale;
207         this->radius = prev.radius * prev_scale + next.radius * next_scale;
219 WhirlWindow::WhirlWindow(WhirlEffect *plugin, int x, int y)
220  : BC_Window(plugin->gui_string, 
221         x, 
222         y, 
223         220, 
224         200, 
225         220, 
226         200, 
227         0, 
228         0,
229         1)
231         this->plugin = plugin;
236 void WhirlWindow::create_objects()
238         int x = 10, y = 10;
239         add_subwindow(new BC_Title(x, y, _("Radius")));
240         y += 20;
241         add_subwindow(radius = new WhirlRadius(plugin, x, y));
242         y += 40;
243         add_subwindow(new BC_Title(x, y, _("Pinch")));
244         y += 20;
245         add_subwindow(pinch = new WhirlPinch(plugin, x, y));
246         y += 40;
247         add_subwindow(new BC_Title(x, y, _("Angle")));
248         y += 20;
249         add_subwindow(angle = new WhirlAngle(plugin, x, y));
251         show_window();
252         flush();
255 int WhirlWindow::close_event()
257         set_done(1);
258         return 1;
273 WhirlAngle::WhirlAngle(WhirlEffect *plugin, int x, int y)
274  : BC_FSlider(x, 
275                 y, 
276                 0,
277                 200,
278                 200, 
279                 (float)0, 
280                 (float)360,
281                 plugin->config.angle)
283         this->plugin = plugin;
285 int WhirlAngle::handle_event()
287         plugin->config.angle = get_value();
288         plugin->send_configure_change();
289         return 1;
295 WhirlPinch::WhirlPinch(WhirlEffect *plugin, int x, int y)
296  : BC_FSlider(x, 
297                 y, 
298                 0,
299                 200,
300                 200, 
301                 (float)0, 
302                 (float)MAXPINCH, 
303                 plugin->config.pinch)
305         this->plugin = plugin;
307 int WhirlPinch::handle_event()
309         plugin->config.pinch = get_value();
310         plugin->send_configure_change();
311         return 1;
317 WhirlRadius::WhirlRadius(WhirlEffect *plugin, int x, int y)
318  : BC_FSlider(x, 
319                 y, 
320                 0,
321                 200,
322                 200, 
323                 (float)0, 
324                 (float)MAXRADIUS, 
325                 plugin->config.radius)
327         this->plugin = plugin;
329 int WhirlRadius::handle_event()
331         plugin->config.radius = get_value();
332         plugin->send_configure_change();
333         return 1;
346 WhirlEffect::WhirlEffect(PluginServer *server)
347  : PluginVClient(server)
349         need_reconfigure = 1;
350         engine = 0;
351         temp_frame = 0;
352         PLUGIN_CONSTRUCTOR_MACRO
355 WhirlEffect::~WhirlEffect()
357         PLUGIN_DESTRUCTOR_MACRO
358         if(engine) delete engine;
359         if(temp_frame) delete temp_frame;
367 char* WhirlEffect::plugin_title() { return N_("Whirl"); }
368 int WhirlEffect::is_realtime() { return 1; }
370 NEW_PICON_MACRO(WhirlEffect)
372 SHOW_GUI_MACRO(WhirlEffect, WhirlThread)
374 RAISE_WINDOW_MACRO(WhirlEffect)
376 SET_STRING_MACRO(WhirlEffect)
378 void WhirlEffect::update_gui()
380         if(thread)
381         {
382                 load_configuration();
383                 thread->window->lock_window();
384                 thread->window->angle->update(config.angle);
385                 thread->window->pinch->update(config.pinch);
386                 thread->window->radius->update(config.radius);
387                 thread->window->unlock_window();
388         }
391 LOAD_CONFIGURATION_MACRO(WhirlEffect, WhirlConfig)
396 int WhirlEffect::load_defaults()
398         char directory[1024], string[1024];
399 // set the default directory
400         sprintf(directory, "%swhirl.rc", BCASTDIR);
402 // load the defaults
403         defaults = new BC_Hash(directory);
404         defaults->load();
406         config.angle = defaults->get("ANGLE", config.angle);
407         config.pinch = defaults->get("PINCH", config.pinch);
408         config.radius = defaults->get("RADIUS", config.radius);
411 int WhirlEffect::save_defaults()
413         defaults->update("ANGLE", config.angle);
414         defaults->update("PINCH", config.pinch);
415         defaults->update("RADIUS", config.radius);
416         defaults->save();
419 void WhirlEffect::save_data(KeyFrame *keyframe)
421         FileXML output;
423 // cause data to be stored directly in text
424         output.set_shared_string(keyframe->data, MESSAGESIZE);
426         output.tag.set_title("WHIRL");
427         output.tag.set_property("ANGLE", config.angle);
428         output.tag.set_property("PINCH", config.pinch);
429         output.tag.set_property("RADIUS", config.radius);
430         output.append_tag();
431         output.terminate_string();
432 // data is now in *text
435 void WhirlEffect::read_data(KeyFrame *keyframe)
437         FileXML input;
439         input.set_shared_string(keyframe->data, strlen(keyframe->data));
441         int result = 0;
443         while(!result)
444         {
445                 result = input.read_tag();
447                 if(!result)
448                 {
449                         if(input.tag.title_is("WHIRL"))
450                         {
451                                 config.angle = input.tag.get_property("ANGLE", config.angle);
452                                 config.pinch = input.tag.get_property("PINCH", config.pinch);
453                                 config.radius = input.tag.get_property("RADIUS", config.radius);
454                         }
455                 }
456         }
459 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
461         need_reconfigure |= load_configuration();
462         this->input = input;
463         this->output = output;
465         if(EQUIV(config.angle, 0) || 
466                 (EQUIV(config.radius, 0) && EQUIV(config.pinch, 0)))
467         {
468                 if(input->get_rows()[0] != output->get_rows()[0])
469                         output->copy_from(input);
470         }
471         else
472         {
473                 if(input->get_rows()[0] == output->get_rows()[0])
474                 {
475                         if(!temp_frame) temp_frame = new VFrame(0,
476                                 input->get_w(),
477                                 input->get_h(),
478                                 input->get_color_model());
479                         temp_frame->copy_from(input);
480                         this->input = temp_frame;
481 //printf("WhirlEffect::process_realtime 1\n");
482                 }
484                 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
485                 
486                 engine->process_packages();
487         }
497 WhirlPackage::WhirlPackage()
498  : LoadPackage()
504 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
505  : LoadClient(server)
507         this->plugin = plugin;
512 static int calc_undistorted_coords(double cen_x,
513                         double cen_y,
514                         double scale_x,
515                         double scale_y,
516                         double radius,
517                         double radius2,
518                         double radius3,
519                         double pinch,
520                         double wx,
521                         double wy,
522                         double &whirl,
523                         double &x,
524                         double &y)
526         double dx, dy;
527         double d, factor;
528         double dist;
529         double ang, sina, cosa;
530         int inside;
532 /* Distances to center, scaled */
534         dx = (wx - cen_x) * scale_x;
535         dy = (wy - cen_y) * scale_y;
537 /* Distance^2 to center of *circle* (scaled ellipse) */
539         d = dx * dx + dy * dy;
541 /*  If we are inside circle, then distort.
542  *  Else, just return the same position
543  */
545         inside = (d < radius2);
547         if(inside)
548     {
549         dist = sqrt(d / radius3) / radius;
551 /* Pinch */
553         factor = pow(sin(M_PI / 2 * dist), -pinch);
555         dx *= factor;
556         dy *= factor;
558 /* Whirl */
560         factor = 1.0 - dist;
562         ang = whirl * factor * factor;
564         sina = sin(ang);
565         cosa = cos(ang);
567         x = (cosa * dx - sina * dy) / scale_x + cen_x;
568         y = (sina * dx + cosa * dy) / scale_y + cen_y;
569     }
571         return inside;
576 #define GET_PIXEL(components, x, y, input_rows) \
577         input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
584 static float bilinear(double x, double y, double *values)
586         double m0, m1;
587         x = fmod(x, 1.0);
588         y = fmod(y, 1.0);
590         if(x < 0.0) x += 1.0;
591         if(y < 0.0) y += 1.0;
593         m0 = (double)values[0] + x * ((double)values[1] - values[0]);
594         m1 = (double)values[2] + x * ((double)values[3] - values[2]);
595         return m0 + y * (m1 - m0);
602 #define WHIRL_MACRO(type, max, components) \
603 { \
604         type **input_rows = (type**)plugin->input->get_rows(); \
605         double values[components]; \
606         for(int row = pkg->row1; row <= (pkg->row2 + pkg->row1) / 2; row++) \
607         { \
608                 type *top_row = (type*)plugin->output->get_rows()[row]; \
609                 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
610                 type *top_p = top_row; \
611                 type *bot_p = bot_row + components * w - components; \
612                  \
613                 for(int col = 0; col < w; col++) \
614                 { \
615                         if(calc_undistorted_coords(cen_x, \
616                                 cen_y, \
617                                 scale_x, \
618                                 scale_y, \
619                                 radius, \
620                                 radius2, \
621                                 radius3, \
622                                 pinch, \
623                                 col, \
624                                 row, \
625                                 whirl, \
626                                 cx, \
627                                 cy)) \
628                         { \
629 /* Inside distortion area */ \
630 /* Do top */ \
631                                 if(cx >= 0.0) \
632                                         ix = (int)cx; \
633                                 else \
634                                         ix = -((int)-cx + 1); \
636                                 if(cy >= 0.0) \
637                                         iy = (int)cy; \
638                                 else \
639                                         iy = -((int)-cy + 1); \
641                                 type *pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
642                                 type *pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
643                                 type *pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
644                                 type *pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
646                                 values[0] = pixel1[0]; \
647                                 values[1] = pixel2[0]; \
648                                 values[2] = pixel3[0]; \
649                                 values[3] = pixel4[0]; \
650                                 top_p[0] = (type)bilinear(cx, cy, values); \
652                                 values[0] = pixel1[1]; \
653                                 values[1] = pixel2[1]; \
654                                 values[2] = pixel3[1]; \
655                                 values[3] = pixel4[1]; \
656                                 top_p[1] = (type)bilinear(cx, cy, values); \
658                                 values[0] = pixel1[2]; \
659                                 values[1] = pixel2[2]; \
660                                 values[2] = pixel3[2]; \
661                                 values[3] = pixel4[2]; \
662                                 top_p[2] = (type)bilinear(cx, cy, values); \
664                                 if(components == 4) \
665                                 { \
666                                         values[0] = pixel1[3]; \
667                                         values[1] = pixel2[3]; \
668                                         values[2] = pixel3[3]; \
669                                         values[3] = pixel4[3]; \
670                                         top_p[3] = (type)bilinear(cx, cy, values); \
671                                 } \
673                                 top_p += components; \
675 /* Do bottom */ \
676                         cx = cen_x + (cen_x - cx); \
677                         cy = cen_y + (cen_y - cy); \
679                         if(cx >= 0.0) \
680                                         ix = (int)cx; \
681                         else \
682                                         ix = -((int)-cx + 1); \
684                         if(cy >= 0.0) \
685                                         iy = (int)cy; \
686                         else \
687                                         iy = -((int)-cy + 1); \
689                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
690                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
691                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
692                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
696                                 values[0] = pixel1[0]; \
697                                 values[1] = pixel2[0]; \
698                                 values[2] = pixel3[0]; \
699                                 values[3] = pixel4[0]; \
700                                 bot_p[0] = (type)bilinear(cx, cy, values); \
702                                 values[0] = pixel1[1]; \
703                                 values[1] = pixel2[1]; \
704                                 values[2] = pixel3[1]; \
705                                 values[3] = pixel4[1]; \
706                                 bot_p[1] = (type)bilinear(cx, cy, values); \
708                                 values[0] = pixel1[2]; \
709                                 values[1] = pixel2[2]; \
710                                 values[2] = pixel3[2]; \
711                                 values[3] = pixel4[2]; \
712                                 bot_p[2] = (type)bilinear(cx, cy, values); \
714                                 if(components == 4) \
715                                 { \
716                                         values[0] = pixel1[3]; \
717                                         values[1] = pixel2[3]; \
718                                         values[2] = pixel3[3]; \
719                                         values[3] = pixel4[3]; \
720                                         bot_p[3] = (type)bilinear(cx, cy, values); \
721                                 } \
723                                 bot_p -= components; \
726                         } \
727                         else \
728                         { \
729 /* Outside distortion area */ \
730 /* Do top */ \
731                                 top_p[0] = input_rows[row][components * col + 0]; \
732                                 top_p[1] = input_rows[row][components * col + 1]; \
733                                 top_p[2] = input_rows[row][components * col + 2]; \
734                                 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
737                                 top_p += components; \
739 /* Do bottom */ \
740                                 int bot_offset = w * components - col * components - components; \
741                                 int bot_row = h - 1 - row; \
742                                 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
743                                 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
744                                 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
745                                 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
746                                 bot_p -= components; \
747                         } \
748                 } \
749         } \
752 void WhirlUnit::process_package(LoadPackage *package)
754         WhirlPackage *pkg = (WhirlPackage*)package;
755         int w = plugin->input->get_w();
756         int h = plugin->input->get_h();
757         double whirl = plugin->config.angle * M_PI / 180;
758         double pinch = plugin->config.pinch / MAXPINCH;
759         double cx, cy;
760     int ix, iy;
761         double cen_x = (double)(w - 1) / 2.0;
762         double cen_y = (double)(h - 1) / 2.0;
763         double radius = MAX(w, h);
764         double radius3 = plugin->config.radius / MAXRADIUS;
765         double radius2 = radius * radius * radius3;
766         double scale_x;
767         double scale_y;
770 //printf("WhirlUnit::process_package 1 %f %f %f\n", 
771 //      plugin->config.angle, plugin->config.pinch, plugin->config.radius);
772         if(w < h)
773         {
774         scale_x = (double)h / w;
775         scale_y = 1.0;
776         }
777         else
778         if(w > h)
779         {
780         scale_x = 1.0;
781         scale_y = (double)w / h;
782         }
783         else
784         {
785         scale_x = 1.0;
786         scale_y = 1.0;
787         }
791         switch(plugin->input->get_color_model())
792         {
793                 case BC_RGB_FLOAT:
794                         WHIRL_MACRO(float, 1, 3);
795                         break;
796                 case BC_RGB888:
797                 case BC_YUV888:
798                         WHIRL_MACRO(unsigned char, 0xff, 3);
799                         break;
800                 case BC_RGBA_FLOAT:
801                         WHIRL_MACRO(float, 1, 4);
802                         break;
803                 case BC_RGBA8888:
804                 case BC_YUVA8888:
805                         WHIRL_MACRO(unsigned char, 0xff, 4);
806                         break;
807                 case BC_RGB161616:
808                 case BC_YUV161616:
809                         WHIRL_MACRO(uint16_t, 0xffff, 3);
810                         break;
811                 case BC_RGBA16161616:
812                 case BC_YUVA16161616:
813                         WHIRL_MACRO(uint16_t, 0xffff, 4);
814                         break;
815                 
816         }
825 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
826  : LoadServer(cpus, cpus)
828         this->plugin = plugin;
830 void WhirlEngine::init_packages()
832         for(int i = 0; i < LoadServer::get_total_packages(); i++)
833         {
834                 WhirlPackage *pkg = (WhirlPackage*)get_package(i);
835                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
836                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
837         }
838         
841 LoadClient* WhirlEngine::new_client()
843         return new WhirlUnit(plugin, this);
846 LoadPackage* WhirlEngine::new_package()
848         return new WhirlPackage;