4 * Copyright (C) 2017 Adam Williams <broadcast at earthling dot net>
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.
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.
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
23 #include "bcdisplayinfo.h"
25 #include "bcsignals.h"
29 #include "spherecam.h"
34 // largely based on equations from http://paulbourke.net/dome/fish2/
36 REGISTER_PLUGIN(SphereCamMain)
39 SphereCamConfig::SphereCamConfig()
43 for( int i = 0; i < EYES; i++ ) {
57 mode = SphereCamConfig::DO_NOTHING;
60 int SphereCamConfig::equivalent(SphereCamConfig &that)
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]) ) {
75 if( feather != that.feather || mode != that.mode ||
76 draw_guides != that.draw_guides ) {
84 void SphereCamConfig::copy_from(SphereCamConfig &that)
89 void SphereCamConfig::interpolate(SphereCamConfig &prev,
90 SphereCamConfig &next,
93 int64_t current_frame)
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);
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;
109 feather = prev.feather * prev_scale + next.feather * next_scale;
110 draw_guides = prev.draw_guides;
116 void SphereCamConfig::boundaries()
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);
128 CLAMP(feather, 0, 50);
132 SphereCamSlider::SphereCamSlider(SphereCamMain *client,
143 gui->get_w() / 2 - client->get_theme()->widget_border * xS(3) - xS(100),
144 gui->get_w() / 2 - client->get_theme()->widget_border * xS(3) - xS(100),
150 this->client = client;
151 this->output = output;
156 int SphereCamSlider::handle_event()
158 *output = get_value();
159 text->update(*output);
160 client->send_configure_change();
165 SphereCamText::SphereCamText(SphereCamMain *client,
167 SphereCamSlider *slider,
171 : BC_TextBox(x, y, xS(100), 1, *output)
174 this->client = client;
175 this->output = output;
176 this->slider = slider;
179 int SphereCamText::handle_event()
181 *output = atof(get_text());
182 slider->update(*output);
183 client->send_configure_change();
188 SphereCamToggle::SphereCamToggle(SphereCamMain *client,
189 int *output, int x, int y, const char *text)
190 : BC_CheckBox(x, y, *output, text)
192 this->output = output;
193 this->client = client;
196 int SphereCamToggle::handle_event()
198 *output = get_value();
199 client->send_configure_change();
204 SphereCamMode::SphereCamMode(SphereCamMain *plugin,
205 SphereCamGUI *gui, int x, int y)
206 : BC_PopupMenu(x, y, calculate_w(gui), "", 1)
208 this->plugin = plugin;
212 int SphereCamMode::handle_event()
214 plugin->config.mode = from_text(get_text());
215 plugin->send_configure_change();
220 void SphereCamMode::create_objects()
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);
228 void SphereCamMode::update(int mode)
230 char string[BCTEXTLEN];
231 sprintf(string, "%s", to_text(mode));
235 int SphereCamMode::calculate_w(SphereCamGUI *gui)
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 + xS(50);
244 int SphereCamMode::from_text(char *text)
246 for( int i = 0; i < 3; i++ ) {
247 if( !strcmp(text, to_text(i)) ) {
252 return SphereCamConfig::EQUIRECT;
255 const char* SphereCamMode::to_text(int mode)
258 case SphereCamConfig::DO_NOTHING:
261 case SphereCamConfig::EQUIRECT:
262 return "Equirectangular";
264 case SphereCamConfig::ALIGN:
268 return "Equirectangular";
272 SphereCamGUI::SphereCamGUI(SphereCamMain *client)
273 : PluginClientWindow(client, xS(640), yS(600), xS(640), yS(600), 0)
275 this->client = client;
278 SphereCamGUI::~SphereCamGUI()
283 void SphereCamGUI::create_objects()
285 int margin = client->get_theme()->widget_border;
286 int margin2 = margin;
291 x[1] = get_w() / 2 + margin / 2;
295 for( int i = 0; i < EYES; i++ ) {
299 add_tool(title = new BC_Title(x[i], y,
300 i == 0 ? _("Left Eye") : _("Right Eye"), LARGEFONT));
301 y += title->get_h() + margin2;
303 add_tool(enabled[i] = new SphereCamToggle(client,
304 &client->config.enabled[i], x3, y, _("Enabled")));
305 y += enabled[i]->get_h() + margin2;
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;
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;
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;
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;
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;
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;
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;
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;
403 //printf("SphereCamGUI::create_objects %d %f\n", __LINE__, client->config.distance);
406 add_tool(draw_guides = new SphereCamToggle(client,
407 &client->config.draw_guides, x3, y, _("Draw guides")));
408 y += draw_guides->get_h() + margin2;
410 add_tool(title = new BC_Title(x3, y, _("Mode:")));
411 add_tool(mode = new SphereCamMode(client,
413 x3 + title->get_w() + margin,
415 mode->create_objects();
416 y += mode->get_h() + margin2;
423 SphereCamMain::SphereCamMain(PluginServer *server)
424 : PluginVClient(server)
430 SphereCamMain::~SphereCamMain()
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"); }
441 void SphereCamMain::update_gui()
443 if( !thread ) return;
444 if( !load_configuration() ) return;
445 ((SphereCamGUI*)thread->window)->lock_window("SphereCamMain::update_gui");
446 SphereCamGUI *window = (SphereCamGUI*)thread->window;
448 for( int i = 0; i < EYES; i++ ) {
449 window->enabled[i]->update(config.enabled[i]);
451 window->fov_slider[i]->update(config.fov[i]);
452 window->fov_text[i]->update(config.fov[i]);
454 window->radius_slider[i]->update(config.radius[i]);
455 window->radius_text[i]->update(config.radius[i]);
457 window->centerx_slider[i]->update(config.center_x[i]);
458 window->centerx_text[i]->update(config.center_x[i]);
460 window->centery_slider[i]->update(config.center_y[i]);
461 window->centery_text[i]->update(config.center_y[i]);
463 window->rotatex_slider[i]->update(config.rotate_x[i]);
464 window->rotatex_text[i]->update(config.rotate_x[i]);
466 window->rotatey_slider[i]->update(config.rotate_y[i]);
467 window->rotatey_text[i]->update(config.rotate_y[i]);
469 window->rotatez_slider[i]->update(config.rotate_z[i]);
470 window->rotatez_text[i]->update(config.rotate_z[i]);
473 window->feather_slider->update(config.feather);
474 window->feather_text->update(config.feather);
476 window->mode->update(config.mode);
477 window->draw_guides->update(config.draw_guides);
478 window->unlock_window();
481 void SphereCamMain::save_data(KeyFrame *keyframe)
484 char string[BCTEXTLEN];
486 // cause data to be stored directly in text
487 output.set_shared_output(keyframe->xbuf);
488 output.tag.set_title("SPHERECAM");
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]);
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);
513 output.terminate_string();
517 void SphereCamMain::read_data(KeyFrame *keyframe)
520 char string[BCTEXTLEN];
523 input.set_shared_input(keyframe->xbuf);
529 result = input.read_tag();
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]);
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);
562 int SphereCamMain::process_buffer(VFrame *frame,
563 int64_t start_position,
566 load_configuration();
568 VFrame *input = new_temp(frame->get_w(), frame->get_h(), frame->get_color_model());
570 read_frame(input, 0, start_position, frame_rate, 0); // use opengl
575 if( config.mode == SphereCamConfig::DO_NOTHING ) {
576 get_output()->copy_from(input);
579 get_output()->clear_frame();
580 if( !engine ) engine = new SphereCamEngine(this);
581 engine->process_packages();
584 if( config.draw_guides ) {
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] ) {
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]);
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);
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);
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);
621 void SphereCamMain::draw_feather(int left, int right, int eye, int y)
630 int left2 = w + left;
631 get_output()->draw_line(left2, y, left2 + right - left, y + feather * slope);
635 int right2 = right - w;
636 get_output()->draw_line(0, y, right2, y + feather * slope);
641 get_output()->draw_line(left, y, right, y + feather * slope);
646 // void SphereCamMain::find_lenses()
651 void SphereCamMain::calculate_extents()
653 w = get_output()->get_w();
654 h = get_output()->get_h();
656 feather = (int)(config.feather * w / 100);
658 for( int i = 0; i < EYES; i++ ) {
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);
665 output_x[i] = (int)(config.rotate_x[i] * w / 100);
666 output_y[i] = (int)(config.rotate_y[i] * h / 100);
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;
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;
684 if( x_separation < 0 ) {
685 out_x1[0] -= (x_separation + 50) * w / 100;
688 if( x_separation < 50 ) {
689 out_x4[0] += (50.5 - x_separation) * w / 100;
692 out_x1[0] -= (x_separation - 49.5) * w / 100;
698 for( int i = 0; i < EYES; i++ ) {
699 if( out_x1[i] < 0 ) {
700 out_x1[i] = w + out_x1[i];
705 if( out_x4[i] > w ) {
711 // printf("SphereCamMain::calculate_extents %d i=%d x=%d y=%d radius=%d\n",
720 SphereCamPackage::SphereCamPackage()
723 SphereCamUnit::SphereCamUnit(SphereCamEngine *engine, SphereCamMain *plugin)
726 this->plugin = plugin;
730 SphereCamUnit::~SphereCamUnit()
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; \
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; \
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; \
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; \
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; \
786 void SphereCamUnit::process_equirect(SphereCamPackage *pkg)
788 VFrame *input = plugin->get_temp();
789 VFrame *output = plugin->get_output();
792 // overlay a single eye
793 #define PROCESS_EQUIRECT(type, components, chroma) \
795 type **in_rows = (type**)input->get_rows(); \
796 type **out_rows = (type**)output->get_rows(); \
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); \
804 for( int out_x = 0; out_x < w; out_x++ ) { \
806 float a = alpha[out_x]; \
807 float inv_a = 1.0f - a; \
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); \
825 BLEND_PIXEL(type, components) \
828 out_row += components; \
834 int row1 = pkg->row1;
835 int row2 = pkg->row2;
836 for( int eye = 0; eye < EYES; eye++ ) {
837 if( plugin->config.enabled[eye] ) {
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];
854 // compute the alpha for every X
856 for( int i = 0; i < w; i++ ) {
860 for( int i = out_x1; i < out_x2; i++ ) {
864 for( int i = out_x3; i < out_x4; i++ ) {
869 for( int i = out_x1; i < out_x1 + feather; i++ ) {
870 float value = (float)(i - out_x1) / feather;
872 alpha[i - w] = value;
879 for( int i = out_x4 - feather; i < out_x4; i++ ) {
880 float value = (float)(out_x4 - i) / feather;
882 alpha[w + i] = value;
891 //printf("SphereCamUnit::process_equirect %d rotate_x=%f rotate_y=%f rotate_z=%f\n",
892 // __LINE__, rotate_x, rotate_y, rotate_z);
894 COLORSPACE_SWITCH(PROCESS_EQUIRECT)
901 void SphereCamUnit::process_align(SphereCamPackage *pkg)
903 VFrame *input = plugin->get_temp();
904 VFrame *output = plugin->get_output();
907 // overlay a single eye
908 #define PROCESS_ALIGN(type, components, chroma) \
910 type **in_rows = (type**)input->get_rows(); \
911 type **out_rows = (type**)output->get_rows(); \
913 for( int out_y = row1; out_y < row2; out_y++ ) { \
914 type *out_row = out_rows[out_y]; \
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); \
920 for( int out_x = 0; out_x < w; out_x++ ) { \
922 float a = alpha[out_x]; \
923 float inv_a = 1.0f - a; \
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); \
941 BLEND_NEAREST(type, components) \
944 out_row += components; \
950 int row1 = pkg->row1;
951 int row2 = pkg->row2;
952 for( int eye = 0; eye < EYES; eye++ ) {
953 if( plugin->config.enabled[eye] ) {
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
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;
976 COLORSPACE_SWITCH(PROCESS_ALIGN)
981 void SphereCamUnit::process_package(LoadPackage *package)
983 SphereCamPackage *pkg = (SphereCamPackage*)package;
985 switch( plugin->config.mode ) {
986 case SphereCamConfig::EQUIRECT:
987 process_equirect(pkg);
989 case SphereCamConfig::ALIGN:
996 SphereCamEngine::SphereCamEngine(SphereCamMain *plugin)
997 : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
998 // : LoadServer(1, 1)
1000 this->plugin = plugin;
1003 SphereCamEngine::~SphereCamEngine()
1007 void SphereCamEngine::init_packages()
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();
1016 LoadClient* SphereCamEngine::new_client()
1018 return new SphereCamUnit(this, plugin);
1021 LoadPackage* SphereCamEngine::new_package()
1023 return new SphereCamPackage;