update internationalization data
[goodguy/history.git] / cinelerra-5.0 / plugins / whirl / whirl.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "clip.h"
24 #include "bchash.h"
25 #include "filexml.h"
26 #include "guicast.h"
27 #include "keyframe.h"
28 #include "language.h"
29 #include "loadbalance.h"
30 #include "pluginvclient.h"
31 #include "vframe.h"
32
33
34
35 #include <stdint.h>
36 #include <string.h>
37
38 class WhirlEffect;
39 class WhirlWindow;
40 class WhirlEngine;
41
42 #define MAXRADIUS 100
43 #define MAXPINCH 100
44
45
46
47
48
49
50 class WhirlConfig
51 {
52 public:
53         WhirlConfig();
54         
55         void copy_from(WhirlConfig &src);
56         int equivalent(WhirlConfig &src);
57         void interpolate(WhirlConfig &prev, 
58                 WhirlConfig &next, 
59                 long prev_frame, 
60                 long next_frame, 
61                 long current_frame);
62         
63         float angle;
64         float pinch;
65         float radius;
66 };
67
68
69 class WhirlAngle : public BC_FSlider
70 {
71 public:
72         WhirlAngle(WhirlEffect *plugin, int x, int y);
73         int handle_event();
74         WhirlEffect *plugin;
75 };
76
77
78
79 class WhirlPinch : public BC_FSlider
80 {
81 public:
82         WhirlPinch(WhirlEffect *plugin, int x, int y);
83         int handle_event();
84         WhirlEffect *plugin;
85 };
86
87
88
89 class WhirlRadius : public BC_FSlider
90 {
91 public:
92         WhirlRadius(WhirlEffect *plugin, int x, int y);
93         int handle_event();
94         WhirlEffect *plugin;
95 };
96
97 class WhirlWindow : public PluginClientWindow
98 {
99 public:
100         WhirlWindow(WhirlEffect *plugin);
101         void create_objects();
102         WhirlEffect *plugin;
103         WhirlRadius *radius;
104         WhirlPinch *pinch;
105         WhirlAngle *angle;
106 };
107
108
109
110
111
112 class WhirlPackage : public LoadPackage
113 {
114 public:
115         WhirlPackage();
116         int row1, row2;
117 };
118
119 class WhirlUnit : public LoadClient
120 {
121 public:
122         WhirlUnit(WhirlEffect *plugin, WhirlEngine *server);
123         void process_package(LoadPackage *package);
124         WhirlEngine *server;
125         WhirlEffect *plugin;
126         
127 };
128
129
130 class WhirlEngine : public LoadServer
131 {
132 public:
133         WhirlEngine(WhirlEffect *plugin, int cpus);
134         void init_packages();
135         LoadClient* new_client();
136         LoadPackage* new_package();
137         WhirlEffect *plugin;
138 };
139
140
141
142 class WhirlEffect : public PluginVClient
143 {
144 public:
145         WhirlEffect(PluginServer *server);
146         ~WhirlEffect();
147
148         PLUGIN_CLASS_MEMBERS(WhirlConfig)
149         int process_realtime(VFrame *input, VFrame *output);
150         int is_realtime();
151         void update_gui();
152         void save_data(KeyFrame *keyframe);
153         void read_data(KeyFrame *keyframe);
154
155         WhirlEngine *engine;
156         VFrame *temp_frame;
157         VFrame *input, *output;
158         int need_reconfigure;
159 };
160
161
162
163
164
165
166 REGISTER_PLUGIN(WhirlEffect)
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 WhirlConfig::WhirlConfig()
184 {
185         angle = 0.0;
186         pinch = 0.0;
187         radius = 0.0;
188 }
189
190 void WhirlConfig::copy_from(WhirlConfig &src)
191 {
192         this->angle = src.angle;
193         this->pinch = src.pinch;
194         this->radius = src.radius;
195 }
196
197 int WhirlConfig::equivalent(WhirlConfig &src)
198 {
199         return EQUIV(this->angle, src.angle) &&
200                 EQUIV(this->pinch, src.pinch) &&
201                 EQUIV(this->radius, src.radius);
202 }
203
204 void WhirlConfig::interpolate(WhirlConfig &prev, 
205         WhirlConfig &next, 
206         long prev_frame, 
207         long next_frame, 
208         long current_frame)
209 {
210         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
211         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
212
213         this->angle = prev.angle * prev_scale + next.angle * next_scale;
214         this->pinch = prev.pinch * prev_scale + next.pinch * next_scale;
215         this->radius = prev.radius * prev_scale + next.radius * next_scale;
216 }
217
218
219
220
221
222
223
224
225
226
227 WhirlWindow::WhirlWindow(WhirlEffect *plugin)
228  : PluginClientWindow(plugin, 
229         220, 
230         200, 
231         220, 
232         200, 
233         0)
234 {
235         this->plugin = plugin;
236 }
237
238
239
240 void WhirlWindow::create_objects()
241 {
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));
254
255         show_window();
256         flush();
257 }
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272 WhirlAngle::WhirlAngle(WhirlEffect *plugin, int x, int y)
273  : BC_FSlider(x, 
274                 y, 
275                 0,
276                 200,
277                 200, 
278                 (float)0, 
279                 (float)360,
280                 plugin->config.angle)
281 {
282         this->plugin = plugin;
283 }
284 int WhirlAngle::handle_event()
285 {
286         plugin->config.angle = get_value();
287         plugin->send_configure_change();
288         return 1;
289 }
290
291
292
293
294 WhirlPinch::WhirlPinch(WhirlEffect *plugin, int x, int y)
295  : BC_FSlider(x, 
296                 y, 
297                 0,
298                 200,
299                 200, 
300                 (float)0, 
301                 (float)MAXPINCH, 
302                 plugin->config.pinch)
303 {
304         this->plugin = plugin;
305 }
306 int WhirlPinch::handle_event()
307 {
308         plugin->config.pinch = get_value();
309         plugin->send_configure_change();
310         return 1;
311 }
312
313
314
315
316 WhirlRadius::WhirlRadius(WhirlEffect *plugin, int x, int y)
317  : BC_FSlider(x, 
318                 y, 
319                 0,
320                 200,
321                 200, 
322                 (float)0, 
323                 (float)MAXRADIUS, 
324                 plugin->config.radius)
325 {
326         this->plugin = plugin;
327 }
328 int WhirlRadius::handle_event()
329 {
330         plugin->config.radius = get_value();
331         plugin->send_configure_change();
332         return 1;
333 }
334
335
336
337
338
339
340
341
342
343
344
345 WhirlEffect::WhirlEffect(PluginServer *server)
346  : PluginVClient(server)
347 {
348         need_reconfigure = 1;
349         engine = 0;
350         temp_frame = 0;
351         
352 }
353
354 WhirlEffect::~WhirlEffect()
355 {
356         
357         if(engine) delete engine;
358         if(temp_frame) delete temp_frame;
359 }
360
361
362
363
364
365
366 const char* WhirlEffect::plugin_title() { return _("Whirl"); }
367 int WhirlEffect::is_realtime() { return 1; }
368
369
370
371 NEW_WINDOW_MACRO(WhirlEffect, WhirlWindow)
372
373
374 void WhirlEffect::update_gui()
375 {
376         if(thread)
377         {
378                 load_configuration();
379                 thread->window->lock_window();
380                 ((WhirlWindow*)thread->window)->angle->update(config.angle);
381                 ((WhirlWindow*)thread->window)->pinch->update(config.pinch);
382                 ((WhirlWindow*)thread->window)->radius->update(config.radius);
383                 thread->window->unlock_window();
384         }
385 }
386
387 LOAD_CONFIGURATION_MACRO(WhirlEffect, WhirlConfig)
388
389
390
391
392 void WhirlEffect::save_data(KeyFrame *keyframe)
393 {
394         FileXML output;
395
396 // cause data to be stored directly in text
397         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
398
399         output.tag.set_title("WHIRL");
400         output.tag.set_property("ANGLE", config.angle);
401         output.tag.set_property("PINCH", config.pinch);
402         output.tag.set_property("RADIUS", config.radius);
403         output.append_tag();
404         output.terminate_string();
405 // data is now in *text
406 }
407
408 void WhirlEffect::read_data(KeyFrame *keyframe)
409 {
410         FileXML input;
411
412         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
413
414         int result = 0;
415
416         while(!result)
417         {
418                 result = input.read_tag();
419
420                 if(!result)
421                 {
422                         if(input.tag.title_is("WHIRL"))
423                         {
424                                 config.angle = input.tag.get_property("ANGLE", config.angle);
425                                 config.pinch = input.tag.get_property("PINCH", config.pinch);
426                                 config.radius = input.tag.get_property("RADIUS", config.radius);
427                         }
428                 }
429         }
430 }
431
432 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
433 {
434         need_reconfigure |= load_configuration();
435         this->input = input;
436         this->output = output;
437
438         if(EQUIV(config.angle, 0) || 
439                 (EQUIV(config.radius, 0) && EQUIV(config.pinch, 0)))
440         {
441                 if(input->get_rows()[0] != output->get_rows()[0])
442                         output->copy_from(input);
443         }
444         else
445         {
446                 if(input->get_rows()[0] == output->get_rows()[0])
447                 {
448                         if(!temp_frame) temp_frame = new VFrame(0,
449                                 -1,
450                                 input->get_w(),
451                                 input->get_h(),
452                                 input->get_color_model(),
453                                 -1);
454                         temp_frame->copy_from(input);
455                         this->input = temp_frame;
456                 }
457
458                 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
459
460                 engine->process_packages();
461         }
462         return 0;
463 }
464
465
466
467
468
469
470
471
472 WhirlPackage::WhirlPackage()
473  : LoadPackage()
474 {
475 }
476
477
478
479 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
480  : LoadClient(server)
481 {
482         this->plugin = plugin;
483 }
484
485
486
487 static int calc_undistorted_coords(double cen_x,
488                         double cen_y,
489                         double scale_x,
490                         double scale_y,
491                         double radius,
492                         double radius2,
493                         double radius3,
494                         double pinch,
495                         double wx,
496                         double wy,
497                         double &whirl,
498                         double &x,
499                         double &y)
500 {
501         double dx, dy;
502         double d, factor;
503         double dist;
504         double ang, sina, cosa;
505         int inside;
506
507 /* Distances to center, scaled */
508
509         dx = (wx - cen_x) * scale_x;
510         dy = (wy - cen_y) * scale_y;
511
512 /* Distance^2 to center of *circle* (scaled ellipse) */
513
514         d = dx * dx + dy * dy;
515
516 /*  If we are inside circle, then distort.
517  *  Else, just return the same position
518  */
519
520         inside = (d < radius2);
521
522         if(inside)
523     {
524         dist = sqrt(d / radius3) / radius;
525
526 /* Pinch */
527
528         factor = pow(sin(M_PI / 2 * dist), -pinch);
529
530         dx *= factor;
531         dy *= factor;
532
533 /* Whirl */
534
535         factor = 1.0 - dist;
536
537         ang = whirl * factor * factor;
538
539         sina = sin(ang);
540         cosa = cos(ang);
541
542         x = (cosa * dx - sina * dy) / scale_x + cen_x;
543         y = (sina * dx + cosa * dy) / scale_y + cen_y;
544     }
545
546         return inside;
547 }
548
549
550
551 #define GET_PIXEL(components, x, y, input_rows) \
552         input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
553
554
555
556
557
558
559 static float bilinear(double x, double y, double *values)
560 {
561         double m0, m1;
562         x = fmod(x, 1.0);
563         y = fmod(y, 1.0);
564
565         if(x < 0.0) x += 1.0;
566         if(y < 0.0) y += 1.0;
567
568         m0 = (double)values[0] + x * ((double)values[1] - values[0]);
569         m1 = (double)values[2] + x * ((double)values[3] - values[2]);
570         return m0 + y * (m1 - m0);
571 }
572
573
574
575
576
577 #define WHIRL_MACRO(type, max, components) \
578 { \
579         type **input_rows = (type**)plugin->input->get_rows(); \
580 /* Compiler error requires separate arrays */ \
581         double top_values[4], bot_values[4]; \
582         for( int i=0; i<4; ++i ) top_values[i] = bot_values[i] = 0; \
583         for(int row = pkg->row1 / 2; row <= (pkg->row2 + pkg->row1) / 2; row++) \
584         { \
585                 type *top_row = (type*)plugin->output->get_rows()[row]; \
586                 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
587                 type *top_p = top_row; \
588                 type *bot_p = bot_row + components * w - components; \
589                 type *pixel1; \
590                 type *pixel2; \
591                 type *pixel3; \
592                 type *pixel4; \
593  \
594                 for(int col = 0; col < w; col++) \
595                 { \
596                         if(calc_undistorted_coords(cen_x, \
597                                 cen_y, \
598                                 scale_x, \
599                                 scale_y, \
600                                 radius, \
601                                 radius2, \
602                                 radius3, \
603                                 pinch, \
604                                 col, \
605                                 row, \
606                                 whirl, \
607                                 cx, \
608                                 cy)) \
609                         { \
610 /* Inside distortion area */ \
611 /* Do top */ \
612                                 if(cx >= 0.0) \
613                                         ix = (int)cx; \
614                                 else \
615                                         ix = -((int)-cx + 1); \
616  \
617                                 if(cy >= 0.0) \
618                                         iy = (int)cy; \
619                                 else \
620                                         iy = -((int)-cy + 1); \
621  \
622                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
623                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
624                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
625                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
626  \
627                                 top_values[0] = pixel1[0]; \
628                                 top_values[1] = pixel2[0]; \
629                                 top_values[2] = pixel3[0]; \
630                                 top_values[3] = pixel4[0]; \
631                                 top_p[0] = (type)bilinear(cx, cy, top_values); \
632  \
633                                 top_values[0] = pixel1[1]; \
634                                 top_values[1] = pixel2[1]; \
635                                 top_values[2] = pixel3[1]; \
636                                 top_values[3] = pixel4[1]; \
637                                 top_p[1] = (type)bilinear(cx, cy, top_values); \
638  \
639                                 top_values[0] = pixel1[2]; \
640                                 top_values[1] = pixel2[2]; \
641                                 top_values[2] = pixel3[2]; \
642                                 top_values[3] = pixel4[2]; \
643                                 top_p[2] = (type)bilinear(cx, cy, top_values); \
644  \
645                                 if(components == 4) \
646                                 { \
647                                         top_values[0] = pixel1[3]; \
648                                         top_values[1] = pixel2[3]; \
649                                         top_values[2] = pixel3[3]; \
650                                         top_values[3] = pixel4[3]; \
651                                         top_p[3] = (type)bilinear(cx, cy, top_values); \
652                                 } \
653  \
654                                 top_p += components; \
655  \
656 /* Do bottom */ \
657                         cx = cen_x + (cen_x - cx); \
658                         cy = cen_y + (cen_y - cy); \
659  \
660                         if(cx >= 0.0) \
661                                         ix = (int)cx; \
662                         else \
663                                         ix = -((int)-cx + 1); \
664  \
665                         if(cy >= 0.0) \
666                                         iy = (int)cy; \
667                         else \
668                                         iy = -((int)-cy + 1); \
669  \
670                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
671                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
672                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
673                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
674  \
675  \
676  \
677                                 bot_values[0] = pixel1[0]; \
678                                 bot_values[1] = pixel2[0]; \
679                                 bot_values[2] = pixel3[0]; \
680                                 bot_values[3] = pixel4[0]; \
681                                 bot_p[0] = (type)bilinear(cx, cy, bot_values); \
682  \
683                                 bot_values[0] = pixel1[1]; \
684                                 bot_values[1] = pixel2[1]; \
685                                 bot_values[2] = pixel3[1]; \
686                                 bot_values[3] = pixel4[1]; \
687                                 bot_p[1] = (type)bilinear(cx, cy, bot_values); \
688  \
689                                 bot_values[0] = pixel1[2]; \
690                                 bot_values[1] = pixel2[2]; \
691                                 bot_values[2] = pixel3[2]; \
692                                 bot_values[3] = pixel4[2]; \
693                                 bot_p[2] = (type)bilinear(cx, cy, bot_values); \
694  \
695                                 if(components == 4) \
696                                 { \
697                                         bot_values[0] = pixel1[3]; \
698                                         bot_values[1] = pixel2[3]; \
699                                         bot_values[2] = pixel3[3]; \
700                                         bot_values[3] = pixel4[3]; \
701                                         bot_p[3] = (type)bilinear(cx, cy, bot_values); \
702                                 } \
703  \
704                                 bot_p -= components; \
705  \
706  \
707                         } \
708                         else \
709                         { \
710 /* Outside distortion area */ \
711 /* Do top */ \
712                                 top_p[0] = input_rows[row][components * col + 0]; \
713                                 top_p[1] = input_rows[row][components * col + 1]; \
714                                 top_p[2] = input_rows[row][components * col + 2]; \
715                                 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
716  \
717  \
718                                 top_p += components; \
719  \
720 /* Do bottom */ \
721                                 int bot_offset = w * components - col * components - components; \
722                                 int bot_row = h - 1 - row; \
723                                 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
724                                 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
725                                 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
726                                 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
727                                 bot_p -= components; \
728                         } \
729                 } \
730         } \
731 }
732
733 void WhirlUnit::process_package(LoadPackage *package)
734 {
735         WhirlPackage *pkg = (WhirlPackage*)package;
736         int w = plugin->input->get_w();
737         int h = plugin->input->get_h();
738         double whirl = plugin->config.angle * M_PI / 180;
739         double pinch = plugin->config.pinch / MAXPINCH;
740         double cx, cy;
741     int ix, iy;
742         double cen_x = (double)(w - 1) / 2.0;
743         double cen_y = (double)(h - 1) / 2.0;
744         double radius = MAX(w, h);
745         double radius3 = plugin->config.radius / MAXRADIUS;
746         double radius2 = radius * radius * radius3;
747         double scale_x;
748         double scale_y;
749
750
751 //printf("WhirlUnit::process_package 1 %f %f %f\n", 
752 //      plugin->config.angle, plugin->config.pinch, plugin->config.radius);
753         if(w < h)
754         {
755         scale_x = (double)h / w;
756         scale_y = 1.0;
757         }
758         else
759         if(w > h)
760         {
761         scale_x = 1.0;
762         scale_y = (double)w / h;
763         }
764         else
765         {
766         scale_x = 1.0;
767         scale_y = 1.0;
768         }
769
770
771
772         switch(plugin->input->get_color_model())
773         {
774                 case BC_RGB_FLOAT:
775                         WHIRL_MACRO(float, 1, 3);
776                         break;
777                 case BC_RGB888:
778                 case BC_YUV888:
779                         WHIRL_MACRO(unsigned char, 0xff, 3);
780                         break;
781                 case BC_RGBA_FLOAT:
782                         WHIRL_MACRO(float, 1, 4);
783                         break;
784                 case BC_RGBA8888:
785                 case BC_YUVA8888:
786                         WHIRL_MACRO(unsigned char, 0xff, 4);
787                         break;
788                 case BC_RGB161616:
789                 case BC_YUV161616:
790                         WHIRL_MACRO(uint16_t, 0xffff, 3);
791                         break;
792                 case BC_RGBA16161616:
793                 case BC_YUVA16161616:
794                         WHIRL_MACRO(uint16_t, 0xffff, 4);
795                         break;
796                 
797         }
798 }
799
800
801
802
803
804
805
806 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
807 // : LoadServer(1, 1)
808  : LoadServer(cpus, cpus)
809 {
810         this->plugin = plugin;
811 }
812 void WhirlEngine::init_packages()
813 {
814         for(int i = 0; i < LoadServer::get_total_packages(); i++)
815         {
816                 WhirlPackage *pkg = (WhirlPackage*)get_package(i);
817                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
818                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
819         }
820         
821 }
822
823 LoadClient* WhirlEngine::new_client()
824 {
825         return new WhirlUnit(plugin, this);
826 }
827
828 LoadPackage* WhirlEngine::new_package()
829 {
830         return new WhirlPackage;
831 }
832