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