a6777be275f8cd1aa1bbff7a7b47c3090b27b195
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / lens / lens.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "bchash.h"
24 #include "bcsignals.h"
25 #include "clip.h"
26 #include "file.h"
27 #include "filexml.h"
28 #include "interp.h"
29 #include "language.h"
30 #include "lens.h"
31
32
33 #include <string.h>
34
35
36
37
38 REGISTER_PLUGIN(LensMain)
39
40 LensConfig::LensConfig()
41 {
42         reset();
43 }
44
45 void LensConfig::reset()
46 {
47         for( int i=0; i<FOV_CHANNELS; ++i )
48                 fov[i] = 1.0;
49         aspect = 1.0;
50         radius = 1.0;
51         mode = SPHERICAL_SHRINK;
52         interp = INTERP_DEFAULT;
53         center_x = 50.0;
54         center_y = 50.0;
55         draw_guides = 0;
56 }
57
58 int LensConfig::equivalent(LensConfig &that)
59 {
60         for( int i=0; i<FOV_CHANNELS; ++i )
61                 if( !EQUIV(fov[i], that.fov[i]) ) return 0;
62         return EQUIV(aspect, that.aspect) &&
63                 EQUIV(radius, that.radius) &&
64                 EQUIV(center_x, that.center_x) &&
65                 EQUIV(center_y, that.center_y) &&
66                 mode == that.mode &&
67                 interp == that.interp &&
68                 draw_guides == that.draw_guides;
69 }
70
71 void LensConfig::copy_from(LensConfig &that)
72 {
73         for( int i=0; i<FOV_CHANNELS; ++i )
74                 fov[i] = that.fov[i];
75         aspect = that.aspect;
76         radius = that.radius;
77         mode = that.mode;
78         interp = that.interp;
79         center_x = that.center_x;
80         center_y = that.center_y;
81         draw_guides = that.draw_guides;
82 }
83
84 void LensConfig::interpolate(LensConfig &prev, LensConfig &next,
85         int64_t prev_frame, int64_t next_frame, int64_t current_frame)
86 {
87         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
88         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
89
90         for( int i=0; i<FOV_CHANNELS; ++i )
91                 fov[i] = prev.fov[i] * prev_scale + next.fov[i] * next_scale;
92         aspect = prev.aspect * prev_scale + next.aspect * next_scale;
93         radius = prev.radius * prev_scale + next.radius * next_scale;
94         center_x = prev.center_x * prev_scale + next.center_x * next_scale;
95         center_y = prev.center_y * prev_scale + next.center_y * next_scale;
96         mode = prev.mode;
97         interp = prev.interp;
98         draw_guides = prev.draw_guides;
99
100         boundaries();
101 }
102
103 void LensConfig::boundaries()
104 {
105         CLAMP(center_x, 0.0, 99.0);
106         CLAMP(center_y, 0.0, 99.0);
107         for( int i=0; i<FOV_CHANNELS; ++i )
108                 CLAMP(fov[i], 0.0, 1.0);
109         CLAMP(aspect, 0.3, 3.0);
110         CLAMP(radius, 0.3, 3.0);
111 }
112
113
114
115
116 LensSlider::LensSlider(LensMain *plugin, LensGUI *gui,
117         LensText *text, float *output, int x, int y, float min, float max)
118  : BC_FSlider(x, y, 0, xS(200), yS(200), min, max, *output)
119 {
120         this->gui = gui;
121         this->plugin = plugin;
122         this->output = output;
123         this->text = text;
124         set_precision(0.01);
125 }
126
127 int LensSlider::handle_event()
128 {
129         float prev_output = *output;
130         *output = get_value();
131         text->update(*output);
132
133         float difference = *output - prev_output;
134         int is_fov = 0;
135
136         if( plugin->lock ) {
137                 for( int i=0; i<FOV_CHANNELS; ++i ) {
138                         if( output == &plugin->config.fov[i] ) {
139                                 is_fov = 1;
140                                 break;
141                         }
142                 }
143
144                 if( is_fov ) {
145                         for( int i=0; i<FOV_CHANNELS; ++i ) {
146                                 if( output != &plugin->config.fov[i] ) {
147                                         plugin->config.fov[i] += difference;
148                                         plugin->config.boundaries();
149                                         gui->fov_slider[i]->update(plugin->config.fov[i]);
150                                         gui->fov_text[i]->update(plugin->config.fov[i]);
151                                 }
152                         }
153                 }
154         }
155
156         plugin->send_configure_change();
157         return 1;
158 }
159
160
161
162 LensText::LensText(LensMain *plugin, LensGUI *gui,
163         LensSlider *slider, float *output, int x, int y)
164  : BC_TextBox(x, y, xS(100), 1, *output)
165 {
166         this->gui = gui;
167         this->plugin = plugin;
168         this->output = output;
169         this->slider = slider;
170 }
171
172 int LensText::handle_event()
173 {
174         float prev_output = *output;
175         *output = atof(get_text());
176         slider->update(*output);
177
178         float difference = *output - prev_output;
179         int is_fov = 0;
180
181         if( plugin->lock ) {
182                 for( int i=0; i<FOV_CHANNELS; ++i ) {
183                         if( output == &plugin->config.fov[i] ) {
184                                 is_fov = 1;
185                                 break;
186                         }
187                 }
188
189                 if( is_fov ) {
190                         for( int i=0; i<FOV_CHANNELS; ++i ) {
191                                 if( output != &plugin->config.fov[i] ) {
192                                         plugin->config.fov[i] += difference;
193                                         plugin->config.boundaries();
194                                         gui->fov_slider[i]->update(plugin->config.fov[i]);
195                                         gui->fov_text[i]->update(plugin->config.fov[i]);
196                                 }
197                         }
198                 }
199         }
200
201         plugin->send_configure_change();
202         return 1;
203 }
204
205
206 LensToggle::LensToggle(LensMain *plugin,
207         int *output,
208         int x,
209         int y,
210         const char *text)
211  : BC_CheckBox(x, y, *output, text)
212 {
213         this->output = output;
214         this->plugin = plugin;
215 }
216
217 int LensToggle::handle_event()
218 {
219         *output = get_value();
220         plugin->send_configure_change();
221         return 1;
222 }
223
224
225 LensMode::LensMode(LensMain *plugin, LensGUI *gui, int x, int y)
226  : BC_PopupMenu(x, y, calculate_w(gui), "", 1)
227 {
228         this->plugin = plugin;
229         this->gui = gui;
230 }
231
232 int LensMode::handle_event()
233 {
234         plugin->config.mode = from_text(get_text());
235         plugin->send_configure_change();
236         return 1;
237
238 }
239
240 void LensMode::create_objects()
241 {
242         add_item(new BC_MenuItem(to_text(LensConfig::SPHERICAL_SHRINK)));
243         add_item(new BC_MenuItem(to_text(LensConfig::SPHERICAL_STRETCH)));
244         add_item(new BC_MenuItem(to_text(LensConfig::RECTILINEAR_STRETCH)));
245         add_item(new BC_MenuItem(to_text(LensConfig::RECTILINEAR_SHRINK)));
246         update(plugin->config.mode);
247 }
248
249 void LensMode::update(int mode)
250 {
251         char string[BCTEXTLEN];
252         sprintf(string, "%s", to_text(mode));
253         set_text(string);
254 }
255
256 int LensMode::calculate_w(LensGUI *gui)
257 {
258         int result = 0;
259         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(LensConfig::SPHERICAL_STRETCH)));
260         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(LensConfig::SPHERICAL_SHRINK)));
261         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(LensConfig::RECTILINEAR_STRETCH)));
262         result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(LensConfig::RECTILINEAR_SHRINK)));
263         return result + xS(50);
264 }
265
266 int LensMode::from_text(char *text)
267 {
268         if( !strcmp(text, _("Sphere Stretch")) )           return LensConfig::SPHERICAL_STRETCH;
269         else if( !strcmp(text, _("Sphere Shrink")) )       return LensConfig::SPHERICAL_SHRINK;
270         else if( !strcmp(text, _("Rectilinear Stretch")) ) return LensConfig::RECTILINEAR_STRETCH;
271         else if( !strcmp(text, _("Rectilinear Shrink")) )  return LensConfig::RECTILINEAR_SHRINK;
272         return LensConfig::SPHERICAL_STRETCH;
273 }
274
275 const char* LensMode::to_text(int mode)
276 {
277         switch( mode ) {
278         case LensConfig::SPHERICAL_STRETCH:   return _("Sphere Stretch");
279         case LensConfig::SPHERICAL_SHRINK:    return _("Sphere Shrink");
280         case LensConfig::RECTILINEAR_STRETCH: return _("Rectilinear Stretch");
281         case LensConfig::RECTILINEAR_SHRINK:  return _("Rectilinear Shrink");
282         }
283         return _("Sphere Stretch");
284 }
285
286
287 // LensPresets::LensPresets(LensMain *plugin,
288 //      LensGUI *gui, int x, int y, int w)
289 //  : BC_PopupMenu(x, y, w, "", 1)
290 // {
291 //      this->plugin = plugin;
292 //      this->gui = gui;
293 // }
294 //
295 // int LensPresets::handle_event()
296 // {
297 //      return 1;
298 // }
299 //
300 // void LensPresets::create_objects()
301 // {
302 // // Remove existing items
303 //      int total = total_items();
304 //      for( int i=0; i<total; ++i ) {
305 //              del_item();
306 //      }
307 //
308 // // Create current items
309 //      plugin->load_presets();
310 //      for( int i=0; i<plugin->presets.total; ++i ) {
311 //              add_item(new BC_MenuItem(plugin->presets.values[i]->title));
312 //      }
313 //
314 // // Update text
315 //      if( plugin->current_preset >= 0 &&
316 //          plugin->current_preset < plugin->presets.total ) {
317 //              set_text(plugin->presets.values[plugin->current_preset]->title);
318 //      }
319 //      else {
320 //              set_text("None");
321 //      }
322 // }
323 //
324 // int LensPresets::from_text(LensMain *plugin, char *text)
325 // {
326 // }
327 //
328 // char* LensPresets::to_text(LensMain *plugin, int preset)
329 // {
330 // }
331 //
332 // void LensPresets::update(int preset)
333 // {
334 // }
335 //
336 //
337 // LensSavePreset::LensSavePreset(LensMain *plugin, LensGUI *gui, int x, int y)
338 //  : BC_GenericButton(x, y, _("Save Preset"))
339 // {
340 //      this->plugin = plugin;
341 //      this->gui = gui;
342 // }
343 //
344 // int LensSavePreset::handle_event()
345 // {
346 // }
347 //
348 //
349 // LensDeletePreset::LensDeletePreset(LensMain *plugin, LensGUI *gui, int x, int y)
350 //  : BC_GenericButton(x, y, _("Delete Preset"))
351 // {
352 // }
353 //
354 // int LensDeletePreset::handle_event()
355 // {
356 // }
357 //
358 //
359 // LensPresetText::LensPresetText(LensMain *plugin, LensGUI *gui, int x, int y, int w)
360 //  : BC_TextBox(x, y, w, 1, "")
361 // {
362 //      this->plugin = plugin;
363 //      this->gui = gui;
364 // }
365 //
366 // int LensPresetText::handle_event()
367 // {
368 // }
369
370
371 LensInterpItem::LensInterpItem(const char *text, int id)
372  : BC_MenuItem(text)
373 {
374         this->id = id;
375 }
376
377 int LensInterpItem::handle_event()
378 {
379         LensInterp *interp = (LensInterp *)get_popup_menu();
380         interp->set_value(id);
381         LensMain *plugin = interp->plugin;
382         plugin->config.interp = id;
383         plugin->send_configure_change();
384         return 1;
385 }
386
387 LensInterp::LensInterp(LensMain *plugin, int x, int y)
388  : BC_PopupMenu(x, y, xS(120), "")
389 {
390         this->plugin = plugin;
391 }
392
393 void LensInterp::create_objects()
394 {
395         add_item(new LensInterpItem(_("Default"), LensConfig::INTERP_DEFAULT));
396         add_item(new LensInterpItem(_("Nearest"), LensConfig::INTERP_NEAREST));
397         add_item(new LensInterpItem(_("BiLinear"), LensConfig::INTERP_BILINEAR));
398         add_item(new LensInterpItem(_("BiCubic"), LensConfig::INTERP_BICUBIC));
399         set_value(plugin->config.interp);
400 }
401
402 void LensInterp::set_value(int id)
403 {
404         for( int i=0, n=total_items(); i<n; ++i ) {
405                 LensInterpItem *item = (LensInterpItem *)get_item(i);
406                 if( item->id == id ) {
407                         set_text(item->get_text());
408                         value = id;
409                         return;
410                 }
411         }
412 }
413
414 int LensInterp::get_value()
415 {
416         return value;
417 }
418
419
420 LensReset::LensReset(LensMain *plugin, LensGUI *gui, int x, int y)
421  : BC_GenericButton(x, y, _("Reset"))
422 {
423         this->plugin = plugin;
424         this->gui = gui;
425 }
426
427 int LensReset::handle_event()
428 {
429         plugin->config.reset();
430         gui->update_gui();
431         plugin->send_configure_change();
432         return 1;
433 }
434
435
436 LensGUI::LensGUI(LensMain *plugin)
437  : PluginClientWindow(plugin, xS(350), yS(556), xS(350), yS(556), 0)
438 {
439         this->plugin = plugin;
440 }
441
442 LensGUI::~LensGUI()
443 {
444 }
445
446
447 void LensGUI::create_objects()
448 {
449         int xs5 = xS(5), xs10 = xS(10);
450         int ys5 = yS(5), ys10 = yS(10);
451         int x = xs10;
452         int y = ys10;
453         int x1;
454         BC_Title *title = 0;
455         LensToggle *toggle;
456
457         for( int i=0; i<FOV_CHANNELS; ++i ) {
458                 switch( i ) {
459                 case 0: add_tool(title = new BC_Title(x, y, _("R Field of View:"))); break;
460                 case 1: add_tool(title = new BC_Title(x, y, _("G Field of View:"))); break;
461                 case 2: add_tool(title = new BC_Title(x, y, _("B Field of View:"))); break;
462                 case 3: add_tool(title = new BC_Title(x, y, _("A Field of View:"))); break;
463                 }
464
465                 y += title->get_h() + ys5;
466                 add_tool(fov_slider[i] = new LensSlider(plugin, this,
467                         0, &plugin->config.fov[i], x, y, 0.0001, 1.0));
468                 x1 = x + fov_slider[i]->get_w() + xs5;
469                 add_tool(fov_text[i] = new LensText(plugin, this,
470                         fov_slider[i], &plugin->config.fov[i], x1, y));
471                 fov_slider[i]->text = fov_text[i];
472                 y += fov_text[i]->get_h() + ys5;
473         }
474
475         add_tool(toggle = new LensToggle(plugin, &plugin->lock, x, y, _("Lock")));
476         y += toggle->get_h() + ys10;
477
478         BC_Bar *bar;
479         add_tool(bar = new BC_Bar(x, y, get_w() - x * 2));
480         y += bar->get_h() + ys5;
481
482         add_tool(title = new BC_Title(x, y, _("Aspect Ratio:")));
483         y += title->get_h() + ys5;
484         add_tool(aspect_slider = new LensSlider(plugin, this,
485                 0, &plugin->config.aspect, x, y, 0.333, 3.0));
486         x1 = x + aspect_slider->get_w() + xs5;
487         add_tool(aspect_text = new LensText(plugin, this,
488                 aspect_slider, &plugin->config.aspect, x1, y));
489         aspect_slider->text = aspect_text;
490         y += aspect_text->get_h() + ys5;
491
492         add_tool(title = new BC_Title(x, y, _("Radius:")));
493         y += title->get_h() + ys5;
494         add_tool(radius_slider = new LensSlider(plugin, this,
495                 0, &plugin->config.radius, x, y, 0.333, 3.0));
496         x1 = x + radius_slider->get_w() + xs5;
497         add_tool(radius_text = new LensText(plugin, this,
498                 radius_slider, &plugin->config.radius, x1, y));
499         radius_slider->text = radius_text;
500         y += radius_text->get_h() + ys5;
501
502         add_tool(title = new BC_Title(x, y, _("Center X:")));
503         y += title->get_h() + ys5;
504         add_tool(centerx_slider = new LensSlider(plugin, this,
505                 0, &plugin->config.center_x, x, y, 0.0, 99.0));
506         x1 = x + centerx_slider->get_w() + xs5;
507         add_tool(centerx_text = new LensText(plugin, this,
508                 centerx_slider, &plugin->config.center_x, x1, y));
509         centerx_slider->text = centerx_text;
510         centerx_slider->set_precision(1.0);
511         y += centerx_text->get_h() + ys5;
512
513
514         add_tool(title = new BC_Title(x, y, _("Center Y:")));
515         y += title->get_h() + ys5;
516         add_tool(centery_slider = new LensSlider(plugin, this,
517                 0, &plugin->config.center_y, x, y, 0.0, 99.0));
518         x1 = x + centery_slider->get_w() + xs5;
519         add_tool(centery_text = new LensText(plugin, this,
520                 centery_slider, &plugin->config.center_y, x1, y));
521         centery_slider->text = centery_text;
522         centery_slider->set_precision(1.0);
523         y += centery_text->get_h() + ys10;
524
525         add_tool(bar = new BC_Bar(x, y, get_w() - x * 2));
526         y += bar->get_h() + ys5;
527
528
529 //      add_tool(reverse = new LensToggle(plugin,
530 //              &plugin->config.reverse, x, y, _("Reverse")));
531 //      y += reverse->get_h() + ys5;
532         add_tool(draw_guides = new LensToggle(plugin,
533                 &plugin->config.draw_guides, x, y, _("Draw center")));
534         y += draw_guides->get_h() + ys5;
535
536
537         add_tool(title = new BC_Title(x, y, _("Mode:")));
538         add_tool(mode = new LensMode(plugin, this,
539                 x + title->get_w() + xs5, y));
540         mode->create_objects();
541         y += mode->get_h() + ys5;
542
543         add_tool(title = new BC_Title(x, y, _("Interp:")));
544         x1 = x + title->get_w() + xs5;
545         add_tool(interp = new LensInterp(plugin, x1, y));
546         interp->create_objects();
547         y += interp->get_h() + ys5;
548
549         add_tool(reset = new LensReset(plugin, this, x, y));
550         y += reset->get_h() + ys5;
551
552 //      add_tool(title = new BC_Title(x, y, _("Preset:")));
553 //      add_tool(presets = new LensPresets(plugin, this,
554 //              x + title->get_w() + xs5, y, get_w() - x - title->get_w() - xS(50)));
555 //      presets->create_objects();
556 //      y += presets->get_h() + ys5;
557 //
558 //      add_tool(save_preset = new LensSavePreset(plugin, this, x, y));
559 //      add_tool(preset_text = new LensPresetText(plugin, this,
560 //              x + save_preset->get_w() + xs5, y,
561 //              get_w() - x - save_preset->get_w() - xs10));
562 //      y += preset_text->get_h() + ys5;
563 //      add_tool(delete_preset = new LensDeletePreset(plugin, this, x, y));
564
565         show_window();
566         flush();
567 }
568
569
570 LensMain::LensMain(PluginServer *server)
571  : PluginVClient(server)
572 {
573
574         engine = 0;
575         lock = 0;
576         current_preset = -1;
577 }
578
579 LensMain::~LensMain()
580 {
581
582         delete engine;
583         presets.remove_all_objects();
584 }
585
586 NEW_WINDOW_MACRO(LensMain, LensGUI)
587 LOAD_CONFIGURATION_MACRO(LensMain, LensConfig)
588 int LensMain::is_realtime() { return 1; }
589 const char* LensMain::plugin_title() { return N_("Lens"); }
590
591 void LensMain::update_gui()
592 {
593         if( !thread ) return;
594         if( !load_configuration() ) return;
595         ((LensGUI *)thread->window)->lock_window("LensMain::update_gui");
596         LensGUI *gui = (LensGUI *)thread->window;
597         gui->update_gui();
598         gui->unlock_window();
599 }
600
601 void LensGUI::update_gui()
602 {
603         LensConfig &config = plugin->config;
604         for( int i=0; i<FOV_CHANNELS; ++i ) {
605                 fov_slider[i]->update(config.fov[i]);
606                 fov_text[i]->update(config.fov[i]);
607         }
608         aspect_slider->update(config.aspect);
609         aspect_text->update(config.aspect);
610         radius_slider->update(config.radius);
611         radius_text->update(config.radius);
612         centerx_slider->update(config.center_x);
613         centerx_text->update(config.center_x);
614         centery_slider->update(config.center_y);
615         centery_text->update(config.center_y);
616         mode->update(config.mode);
617         draw_guides->update(config.draw_guides);
618         interp->set_value(config.interp);
619 }
620
621 //void LensMain::save_presets()
622 //{
623 //      char path[BCTEXTLEN], string[BCTEXTLEN];
624 //      sprintf(path, "%s/lenspresets.rc", File::get_config_path());
625 //      BC_Hash *defaults = new BC_Hash(path);
626 //
627 //// Save presets
628 //      defaults->update("TOTAL_PRESETS", presets.total);
629 //      for( int i=0; i<presets.total; ++i ) {
630 //              LensPreset *preset = presets.values[i];
631 //              sprintf(string, "TITLE_%d", i);
632 //              defaults->update(string, preset->title);
633 //
634 //              for( int j=0; j<FOV_CHANNELS; ++j ) {
635 //                      sprintf(string, "FOCAL_LENGTH_%d_%d", i, j);
636 //                      defaults->update(string, preset->fov[j]);
637 //              }
638 //
639 //              sprintf(string, "ASPECT_%d", i);
640 //              defaults->update(string, preset->aspect);
641 //              sprintf(string, "RADIUS_%d", i);
642 //              defaults->update(string, preset->radius);
643 //              sprintf(string, "MODE_%d", i);
644 //              defaults->update(string, preset->mode);
645 //      }
646 //
647 //      defaults->save();
648 //      delete defaults;
649 //}
650
651
652 void LensMain::save_data(KeyFrame *keyframe)
653 {
654         FileXML output;
655         char string[BCTEXTLEN];
656
657
658
659 // cause data to be stored directly in text
660         output.set_shared_output(keyframe->xbuf);
661         output.tag.set_title("LENS");
662         for( int i = 0; i < FOV_CHANNELS; ++i ) {
663                 sprintf(string, "FOCAL_LENGTH%d", i);
664                 output.tag.set_property(string, config.fov[i]);
665         }
666         output.tag.set_property("ASPECT", config.aspect);
667         output.tag.set_property("RADIUS", config.radius);
668         output.tag.set_property("MODE", config.mode);
669         output.tag.set_property("INTERP", config.interp);
670         output.tag.set_property("CENTER_X", config.center_x);
671         output.tag.set_property("CENTER_Y", config.center_y);
672         output.tag.set_property("DRAW_GUIDES", config.draw_guides);
673         output.append_tag();
674         output.tag.set_title("/LENS");
675         output.append_tag();
676         output.append_newline();
677         output.terminate_string();
678
679 }
680
681
682 void LensMain::read_data(KeyFrame *keyframe)
683 {
684         FileXML input;
685         char string[BCTEXTLEN];
686
687
688         input.set_shared_input(keyframe->xbuf);
689
690         int result = 0;
691
692         while(!result)
693         {
694                 result = input.read_tag();
695
696                 if( !result ) {
697                         if( input.tag.title_is("LENS") ) {
698                                 for( int i=0; i<FOV_CHANNELS; ++i ) {
699                                         sprintf(string, "FOCAL_LENGTH%d", i);
700                                         config.fov[i] = input.tag.get_property(string, config.fov[i]);
701                                 }
702                                 config.aspect = input.tag.get_property("ASPECT", config.aspect);
703                                 config.radius = input.tag.get_property("RADIUS", config.radius);
704                                 config.mode = input.tag.get_property("MODE", config.mode);
705                                 config.interp = input.tag.get_property("INTERP", config.interp);
706                                 config.center_x = input.tag.get_property("CENTER_X", config.center_x);
707                                 config.center_y = input.tag.get_property("CENTER_Y", config.center_y);
708                                 config.draw_guides = input.tag.get_property("DRAW_GUIDES", config.draw_guides);
709                         }
710                 }
711         }
712 }
713
714
715
716 int LensMain::process_buffer(VFrame *frame,
717         int64_t start_position,
718         double frame_rate)
719 {
720         load_configuration();
721         int use_opengl = config.interp != LensConfig::INTERP_DEFAULT ? 0 :
722                 get_use_opengl();
723         VFrame *input = use_opengl ? frame :
724                 new_temp(frame->get_w(), frame->get_h(), frame->get_color_model());
725         read_frame(input, 0, start_position, frame_rate, use_opengl);
726
727         if( use_opengl ) {
728                 run_opengl();
729                 return 0;
730         }
731
732         if( !engine ) engine = new LensEngine(this);
733         engine->process_packages();
734
735         if( config.draw_guides ) {
736 // Draw center
737 #define CENTER_H 20
738 #define CENTER_W 20
739 #define DRAW_GUIDES(components, type, max) \
740 { \
741         type **rows = (type**)get_output()->get_rows(); \
742         if( (center_x >= 0 && center_x < w) || (center_y >= 0 && center_y < h) ) { \
743                 type *hrow = rows[center_y] + components * (center_x - CENTER_W / 2); \
744                 for( int i=center_x-CENTER_W/2; i<=center_x+CENTER_W/2; ++i ) { \
745                         if( i >= 0 && i < w ) { \
746                                 hrow[0] = max - hrow[0]; \
747                                 hrow[1] = max - hrow[1]; \
748                                 hrow[2] = max - hrow[2]; \
749                                 hrow += components; \
750                         } \
751                 } \
752  \
753                 for( int i=center_y-CENTER_H/2; i<=center_y+CENTER_H/2; ++i ) { \
754                         if( i >= 0 && i < h ) { \
755                                 type *vrow = rows[i] + center_x * components; \
756                                 vrow[0] = max - vrow[0]; \
757                                 vrow[1] = max - vrow[1]; \
758                                 vrow[2] = max - vrow[2]; \
759                         } \
760                 } \
761         } \
762 }
763
764                 int w = get_output()->get_w();
765                 int h = get_output()->get_h();
766                 int center_x = (int)(config.center_x * w / 100);
767                 int center_y = (int)(config.center_y * h / 100);
768                 switch( get_output()->get_color_model() ) {
769                 case BC_RGB_FLOAT:
770                         DRAW_GUIDES(3, float, 1.0)
771                         break;
772                 case BC_RGBA_FLOAT:
773                         DRAW_GUIDES(4, float, 1.0)
774                         break;
775                 case BC_RGB888:
776                         DRAW_GUIDES(3, unsigned char, 0xff)
777                         break;
778                 case BC_RGBA8888:
779                         DRAW_GUIDES(4, unsigned char, 0xff)
780                         break;
781                 case BC_YUV888:
782                         DRAW_GUIDES(3, unsigned char, 0xff)
783                         break;
784                 case BC_YUVA8888:
785                         DRAW_GUIDES(4, unsigned char, 0xff)
786                         break;
787                 }
788         }
789
790         return 0;
791 }
792
793
794 int LensMain::handle_opengl()
795 {
796 #ifdef HAVE_GL
797         static const char *shrink_frag =
798                 "uniform sampler2D tex;\n"
799                 "uniform vec2 texture_extents;\n"
800                 "uniform vec2 image_extents;\n"
801                 "uniform vec2 aspect;\n"
802                 "uniform vec2 center_coord;\n"
803                 "uniform vec4 border_color;\n"
804                 "uniform vec4 r;\n"
805                 "uniform vec4 max_z;\n"
806                 "void main()\n"
807                 "{\n"
808                 "       vec2 outcoord = gl_TexCoord[0].st * texture_extents;\n"
809                 "       vec2 coord_diff = outcoord - center_coord;\n"
810                 "       if( coord_diff.x == 0.0 && coord_diff.y == 0.0 )\n"
811                 "       {\n"
812                 "               gl_FragColor = texture2D(tex, outcoord);\n"
813                 "       }\n"
814                 "       else\n"
815                 "       {\n"
816                 "               float z = sqrt(coord_diff.x * coord_diff.x +\n"
817                 "                                               coord_diff.y * coord_diff.y);\n"
818                 "               float a2 = atan(coord_diff.y, coord_diff.x);\n"
819                 "               vec4 a1 = asin(vec4(z, z, z, z) / r);\n"
820                 "               vec4 z_in = a1 * max_z * 2.0 / 3.14159;\n"
821                 "               vec4 in_x;\n"
822                 "               vec4 in_y;\n"
823                 "               in_x = z_in * cos(a2) * aspect.x + center_coord.x;\n"
824                 "               in_y = z_in * sin(a2) * aspect.y + center_coord.y;\n"
825                 "               if( z > r.r || in_x.r < 0.0 || in_x.r >= image_extents.x || in_y.r < 0.0 || in_y.r >= image_extents.y )\n"
826                 "                       gl_FragColor.r = border_color.r;\n"
827                 "               else\n"
828                 "                       gl_FragColor.r = texture2D(tex, vec2(in_x.r, in_y.r) / texture_extents).r;\n"
829                 "               if( z > r.g || in_x.g < 0.0 || in_x.g >= image_extents.x || in_y.g < 0.0 || in_y.g >= image_extents.y )\n"
830                 "                       gl_FragColor.g = border_color.g;\n"
831                 "               else\n"
832                 "                       gl_FragColor.g = texture2D(tex, vec2(in_x.g, in_y.g) / texture_extents).g;\n"
833                 "               if( z > r.b || in_x.b < 0.0 || in_x.b >= image_extents.x || in_y.b < 0.0 || in_y.b >= image_extents.y )\n"
834                 "                       gl_FragColor.b = border_color.b;\n"
835                 "               else\n"
836                 "                       gl_FragColor.b = texture2D(tex, vec2(in_x.b, in_y.b) / texture_extents).b;\n"
837                 "               if( z > r.a || in_x.a < 0.0 || in_x.a >= image_extents.x || in_y.a < 0.0 || in_y.a >= image_extents.y )\n"
838                 "                       gl_FragColor.a = border_color.a;\n"
839                 "               else\n"
840                 "                       gl_FragColor.a = texture2D(tex, vec2(in_x.a, in_y.a) / texture_extents).a;\n"
841                 "       }\n"
842                 "}\n";
843
844         static const char *stretch_frag =
845                 "uniform sampler2D tex;\n"
846                 "uniform vec2 texture_extents;\n"
847                 "uniform vec2 image_extents;\n"
848                 "uniform vec2 aspect;\n"
849                 "uniform vec2 center_coord;\n"
850                 "uniform vec4 border_color;\n"
851                 "uniform vec4 r;\n"
852                 "void main()\n"
853                 "{\n"
854                 "       vec2 outcoord = gl_TexCoord[0].st * texture_extents;\n"
855                 "       vec2 coord_diff = outcoord - center_coord;\n"
856                 "       float z = sqrt(coord_diff.x * coord_diff.x +\n"
857                 "                                       coord_diff.y * coord_diff.y);\n"
858                 "       vec4 a1 = (vec4(z, z, z, z) / (3.14159 * r / 2.0)) * (3.14159 / 2.0);\n"
859                 "       vec4 z_in = r * sin(a1);\n"
860                 "       float a2;\n"
861                 "       if( coord_diff.x == 0.0 )\n"
862                 "       {\n"
863                 "               if( coord_diff.y < 0.0 )\n"
864                 "                       a2 = 3.0 * 3.14159 / 2.0;\n"
865                 "               else\n"
866                 "                       a2 = 3.14159 / 2.0;\n"
867                 "       }\n"
868                 "       else\n"
869                 "               a2 = atan(coord_diff.y, coord_diff.x);\n"
870                 "       vec4 in_x;\n"
871                 "       vec4 in_y;\n"
872                 "       in_x = z_in * cos(a2) * aspect.x + center_coord.x;\n"
873                 "       in_y = z_in * sin(a2) * aspect.y + center_coord.y;\n"
874                 "       if( in_x.r < 0.0 || in_x.r >= image_extents.x || in_y.r < 0.0 || in_y.r >= image_extents.y )\n"
875                 "               gl_FragColor.r = border_color.r;\n"
876                 "       else\n"
877                 "               gl_FragColor.r = texture2D(tex, vec2(in_x.r, in_y.r) / texture_extents).r;\n"
878                 "       if( in_x.g < 0.0 || in_x.g >= image_extents.x || in_y.g < 0.0 || in_y.g >= image_extents.y )\n"
879                 "               gl_FragColor.g = border_color.g;\n"
880                 "       else\n"
881                 "               gl_FragColor.g = texture2D(tex, vec2(in_x.g, in_y.g) / texture_extents).g;\n"
882                 "       if( in_x.b < 0.0 || in_x.b >= image_extents.x || in_y.b < 0.0 || in_y.b >= image_extents.y )\n"
883                 "               gl_FragColor.b = border_color.b;\n"
884                 "       else\n"
885                 "               gl_FragColor.b = texture2D(tex, vec2(in_x.b, in_y.b) / texture_extents).b;\n"
886                 "       if( in_x.a < 0.0 || in_x.a >= image_extents.x || in_y.a < 0.0 || in_y.a >= image_extents.y )\n"
887                 "               gl_FragColor.a = border_color.a;\n"
888                 "       else\n"
889                 "               gl_FragColor.a = texture2D(tex, vec2(in_x.a, in_y.a) / texture_extents).a;\n"
890                 "}\n";
891
892
893         static const char *rectilinear_stretch_frag =
894                 "uniform sampler2D tex;\n"
895                 "uniform vec2 texture_extents;\n"
896                 "uniform vec2 image_extents;\n"
897                 "uniform vec2 aspect;\n"
898                 "uniform vec2 center_coord;\n"
899                 "uniform vec4 border_color;\n"
900                 "uniform vec4 r;\n"
901                 "uniform float radius;\n"
902                 "void main()\n"
903                 "{\n"
904                 "       vec2 outcoord = gl_TexCoord[0].st * texture_extents;\n"
905                 "       vec2 coord_diff = outcoord - center_coord;\n"
906                 "       float z = sqrt(coord_diff.x * coord_diff.x +\n"
907                 "                                       coord_diff.y * coord_diff.y);\n"
908                 "       vec4 radius1 = (vec4(z, z, z, z) / r) * 2.0 * radius;\n"
909                 "       vec4 z_in = r * atan(radius1) / (3.14159 / 2.0);\n"
910                 "\n"
911                 "       float angle;\n"
912                 "       if( coord_diff.x == 0.0 )\n"
913                 "       {\n"
914                 "               if( coord_diff.y < 0.0 )\n"
915                 "                       angle = 3.0 * 3.14159 / 2.0;\n"
916                 "               else\n"
917                 "                       angle = 3.14159 / 2.0;\n"
918                 "       }\n"
919                 "       else\n"
920                 "               angle = atan(coord_diff.y, coord_diff.x);\n"
921                 "       vec4 in_x;\n"
922                 "       vec4 in_y;\n"
923                 "\n"
924                 "       in_x = z_in * cos(angle) * aspect.x + center_coord.x;\n"
925                 "       in_y = z_in * sin(angle) * aspect.y + center_coord.y;\n"
926                 "       if( in_x.r < 0.0 || in_x.r >= image_extents.x || in_y.r < 0.0 || in_y.r >= image_extents.y )\n"
927                 "               gl_FragColor.r = border_color.r;\n"
928                 "       else\n"
929                 "               gl_FragColor.r = texture2D(tex, vec2(in_x.r, in_y.r) / texture_extents).r;\n"
930                 "       if( in_x.g < 0.0 || in_x.g >= image_extents.x || in_y.g < 0.0 || in_y.g >= image_extents.y )\n"
931                 "               gl_FragColor.g = border_color.g;\n"
932                 "       else\n"
933                 "               gl_FragColor.g = texture2D(tex, vec2(in_x.g, in_y.g) / texture_extents).g;\n"
934                 "       if( in_x.b < 0.0 || in_x.b >= image_extents.x || in_y.b < 0.0 || in_y.b >= image_extents.y )\n"
935                 "               gl_FragColor.b = border_color.b;\n"
936                 "       else\n"
937                 "               gl_FragColor.b = texture2D(tex, vec2(in_x.b, in_y.b) / texture_extents).b;\n"
938                 "       if( in_x.a < 0.0 || in_x.a >= image_extents.x || in_y.a < 0.0 || in_y.a >= image_extents.y )\n"
939                 "               gl_FragColor.a = border_color.a;\n"
940                 "       else\n"
941                 "               gl_FragColor.a = texture2D(tex, vec2(in_x.a, in_y.a) / texture_extents).a;\n"
942                 "}\n";
943
944         static const char *rectilinear_shrink_frag =
945                 "uniform sampler2D tex;\n"
946                 "uniform vec2 texture_extents;\n"
947                 "uniform vec2 image_extents;\n"
948                 "uniform vec2 aspect;\n"
949                 "uniform vec2 center_coord;\n"
950                 "uniform vec4 border_color;\n"
951                 "uniform vec4 r;\n"
952                 "void main()\n"
953                 "{\n"
954                 "       vec2 outcoord = gl_TexCoord[0].st * texture_extents;\n"
955                 "       vec2 coord_diff = outcoord - center_coord;\n"
956                 "       float z = sqrt(coord_diff.x * coord_diff.x +\n"
957                 "                                       coord_diff.y * coord_diff.y);\n"
958                 "       vec4 radius1 = vec4(z, z, z, z) / r;\n"
959                 "       vec4 z_in = r * tan(radius1) / (3.14159 / 2.0);\n"
960                 "\n"
961                 "       float angle;\n"
962                 "       if( coord_diff.x == 0.0 )\n"
963                 "       {\n"
964                 "               if( coord_diff.y < 0.0 )\n"
965                 "                       angle = 3.0 * 3.14159 / 2.0;\n"
966                 "               else\n"
967                 "                       angle = 3.14159 / 2.0;\n"
968                 "       }\n"
969                 "       else\n"
970                 "               angle = atan(coord_diff.y, coord_diff.x);\n"
971                 "       vec4 in_x;\n"
972                 "       vec4 in_y;\n"
973                 "\n"
974                 "       in_x = z_in * cos(angle) * aspect.x + center_coord.x;\n"
975                 "       in_y = z_in * sin(angle) * aspect.y + center_coord.y;\n"
976                 "       if( in_x.r < 0.0 || in_x.r >= image_extents.x || in_y.r < 0.0 || in_y.r >= image_extents.y )\n"
977                 "               gl_FragColor.r = border_color.r;\n"
978                 "       else\n"
979                 "               gl_FragColor.r = texture2D(tex, vec2(in_x.r, in_y.r) / texture_extents).r;\n"
980                 "       if( in_x.g < 0.0 || in_x.g >= image_extents.x || in_y.g < 0.0 || in_y.g >= image_extents.y )\n"
981                 "               gl_FragColor.g = border_color.g;\n"
982                 "       else\n"
983                 "               gl_FragColor.g = texture2D(tex, vec2(in_x.g, in_y.g) / texture_extents).g;\n"
984                 "       if( in_x.b < 0.0 || in_x.b >= image_extents.x || in_y.b < 0.0 || in_y.b >= image_extents.y )\n"
985                 "               gl_FragColor.b = border_color.b;\n"
986                 "       else\n"
987                 "               gl_FragColor.b = texture2D(tex, vec2(in_x.b, in_y.b) / texture_extents).b;\n"
988                 "       if( in_x.a < 0.0 || in_x.a >= image_extents.x || in_y.a < 0.0 || in_y.a >= image_extents.y )\n"
989                 "               gl_FragColor.a = border_color.a;\n"
990                 "       else\n"
991                 "               gl_FragColor.a = texture2D(tex, vec2(in_x.a, in_y.a) / texture_extents).a;\n"
992                 "}\n";
993
994         get_output()->to_texture();
995         get_output()->enable_opengl();
996
997         unsigned int shader = 0;
998         const char *shader_frag = 0;
999         switch( config.mode ) {
1000         case LensConfig::SPHERICAL_SHRINK:
1001                 shader_frag = shrink_frag;
1002                 break;
1003         case LensConfig::SPHERICAL_STRETCH:
1004                 shader_frag = stretch_frag;
1005                 break;
1006         case LensConfig::RECTILINEAR_STRETCH:
1007                 shader_frag = rectilinear_stretch_frag;
1008                 break;
1009         case LensConfig::RECTILINEAR_SHRINK:
1010                 shader_frag = rectilinear_shrink_frag;
1011                 break;
1012         }
1013         if( shader_frag )
1014                 shader = VFrame::make_shader(0, shader_frag, 0);
1015         if( shader > 0 ) {
1016                 float border_color[] = { 0, 0, 0, 0 };
1017                 if( BC_CModels::is_yuv(get_output()->get_color_model()) ) {
1018                         border_color[1] = border_color[2] = 0.5;
1019                 }
1020
1021                 double x_factor = config.aspect;
1022                 double y_factor = 1.0 / config.aspect;
1023                 if( x_factor < 1 ) x_factor = 1;
1024                 if( y_factor < 1 ) y_factor = 1;
1025
1026                 glUseProgram(shader);
1027                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
1028                 glUniform2f(glGetUniformLocation(shader, "aspect"),
1029                         x_factor,
1030                         y_factor);
1031                 glUniform2f(glGetUniformLocation(shader, "center_coord"),
1032                                 (GLfloat)get_input()->get_w() * config.center_x / 100.0,
1033                                 (GLfloat)get_input()->get_h() * config.center_y / 100.0);
1034                 glUniform2f(glGetUniformLocation(shader, "texture_extents"),
1035                                 (GLfloat)get_input()->get_texture_w(),
1036                                 (GLfloat)get_input()->get_texture_h());
1037                 glUniform2f(glGetUniformLocation(shader, "image_extents"),
1038                                 (GLfloat)get_input()->get_w(),
1039                                 (GLfloat)get_input()->get_h());
1040
1041                 int width = get_output()->get_w();
1042                 int height = get_output()->get_h();
1043                 float *fov = config.fov;
1044                 float dim;
1045                 float max_z;
1046                 switch( config.mode ) {
1047                 case LensConfig::SPHERICAL_SHRINK:
1048                         dim = MAX(width, height) * config.radius;
1049                         max_z = dim * sqrt(2.0) / 2;
1050                         glUniform4fv(glGetUniformLocation(shader, "border_color"),
1051                                         1, (GLfloat*)border_color);
1052                         glUniform4f(glGetUniformLocation(shader, "max_z"),
1053                                         max_z / fov[0],
1054                                         max_z / fov[1],
1055                                         max_z / fov[2],
1056                                         max_z / fov[3]);
1057                         glUniform4f(glGetUniformLocation(shader, "r"),
1058                                         (max_z / fov[0]) * 2 / M_PI,
1059                                         (max_z / fov[1]) * 2 / M_PI,
1060                                         (max_z / fov[2]) * 2 / M_PI,
1061                                         (max_z / fov[3]) * 2 / M_PI);
1062                         break;
1063
1064                 case LensConfig::SPHERICAL_STRETCH:
1065                         dim = MAX(width, height) * config.radius;
1066                         max_z = dim * sqrt(2.0) / 2;
1067                         glUniform4f(glGetUniformLocation(shader, "r"),
1068                                         max_z / M_PI / (fov[0] / 2.0),
1069                                         max_z / M_PI / (fov[1] / 2.0),
1070                                         max_z / M_PI / (fov[2] / 2.0),
1071                                         max_z / M_PI / (fov[3] / 2.0));
1072                         break;
1073
1074                 case LensConfig::RECTILINEAR_STRETCH:
1075                         max_z = sqrt(SQR(width) + SQR(height)) / 2;
1076                         glUniform4f(glGetUniformLocation(shader, "r"),
1077                                         max_z / M_PI / (fov[0] / 2.0),
1078                                         max_z / M_PI / (fov[1] / 2.0),
1079                                         max_z / M_PI / (fov[2] / 2.0),
1080                                         max_z / M_PI / (fov[3] / 2.0));
1081                         glUniform1f(glGetUniformLocation(shader, "radius"),
1082                                         config.radius);
1083                         break;
1084
1085                 case LensConfig::RECTILINEAR_SHRINK:
1086                         max_z = MAX(width, height) / 2 * config.radius;
1087                         glUniform4f(glGetUniformLocation(shader, "r"),
1088                                         max_z / fov[0],
1089                                         max_z / fov[1],
1090                                         max_z / fov[2],
1091                                         max_z / fov[3]);
1092                         break;
1093                 }
1094
1095                 get_output()->init_screen();
1096                 get_output()->bind_texture(0);
1097                 glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
1098                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
1099                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
1100                 get_output()->draw_texture();
1101                 glUseProgram(0);
1102
1103                 if( config.draw_guides ) {
1104                         int w = get_output()->get_w();
1105                         int h = get_output()->get_h();
1106                         int center_x = (int)(config.center_x * w / 100);
1107                         int center_y = (int)(config.center_y * h / 100);
1108
1109                         glDisable(GL_TEXTURE_2D);
1110                         glColor4f(0.0, 0.0, 0.0, 1.0);
1111                         glBegin(GL_LINES);
1112                         glVertex3f(center_x, -h + center_y - CENTER_H / 2, 0.0);
1113                         glVertex3f(center_x, -h + center_y + CENTER_H / 2, 0.0);
1114                         glEnd();
1115                         glBegin(GL_LINES);
1116                         glVertex3f(center_x - CENTER_W / 2, -h + center_y, 0.0);
1117                         glVertex3f(center_x + CENTER_W / 2, -h + center_y, 0.0);
1118                         glEnd();
1119                         glColor4f(1.0, 1.0, 1.0, 1.0);
1120                         glBegin(GL_LINES);
1121                         glVertex3f(center_x - 1, -h + center_y - CENTER_H / 2 - 1, 0.0);
1122                         glVertex3f(center_x - 1, -h + center_y + CENTER_H / 2 - 1, 0.0);
1123                         glEnd();
1124                         glBegin(GL_LINES);
1125                         glVertex3f(center_x - CENTER_W / 2 - 1, -h + center_y - 1, 0.0);
1126                         glVertex3f(center_x + CENTER_W / 2 - 1, -h + center_y - 1, 0.0);
1127                         glEnd();
1128                 }
1129                 get_output()->set_opengl_state(VFrame::SCREEN);
1130         }
1131
1132 #endif
1133         return 0;
1134 }
1135
1136 // do using specified interpolation
1137 #define DO_LENS(type) \
1138         int icolor_model = plugin->get_input()->get_color_model(); \
1139         switch( plugin->config.interp ) { \
1140         case LensConfig::INTERP_NEAREST: \
1141                 switch( icolor_model ) { \
1142                 case BC_RGB888: \
1143                         DO_LENS_##type(unsigned char, 3, 0xff, 0x0, nearest); \
1144                         break; \
1145                 case BC_RGBA8888: \
1146                         DO_LENS_##type(unsigned char, 4, 0xff, 0x0, nearest); \
1147                         break; \
1148                 case BC_RGB_FLOAT: \
1149                         DO_LENS_##type(float, 3, 1.0, 0.0, nearest); \
1150                         break; \
1151                 case BC_RGBA_FLOAT: \
1152                         DO_LENS_##type(float, 4, 1.0, 0.0, nearest); \
1153                         break; \
1154                 case BC_YUV888: \
1155                         DO_LENS_##type(unsigned char, 3, 0xff, 0x80, nearest); \
1156                         break; \
1157                 case BC_YUVA8888: \
1158                         DO_LENS_##type(unsigned char, 4, 0xff, 0x80, nearest); \
1159                         break; \
1160                 } \
1161                 break; \
1162         case LensConfig::INTERP_DEFAULT: \
1163         case LensConfig::INTERP_BILINEAR: \
1164                 switch( icolor_model ) { \
1165                 case BC_RGB888: \
1166                         DO_LENS_##type(unsigned char, 3, 0xff, 0x0, bi_linear); \
1167                         break; \
1168                 case BC_RGBA8888: \
1169                         DO_LENS_##type(unsigned char, 4, 0xff, 0x0, bi_linear); \
1170                         break; \
1171                 case BC_RGB_FLOAT: \
1172                         DO_LENS_##type(float, 3, 1.0, 0.0, bi_linear); \
1173                         break; \
1174                 case BC_RGBA_FLOAT: \
1175                         DO_LENS_##type(float, 4, 1.0, 0.0, bi_linear); \
1176                         break; \
1177                 case BC_YUV888: \
1178                         DO_LENS_##type(unsigned char, 3, 0xff, 0x80, bi_linear); \
1179                         break; \
1180                 case BC_YUVA8888: \
1181                         DO_LENS_##type(unsigned char, 4, 0xff, 0x80, bi_linear); \
1182                         break; \
1183                 } \
1184                 break; \
1185         case LensConfig::INTERP_BICUBIC: \
1186                 switch( icolor_model ) { \
1187                 case BC_RGB888: \
1188                         DO_LENS_##type(unsigned char, 3, 0xff, 0x0, bi_cubic); \
1189                         break; \
1190                 case BC_RGBA8888: \
1191                         DO_LENS_##type(unsigned char, 4, 0xff, 0x0, bi_cubic); \
1192                         break; \
1193                 case BC_RGB_FLOAT: \
1194                         DO_LENS_##type(float, 3, 1.0, 0.0, bi_cubic); \
1195                         break; \
1196                 case BC_RGBA_FLOAT: \
1197                         DO_LENS_##type(float, 4, 1.0, 0.0, bi_cubic); \
1198                         break; \
1199                 case BC_YUV888: \
1200                         DO_LENS_##type(unsigned char, 3, 0xff, 0x80, bi_cubic); \
1201                         break; \
1202                 case BC_YUVA8888: \
1203                         DO_LENS_##type(unsigned char, 4, 0xff, 0x80, bi_cubic); \
1204                         break; \
1205                 } \
1206                 break; \
1207         }
1208
1209 LensPackage::LensPackage()
1210  : LoadPackage()
1211 {
1212 }
1213
1214 LensUnit::LensUnit(LensEngine *engine, LensMain *plugin)
1215  : LoadClient(engine)
1216 {
1217         this->plugin = plugin;
1218 }
1219
1220 LensUnit::~LensUnit()
1221 {
1222 }
1223
1224 void LensUnit::process_spherical_stretch(LensPackage *pkg)
1225 {
1226         float *fov = plugin->config.fov;
1227         float aspect = plugin->config.aspect;
1228         int row1 = pkg->row1;
1229         int row2 = pkg->row2;
1230         double x_factor = aspect;
1231         double y_factor = 1.0 / aspect;
1232         if( x_factor < 1 ) x_factor = 1;
1233         if( y_factor < 1 ) y_factor = 1;
1234         int width = plugin->get_input()->get_w();
1235         int height = plugin->get_input()->get_h();
1236         double dim = MAX(width, height) * plugin->config.radius;
1237         double max_z = dim * sqrt(2.0) / 2;
1238         double center_x = width * plugin->config.center_x / 100.0;
1239         double center_y = height * plugin->config.center_y / 100.0;
1240         double r[FOV_CHANNELS];
1241
1242         r[0] = max_z / M_PI / (fov[0] / 2.0);
1243         r[1] = max_z / M_PI / (fov[1] / 2.0);
1244         r[2] = max_z / M_PI / (fov[2] / 2.0);
1245         r[3] = max_z / M_PI / (fov[3] / 2.0);
1246
1247 #define DO_LENS_SPHERICAL_STRETCH(type, components, max, chroma, interp) { \
1248         type **in_rows = (type**)plugin->get_temp()->get_rows(); \
1249         type **out_rows = (type**)plugin->get_input()->get_rows(); \
1250         type black[4] = { 0, chroma, chroma, 0 }; \
1251         INTERP_SETUP(in_rows, max, 0,0, width,height); \
1252  \
1253         for( int y=row1; y<row2; ++y ) { \
1254                 type *out_row = out_rows[y]; \
1255                 double y_diff = y - center_y; \
1256                 for( int x=0; x<width; ++x ) { \
1257                         double x_diff = (x - center_x); \
1258                         double z = sqrt(x_diff * x_diff + y_diff * y_diff); \
1259                         double a2 = x != center_x ? atan(y_diff / x_diff) : \
1260                                 y < center_y ? 3*M_PI/2 : M_PI/2; \
1261                         if( x_diff < 0.0 ) a2 += M_PI; \
1262                         for( int i=0; i<components; ++i ) { \
1263                                 double a1 = (z / (M_PI * r[i] / 2)) * (M_PI / 2); \
1264                                 double z_in = r[i] * sin(a1); \
1265                                 double x_in = z_in * cos(a2) * x_factor + center_x; \
1266                                 double y_in = z_in * sin(a2) * y_factor + center_y; \
1267                                 interp##_SETUP(type, components, x_in, y_in); \
1268                                 for( int j=0; j<i; ++j ) interp##_next(); \
1269                                 *out_row++ = interp##_interp(black[i], black[i]); \
1270                         } \
1271                 } \
1272         } \
1273  \
1274         type *out_pixel = out_rows[(int)center_y] + (int)center_x * components; \
1275         type *inp_pixel = in_rows[(int)center_y] + (int)center_x * components; \
1276         for( int i=0; i<components; ++i ) *out_pixel++ = *inp_pixel++; \
1277 }
1278
1279         DO_LENS(SPHERICAL_STRETCH);
1280 }
1281
1282 void LensUnit::process_spherical_shrink(LensPackage *pkg)
1283 {
1284
1285         float *fov = plugin->config.fov;
1286         float aspect = plugin->config.aspect;
1287         int row1 = pkg->row1;
1288         int row2 = pkg->row2;
1289         int width = plugin->get_input()->get_w();
1290         int height = plugin->get_input()->get_h();
1291         double x_factor = aspect;
1292         double y_factor = 1.0 / aspect;
1293         if( x_factor < 1 ) x_factor = 1;
1294         if( y_factor < 1 ) y_factor = 1;
1295         double dim = MAX(width, height) * plugin->config.radius;
1296         double max_z[FOV_CHANNELS];
1297         double center_x = width * plugin->config.center_x / 100.0;
1298         double center_y = height * plugin->config.center_y / 100.0;
1299         double r[FOV_CHANNELS];
1300
1301 //      max_z[0] = sqrt(SQR(width) + SQR(height)) / 2 / fov[0];
1302 //      max_z[1] = sqrt(SQR(width) + SQR(height)) / 2 / fov[1];
1303 //      max_z[2] = sqrt(SQR(width) + SQR(height)) / 2 / fov[2];
1304 //      max_z[3] = sqrt(SQR(width) + SQR(height)) / 2 / fov[3];
1305         max_z[0] = dim * sqrt(2.0) / 2 / fov[0];
1306         max_z[1] = dim * sqrt(2.0) / 2 / fov[1];
1307         max_z[2] = dim * sqrt(2.0) / 2 / fov[2];
1308         max_z[3] = dim * sqrt(2.0) / 2 / fov[3];
1309         r[0] = max_z[0] * 2 / M_PI;
1310         r[1] = max_z[1] * 2 / M_PI;
1311         r[2] = max_z[2] * 2 / M_PI;
1312         r[3] = max_z[3] * 2 / M_PI;
1313
1314 #define DO_LENS_SPHERICAL_SHRINK(type, components, max, chroma, interp) { \
1315         type **in_rows = (type**)plugin->get_temp()->get_rows(); \
1316         type **out_rows = (type**)plugin->get_input()->get_rows(); \
1317         type black[4] = { 0, chroma, chroma, 0 }; \
1318         INTERP_SETUP(in_rows, max, 0,0, width,height); \
1319  \
1320         for( int y=row1; y<row2; ++y ) { \
1321                 type *out_row = out_rows[y]; \
1322                 type *in_row = in_rows[y]; \
1323                 double y_diff = y - center_y; \
1324                 for( int x=0; x<width; ++x ) { \
1325                         double x_diff = x - center_x; \
1326                         if( !x_diff && !y_diff ) { \
1327                                 type *inp_pixel = in_row + x * components; \
1328                                 for( int c=0; c<components; ++c ) \
1329                                         *out_row++ = *inp_pixel++; \
1330                                 continue; \
1331                         } \
1332                         double z = sqrt(x_diff * x_diff + y_diff * y_diff); \
1333                         double a2 = atan(y_diff / x_diff); \
1334                         if( x_diff < 0.0 ) a2 += M_PI; \
1335                         for( int i=0; i<components; ++i ) { \
1336                                 if( z > r[i] ) { *out_row++ = black[i]; continue; } \
1337                                 double a1 = asin(z / r[i]); \
1338                                 double z_in = a1 * max_z[i] * 2 / M_PI; \
1339                                 float x_in = z_in * cos(a2) * x_factor + center_x; \
1340                                 float y_in = z_in * sin(a2) * y_factor + center_y; \
1341                                 interp##_SETUP(type, components, x_in, y_in); \
1342                                 for( int j=0; j<i; ++j ) interp##_next(); \
1343                                 *out_row++ = interp##_interp(black[i], black[i]); \
1344                         } \
1345                 } \
1346         } \
1347  \
1348         type *out_pixel = out_rows[(int)center_y] + (int)center_x * components; \
1349         type *inp_pixel = in_rows[(int)center_y] + (int)center_x * components; \
1350         for( int i=0; i<components; ++i ) *out_pixel++ = *inp_pixel++; \
1351 }
1352
1353         DO_LENS(SPHERICAL_SHRINK);
1354 }
1355
1356 void LensUnit::process_rectilinear_stretch(LensPackage *pkg)
1357 {
1358         float *fov = plugin->config.fov;
1359         float aspect = plugin->config.aspect;
1360         int row1 = pkg->row1;
1361         int row2 = pkg->row2;
1362         double x_factor = aspect;
1363         double y_factor = 1.0 / aspect;
1364         if( x_factor < 1 ) x_factor = 1;
1365         if( y_factor < 1 ) y_factor = 1;
1366         int width = plugin->get_input()->get_w();
1367         int height = plugin->get_input()->get_h();
1368 //      double dim = MAX(width, height) * plugin->config.radius;
1369 //      double max_z = dim * sqrt(2.0) / 2;
1370         double max_z = sqrt(SQR(width) + SQR(height)) / 2;
1371         double center_x = width * plugin->config.center_x / 100.0;
1372         double center_y = height * plugin->config.center_y / 100.0;
1373         double r[FOV_CHANNELS];
1374
1375         r[0] = max_z / M_PI / (fov[0] / 2.0);
1376         r[1] = max_z / M_PI / (fov[1] / 2.0);
1377         r[2] = max_z / M_PI / (fov[2] / 2.0);
1378         r[3] = max_z / M_PI / (fov[3] / 2.0);
1379
1380 #define DO_LENS_RECTILINEAR_STRETCH(type, components, max, chroma, interp) { \
1381         type **in_rows = (type**)plugin->get_temp()->get_rows(); \
1382         type **out_rows = (type**)plugin->get_input()->get_rows(); \
1383         type black[4] = { 0, chroma, chroma, 0 }; \
1384         INTERP_SETUP(in_rows, max, 0,0, width,height); \
1385  \
1386         for( int y=row1; y<row2; ++y ) { \
1387                 type *out_row = out_rows[y]; \
1388                 double y_diff = y - center_y; \
1389                 for( int x=0; x<width; ++x ) { \
1390                         double x_diff = (x - center_x); /* Compute magnitude / angle */ \
1391                         double z = sqrt(x_diff * x_diff + y_diff * y_diff); \
1392                         double angle = x != center_x ? atan(y_diff / x_diff) : \
1393                                 y < center_y ? 3*M_PI/2 : M_PI/2; \
1394                         if( x_diff < 0.0 ) angle += M_PI; \
1395                         for( int i=0; i<components; ++i ) { /* Compute new radius */ \
1396                                 double radius1 = (z / r[i]) * 2 * plugin->config.radius; \
1397                                 double z_in = r[i] * atan(radius1) / (M_PI / 2); \
1398                                 double x_in = z_in * cos(angle) * x_factor + center_x; \
1399                                 double y_in = z_in * sin(angle) * y_factor + center_y; \
1400                                 interp##_SETUP(type, components, x_in, y_in); \
1401                                 for( int j=0; j<i; ++j ) interp##_next(); \
1402                                 *out_row++ = interp##_interp(black[i], black[i]); \
1403                         } \
1404                 } \
1405         } \
1406  \
1407         type *out_pixel = out_rows[(int)center_y] + (int)center_x * components; \
1408         type *inp_pixel = in_rows[(int)center_y] + (int)center_x * components; \
1409         for( int i=0; i<components; ++i ) *out_pixel++ = *inp_pixel++; \
1410 }
1411
1412         DO_LENS(RECTILINEAR_STRETCH);
1413 }
1414
1415 void LensUnit::process_rectilinear_shrink(LensPackage *pkg)
1416 {
1417         float *fov = plugin->config.fov;
1418         float aspect = plugin->config.aspect;
1419         int row1 = pkg->row1;
1420         int row2 = pkg->row2;
1421         double x_factor = aspect;
1422         double y_factor = 1.0 / aspect;
1423         if( x_factor < 1 ) x_factor = 1;
1424         if( y_factor < 1 ) y_factor = 1;
1425         int width = plugin->get_input()->get_w();
1426         int height = plugin->get_input()->get_h();
1427         double max_z = MAX(width, height) / 2 * plugin->config.radius;
1428         double center_x = width * plugin->config.center_x / 100.0;
1429         double center_y = height * plugin->config.center_y / 100.0;
1430         double r[FOV_CHANNELS];
1431
1432         r[0] = max_z / fov[0];
1433         r[1] = max_z / fov[1];
1434         r[2] = max_z / fov[2];
1435         r[3] = max_z / fov[3];
1436
1437 #define DO_LENS_RECTILINEAR_SHRINK(type, components, max, chroma, interp) { \
1438         type **in_rows = (type**)plugin->get_temp()->get_rows(); \
1439         type **out_rows = (type**)plugin->get_input()->get_rows(); \
1440         type black[4] = { 0, chroma, chroma, 0 }; \
1441         INTERP_SETUP(in_rows, max, 0,0, width,height); \
1442  \
1443         for( int y=row1; y<row2; ++y ) { \
1444                 type *out_row = out_rows[y]; \
1445                 double y_diff = y - center_y; \
1446                 for( int x=0; x<width; ++x ) { \
1447                         double x_diff = (x - center_x); /* Compute magnitude/angle */ \
1448                         double z = sqrt(x_diff * x_diff + y_diff * y_diff); \
1449                         double angle = x != center_x ? atan(y_diff / x_diff) : \
1450                                 y < center_y ? 3*M_PI/2 : M_PI/2; \
1451                         if( x_diff < 0.0 ) angle += M_PI; \
1452                         for( int i=0; i<components; ++i ) { /* Compute new radius */ \
1453                                 double radius1 = z / r[i]; \
1454                                 double z_in = r[i] * tan(radius1) / (M_PI / 2); \
1455                                 double x_in = z_in * cos(angle) * x_factor + center_x; \
1456                                 double y_in = z_in * sin(angle) * y_factor + center_y; \
1457                                 interp##_SETUP(type, components, x_in, y_in); \
1458                                 for( int j=0; j<i; ++j ) interp##_next(); \
1459                                 *out_row++ = interp##_interp(black[i], black[i]); \
1460                         } \
1461                 } \
1462         } \
1463  \
1464         type *out_pixel = out_rows[(int)center_y] + (int)center_x * components; \
1465         type *inp_pixel = in_rows[(int)center_y] + (int)center_x * components; \
1466         for( int i=0; i<components; ++i ) *out_pixel++ = *inp_pixel++; \
1467 }
1468
1469         DO_LENS(RECTILINEAR_SHRINK);
1470 }
1471
1472 void LensUnit::process_package(LoadPackage *package)
1473 {
1474         LensPackage *pkg = (LensPackage*)package;
1475
1476         switch( plugin->config.mode ) {
1477         case LensConfig::SPHERICAL_STRETCH:
1478                 process_spherical_stretch(pkg);
1479                 break;
1480         case LensConfig::SPHERICAL_SHRINK:
1481                 process_spherical_shrink(pkg);
1482                 break;
1483         case LensConfig::RECTILINEAR_STRETCH:
1484                 process_rectilinear_stretch(pkg);
1485                 break;
1486         case LensConfig::RECTILINEAR_SHRINK:
1487                 process_rectilinear_shrink(pkg);
1488                 break;
1489         }
1490 }
1491
1492
1493 LensEngine::LensEngine(LensMain *plugin)
1494  : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
1495 // : LoadServer(1, 1)
1496 {
1497         this->plugin = plugin;
1498 }
1499
1500 LensEngine::~LensEngine()
1501 {
1502 }
1503
1504 void LensEngine::init_packages()
1505 {
1506         int row1 = 0, row2 = 0, n = LoadServer::get_total_packages();
1507         for( int i=0; i<n; row1=row2 ) {
1508                 LensPackage *package = (LensPackage*)LoadServer::get_package(i);
1509                 row2 = plugin->get_input()->get_h() * ++i / n;
1510                 package->row1 = row1;  package->row2 = row2;
1511         }
1512 }
1513
1514 LoadClient* LensEngine::new_client()
1515 {
1516         return new LensUnit(this, plugin);
1517 }
1518
1519 LoadPackage* LensEngine::new_package()
1520 {
1521         return new LensPackage;
1522 }
1523
1524