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