remove whitespace at eol
[goodguy/history.git] / cinelerra-5.1 / 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.tag.set_title("/WHIRL");
405         output.append_tag();
406         output.append_newline();
407         output.terminate_string();
408 // data is now in *text
409 }
410
411 void WhirlEffect::read_data(KeyFrame *keyframe)
412 {
413         FileXML input;
414
415         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
416
417         int result = 0;
418
419         while(!result)
420         {
421                 result = input.read_tag();
422
423                 if(!result)
424                 {
425                         if(input.tag.title_is("WHIRL"))
426                         {
427                                 config.angle = input.tag.get_property("ANGLE", config.angle);
428                                 config.pinch = input.tag.get_property("PINCH", config.pinch);
429                                 config.radius = input.tag.get_property("RADIUS", config.radius);
430                         }
431                 }
432         }
433 }
434
435 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
436 {
437         need_reconfigure |= load_configuration();
438         this->input = input;
439         this->output = output;
440
441         if(EQUIV(config.angle, 0) ||
442                 (EQUIV(config.radius, 0) && EQUIV(config.pinch, 0)))
443         {
444                 if(input->get_rows()[0] != output->get_rows()[0])
445                         output->copy_from(input);
446         }
447         else
448         {
449                 if(input->get_rows()[0] == output->get_rows()[0])
450                 {
451                         if(!temp_frame) temp_frame = new VFrame(0,
452                                 -1,
453                                 input->get_w(),
454                                 input->get_h(),
455                                 input->get_color_model(),
456                                 -1);
457                         temp_frame->copy_from(input);
458                         this->input = temp_frame;
459                 }
460
461                 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
462
463                 engine->process_packages();
464         }
465         return 0;
466 }
467
468
469
470
471
472
473
474
475 WhirlPackage::WhirlPackage()
476  : LoadPackage()
477 {
478 }
479
480
481
482 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
483  : LoadClient(server)
484 {
485         this->plugin = plugin;
486 }
487
488
489
490 static int calc_undistorted_coords(double cen_x,
491                         double cen_y,
492                         double scale_x,
493                         double scale_y,
494                         double radius,
495                         double radius2,
496                         double radius3,
497                         double pinch,
498                         double wx,
499                         double wy,
500                         double &whirl,
501                         double &x,
502                         double &y)
503 {
504         double dx, dy;
505         double d, factor;
506         double dist;
507         double ang, sina, cosa;
508         int inside;
509
510 /* Distances to center, scaled */
511
512         dx = (wx - cen_x) * scale_x;
513         dy = (wy - cen_y) * scale_y;
514
515 /* Distance^2 to center of *circle* (scaled ellipse) */
516
517         d = dx * dx + dy * dy;
518
519 /*  If we are inside circle, then distort.
520  *  Else, just return the same position
521  */
522
523         inside = (d < radius2);
524
525         if(inside)
526     {
527         dist = sqrt(d / radius3) / radius;
528
529 /* Pinch */
530
531         factor = pow(sin(M_PI / 2 * dist), -pinch);
532
533         dx *= factor;
534         dy *= factor;
535
536 /* Whirl */
537
538         factor = 1.0 - dist;
539
540         ang = whirl * factor * factor;
541
542         sina = sin(ang);
543         cosa = cos(ang);
544
545         x = (cosa * dx - sina * dy) / scale_x + cen_x;
546         y = (sina * dx + cosa * dy) / scale_y + cen_y;
547     }
548
549         return inside;
550 }
551
552
553
554 #define GET_PIXEL(components, x, y, input_rows) \
555         input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
556
557
558
559
560
561
562 static float bilinear(double x, double y, double *values)
563 {
564         double m0, m1;
565         x = fmod(x, 1.0);
566         y = fmod(y, 1.0);
567
568         if(x < 0.0) x += 1.0;
569         if(y < 0.0) y += 1.0;
570
571         m0 = (double)values[0] + x * ((double)values[1] - values[0]);
572         m1 = (double)values[2] + x * ((double)values[3] - values[2]);
573         return m0 + y * (m1 - m0);
574 }
575
576
577
578
579
580 #define WHIRL_MACRO(type, max, components) \
581 { \
582         type **input_rows = (type**)plugin->input->get_rows(); \
583 /* Compiler error requires separate arrays */ \
584         double top_values[4], bot_values[4]; \
585         for( int i=0; i<4; ++i ) top_values[i] = bot_values[i] = 0; \
586         for(int row = pkg->row1 / 2; row <= (pkg->row2 + pkg->row1) / 2; row++) \
587         { \
588                 type *top_row = (type*)plugin->output->get_rows()[row]; \
589                 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
590                 type *top_p = top_row; \
591                 type *bot_p = bot_row + components * w - components; \
592                 type *pixel1; \
593                 type *pixel2; \
594                 type *pixel3; \
595                 type *pixel4; \
596  \
597                 for(int col = 0; col < w; col++) \
598                 { \
599                         if(calc_undistorted_coords(cen_x, \
600                                 cen_y, \
601                                 scale_x, \
602                                 scale_y, \
603                                 radius, \
604                                 radius2, \
605                                 radius3, \
606                                 pinch, \
607                                 col, \
608                                 row, \
609                                 whirl, \
610                                 cx, \
611                                 cy)) \
612                         { \
613 /* Inside distortion area */ \
614 /* Do top */ \
615                                 if(cx >= 0.0) \
616                                         ix = (int)cx; \
617                                 else \
618                                         ix = -((int)-cx + 1); \
619  \
620                                 if(cy >= 0.0) \
621                                         iy = (int)cy; \
622                                 else \
623                                         iy = -((int)-cy + 1); \
624  \
625                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
626                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
627                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
628                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
629  \
630                                 top_values[0] = pixel1[0]; \
631                                 top_values[1] = pixel2[0]; \
632                                 top_values[2] = pixel3[0]; \
633                                 top_values[3] = pixel4[0]; \
634                                 top_p[0] = (type)bilinear(cx, cy, top_values); \
635  \
636                                 top_values[0] = pixel1[1]; \
637                                 top_values[1] = pixel2[1]; \
638                                 top_values[2] = pixel3[1]; \
639                                 top_values[3] = pixel4[1]; \
640                                 top_p[1] = (type)bilinear(cx, cy, top_values); \
641  \
642                                 top_values[0] = pixel1[2]; \
643                                 top_values[1] = pixel2[2]; \
644                                 top_values[2] = pixel3[2]; \
645                                 top_values[3] = pixel4[2]; \
646                                 top_p[2] = (type)bilinear(cx, cy, top_values); \
647  \
648                                 if(components == 4) \
649                                 { \
650                                         top_values[0] = pixel1[3]; \
651                                         top_values[1] = pixel2[3]; \
652                                         top_values[2] = pixel3[3]; \
653                                         top_values[3] = pixel4[3]; \
654                                         top_p[3] = (type)bilinear(cx, cy, top_values); \
655                                 } \
656  \
657                                 top_p += components; \
658  \
659 /* Do bottom */ \
660                         cx = cen_x + (cen_x - cx); \
661                         cy = cen_y + (cen_y - cy); \
662  \
663                         if(cx >= 0.0) \
664                                         ix = (int)cx; \
665                         else \
666                                         ix = -((int)-cx + 1); \
667  \
668                         if(cy >= 0.0) \
669                                         iy = (int)cy; \
670                         else \
671                                         iy = -((int)-cy + 1); \
672  \
673                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
674                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
675                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
676                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
677  \
678  \
679  \
680                                 bot_values[0] = pixel1[0]; \
681                                 bot_values[1] = pixel2[0]; \
682                                 bot_values[2] = pixel3[0]; \
683                                 bot_values[3] = pixel4[0]; \
684                                 bot_p[0] = (type)bilinear(cx, cy, bot_values); \
685  \
686                                 bot_values[0] = pixel1[1]; \
687                                 bot_values[1] = pixel2[1]; \
688                                 bot_values[2] = pixel3[1]; \
689                                 bot_values[3] = pixel4[1]; \
690                                 bot_p[1] = (type)bilinear(cx, cy, bot_values); \
691  \
692                                 bot_values[0] = pixel1[2]; \
693                                 bot_values[1] = pixel2[2]; \
694                                 bot_values[2] = pixel3[2]; \
695                                 bot_values[3] = pixel4[2]; \
696                                 bot_p[2] = (type)bilinear(cx, cy, bot_values); \
697  \
698                                 if(components == 4) \
699                                 { \
700                                         bot_values[0] = pixel1[3]; \
701                                         bot_values[1] = pixel2[3]; \
702                                         bot_values[2] = pixel3[3]; \
703                                         bot_values[3] = pixel4[3]; \
704                                         bot_p[3] = (type)bilinear(cx, cy, bot_values); \
705                                 } \
706  \
707                                 bot_p -= components; \
708  \
709  \
710                         } \
711                         else \
712                         { \
713 /* Outside distortion area */ \
714 /* Do top */ \
715                                 top_p[0] = input_rows[row][components * col + 0]; \
716                                 top_p[1] = input_rows[row][components * col + 1]; \
717                                 top_p[2] = input_rows[row][components * col + 2]; \
718                                 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
719  \
720  \
721                                 top_p += components; \
722  \
723 /* Do bottom */ \
724                                 int bot_offset = w * components - col * components - components; \
725                                 int bot_row = h - 1 - row; \
726                                 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
727                                 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
728                                 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
729                                 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
730                                 bot_p -= components; \
731                         } \
732                 } \
733         } \
734 }
735
736 void WhirlUnit::process_package(LoadPackage *package)
737 {
738         WhirlPackage *pkg = (WhirlPackage*)package;
739         int w = plugin->input->get_w();
740         int h = plugin->input->get_h();
741         double whirl = plugin->config.angle * M_PI / 180;
742         double pinch = plugin->config.pinch / MAXPINCH;
743         double cx, cy;
744     int ix, iy;
745         double cen_x = (double)(w - 1) / 2.0;
746         double cen_y = (double)(h - 1) / 2.0;
747         double radius = MAX(w, h);
748         double radius3 = plugin->config.radius / MAXRADIUS;
749         double radius2 = radius * radius * radius3;
750         double scale_x;
751         double scale_y;
752
753
754 //printf("WhirlUnit::process_package 1 %f %f %f\n",
755 //      plugin->config.angle, plugin->config.pinch, plugin->config.radius);
756         if(w < h)
757         {
758         scale_x = (double)h / w;
759         scale_y = 1.0;
760         }
761         else
762         if(w > h)
763         {
764         scale_x = 1.0;
765         scale_y = (double)w / h;
766         }
767         else
768         {
769         scale_x = 1.0;
770         scale_y = 1.0;
771         }
772
773
774
775         switch(plugin->input->get_color_model())
776         {
777                 case BC_RGB_FLOAT:
778                         WHIRL_MACRO(float, 1, 3);
779                         break;
780                 case BC_RGB888:
781                 case BC_YUV888:
782                         WHIRL_MACRO(unsigned char, 0xff, 3);
783                         break;
784                 case BC_RGBA_FLOAT:
785                         WHIRL_MACRO(float, 1, 4);
786                         break;
787                 case BC_RGBA8888:
788                 case BC_YUVA8888:
789                         WHIRL_MACRO(unsigned char, 0xff, 4);
790                         break;
791                 case BC_RGB161616:
792                 case BC_YUV161616:
793                         WHIRL_MACRO(uint16_t, 0xffff, 3);
794                         break;
795                 case BC_RGBA16161616:
796                 case BC_YUVA16161616:
797                         WHIRL_MACRO(uint16_t, 0xffff, 4);
798                         break;
799
800         }
801 }
802
803
804
805
806
807
808
809 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
810 // : LoadServer(1, 1)
811  : LoadServer(cpus, cpus)
812 {
813         this->plugin = plugin;
814 }
815 void WhirlEngine::init_packages()
816 {
817         for(int i = 0; i < LoadServer::get_total_packages(); i++)
818         {
819                 WhirlPackage *pkg = (WhirlPackage*)get_package(i);
820                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
821                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
822         }
823
824 }
825
826 LoadClient* WhirlEngine::new_client()
827 {
828         return new WhirlUnit(plugin, this);
829 }
830
831 LoadPackage* WhirlEngine::new_package()
832 {
833         return new WhirlPackage;
834 }
835