minor changes; mostly for new Context Help feature
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / spherecam / spherecam.C.bak
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2017 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 "affine.h"
23 #include "bcdisplayinfo.h"
24 #include "bchash.h"
25 #include "bcsignals.h"
26 #include "clip.h"
27 #include "filexml.h"
28 #include "language.h"
29 #include "360fuser.h"
30
31
32 #include <string.h>
33
34
35
36 // largely based on equations from http://paulbourke.net/dome/fish2/
37
38
39
40 REGISTER_PLUGIN(Fuse360Main)
41
42
43
44
45 Fuse360Config::Fuse360Config()
46 {
47         fov = 1.0;
48         radius_x = 0.5;
49         radius_y = 0.5;
50         center_x = 50.0;
51         center_y = 50.0;
52         distance_x = 50;
53         distance_y = 0;
54         feather = 0;
55         translate_x = 0;
56         rotation = 0;
57         draw_guides = 1;
58         mode = Fuse360Config::DO_NOTHING;
59 }
60
61 int Fuse360Config::equivalent(Fuse360Config &that)
62 {
63         if(EQUIV(fov, that.fov) &&
64                 EQUIV(radius_x, that.radius_x) &&
65                 EQUIV(radius_y, that.radius_y) &&
66                 EQUIV(feather, that.feather) && 
67                 EQUIV(center_x, that.center_x) &&
68                 EQUIV(center_y, that.center_y) &&
69                 EQUIV(distance_x, that.distance_x) &&
70                 EQUIV(distance_y, that.distance_y) &&
71                 EQUIV(translate_x, that.translate_x) &&
72                 EQUIV(rotation, that.rotation) &&
73                 mode == that.mode &&
74                 draw_guides == that.draw_guides)
75         {
76                 return 1;
77         }
78         else
79         {
80                 return 0;
81         }
82 }
83
84 void Fuse360Config::copy_from(Fuse360Config &that)
85 {
86         *this = that;
87 }
88
89 void Fuse360Config::interpolate(Fuse360Config &prev, 
90         Fuse360Config &next, 
91         int64_t prev_frame, 
92         int64_t next_frame, 
93         int64_t current_frame)
94 {
95         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
96         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
97
98         fov = prev.fov * prev_scale + next.fov * next_scale;
99         radius_x = prev.radius_x * prev_scale + next.radius_x * next_scale;
100         radius_y = prev.radius_y * prev_scale + next.radius_y * next_scale;
101         feather = prev.feather * prev_scale + next.feather * next_scale;
102         center_x = prev.center_x * prev_scale + next.center_x * next_scale;
103         center_y = prev.center_y * prev_scale + next.center_y * next_scale;
104         distance_x = prev.distance_x * prev_scale + next.distance_x * next_scale;
105         distance_y = prev.distance_y * prev_scale + next.distance_y * next_scale;
106         rotation = prev.rotation * prev_scale + next.rotation * next_scale;
107         translate_x = prev.translate_x * prev_scale + next.translate_x * next_scale;
108         draw_guides = prev.draw_guides;
109         mode = prev.mode;
110
111         boundaries();
112 }
113
114 void Fuse360Config::boundaries()
115 {
116         CLAMP(fov, 0.0, 1.0);
117         CLAMP(radius_x, 0.3, 1.0);
118         CLAMP(radius_y, 0.3, 1.0);
119         CLAMP(feather, 0, 50);
120         CLAMP(center_x, 0.0, 99.0);
121         CLAMP(center_y, 0.0, 99.0);
122         CLAMP(distance_x, 0.0, 100.0);
123         CLAMP(distance_y, -50.0, 50.0);
124         CLAMP(translate_x, -100, 100);
125         CLAMP(rotation, -180, 180);
126 }
127
128
129
130
131 Fuse360Slider::Fuse360Slider(Fuse360Main *client, 
132         Fuse360GUI *gui,
133         Fuse360Text *text,
134         float *output, 
135         int x, 
136         int y, 
137         float min,
138         float max)
139  : BC_FSlider(x, y, 0, 200, 200, min, max, *output)
140 {
141         this->gui = gui;
142         this->client = client;
143         this->output = output;
144         this->text = text;
145         set_precision(0.01);
146 }
147
148 int Fuse360Slider::handle_event()
149 {
150         float prev_output = *output;
151         *output = get_value();
152         text->update(*output);
153
154
155         client->send_configure_change();
156         return 1;
157 }
158
159
160
161 Fuse360Text::Fuse360Text(Fuse360Main *client, 
162         Fuse360GUI *gui,
163         Fuse360Slider *slider,
164         float *output, 
165         int x, 
166         int y)
167  : BC_TextBox(x, y, 100, 1, *output)
168 {
169         this->gui = gui;
170         this->client = client;
171         this->output = output;
172         this->slider = slider;
173 }
174
175 int Fuse360Text::handle_event()
176 {
177         float prev_output = *output;
178         *output = atof(get_text());
179         slider->update(*output);
180
181
182         client->send_configure_change();
183         return 1;
184 }
185
186
187
188 Fuse360Toggle::Fuse360Toggle(Fuse360Main *client, 
189         int *output, 
190         int x, 
191         int y,
192         const char *text)
193  : BC_CheckBox(x, y, *output, text)
194 {
195         this->output = output;
196         this->client = client;
197 }
198
199 int Fuse360Toggle::handle_event()
200 {
201         *output = get_value();
202         client->send_configure_change();
203         return 1;
204 }
205
206
207
208
209
210
211
212
213
214
215 Fuse360Mode::Fuse360Mode(Fuse360Main *plugin,  
216         Fuse360GUI *gui,
217         int x,
218         int y)
219  : BC_PopupMenu(x,
220         y,
221         calculate_w(gui),
222         "",
223         1)
224 {
225         this->plugin = plugin;
226         this->gui = gui;
227 }
228
229 int Fuse360Mode::handle_event()
230 {
231         plugin->config.mode = from_text(get_text());
232         plugin->send_configure_change();
233         return 1;
234
235 }
236
237 void Fuse360Mode::create_objects()
238 {
239         add_item(new BC_MenuItem(to_text(Fuse360Config::DO_NOTHING)));
240         add_item(new BC_MenuItem(to_text(Fuse360Config::STRETCHXY)));
241         add_item(new BC_MenuItem(to_text(Fuse360Config::STANDARD)));
242         add_item(new BC_MenuItem(to_text(Fuse360Config::BLEND)));
243         update(plugin->config.mode);
244 }
245
246 void Fuse360Mode::update(int mode)
247 {
248         char string[BCTEXTLEN];
249         sprintf(string, "%s", to_text(mode));
250         set_text(string);
251 }
252
253 int Fuse360Mode::calculate_w(Fuse360GUI *gui)
254 {
255         int result = 0;
256         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(Fuse360Config::STRETCHXY)));
257         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(Fuse360Config::STANDARD)));
258         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(Fuse360Config::BLEND)));
259         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(Fuse360Config::DO_NOTHING)));
260         return result + 50;
261 }
262
263 int Fuse360Mode::from_text(char *text)
264 {
265         for(int i = 0; i < 4; i++)
266         {
267                 if(!strcmp(text, to_text(i)))
268                 {
269                         return i;
270                 }
271         }
272
273         return Fuse360Config::STRETCHXY;
274 }
275
276 const char* Fuse360Mode::to_text(int mode)
277 {
278         switch(mode)
279         {
280                 case Fuse360Config::DO_NOTHING:
281                         return "Do nothing";
282                         break;
283                 case Fuse360Config::STRETCHXY:
284                         return "Stretch";
285                         break;
286                 case Fuse360Config::STANDARD:
287                         return "Equirectangular";
288                         break;
289         }
290         return "Blend";
291 }
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308 Fuse360GUI::Fuse360GUI(Fuse360Main *client)
309  : PluginClientWindow(client,
310         350, 
311         650, 
312         350, 
313         650, 
314         0)
315 {
316         this->client = client;
317 }
318
319 Fuse360GUI::~Fuse360GUI()
320 {
321 }
322
323
324 void Fuse360GUI::create_objects()
325 {
326         int x = 10;
327         int y = 10;
328         int x1;
329         int margin = 10;
330         BC_Title *title;
331         Fuse360Toggle *toggle;
332
333 //      add_tool(title = new BC_Title(x, y, _("Field of View:")));
334 //      y += title->get_h() + 5;
335 //      add_tool(fov_slider = new Fuse360Slider(client, 
336 //              this,
337 //              0,
338 //              &client->config.fov, 
339 //              x, 
340 //              y, 
341 //              0.0001,
342 //              1.0));
343 // 
344 // 
345 //      x1 = x + fov_slider->get_w() + margin;
346 //      add_tool(fov_text = new Fuse360Text(client, 
347 //              this,
348 //              fov_slider,
349 //              &client->config.fov, 
350 //              x1, 
351 //              y));
352 //      fov_slider->text = fov_text;
353 //      y += fov_text->get_h() + margin;
354
355
356
357
358         add_tool(title = new BC_Title(x, y, _("Eye Radius X:")));
359         y += title->get_h() + 5;
360         add_tool(radiusx_slider = new Fuse360Slider(client, 
361                 this,
362                 0,
363                 &client->config.radius_x, 
364                 x, 
365                 y, 
366                 0.3,
367                 1.0));
368         x1 = x + radiusx_slider->get_w() + margin;
369         add_tool(radiusx_text = new Fuse360Text(client, 
370                 this,
371                 radiusx_slider,
372                 &client->config.radius_x, 
373                 x1, 
374                 y));
375         radiusx_slider->text = radiusx_text;
376         y += radiusx_text->get_h() + 5;
377
378
379         add_tool(title = new BC_Title(x, y, _("Eye Radius Y:")));
380         y += title->get_h() + margin;
381         add_tool(radiusy_slider = new Fuse360Slider(client, 
382                 this,
383                 0,
384                 &client->config.radius_y, 
385                 x, 
386                 y, 
387                 0.3,
388                 1.0));
389         x1 = x + radiusy_slider->get_w() + margin;
390         add_tool(radiusy_text = new Fuse360Text(client, 
391                 this,
392                 radiusy_slider,
393                 &client->config.radius_y, 
394                 x1, 
395                 y));
396         radiusy_slider->text = radiusy_text;
397         y += radiusy_text->get_h() + margin;
398
399
400 //      add_tool(title = new BC_Title(x, y, _("Center X:")));
401 //      y += title->get_h() + 5;
402 //      add_tool(centerx_slider = new Fuse360Slider(client, 
403 //              this,
404 //              0,
405 //              &client->config.center_x, 
406 //              x, 
407 //              y, 
408 //              0.0,
409 //              99.0));
410 //      x1 = x + centerx_slider->get_w() + margin;
411 //      add_tool(centerx_text = new Fuse360Text(client, 
412 //              this,
413 //              centerx_slider,
414 //              &client->config.center_x, 
415 //              x1, 
416 //              y));
417 //      centerx_slider->text = centerx_text;
418 //      centerx_slider->set_precision(0.1);
419 //      y += centerx_text->get_h() + margin;
420 // 
421 // 
422 //      add_tool(title = new BC_Title(x, y, _("Center Y:")));
423 //      y += title->get_h() + 5;
424 //      add_tool(centery_slider = new Fuse360Slider(client, 
425 //              this,
426 //              0,
427 //              &client->config.center_y, 
428 //              x, 
429 //              y, 
430 //              0.0,
431 //              99.0));
432 //      x1 = x + centery_slider->get_w() + margin;
433 //      add_tool(centery_text = new Fuse360Text(client, 
434 //              this,
435 //              centery_slider,
436 //              &client->config.center_y, 
437 //              x1, 
438 //              y));
439 //      centery_slider->text = centery_text;
440 //      centery_slider->set_precision(0.1);
441 //      y += centery_text->get_h() + margin;
442
443
444
445
446         add_tool(title = new BC_Title(x, y, _("X Eye Spacing:")));
447         y += title->get_h() + 5;
448         add_tool(distancex_slider = new Fuse360Slider(client, 
449                 this,
450                 0,
451                 &client->config.distance_x, 
452                 x, 
453                 y, 
454                 0.0,
455                 100.0));
456         x1 = x + distancex_slider->get_w() + margin;
457         add_tool(distancex_text = new Fuse360Text(client, 
458                 this,
459                 distancex_slider,
460                 &client->config.distance_x, 
461                 x1, 
462                 y));
463         distancex_slider->text = distancex_text;
464         distancex_slider->set_precision(0.1);
465         y += distancex_text->get_h() + margin;
466
467
468
469
470
471
472
473         add_tool(title = new BC_Title(x, y, _("Y Eye Spacing:")));
474         y += title->get_h() + 5;
475         add_tool(distancey_slider = new Fuse360Slider(client, 
476                 this,
477                 0,
478                 &client->config.distance_y, 
479                 x, 
480                 y, 
481                 -50,
482                 50));
483         x1 = x + distancey_slider->get_w() + margin;
484         add_tool(distancey_text = new Fuse360Text(client, 
485                 this,
486                 distancey_slider,
487                 &client->config.distance_y, 
488                 x1, 
489                 y));
490         distancey_slider->text = distancey_text;
491         distancey_slider->set_precision(0.1);
492         y += distancey_text->get_h() + margin;
493
494
495
496
497
498
499         add_tool(title = new BC_Title(x, y, _("X Translation:")));
500         y += title->get_h() + 5;
501         add_tool(translatex_slider = new Fuse360Slider(client, 
502                 this,
503                 0,
504                 &client->config.translate_x, 
505                 x, 
506                 y, 
507                 -50.0,
508                 50.0));
509         x1 = x + translatex_slider->get_w() + margin;
510         add_tool(translatex_text = new Fuse360Text(client, 
511                 this,
512                 translatex_slider,
513                 &client->config.translate_x, 
514                 x1, 
515                 y));
516         translatex_slider->text = translatex_text;
517         translatex_slider->set_precision(0.1);
518         y += translatex_text->get_h() + margin;
519
520
521
522
523
524
525         add_tool(title = new BC_Title(x, y, _("Feather:")));
526         y += title->get_h() + 5;
527         add_tool(feather_slider = new Fuse360Slider(client, 
528                 this,
529                 0,
530                 &client->config.feather, 
531                 x, 
532                 y, 
533                 0,
534                 50));
535         x1 = x + feather_slider->get_w() + margin;
536         add_tool(feather_text = new Fuse360Text(client, 
537                 this,
538                 feather_slider,
539                 &client->config.feather, 
540                 x1, 
541                 y));
542         feather_slider->text = feather_text;
543         feather_slider->set_precision(1);
544         y += feather_text->get_h() + margin;
545
546
547
548
549
550
551         add_tool(title = new BC_Title(x, y, _("Rotation:")));
552         y += title->get_h() + 5;
553         add_tool(rotation_slider = new Fuse360Slider(client, 
554                 this,
555                 0,
556                 &client->config.rotation, 
557                 x, 
558                 y, 
559                 -180,
560                 180));
561         x1 = x + rotation_slider->get_w() + margin;
562         add_tool(rotation_text = new Fuse360Text(client, 
563                 this,
564                 rotation_slider,
565                 &client->config.rotation, 
566                 x1, 
567                 y));
568         rotation_slider->text = rotation_text;
569         rotation_slider->set_precision(0.1);
570         y += rotation_text->get_h() + margin;
571
572
573
574
575
576
577
578 //printf("Fuse360GUI::create_objects %d %f\n", __LINE__, client->config.distance);
579
580
581         add_tool(draw_guides = new Fuse360Toggle(client, 
582                 &client->config.draw_guides, 
583                 x, 
584                 y,
585                 _("Draw guides")));
586         y += draw_guides->get_h() + margin;
587
588         
589         add_tool(title = new BC_Title(x, y, _("Mode:")));
590         add_tool(mode = new Fuse360Mode(client, 
591                 this, 
592                 x + title->get_w() + margin, 
593                 y));
594         mode->create_objects();
595         y += mode->get_h() + margin;
596
597
598
599         show_window();
600         flush();
601 }
602
603
604
605
606
607
608
609 Fuse360Main::Fuse360Main(PluginServer *server)
610  : PluginVClient(server)
611 {
612         engine = 0;
613         affine = 0;
614 }
615
616 Fuse360Main::~Fuse360Main()
617 {
618         delete engine;
619         delete affine;
620 }
621
622 NEW_WINDOW_MACRO(Fuse360Main, Fuse360GUI)
623 LOAD_CONFIGURATION_MACRO(Fuse360Main, Fuse360Config)
624 int Fuse360Main::is_realtime() { return 1; }
625 const char* Fuse360Main::plugin_title() { return "360 Fuser"; }
626
627 void Fuse360Main::update_gui()
628 {
629         if(thread)
630         {
631                 if(load_configuration())
632                 {
633                         ((Fuse360GUI*)thread->window)->lock_window("Fuse360Main::update_gui");
634 //                      ((Fuse360GUI*)thread->window)->fov_slider->update(config.fov);
635 //                      ((Fuse360GUI*)thread->window)->fov_text->update(config.fov);
636                         ((Fuse360GUI*)thread->window)->radiusx_slider->update(config.radius_x);
637                         ((Fuse360GUI*)thread->window)->radiusx_text->update(config.radius_x);
638                         ((Fuse360GUI*)thread->window)->radiusy_slider->update(config.radius_y);
639                         ((Fuse360GUI*)thread->window)->radiusy_text->update(config.radius_y);
640 //                      ((Fuse360GUI*)thread->window)->centerx_slider->update(config.center_x);
641 //                      ((Fuse360GUI*)thread->window)->centerx_text->update(config.center_x);
642 //                      ((Fuse360GUI*)thread->window)->centery_slider->update(config.center_y);
643 //                      ((Fuse360GUI*)thread->window)->centery_text->update(config.center_y);
644                         ((Fuse360GUI*)thread->window)->distancex_slider->update(config.distance_x);
645                         ((Fuse360GUI*)thread->window)->distancex_text->update(config.distance_x);
646                         ((Fuse360GUI*)thread->window)->distancey_slider->update(config.distance_y);
647                         ((Fuse360GUI*)thread->window)->distancey_text->update(config.distance_y);
648                         ((Fuse360GUI*)thread->window)->translatex_slider->update(config.translate_x);
649                         ((Fuse360GUI*)thread->window)->translatex_text->update(config.translate_x);
650                         ((Fuse360GUI*)thread->window)->feather_slider->update(config.feather);
651                         ((Fuse360GUI*)thread->window)->feather_text->update(config.feather);
652                         ((Fuse360GUI*)thread->window)->rotation_slider->update(config.rotation);
653                         ((Fuse360GUI*)thread->window)->rotation_text->update(config.rotation);
654                         ((Fuse360GUI*)thread->window)->mode->update(config.mode);
655                         ((Fuse360GUI*)thread->window)->draw_guides->update(config.draw_guides);
656                         ((Fuse360GUI*)thread->window)->unlock_window();
657                 }
658         }
659 }
660
661
662 void Fuse360Main::save_data(KeyFrame *keyframe)
663 {
664         FileXML output;
665         char string[BCTEXTLEN];
666
667
668
669 // cause data to be stored directly in text
670         output.set_shared_string(keyframe->get_data(), MESSAGESIZE);
671         output.tag.set_title("360FUSER");
672         output.tag.set_property("FOCAL_LENGTH", config.fov);
673         output.tag.set_property("RADIUS_X", config.radius_x);
674         output.tag.set_property("RADIUS_Y", config.radius_y);
675         output.tag.set_property("FEATHER", config.feather);
676         output.tag.set_property("CENTER_X", config.center_x);
677         output.tag.set_property("CENTER_Y", config.center_y);
678         output.tag.set_property("DISTANCE_X", config.distance_x);
679         output.tag.set_property("DISTANCE_Y", config.distance_y);
680         output.tag.set_property("TRANSLATE_X", config.translate_x);
681         output.tag.set_property("ROTATION", config.rotation);
682         output.tag.set_property("DRAW_GUIDES", config.draw_guides);
683         output.tag.set_property("MODE", config.mode);
684         output.append_tag();
685         output.terminate_string();
686
687 }
688
689
690 void Fuse360Main::read_data(KeyFrame *keyframe)
691 {
692         FileXML input;
693         char string[BCTEXTLEN];
694
695
696         input.set_shared_string(keyframe->get_data(), strlen(keyframe->get_data()));
697
698         int result = 0;
699
700         while(!result)
701         {
702                 result = input.read_tag();
703
704                 if(!result)
705                 {
706                         if(input.tag.title_is("360FUSER"))
707                         {
708                                 config.fov = input.tag.get_property("FOCAL_LENGTH", config.fov);
709                                 config.radius_x = input.tag.get_property("RADIUS_X", config.radius_x);
710                                 config.radius_y = input.tag.get_property("RADIUS_Y", config.radius_y);
711                                 config.feather = input.tag.get_property("FEATHER", config.feather);
712                                 config.center_x = input.tag.get_property("CENTER_X", config.center_x);
713                                 config.center_y = input.tag.get_property("CENTER_Y", config.center_y);
714                                 config.distance_x = input.tag.get_property("DISTANCE_X", config.distance_x);
715                                 config.distance_y = input.tag.get_property("DISTANCE_Y", config.distance_y);
716                                 config.translate_x = input.tag.get_property("TRANSLATE_X", config.translate_x);
717                                 config.rotation = input.tag.get_property("ROTATION", config.rotation);
718                                 config.draw_guides = input.tag.get_property("DRAW_GUIDES", config.draw_guides);
719                                 config.mode = input.tag.get_property("MODE", config.mode);
720                         }
721                 }
722         }
723 }
724
725
726
727 int Fuse360Main::process_buffer(VFrame *frame,
728         int64_t start_position,
729         double frame_rate)
730 {
731         load_configuration();
732         
733         VFrame *input = new_temp(frame->get_w(), frame->get_h(), frame->get_color_model());
734         
735         read_frame(input, 
736                 0, 
737                 start_position, 
738                 frame_rate,
739                 0); // use opengl
740
741         calculate_extents();
742
743 // always rotate it
744         if(!EQUIV(config.rotation, 0))
745         {
746                 int center_x = w / 2;
747                 int center_y = h / 2;
748                 int center_x1 = w / 4;
749                 int center_y1 = h / 2;
750                 int center_x2 = w * 3 / 4;
751                 int center_y2 = h / 2;
752                 int radius_x = 0;
753                 int radius_y = 0;
754
755                 radius_x = config.radius_x * w / 2;
756                 radius_y = config.radius_y * h;
757
758                 if(!affine) affine = new AffineEngine(PluginClient::smp + 1, 
759                         PluginClient::smp + 1);
760                 affine->set_in_pivot(center_x1, center_y1);
761                 affine->set_in_viewport(0, 0, center_x, h);
762                 affine->set_out_viewport(0, 0, center_x, h);
763                 affine->rotate(get_output(),
764                         input, 
765                         config.rotation);
766
767                 affine->set_in_pivot(center_x2, center_y2);
768                 affine->set_in_viewport(center_x, 0, w - center_x, h);
769                 affine->set_out_viewport(center_x, 0, w - center_x, h);
770                 affine->rotate(get_output(),
771                         input, 
772                         -config.rotation);
773
774                 input->copy_from(get_output());
775                 get_output()->clear_frame();
776
777         }
778
779         if(config.mode == Fuse360Config::DO_NOTHING)
780         {
781                 get_output()->copy_from(input);
782         }
783         else
784         {
785
786                 if(!engine) engine = new Fuse360Engine(this);
787                 engine->process_packages();
788         }
789
790
791
792         if(config.draw_guides)
793         {
794
795                 get_output()->draw_line(center_x, 0, center_x, h);
796
797 // draw lenses
798                 get_output()->draw_oval(center_x1 - radius_x + distance_x, 
799                         center_y1 - radius_y + distance_y, 
800                         center_x1 + radius_x + distance_x, 
801                         center_y1 + radius_y + distance_y);
802                 get_output()->draw_oval(center_x2 - radius_x - distance_x, 
803                         center_y2 - radius_y - distance_y, 
804                         center_x2 + radius_x - distance_x, 
805                         center_y2 + radius_y - distance_y);
806
807 // draw feather
808                 get_output()->draw_line(feather_x1, 0, feather_x1, h);
809                 get_output()->draw_line(feather_x2, 0, feather_x2, h);
810                 
811
812         }
813
814         return 0;
815 }
816
817
818 void Fuse360Main::calculate_extents()
819 {
820         w = get_output()->get_w();
821         h = get_output()->get_h();
822         center_x = w / 2;
823         center_y = h / 2;
824         center_x1 = w * 1 / 4;
825         center_y1 = h / 2;
826         center_x2 = w * 3 / 4;
827         center_y2 = h / 2;
828         radius_x = 0;
829         radius_y = 0;
830         distance_x = (int)((50 - config.distance_x) * w / 100 / 2);
831         distance_y = (int)(config.distance_y * h / 100 / 2);
832         feather = (int)(config.feather * w / 100);
833         feather_x1 = center_x - (int)(config.feather * w / 100 / 2);
834         feather_x2 = center_x + (int)(config.feather * w / 100 / 2);
835         radius_x = config.radius_x * w / 2;
836         radius_y = config.radius_y * h;
837 //printf("Fuse360Main::calculate_extents %d radius_y=%d\n", __LINE__, radius_y);
838 }
839
840
841
842
843
844 Fuse360Package::Fuse360Package()
845  : LoadPackage() {}
846
847
848
849
850
851 Fuse360Unit::Fuse360Unit(Fuse360Engine *engine, Fuse360Main *plugin)
852  : LoadClient(engine)
853 {
854         this->plugin = plugin;
855 }
856
857
858 Fuse360Unit::~Fuse360Unit()
859 {
860 }
861
862
863
864 void Fuse360Unit::process_blend(Fuse360Package *pkg)
865 {
866         VFrame *input = plugin->get_temp();
867         VFrame *output = plugin->get_output();
868
869         int row1 = pkg->row1;
870         int row2 = pkg->row2;
871         int width = input->get_w();
872         int height = input->get_h();
873
874 #define PROCESS_BLEND(type, components, chroma) \
875 { \
876         type **in_rows = (type**)input->get_rows(); \
877         type **out_rows = (type**)output->get_rows(); \
878         type black[4] = { 0, chroma, chroma, 0 }; \
879  \
880         for(int y = row1; y < row2; y++) \
881         { \
882                 type *out_row = out_rows[y]; \
883                 type *in_row = in_rows[y]; \
884  \
885                 for(int x = 0; x < width; x++) \
886                 { \
887                 } \
888         } \
889 }
890
891
892         switch(plugin->get_input()->get_color_model())
893         {
894                 case BC_RGB888:
895                         PROCESS_BLEND(unsigned char, 3, 0x0);
896                         break;
897                 case BC_RGBA8888:
898                         PROCESS_BLEND(unsigned char, 4, 0x0);
899                         break;
900                 case BC_RGB_FLOAT:
901                         PROCESS_BLEND(float, 3, 0.0);
902                         break;
903                 case BC_RGBA_FLOAT:
904                         PROCESS_BLEND(float, 4, 0.0);
905                         break;
906                 case BC_YUV888:
907                         PROCESS_BLEND(unsigned char, 3, 0x80);
908                         break;
909                 case BC_YUVA8888:
910                         PROCESS_BLEND(unsigned char, 4, 0x80);
911                         break;
912         }
913         
914 }
915
916 // interpolate 1 eye
917 #define BLEND_PIXEL(type, components) \
918         if(x_in < 0.0 || x_in >= w - 1 || \
919                 y_in < 0.0 || y_in >= h - 1) \
920         { \
921                 *out_row++ = black[0]; \
922                 *out_row++ = black[1]; \
923                 *out_row++ = black[2]; \
924                 if(components == 4) *out_row++ = black[3]; \
925         } \
926         else \
927         { \
928                 float y1_fraction = y_in - floor(y_in); \
929                 float y2_fraction = 1.0 - y1_fraction; \
930                 float x1_fraction = x_in - floor(x_in); \
931                 float x2_fraction = 1.0 - x1_fraction; \
932                 type *in_pixel1 = in_rows[(int)y_in] + (int)x_in * components; \
933                 type *in_pixel2 = in_rows[(int)y_in + 1] + (int)x_in * components; \
934                 for(int i = 0; i < components; i++) \
935                 { \
936                         *out_row++ = (type)(in_pixel1[i] * x2_fraction * y2_fraction + \
937                                 in_pixel2[i] * x2_fraction * y1_fraction + \
938                                 in_pixel1[i + components] * x1_fraction * y2_fraction + \
939                                 in_pixel2[i + components] * x1_fraction * y1_fraction); \
940                 } \
941         }
942
943
944 // interpolate 2 eyes
945 #define BLEND_PIXEL2(type, components) \
946         type pixel1[components], pixel2[components]; \
947  \
948 /* calculate the left eye */ \
949         if(x_in1 < 0.0 || x_in1 >= w - 1 || \
950                 y_in1 < 0.0 || y_in1 >= h - 1) \
951         { \
952                 pixel1[0] = black[0]; \
953                 pixel1[1] = black[1]; \
954                 pixel1[2] = black[2]; \
955                 if(components == 4) pixel1[3] = black[3]; \
956         } \
957         else \
958         { \
959                 float y1_fraction = y_in1 - floor(y_in1); \
960                 float y2_fraction = 1.0 - y1_fraction; \
961                 float x1_fraction = x_in1 - floor(x_in1); \
962                 float x2_fraction = 1.0 - x1_fraction; \
963                 type *in_pixel1 = in_rows[(int)y_in1] + (int)x_in1 * components; \
964                 type *in_pixel2 = in_rows[(int)y_in1 + 1] + (int)x_in1 * components; \
965                 for(int i = 0; i < components; i++) \
966                 { \
967                         pixel1[i] = (type)(in_pixel1[i] * x2_fraction * y2_fraction + \
968                                 in_pixel2[i] * x2_fraction * y1_fraction + \
969                                 in_pixel1[i + components] * x1_fraction * y2_fraction + \
970                                 in_pixel2[i + components] * x1_fraction * y1_fraction); \
971                 } \
972         } \
973  \
974  \
975 /* calculate the right eye */ \
976         if(x_in2 < 0.0 || x_in2 >= w - 1 || \
977                 y_in2 < 0.0 || y_in2 >= h - 1) \
978         { \
979                 pixel2[0] = black[0]; \
980                 pixel2[1] = black[1]; \
981                 pixel2[2] = black[2]; \
982                 if(components == 4) pixel2[3] = black[3]; \
983         } \
984         else \
985         { \
986                 float y1_fraction = y_in2 - floor(y_in2); \
987                 float y2_fraction = 1.0 - y1_fraction; \
988                 float x1_fraction = x_in2 - floor(x_in2); \
989                 float x2_fraction = 1.0 - x1_fraction; \
990                 type *in_pixel1 = in_rows[(int)y_in2] + (int)x_in2 * components; \
991                 type *in_pixel2 = in_rows[(int)y_in2 + 1] + (int)x_in2 * components; \
992                 for(int i = 0; i < components; i++) \
993                 { \
994                         pixel2[i] = (type)(in_pixel1[i] * x2_fraction * y2_fraction + \
995                                 in_pixel2[i] * x2_fraction * y1_fraction + \
996                                 in_pixel1[i + components] * x1_fraction * y2_fraction + \
997                                 in_pixel2[i + components] * x1_fraction * y1_fraction); \
998                 } \
999         } \
1000  \
1001 /* blend the 2 eyes */ \
1002         for(int i = 0; i < components; i++) \
1003         { \
1004                 *out_row++ = (type)(pixel1[i] * (1.0f - fraction) + \
1005                         pixel2[i] * fraction); \
1006         } \
1007
1008
1009
1010
1011 #define COLORSPACE_SWITCH(function) \
1012         switch(plugin->get_input()->get_color_model()) \
1013         { \
1014                 case BC_RGB888: \
1015                         function(unsigned char, 3, 0x0); \
1016                         break; \
1017                 case BC_RGBA8888: \
1018                         function(unsigned char, 4, 0x0); \
1019                         break; \
1020                 case BC_RGB_FLOAT: \
1021                         function(float, 3, 0.0); \
1022                         break; \
1023                 case BC_RGBA_FLOAT: \
1024                         function(float, 4, 0.0); \
1025                         break; \
1026                 case BC_YUV888: \
1027                         function(unsigned char, 3, 0x80); \
1028                         break; \
1029                 case BC_YUVA8888: \
1030                         function(unsigned char, 4, 0x80); \
1031                         break; \
1032         }
1033
1034
1035 double Fuse360Unit::calculate_max_z(double a, double r)
1036 {
1037         if(a < 0) a += 2 * M_PI;
1038
1039         if(a < M_PI / 4)
1040         {
1041                 return r / cos(a); // bottom right edge
1042         }
1043         else
1044         if(a < 3 * M_PI / 4)
1045         {
1046                 return r / cos(M_PI / 2 - a); // bottom edge
1047         }
1048         else
1049         if(a < 5 * M_PI / 4)
1050         {
1051                 return r / cos(M_PI - a); // left edge
1052         }
1053         else
1054         if(a < 7 * M_PI / 4)
1055         {
1056                 return r / cos(3 * M_PI / 2 - a); // top edge
1057         }
1058         else
1059         {
1060                 return r / cos(a); // top right edge
1061         }
1062 }
1063
1064 void Fuse360Unit::process_stretch(Fuse360Package *pkg)
1065 {
1066         VFrame *input = plugin->get_temp();
1067         VFrame *output = plugin->get_output();
1068
1069         float fov = plugin->config.fov;
1070         int row1 = pkg->row1;
1071         int row2 = pkg->row2;
1072         int center_x1 = plugin->center_x1;
1073         int center_x2 = plugin->center_x2;
1074         int center_y1 = plugin->center_y1;
1075         int center_y2 = plugin->center_y2;
1076         int center_x = plugin->center_x;
1077         int center_y = plugin->center_y;
1078         int w = plugin->w;
1079         int h = plugin->h;
1080         double radius = plugin->radius_x;
1081
1082
1083 #define PROCESS_STRETCH(type, components, chroma) \
1084 { \
1085         type **in_rows = (type**)input->get_rows(); \
1086         type **out_rows = (type**)output->get_rows(); \
1087         type black[4] = { 0, chroma, chroma, 0 }; \
1088  \
1089         for(int y = row1; y < row2; y++) \
1090         { \
1091                 type *out_row = out_rows[y]; \
1092                 type *in_row = in_rows[y]; \
1093                 double y_diff = y - center_y1; \
1094  \
1095 /* left eye */ \
1096                 for(int x = 0; x < center_x; x++) \
1097                 { \
1098                         double x_diff = x - center_x1; \
1099 /* polar output coordinate */ \
1100                         double z = hypot(x_diff, y_diff); \
1101                         double a = atan2(y_diff, x_diff); \
1102  \
1103 /* scale the magnitude to the radius */ \
1104                         double scaled_z = z * radius / calculate_max_z(a, radius); \
1105 /* xy input coordinate */ \
1106                         double x_in = scaled_z * cos(a) + center_x1; \
1107                         double y_in = scaled_z * sin(a) + center_y1; \
1108  \
1109                         if(x_in < center_x) \
1110                         { \
1111                                 BLEND_PIXEL(type, components) \
1112                         } \
1113                 } \
1114         } \
1115  \
1116         for(int y = row1; y < row2; y++) \
1117         { \
1118                 type *out_row = out_rows[y] + center_x * components; \
1119                 type *in_row = in_rows[y]; \
1120                 double y_diff = y - center_y2; \
1121  \
1122 /* right eye */ \
1123                 for(int x = center_x; x < w; x++) \
1124                 { \
1125                         double x_diff = x - center_x2; \
1126 /* polar output coordinate */ \
1127                         double z = hypot(x_diff, y_diff); \
1128                         double a = atan2(y_diff, x_diff); \
1129  \
1130 /* scale the magnitude to the radius */ \
1131                         double scaled_z = z * radius / calculate_max_z(a, radius); \
1132 /* xy input coordinate */ \
1133                         double x_in = scaled_z * cos(a) + center_x2; \
1134                         double y_in = scaled_z * sin(a) + center_y2; \
1135  \
1136                         if(x_in >= center_x) \
1137                         { \
1138                                 BLEND_PIXEL(type, components) \
1139                         } \
1140                 } \
1141         } \
1142 }
1143
1144
1145
1146         COLORSPACE_SWITCH(PROCESS_STRETCH)
1147 }
1148
1149
1150
1151
1152
1153 void Fuse360Unit::process_standard(Fuse360Package *pkg)
1154 {
1155         VFrame *input = plugin->get_temp();
1156         VFrame *output = plugin->get_output();
1157
1158         float fov = plugin->config.fov;
1159         int row1 = pkg->row1;
1160         int row2 = pkg->row2;
1161         int center_x1 = plugin->center_x1;
1162         int center_x2 = plugin->center_x2;
1163         int center_y1 = plugin->center_y1;
1164         int center_y2 = plugin->center_y2;
1165         int center_x = plugin->center_x;
1166         int center_y = plugin->center_y;
1167         int feather = plugin->feather;
1168         int feather_x1 = plugin->feather_x1;
1169         int feather_x2 = plugin->feather_x2;
1170         int w = plugin->w;
1171         int h = plugin->h;
1172         float radius_x = plugin->radius_x;
1173         float radius_y = plugin->radius_y;
1174         int distance_x = plugin->distance_x;
1175         int distance_y = plugin->distance_y;
1176 // field of view of the fisheye
1177         float FOV = M_PI;
1178
1179 #define PROCESS_STANDARD(type, components, chroma) \
1180 { \
1181         type **in_rows = (type**)input->get_rows(); \
1182         type **out_rows = (type**)output->get_rows(); \
1183         type black[4] = { 0, chroma, chroma, 0 }; \
1184  \
1185 /* left eye */ \
1186         for(int y = row1; y < row2; y++) \
1187         { \
1188                 type *out_row = out_rows[y]; \
1189  \
1190 /* -M_PI/2 to M_PI/2 */ \
1191                 float y_diff = y - center_y1; \
1192 /* polar angles */ \
1193                 float phi = M_PI / 2 * (y_diff / radius_y); \
1194                 for(int x = 0; x < feather_x1; x++) \
1195                 { \
1196                         float x_diff = x - center_x1; \
1197 /* polar angles */ \
1198 /* -M_PI/2 to M_PI/2 */ \
1199                         float theta = M_PI / 2 * (x_diff / radius_x); \
1200 /* vector in 3D space */ \
1201                         float vect_x = cos(phi) * sin(theta); \
1202                         float vect_y = cos(phi) * cos(theta); \
1203                         float vect_z = sin(phi); \
1204 /* fisheye angle & radius */ \
1205                         float theta2 = atan2(vect_z, vect_x); \
1206                         float phi2 = atan2(hypot(vect_x, vect_z), vect_y); \
1207                         float r = radius_x * 2 * phi2 / FOV; \
1208 /* pixel in fisheye space */ \
1209                         float x_in = center_x1 + r * cos(theta2); \
1210                         float y_in = center_y1 + r * sin(theta2); \
1211  \
1212                         BLEND_PIXEL(type, components) \
1213                 } \
1214         } \
1215  \
1216  \
1217  \
1218 /* right eye */ \
1219         for(int y = row1; y < row2; y++) \
1220         { \
1221                 type *out_row = out_rows[y] + components * feather_x2; \
1222  \
1223 /* -M_PI/2 to M_PI/2 */ \
1224                 float y_diff = y - center_y2; \
1225 /* polar angles */ \
1226                 float phi = M_PI / 2 * (y_diff / radius_y); \
1227                 for(int x = feather_x2; x < w; x++) \
1228                 { \
1229                         float x_diff = x - center_x2; \
1230 /* polar angles */ \
1231 /* -M_PI/2 to M_PI/2 */ \
1232                         float theta = M_PI / 2 * (x_diff / radius_x); \
1233 /* vector in 3D space */ \
1234                         float vect_x = cos(phi) * sin(theta); \
1235                         float vect_y = cos(phi) * cos(theta); \
1236                         float vect_z = sin(phi); \
1237 /* fisheye angle & radius */ \
1238                         float theta2 = atan2(vect_z, vect_x); \
1239                         float phi2 = atan2(hypot(vect_x, vect_z), vect_y); \
1240                         float r = radius_x * 2 * phi2 / FOV; \
1241 /* pixel in fisheye space */ \
1242                         float x_in = center_x2 + r * cos(theta2); \
1243                         float y_in = center_y2 + r * sin(theta2); \
1244  \
1245                         BLEND_PIXEL(type, components) \
1246                 } \
1247         } \
1248  \
1249  \
1250 }
1251
1252         COLORSPACE_SWITCH(PROCESS_STANDARD)
1253 }
1254
1255
1256
1257 void Fuse360Unit::process_package(LoadPackage *package)
1258 {
1259         Fuse360Package *pkg = (Fuse360Package*)package;
1260
1261         switch(plugin->config.mode)
1262         {
1263                 case Fuse360Config::STRETCHXY:
1264                         process_stretch(pkg);
1265                         break;
1266                 case Fuse360Config::STANDARD:
1267                         process_standard(pkg);
1268                         break;
1269                 case Fuse360Config::BLEND:
1270                         process_blend(pkg);
1271                         break;
1272         }
1273 }
1274
1275
1276
1277
1278
1279 Fuse360Engine::Fuse360Engine(Fuse360Main *plugin)
1280 // : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
1281  : LoadServer(1, 1)
1282 {
1283         this->plugin = plugin;
1284 }
1285
1286 Fuse360Engine::~Fuse360Engine()
1287 {
1288 }
1289
1290 void Fuse360Engine::init_packages()
1291 {
1292         for(int i = 0; i < LoadServer::get_total_packages(); i++)
1293         {
1294                 Fuse360Package *package = (Fuse360Package*)LoadServer::get_package(i);
1295                 package->row1 = plugin->get_input()->get_h() * i / LoadServer::get_total_packages();
1296                 package->row2 = plugin->get_input()->get_h() * (i + 1) / LoadServer::get_total_packages();
1297         }
1298 }
1299
1300 LoadClient* Fuse360Engine::new_client()
1301 {
1302         return new Fuse360Unit(this, plugin);
1303 }
1304
1305 LoadPackage* Fuse360Engine::new_package()
1306 {
1307         return new Fuse360Package;
1308 }
1309
1310