X-Git-Url: https://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fchromakeyhsv%2Fchromakey.C;fp=cinelerra-5.1%2Fplugins%2Fchromakeyhsv%2Fchromakey.C;h=3541143cd180204f883e03aac90a68027c3f5289;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/plugins/chromakeyhsv/chromakey.C b/cinelerra-5.1/plugins/chromakeyhsv/chromakey.C new file mode 100644 index 00000000..3541143c --- /dev/null +++ b/cinelerra-5.1/plugins/chromakeyhsv/chromakey.C @@ -0,0 +1,1181 @@ + +/* + * CINELERRA + * Copyright (C) 2012 Adam Williams + * + * 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 +#include +#include + + + +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 +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 (3, 1.0, 0, pkg); + break; + case BC_RGBA_FLOAT: + process_chromakey ( 4, 1.0, 0, pkg); + break; + case BC_RGB888: + process_chromakey ( 3, 0xff, 0, pkg); + break; + case BC_RGBA8888: + process_chromakey ( 4, 0xff, 0, pkg); + break; + case BC_YUV888: + process_chromakey ( 3, 0xff, 1, pkg); + break; + case BC_YUVA8888: + process_chromakey ( 4, 0xff, 1, pkg); + break; + case BC_YUV161616: + process_chromakey (3, 0xffff, 1, pkg); + break; + case BC_YUVA16161616: + process_chromakey (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; +} +