Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / plugins / chromakeyhsv / chromakey.C
diff --git a/cinelerra-5.1/plugins/chromakeyhsv/chromakey.C b/cinelerra-5.1/plugins/chromakeyhsv/chromakey.C
new file mode 100644 (file)
index 0000000..3541143
--- /dev/null
@@ -0,0 +1,1181 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2012 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 "bcsignals.h"
+#include "chromakey.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "guicast.h"
+#include "keyframe.h"
+#include "language.h"
+#include "loadbalance.h"
+#include "playback3d.h"
+#include "cicolors.h"
+#include "pluginvclient.h"
+#include "vframe.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+
+
+ChromaKeyConfig::ChromaKeyConfig ()
+{
+  red = 0.0;
+  green = 1.0;
+  blue = 0.0;
+
+  min_brightness = 50.0;
+  max_brightness = 100.0;
+  tolerance = 15.0;
+  saturation = 0.0;
+  min_saturation = 50.0;
+
+  in_slope = 2;
+  out_slope = 2;
+  alpha_offset = 0;
+
+  spill_threshold = 0.0;
+  spill_amount = 90.0;
+
+  show_mask = 0;
+
+}
+
+
+void
+ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
+{
+  red = src.red;
+  green = src.green;
+  blue = src.blue;
+  spill_threshold = src.spill_threshold;
+  spill_amount = src.spill_amount;
+  min_brightness = src.min_brightness;
+  max_brightness = src.max_brightness;
+  saturation = src.saturation;
+  min_saturation = src.min_saturation;
+  tolerance = src.tolerance;
+  in_slope = src.in_slope;
+  out_slope = src.out_slope;
+  alpha_offset = src.alpha_offset;
+  show_mask = src.show_mask;
+}
+
+int
+ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
+{
+  return (EQUIV (red, src.red) &&
+         EQUIV (green, src.green) &&
+         EQUIV (blue, src.blue) &&
+         EQUIV (spill_threshold, src.spill_threshold) &&
+         EQUIV (spill_amount, src.spill_amount) &&
+         EQUIV (min_brightness, src.min_brightness) &&
+         EQUIV (max_brightness, src.max_brightness) &&
+         EQUIV (saturation, src.saturation) &&
+         EQUIV (min_saturation, src.min_saturation) &&
+         EQUIV (tolerance, src.tolerance) &&
+         EQUIV (in_slope, src.in_slope) &&
+         EQUIV (out_slope, src.out_slope) &&
+         EQUIV (show_mask, src.show_mask) &&
+         EQUIV (alpha_offset, src.alpha_offset));
+}
+
+void
+ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
+                             ChromaKeyConfig & 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);
+
+  this->red = prev.red * prev_scale + next.red * next_scale;
+  this->green = prev.green * prev_scale + next.green * next_scale;
+  this->blue = prev.blue * prev_scale + next.blue * next_scale;
+  this->spill_threshold =
+    prev.spill_threshold * prev_scale + next.spill_threshold * next_scale;
+  this->spill_amount =
+    prev.spill_amount * prev_scale + next.tolerance * next_scale;
+  this->min_brightness =
+    prev.min_brightness * prev_scale + next.min_brightness * next_scale;
+  this->max_brightness =
+    prev.max_brightness * prev_scale + next.max_brightness * next_scale;
+  this->saturation =
+    prev.saturation * prev_scale + next.saturation * next_scale;
+  this->min_saturation =
+    prev.min_saturation * prev_scale + next.min_saturation * next_scale;
+  this->tolerance = prev.tolerance * prev_scale + next.tolerance * next_scale;
+  this->in_slope = prev.in_slope * prev_scale + next.in_slope * next_scale;
+  this->out_slope = prev.out_slope * prev_scale + next.out_slope * next_scale;
+  this->alpha_offset =
+    prev.alpha_offset * prev_scale + next.alpha_offset * next_scale;
+  this->show_mask = next.show_mask;
+
+}
+
+int
+ChromaKeyConfig::get_color ()
+{
+  int red = (int) (CLIP (this->red, 0, 1) * 0xff);
+  int green = (int) (CLIP (this->green, 0, 1) * 0xff);
+  int blue = (int) (CLIP (this->blue, 0, 1) * 0xff);
+  return (red << 16) | (green << 8) | blue;
+}
+
+
+
+ChromaKeyWindow::ChromaKeyWindow (ChromaKeyHSV * plugin)
+ : PluginClientWindow(plugin, 
+          400, 
+          450, 
+          400, 
+          450, 
+          0)
+{
+  this->plugin = plugin;
+  color_thread = 0;
+}
+
+ChromaKeyWindow::~ChromaKeyWindow ()
+{
+  delete color_thread;
+}
+
+void
+ChromaKeyWindow::create_objects ()
+{
+  int y = 10, y1, x1 = 0, x2 = 10;
+  int x = 30;
+
+  BC_Title *title;
+  BC_Bar *bar;
+  int ymargin = get_text_height(MEDIUMFONT) + 5;
+  int ymargin2 = get_text_height(MEDIUMFONT) + 10;
+
+  add_subwindow (title = new BC_Title (x2, y, _("Color:")));
+
+  add_subwindow (color = new ChromaKeyColor (plugin, this, x, y + 25));
+
+  add_subwindow (sample =
+                new BC_SubWindow (x + color->get_w () + 10, y, 100, 50));
+  y += sample->get_h () + 10;
+
+  add_subwindow (use_colorpicker =
+                new ChromaKeyUseColorPicker (plugin, this, x, y));
+  y += use_colorpicker->get_h() + 10;
+  add_subwindow (show_mask = new ChromaKeyShowMask (plugin, x2, y));
+       y += show_mask->get_h() + 5;
+
+       add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
+       y += bar->get_h() + 5;
+       y1 = y;
+  add_subwindow (new BC_Title (x2, y, _("Key parameters:")));
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Saturation Offset:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Min Saturation:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin2;
+
+       add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
+       y += bar->get_h() + 5;
+  add_subwindow (title = new BC_Title (x2, y, _("Mask tweaking:")));
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin2;
+
+
+
+       add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
+       y += bar->get_h() + 5;
+  add_subwindow (title = new BC_Title (x2, y, _("Spill light control:")));
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Spill Threshold:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin;
+  add_subwindow (title = new BC_Title (x, y, _("Spill Compensation:")));
+  if(title->get_w() > x1) x1 = title->get_w();
+  y += ymargin;
+
+
+       y = y1;
+  y += ymargin;
+       x1 += x;
+
+
+
+  add_subwindow (tolerance = new ChromaKeyTolerance (plugin, x1, y));
+  y += ymargin;
+  add_subwindow (min_brightness = new ChromaKeyMinBrightness (plugin, x1, y));
+  y += ymargin;
+  add_subwindow (max_brightness = new ChromaKeyMaxBrightness (plugin, x1, y));
+  y += ymargin;
+  add_subwindow (saturation = new ChromaKeySaturation (plugin, x1, y));
+  y += ymargin;
+  add_subwindow (min_saturation = new ChromaKeyMinSaturation (plugin, x1, y));
+  y += ymargin;
+
+       y += bar->get_h() + 5;
+  y += ymargin2;
+  add_subwindow (in_slope = new ChromaKeyInSlope (plugin, x1, y));
+  y += ymargin;
+  add_subwindow (out_slope = new ChromaKeyOutSlope (plugin, x1, y));
+  y += ymargin;
+  add_subwindow (alpha_offset = new ChromaKeyAlphaOffset (plugin, x1, y));
+   y += ymargin;
+
+       y += bar->get_h() + 5;
+  y += ymargin2;
+  add_subwindow (spill_threshold = new ChromaKeySpillThreshold (plugin, x1, y));
+  y += ymargin;
+  add_subwindow (spill_amount = new ChromaKeySpillAmount (plugin, x1, y));
+
+  color_thread = new ChromaKeyColorThread (plugin, this);
+
+  update_sample ();
+  show_window ();
+}
+
+void
+ChromaKeyWindow::update_sample ()
+{
+  sample->set_color (plugin->config.get_color ());
+  sample->draw_box (0, 0, sample->get_w (), sample->get_h ());
+  sample->set_color (BLACK);
+  sample->draw_rectangle (0, 0, sample->get_w (), sample->get_h ());
+  sample->flash ();
+}
+
+
+
+ChromaKeyColor::ChromaKeyColor (ChromaKeyHSV * plugin,
+                                 ChromaKeyWindow * gui, int x, int y):
+BC_GenericButton (x, y, _("Color..."))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int
+ChromaKeyColor::handle_event ()
+{
+  gui->color_thread->start_window (plugin->config.get_color (), 0xff);
+  return 1;
+}
+
+
+
+
+ChromaKeyMinBrightness::ChromaKeyMinBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0,
+           200, 200, (float) 0, (float) 100, plugin->config.min_brightness)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeyMinBrightness::handle_event ()
+{
+  plugin->config.min_brightness = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+ChromaKeyMaxBrightness::ChromaKeyMaxBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0,
+           200, 200, (float) 0, (float) 100, plugin->config.max_brightness)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeyMaxBrightness::handle_event ()
+{
+  plugin->config.max_brightness = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+ChromaKeySaturation::ChromaKeySaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0, 200, 200, (float) 0, (float) 100, plugin->config.saturation)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeySaturation::handle_event ()
+{
+  plugin->config.saturation = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+ChromaKeyMinSaturation::ChromaKeyMinSaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0,
+           200, 200, (float) 0, (float) 100, plugin->config.min_saturation)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeyMinSaturation::handle_event ()
+{
+  plugin->config.min_saturation = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+ChromaKeyTolerance::ChromaKeyTolerance (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0, 200, 200, (float) 0, (float) 100, plugin->config.tolerance)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeyTolerance::handle_event ()
+{
+  plugin->config.tolerance = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+
+ChromaKeyInSlope::ChromaKeyInSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0, 200, 200, (float) 0, (float) 20, plugin->config.in_slope)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeyInSlope::handle_event ()
+{
+  plugin->config.in_slope = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+ChromaKeyOutSlope::ChromaKeyOutSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0, 200, 200, (float) 0, (float) 20, plugin->config.out_slope)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeyOutSlope::handle_event ()
+{
+  plugin->config.out_slope = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+ChromaKeyAlphaOffset::ChromaKeyAlphaOffset (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0,
+           200, 200, (float) -100, (float) 100, plugin->config.alpha_offset)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeyAlphaOffset::handle_event ()
+{
+  plugin->config.alpha_offset = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+ChromaKeyShowMask::ChromaKeyShowMask (ChromaKeyHSV * plugin, int x, int y):BC_CheckBox (x, y, plugin->config.show_mask,
+            _
+            ("Show Mask"))
+{
+  this->plugin = plugin;
+
+}
+
+int
+ChromaKeyShowMask::handle_event ()
+{
+  plugin->config.show_mask = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyHSV * plugin, ChromaKeyWindow * gui, int x, int y)
+ : BC_GenericButton (x, y,
+                 _
+                 ("Use color picker"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int
+ChromaKeyUseColorPicker::handle_event ()
+{
+  plugin->config.red = plugin->get_red ();
+  plugin->config.green = plugin->get_green ();
+  plugin->config.blue = plugin->get_blue ();
+
+  gui->update_sample ();
+
+
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+
+ChromaKeySpillThreshold::ChromaKeySpillThreshold (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0,
+           200, 200, (float) 0, (float) 100, plugin->config.spill_threshold)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeySpillThreshold::handle_event ()
+{
+  plugin->config.spill_threshold = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+ChromaKeySpillAmount::ChromaKeySpillAmount (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
+           y,
+           0, 200, 200, (float) 0, (float) 100, plugin->config.spill_amount)
+{
+  this->plugin = plugin;
+  set_precision (0.01);
+}
+
+int
+ChromaKeySpillAmount::handle_event ()
+{
+  plugin->config.spill_amount = get_value ();
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+
+
+
+
+ChromaKeyColorThread::ChromaKeyColorThread (ChromaKeyHSV * plugin, ChromaKeyWindow * gui)
+ : ColorThread (1, _("Inner color"))
+{
+  this->plugin = plugin;
+  this->gui = gui;
+}
+
+int
+ChromaKeyColorThread::handle_new_color (int output, int alpha)
+{
+  plugin->config.red = (float) (output & 0xff0000) / 0xff0000;
+  plugin->config.green = (float) (output & 0xff00) / 0xff00;
+  plugin->config.blue = (float) (output & 0xff) / 0xff;
+
+  get_gui()->unlock_window();
+  gui->lock_window("ChromaKeyColorThread::handle_new_color");
+  gui->update_sample ();
+  gui->unlock_window();
+  get_gui()->lock_window("ChromaKeyColorThread::handle_new_color");
+
+
+  plugin->send_configure_change ();
+  return 1;
+}
+
+
+
+
+
+
+
+
+ChromaKeyServer::ChromaKeyServer (ChromaKeyHSV * plugin):LoadServer (plugin->PluginClient::smp + 1,
+           plugin->PluginClient::smp +
+           1)
+{
+  this->plugin = plugin;
+}
+
+void
+ChromaKeyServer::init_packages ()
+{
+  for (int i = 0; i < get_total_packages (); i++)
+    {
+      ChromaKeyPackage *pkg = (ChromaKeyPackage *) get_package (i);
+      pkg->y1 = plugin->input->get_h () * i / get_total_packages ();
+      pkg->y2 = plugin->input->get_h () * (i + 1) / get_total_packages ();
+    }
+
+}
+LoadClient *
+ChromaKeyServer::new_client ()
+{
+  return new ChromaKeyUnit (plugin, this);
+}
+
+LoadPackage *
+ChromaKeyServer::new_package ()
+{
+  return new ChromaKeyPackage;
+}
+
+
+
+ChromaKeyPackage::ChromaKeyPackage ():LoadPackage ()
+{
+}
+
+ChromaKeyUnit::ChromaKeyUnit (ChromaKeyHSV * plugin, ChromaKeyServer * server):LoadClient
+  (server)
+{
+  this->plugin = plugin;
+}
+
+
+
+
+#define ABS(a) ((a<0)?-(a):a)
+// Reuse as much as possible in the opengl version
+#define OUTER_VARIABLES \
+  float red = plugin->config.red; \
+  float green = plugin->config.green; \
+  float blue = plugin->config.blue; \
+ \
+  float in_slope = plugin->config.in_slope / 100; \
+  float out_slope = plugin->config.out_slope / 100; \
+ \
+  float tolerance = plugin->config.tolerance / 100; \
+  float tolerance_in = tolerance - in_slope; \
+  float tolerance_out = tolerance + out_slope; \
+ \
+  float sat = plugin->config.saturation / 100; \
+  float min_s = plugin->config.min_saturation / 100; \
+  float min_s_in = min_s + in_slope; \
+  float min_s_out = min_s - out_slope; \
+ \
+  float min_v = plugin->config.min_brightness / 100; \
+  float min_v_in = min_v + in_slope; \
+  float min_v_out = min_v - out_slope; \
+ \
+  float max_v = plugin->config.max_brightness / 100; \
+  float max_v_in = max_v - in_slope; \
+  float max_v_out = max_v + out_slope; \
+ \
+  float spill_threshold = plugin->config.spill_threshold / 100; \
+  float spill_amount = 1.0 - plugin->config.spill_amount / 100; \
+ \
+  float alpha_offset = plugin->config.alpha_offset / 100; \
+ \
+/* Convert RGB key to HSV key */ \
+       float hue_key, saturation_key, value_key; \
+       HSV::rgb_to_hsv(red,  \
+               green, \
+               blue, \
+               hue_key, \
+               saturation_key, \
+               value_key);
+
+
+template <typename component_type> 
+void ChromaKeyUnit::process_chromakey(int components, 
+       component_type max, 
+       bool use_yuv, 
+       ChromaKeyPackage *pkg) 
+{      
+       OUTER_VARIABLES
+
+       int w = plugin->input->get_w();
+
+       for (int i = pkg->y1; i < pkg->y2; i++)
+       {
+       component_type *row = (component_type *) plugin->input->get_rows ()[i];
+
+       for (int j = 0; j < w; j++)
+               {
+                       float a = 1;
+
+                       float r = (float) row[0] / max;
+                       float g = (float) row[1] / max;
+                       float b = (float) row[2] / max;
+
+                       float h, s, v;
+
+                       float av = 1, ah = 1, as = 1, avm = 1;
+                       bool has_match = true;
+
+                       if (use_yuv)
+               {
+/* Convert pixel to RGB float */
+                       float y = r;
+                       float u = g;
+                       float v = b;
+                       YUV::yuv_to_rgb_f (r, g, b, y, u - 0.5, v - 0.5);
+               }
+
+                       HSV::rgb_to_hsv (r, g, b, h, s, v);
+
+// First, test if the hue is in range
+
+/* Hue wrap */
+                       if(h <= hue_key - tolerance_in * 180.0)
+                               h += 360;
+                       else
+                       if(h >= hue_key + tolerance_in * 180.0)
+                               h -= 360;
+
+
+                       if (tolerance == 0)
+                   ah = 1.0;
+                       else 
+                       if (ABS (h - hue_key) < tolerance_in * 180)
+                   ah = 0;
+                       else 
+                       if ((out_slope != 0) && (ABS (h - hue_key) < tolerance * 180))
+/* we scale alpha between 0 and 1/2 */
+                   ah = ABS (h - hue_key) / tolerance / 360;   
+                       else 
+                       if (ABS (h - hue_key) < tolerance_out * 180)
+/* we scale alpha between 1/2 and 1 */
+                   ah = ABS (h - hue_key) / tolerance_out / 360;       
+                       else
+                   has_match = false;
+
+// Check if the saturation matches
+                       if (has_match)
+               {
+                       if (min_s == 0)
+                                       as = 0;
+                       else if (s - sat >= min_s_in)
+                                       as = 0;
+                       else if ((out_slope != 0) && (s - sat > min_s))
+                                       as = (s - sat - min_s) / (min_s * 2);
+                       else if (s - sat > min_s_out)
+                                       as = (s - sat - min_s_out) / (min_s_out * 2);
+                       else
+                                       has_match = false;
+               }
+
+// Check if the value is more than the minimun
+                       if (has_match)
+               {
+                       if (min_v == 0)
+                                       av = 0;
+                       else if (v >= min_v_in)
+                                       av = 0;
+                       else if ((out_slope != 0) && (v > min_v))
+                                       av = (v - min_v) / (min_v * 2);
+                       else if (v > min_v_out)
+                                       av = (v - min_v_out) / (min_v_out * 2);
+                       else
+                                       has_match = false;
+               }
+
+// Check if the value is less than the maximum
+                       if (has_match)
+               {
+                       if (max_v == 0)
+                                       avm = 1;
+                       else if (v <= max_v_in)
+                                       avm = 0;
+                       else if ((out_slope != 0) && (v < max_v))
+                                       avm = (v - max_v) / (max_v * 2);
+                       else if (v < max_v_out)
+                                       avm = (v - max_v_out) / (max_v_out * 2);
+                       else
+                                       has_match = false;
+               }
+
+         // If the color is part of the key, update the alpha channel
+         if (has_match)
+           a = MAX (MAX (ah, av), MAX (as, avm));
+
+         // Spill light processing       
+         if ((ABS (h - hue_key) < spill_threshold * 180) ||
+             ((ABS (h - hue_key) > 360)
+              && (ABS (h - hue_key) - 360 < spill_threshold * 180)))
+           {
+             s = s * spill_amount * ABS (h - hue_key) / (spill_threshold * 180);
+
+             HSV::hsv_to_rgb (r, g, b, h, s, v);
+
+             if (use_yuv)
+               {
+                 float y;
+                 float u;
+                 float v;
+                 YUV::rgb_to_yuv_f (r, g, b, y, u, v);
+             CLAMP (y, 0, 1.0);
+             CLAMP (u, 0, 1.0);
+             CLAMP (v, 0, 1.0);
+                 row[0] = (component_type) ((float) y * max);
+                 row[1] = (component_type) ((float) (u + 0.5) * max);
+                 row[2] = (component_type) ((float) (v + 0.5) * max);
+               }
+             else
+               {
+             CLAMP (r, 0, 1.0);
+             CLAMP (g, 0, 1.0);
+             CLAMP (b, 0, 1.0);
+                 row[0] = (component_type) ((float) r * max);
+                 row[1] = (component_type) ((float) g * max);
+                 row[2] = (component_type) ((float) b * max);
+               }
+           }
+
+         a += alpha_offset;
+         CLAMP (a, 0.0, 1.0);
+
+         if (plugin->config.show_mask)
+           {
+
+             if (use_yuv)
+               {
+                 row[0] = (component_type) ((float) a * max);
+                 row[1] = (component_type) ((float) max / 2);
+                 row[2] = (component_type) ((float) max / 2);
+               }
+             else
+               {
+                 row[0] = (component_type) ((float) a * max);
+                 row[1] = (component_type) ((float) a * max);
+                 row[2] = (component_type) ((float) a * max);
+               }
+           }
+
+         /* Multiply alpha and put back in frame */
+         if (components == 4)
+           {
+             row[3] = MIN ((component_type) (a * max), row[3]);
+           }
+         else if (use_yuv)
+           {
+             row[0] = (component_type) ((float) a * row[0]);
+             row[1] =
+               (component_type) ((float) a * (row[1] - (max / 2 + 1)) +
+                                 max / 2 + 1);
+             row[2] =
+               (component_type) ((float) a * (row[2] - (max / 2 + 1)) +
+                                 max / 2 + 1);
+           }
+         else
+           {
+             row[0] = (component_type) ((float) a * row[0]);
+             row[1] = (component_type) ((float) a * row[1]);
+             row[2] = (component_type) ((float) a * row[2]);
+           }
+
+         row += components;
+       }
+    }
+}
+
+
+
+
+void ChromaKeyUnit::process_package(LoadPackage *package)
+{
+       ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
+
+
+       switch(plugin->input->get_color_model())
+       {
+               case BC_RGB_FLOAT:
+                       process_chromakey<float> (3, 1.0, 0, pkg);
+                       break;
+               case BC_RGBA_FLOAT:
+                       process_chromakey<float> ( 4, 1.0, 0, pkg);
+                       break;
+               case BC_RGB888:
+                       process_chromakey<unsigned char> ( 3, 0xff, 0, pkg);
+                       break;
+               case BC_RGBA8888:
+                       process_chromakey<unsigned char> ( 4, 0xff, 0, pkg);
+                       break;
+               case BC_YUV888:
+                       process_chromakey<unsigned char> ( 3, 0xff, 1, pkg);
+                       break;
+               case BC_YUVA8888:
+                       process_chromakey<unsigned char> ( 4, 0xff, 1, pkg);
+                       break;
+               case BC_YUV161616:
+                       process_chromakey<uint16_t> (3, 0xffff, 1, pkg);
+                       break;
+               case BC_YUVA16161616:
+                       process_chromakey<uint16_t> (4, 0xffff, 1, pkg);
+                       break;
+       }
+
+}
+
+
+
+
+
+REGISTER_PLUGIN(ChromaKeyHSV)
+
+
+
+ChromaKeyHSV::ChromaKeyHSV(PluginServer *server)
+ : PluginVClient(server)
+{
+       
+       engine = 0;
+}
+
+ChromaKeyHSV::~ChromaKeyHSV()
+{
+       
+       if(engine) delete engine;
+}
+
+
+int ChromaKeyHSV::process_buffer(VFrame *frame,
+               int64_t start_position,
+               double frame_rate)
+{
+       load_configuration();
+       this->input = frame;
+       this->output = frame;
+
+
+       read_frame(frame, 
+               0, 
+               start_position, 
+               frame_rate,
+               get_use_opengl());
+       if(get_use_opengl()) return run_opengl();
+
+
+       if(!engine) engine = new ChromaKeyServer(this);
+       engine->process_packages();
+
+       return 0;
+}
+
+const char* ChromaKeyHSV::plugin_title() { return _("Chroma key (HSV)"); }
+int ChromaKeyHSV::is_realtime() { return 1; }
+
+
+LOAD_CONFIGURATION_MACRO(ChromaKeyHSV, ChromaKeyConfig)
+
+
+void ChromaKeyHSV::save_data(KeyFrame * keyframe)
+{
+       FileXML output;
+       output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+       output.tag.set_title("CHROMAKEY_HSV");
+       output.tag.set_property("RED", config.red);
+       output.tag.set_property("GREEN", config.green);
+       output.tag.set_property("BLUE", config.blue);
+       output.tag.set_property("MIN_BRIGHTNESS", config.min_brightness);
+       output.tag.set_property("MAX_BRIGHTNESS", config.max_brightness);
+       output.tag.set_property("SATURATION", config.saturation);
+       output.tag.set_property("MIN_SATURATION", config.min_saturation);
+       output.tag.set_property("TOLERANCE", config.tolerance);
+       output.tag.set_property("IN_SLOPE", config.in_slope);
+       output.tag.set_property("OUT_SLOPE", config.out_slope);
+       output.tag.set_property("ALPHA_OFFSET", config.alpha_offset);
+       output.tag.set_property("SPILL_THRESHOLD", config.spill_threshold);
+       output.tag.set_property("SPILL_AMOUNT", config.spill_amount);
+       output.tag.set_property("SHOW_MASK", config.show_mask);
+       output.append_tag();
+       output.tag.set_title("/CHROMAKEY_HSV");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void ChromaKeyHSV::read_data(KeyFrame * keyframe)
+{
+       FileXML input;
+
+       input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+
+       while( !input.read_tag() ) {
+               if( input.tag.title_is("CHROMAKEY_HSV") ) {
+                       config.red = input.tag.get_property("RED", config.red);
+                       config.green = input.tag.get_property("GREEN", config.green);
+                       config.blue = input.tag.get_property("BLUE", config.blue);
+                       config.min_brightness =
+                               input.tag.get_property("MIN_BRIGHTNESS", config.min_brightness);
+                       config.max_brightness =
+                               input.tag.get_property("MAX_BRIGHTNESS", config.max_brightness);
+                       config.saturation =
+                               input.tag.get_property("SATURATION", config.saturation);
+                       config.min_saturation =
+                               input.tag.get_property("MIN_SATURATION", config.min_saturation);
+                       config.tolerance =
+                               input.tag.get_property("TOLERANCE", config.tolerance);
+                       config.in_slope =
+                               input.tag.get_property("IN_SLOPE", config.in_slope);
+                       config.out_slope =
+                               input.tag.get_property("OUT_SLOPE", config.out_slope);
+                       config.alpha_offset =
+                               input.tag.get_property("ALPHA_OFFSET", config.alpha_offset);
+                       config.spill_threshold =
+                               input.tag.get_property("SPILL_THRESHOLD",
+                                                       config.spill_threshold);
+                       config.spill_amount =
+                               input.tag.get_property("SPILL_AMOUNT", config.spill_amount);
+                       config.show_mask =
+                               input.tag.get_property("SHOW_MASK", config.show_mask);
+               }
+       }
+}
+
+
+NEW_WINDOW_MACRO(ChromaKeyHSV, ChromaKeyWindow)
+
+void ChromaKeyHSV::update_gui()
+{
+       if( thread ) {
+               load_configuration();
+               ChromaKeyWindow *window = (ChromaKeyWindow*)thread->window;
+               window->lock_window();
+               window->min_brightness->update(config.min_brightness);
+               window->max_brightness->update(config.max_brightness);
+               window->saturation->update(config.saturation);
+               window->min_saturation->update(config.min_saturation);
+               window->tolerance->update(config.tolerance);
+               window->in_slope->update(config.in_slope);
+               window->out_slope->update(config.out_slope);
+               window->alpha_offset->update(config.alpha_offset);
+               window->spill_threshold->update(config.spill_threshold);
+               window->spill_amount->update(config.spill_amount);
+               window->show_mask->update(config.show_mask);
+               window->update_sample();
+               window->unlock_window();
+       }
+}
+
+
+
+
+int ChromaKeyHSV::handle_opengl()
+{
+#ifdef HAVE_GL
+// For macro
+       ChromaKeyHSV *plugin = this;
+       OUTER_VARIABLES
+
+       static const char *yuv_shader = 
+               "const vec3 black = vec3(0.0, 0.5, 0.5);\n"
+               "\n"
+               "vec4 yuv_to_rgb(vec4 color)\n"
+               "{\n"
+                       YUV_TO_RGB_FRAG("color")
+               "       return color;\n"
+               "}\n"
+               "\n"
+               "vec4 rgb_to_yuv(vec4 color)\n"
+               "{\n"
+                       RGB_TO_YUV_FRAG("color")
+               "       return color;\n"
+               "}\n";
+
+       static const char *rgb_shader = 
+               "const vec3 black = vec3(0.0, 0.0, 0.0);\n"
+               "\n"
+               "vec4 yuv_to_rgb(vec4 color)\n"
+               "{\n"
+               "       return color;\n"
+               "}\n"
+               "vec4 rgb_to_yuv(vec4 color)\n"
+               "{\n"
+               "       return color;\n"
+               "}\n";
+
+       static const char *hsv_shader = 
+               "vec4 rgb_to_hsv(vec4 color)\n"
+               "{\n"
+                       RGB_TO_HSV_FRAG("color")
+               "       return color;\n"
+               "}\n"
+               "\n"
+               "vec4 hsv_to_rgb(vec4 color)\n"
+               "{\n"
+                       HSV_TO_RGB_FRAG("color")
+               "       return color;\n"
+               "}\n"
+               "\n";
+
+       static const char *show_rgbmask_shader = 
+               "vec4 show_mask(vec4 color, vec4 color2)\n"
+               "{\n"
+               "       return vec4(1.0, 1.0, 1.0, min(color.a, color2.a));"
+               "}\n";
+
+       static const char *show_yuvmask_shader = 
+               "vec4 show_mask(vec4 color, vec4 color2)\n"
+               "{\n"
+               "       return vec4(1.0, 0.5, 0.5, min(color.a, color2.a));"
+               "}\n";
+
+       static const char *nomask_shader = 
+               "vec4 show_mask(vec4 color, vec4 color2)\n"
+               "{\n"
+               "       return vec4(color.rgb, min(color.a, color2.a));"
+               "}\n";
+
+       extern unsigned char _binary_chromakey_sl_start[];
+       static const char *shader = (char*)_binary_chromakey_sl_start;
+
+       get_output()->to_texture();
+       get_output()->enable_opengl();
+       get_output()->init_screen();
+
+       const char* shader_stack[] = { 0, 0, 0, 0, 0 };
+
+       switch(get_output()->get_color_model())
+       {
+               case BC_YUV888:
+               case BC_YUVA8888:
+                       shader_stack[0] = yuv_shader;
+                       shader_stack[1] = hsv_shader;
+                       if(config.show_mask) 
+                               shader_stack[2] = show_yuvmask_shader;
+                       else
+                               shader_stack[2] = nomask_shader;
+                       shader_stack[3] = shader;
+                       break;
+
+               default:
+                       shader_stack[0] = rgb_shader;
+                       shader_stack[1] = hsv_shader;
+                       if(config.show_mask) 
+                               shader_stack[2] = show_rgbmask_shader;
+                       else
+                               shader_stack[2] = nomask_shader;
+                       shader_stack[3] = shader;
+                       break;
+       }
+
+
+       unsigned int frag = VFrame::make_shader(0, 
+               shader_stack[0], 
+               shader_stack[1], 
+               shader_stack[2], 
+               shader_stack[3], 
+               0);
+
+       if(frag)
+       {
+               glUseProgram(frag);
+               glUniform1i(glGetUniformLocation(frag, "tex"), 0);
+               glUniform1f(glGetUniformLocation(frag, "red"), red);
+               glUniform1f(glGetUniformLocation(frag, "green"), green);
+               glUniform1f(glGetUniformLocation(frag, "blue"), blue);
+               glUniform1f(glGetUniformLocation(frag, "in_slope"), in_slope);
+               glUniform1f(glGetUniformLocation(frag, "out_slope"), out_slope);
+               glUniform1f(glGetUniformLocation(frag, "tolerance"), tolerance);
+               glUniform1f(glGetUniformLocation(frag, "tolerance_in"), tolerance_in);
+               glUniform1f(glGetUniformLocation(frag, "tolerance_out"), tolerance_out);
+               glUniform1f(glGetUniformLocation(frag, "sat"), sat);
+               glUniform1f(glGetUniformLocation(frag, "min_s"), min_s);
+               glUniform1f(glGetUniformLocation(frag, "min_s_in"), min_s_in);
+               glUniform1f(glGetUniformLocation(frag, "min_s_out"), min_s_out);
+               glUniform1f(glGetUniformLocation(frag, "min_v"), min_v);
+               glUniform1f(glGetUniformLocation(frag, "min_v_in"), min_v_in);
+               glUniform1f(glGetUniformLocation(frag, "min_v_out"), min_v_out);
+               glUniform1f(glGetUniformLocation(frag, "max_v"), max_v);
+               glUniform1f(glGetUniformLocation(frag, "max_v_in"), max_v_in);
+               glUniform1f(glGetUniformLocation(frag, "max_v_out"), max_v_out);
+               glUniform1f(glGetUniformLocation(frag, "spill_threshold"), spill_threshold);
+               glUniform1f(glGetUniformLocation(frag, "spill_amount"), spill_amount);
+               glUniform1f(glGetUniformLocation(frag, "alpha_offset"), alpha_offset);
+               glUniform1f(glGetUniformLocation(frag, "hue_key"), hue_key);
+               glUniform1f(glGetUniformLocation(frag, "saturation_key"), saturation_key);
+               glUniform1f(glGetUniformLocation(frag, "value_key"), value_key);
+       }
+
+
+       get_output()->bind_texture(0);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+       if(BC_CModels::components(get_output()->get_color_model()) == 3)
+       {
+               glEnable(GL_BLEND);
+               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+               get_output()->clear_pbuffer();
+       }
+       get_output()->draw_texture();
+
+       glUseProgram(0);
+       get_output()->set_opengl_state(VFrame::SCREEN);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glDisable(GL_BLEND);
+
+
+#endif
+       return 0;
+}
+