Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / plugins / lens / lens.C
diff --git a/cinelerra-5.1/plugins/lens/lens.C b/cinelerra-5.1/plugins/lens/lens.C
new file mode 100644 (file)
index 0000000..2491615
--- /dev/null
@@ -0,0 +1,1835 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#include "bcdisplayinfo.h"
+#include "bchash.h"
+#include "bcsignals.h"
+#include "clip.h"
+#include "filexml.h"
+#include "language.h"
+#include "lens.h"
+
+
+#include <string.h>
+
+
+
+
+REGISTER_PLUGIN(LensMain)
+
+
+
+
+LensConfig::LensConfig()
+{
+       for(int i = 0; i < FOV_CHANNELS; i++)
+               fov[i] = 1.0;
+       aspect = 1.0;
+       radius = 1.0;
+       mode = LensConfig::SHRINK;
+       center_x = 50.0;
+       center_y = 50.0;
+       draw_guides = 0;
+}
+
+int LensConfig::equivalent(LensConfig &that)
+{
+       for(int i = 0; i < FOV_CHANNELS; i++)
+               if(!EQUIV(fov[i], that.fov[i])) return 0;
+       return EQUIV(aspect, that.aspect) &&
+               EQUIV(radius, that.radius) &&
+               EQUIV(center_x, that.center_x) &&
+               EQUIV(center_y, that.center_y) &&
+               mode == that.mode &&
+               draw_guides == that.draw_guides;
+}
+
+void LensConfig::copy_from(LensConfig &that)
+{
+       for(int i = 0; i < FOV_CHANNELS; i++)
+               fov[i] = that.fov[i];
+       aspect = that.aspect;
+       radius = that.radius;
+       mode = that.mode;
+       center_x = that.center_x;
+       center_y = that.center_y;
+       draw_guides = that.draw_guides;
+}
+
+void LensConfig::interpolate(LensConfig &prev, 
+       LensConfig &next, 
+       int64_t prev_frame, 
+       int64_t next_frame, 
+       int64_t current_frame)
+{
+       double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
+       double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
+
+       for(int i = 0; i < FOV_CHANNELS; i++)
+               fov[i] = prev.fov[i] * prev_scale + next.fov[i] * next_scale;
+       aspect = prev.aspect * prev_scale + next.aspect * next_scale;
+       radius = prev.radius * prev_scale + next.radius * next_scale;
+       center_x = prev.center_x * prev_scale + next.center_x * next_scale;
+       center_y = prev.center_y * prev_scale + next.center_y * next_scale;
+       mode = prev.mode;
+       draw_guides = prev.draw_guides;
+
+       boundaries();
+}
+
+void LensConfig::boundaries()
+{
+       CLAMP(center_x, 0.0, 99.0);
+       CLAMP(center_y, 0.0, 99.0);
+       for(int i = 0; i < FOV_CHANNELS; i++)
+               CLAMP(fov[i], 0.0, 1.0);
+       CLAMP(aspect, 0.3, 3.0);
+       CLAMP(radius, 0.3, 3.0);
+}
+
+
+
+
+LensSlider::LensSlider(LensMain *client, 
+       LensGUI *gui,
+       LensText *text,
+       float *output, 
+       int x, 
+       int y, 
+       float min,
+       float max)
+ : BC_FSlider(x, y, 0, 200, 200, min, max, *output)
+{
+       this->gui = gui;
+       this->client = client;
+       this->output = output;
+       this->text = text;
+       set_precision(0.01);
+}
+
+int LensSlider::handle_event()
+{
+       float prev_output = *output;
+       *output = get_value();
+       text->update(*output);
+
+       float difference = *output - prev_output;
+       int is_fov = 0;
+
+       if(client->lock)
+       {
+               for(int i = 0; i < FOV_CHANNELS; i++)
+               {
+                       if(output == &client->config.fov[i])
+                       {
+                               is_fov = 1;
+                               break;
+                       }
+               }
+
+               if(is_fov)
+               {
+                       for(int i = 0; i < FOV_CHANNELS; i++)
+                       {
+                               if(output != &client->config.fov[i])
+                               {
+                                       client->config.fov[i] += difference;
+                                       client->config.boundaries();
+                                       gui->fov_slider[i]->update(client->config.fov[i]);
+                                       gui->fov_text[i]->update(client->config.fov[i]);
+                               }
+                       }
+               }
+       }
+
+       client->send_configure_change();
+       return 1;
+}
+
+
+
+LensText::LensText(LensMain *client, 
+       LensGUI *gui,
+       LensSlider *slider,
+       float *output, 
+       int x, 
+       int y)
+ : BC_TextBox(x, y, 100, 1, *output)
+{
+       this->gui = gui;
+       this->client = client;
+       this->output = output;
+       this->slider = slider;
+}
+
+int LensText::handle_event()
+{
+       float prev_output = *output;
+       *output = atof(get_text());
+       slider->update(*output);
+
+       float difference = *output - prev_output;
+       int is_fov = 0;
+
+       if(client->lock)
+       {
+               for(int i = 0; i < FOV_CHANNELS; i++)
+               {
+                       if(output == &client->config.fov[i])
+                       {
+                               is_fov = 1;
+                               break;
+                       }
+               }
+
+               if(is_fov)
+               {
+                       for(int i = 0; i < FOV_CHANNELS; i++)
+                       {
+                               if(output != &client->config.fov[i])
+                               {
+                                       client->config.fov[i] += difference;
+                                       client->config.boundaries();
+                                       gui->fov_slider[i]->update(client->config.fov[i]);
+                                       gui->fov_text[i]->update(client->config.fov[i]);
+                               }
+                       }
+               }
+       }
+
+       client->send_configure_change();
+       return 1;
+}
+
+
+
+LensToggle::LensToggle(LensMain *client, 
+       int *output, 
+       int x, 
+       int y,
+       const char *text)
+ : BC_CheckBox(x, y, *output, text)
+{
+       this->output = output;
+       this->client = client;
+}
+
+int LensToggle::handle_event()
+{
+       *output = get_value();
+       client->send_configure_change();
+       return 1;
+}
+
+
+
+
+
+
+
+
+
+
+LensMode::LensMode(LensMain *plugin,  
+       LensGUI *gui,
+       int x,
+       int y)
+ : BC_PopupMenu(x,
+       y,
+       calculate_w(gui),
+       "",
+       1)
+{
+       this->plugin = plugin;
+       this->gui = gui;
+}
+
+int LensMode::handle_event()
+{
+       plugin->config.mode = from_text(get_text());
+       plugin->send_configure_change();
+       return 1;
+
+}
+
+void LensMode::create_objects()
+{
+       add_item(new BC_MenuItem(to_text(LensConfig::SHRINK)));
+       add_item(new BC_MenuItem(to_text(LensConfig::STRETCH)));
+       add_item(new BC_MenuItem(to_text(LensConfig::RECTILINEAR_STRETCH)));
+       add_item(new BC_MenuItem(to_text(LensConfig::RECTILINEAR_SHRINK)));
+       update(plugin->config.mode);
+}
+
+void LensMode::update(int mode)
+{
+       char string[BCTEXTLEN];
+       sprintf(string, "%s", to_text(mode));
+       set_text(string);
+}
+
+int LensMode::calculate_w(LensGUI *gui)
+{
+       int result = 0;
+       result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(LensConfig::STRETCH)));
+       result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(LensConfig::SHRINK)));
+       result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(LensConfig::RECTILINEAR_STRETCH)));
+       result = MAX(result, gui->get_text_width(MEDIUMFONT, to_text(LensConfig::RECTILINEAR_SHRINK)));
+       return result + 50;
+}
+
+int LensMode::from_text(char *text)
+{
+       if(!strcmp(text, _("Sphere Stretch"))) return LensConfig::STRETCH;
+       else
+       if(!strcmp(text, _("Sphere Shrink"))) return LensConfig::SHRINK;
+       else
+       if(!strcmp(text, _("Rectilinear Stretch"))) return LensConfig::RECTILINEAR_STRETCH;
+       else
+       if(!strcmp(text, _("Rectilinear Shrink"))) return LensConfig::RECTILINEAR_SHRINK;
+       return LensConfig::STRETCH;
+}
+
+const char* LensMode::to_text(int mode)
+{
+       switch(mode)
+       {
+               case LensConfig::STRETCH:
+                       return _("Sphere Stretch");
+                       break;
+               case LensConfig::SHRINK:
+                       return _("Sphere Shrink");
+                       break;
+               case LensConfig::RECTILINEAR_STRETCH:
+                       return _("Rectilinear Stretch");
+                       break;
+               case LensConfig::RECTILINEAR_SHRINK:
+                       return _("Rectilinear Shrink");
+                       break;
+       }
+       return _("Stretch");
+}
+
+
+
+
+
+// LensPresets::LensPresets(LensMain *plugin,  
+//     LensGUI *gui,
+//     int x,
+//     int y,
+//     int w)
+//  : BC_PopupMenu(x,
+//     y,
+//     w,
+//     "",
+//     1)
+// {
+//     this->plugin = plugin;
+//     this->gui = gui;
+// }
+// 
+// int LensPresets::handle_event()
+// {
+//     return 1;
+// }
+// 
+// void LensPresets::create_objects()
+// {
+// // Remove existing items
+//     int total = total_items();
+//     for(int i = 0; i < total; i++)
+//     {
+//             BC_MenuItem *item = get_item(0);
+//             remove_item(item);
+//     }
+// 
+// // Create current items
+//     plugin->load_presets();
+//     for(int i = 0; i < plugin->presets.total; i++)
+//     {
+//             add_item(new BC_MenuItem(plugin->presets.values[i]->title));
+//     }
+// 
+// // Update text
+//     if(plugin->current_preset >= 0 &&
+//             plugin->current_preset < plugin->presets.total)
+//     {
+//             set_text(plugin->presets.values[plugin->current_preset]->title);
+//     }
+//     else
+//     {
+//             set_text("None");
+//     }
+// }
+// 
+// int LensPresets::from_text(LensMain *plugin, char *text)
+// {
+// }
+// 
+// char* LensPresets::to_text(LensMain *plugin, int preset)
+// {
+// }
+// 
+// void LensPresets::update(int preset)
+// {
+// }
+// 
+// 
+// 
+// 
+// 
+// 
+// LensSavePreset::LensSavePreset(LensMain *plugin,  
+//     LensGUI *gui,
+//     int x,
+//     int y)
+//  : BC_GenericButton(x, y, "Save Preset")
+// {
+//     this->plugin = plugin;
+//     this->gui = gui;
+// }
+// 
+// int LensSavePreset::handle_event()
+// {
+// }
+// 
+// 
+// 
+// 
+// 
+// 
+// 
+// LensDeletePreset::LensDeletePreset(LensMain *plugin,  
+//     LensGUI *gui,
+//     int x,
+//     int y)
+//  : BC_GenericButton(x, y, "Delete Preset")
+// {
+// }
+// 
+// int LensDeletePreset::handle_event()
+// {
+// }
+// 
+// 
+// 
+// 
+// 
+// 
+// 
+// LensPresetText::LensPresetText(LensMain *plugin,  
+//     LensGUI *gui,
+//     int x,
+//     int y,
+//     int w)
+//  : BC_TextBox(x, y, w, 1, "")
+// {
+//     this->plugin = plugin;
+//     this->gui = gui;
+// }
+// 
+// int LensPresetText::handle_event()
+// {
+// }
+
+
+
+
+
+
+
+
+
+
+
+
+
+LensGUI::LensGUI(LensMain *client)
+ : PluginClientWindow(client,
+       350, 
+       510, 
+       350, 
+       510, 
+       0)
+{
+       this->client = client;
+}
+
+LensGUI::~LensGUI()
+{
+}
+
+
+void LensGUI::create_objects()
+{
+       int x = 10;
+       int y = 10;
+       int x1;
+       BC_Title *title = 0;
+       LensToggle *toggle;
+       
+       for(int i = 0; i < FOV_CHANNELS; i++)
+       {
+               switch(i)
+               {
+                       case 0: add_tool(title = new BC_Title(x, y, _("R Field of View:"))); break;
+                       case 1: add_tool(title = new BC_Title(x, y, _("G Field of View:"))); break;
+                       case 2: add_tool(title = new BC_Title(x, y, _("B Field of View:"))); break;
+                       case 3: add_tool(title = new BC_Title(x, y, _("A Field of View:"))); break;
+               }
+
+               y += title->get_h() + 5;
+               add_tool(fov_slider[i] = new LensSlider(client, 
+                       this,
+                       0,
+                       &client->config.fov[i], 
+                       x, 
+                       y, 
+                       0.0001,
+                       1.0));
+               x1 = x + fov_slider[i]->get_w() + 5;
+               add_tool(fov_text[i] = new LensText(client, 
+                       this,
+                       fov_slider[i],
+                       &client->config.fov[i], 
+                       x1, 
+                       y));
+               fov_slider[i]->text = fov_text[i];
+               y += fov_text[i]->get_h() + 5;
+       }
+
+       add_tool(toggle = new LensToggle(client, 
+               &client->lock, 
+               x, 
+               y,
+               _("Lock")));
+       y += toggle->get_h() + 10;
+       
+       BC_Bar *bar;
+       add_tool(bar = new BC_Bar(x, y, get_w() - x * 2));
+       y += bar->get_h() + 5;
+
+       add_tool(title = new BC_Title(x, y, _("Aspect Ratio:")));
+       y += title->get_h() + 5;
+       add_tool(aspect_slider = new LensSlider(client, 
+               this,
+               0,
+               &client->config.aspect, 
+               x, 
+               y, 
+               0.333,
+               3.0));
+       x1 = x + aspect_slider->get_w() + 5;
+       add_tool(aspect_text = new LensText(client, 
+               this,
+               aspect_slider,
+               &client->config.aspect, 
+               x1, 
+               y));
+       aspect_slider->text = aspect_text;
+       y += aspect_text->get_h() + 5;
+
+
+       add_tool(title = new BC_Title(x, y, _("Radius:")));
+       y += title->get_h() + 5;
+       add_tool(radius_slider = new LensSlider(client, 
+               this,
+               0,
+               &client->config.radius, 
+               x, 
+               y, 
+               0.333,
+               3.0));
+       x1 = x + radius_slider->get_w() + 5;
+       add_tool(radius_text = new LensText(client, 
+               this,
+               radius_slider,
+               &client->config.radius, 
+               x1, 
+               y));
+       radius_slider->text = radius_text;
+       y += radius_text->get_h() + 5;
+
+
+       add_tool(title = new BC_Title(x, y, _("Center X:")));
+       y += title->get_h() + 5;
+       add_tool(centerx_slider = new LensSlider(client, 
+               this,
+               0,
+               &client->config.center_x, 
+               x, 
+               y, 
+               0.0,
+               99.0));
+       x1 = x + centerx_slider->get_w() + 5;
+       add_tool(centerx_text = new LensText(client, 
+               this,
+               centerx_slider,
+               &client->config.center_x, 
+               x1, 
+               y));
+       centerx_slider->text = centerx_text;
+       centerx_slider->set_precision(1.0);
+       y += centerx_text->get_h() + 5;
+
+
+       add_tool(title = new BC_Title(x, y, _("Center Y:")));
+       y += title->get_h() + 5;
+       add_tool(centery_slider = new LensSlider(client, 
+               this,
+               0,
+               &client->config.center_y, 
+               x, 
+               y, 
+               0.0,
+               99.0));
+       x1 = x + centery_slider->get_w() + 5;
+       add_tool(centery_text = new LensText(client, 
+               this,
+               centery_slider,
+               &client->config.center_y, 
+               x1, 
+               y));
+       centery_slider->text = centery_text;
+       centery_slider->set_precision(1.0);
+       y += centery_text->get_h() + 10;
+
+       add_tool(bar = new BC_Bar(x, y, get_w() - x * 2));
+       y += bar->get_h() + 5;
+
+
+//     add_tool(reverse = new LensToggle(client, 
+//             &client->config.reverse, 
+//             x, 
+//             y,
+//             _("Reverse")));
+//     y += reverse->get_h() + 5;
+
+       add_tool(draw_guides = new LensToggle(client, 
+               &client->config.draw_guides, 
+               x, 
+               y,
+               _("Draw center")));
+       y += draw_guides->get_h() + 5;
+
+       
+       add_tool(title = new BC_Title(x, y, _("Mode:")));
+       add_tool(mode = new LensMode(client, 
+               this, 
+               x + title->get_w() + 5, 
+               y));
+       mode->create_objects();
+       y += mode->get_h() + 5;
+
+
+//     add_tool(title = new BC_Title(x, y, _("Preset:")));
+//     add_tool(presets = new LensPresets(client, 
+//             this, 
+//             x + title->get_w() + 5, 
+//             y,
+//             get_w() - x - title->get_w() - 50));
+//     presets->create_objects();
+//     y += presets->get_h() + 5;
+// 
+//     add_tool(save_preset = new LensSavePreset(client,
+//             this,
+//             x, 
+//             y));
+//     add_tool(preset_text = new LensPresetText(client,
+//             this,
+//             x + save_preset->get_w() + 5,
+//             y,
+//             get_w() - x - save_preset->get_w() - 10));
+//     y += preset_text->get_h() + 5;
+//     add_tool(delete_preset = new LensDeletePreset(client,
+//             this,
+//             x,
+//             y));
+
+       show_window();
+       flush();
+}
+
+
+
+
+
+
+
+LensMain::LensMain(PluginServer *server)
+ : PluginVClient(server)
+{
+       
+       engine = 0;
+       lock = 0;
+       current_preset = -1;
+}
+
+LensMain::~LensMain()
+{
+       
+       delete engine;
+       presets.remove_all_objects();
+}
+
+NEW_WINDOW_MACRO(LensMain, LensGUI)
+LOAD_CONFIGURATION_MACRO(LensMain, LensConfig)
+int LensMain::is_realtime() { return 1; }
+const char* LensMain::plugin_title() { return _("Lens"); }
+
+void LensMain::update_gui()
+{
+       if(thread)
+       {
+               if(load_configuration())
+               {
+                       ((LensGUI*)thread->window)->lock_window("LensMain::update_gui");
+                       for(int i = 0; i < FOV_CHANNELS; i++)
+                       {
+                               ((LensGUI*)thread->window)->fov_slider[i]->update(config.fov[i]);
+                               ((LensGUI*)thread->window)->fov_text[i]->update(config.fov[i]);
+                       }
+                       ((LensGUI*)thread->window)->aspect_slider->update(config.aspect);
+                       ((LensGUI*)thread->window)->aspect_text->update(config.aspect);
+                       ((LensGUI*)thread->window)->radius_slider->update(config.radius);
+                       ((LensGUI*)thread->window)->radius_text->update(config.radius);
+                       ((LensGUI*)thread->window)->centerx_slider->update(config.center_x);
+                       ((LensGUI*)thread->window)->centerx_text->update(config.center_x);
+                       ((LensGUI*)thread->window)->centery_slider->update(config.center_y);
+                       ((LensGUI*)thread->window)->centery_text->update(config.center_y);
+                       ((LensGUI*)thread->window)->mode->update(config.mode);
+                       ((LensGUI*)thread->window)->draw_guides->update(config.draw_guides);
+                       ((LensGUI*)thread->window)->unlock_window();
+               }
+       }
+}
+
+//void LensMain::save_presets()
+//{
+//     char path[BCTEXTLEN], string[BCTEXTLEN];
+//     sprintf(path, "%slenspresets.rc", BCASTDIR);
+//     BC_Hash *defaults = new BC_Hash(path);
+//
+//// Save presets
+//     defaults->update("TOTAL_PRESETS", presets.total);
+//     for(int i = 0; i < presets.total; i++)
+//     {
+//             LensPreset *preset = presets.values[i];
+//             sprintf(string, "TITLE_%d", i);
+//             defaults->update(string, preset->title);
+//
+//             for(int j = 0; j < FOV_CHANNELS; j++)
+//             {
+//                     sprintf(string, "FOCAL_LENGTH_%d_%d", i, j);
+//                     defaults->update(string, preset->fov[j]);
+//             }
+//
+//             sprintf(string, "ASPECT_%d", i);
+//             defaults->update(string, preset->aspect);
+//             sprintf(string, "RADIUS_%d", i);
+//             defaults->update(string, preset->radius);
+//             sprintf(string, "MODE_%d", i);
+//             defaults->update(string, preset->mode);
+//     }
+//     
+//     defaults->save();
+//     delete defaults;
+//}
+
+
+void LensMain::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+       char string[BCTEXTLEN];
+
+
+
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+       output.tag.set_title("LENS");
+       for(int i = 0; i < FOV_CHANNELS; i++)
+       {
+               sprintf(string, "FOCAL_LENGTH%d", i);
+               output.tag.set_property(string, config.fov[i]);
+       }
+       output.tag.set_property("ASPECT", config.aspect);
+       output.tag.set_property("RADIUS", config.radius);
+       output.tag.set_property("MODE", config.mode);
+       output.tag.set_property("CENTER_X", config.center_x);
+       output.tag.set_property("CENTER_Y", config.center_y);
+       output.tag.set_property("DRAW_GUIDES", config.draw_guides);
+       output.append_tag();
+       output.tag.set_title("/LENS");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+
+}
+
+
+void LensMain::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+       char string[BCTEXTLEN];
+
+
+       input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+
+       int result = 0;
+
+       while(!result)
+       {
+               result = input.read_tag();
+
+               if(!result)
+               {
+                       if(input.tag.title_is("LENS"))
+                       {
+                               for(int i = 0; i < FOV_CHANNELS; i++)
+                               {
+                                       sprintf(string, "FOCAL_LENGTH%d", i);
+                                       config.fov[i] = input.tag.get_property(string, config.fov[i]);
+                               }
+                               config.aspect = input.tag.get_property("ASPECT", config.aspect);
+                               config.radius = input.tag.get_property("RADIUS", config.radius);
+                               config.mode = input.tag.get_property("MODE", config.mode);
+                               config.center_x = input.tag.get_property("CENTER_X", config.center_x);
+                               config.center_y = input.tag.get_property("CENTER_Y", config.center_y);
+                               config.draw_guides = input.tag.get_property("DRAW_GUIDES", config.draw_guides);
+                       }
+               }
+       }
+}
+
+
+
+int LensMain::process_buffer(VFrame *frame,
+       int64_t start_position,
+       double frame_rate)
+{
+       VFrame *input;
+       load_configuration();
+       
+       if(get_use_opengl())
+       {
+               input = frame;
+       }
+       else
+       {
+               input = new_temp(frame->get_w(), frame->get_h(), frame->get_color_model());
+       }
+       
+       read_frame(input, 
+               0, 
+               start_position, 
+               frame_rate,
+               get_use_opengl());
+
+
+       if(get_use_opengl())
+       {
+               run_opengl();
+               return 0;
+       }
+       else
+       {
+               if(!engine) engine = new LensEngine(this);
+               engine->process_packages();
+               if(config.draw_guides)
+               {
+// Draw center
+#define CENTER_H 20
+#define CENTER_W 20
+#define DRAW_GUIDES(components, type, max) \
+{ \
+       type **rows = (type**)get_output()->get_rows(); \
+       if( (center_x >= 0 && center_x < w) || (center_y >= 0 && center_y < h) ) { \
+               type *hrow = rows[center_y] + components * (center_x - CENTER_W / 2); \
+               for(int i = center_x - CENTER_W / 2; i <= center_x + CENTER_W / 2; i++) \
+               { \
+                       if(i >= 0 && i < w) { \
+                               hrow[0] = max - hrow[0]; \
+                               hrow[1] = max - hrow[1]; \
+                               hrow[2] = max - hrow[2]; \
+                               hrow += components; \
+                       } \
+               } \
+ \
+               for(int i = center_y - CENTER_W / 2; i <= center_y + CENTER_W / 2; i++) \
+               { \
+                       if(i >= 0 && i < h) { \
+                               type *vrow = rows[i] + center_x * components; \
+                               vrow[0] = max - vrow[0]; \
+                               vrow[1] = max - vrow[1]; \
+                               vrow[2] = max - vrow[2]; \
+                       } \
+               } \
+       } \
+}
+
+                       int w = get_output()->get_w();
+                       int h = get_output()->get_h();
+                       int center_x = (int)(config.center_x * w / 100);
+                       int center_y = (int)(config.center_y * h / 100);
+                       switch(get_output()->get_color_model())
+                       {
+                               case BC_RGB_FLOAT:
+                                       DRAW_GUIDES(3, float, 1.0)
+                                       break;
+                               case BC_RGBA_FLOAT:
+                                       DRAW_GUIDES(4, float, 1.0)
+                                       break;
+                               case BC_RGB888:
+                                       DRAW_GUIDES(3, unsigned char, 0xff)
+                                       break;
+                               case BC_RGBA8888:
+                                       DRAW_GUIDES(4, unsigned char, 0xff)
+                                       break;
+                               case BC_YUV888:
+                                       DRAW_GUIDES(3, unsigned char, 0xff)
+                                       break;
+                               case BC_YUVA8888:
+                                       DRAW_GUIDES(4, unsigned char, 0xff)
+                                       break;
+                       }
+
+               }
+       }
+
+       return 0;
+}
+
+
+int LensMain::handle_opengl()
+{
+#ifdef HAVE_GL
+       static const char *shrink_frag = 
+               "uniform sampler2D tex;\n"
+               "uniform vec2 texture_extents;\n"
+               "uniform vec2 image_extents;\n"
+               "uniform vec2 aspect;\n"
+               "uniform vec2 center_coord;\n"
+               "uniform vec4 border_color;\n"
+               "uniform vec4 r;\n"
+               "uniform vec4 max_z;\n"
+               "void main()\n"
+               "{\n"
+               "       vec2 outcoord = gl_TexCoord[0].st * texture_extents;\n"
+               "       vec2 coord_diff = outcoord - center_coord;\n"
+               "       if(coord_diff.x == 0.0 && coord_diff.y == 0.0)\n"
+               "       {\n"
+               "               gl_FragColor = texture2D(tex, outcoord);\n"
+               "       }\n"
+               "       else\n"
+               "       {\n"
+               "               float z = sqrt(coord_diff.x * coord_diff.x +\n"
+               "                                               coord_diff.y * coord_diff.y);\n"
+               "               float a2 = atan(coord_diff.y, coord_diff.x);\n"
+               "               vec4 a1 = asin(vec4(z, z, z, z) / r);\n"
+               "               vec4 z_in = a1 * max_z * 2.0 / 3.14159;\n"
+               "               vec4 in_x;\n"
+               "               vec4 in_y;\n"
+               "               in_x = z_in * cos(a2) * aspect.x + center_coord.x;\n"
+               "               in_y = z_in * sin(a2) * aspect.y + center_coord.y;\n"
+               "               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"
+               "                       gl_FragColor.r = border_color.r;\n"
+               "               else\n"
+               "                       gl_FragColor.r = texture2D(tex, vec2(in_x.r, in_y.r) / texture_extents).r;\n"
+               "               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"
+               "                       gl_FragColor.g = border_color.g;\n"
+               "               else\n"
+               "                       gl_FragColor.g = texture2D(tex, vec2(in_x.g, in_y.g) / texture_extents).g;\n"
+               "               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"
+               "                       gl_FragColor.b = border_color.b;\n"
+               "               else\n"
+               "                       gl_FragColor.b = texture2D(tex, vec2(in_x.b, in_y.b) / texture_extents).b;\n"
+               "               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"
+               "                       gl_FragColor.a = border_color.a;\n"
+               "               else\n"
+               "                       gl_FragColor.a = texture2D(tex, vec2(in_x.a, in_y.a) / texture_extents).a;\n"
+               "       }\n"
+               "}\n";
+
+       static const char *stretch_frag = 
+               "uniform sampler2D tex;\n"
+               "uniform vec2 texture_extents;\n"
+               "uniform vec2 image_extents;\n"
+               "uniform vec2 aspect;\n"
+               "uniform vec2 center_coord;\n"
+               "uniform vec4 border_color;\n"
+               "uniform vec4 r;\n"
+               "void main()\n"
+               "{\n"
+               "       vec2 outcoord = gl_TexCoord[0].st * texture_extents;\n"
+               "       vec2 coord_diff = outcoord - center_coord;\n"
+               "       float z = sqrt(coord_diff.x * coord_diff.x +\n"
+               "                                       coord_diff.y * coord_diff.y);\n"
+               "       vec4 a1 = (vec4(z, z, z, z) / (3.14159 * r / 2.0)) * (3.14159 / 2.0);\n"
+               "       vec4 z_in = r * sin(a1);\n"
+               "       float a2;\n"
+               "       if(coord_diff.x == 0.0)\n"
+               "       {\n"
+               "               if(coord_diff.y < 0.0)\n"
+               "                       a2 = 3.0 * 3.14159 / 2.0;\n"
+               "               else\n"
+               "                       a2 = 3.14159 / 2.0;\n"
+               "       }\n"
+               "       else\n"
+               "               a2 = atan(coord_diff.y, coord_diff.x);\n"
+               "       vec4 in_x;\n"
+               "       vec4 in_y;\n"
+               "       in_x = z_in * cos(a2) * aspect.x + center_coord.x;\n"
+               "       in_y = z_in * sin(a2) * aspect.y + center_coord.y;\n"
+               "       if(in_x.r < 0.0 || in_x.r >= image_extents.x || in_y.r < 0.0 || in_y.r >= image_extents.y)\n"
+               "               gl_FragColor.r = border_color.r;\n"
+               "       else\n"
+               "               gl_FragColor.r = texture2D(tex, vec2(in_x.r, in_y.r) / texture_extents).r;\n"
+               "       if(in_x.g < 0.0 || in_x.g >= image_extents.x || in_y.g < 0.0 || in_y.g >= image_extents.y)\n"
+               "               gl_FragColor.g = border_color.g;\n"
+               "       else\n"
+               "               gl_FragColor.g = texture2D(tex, vec2(in_x.g, in_y.g) / texture_extents).g;\n"
+               "       if(in_x.b < 0.0 || in_x.b >= image_extents.x || in_y.b < 0.0 || in_y.b >= image_extents.y)\n"
+               "               gl_FragColor.b = border_color.b;\n"
+               "       else\n"
+               "               gl_FragColor.b = texture2D(tex, vec2(in_x.b, in_y.b) / texture_extents).b;\n"
+               "       if(in_x.a < 0.0 || in_x.a >= image_extents.x || in_y.a < 0.0 || in_y.a >= image_extents.y)\n"
+               "               gl_FragColor.a = border_color.a;\n"
+               "       else\n"
+               "               gl_FragColor.a = texture2D(tex, vec2(in_x.a, in_y.a) / texture_extents).a;\n"
+               "}\n";
+
+
+       static const char *rectilinear_stretch_frag = 
+               "uniform sampler2D tex;\n"
+               "uniform vec2 texture_extents;\n"
+               "uniform vec2 image_extents;\n"
+               "uniform vec2 aspect;\n"
+               "uniform vec2 center_coord;\n"
+               "uniform vec4 border_color;\n"
+               "uniform vec4 r;\n"
+               "uniform float radius;\n"
+               "void main()\n"
+               "{\n"
+               "       vec2 outcoord = gl_TexCoord[0].st * texture_extents;\n"
+               "       vec2 coord_diff = outcoord - center_coord;\n"
+               "       float z = sqrt(coord_diff.x * coord_diff.x +\n"
+               "                                       coord_diff.y * coord_diff.y);\n"
+               "       vec4 radius1 = (vec4(z, z, z, z) / r) * 2.0 * radius;\n"
+               "       vec4 z_in = r * atan(radius1) / (3.14159 / 2.0);\n"
+               "\n"
+               "       float angle;\n"
+               "       if(coord_diff.x == 0.0)\n"
+               "       {\n"
+               "               if(coord_diff.y < 0.0)\n"
+               "                       angle = 3.0 * 3.14159 / 2.0;\n"
+               "               else\n"
+               "                       angle = 3.14159 / 2.0;\n"
+               "       }\n"
+               "       else\n"
+               "               angle = atan(coord_diff.y, coord_diff.x);\n"
+               "       vec4 in_x;\n"
+               "       vec4 in_y;\n"
+               "\n"
+               "       in_x = z_in * cos(angle) * aspect.x + center_coord.x;\n"
+               "       in_y = z_in * sin(angle) * aspect.y + center_coord.y;\n"
+               "       if(in_x.r < 0.0 || in_x.r >= image_extents.x || in_y.r < 0.0 || in_y.r >= image_extents.y)\n"
+               "               gl_FragColor.r = border_color.r;\n"
+               "       else\n"
+               "               gl_FragColor.r = texture2D(tex, vec2(in_x.r, in_y.r) / texture_extents).r;\n"
+               "       if(in_x.g < 0.0 || in_x.g >= image_extents.x || in_y.g < 0.0 || in_y.g >= image_extents.y)\n"
+               "               gl_FragColor.g = border_color.g;\n"
+               "       else\n"
+               "               gl_FragColor.g = texture2D(tex, vec2(in_x.g, in_y.g) / texture_extents).g;\n"
+               "       if(in_x.b < 0.0 || in_x.b >= image_extents.x || in_y.b < 0.0 || in_y.b >= image_extents.y)\n"
+               "               gl_FragColor.b = border_color.b;\n"
+               "       else\n"
+               "               gl_FragColor.b = texture2D(tex, vec2(in_x.b, in_y.b) / texture_extents).b;\n"
+               "       if(in_x.a < 0.0 || in_x.a >= image_extents.x || in_y.a < 0.0 || in_y.a >= image_extents.y)\n"
+               "               gl_FragColor.a = border_color.a;\n"
+               "       else\n"
+               "               gl_FragColor.a = texture2D(tex, vec2(in_x.a, in_y.a) / texture_extents).a;\n"
+               "}\n";
+
+       static const char *rectilinear_shrink_frag = 
+               "uniform sampler2D tex;\n"
+               "uniform vec2 texture_extents;\n"
+               "uniform vec2 image_extents;\n"
+               "uniform vec2 aspect;\n"
+               "uniform vec2 center_coord;\n"
+               "uniform vec4 border_color;\n"
+               "uniform vec4 r;\n"
+               "uniform float radius;\n"
+               "void main()\n"
+               "{\n"
+               "       vec2 outcoord = gl_TexCoord[0].st * texture_extents;\n"
+               "       vec2 coord_diff = outcoord - center_coord;\n"
+               "       float z = sqrt(coord_diff.x * coord_diff.x +\n"
+               "                                       coord_diff.y * coord_diff.y);\n"
+               "       vec4 radius1 = (vec4(z, z, z, z) / r) * 2.0 * radius;\n"
+               "       vec4 z_in = r * atan(radius1) / (3.14159 / 2.0);\n"
+               "\n"
+               "       float angle;\n"
+               "       if(coord_diff.x == 0.0)\n"
+               "       {\n"
+               "               if(coord_diff.y < 0.0)\n"
+               "                       angle = 3.0 * 3.14159 / 2.0;\n"
+               "               else\n"
+               "                       angle = 3.14159 / 2.0;\n"
+               "       }\n"
+               "       else\n"
+               "               angle = atan(coord_diff.y, coord_diff.x);\n"
+               "       vec4 in_x;\n"
+               "       vec4 in_y;\n"
+               "\n"
+               "       in_x = z_in * cos(angle) * aspect.x + center_coord.x;\n"
+               "       in_y = z_in * sin(angle) * aspect.y + center_coord.y;\n"
+               "       if(in_x.r < 0.0 || in_x.r >= image_extents.x || in_y.r < 0.0 || in_y.r >= image_extents.y)\n"
+               "               gl_FragColor.r = border_color.r;\n"
+               "       else\n"
+               "               gl_FragColor.r = texture2D(tex, vec2(in_x.r, in_y.r) / texture_extents).r;\n"
+               "       if(in_x.g < 0.0 || in_x.g >= image_extents.x || in_y.g < 0.0 || in_y.g >= image_extents.y)\n"
+               "               gl_FragColor.g = border_color.g;\n"
+               "       else\n"
+               "               gl_FragColor.g = texture2D(tex, vec2(in_x.g, in_y.g) / texture_extents).g;\n"
+               "       if(in_x.b < 0.0 || in_x.b >= image_extents.x || in_y.b < 0.0 || in_y.b >= image_extents.y)\n"
+               "               gl_FragColor.b = border_color.b;\n"
+               "       else\n"
+               "               gl_FragColor.b = texture2D(tex, vec2(in_x.b, in_y.b) / texture_extents).b;\n"
+               "       if(in_x.a < 0.0 || in_x.a >= image_extents.x || in_y.a < 0.0 || in_y.a >= image_extents.y)\n"
+               "               gl_FragColor.a = border_color.a;\n"
+               "       else\n"
+               "               gl_FragColor.a = texture2D(tex, vec2(in_x.a, in_y.a) / texture_extents).a;\n"
+               "}\n";
+
+       get_output()->to_texture();
+       get_output()->enable_opengl();
+       unsigned int frag_shader = 0;
+
+       switch(config.mode)
+       {
+       case LensConfig::SHRINK:
+               frag_shader = VFrame::make_shader(0, shrink_frag, 0);
+               break;
+       case LensConfig::STRETCH:
+               frag_shader = VFrame::make_shader(0, stretch_frag, 0);
+               break;
+       case LensConfig::RECTILINEAR_STRETCH:
+               frag_shader = VFrame::make_shader(0, rectilinear_stretch_frag, 0);
+               break;
+       case LensConfig::RECTILINEAR_SHRINK:
+               frag_shader = VFrame::make_shader(0, rectilinear_shrink_frag, 0);
+               break;
+       }
+
+       if(frag_shader > 0)
+       {
+               float border_color[] = { 0, 0, 0, 0 };
+               if(BC_CModels::is_yuv(get_output()->get_color_model()))
+               {
+                       border_color[1] = 0.5;
+                       border_color[2] = 0.5;
+               }
+
+               double x_factor = config.aspect;
+               double y_factor = 1.0 / config.aspect;
+               if(x_factor < 1) x_factor = 1;
+               if(y_factor < 1) y_factor = 1;
+
+               glUseProgram(frag_shader);
+               glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0);
+               glUniform2f(glGetUniformLocation(frag_shader, "aspect"), 
+                       x_factor, 
+                       y_factor);
+               glUniform2f(glGetUniformLocation(frag_shader, "center_coord"), 
+                               (GLfloat)get_input()->get_w() * config.center_x / 100.0,
+                               (GLfloat)get_input()->get_h() * config.center_y / 100.0);
+               glUniform2f(glGetUniformLocation(frag_shader, "texture_extents"), 
+                               (GLfloat)get_input()->get_texture_w(),
+                               (GLfloat)get_input()->get_texture_h());
+               glUniform2f(glGetUniformLocation(frag_shader, "image_extents"), 
+                               (GLfloat)get_input()->get_w(),
+                               (GLfloat)get_input()->get_h());
+
+               int width = get_output()->get_w();
+               int height = get_output()->get_h();
+               float *fov = config.fov;
+               float dim;
+               float max_z;
+               switch(config.mode)
+               {
+                       case LensConfig::SHRINK:
+                               dim = MAX(width, height) * config.radius;
+                               max_z = dim * sqrt(2.0) / 2;
+                               glUniform4fv(glGetUniformLocation(frag_shader, "border_color"), 
+                                               1,
+                                               (GLfloat*)border_color);
+                               glUniform4f(glGetUniformLocation(frag_shader, "max_z"), 
+                                       max_z / fov[0],
+                                       max_z / fov[1],
+                                       max_z / fov[2],
+                                       max_z / fov[3]);
+                               glUniform4f(glGetUniformLocation(frag_shader, "r"), 
+                                       (max_z / fov[0]) * 2 / M_PI,
+                                       (max_z / fov[1]) * 2 / M_PI,
+                                       (max_z / fov[2]) * 2 / M_PI,
+                                       (max_z / fov[3]) * 2 / M_PI);
+                               break;
+
+                       case LensConfig::STRETCH:
+                               dim = MAX(width, height) * config.radius;
+                               max_z = dim * sqrt(2.0) / 2;
+                               glUniform4f(glGetUniformLocation(frag_shader, "r"), 
+                                       max_z / M_PI / (fov[0] / 2.0),
+                                       max_z / M_PI / (fov[1] / 2.0),
+                                       max_z / M_PI / (fov[2] / 2.0),
+                                       max_z / M_PI / (fov[3] / 2.0));
+                               break;
+
+                       case LensConfig::RECTILINEAR_STRETCH:
+                               max_z = sqrt(SQR(width) + SQR(height)) / 2;
+                               glUniform4f(glGetUniformLocation(frag_shader, "r"), 
+                                       max_z / M_PI / (fov[0] / 2.0),
+                                       max_z / M_PI / (fov[1] / 2.0),
+                                       max_z / M_PI / (fov[2] / 2.0),
+                                       max_z / M_PI / (fov[3] / 2.0));
+                               glUniform1f(glGetUniformLocation(frag_shader, "radius"), 
+                                       config.radius);
+                               break;
+
+                       case LensConfig::RECTILINEAR_SHRINK:
+                               max_z = sqrt(SQR(width) + SQR(height)) / 2;
+                               glUniform4f(glGetUniformLocation(frag_shader, "r"), 
+                                       max_z / M_PI / (fov[0] / 2.0),
+                                       max_z / M_PI / (fov[1] / 2.0),
+                                       max_z / M_PI / (fov[2] / 2.0),
+                                       max_z / M_PI / (fov[3] / 2.0));
+                               glUniform1f(glGetUniformLocation(frag_shader, "radius"), 
+                                       config.radius);
+                               break;
+               }
+
+
+               get_output()->init_screen();
+               get_output()->bind_texture(0);
+               glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+               glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+               get_output()->draw_texture();
+               glUseProgram(0);
+
+
+               if(config.draw_guides)
+               {
+                       int w = get_output()->get_w();
+                       int h = get_output()->get_h();
+                       int center_x = (int)(config.center_x * w / 100);
+                       int center_y = (int)(config.center_y * h / 100);
+
+                       glDisable(GL_TEXTURE_2D);
+                       glColor4f(0.0, 0.0, 0.0, 1.0);
+                       glBegin(GL_LINES);
+                       glVertex3f(center_x, -h + center_y - CENTER_H / 2, 0.0);
+                       glVertex3f(center_x, -h + center_y + CENTER_H / 2, 0.0);
+                       glEnd();
+                       glBegin(GL_LINES);
+                       glVertex3f(center_x - CENTER_W / 2, -h + center_y, 0.0);
+                       glVertex3f(center_x + CENTER_W / 2, -h + center_y, 0.0);
+                       glEnd();
+                       glColor4f(1.0, 1.0, 1.0, 1.0);
+                       glBegin(GL_LINES);
+                       glVertex3f(center_x - 1, -h + center_y - CENTER_H / 2 - 1, 0.0);
+                       glVertex3f(center_x - 1, -h + center_y + CENTER_H / 2 - 1, 0.0);
+                       glEnd();
+                       glBegin(GL_LINES);
+                       glVertex3f(center_x - CENTER_W / 2 - 1, -h + center_y - 1, 0.0);
+                       glVertex3f(center_x + CENTER_W / 2 - 1, -h + center_y - 1, 0.0);
+                       glEnd();
+               }
+               get_output()->set_opengl_state(VFrame::SCREEN);
+       }
+       
+#endif
+       return 0;
+}
+
+
+
+
+
+
+
+
+LensPackage::LensPackage()
+ : LoadPackage() {}
+
+
+
+
+
+LensUnit::LensUnit(LensEngine *engine, LensMain *plugin)
+ : LoadClient(engine)
+{
+       this->plugin = plugin;
+}
+
+LensUnit::~LensUnit()
+{
+}
+
+void LensUnit::process_shrink(LensPackage *pkg)
+{
+
+       float *fov = plugin->config.fov;
+       float aspect = plugin->config.aspect;
+       int row1 = pkg->row1;
+       int row2 = pkg->row2;
+       int width = plugin->get_input()->get_w();
+       int height = plugin->get_input()->get_h();
+       double x_factor = aspect;
+       double y_factor = 1.0 / aspect;
+       if(x_factor < 1) x_factor = 1;
+       if(y_factor < 1) y_factor = 1;
+       double dim = MAX(width, height) * plugin->config.radius;
+       double max_z[FOV_CHANNELS];
+       double center_x = width * plugin->config.center_x / 100.0;
+       double center_y = height * plugin->config.center_y / 100.0;
+       double r[FOV_CHANNELS];
+
+//     max_z[0] = sqrt(SQR(width) + SQR(height)) / 2 / fov[0];
+//     max_z[1] = sqrt(SQR(width) + SQR(height)) / 2 / fov[1];
+//     max_z[2] = sqrt(SQR(width) + SQR(height)) / 2 / fov[2];
+//     max_z[3] = sqrt(SQR(width) + SQR(height)) / 2 / fov[3];
+       max_z[0] = dim * sqrt(2.0) / 2 / fov[0];
+       max_z[1] = dim * sqrt(2.0) / 2 / fov[1];
+       max_z[2] = dim * sqrt(2.0) / 2 / fov[2];
+       max_z[3] = dim * sqrt(2.0) / 2 / fov[3];
+       r[0] = max_z[0] * 2 / M_PI;
+       r[1] = max_z[1] * 2 / M_PI;
+       r[2] = max_z[2] * 2 / M_PI;
+       r[3] = max_z[3] * 2 / M_PI;
+
+#define DO_LENS_SHRINK(type, components, chroma) \
+{ \
+       type **in_rows = (type**)plugin->get_temp()->get_rows(); \
+       type **out_rows = (type**)plugin->get_input()->get_rows(); \
+       type black[4] = { 0, chroma, chroma, 0 }; \
+ \
+       for(int y = row1; y < row2; y++) \
+       { \
+               type *out_row = out_rows[y]; \
+               type *in_row = in_rows[y]; \
+               double y_diff = y - center_y; \
+ \
+               for(int x = 0; x < width; x++) \
+               { \
+                       double x_diff = x - center_x; \
+                       if(!x_diff && !y_diff) \
+                       { \
+                               type *in_pixel = in_row + x * components; \
+                               for(int c = 0; c < components; c++) \
+                               { \
+                                       *out_row++ = *in_pixel++; \
+                               } \
+                               continue; \
+                       } \
+ \
+                       double z = sqrt(x_diff * x_diff + y_diff * y_diff); \
+                       double a2 = atan(y_diff / x_diff); \
+                       if(x_diff < 0.0) a2 += M_PI; \
+ \
+                       for(int i = 0; i < components; i++) \
+                       { \
+                               if(z > r[i]) \
+                               { \
+                                       *out_row++ = black[i]; \
+                               } \
+                               else \
+                               { \
+                                       double a1 = asin(z / r[i]); \
+                                       double z_in = a1 * max_z[i] * 2 / M_PI; \
+ \
+                                       float x_in = z_in * cos(a2) * x_factor + center_x; \
+                                       float y_in = z_in * sin(a2) * y_factor + center_y; \
+ \
+                                       if(x_in < 0.0 || x_in >= width - 1 || \
+                                               y_in < 0.0 || y_in >= height - 1) \
+                                       { \
+                                               *out_row++ = black[i]; \
+                                       } \
+                                       else \
+                                       { \
+                                               float y1_fraction = y_in - floor(y_in); \
+                                               float y2_fraction = 1.0 - y1_fraction; \
+                                               float x1_fraction = x_in - floor(x_in); \
+                                               float x2_fraction = 1.0 - x1_fraction; \
+                                               type *in_pixel1 = in_rows[(int)y_in] + (int)x_in * components; \
+                                               type *in_pixel2 = in_rows[(int)y_in + 1] + (int)x_in * components; \
+                                               *out_row++ = (type)(in_pixel1[i] * x2_fraction * y2_fraction + \
+                                                                       in_pixel2[i] * x2_fraction * y1_fraction + \
+                                                                       in_pixel1[i + components] * x1_fraction * y2_fraction + \
+                                                                       in_pixel2[i + components] * x1_fraction * y1_fraction); \
+                                       } \
+                               } \
+                       } \
+               } \
+       } \
+ \
+       type *out_pixel = out_rows[(int)center_y] + (int)center_x * components; \
+       type *in_pixel = in_rows[(int)center_y] + (int)center_x * components; \
+       for(int c = 0; c < components; c++) \
+       { \
+               *out_pixel++ = *in_pixel++; \
+       } \
+}
+
+
+               switch(plugin->get_input()->get_color_model())
+               {
+                       case BC_RGB888:
+                               DO_LENS_SHRINK(unsigned char, 3, 0x0);
+                               break;
+                       case BC_RGBA8888:
+                               DO_LENS_SHRINK(unsigned char, 4, 0x0);
+                               break;
+                       case BC_RGB_FLOAT:
+                               DO_LENS_SHRINK(float, 3, 0.0);
+                               break;
+                       case BC_RGBA_FLOAT:
+                               DO_LENS_SHRINK(float, 4, 0.0);
+                               break;
+                       case BC_YUV888:
+                               DO_LENS_SHRINK(unsigned char, 3, 0x80);
+                               break;
+               case BC_YUVA8888:
+                               DO_LENS_SHRINK(unsigned char, 4, 0x80);
+                               break;
+               }
+}
+
+void LensUnit::process_stretch(LensPackage *pkg)
+{
+       float *fov = plugin->config.fov;
+       float aspect = plugin->config.aspect;
+       int row1 = pkg->row1;
+       int row2 = pkg->row2;
+       double x_factor = aspect;
+       double y_factor = 1.0 / aspect;
+       if(x_factor < 1) x_factor = 1;
+       if(y_factor < 1) y_factor = 1;
+       int width = plugin->get_input()->get_w();
+       int height = plugin->get_input()->get_h();
+       double dim = MAX(width, height) * plugin->config.radius;
+       double max_z = dim * sqrt(2.0) / 2;
+       double center_x = width * plugin->config.center_x / 100.0;
+       double center_y = height * plugin->config.center_y / 100.0;
+       double r[FOV_CHANNELS];
+
+       r[0] = max_z / M_PI / (fov[0] / 2.0);
+       r[1] = max_z / M_PI / (fov[1] / 2.0);
+       r[2] = max_z / M_PI / (fov[2] / 2.0);
+       r[3] = max_z / M_PI / (fov[3] / 2.0);
+
+#define DO_LENS_STRETCH(type, components, chroma) \
+{ \
+       type **in_rows = (type**)plugin->get_temp()->get_rows(); \
+       type **out_rows = (type**)plugin->get_input()->get_rows(); \
+       type black[4] = { 0, chroma, chroma, 0 }; \
+ \
+       for(int y = row1; y < row2; y++) \
+       { \
+               type *out_row = out_rows[y]; \
+               double y_diff = y - center_y; \
+ \
+               for(int x = 0; x < width; x++) \
+               { \
+                       double x_diff = (x - center_x); \
+                       double z = sqrt(x_diff * x_diff + \
+                               y_diff * y_diff); \
+                       double a2; \
+                       if(x == center_x) \
+                       { \
+                               if(y < center_y) \
+                                       a2 = 3 * M_PI / 2; \
+                               else \
+                                       a2 = M_PI / 2; \
+                       } \
+                       else \
+                       { \
+                               a2 = atan(y_diff / x_diff); \
+                       } \
+                       if(x_diff < 0.0) a2 += M_PI; \
+ \
+                       for(int i = 0; i < components; i++) \
+                       { \
+                               double a1 = (z / (M_PI * r[i] / 2)) * (M_PI / 2); \
+                               double z_in = r[i] * sin(a1); \
+ \
+                               double x_in = z_in * cos(a2) * x_factor + center_x; \
+                               double y_in = z_in * sin(a2) * y_factor + center_y; \
+ \
+                               if(x_in < 0.0 || x_in >= width - 1 || \
+                                       y_in < 0.0 || y_in >= height - 1) \
+                               { \
+                                       *out_row++ = black[i]; \
+                               } \
+                               else \
+                               { \
+                                       float y1_fraction = y_in - floor(y_in); \
+                                       float y2_fraction = 1.0 - y1_fraction; \
+                                       float x1_fraction = x_in - floor(x_in); \
+                                       float x2_fraction = 1.0 - x1_fraction; \
+                                       type *in_pixel1 = in_rows[(int)y_in] + (int)x_in * components; \
+                                       type *in_pixel2 = in_rows[(int)y_in + 1] + (int)x_in * components; \
+                                       *out_row++ = (type)(in_pixel1[i] * x2_fraction * y2_fraction + \
+                                                               in_pixel2[i] * x2_fraction * y1_fraction + \
+                                                               in_pixel1[i + components] * x1_fraction * y2_fraction + \
+                                                               in_pixel2[i + components] * x1_fraction * y1_fraction); \
+                               } \
+                       } \
+               } \
+       } \
+ \
+       type *out_pixel = out_rows[(int)center_y] + (int)center_x * components; \
+       type *in_pixel = in_rows[(int)center_y] + (int)center_x * components; \
+       for(int c = 0; c < components; c++) \
+       { \
+               *out_pixel++ = *in_pixel++; \
+       } \
+}
+
+
+               switch(plugin->get_input()->get_color_model())
+               {
+                       case BC_RGB888:
+                               DO_LENS_STRETCH(unsigned char, 3, 0x0);
+                               break;
+                       case BC_RGBA8888:
+                               DO_LENS_STRETCH(unsigned char, 4, 0x0);
+                               break;
+                       case BC_RGB_FLOAT:
+                               DO_LENS_STRETCH(float, 3, 0.0);
+                               break;
+                       case BC_RGBA_FLOAT:
+                               DO_LENS_STRETCH(float, 4, 0.0);
+                               break;
+                       case BC_YUV888:
+                               DO_LENS_STRETCH(unsigned char, 3, 0x80);
+                               break;
+                       case BC_YUVA8888:
+                               DO_LENS_STRETCH(unsigned char, 4, 0x80);
+                               break;
+               }
+}
+
+void LensUnit::process_rectilinear_stretch(LensPackage *pkg)
+{
+       float *fov = plugin->config.fov;
+       float aspect = plugin->config.aspect;
+       int row1 = pkg->row1;
+       int row2 = pkg->row2;
+       double x_factor = aspect;
+       double y_factor = 1.0 / aspect;
+       if(x_factor < 1) x_factor = 1;
+       if(y_factor < 1) y_factor = 1;
+       int width = plugin->get_input()->get_w();
+       int height = plugin->get_input()->get_h();
+//     double dim = MAX(width, height) * plugin->config.radius;
+//     double max_z = dim * sqrt(2.0) / 2;
+       double max_z = sqrt(SQR(width) + SQR(height)) / 2;
+       double center_x = width * plugin->config.center_x / 100.0;
+       double center_y = height * plugin->config.center_y / 100.0;
+       double r[FOV_CHANNELS];
+
+       r[0] = max_z / M_PI / (fov[0] / 2.0);
+       r[1] = max_z / M_PI / (fov[1] / 2.0);
+       r[2] = max_z / M_PI / (fov[2] / 2.0);
+       r[3] = max_z / M_PI / (fov[3] / 2.0);
+
+#define DO_LENS_RECTILINEAR_STRETCH(type, components, chroma) \
+{ \
+       type **in_rows = (type**)plugin->get_temp()->get_rows(); \
+       type **out_rows = (type**)plugin->get_input()->get_rows(); \
+       type black[4] = { 0, chroma, chroma, 0 }; \
+ \
+       for(int y = row1; y < row2; y++) \
+       { \
+               type *out_row = out_rows[y]; \
+               double y_diff = y - center_y; \
+ \
+               for(int x = 0; x < width; x++) \
+               { \
+                       double x_diff = (x - center_x); \
+/* Compute magnitude */ \
+                       double z = sqrt(x_diff * x_diff + \
+                               y_diff * y_diff); \
+/* Compute angle */ \
+                       double angle; \
+                       if(x == center_x) \
+                       { \
+                               if(y < center_y) \
+                                       angle = 3 * M_PI / 2; \
+                               else \
+                                       angle = M_PI / 2; \
+                       } \
+                       else \
+                       { \
+                               angle = atan(y_diff / x_diff); \
+                       } \
+                       if(x_diff < 0.0) angle += M_PI; \
+ \
+                       for(int i = 0; i < components; i++) \
+                       { \
+/* Compute new radius */ \
+                               double radius1 = (z / r[i]) * 2 * plugin->config.radius; \
+                               double z_in = r[i] * atan(radius1) / (M_PI / 2); \
+ \
+                               double x_in = z_in * cos(angle) * x_factor + center_x; \
+                               double y_in = z_in * sin(angle) * y_factor + center_y; \
+ \
+                               if(x_in < 0.0 || x_in >= width - 1 || \
+                                       y_in < 0.0 || y_in >= height - 1) \
+                               { \
+                                       *out_row++ = black[i]; \
+                               } \
+                               else \
+                               { \
+                                       float y1_fraction = y_in - floor(y_in); \
+                                       float y2_fraction = 1.0 - y1_fraction; \
+                                       float x1_fraction = x_in - floor(x_in); \
+                                       float x2_fraction = 1.0 - x1_fraction; \
+                                       type *in_pixel1 = in_rows[(int)y_in] + (int)x_in * components; \
+                                       type *in_pixel2 = in_rows[(int)y_in + 1] + (int)x_in * components; \
+                                       *out_row++ = (type)(in_pixel1[i] * x2_fraction * y2_fraction + \
+                                                               in_pixel2[i] * x2_fraction * y1_fraction + \
+                                                               in_pixel1[i + components] * x1_fraction * y2_fraction + \
+                                                               in_pixel2[i + components] * x1_fraction * y1_fraction); \
+                               } \
+                       } \
+               } \
+       } \
+ \
+       type *out_pixel = out_rows[(int)center_y] + (int)center_x * components; \
+       type *in_pixel = in_rows[(int)center_y] + (int)center_x * components; \
+       for(int c = 0; c < components; c++) \
+       { \
+               *out_pixel++ = *in_pixel++; \
+       } \
+}
+
+
+               switch(plugin->get_input()->get_color_model())
+               {
+                       case BC_RGB888:
+                               DO_LENS_RECTILINEAR_STRETCH(unsigned char, 3, 0x0);
+                               break;
+                       case BC_RGBA8888:
+                               DO_LENS_RECTILINEAR_STRETCH(unsigned char, 4, 0x0);
+                               break;
+                       case BC_RGB_FLOAT:
+                               DO_LENS_RECTILINEAR_STRETCH(float, 3, 0.0);
+                               break;
+                       case BC_RGBA_FLOAT:
+                               DO_LENS_RECTILINEAR_STRETCH(float, 4, 0.0);
+                               break;
+                       case BC_YUV888:
+                               DO_LENS_RECTILINEAR_STRETCH(unsigned char, 3, 0x80);
+                               break;
+                       case BC_YUVA8888:
+                               DO_LENS_RECTILINEAR_STRETCH(unsigned char, 4, 0x80);
+                               break;
+               }
+}
+
+void LensUnit::process_rectilinear_shrink(LensPackage *pkg)
+{
+       float *fov = plugin->config.fov;
+       float aspect = plugin->config.aspect;
+       int row1 = pkg->row1;
+       int row2 = pkg->row2;
+       double x_factor = aspect;
+       double y_factor = 1.0 / aspect;
+       if(x_factor < 1) x_factor = 1;
+       if(y_factor < 1) y_factor = 1;
+       int width = plugin->get_input()->get_w();
+       int height = plugin->get_input()->get_h();
+       double max_z = MAX(width, height) / 2 * plugin->config.radius;
+       double center_x = width * plugin->config.center_x / 100.0;
+       double center_y = height * plugin->config.center_y / 100.0;
+       double r[FOV_CHANNELS];
+
+       r[0] = max_z / fov[0];
+       r[1] = max_z / fov[1];
+       r[2] = max_z / fov[2];
+       r[3] = max_z / fov[3];
+
+#define DO_LENS_RECTILINEAR_SHRINK(type, components, chroma) \
+{ \
+       type **in_rows = (type**)plugin->get_temp()->get_rows(); \
+       type **out_rows = (type**)plugin->get_input()->get_rows(); \
+       type black[4] = { 0, chroma, chroma, 0 }; \
+ \
+       for(int y = row1; y < row2; y++) \
+       { \
+               type *out_row = out_rows[y]; \
+               double y_diff = y - center_y; \
+ \
+               for(int x = 0; x < width; x++) \
+               { \
+                       double x_diff = (x - center_x); \
+/* Compute magnitude */ \
+                       double z = sqrt(x_diff * x_diff + \
+                               y_diff * y_diff); \
+/* Compute angle */ \
+                       double angle; \
+                       if(x == center_x) \
+                       { \
+                               if(y < center_y) \
+                                       angle = 3 * M_PI / 2; \
+                               else \
+                                       angle = M_PI / 2; \
+                       } \
+                       else \
+                       { \
+                               angle = atan(y_diff / x_diff); \
+                       } \
+                       if(x_diff < 0.0) angle += M_PI; \
+ \
+                       for(int i = 0; i < components; i++) \
+                       { \
+/* Compute new radius */ \
+                               double radius1 = z / r[i]; \
+                               double z_in = r[i] * tan(radius1) / (M_PI / 2); \
+ \
+                               double x_in = z_in * cos(angle) * x_factor + center_x; \
+                               double y_in = z_in * sin(angle) * y_factor + center_y; \
+ \
+                               if(x_in < 0.0 || x_in >= width - 1 || \
+                                       y_in < 0.0 || y_in >= height - 1) \
+                               { \
+                                       *out_row++ = black[i]; \
+                               } \
+                               else \
+                               { \
+                                       float y1_fraction = y_in - floor(y_in); \
+                                       float y2_fraction = 1.0 - y1_fraction; \
+                                       float x1_fraction = x_in - floor(x_in); \
+                                       float x2_fraction = 1.0 - x1_fraction; \
+                                       type *in_pixel1 = in_rows[(int)y_in] + (int)x_in * components; \
+                                       type *in_pixel2 = in_rows[(int)y_in + 1] + (int)x_in * components; \
+                                       *out_row++ = (type)(in_pixel1[i] * x2_fraction * y2_fraction + \
+                                                               in_pixel2[i] * x2_fraction * y1_fraction + \
+                                                               in_pixel1[i + components] * x1_fraction * y2_fraction + \
+                                                               in_pixel2[i + components] * x1_fraction * y1_fraction); \
+                               } \
+                       } \
+               } \
+       } \
+ \
+       type *out_pixel = out_rows[(int)center_y] + (int)center_x * components; \
+       type *in_pixel = in_rows[(int)center_y] + (int)center_x * components; \
+       for(int c = 0; c < components; c++) \
+       { \
+               *out_pixel++ = *in_pixel++; \
+       } \
+}
+
+
+               switch(plugin->get_input()->get_color_model())
+               {
+                       case BC_RGB888:
+                               DO_LENS_RECTILINEAR_SHRINK(unsigned char, 3, 0x0);
+                               break;
+                       case BC_RGBA8888:
+                               DO_LENS_RECTILINEAR_SHRINK(unsigned char, 4, 0x0);
+                               break;
+                       case BC_RGB_FLOAT:
+                               DO_LENS_RECTILINEAR_SHRINK(float, 3, 0.0);
+                               break;
+                       case BC_RGBA_FLOAT:
+                               DO_LENS_RECTILINEAR_SHRINK(float, 4, 0.0);
+                               break;
+                       case BC_YUV888:
+                               DO_LENS_RECTILINEAR_SHRINK(unsigned char, 3, 0x80);
+                               break;
+                       case BC_YUVA8888:
+                               DO_LENS_RECTILINEAR_SHRINK(unsigned char, 4, 0x80);
+                               break;
+               }
+}
+
+void LensUnit::process_package(LoadPackage *package)
+{
+       LensPackage *pkg = (LensPackage*)package;
+
+       switch(plugin->config.mode)
+       {
+               case LensConfig::SHRINK:
+                       process_shrink(pkg);
+                       break;
+               case LensConfig::STRETCH:
+                       process_stretch(pkg);
+                       break;
+               case LensConfig::RECTILINEAR_STRETCH:
+                       process_rectilinear_stretch(pkg);
+                       break;
+               case LensConfig::RECTILINEAR_SHRINK     :
+                       process_rectilinear_shrink(pkg);
+                       break;
+       }
+}
+
+
+
+
+
+LensEngine::LensEngine(LensMain *plugin)
+ : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
+// : LoadServer(1, 1)
+{
+       this->plugin = plugin;
+}
+
+LensEngine::~LensEngine()
+{
+}
+
+void LensEngine::init_packages()
+{
+       for(int i = 0; i < LoadServer::get_total_packages(); i++)
+       {
+               LensPackage *package = (LensPackage*)LoadServer::get_package(i);
+               package->row1 = plugin->get_input()->get_h() * i / LoadServer::get_total_packages();
+               package->row2 = plugin->get_input()->get_h() * (i + 1) / LoadServer::get_total_packages();
+       }
+}
+
+LoadClient* LensEngine::new_client()
+{
+       return new LensUnit(this, plugin);
+}
+
+LoadPackage* LensEngine::new_package()
+{
+       return new LensPackage;
+}
+
+