dvb chan tuner api upgrade, slip/ripple handle drag keyfrm fix, load menu tweaks
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / spherecam / spherecam.C
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 "spherecam.h"
30 #include "theme.h"
31
32 #include <string.h>
33
34 // largely based on equations from http://paulbourke.net/dome/fish2/
35
36 REGISTER_PLUGIN(SphereCamMain)
37
38
39 SphereCamConfig::SphereCamConfig()
40 {
41         feather = 0;
42         
43         for( int i = 0; i < EYES; i++ ) {
44                 enabled[i] = 1;
45                 fov[i] = 180;
46                 radius[i] = 100.0;
47                 center_y[i] = 50.0;
48                 rotate_y[i] = 50.0;
49                 rotate_z[i] = 0;
50         }
51         
52         center_x[0] = 25.0;
53         center_x[1] = 75.0;
54         rotate_x[0] = 50.0;
55         rotate_x[1] = 100.0;
56         draw_guides = 1;
57         mode = SphereCamConfig::DO_NOTHING;
58 }
59
60 int SphereCamConfig::equivalent(SphereCamConfig &that)
61 {
62         for( int i = 0; i < EYES; i++ ) {
63                 if( enabled[i] != that.enabled[i] ||
64                         !EQUIV(fov[i], that.fov[i]) ||
65                         !EQUIV(radius[i], that.radius[i]) ||
66                         !EQUIV(center_x[i], that.center_x[i]) ||
67                         !EQUIV(center_y[i], that.center_y[i]) ||
68                         !EQUIV(rotate_x[i], that.rotate_x[i]) ||
69                         !EQUIV(rotate_y[i], that.rotate_y[i]) ||
70                         !EQUIV(rotate_z[i], that.rotate_z[i]) ) {
71                         return 0;
72                 }
73         }
74
75         if( feather != that.feather || mode != that.mode ||
76             draw_guides != that.draw_guides ) {
77                 return 0;
78         }
79
80
81         return 1;
82 }
83
84 void SphereCamConfig::copy_from(SphereCamConfig &that)
85 {
86         *this = that;
87 }
88
89 void SphereCamConfig::interpolate(SphereCamConfig &prev, 
90         SphereCamConfig &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         for( int i = 0; i < EYES; i++ ) {
99                 enabled[i] = prev.enabled[i];
100                 fov[i] = prev.fov[i] * prev_scale + next.fov[i] * next_scale;
101                 radius[i] = prev.radius[i] * prev_scale + next.radius[i] * next_scale;
102                 center_x[i] = prev.center_x[i] * prev_scale + next.center_x[i] * next_scale;
103                 center_y[i] = prev.center_y[i] * prev_scale + next.center_y[i] * next_scale;
104                 rotate_x[i] = prev.rotate_x[i] * prev_scale + next.rotate_x[i] * next_scale;
105                 rotate_y[i] = prev.rotate_y[i] * prev_scale + next.rotate_y[i] * next_scale;
106                 rotate_z[i] = prev.rotate_z[i] * prev_scale + next.rotate_z[i] * next_scale;
107         }
108
109         feather = prev.feather * prev_scale + next.feather * next_scale;
110         draw_guides = prev.draw_guides;
111         mode = prev.mode;
112
113         boundaries();
114 }
115
116 void SphereCamConfig::boundaries()
117 {
118         for( int i = 0; i < EYES; i++ ) {
119                 CLAMP(fov[i], 1.0, 359.0);
120                 CLAMP(radius[i], 1.0, 150.0);
121                 CLAMP(center_x[i], 0.0, 100.0);
122                 CLAMP(center_y[i], 0.0, 100.0);
123                 CLAMP(rotate_x[i], 0.0, 100.0);
124                 CLAMP(rotate_y[i], 0.0, 100.0);
125                 CLAMP(rotate_z[i], -180.0, 180.0);
126         }
127         
128         CLAMP(feather, 0, 50);
129 }
130
131
132 SphereCamSlider::SphereCamSlider(SphereCamMain *client, 
133         SphereCamGUI *gui,
134         SphereCamText *text,
135         float *output, 
136         int x, 
137         int y, 
138         float min,
139         float max)
140  : BC_FSlider(x, 
141         y, 
142         0, 
143         gui->get_w() / 2 - client->get_theme()->widget_border * 3 - 100, 
144         gui->get_w() / 2 - client->get_theme()->widget_border * 3 - 100, 
145         min, 
146         max, 
147         *output)
148 {
149         this->gui = gui;
150         this->client = client;
151         this->output = output;
152         this->text = text;
153         set_precision(0.01);
154 }
155
156 int SphereCamSlider::handle_event()
157 {
158         *output = get_value();
159         text->update(*output);
160         client->send_configure_change();
161         return 1;
162 }
163
164
165 SphereCamText::SphereCamText(SphereCamMain *client, 
166         SphereCamGUI *gui,
167         SphereCamSlider *slider,
168         float *output, 
169         int x, 
170         int y)
171  : BC_TextBox(x, y, 100, 1, *output)
172 {
173         this->gui = gui;
174         this->client = client;
175         this->output = output;
176         this->slider = slider;
177 }
178
179 int SphereCamText::handle_event()
180 {
181         *output = atof(get_text());
182         slider->update(*output);
183         client->send_configure_change();
184         return 1;
185 }
186
187
188 SphereCamToggle::SphereCamToggle(SphereCamMain *client, 
189         int *output, int x, int y, const char *text)
190  : BC_CheckBox(x, y, *output, text)
191 {
192         this->output = output;
193         this->client = client;
194 }
195
196 int SphereCamToggle::handle_event()
197 {
198         *output = get_value();
199         client->send_configure_change();
200         return 1;
201 }
202
203
204 SphereCamMode::SphereCamMode(SphereCamMain *plugin,  
205         SphereCamGUI *gui, int x, int y)
206  : BC_PopupMenu(x, y, calculate_w(gui), "", 1)
207 {
208         this->plugin = plugin;
209         this->gui = gui;
210 }
211
212 int SphereCamMode::handle_event()
213 {
214         plugin->config.mode = from_text(get_text());
215         plugin->send_configure_change();
216         return 1;
217
218 }
219
220 void SphereCamMode::create_objects()
221 {
222         add_item(new BC_MenuItem(to_text(SphereCamConfig::DO_NOTHING)));
223         add_item(new BC_MenuItem(to_text(SphereCamConfig::EQUIRECT)));
224         add_item(new BC_MenuItem(to_text(SphereCamConfig::ALIGN)));
225         update(plugin->config.mode);
226 }
227
228 void SphereCamMode::update(int mode)
229 {
230         char string[BCTEXTLEN];
231         sprintf(string, "%s", to_text(mode));
232         set_text(string);
233 }
234
235 int SphereCamMode::calculate_w(SphereCamGUI *gui)
236 {
237         int result = 0;
238         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(SphereCamConfig::EQUIRECT)));
239         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(SphereCamConfig::DO_NOTHING)));
240         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(SphereCamConfig::ALIGN)));
241         return result + 50;
242 }
243
244 int SphereCamMode::from_text(char *text)
245 {
246         for( int i = 0; i < 3; i++ ) {
247                 if( !strcmp(text, to_text(i)) ) {
248                         return i;
249                 }
250         }
251
252         return SphereCamConfig::EQUIRECT;
253 }
254
255 const char* SphereCamMode::to_text(int mode)
256 {
257         switch( mode ) {
258         case SphereCamConfig::DO_NOTHING:
259                 return "Do nothing";
260                 break;
261         case SphereCamConfig::EQUIRECT:
262                 return "Equirectangular";
263                 break;
264         case SphereCamConfig::ALIGN:
265                 return "Align only";
266                 break;
267         }
268         return "Equirectangular";
269 }
270
271
272 SphereCamGUI::SphereCamGUI(SphereCamMain *client)
273  : PluginClientWindow(client, 640, 600, 640, 600, 0)
274 {
275         this->client = client;
276 }
277
278 SphereCamGUI::~SphereCamGUI()
279 {
280 }
281
282
283 void SphereCamGUI::create_objects()
284 {
285         int margin = client->get_theme()->widget_border;
286         int margin2 = margin;
287         int y = margin;
288         int x[EYES];
289
290         x[0] = margin;
291         x[1] = get_w() / 2 + margin / 2;
292
293         BC_Title *title;
294
295         for( int i = 0; i < EYES; i++ ) {
296                 int x3 = x[i];
297                 y = margin;
298
299                 add_tool(title = new BC_Title(x[i], y,
300                         i == 0 ? _("Left Eye") : _("Right Eye"), LARGEFONT));
301                 y += title->get_h() + margin2;
302                 
303                 add_tool(enabled[i] = new SphereCamToggle(client, 
304                         &client->config.enabled[i], x3, y, _("Enabled")));
305                 y += enabled[i]->get_h() + margin2;
306                 
307                 add_tool(title = new BC_Title(x3, y, _("FOV:")));
308                 y += title->get_h() + margin2;
309                 add_tool(fov_slider[i] = new SphereCamSlider(client, this,
310                         0, &client->config.fov[i], x3, y, 1, 359));
311                 fov_slider[i]->set_precision(0.1);
312                 x3 += fov_slider[i]->get_w() + margin;
313                 add_tool(fov_text[i] = new SphereCamText(client, this,
314                         fov_slider[i], &client->config.fov[i], x3, y));
315                 fov_slider[i]->text = fov_text[i];
316                 y += fov_text[i]->get_h() + margin2;
317
318                 x3 = x[i];
319                 add_tool(title = new BC_Title(x3, y, _("Radius:")));
320                 y += title->get_h() + margin2;
321                 add_tool(radius_slider[i] = new SphereCamSlider(client, this,
322                         0, &client->config.radius[i], x3, y, 1, 150));
323                 radius_slider[i]->set_precision(0.1);
324                 x3 += radius_slider[i]->get_w() + margin;
325                 add_tool(radius_text[i] = new SphereCamText(client, this,
326                         radius_slider[i], &client->config.radius[i], x3, y));
327                 radius_slider[i]->text = radius_text[i];
328                 y += radius_text[i]->get_h() + margin2;
329
330                 x3 = x[i];
331                 add_tool(title = new BC_Title(x3, y, _("Input X:")));
332                 y += title->get_h() + margin2;
333                 add_tool(centerx_slider[i] = new SphereCamSlider(client, this,
334                         0, &client->config.center_x[i], x3, y, 0, 100));
335                 centerx_slider[i]->set_precision(0.1);
336                 x3 += centerx_slider[i]->get_w() + margin;
337                 add_tool(centerx_text[i] = new SphereCamText(client, this,
338                         centerx_slider[i], &client->config.center_x[i], x3, y));
339                 centerx_slider[i]->text = centerx_text[i];
340                 y += centerx_text[i]->get_h() + margin2;
341
342                 x3 = x[i];
343                 add_tool(title = new BC_Title(x3, y, _("Input Y:")));
344                 y += title->get_h() + margin2;
345                 add_tool(centery_slider[i] = new SphereCamSlider(client, this,
346                         0, &client->config.center_y[i], x3, y, 0, 100));
347                 centery_slider[i]->set_precision(0.1);
348                 x3 += centery_slider[i]->get_w() + margin;
349                 add_tool(centery_text[i] = new SphereCamText(client, this,
350                         centery_slider[i], &client->config.center_y[i], x3, y));
351                 centery_slider[i]->text = centery_text[i];
352                 y += centery_text[i]->get_h() + margin2;
353
354                 x3 = x[i];
355                 add_tool(title = new BC_Title(x3, y, _("Output X:")));
356                 y += title->get_h() + margin2;
357                 add_tool(rotatex_slider[i] = new SphereCamSlider(client, this,
358                         0, &client->config.rotate_x[i], x3, y, 0, 100));
359                 rotatex_slider[i]->set_precision(0.1);
360                 x3 += rotatex_slider[i]->get_w() + margin;
361                 add_tool(rotatex_text[i] = new SphereCamText(client, this,
362                         rotatex_slider[i], &client->config.rotate_x[i], x3, y));
363                 rotatex_slider[i]->text = rotatex_text[i];
364                 y += rotatex_text[i]->get_h() + margin2;
365
366                 x3 = x[i];
367                 add_tool(title = new BC_Title(x3, y, _("Output Y:")));
368                 y += title->get_h() + margin2;
369                 add_tool(rotatey_slider[i] = new SphereCamSlider(client, this,
370                         0, &client->config.rotate_y[i], x3, y, 0, 100));
371                 rotatey_slider[i]->set_precision(0.1);
372                 x3 += rotatey_slider[i]->get_w() + margin;
373                 add_tool(rotatey_text[i] = new SphereCamText(client, this,
374                         rotatey_slider[i], &client->config.rotate_y[i], x3, y));
375                 rotatey_slider[i]->text = rotatey_text[i];
376                 y += rotatey_text[i]->get_h() + margin2;
377
378                 x3 = x[i];
379                 add_tool(title = new BC_Title(x3, y, _("Rotate:")));
380                 y += title->get_h() + margin2;
381                 add_tool(rotatez_slider[i] = new SphereCamSlider(client, this,
382                         0, &client->config.rotate_z[i], x3, y, -180, 180));
383                 rotatez_slider[i]->set_precision(0.1);
384                 x3 += rotatez_slider[i]->get_w() + margin;
385                 add_tool(rotatez_text[i] = new SphereCamText(client, this,
386                         rotatez_slider[i], &client->config.rotate_z[i], x3, y));
387                 rotatez_slider[i]->text = rotatez_text[i];
388                 y += rotatez_text[i]->get_h() + margin2;
389         }
390
391         int x3 = x[0];
392         add_tool(title = new BC_Title(x3, y, _("Feather:")));
393         y += title->get_h() + margin2;
394         add_tool(feather_slider = new SphereCamSlider(client, this,
395                 0, &client->config.feather, x3, y, 0, 100));
396         feather_slider->set_precision(0.1);
397         x3 += feather_slider->get_w() + margin;
398         add_tool(feather_text = new SphereCamText(client, this,
399                 feather_slider, &client->config.feather, x3, y));
400         feather_slider->text = feather_text;
401         y += feather_text->get_h() + margin2;
402
403 //printf("SphereCamGUI::create_objects %d %f\n", __LINE__, client->config.distance);
404
405         x3 = x[0];
406         add_tool(draw_guides = new SphereCamToggle(client, 
407                 &client->config.draw_guides, x3, y, _("Draw guides")));
408         y += draw_guides->get_h() + margin2;
409         
410         add_tool(title = new BC_Title(x3, y, _("Mode:")));
411         add_tool(mode = new SphereCamMode(client, 
412                 this, 
413                 x3 + title->get_w() + margin, 
414                 y));
415         mode->create_objects();
416         y += mode->get_h() + margin2;
417
418         show_window();
419         flush();
420 }
421
422
423 SphereCamMain::SphereCamMain(PluginServer *server)
424  : PluginVClient(server)
425 {
426         engine = 0;
427         affine = 0;
428 }
429
430 SphereCamMain::~SphereCamMain()
431 {
432         delete engine;
433         delete affine;
434 }
435
436 NEW_WINDOW_MACRO(SphereCamMain, SphereCamGUI)
437 LOAD_CONFIGURATION_MACRO(SphereCamMain, SphereCamConfig)
438 int SphereCamMain::is_realtime() { return 1; }
439 const char* SphereCamMain::plugin_title() { return N_("Sphere Cam"); }
440
441 void SphereCamMain::update_gui()
442 {
443         if( !thread ) return;
444         if( !load_configuration() ) return;
445         ((SphereCamGUI*)thread->window)->lock_window("SphereCamMain::update_gui");
446         SphereCamGUI *window = (SphereCamGUI*)thread->window;
447
448         for( int i = 0; i < EYES; i++ ) {
449                 window->enabled[i]->update(config.enabled[i]);
450
451                 window->fov_slider[i]->update(config.fov[i]);
452                 window->fov_text[i]->update(config.fov[i]);
453
454                 window->radius_slider[i]->update(config.radius[i]);
455                 window->radius_text[i]->update(config.radius[i]);
456
457                 window->centerx_slider[i]->update(config.center_x[i]);
458                 window->centerx_text[i]->update(config.center_x[i]);
459
460                 window->centery_slider[i]->update(config.center_y[i]);
461                 window->centery_text[i]->update(config.center_y[i]);
462
463                 window->rotatex_slider[i]->update(config.rotate_x[i]);
464                 window->rotatex_text[i]->update(config.rotate_x[i]);
465
466                 window->rotatey_slider[i]->update(config.rotate_y[i]);
467                 window->rotatey_text[i]->update(config.rotate_y[i]);
468
469                 window->rotatez_slider[i]->update(config.rotate_z[i]);
470                 window->rotatez_text[i]->update(config.rotate_z[i]);
471         }
472
473         window->feather_slider->update(config.feather);
474         window->feather_text->update(config.feather);
475
476         window->mode->update(config.mode);
477         window->draw_guides->update(config.draw_guides);
478         window->unlock_window();
479 }
480
481 void SphereCamMain::save_data(KeyFrame *keyframe)
482 {
483         FileXML output;
484         char string[BCTEXTLEN];
485
486 // cause data to be stored directly in text
487         output.set_shared_output(keyframe->xbuf);
488         output.tag.set_title("SPHERECAM");
489         
490         for( int i = 0; i < EYES; i++ ) {
491                 sprintf(string, "ENABLED_%d", i);
492                 output.tag.set_property(string, config.enabled[i]);
493                 sprintf(string, "FOV_%d", i);
494                 output.tag.set_property(string, config.fov[i]);
495                 sprintf(string, "RADIUS_%d", i);
496                 output.tag.set_property(string, config.radius[i]);
497                 sprintf(string, "CENTER_X_%d", i);
498                 output.tag.set_property(string, config.center_x[i]);
499                 sprintf(string, "CENTER_Y_%d", i);
500                 output.tag.set_property(string, config.center_y[i]);
501                 sprintf(string, "ROTATE_X_%d", i);
502                 output.tag.set_property(string, config.rotate_x[i]);
503                 sprintf(string, "ROTATE_Y_%d", i);
504                 output.tag.set_property(string, config.rotate_y[i]);
505                 sprintf(string, "ROTATE_Z_%d", i);
506                 output.tag.set_property(string, config.rotate_z[i]);
507         }
508         
509         output.tag.set_property("FEATHER", config.feather);
510         output.tag.set_property("DRAW_GUIDES", config.draw_guides);
511         output.tag.set_property("MODE", config.mode);
512         output.append_tag();
513         output.terminate_string();
514 }
515
516
517 void SphereCamMain::read_data(KeyFrame *keyframe)
518 {
519         FileXML input;
520         char string[BCTEXTLEN];
521
522
523         input.set_shared_input(keyframe->xbuf);
524
525         int result = 0;
526
527         while(!result)
528         {
529                 result = input.read_tag();
530
531                 if( !result ) {
532                         if( input.tag.title_is("SPHERECAM") ) {
533                                 for( int i = 0; i < EYES; i++ ) {
534                                         sprintf(string, "ENABLED_%d", i);
535                                         config.enabled[i] = input.tag.get_property(string, config.enabled[i]);
536                                         sprintf(string, "FOV_%d", i);
537                                         config.fov[i] = input.tag.get_property(string, config.fov[i]);
538                                         sprintf(string, "RADIUS_%d", i);
539                                         config.radius[i] = input.tag.get_property(string, config.radius[i]);
540                                         sprintf(string, "CENTER_X_%d", i);
541                                         config.center_x[i] = input.tag.get_property(string, config.center_x[i]);
542                                         sprintf(string, "CENTER_Y_%d", i);
543                                         config.center_y[i] = input.tag.get_property(string, config.center_y[i]);
544                                         sprintf(string, "ROTATE_X_%d", i);
545                                         config.rotate_x[i] = input.tag.get_property(string, config.rotate_x[i]);
546                                         sprintf(string, "ROTATE_Y_%d", i);
547                                         config.rotate_y[i] = input.tag.get_property(string, config.rotate_y[i]);
548                                         sprintf(string, "ROTATE_Z_%d", i);
549                                         config.rotate_z[i] = input.tag.get_property(string, config.rotate_z[i]);
550                                 }
551
552                                 config.feather = input.tag.get_property("FEATHER", config.feather);
553                                 config.draw_guides = input.tag.get_property("DRAW_GUIDES", config.draw_guides);
554                                 config.mode = input.tag.get_property("MODE", config.mode);
555                         }
556                 }
557         }
558 }
559
560
561
562 int SphereCamMain::process_buffer(VFrame *frame,
563         int64_t start_position,
564         double frame_rate)
565 {
566         load_configuration();
567         
568         VFrame *input = new_temp(frame->get_w(), frame->get_h(), frame->get_color_model());
569         
570         read_frame(input, 0, start_position, frame_rate, 0); // use opengl
571
572 //      find_lenses();
573         calculate_extents();
574
575         if( config.mode == SphereCamConfig::DO_NOTHING ) {
576                 get_output()->copy_from(input);
577         }
578         else {
579                 get_output()->clear_frame();
580                 if( !engine ) engine = new SphereCamEngine(this);
581                 engine->process_packages();
582         }
583
584         if( config.draw_guides ) {
585 // input regions
586 // printf("SphereCamMain::process_buffer %d %d %d\n", __LINE__, out_x1[0], out_x4[0]);
587                 for( int eye = 0; eye < EYES; eye++ ) {
588                         if( config.enabled[eye] ) {
589 // input regions
590                                 get_output()->draw_oval(input_x[eye] - radius[eye], 
591                                         input_y[eye] - radius[eye], 
592                                         input_x[eye] + radius[eye], 
593                                         input_y[eye] + radius[eye]);
594
595
596 // output regions.  If they overlap, don't xor out the line
597                                 if( eye == 0 || (out_x1[eye] != out_x1[0] && out_x1[eye] != out_x4[0]) ) {
598                                         get_output()->draw_line(out_x1[eye], 0, out_x1[eye], h);
599                                 }
600                                 
601                                 if( eye == 0 || (out_x4[eye] != out_x4[0] && out_x4[eye] != out_x1[0]) ) {
602                                         get_output()->draw_line(out_x4[eye], 0, out_x4[eye], h);
603                                 }
604
605                                 if( feather != 0 && eye == 1 ) {
606 // crosshatch feather of right eye only, since only the right eye feathers
607                                         int feather_gap = h / 20;
608                                         for( int j = 0; j < h + feather; j += feather_gap ) {
609                                                 draw_feather(out_x1[eye], out_x1[eye] + feather, eye, j);
610                                                 draw_feather(out_x4[eye] - feather, out_x4[eye], eye, j);
611                                         }
612                                 }
613                         }
614                 }
615
616         }
617
618         return 0;
619 }
620
621 void SphereCamMain::draw_feather(int left, int right, int eye, int y)
622 {
623         int slope = 1;
624         if( eye == 1 ) {
625                 slope = -1;
626         }
627
628 // wrap
629         if( left < 0 ) {
630                 int left2 = w + left;
631                 get_output()->draw_line(left2, y, left2 + right - left, y + feather * slope);
632         }
633
634         if( right > w ) {
635                 int right2 = right - w;
636                 get_output()->draw_line(0, y, right2, y + feather * slope);
637
638         }
639
640 // proper
641         get_output()->draw_line(left, y, right, y + feather * slope);
642
643 }
644
645
646 // void SphereCamMain::find_lenses()
647 // {
648 // }
649
650
651 void SphereCamMain::calculate_extents()
652 {
653         w = get_output()->get_w();
654         h = get_output()->get_h();
655
656         feather = (int)(config.feather * w / 100);
657         
658         for( int i = 0; i < EYES; i++ ) {
659 // input regions
660                 input_x[i] = (int)(config.center_x[i] * w / 100);
661                 input_y[i] = (int)(config.center_y[i] * h / 100);
662                 radius[i] = (int)(h / 2 * config.radius[i] / 100);
663
664 // output regions
665                 output_x[i] = (int)(config.rotate_x[i] * w / 100);
666                 output_y[i] = (int)(config.rotate_y[i] * h / 100);
667
668
669 // Assume each lens fills 1/2 the width
670                 out_x1[i] = output_x[i] - w / 4 - feather / 2;
671                 out_x2[i] = output_x[i];
672                 out_x3[i] = output_x[i];
673                 out_x4[i] = output_x[i] + w / 4 + feather / 2;
674         }
675
676 // If the output isn't 50% apart, we have to expand the left eye to fill the feathering region
677 //printf("SphereCamMain::calculate_extents %d %f\n", __LINE__, config.rotate_x[0] - config.rotate_x[1]);
678         float x_separation = config.rotate_x[0] - config.rotate_x[1];
679         if( !EQUIV(fabs(x_separation), 50) ) {
680                 if( x_separation < -50 ) {
681                         out_x4[0] += (-49.5 - x_separation) * w / 100;
682                 }
683                 else
684                 if( x_separation < 0 ) {
685                         out_x1[0] -= (x_separation + 50) * w / 100;
686                 }
687                 else
688                 if( x_separation < 50 ) {
689                         out_x4[0] += (50.5 - x_separation) * w / 100;
690                 }
691                 else {
692                         out_x1[0] -= (x_separation - 49.5) * w / 100;
693                 }
694         }
695
696
697 // wrap around
698         for( int i = 0; i < EYES; i++ ) {
699                 if( out_x1[i] < 0 ) {
700                         out_x1[i] = w + out_x1[i];
701                         out_x2[i] = w;
702                         out_x3[i] = 0;
703                 }
704
705                 if( out_x4[i] > w ) {
706                         out_x2[i] = w;
707                         out_x3[i] = 0;
708                         out_x4[i] -= w;
709                 }
710
711 // printf("SphereCamMain::calculate_extents %d i=%d x=%d y=%d radius=%d\n", 
712 // __LINE__, 
713 // i, 
714 // center_x[i],
715 // center_y[i],
716 // radius[i]);
717         }
718 }
719
720 SphereCamPackage::SphereCamPackage()
721  : LoadPackage() {}
722
723 SphereCamUnit::SphereCamUnit(SphereCamEngine *engine, SphereCamMain *plugin)
724  : LoadClient(engine)
725 {
726         this->plugin = plugin;
727 }
728
729
730 SphereCamUnit::~SphereCamUnit()
731 {
732 }
733
734
735
736 // interpolate & accumulate a pixel in the output
737 #define BLEND_PIXEL(type, components) \
738         if( x_in < 0.0 || x_in >= w - 1 || \
739             y_in < 0.0 || y_in >= h - 1 ) { \
740                 out_row += components; \
741         } \
742         else { \
743                 float y1_fraction = y_in - floor(y_in); \
744                 float y2_fraction = 1.0 - y1_fraction; \
745                 float x1_fraction = x_in - floor(x_in); \
746                 float x2_fraction = 1.0 - x1_fraction; \
747                 type *in_pixel1 = in_rows[(int)y_in] + (int)x_in * components; \
748                 type *in_pixel2 = in_rows[(int)y_in + 1] + (int)x_in * components; \
749                 for( int i = 0; i < components; i++ ) { \
750                         float value = in_pixel1[i] * x2_fraction * y2_fraction + \
751                                 in_pixel2[i] * x2_fraction * y1_fraction + \
752                                 in_pixel1[i + components] * x1_fraction * y2_fraction + \
753                                 in_pixel2[i + components] * x1_fraction * y1_fraction; \
754                         value = *out_row * inv_a + value * a; \
755                         *out_row++ = (type)value; \
756                 } \
757         }
758
759
760 // nearest neighbor & accumulate a pixel in the output
761 #define BLEND_NEAREST(type, components) \
762         if( x_in < 0.0 || x_in >= w - 1 || \
763             y_in < 0.0 || y_in >= h - 1 ) { \
764                 out_row += components; \
765         } \
766         else { \
767                 type *in_pixel = in_rows[(int)y_in] + (int)x_in * components; \
768                 for( int i = 0; i < components; i++ ) { \
769                         float value = in_pixel[i]; \
770                         value = *out_row * inv_a + value * a; \
771                         *out_row++ = (type)value; \
772                 } \
773         }
774
775
776 #define COLORSPACE_SWITCH(function) \
777         switch( plugin->get_input()->get_color_model() ) { \
778         case BC_RGB888:     function(unsigned char, 3, 0x0); break; \
779         case BC_RGBA8888:   function(unsigned char, 4, 0x0); break; \
780         case BC_RGB_FLOAT:  function(float, 3, 0.0); break; \
781         case BC_RGBA_FLOAT: function(float, 4, 0.0); break; \
782         case BC_YUV888:     function(unsigned char, 3, 0x80); break; \
783         case BC_YUVA8888:   function(unsigned char, 4, 0x80); break; \
784         }
785
786 void SphereCamUnit::process_equirect(SphereCamPackage *pkg)
787 {
788         VFrame *input = plugin->get_temp();
789         VFrame *output = plugin->get_output();
790
791
792 // overlay a single eye
793 #define PROCESS_EQUIRECT(type, components, chroma) \
794 { \
795         type **in_rows = (type**)input->get_rows(); \
796         type **out_rows = (type**)output->get_rows(); \
797  \
798         for( int out_y = row1; out_y < row2; out_y++ ) { \
799                 type *out_row = out_rows[out_y]; \
800 /* polar angle - -M_PI/2 to M_PI/2 */ \
801                 float y_diff = out_y - output_y; \
802                 float phi = M_PI / 2 * (y_diff / radius); \
803  \
804                 for( int out_x = 0; out_x < w; out_x++ ) { \
805 /* alpha */ \
806                         float a = alpha[out_x]; \
807                         float inv_a = 1.0f - a; \
808                         if( a > 0 ) { \
809 /* polar angle */ \
810                                 float x_diff = out_x - output_x; \
811 /* -M_PI/2 to M_PI/2 */ \
812                                 float theta = M_PI / 2 * (x_diff / radius); \
813 /* vector in 3D space */ \
814                                 float vect_x = cos(phi) * sin(theta); \
815                                 float vect_y = cos(phi) * cos(theta); \
816                                 float vect_z = sin(phi); \
817 /* fisheye angle & radius */ \
818                                 float theta2 = atan2(vect_z, vect_x) - rotate_z; \
819                                 float phi2 = atan2(hypot(vect_x, vect_z), vect_y); \
820                                 float r = radius * 2 * phi2 / fov; \
821 /* pixel in fisheye space */ \
822                                 float x_in = input_x + r * cos(theta2); \
823                                 float y_in = input_y + r * sin(theta2); \
824  \
825                                 BLEND_PIXEL(type, components) \
826                         } \
827                         else { \
828                                 out_row += components; \
829                         } \
830                 } \
831         } \
832 }
833
834         int row1 = pkg->row1;
835         int row2 = pkg->row2;
836         for( int eye = 0; eye < EYES; eye++ ) {
837                 if( plugin->config.enabled[eye] ) {
838                         int w = plugin->w;
839                         int h = plugin->h;
840                         float fov = plugin->config.fov[eye] * M_PI * 2 / 360;
841 //                      float radius = plugin->radius[eye];
842                         float radius = h / 2;
843                         float input_x = plugin->input_x[eye];
844                         float input_y = plugin->input_y[eye];
845                         float output_x = plugin->output_x[eye];
846                         float output_y = plugin->output_y[eye];
847                         float rotate_z = plugin->config.rotate_z[eye] * M_PI * 2 / 360;
848                         int feather = plugin->feather;
849                         int out_x1 = plugin->out_x1[eye];
850                         int out_x2 = plugin->out_x2[eye];
851                         int out_x3 = plugin->out_x3[eye];
852                         int out_x4 = plugin->out_x4[eye];
853
854 // compute the alpha for every X
855                         float alpha[w];
856                         for( int i = 0; i < w; i++ ) {
857                                 alpha[i] = 0;
858                         }
859
860                         for( int i = out_x1; i < out_x2; i++ ) {
861                                 alpha[i] = 1.0f;
862                         }
863
864                         for( int i = out_x3; i < out_x4; i++ ) {
865                                 alpha[i] = 1.0f;
866                         }
867                         
868                         if( eye == 1 ) {
869                                 for( int i = out_x1; i < out_x1 + feather; i++ ) {
870                                         float value = (float)(i - out_x1) / feather;
871                                         if( i >= w ) {
872                                                 alpha[i - w] = value;
873                                         }
874                                         else {
875                                                 alpha[i] = value;
876                                         }
877                                 }
878
879                                 for( int i = out_x4 - feather; i < out_x4; i++ ) {
880                                         float value = (float)(out_x4 - i) / feather;
881                                         if( i < 0 ) {
882                                                 alpha[w + i] = value;
883                                         }
884                                         else {
885                                                 alpha[i] = value;
886                                         }
887                                 }
888                         }
889                         
890
891 //printf("SphereCamUnit::process_equirect %d rotate_x=%f rotate_y=%f rotate_z=%f\n", 
892 // __LINE__, rotate_x, rotate_y, rotate_z);
893
894                         COLORSPACE_SWITCH(PROCESS_EQUIRECT)
895                 }
896         }
897 }
898
899
900
901 void SphereCamUnit::process_align(SphereCamPackage *pkg)
902 {
903         VFrame *input = plugin->get_temp();
904         VFrame *output = plugin->get_output();
905
906
907 // overlay a single eye
908 #define PROCESS_ALIGN(type, components, chroma) \
909 { \
910         type **in_rows = (type**)input->get_rows(); \
911         type **out_rows = (type**)output->get_rows(); \
912  \
913         for( int out_y = row1; out_y < row2; out_y++ ) { \
914                 type *out_row = out_rows[out_y]; \
915 /* polar angle */ \
916 /* -M_PI/2 to M_PI/2 */ \
917                 float y_diff = out_y - output_y; \
918                 float phi = M_PI / 2 * (y_diff / radius); \
919  \
920                 for( int out_x = 0; out_x < w; out_x++ ) { \
921 /* alpha */ \
922                         float a = alpha[out_x]; \
923                         float inv_a = 1.0f - a; \
924                         if( a > 0 ) { \
925 /* polar angle */ \
926                                 float x_diff = out_x - output_x; \
927 /* -M_PI/2 to M_PI/2 */ \
928                                 float theta = M_PI / 2 * (x_diff / radius); \
929         /* vector in 3D space */ \
930                                 float vect_x = cos(phi) * sin(theta); \
931                                 float vect_y = cos(phi) * cos(theta); \
932                                 float vect_z = sin(phi); \
933         /* fisheye angle & radius */ \
934                                 float theta2 = atan2(vect_z, vect_x) - rotate_z; \
935                                 float phi2 = atan2(hypot(vect_x, vect_z), vect_y); \
936                                 float r = radius * 2 * phi2 / fov; \
937         /* pixel in fisheye space */ \
938                                 float x_in = input_x + r * cos(theta2); \
939                                 float y_in = input_y + r * sin(theta2); \
940         \
941                                 BLEND_NEAREST(type, components) \
942                         } \
943                         else { \
944                                 out_row += components; \
945                         } \
946                 } \
947         } \
948 }
949
950         int row1 = pkg->row1;
951         int row2 = pkg->row2;
952         for( int eye = 0; eye < EYES; eye++ ) {
953                 if( plugin->config.enabled[eye] ) {
954                         int w = plugin->w;
955                         int h = plugin->h;
956                         float fov = plugin->config.fov[eye] * M_PI * 2 / 360;
957 //                      float radius = plugin->radius[eye];
958                         float radius = h / 2;
959                         float input_x = plugin->input_x[eye];
960                         float input_y = plugin->input_y[eye];
961                         float output_x = plugin->output_x[eye];
962                         float output_y = plugin->output_y[eye];
963                         float rotate_z = plugin->config.rotate_z[eye] * M_PI * 2 / 360;
964                         int out_x1 = plugin->out_x1[eye];
965                         int out_x2 = plugin->out_x2[eye];
966                         int out_x3 = plugin->out_x3[eye];
967                         int out_x4 = plugin->out_x4[eye];
968 // left eye is opaque.  right eye overlaps
969 // compute the alpha for every X
970                         float alpha[w];
971                         for( int i = 0; i < w; i++ ) alpha[i] = 0;
972                         float v = eye == 0 || !plugin->config.enabled[0] ? 1.0f : 0.5f;
973                         for( int i = out_x1; i < out_x2; i++ ) alpha[i] = v;
974                         for( int i = out_x3; i < out_x4; i++ ) alpha[i] = v;
975
976                         COLORSPACE_SWITCH(PROCESS_ALIGN)
977                 }
978         }
979 }
980
981 void SphereCamUnit::process_package(LoadPackage *package)
982 {
983         SphereCamPackage *pkg = (SphereCamPackage*)package;
984
985         switch( plugin->config.mode ) {
986         case SphereCamConfig::EQUIRECT:
987                 process_equirect(pkg);
988                 break;
989         case SphereCamConfig::ALIGN:
990                 process_align(pkg);
991                 break;
992         }
993 }
994
995
996 SphereCamEngine::SphereCamEngine(SphereCamMain *plugin)
997  : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
998 // : LoadServer(1, 1)
999 {
1000         this->plugin = plugin;
1001 }
1002
1003 SphereCamEngine::~SphereCamEngine()
1004 {
1005 }
1006
1007 void SphereCamEngine::init_packages()
1008 {
1009         for( int i = 0; i < LoadServer::get_total_packages(); i++ ) {
1010                 SphereCamPackage *package = (SphereCamPackage*)LoadServer::get_package(i);
1011                 package->row1 = plugin->get_input()->get_h() * i / LoadServer::get_total_packages();
1012                 package->row2 = plugin->get_input()->get_h() * (i + 1) / LoadServer::get_total_packages();
1013         }
1014 }
1015
1016 LoadClient* SphereCamEngine::new_client()
1017 {
1018         return new SphereCamUnit(this, plugin);
1019 }
1020
1021 LoadPackage* SphereCamEngine::new_package()
1022 {
1023         return new SphereCamPackage;
1024 }
1025
1026