ac598991d75abeb9d93fc8aaff8a1466a60deae0
[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, xS(280), yS(195), xS(280), yS(195), 0)
293 {
294         this->plugin = plugin;
295 }
296
297
298
299 void WhirlWindow::create_objects()
300 {
301         int xs10 = xS(10), xs50 = xS(50), xs100 = xS(100);
302         int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys35 = yS(35);
303         int x = xs10, y = ys10;
304         int x1 = 0; int clrBtn_w = xs50;
305         int defaultBtn_w = xs100;
306
307         add_subwindow(new BC_Title(x, y, _("Radius")));
308         y += ys20;
309         add_subwindow(radius = new WhirlRadius(plugin, x, y));
310         x1 = x + radius->get_w() + xs10;
311         add_subwindow(radiusClr = new WhirlSliderClr(plugin, this, x1, y, clrBtn_w, RESET_RADIUS));
312
313         y += ys30;
314         add_subwindow(new BC_Title(x, y, _("Pinch")));
315         y += ys20;
316         add_subwindow(pinch = new WhirlPinch(plugin, x, y));
317         add_subwindow(pinchClr = new WhirlSliderClr(plugin, this, x1, y, clrBtn_w, RESET_PINCH));
318
319         y += ys30;
320         add_subwindow(new BC_Title(x, y, _("Angle")));
321         y += ys20;
322         add_subwindow(angle = new WhirlAngle(plugin, x, y));
323         add_subwindow(angleClr = new WhirlSliderClr(plugin, this, x1, y, clrBtn_w, RESET_ANGLE));
324
325         y += ys35;
326         add_subwindow(reset = new WhirlReset(plugin, this, x, y));
327         add_subwindow(default_settings = new WhirlDefaultSettings(plugin, this,
328                 (xS(280) - xs10 - defaultBtn_w), y, defaultBtn_w));
329         show_window();
330         flush();
331 }
332
333
334 // for Reset button
335 void WhirlWindow::update_gui(int clear)
336 {
337         switch(clear) {
338                 case RESET_RADIUS : radius->update(plugin->config.radius);
339                         break;
340                 case RESET_PINCH : pinch->update(plugin->config.pinch);
341                         break;
342                 case RESET_ANGLE : angle->update(plugin->config.angle);
343                         break;
344                 case RESET_ALL :
345                 case RESET_DEFAULT_SETTINGS :
346                 default:
347                         radius->update(plugin->config.radius);
348                         pinch->update(plugin->config.pinch);
349                         angle->update(plugin->config.angle);
350                         break;
351         }
352 }
353
354
355
356
357
358
359
360
361
362
363
364 WhirlAngle::WhirlAngle(WhirlEffect *plugin, int x, int y)
365  : BC_FSlider(x,
366                 y,
367                 0,
368                 xS(200),
369                 yS(200),
370                 (float)0,
371                 (float)360,
372                 plugin->config.angle)
373 {
374         this->plugin = plugin;
375 }
376 int WhirlAngle::handle_event()
377 {
378         plugin->config.angle = get_value();
379         plugin->send_configure_change();
380         return 1;
381 }
382
383
384
385
386 WhirlPinch::WhirlPinch(WhirlEffect *plugin, int x, int y)
387  : BC_FSlider(x,
388                 y,
389                 0,
390                 xS(200),
391                 yS(200),
392                 (float)0,
393                 (float)MAXPINCH,
394                 plugin->config.pinch)
395 {
396         this->plugin = plugin;
397 }
398 int WhirlPinch::handle_event()
399 {
400         plugin->config.pinch = get_value();
401         plugin->send_configure_change();
402         return 1;
403 }
404
405
406
407
408 WhirlRadius::WhirlRadius(WhirlEffect *plugin, int x, int y)
409  : BC_FSlider(x,
410                 y,
411                 0,
412                 xS(200),
413                 yS(200),
414                 (float)0,
415                 (float)MAXRADIUS,
416                 plugin->config.radius)
417 {
418         this->plugin = plugin;
419 }
420 int WhirlRadius::handle_event()
421 {
422         plugin->config.radius = get_value();
423         plugin->send_configure_change();
424         return 1;
425 }
426
427
428
429 WhirlReset::WhirlReset(WhirlEffect *plugin, WhirlWindow *window, int x, int y)
430  : BC_GenericButton(x, y, _("Reset"))
431 {
432         this->plugin = plugin;
433         this->window = window;
434 }
435 WhirlReset::~WhirlReset()
436 {
437 }
438 int WhirlReset::handle_event()
439 {
440         plugin->config.reset(RESET_ALL);
441         window->update_gui(RESET_ALL);
442         plugin->send_configure_change();
443         return 1;
444 }
445
446 WhirlDefaultSettings::WhirlDefaultSettings(WhirlEffect *plugin, WhirlWindow *window, int x, int y, int w)
447  : BC_GenericButton(x, y, w, _("Default"))
448 {
449         this->plugin = plugin;
450         this->window = window;
451 }
452 WhirlDefaultSettings::~WhirlDefaultSettings()
453 {
454 }
455 int WhirlDefaultSettings::handle_event()
456 {
457         plugin->config.reset(RESET_DEFAULT_SETTINGS);
458         window->update_gui(RESET_DEFAULT_SETTINGS);
459         plugin->send_configure_change();
460         return 1;
461 }
462
463 WhirlSliderClr::WhirlSliderClr(WhirlEffect *plugin, WhirlWindow *window, int x, int y, int w, int clear)
464  : BC_Button(x, y, w, plugin->get_theme()->get_image_set("reset_button"))
465 {
466         this->plugin = plugin;
467         this->window = window;
468         this->clear = clear;
469 }
470 WhirlSliderClr::~WhirlSliderClr()
471 {
472 }
473 int WhirlSliderClr::handle_event()
474 {
475         // clear==1 ==> Radius slider
476         // clear==2 ==> Pinch slider
477         // clear==3 ==> Angle slider
478         plugin->config.reset(clear);
479         window->update_gui(clear);
480         plugin->send_configure_change();
481         return 1;
482 }
483
484
485
486
487
488 WhirlEffect::WhirlEffect(PluginServer *server)
489  : PluginVClient(server)
490 {
491         need_reconfigure = 1;
492         engine = 0;
493         temp_frame = 0;
494
495 }
496
497 WhirlEffect::~WhirlEffect()
498 {
499
500         if(engine) delete engine;
501         if(temp_frame) delete temp_frame;
502 }
503
504
505
506
507
508
509 const char* WhirlEffect::plugin_title() { return N_("Whirl"); }
510 int WhirlEffect::is_realtime() { return 1; }
511
512
513
514 NEW_WINDOW_MACRO(WhirlEffect, WhirlWindow)
515
516
517 void WhirlEffect::update_gui()
518 {
519         if(thread)
520         {
521                 load_configuration();
522                 thread->window->lock_window();
523                 ((WhirlWindow*)thread->window)->angle->update(config.angle);
524                 ((WhirlWindow*)thread->window)->pinch->update(config.pinch);
525                 ((WhirlWindow*)thread->window)->radius->update(config.radius);
526                 thread->window->unlock_window();
527         }
528 }
529
530 LOAD_CONFIGURATION_MACRO(WhirlEffect, WhirlConfig)
531
532
533
534
535 void WhirlEffect::save_data(KeyFrame *keyframe)
536 {
537         FileXML output;
538
539 // cause data to be stored directly in text
540         output.set_shared_output(keyframe->xbuf);
541
542         output.tag.set_title("WHIRL");
543         output.tag.set_property("ANGLE", config.angle);
544         output.tag.set_property("PINCH", config.pinch);
545         output.tag.set_property("RADIUS", config.radius);
546         output.append_tag();
547         output.tag.set_title("/WHIRL");
548         output.append_tag();
549         output.append_newline();
550         output.terminate_string();
551 // data is now in *text
552 }
553
554 void WhirlEffect::read_data(KeyFrame *keyframe)
555 {
556         FileXML input;
557
558         input.set_shared_input(keyframe->xbuf);
559
560         int result = 0;
561
562         while(!result)
563         {
564                 result = input.read_tag();
565
566                 if(!result)
567                 {
568                         if(input.tag.title_is("WHIRL"))
569                         {
570                                 config.angle = input.tag.get_property("ANGLE", config.angle);
571                                 config.pinch = input.tag.get_property("PINCH", config.pinch);
572                                 config.radius = input.tag.get_property("RADIUS", config.radius);
573                         }
574                 }
575         }
576 }
577
578 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
579 {
580         need_reconfigure |= load_configuration();
581         this->input = input;
582         this->output = output;
583
584         if(EQUIV(config.angle, 0) ||
585                 (EQUIV(config.radius, 0) && EQUIV(config.pinch, 0)))
586         {
587                 if(input->get_rows()[0] != output->get_rows()[0])
588                         output->copy_from(input);
589         }
590         else
591         {
592                 if(input->get_rows()[0] == output->get_rows()[0])
593                 {
594                         if(!temp_frame) temp_frame = new VFrame(input->get_w(), input->get_h(),
595                                 input->get_color_model(), 0);
596                         temp_frame->copy_from(input);
597                         this->input = temp_frame;
598                 }
599
600                 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
601
602                 engine->process_packages();
603         }
604         return 0;
605 }
606
607
608
609
610
611
612
613
614 WhirlPackage::WhirlPackage()
615  : LoadPackage()
616 {
617 }
618
619
620
621 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
622  : LoadClient(server)
623 {
624         this->plugin = plugin;
625 }
626
627
628
629 static int calc_undistorted_coords(double cen_x,
630                         double cen_y,
631                         double scale_x,
632                         double scale_y,
633                         double radius,
634                         double radius2,
635                         double radius3,
636                         double pinch,
637                         double wx,
638                         double wy,
639                         double &whirl,
640                         double &x,
641                         double &y)
642 {
643         double dx, dy;
644         double d, factor;
645         double dist;
646         double ang, sina, cosa;
647         int inside;
648
649 /* Distances to center, scaled */
650
651         dx = (wx - cen_x) * scale_x;
652         dy = (wy - cen_y) * scale_y;
653
654 /* Distance^2 to center of *circle* (scaled ellipse) */
655
656         d = dx * dx + dy * dy;
657
658 /*  If we are inside circle, then distort.
659  *  Else, just return the same position
660  */
661
662         inside = (d < radius2);
663
664         if(inside)
665     {
666         dist = sqrt(d / radius3) / radius;
667
668 /* Pinch */
669
670         factor = pow(sin(M_PI / 2 * dist), -pinch);
671
672         dx *= factor;
673         dy *= factor;
674
675 /* Whirl */
676
677         factor = 1.0 - dist;
678
679         ang = whirl * factor * factor;
680
681         sina = sin(ang);
682         cosa = cos(ang);
683
684         x = (cosa * dx - sina * dy) / scale_x + cen_x;
685         y = (sina * dx + cosa * dy) / scale_y + cen_y;
686     }
687
688         return inside;
689 }
690
691
692
693 #define GET_PIXEL(components, x, y, input_rows) \
694         input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
695
696
697
698
699
700
701 static float bilinear(double x, double y, double *values)
702 {
703         double m0, m1;
704         x = fmod(x, 1.0);
705         y = fmod(y, 1.0);
706
707         if(x < 0.0) x += 1.0;
708         if(y < 0.0) y += 1.0;
709
710         m0 = (double)values[0] + x * ((double)values[1] - values[0]);
711         m1 = (double)values[2] + x * ((double)values[3] - values[2]);
712         return m0 + y * (m1 - m0);
713 }
714
715
716
717
718
719 #define WHIRL_MACRO(type, max, components) \
720 { \
721         type **input_rows = (type**)plugin->input->get_rows(); \
722 /* Compiler error requires separate arrays */ \
723         double top_values[4], bot_values[4]; \
724         for( int i=0; i<4; ++i ) top_values[i] = bot_values[i] = 0; \
725         for(int row = pkg->row1 / 2; row <= (pkg->row2 + pkg->row1) / 2; row++) \
726         { \
727                 type *top_row = (type*)plugin->output->get_rows()[row]; \
728                 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
729                 type *top_p = top_row; \
730                 type *bot_p = bot_row + components * w - components; \
731                 type *pixel1; \
732                 type *pixel2; \
733                 type *pixel3; \
734                 type *pixel4; \
735  \
736                 for(int col = 0; col < w; col++) \
737                 { \
738                         if(calc_undistorted_coords(cen_x, \
739                                 cen_y, \
740                                 scale_x, \
741                                 scale_y, \
742                                 radius, \
743                                 radius2, \
744                                 radius3, \
745                                 pinch, \
746                                 col, \
747                                 row, \
748                                 whirl, \
749                                 cx, \
750                                 cy)) \
751                         { \
752 /* Inside distortion area */ \
753 /* Do top */ \
754                                 if(cx >= 0.0) \
755                                         ix = (int)cx; \
756                                 else \
757                                         ix = -((int)-cx + 1); \
758  \
759                                 if(cy >= 0.0) \
760                                         iy = (int)cy; \
761                                 else \
762                                         iy = -((int)-cy + 1); \
763  \
764                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
765                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
766                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
767                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
768  \
769                                 top_values[0] = pixel1[0]; \
770                                 top_values[1] = pixel2[0]; \
771                                 top_values[2] = pixel3[0]; \
772                                 top_values[3] = pixel4[0]; \
773                                 top_p[0] = (type)bilinear(cx, cy, top_values); \
774  \
775                                 top_values[0] = pixel1[1]; \
776                                 top_values[1] = pixel2[1]; \
777                                 top_values[2] = pixel3[1]; \
778                                 top_values[3] = pixel4[1]; \
779                                 top_p[1] = (type)bilinear(cx, cy, top_values); \
780  \
781                                 top_values[0] = pixel1[2]; \
782                                 top_values[1] = pixel2[2]; \
783                                 top_values[2] = pixel3[2]; \
784                                 top_values[3] = pixel4[2]; \
785                                 top_p[2] = (type)bilinear(cx, cy, top_values); \
786  \
787                                 if(components == 4) \
788                                 { \
789                                         top_values[0] = pixel1[3]; \
790                                         top_values[1] = pixel2[3]; \
791                                         top_values[2] = pixel3[3]; \
792                                         top_values[3] = pixel4[3]; \
793                                         top_p[3] = (type)bilinear(cx, cy, top_values); \
794                                 } \
795  \
796                                 top_p += components; \
797  \
798 /* Do bottom */ \
799                         cx = cen_x + (cen_x - cx); \
800                         cy = cen_y + (cen_y - cy); \
801  \
802                         if(cx >= 0.0) \
803                                         ix = (int)cx; \
804                         else \
805                                         ix = -((int)-cx + 1); \
806  \
807                         if(cy >= 0.0) \
808                                         iy = (int)cy; \
809                         else \
810                                         iy = -((int)-cy + 1); \
811  \
812                                 pixel1 = GET_PIXEL(components, ix,     iy,     input_rows); \
813                                 pixel2 = GET_PIXEL(components, ix + 1, iy,     input_rows); \
814                                 pixel3 = GET_PIXEL(components, ix,     iy + 1, input_rows); \
815                                 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
816  \
817  \
818  \
819                                 bot_values[0] = pixel1[0]; \
820                                 bot_values[1] = pixel2[0]; \
821                                 bot_values[2] = pixel3[0]; \
822                                 bot_values[3] = pixel4[0]; \
823                                 bot_p[0] = (type)bilinear(cx, cy, bot_values); \
824  \
825                                 bot_values[0] = pixel1[1]; \
826                                 bot_values[1] = pixel2[1]; \
827                                 bot_values[2] = pixel3[1]; \
828                                 bot_values[3] = pixel4[1]; \
829                                 bot_p[1] = (type)bilinear(cx, cy, bot_values); \
830  \
831                                 bot_values[0] = pixel1[2]; \
832                                 bot_values[1] = pixel2[2]; \
833                                 bot_values[2] = pixel3[2]; \
834                                 bot_values[3] = pixel4[2]; \
835                                 bot_p[2] = (type)bilinear(cx, cy, bot_values); \
836  \
837                                 if(components == 4) \
838                                 { \
839                                         bot_values[0] = pixel1[3]; \
840                                         bot_values[1] = pixel2[3]; \
841                                         bot_values[2] = pixel3[3]; \
842                                         bot_values[3] = pixel4[3]; \
843                                         bot_p[3] = (type)bilinear(cx, cy, bot_values); \
844                                 } \
845  \
846                                 bot_p -= components; \
847  \
848  \
849                         } \
850                         else \
851                         { \
852 /* Outside distortion area */ \
853 /* Do top */ \
854                                 top_p[0] = input_rows[row][components * col + 0]; \
855                                 top_p[1] = input_rows[row][components * col + 1]; \
856                                 top_p[2] = input_rows[row][components * col + 2]; \
857                                 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
858  \
859  \
860                                 top_p += components; \
861  \
862 /* Do bottom */ \
863                                 int bot_offset = w * components - col * components - components; \
864                                 int bot_row = h - 1 - row; \
865                                 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
866                                 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
867                                 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
868                                 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
869                                 bot_p -= components; \
870                         } \
871                 } \
872         } \
873 }
874
875 void WhirlUnit::process_package(LoadPackage *package)
876 {
877         WhirlPackage *pkg = (WhirlPackage*)package;
878         int w = plugin->input->get_w();
879         int h = plugin->input->get_h();
880         double whirl = plugin->config.angle * M_PI / 180;
881         double pinch = plugin->config.pinch / MAXPINCH;
882         double cx, cy;
883     int ix, iy;
884         double cen_x = (double)(w-1) / 2.0;
885         double cen_y = (double)(h-1) / 2.0;
886         double radius = MAX(w, h);
887         double radius3 = plugin->config.radius / MAXRADIUS;
888         double radius2 = radius * radius * radius3;
889         double scale_x;
890         double scale_y;
891
892
893 //printf("WhirlUnit::process_package 1 %f %f %f\n",
894 //      plugin->config.angle, plugin->config.pinch, plugin->config.radius);
895         if(w < h)
896         {
897         scale_x = (double)h / w;
898         scale_y = 1.0;
899         }
900         else
901         if(w > h)
902         {
903         scale_x = 1.0;
904         scale_y = (double)w / h;
905         }
906         else
907         {
908         scale_x = 1.0;
909         scale_y = 1.0;
910         }
911
912
913
914         switch(plugin->input->get_color_model())
915         {
916                 case BC_RGB_FLOAT:
917                         WHIRL_MACRO(float, 1, 3);
918                         break;
919                 case BC_RGB888:
920                 case BC_YUV888:
921                         WHIRL_MACRO(unsigned char, 0xff, 3);
922                         break;
923                 case BC_RGBA_FLOAT:
924                         WHIRL_MACRO(float, 1, 4);
925                         break;
926                 case BC_RGBA8888:
927                 case BC_YUVA8888:
928                         WHIRL_MACRO(unsigned char, 0xff, 4);
929                         break;
930                 case BC_RGB161616:
931                 case BC_YUV161616:
932                         WHIRL_MACRO(uint16_t, 0xffff, 3);
933                         break;
934                 case BC_RGBA16161616:
935                 case BC_YUVA16161616:
936                         WHIRL_MACRO(uint16_t, 0xffff, 4);
937                         break;
938
939         }
940 }
941
942
943
944
945
946
947
948 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
949 // : LoadServer(1, 1)
950  : LoadServer(cpus, cpus)
951 {
952         this->plugin = plugin;
953 }
954 void WhirlEngine::init_packages()
955 {
956         for(int i = 0; i < LoadServer::get_total_packages(); i++)
957         {
958                 WhirlPackage *pkg = (WhirlPackage*)get_package(i);
959                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
960                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
961         }
962
963 }
964
965 LoadClient* WhirlEngine::new_client()
966 {
967         return new WhirlUnit(plugin, this);
968 }
969
970 LoadPackage* WhirlEngine::new_package()
971 {
972         return new WhirlPackage;
973 }
974