1 #include "bcdisplayinfo.h"
10 #include "loadbalance.h"
11 #include "picon_png.h"
12 #include "playback3d.h"
13 #include "plugincolors.h"
14 #include "pluginvclient.h"
24 ChromaKeyConfig::ChromaKeyConfig()
35 void ChromaKeyConfig::copy_from(ChromaKeyConfig &src)
40 threshold = src.threshold;
41 use_value = src.use_value;
45 int ChromaKeyConfig::equivalent(ChromaKeyConfig &src)
47 return (EQUIV(red, src.red) &&
48 EQUIV(green, src.green) &&
49 EQUIV(blue, src.blue) &&
50 EQUIV(threshold, src.threshold) &&
51 EQUIV(slope, src.slope) &&
52 use_value == src.use_value);
55 void ChromaKeyConfig::interpolate(ChromaKeyConfig &prev,
56 ChromaKeyConfig &next,
59 int64_t current_frame)
61 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
62 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
64 this->red = prev.red * prev_scale + next.red * next_scale;
65 this->green = prev.green * prev_scale + next.green * next_scale;
66 this->blue = prev.blue * prev_scale + next.blue * next_scale;
67 this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
68 this->slope = prev.slope * prev_scale + next.slope * next_scale;
69 this->use_value = prev.use_value;
72 int ChromaKeyConfig::get_color()
74 int red = (int)(CLIP(this->red, 0, 1) * 0xff);
75 int green = (int)(CLIP(this->green, 0, 1) * 0xff);
76 int blue = (int)(CLIP(this->blue, 0, 1) * 0xff);
77 return (red << 16) | (green << 8) | blue;
86 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin, int x, int y)
87 : BC_Window(plugin->gui_string,
98 this->plugin = plugin;
102 ChromaKeyWindow::~ChromaKeyWindow()
107 void ChromaKeyWindow::create_objects()
109 int x = 10, y = 10, x1 = 100;
112 add_subwindow(title = new BC_Title(x, y, _("Color:")));
113 x += title->get_w() + 10;
114 add_subwindow(color = new ChromaKeyColor(plugin, this, x, y));
115 x += color->get_w() + 10;
116 add_subwindow(sample = new BC_SubWindow(x, y, 100, 50));
117 y += sample->get_h() + 10;
120 add_subwindow(new BC_Title(x, y, _("Slope:")));
121 add_subwindow(slope = new ChromaKeySlope(plugin, x1, y));
124 add_subwindow(new BC_Title(x, y, _("Threshold:")));
125 add_subwindow(threshold = new ChromaKeyThreshold(plugin, x1, y));
129 add_subwindow(use_value = new ChromaKeyUseValue(plugin, x1, y));
132 add_subwindow(use_colorpicker = new ChromaKeyUseColorPicker(plugin, this, x1, y));
134 color_thread = new ChromaKeyColorThread(plugin, this);
141 void ChromaKeyWindow::update_sample()
143 sample->set_color(plugin->config.get_color());
148 sample->set_color(BLACK);
149 sample->draw_rectangle(0,
158 WINDOW_CLOSE_EVENT(ChromaKeyWindow)
165 ChromaKeyColor::ChromaKeyColor(ChromaKey *plugin,
166 ChromaKeyWindow *gui,
169 : BC_GenericButton(x,
173 this->plugin = plugin;
176 int ChromaKeyColor::handle_event()
178 gui->color_thread->start_window(
179 plugin->config.get_color(),
187 ChromaKeyThreshold::ChromaKeyThreshold(ChromaKey *plugin, int x, int y)
195 plugin->config.threshold)
197 this->plugin = plugin;
200 int ChromaKeyThreshold::handle_event()
202 plugin->config.threshold = get_value();
203 plugin->send_configure_change();
208 ChromaKeySlope::ChromaKeySlope(ChromaKey *plugin, int x, int y)
216 plugin->config.slope)
218 this->plugin = plugin;
222 int ChromaKeySlope::handle_event()
224 plugin->config.slope = get_value();
225 plugin->send_configure_change();
230 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
231 : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
233 this->plugin = plugin;
235 int ChromaKeyUseValue::handle_event()
237 plugin->config.use_value = get_value();
238 plugin->send_configure_change();
243 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker(ChromaKey *plugin,
244 ChromaKeyWindow *gui,
247 : BC_GenericButton(x, y, _("Use color picker"))
249 this->plugin = plugin;
253 int ChromaKeyUseColorPicker::handle_event()
255 plugin->config.red = plugin->get_red();
256 plugin->config.green = plugin->get_green();
257 plugin->config.blue = plugin->get_blue();
258 gui->update_sample();
259 plugin->send_configure_change();
266 ChromaKeyColorThread::ChromaKeyColorThread(ChromaKey *plugin, ChromaKeyWindow *gui)
267 : ColorThread(1, _("Inner color"))
269 this->plugin = plugin;
273 int ChromaKeyColorThread::handle_new_color(int output, int alpha)
275 plugin->config.red = (float)(output & 0xff0000) / 0xff0000;
276 plugin->config.green = (float)(output & 0xff00) / 0xff00;
277 plugin->config.blue = (float)(output & 0xff) / 0xff;
278 gui->update_sample();
279 plugin->send_configure_change();
290 PLUGIN_THREAD_OBJECT(ChromaKey, ChromaKeyThread, ChromaKeyWindow)
293 ChromaKeyServer::ChromaKeyServer(ChromaKey *plugin)
294 : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
296 this->plugin = plugin;
298 void ChromaKeyServer::init_packages()
300 for(int i = 0; i < get_total_packages(); i++)
302 ChromaKeyPackage *pkg = (ChromaKeyPackage*)get_package(i);
303 pkg->y1 = plugin->input->get_h() * i / get_total_packages();
304 pkg->y2 = plugin->input->get_h() * (i + 1) / get_total_packages();
308 LoadClient* ChromaKeyServer::new_client()
310 return new ChromaKeyUnit(plugin, this);
312 LoadPackage* ChromaKeyServer::new_package()
314 return new ChromaKeyPackage;
319 ChromaKeyPackage::ChromaKeyPackage()
324 ChromaKeyUnit::ChromaKeyUnit(ChromaKey *plugin, ChromaKeyServer *server)
327 this->plugin = plugin;
331 void ChromaKeyUnit::process_package(LoadPackage *package)
333 ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
335 int w = plugin->input->get_w();
338 HSV::rgb_to_hsv(plugin->config.red,
339 plugin->config.green,
344 float min_hue = h - plugin->config.threshold * 360 / 100;
345 float max_hue = h + plugin->config.threshold * 360 / 100;
348 #define RGB_TO_VALUE(r, g, b) \
349 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
351 #define SQR(x) ((x) * (x))
353 #define OUTER_VARIABLES(plugin) \
355 float value = RGB_TO_VALUE(plugin->config.red, \
356 plugin->config.green, \
357 plugin->config.blue); \
358 float threshold = plugin->config.threshold / 100; \
359 float min_v = value - threshold; \
360 float max_v = value + threshold; \
361 float r_key = plugin->config.red; \
362 float g_key = plugin->config.green; \
363 float b_key = plugin->config.blue; \
364 int y_key, u_key, v_key; \
365 yuv.rgb_to_yuv_8((int)(r_key * 0xff), (int)(g_key * 0xff), (int)(b_key * 0xff), y_key, u_key, v_key); \
366 float run = plugin->config.slope / 100; \
367 float threshold_run = threshold + run;
369 OUTER_VARIABLES(plugin)
373 #define CHROMAKEY(type, components, max, use_yuv) \
375 for(int i = pkg->y1; i < pkg->y2; i++) \
377 type *row = (type*)plugin->input->get_rows()[i]; \
379 for(int j = 0; j < w; j++) \
383 /* Test for value in range */ \
384 if(plugin->config.use_value) \
386 float current_value; \
389 float r = (float)row[0] / max; \
394 float r = (float)row[0] / max; \
395 float g = (float)row[1] / max; \
396 float b = (float)row[2] / max; \
397 current_value = RGB_TO_VALUE(r, g, b); \
400 /* Full transparency if in range */ \
401 if(current_value >= min_v && current_value < max_v) \
406 /* Phased out if below or above range */ \
407 if(current_value < min_v) \
409 if(min_v - current_value < run) \
410 a = (min_v - current_value) / run; \
413 if(current_value - max_v < run) \
414 a = (current_value - max_v) / run; \
417 /* Use color cube */ \
425 difference = sqrt(SQR(y - y_key) + \
427 SQR(v - v_key)) / max; \
431 float r = (float)row[0] / max; \
432 float g = (float)row[1] / max; \
433 float b = (float)row[2] / max; \
434 difference = sqrt(SQR(r - r_key) + \
438 if(difference < threshold) \
443 if(difference < threshold_run) \
445 a = (difference - threshold) / run; \
450 /* Multiply alpha and put back in frame */ \
451 if(components == 4) \
453 row[3] = MIN((type)(a * max), row[3]); \
458 row[0] = (type)(a * row[0]); \
459 row[1] = (type)(a * (row[1] - (max / 2 + 1)) + max / 2 + 1); \
460 row[2] = (type)(a * (row[2] - (max / 2 + 1)) + max / 2 + 1); \
464 row[0] = (type)(a * row[0]); \
465 row[1] = (type)(a * row[1]); \
466 row[2] = (type)(a * row[2]); \
477 switch(plugin->input->get_color_model())
480 CHROMAKEY(float, 3, 1.0, 0);
483 CHROMAKEY(float, 4, 1.0, 0);
486 CHROMAKEY(unsigned char, 3, 0xff, 0);
489 CHROMAKEY(unsigned char, 4, 0xff, 0);
492 CHROMAKEY(unsigned char, 3, 0xff, 1);
495 CHROMAKEY(unsigned char, 4, 0xff, 1);
498 CHROMAKEY(uint16_t, 3, 0xffff, 1);
500 case BC_YUVA16161616:
501 CHROMAKEY(uint16_t, 4, 0xffff, 1);
511 REGISTER_PLUGIN(ChromaKey)
515 ChromaKey::ChromaKey(PluginServer *server)
516 : PluginVClient(server)
518 PLUGIN_CONSTRUCTOR_MACRO
522 ChromaKey::~ChromaKey()
524 PLUGIN_DESTRUCTOR_MACRO
529 int ChromaKey::process_buffer(VFrame *frame,
530 int64_t start_position,
535 load_configuration();
537 this->output = frame;
545 if(EQUIV(config.threshold, 0))
551 if(get_use_opengl()) return run_opengl();
553 if(!engine) engine = new ChromaKeyServer(this);
554 engine->process_packages();
561 char* ChromaKey::plugin_title() { return N_("Chroma key"); }
562 int ChromaKey::is_realtime() { return 1; }
564 NEW_PICON_MACRO(ChromaKey)
566 LOAD_CONFIGURATION_MACRO(ChromaKey, ChromaKeyConfig)
568 int ChromaKey::load_defaults()
571 char directory[BCTEXTLEN];
572 // set the default directory
573 sprintf(directory, "%schromakey.rc", BCASTDIR);
576 defaults = new BC_Hash(directory);
579 config.red = defaults->get("RED", config.red);
580 config.green = defaults->get("GREEN", config.green);
581 config.blue = defaults->get("BLUE", config.blue);
582 config.threshold = defaults->get("THRESHOLD", config.threshold);
583 config.slope = defaults->get("SLOPE", config.slope);
584 config.use_value = defaults->get("USE_VALUE", config.use_value);
589 int ChromaKey::save_defaults()
592 defaults->update("RED", config.red);
593 defaults->update("GREEN", config.green);
594 defaults->update("BLUE", config.blue);
595 defaults->update("THRESHOLD", config.threshold);
596 defaults->update("SLOPE", config.slope);
597 defaults->update("USE_VALUE", config.use_value);
603 void ChromaKey::save_data(KeyFrame *keyframe)
606 output.set_shared_string(keyframe->data, MESSAGESIZE);
607 output.tag.set_title("CHROMAKEY");
608 output.tag.set_property("RED", config.red);
609 output.tag.set_property("GREEN", config.green);
610 output.tag.set_property("BLUE", config.blue);
611 output.tag.set_property("THRESHOLD", config.threshold);
612 output.tag.set_property("SLOPE", config.slope);
613 output.tag.set_property("USE_VALUE", config.use_value);
615 output.terminate_string();
618 void ChromaKey::read_data(KeyFrame *keyframe)
622 input.set_shared_string(keyframe->data, strlen(keyframe->data));
624 while(!input.read_tag())
626 if(input.tag.title_is("CHROMAKEY"))
628 config.red = input.tag.get_property("RED", config.red);
629 config.green = input.tag.get_property("GREEN", config.green);
630 config.blue = input.tag.get_property("BLUE", config.blue);
631 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
632 config.slope = input.tag.get_property("SLOPE", config.slope);
633 config.use_value = input.tag.get_property("USE_VALUE", config.use_value);
639 SHOW_GUI_MACRO(ChromaKey, ChromaKeyThread)
641 SET_STRING_MACRO(ChromaKey)
643 RAISE_WINDOW_MACRO(ChromaKey)
645 void ChromaKey::update_gui()
649 load_configuration();
650 thread->window->lock_window();
651 thread->window->threshold->update(config.threshold);
652 thread->window->slope->update(config.slope);
653 thread->window->use_value->update(config.use_value);
654 thread->window->update_sample();
656 thread->window->unlock_window();
660 int ChromaKey::handle_opengl()
663 OUTER_VARIABLES(this)
667 static char *uniform_frag =
668 "uniform sampler2D tex;\n"
669 "uniform float min_v;\n"
670 "uniform float max_v;\n"
671 "uniform float run;\n"
672 "uniform float threshold;\n"
673 "uniform float threshold_run;\n"
674 "uniform vec3 key;\n";
676 static char *get_yuvvalue_frag =
677 "float get_value(vec4 color)\n"
679 " return abs(color.r);\n"
682 static char *get_rgbvalue_frag =
683 "float get_value(vec4 color)\n"
685 " return dot(color.rgb, vec3(0.29900, 0.58700, 0.11400));\n"
688 static char *value_frag =
691 " vec4 color = texture2D(tex, gl_TexCoord[0].st);\n"
692 " float value = get_value(color);\n"
693 " float alpha = 1.0;\n"
695 " if(value >= min_v && value < max_v)\n"
698 " if(value < min_v)\n"
700 " if(min_v - value < run)\n"
701 " alpha = (min_v - value) / run;\n"
704 " if(value - max_v < run)\n"
705 " alpha = (value - max_v) / run;\n"
707 " gl_FragColor = vec4(color.rgb, alpha);\n"
710 static char *cube_frag =
713 " vec4 color = texture2D(tex, gl_TexCoord[0].st);\n"
714 " float difference = length(color.rgb - key);\n"
715 " float alpha = 1.0;\n"
716 " if(difference < threshold)\n"
719 " if(difference < threshold_run)\n"
720 " alpha = (difference - threshold) / run;\n"
721 " gl_FragColor = vec4(color.rgb, min(color.a, alpha));\n"
724 get_output()->to_texture();
725 get_output()->enable_opengl();
726 get_output()->init_screen();
727 char *shader_stack[] = { 0, 0, 0, 0, 0 };
728 int current_shader = 0;
730 shader_stack[current_shader++] = uniform_frag;
731 switch(get_output()->get_color_model())
737 shader_stack[current_shader++] = get_yuvvalue_frag;
738 shader_stack[current_shader++] = value_frag;
742 shader_stack[current_shader++] = cube_frag;
749 shader_stack[current_shader++] = get_rgbvalue_frag;
750 shader_stack[current_shader++] = value_frag;
754 shader_stack[current_shader++] = cube_frag;
760 unsigned int frag = VFrame::make_shader(0,
766 get_output()->bind_texture(0);
771 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
772 glUniform1f(glGetUniformLocation(frag, "min_v"), min_v);
773 glUniform1f(glGetUniformLocation(frag, "max_v"), max_v);
774 glUniform1f(glGetUniformLocation(frag, "run"), run);
775 glUniform1f(glGetUniformLocation(frag, "threshold"), threshold);
776 glUniform1f(glGetUniformLocation(frag, "threshold_run"), threshold_run);
777 if(get_output()->get_color_model() != BC_YUV888 &&
778 get_output()->get_color_model() != BC_YUVA8888)
779 glUniform3f(glGetUniformLocation(frag, "key"),
780 r_key, g_key, b_key);
782 glUniform3f(glGetUniformLocation(frag, "key"),
783 (float)y_key / 0xff, (float)u_key / 0xff, (float)v_key / 0xff);
788 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
789 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
791 if(cmodel_components(get_output()->get_color_model()) == 3)
794 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
795 get_output()->clear_pbuffer();
799 get_output()->draw_texture();
802 get_output()->set_opengl_state(VFrame::SCREEN);
803 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
804 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);