upgrade libx265 to 3.1.2, tweak menupopup placement
[goodguy/cinelerra.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 "theme.h"
32 #include "vframe.h"
33
34
35
36 #include <stdint.h>
37 #include <string.h>
38
39 class WhirlEffect;
40 class WhirlWindow;
41 class WhirlEngine;
42 class WhirlReset;
43 class WhirlDefaultSettings;
44 class WhirlSliderClr;
45
46 #define MAXRADIUS 100
47 #define MAXPINCH 100
48
49 #define RESET_DEFAULT_SETTINGS 10
50 #define RESET_ALL    0
51 #define RESET_RADIUS 1
52 #define RESET_PINCH  2
53 #define RESET_ANGLE  3
54
55
56 class WhirlConfig
57 {
58 public:
59         WhirlConfig();
60         void reset(int clear);
61
62         void copy_from(WhirlConfig &src);
63         int equivalent(WhirlConfig &src);
64         void interpolate(WhirlConfig &prev,
65                 WhirlConfig &next,
66                 long prev_frame,
67                 long next_frame,
68                 long current_frame);
69
70         float angle;
71         float pinch;
72         float radius;
73 };
74
75
76 class WhirlAngle : public BC_FSlider
77 {
78 public:
79         WhirlAngle(WhirlEffect *plugin, int x, int y);
80         int handle_event();
81         WhirlEffect *plugin;
82 };
83
84
85
86 class WhirlPinch : public BC_FSlider
87 {
88 public:
89         WhirlPinch(WhirlEffect *plugin, int x, int y);
90         int handle_event();
91         WhirlEffect *plugin;
92 };
93
94
95
96 class WhirlRadius : public BC_FSlider
97 {
98 public:
99         WhirlRadius(WhirlEffect *plugin, int x, int y);
100         int handle_event();
101         WhirlEffect *plugin;
102 };
103
104
105 class WhirlReset : public BC_GenericButton
106 {
107 public:
108         WhirlReset(WhirlEffect *plugin, WhirlWindow *window, int x, int y);
109         ~WhirlReset();
110         int handle_event();
111         WhirlEffect *plugin;
112         WhirlWindow *window;
113 };
114
115 class WhirlDefaultSettings : public BC_GenericButton
116 {
117 public:
118         WhirlDefaultSettings(WhirlEffect *plugin, WhirlWindow *window, int x, int y, int w);
119         ~WhirlDefaultSettings();
120         int handle_event();
121         WhirlEffect *plugin;
122         WhirlWindow *window;
123 };
124
125 class WhirlSliderClr : public BC_Button
126 {
127 public:
128         WhirlSliderClr(WhirlEffect *plugin, WhirlWindow *window, int x, int y, int w, int clear);
129         ~WhirlSliderClr();
130         int handle_event();
131         WhirlEffect *plugin;
132         WhirlWindow *window;
133         int clear;
134 };
135
136
137
138 class WhirlWindow : public PluginClientWindow
139 {
140 public:
141         WhirlWindow(WhirlEffect *plugin);
142         void create_objects();
143         void update_gui(int clear);
144         WhirlEffect *plugin;
145         WhirlRadius *radius;
146         WhirlPinch *pinch;
147         WhirlAngle *angle;
148         WhirlReset *reset;
149         WhirlDefaultSettings *default_settings;
150         WhirlSliderClr *radiusClr, *pinchClr, *angleClr;
151 };
152
153
154
155
156
157 class WhirlPackage : public LoadPackage
158 {
159 public:
160         WhirlPackage();
161         int row1, row2;
162 };
163
164 class WhirlUnit : public LoadClient
165 {
166 public:
167         WhirlUnit(WhirlEffect *plugin, WhirlEngine *server);
168         void process_package(LoadPackage *package);
169         WhirlEngine *server;
170         WhirlEffect *plugin;
171
172 };
173
174
175 class WhirlEngine : public LoadServer
176 {
177 public:
178         WhirlEngine(WhirlEffect *plugin, int cpus);
179         void init_packages();
180         LoadClient* new_client();
181         LoadPackage* new_package();
182         WhirlEffect *plugin;
183 };
184
185
186
187 class WhirlEffect : public PluginVClient
188 {
189 public:
190         WhirlEffect(PluginServer *server);
191         ~WhirlEffect();
192
193         PLUGIN_CLASS_MEMBERS(WhirlConfig)
194         int process_realtime(VFrame *input, VFrame *output);
195         int is_realtime();
196         void update_gui();
197         void save_data(KeyFrame *keyframe);
198         void read_data(KeyFrame *keyframe);
199
200         WhirlEngine *engine;
201         VFrame *temp_frame;
202         VFrame *input, *output;
203         int need_reconfigure;
204 };
205
206
207
208
209
210
211 REGISTER_PLUGIN(WhirlEffect)
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226 WhirlConfig::WhirlConfig()
227 {
228         reset(RESET_ALL);
229 }
230
231 void WhirlConfig::reset(int clear)
232 {
233         switch(clear) {
234                 case RESET_ALL :
235                         radius = 0.0;
236                         pinch = 0.0;
237                         angle = 0.0;
238                         break;
239                 case RESET_RADIUS : radius = 0.0;
240                         break;
241                 case RESET_PINCH : pinch = 0.0;
242                         break;
243                 case RESET_ANGLE : angle = 0.0;
244                         break;
245                 case RESET_DEFAULT_SETTINGS :
246                 default:
247                         radius = 50.0;
248                         pinch = 10.0;
249                         angle = 180.0;
250                         break;
251         }
252 }
253
254 void WhirlConfig::copy_from(WhirlConfig &src)
255 {
256         this->angle = src.angle;
257         this->pinch = src.pinch;
258         this->radius = src.radius;
259 }
260
261 int WhirlConfig::equivalent(WhirlConfig &src)
262 {
263         return EQUIV(this->angle, src.angle) &&
264                 EQUIV(this->pinch, src.pinch) &&
265                 EQUIV(this->radius, src.radius);
266 }
267
268 void WhirlConfig::interpolate(WhirlConfig &prev,
269         WhirlConfig &next,
270         long prev_frame,
271         long next_frame,
272         long current_frame)
273 {
274         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
275         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
276
277         this->angle = prev.angle * prev_scale + next.angle * next_scale;
278         this->pinch = prev.pinch * prev_scale + next.pinch * next_scale;
279         this->radius = prev.radius * prev_scale + next.radius * next_scale;
280 }
281
282
283
284
285
286
287
288
289
290
291 WhirlWindow::WhirlWindow(WhirlEffect *plugin)
292  : PluginClientWindow(plugin, 280, 195, 280, 195, 0)
293 {
294         this->plugin = plugin;
295 }
296
297
298
299 void WhirlWindow::create_objects()
300 {
301         int x = 10, y = 10;
302         int x1 = 0; int clrBtn_w = 50;
303         int defaultBtn_w = 100;
304
305         add_subwindow(new BC_Title(x, y, _("Radius")));
306         y += 20;
307         add_subwindow(radius = new WhirlRadius(plugin, x, y));
308         x1 = x + radius->get_w() + 10;
309         add_subwindow(radiusClr = new WhirlSliderClr(plugin, this, x1, y, clrBtn_w, RESET_RADIUS));
310
311         y += 30;
312         add_subwindow(new BC_Title(x, y, _("Pinch")));
313         y += 20;
314         add_subwindow(pinch = new WhirlPinch(plugin, x, y));
315         add_subwindow(pinchClr = new WhirlSliderClr(plugin, this, x1, y, clrBtn_w, RESET_PINCH));
316
317         y += 30;
318         add_subwindow(new BC_Title(x, y, _("Angle")));
319         y += 20;
320         add_subwindow(angle = new WhirlAngle(plugin, x, y));
321         add_subwindow(angleClr = new WhirlSliderClr(plugin, this, x1, y, clrBtn_w, RESET_ANGLE));
322
323         y += 35;
324         add_subwindow(reset = new WhirlReset(plugin, this, x, y));
325         add_subwindow(default_settings = new WhirlDefaultSettings(plugin, this,
326                 (280 - 10 - defaultBtn_w), y, defaultBtn_w));
327
328         show_window();
329         flush();
330 }
331
332
333 // for Reset button
334 void WhirlWindow::update_gui(int clear)
335 {
336         switch(clear) {
337                 case RESET_RADIUS : radius->update(plugin->config.radius);
338                         break;
339                 case RESET_PINCH : pinch->update(plugin->config.pinch);
340                         break;
341                 case RESET_ANGLE : angle->update(plugin->config.angle);
342                         break;
343                 case RESET_ALL :
344                 case RESET_DEFAULT_SETTINGS :
345                 default:
346                         radius->update(plugin->config.radius);
347                         pinch->update(plugin->config.pinch);
348                         angle->update(plugin->config.angle);
349                         break;
350         }
351 }
352
353
354
355
356
357
358
359
360
361
362
363 WhirlAngle::WhirlAngle(WhirlEffect *plugin, int x, int y)
364  : BC_FSlider(x,
365                 y,
366                 0,
367                 200,
368                 200,
369                 (float)0,
370                 (float)360,
371                 plugin->config.angle)
372 {
373         this->plugin = plugin;
374 }
375 int WhirlAngle::handle_event()
376 {
377         plugin->config.angle = get_value();
378         plugin->send_configure_change();
379         return 1;
380 }
381
382
383
384
385 WhirlPinch::WhirlPinch(WhirlEffect *plugin, int x, int y)
386  : BC_FSlider(x,
387                 y,
388                 0,
389                 200,
390                 200,
391                 (float)0,
392                 (float)MAXPINCH,
393                 plugin->config.pinch)
394 {
395         this->plugin = plugin;
396 }
397 int WhirlPinch::handle_event()
398 {
399         plugin->config.pinch = get_value();
400         plugin->send_configure_change();
401         return 1;
402 }
403
404
405
406
407 WhirlRadius::WhirlRadius(WhirlEffect *plugin, int x, int y)
408  : BC_FSlider(x,
409                 y,
410                 0,
411                 200,
412                 200,
413                 (float)0,
414                 (float)MAXRADIUS,
415                 plugin->config.radius)
416 {
417         this->plugin = plugin;
418 }
419 int WhirlRadius::handle_event()
420 {
421         plugin->config.radius = get_value();
422         plugin->send_configure_change();
423         return 1;
424 }
425
426
427
428 WhirlReset::WhirlReset(WhirlEffect *plugin, WhirlWindow *window, int x, int y)
429  : BC_GenericButton(x, y, _("Reset"))
430 {
431         this->plugin = plugin;
432         this->window = window;
433 }
434 WhirlReset::~WhirlReset()
435 {
436 }
437 int WhirlReset::handle_event()
438 {
439         plugin->config.reset(RESET_ALL);
440         window->update_gui(RESET_ALL);
441         plugin->send_configure_change();
442         return 1;
443 }
444
445 WhirlDefaultSettings::WhirlDefaultSettings(WhirlEffect *plugin, WhirlWindow *window, int x, int y, int w)
446  : BC_GenericButton(x, y, w, _("Default"))
447 {
448         this->plugin = plugin;
449         this->window = window;
450 }
451 WhirlDefaultSettings::~WhirlDefaultSettings()
452 {
453 }
454 int WhirlDefaultSettings::handle_event()
455 {
456         plugin->config.reset(RESET_DEFAULT_SETTINGS);
457         window->update_gui(RESET_DEFAULT_SETTINGS);
458         plugin->send_configure_change();
459         return 1;
460 }
461
462 WhirlSliderClr::WhirlSliderClr(WhirlEffect *plugin, WhirlWindow *window, int x, int y, int w, int clear)
463  : BC_Button(x, y, w, plugin->get_theme()->get_image_set("reset_button"))
464 {
465         this->plugin = plugin;
466         this->window = window;
467         this->clear = clear;
468 }
469 WhirlSliderClr::~WhirlSliderClr()
470 {
471 }
472 int WhirlSliderClr::handle_event()
473 {
474         // clear==1 ==> Radius slider
475         // clear==2 ==> Pinch slider
476         // clear==3 ==> Angle slider
477         plugin->config.reset(clear);
478         window->update_gui(clear);
479         plugin->send_configure_change();
480         return 1;
481 }
482
483
484
485
486
487 WhirlEffect::WhirlEffect(PluginServer *server)
488  : PluginVClient(server)
489 {
490         need_reconfigure = 1;
491         engine = 0;
492         temp_frame = 0;
493
494 }
495
496 WhirlEffect::~WhirlEffect()
497 {
498
499         if(engine) delete engine;
500         if(temp_frame) delete temp_frame;
501 }
502
503
504
505
506
507
508 const char* WhirlEffect::plugin_title() { return N_("Whirl"); }
509 int WhirlEffect::is_realtime() { return 1; }
510
511
512
513 NEW_WINDOW_MACRO(WhirlEffect, WhirlWindow)
514
515
516 void WhirlEffect::update_gui()
517 {
518         if(thread)
519         {
520                 load_configuration();
521                 thread->window->lock_window();
522                 ((WhirlWindow*)thread->window)->angle->update(config.angle);
523                 ((WhirlWindow*)thread->window)->pinch->update(config.pinch);
524                 ((WhirlWindow*)thread->window)->radius->update(config.radius);
525                 thread->window->unlock_window();
526         }
527 }
528
529 LOAD_CONFIGURATION_MACRO(WhirlEffect, WhirlConfig)
530
531
532
533
534 void WhirlEffect::save_data(KeyFrame *keyframe)
535 {
536         FileXML output;
537
538 // cause data to be stored directly in text
539         output.set_shared_output(keyframe->xbuf);
540
541         output.tag.set_title("WHIRL");
542         output.tag.set_property("ANGLE", config.angle);
543         output.tag.set_property("PINCH", config.pinch);
544         output.tag.set_property("RADIUS", config.radius);
545         output.append_tag();
546         output.tag.set_title("/WHIRL");
547         output.append_tag();
548         output.append_newline();
549         output.terminate_string();
550 // data is now in *text
551 }
552
553 void WhirlEffect::read_data(KeyFrame *keyframe)
554 {
555         FileXML input;
556
557         input.set_shared_input(keyframe->xbuf);
558
559         int result = 0;
560
561         while(!result)
562         {
563                 result = input.read_tag();
564
565                 if(!result)
566                 {
567                         if(input.tag.title_is("WHIRL"))
568                         {
569                                 config.angle = input.tag.get_property("ANGLE", config.angle);
570                                 config.pinch = input.tag.get_property("PINCH", config.pinch);
571                                 config.radius = input.tag.get_property("RADIUS", config.radius);
572                         }
573                 }
574         }
575 }
576
577 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
578 {
579         need_reconfigure |= load_configuration();
580         this->input = input;
581         this->output = output;
582
583         if(EQUIV(config.angle, 0) ||
584                 (EQUIV(config.radius, 0) && EQUIV(config.pinch, 0)))
585         {
586                 if(input->get_rows()[0] != output->get_rows()[0])
587                         output->copy_from(input);
588         }
589         else
590         {
591                 if(input->get_rows()[0] == output->get_rows()[0])
592                 {
593                         if(!temp_frame) temp_frame = new VFrame(input->get_w(), input->get_h(),
594                                 input->get_color_model(), 0);
595                         temp_frame->copy_from(input);
596                         this->input = temp_frame;
597                 }
598
599                 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
600
601                 engine->process_packages();
602         }
603         return 0;
604 }
605
606
607
608
609
610
611
612
613 WhirlPackage::WhirlPackage()
614  : LoadPackage()
615 {
616 }
617
618
619
620 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
621  : LoadClient(server)
622 {
623         this->plugin = plugin;
624 }
625
626
627
628 static int calc_undistorted_coords(double cen_x,
629                         double cen_y,
630                         double scale_x,
631                         double scale_y,
632                         double radius,
633                         double radius2,
634                         double radius3,
635                         double pinch,
636                         double wx,
637                         double wy,
638                         double &whirl,
639                         double &x,
640                         double &y)
641 {
642         double dx, dy;
643         double d, factor;
644         double dist;
645         double ang, sina, cosa;
646         int inside;
647
648 /* Distances to center, scaled */
649
650         dx = (wx - cen_x) * scale_x;
651         dy = (wy - cen_y) * scale_y;
652
653 /* Distance^2 to center of *circle* (scaled ellipse) */
654
655         d = dx * dx + dy * dy;
656
657 /*  If we are inside circle, then distort.
658  *  Else, just return the same position
659  */
660
661         inside = (d < radius2);
662
663         if(inside)
664     {
665         dist = sqrt(d / radius3) / radius;
666
667 /* Pinch */
668
669         factor = pow(sin(M_PI / 2 * dist), -pinch);
670
671         dx *= factor;
672         dy *= factor;
673
674 /* Whirl */
675
676         factor = 1.0 - dist;
677
678         ang = whirl * factor * factor;
679
680         sina = sin(ang);
681         cosa = cos(ang);
682
683         x = (cosa * dx - sina * dy) / scale_x + cen_x;
684         y = (sina * dx + cosa * dy) / scale_y + cen_y;
685     }
686
687         return inside;
688 }
689
690
691
692 #define GET_PIXEL(components, x, y, input_rows) \
693         input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
694
695
696
697
698
699
700 static float bilinear(double x, double y, double *values)
701 {
702         double m0, m1;
703         x = fmod(x, 1.0);
704         y = fmod(y, 1.0);
705
706         if(x < 0.0) x += 1.0;
707         if(y < 0.0) y += 1.0;
708
709         m0 = (double)values[0] + x * ((double)values[1] - values[0]);
710         m1 = (double)values[2] + x * ((double)values[3] - values[2]);
711         return m0 + y * (m1 - m0);
712 }
713
714
715
716
717
718 #define WHIRL_MACRO(type, max, components) \
719 { \
720         type **input_rows = (type**)plugin->input->get_rows(); \
721 /* Compiler error requires separate arrays */ \
722         double top_values[4], bot_values[4]; \
723         for( int i=0; i<4; ++i ) top_values[i] = bot_values[i] = 0; \
724         for(int row = pkg->row1 / 2; row <= (pkg->row2 + pkg->row1) / 2; row++) \
725         { \
726                 type *top_row = (type*)plugin->output->get_rows()[row]; \
727                 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
728                 type *top_p = top_row; \
729                 type *bot_p = bot_row + components * w - components; \
730                 type *pixel1; \
731                 type *pixel2; \
732                 type *pixel3; \
733                 type *pixel4; \
734  \
735                 for(int col = 0; col < w; col++) \
736                 { \
737                         if(calc_undistorted_coords(cen_x, \
738                                 cen_y, \
739                                 scale_x, \
740                                 scale_y, \
741                                 radius, \
742                                 radius2, \
743                                 radius3, \
744                                 pinch, \
745                                 col, \
746                                 row, \
747                                 whirl, \
748                                 cx, \
749                                 cy)) \
750                         { \
751 /* Inside distortion area */ \
752 /* Do top */ \
753                                 if(cx >= 0.0) \
754                                         ix = (int)cx; \
755                                 else \
756                                         ix = -((int)-cx + 1); \
757  \
758                                 if(cy >= 0.0) \
759                                         iy = (int)cy; \
760                                 else \
761                                         iy = -((int)-cy + 1); \
762  \
763                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
764                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
765                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
766                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
767  \
768                                 top_values[0] = pixel1[0]; \
769                                 top_values[1] = pixel2[0]; \
770                                 top_values[2] = pixel3[0]; \
771                                 top_values[3] = pixel4[0]; \
772                                 top_p[0] = (type)bilinear(cx, cy, top_values); \
773  \
774                                 top_values[0] = pixel1[1]; \
775                                 top_values[1] = pixel2[1]; \
776                                 top_values[2] = pixel3[1]; \
777                                 top_values[3] = pixel4[1]; \
778                                 top_p[1] = (type)bilinear(cx, cy, top_values); \
779  \
780                                 top_values[0] = pixel1[2]; \
781                                 top_values[1] = pixel2[2]; \
782                                 top_values[2] = pixel3[2]; \
783                                 top_values[3] = pixel4[2]; \
784                                 top_p[2] = (type)bilinear(cx, cy, top_values); \
785  \
786                                 if(components == 4) \
787                                 { \
788                                         top_values[0] = pixel1[3]; \
789                                         top_values[1] = pixel2[3]; \
790                                         top_values[2] = pixel3[3]; \
791                                         top_values[3] = pixel4[3]; \
792                                         top_p[3] = (type)bilinear(cx, cy, top_values); \
793                                 } \
794  \
795                                 top_p += components; \
796  \
797 /* Do bottom */ \
798                         cx = cen_x + (cen_x - cx); \
799                         cy = cen_y + (cen_y - cy); \
800  \
801                         if(cx >= 0.0) \
802                                         ix = (int)cx; \
803                         else \
804                                         ix = -((int)-cx + 1); \
805  \
806                         if(cy >= 0.0) \
807                                         iy = (int)cy; \
808                         else \
809                                         iy = -((int)-cy + 1); \
810  \
811                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
812                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
813                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
814                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
815  \
816  \
817  \
818                                 bot_values[0] = pixel1[0]; \
819                                 bot_values[1] = pixel2[0]; \
820                                 bot_values[2] = pixel3[0]; \
821                                 bot_values[3] = pixel4[0]; \
822                                 bot_p[0] = (type)bilinear(cx, cy, bot_values); \
823  \
824                                 bot_values[0] = pixel1[1]; \
825                                 bot_values[1] = pixel2[1]; \
826                                 bot_values[2] = pixel3[1]; \
827                                 bot_values[3] = pixel4[1]; \
828                                 bot_p[1] = (type)bilinear(cx, cy, bot_values); \
829  \
830                                 bot_values[0] = pixel1[2]; \
831                                 bot_values[1] = pixel2[2]; \
832                                 bot_values[2] = pixel3[2]; \
833                                 bot_values[3] = pixel4[2]; \
834                                 bot_p[2] = (type)bilinear(cx, cy, bot_values); \
835  \
836                                 if(components == 4) \
837                                 { \
838                                         bot_values[0] = pixel1[3]; \
839                                         bot_values[1] = pixel2[3]; \
840                                         bot_values[2] = pixel3[3]; \
841                                         bot_values[3] = pixel4[3]; \
842                                         bot_p[3] = (type)bilinear(cx, cy, bot_values); \
843                                 } \
844  \
845                                 bot_p -= components; \
846  \
847  \
848                         } \
849                         else \
850                         { \
851 /* Outside distortion area */ \
852 /* Do top */ \
853                                 top_p[0] = input_rows[row][components * col + 0]; \
854                                 top_p[1] = input_rows[row][components * col + 1]; \
855                                 top_p[2] = input_rows[row][components * col + 2]; \
856                                 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
857  \
858  \
859                                 top_p += components; \
860  \
861 /* Do bottom */ \
862                                 int bot_offset = w * components - col * components - components; \
863                                 int bot_row = h - 1 - row; \
864                                 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
865                                 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
866                                 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
867                                 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
868                                 bot_p -= components; \
869                         } \
870                 } \
871         } \
872 }
873
874 void WhirlUnit::process_package(LoadPackage *package)
875 {
876         WhirlPackage *pkg = (WhirlPackage*)package;
877         int w = plugin->input->get_w();
878         int h = plugin->input->get_h();
879         double whirl = plugin->config.angle * M_PI / 180;
880         double pinch = plugin->config.pinch / MAXPINCH;
881         double cx, cy;
882     int ix, iy;
883         double cen_x = (double)(w - 1) / 2.0;
884         double cen_y = (double)(h - 1) / 2.0;
885         double radius = MAX(w, h);
886         double radius3 = plugin->config.radius / MAXRADIUS;
887         double radius2 = radius * radius * radius3;
888         double scale_x;
889         double scale_y;
890
891
892 //printf("WhirlUnit::process_package 1 %f %f %f\n",
893 //      plugin->config.angle, plugin->config.pinch, plugin->config.radius);
894         if(w < h)
895         {
896         scale_x = (double)h / w;
897         scale_y = 1.0;
898         }
899         else
900         if(w > h)
901         {
902         scale_x = 1.0;
903         scale_y = (double)w / h;
904         }
905         else
906         {
907         scale_x = 1.0;
908         scale_y = 1.0;
909         }
910
911
912
913         switch(plugin->input->get_color_model())
914         {
915                 case BC_RGB_FLOAT:
916                         WHIRL_MACRO(float, 1, 3);
917                         break;
918                 case BC_RGB888:
919                 case BC_YUV888:
920                         WHIRL_MACRO(unsigned char, 0xff, 3);
921                         break;
922                 case BC_RGBA_FLOAT:
923                         WHIRL_MACRO(float, 1, 4);
924                         break;
925                 case BC_RGBA8888:
926                 case BC_YUVA8888:
927                         WHIRL_MACRO(unsigned char, 0xff, 4);
928                         break;
929                 case BC_RGB161616:
930                 case BC_YUV161616:
931                         WHIRL_MACRO(uint16_t, 0xffff, 3);
932                         break;
933                 case BC_RGBA16161616:
934                 case BC_YUVA16161616:
935                         WHIRL_MACRO(uint16_t, 0xffff, 4);
936                         break;
937
938         }
939 }
940
941
942
943
944
945
946
947 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
948 // : LoadServer(1, 1)
949  : LoadServer(cpus, cpus)
950 {
951         this->plugin = plugin;
952 }
953 void WhirlEngine::init_packages()
954 {
955         for(int i = 0; i < LoadServer::get_total_packages(); i++)
956         {
957                 WhirlPackage *pkg = (WhirlPackage*)get_package(i);
958                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
959                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
960         }
961
962 }
963
964 LoadClient* WhirlEngine::new_client()
965 {
966         return new WhirlUnit(plugin, this);
967 }
968
969 LoadPackage* WhirlEngine::new_package()
970 {
971         return new WhirlPackage;
972 }
973