4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
24 #include "chromakey.h"
31 #include "loadbalance.h"
32 #include "playback3d.h"
34 #include "pluginvclient.h"
44 ChromaKeyConfig::ChromaKeyConfig()
55 void ChromaKeyConfig::copy_from(ChromaKeyConfig &src)
60 threshold = src.threshold;
61 use_value = src.use_value;
65 int ChromaKeyConfig::equivalent(ChromaKeyConfig &src)
67 return (EQUIV(red, src.red) &&
68 EQUIV(green, src.green) &&
69 EQUIV(blue, src.blue) &&
70 EQUIV(threshold, src.threshold) &&
71 EQUIV(slope, src.slope) &&
72 use_value == src.use_value);
75 void ChromaKeyConfig::interpolate(ChromaKeyConfig &prev,
76 ChromaKeyConfig &next,
79 int64_t current_frame)
81 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
82 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
84 this->red = prev.red * prev_scale + next.red * next_scale;
85 this->green = prev.green * prev_scale + next.green * next_scale;
86 this->blue = prev.blue * prev_scale + next.blue * next_scale;
87 this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
88 this->slope = prev.slope * prev_scale + next.slope * next_scale;
89 this->use_value = prev.use_value;
92 int ChromaKeyConfig::get_color()
94 int red = (int)(CLIP(this->red, 0, 1) * 0xff);
95 int green = (int)(CLIP(this->green, 0, 1) * 0xff);
96 int blue = (int)(CLIP(this->blue, 0, 1) * 0xff);
97 return (red << 16) | (green << 8) | blue;
106 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin)
107 : PluginClientWindow(plugin,
114 this->plugin = plugin;
118 ChromaKeyWindow::~ChromaKeyWindow()
123 void ChromaKeyWindow::create_objects()
125 int x = 10, y = 10, x1 = 100;
128 add_subwindow(title = new BC_Title(x, y, _("Color:")));
129 x += title->get_w() + 10;
130 add_subwindow(color = new ChromaKeyColor(plugin, this, x, y));
131 x += color->get_w() + 10;
132 add_subwindow(sample = new BC_SubWindow(x, y, 100, 50));
133 y += sample->get_h() + 10;
136 add_subwindow(new BC_Title(x, y, _("Slope:")));
137 add_subwindow(slope = new ChromaKeySlope(plugin, x1, y));
140 add_subwindow(new BC_Title(x, y, _("Threshold:")));
141 add_subwindow(threshold = new ChromaKeyThreshold(plugin, x1, y));
145 add_subwindow(use_value = new ChromaKeyUseValue(plugin, x1, y));
148 add_subwindow(use_colorpicker = new ChromaKeyUseColorPicker(plugin, this, x1, y));
150 color_thread = new ChromaKeyColorThread(plugin, this);
157 void ChromaKeyWindow::update_sample()
159 sample->set_color(plugin->config.get_color());
164 sample->set_color(BLACK);
165 sample->draw_rectangle(0,
172 void ChromaKeyWindow::done_event(int result)
174 color_thread->close_window();
185 ChromaKeyColor::ChromaKeyColor(ChromaKey *plugin,
186 ChromaKeyWindow *gui,
189 : BC_GenericButton(x,
193 this->plugin = plugin;
196 int ChromaKeyColor::handle_event()
198 gui->color_thread->start_window(
199 plugin->config.get_color(),
207 ChromaKeyThreshold::ChromaKeyThreshold(ChromaKey *plugin, int x, int y)
215 plugin->config.threshold)
217 this->plugin = plugin;
220 int ChromaKeyThreshold::handle_event()
222 plugin->config.threshold = get_value();
223 plugin->send_configure_change();
228 ChromaKeySlope::ChromaKeySlope(ChromaKey *plugin, int x, int y)
236 plugin->config.slope)
238 this->plugin = plugin;
242 int ChromaKeySlope::handle_event()
244 plugin->config.slope = get_value();
245 plugin->send_configure_change();
250 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
251 : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
253 this->plugin = plugin;
255 int ChromaKeyUseValue::handle_event()
257 plugin->config.use_value = get_value();
258 plugin->send_configure_change();
263 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker(ChromaKey *plugin,
264 ChromaKeyWindow *gui,
267 : BC_GenericButton(x, y, _("Use color picker"))
269 this->plugin = plugin;
273 int ChromaKeyUseColorPicker::handle_event()
275 plugin->config.red = plugin->get_red();
276 plugin->config.green = plugin->get_green();
277 plugin->config.blue = plugin->get_blue();
278 gui->update_sample();
279 plugin->send_configure_change();
286 ChromaKeyColorThread::ChromaKeyColorThread(ChromaKey *plugin, ChromaKeyWindow *gui)
287 : ColorPicker(1, _("Inner color"))
289 this->plugin = plugin;
293 int ChromaKeyColorThread::handle_new_color(int output, int alpha)
295 plugin->config.red = (float)(output & 0xff0000) / 0xff0000;
296 plugin->config.green = (float)(output & 0xff00) / 0xff00;
297 plugin->config.blue = (float)(output & 0xff) / 0xff;
298 gui->update_sample();
299 plugin->send_configure_change();
312 ChromaKeyServer::ChromaKeyServer(ChromaKey *plugin)
313 : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
315 this->plugin = plugin;
317 void ChromaKeyServer::init_packages()
319 for(int i = 0; i < get_total_packages(); i++)
321 ChromaKeyPackage *pkg = (ChromaKeyPackage*)get_package(i);
322 pkg->y1 = plugin->input->get_h() * i / get_total_packages();
323 pkg->y2 = plugin->input->get_h() * (i + 1) / get_total_packages();
327 LoadClient* ChromaKeyServer::new_client()
329 return new ChromaKeyUnit(plugin, this);
331 LoadPackage* ChromaKeyServer::new_package()
333 return new ChromaKeyPackage;
338 ChromaKeyPackage::ChromaKeyPackage()
343 ChromaKeyUnit::ChromaKeyUnit(ChromaKey *plugin, ChromaKeyServer *server)
346 this->plugin = plugin;
350 void ChromaKeyUnit::process_package(LoadPackage *package)
352 ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
354 int w = plugin->input->get_w();
357 HSV::rgb_to_hsv(plugin->config.red,
358 plugin->config.green,
363 //float min_hue = h - plugin->config.threshold * 360 / 100;
364 //float max_hue = h + plugin->config.threshold * 360 / 100;
367 #define RGB_TO_VALUE(r, g, b) \
368 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
371 #define OUTER_VARIABLES(plugin) \
373 float value = RGB_TO_VALUE(plugin->config.red, \
374 plugin->config.green, \
375 plugin->config.blue); \
376 float threshold = plugin->config.threshold / 100; \
377 float min_v = value - threshold; \
378 float max_v = value + threshold; \
379 float r_key = plugin->config.red; \
380 float g_key = plugin->config.green; \
381 float b_key = plugin->config.blue; \
382 int y_key, u_key, v_key; \
383 yuv.rgb_to_yuv_8((int)(r_key * 0xff), (int)(g_key * 0xff), (int)(b_key * 0xff), y_key, u_key, v_key); \
384 float run = plugin->config.slope / 100; \
385 float threshold_run = threshold + run;
387 OUTER_VARIABLES(plugin)
391 #define CHROMAKEY(type, components, max, use_yuv) \
393 for(int i = pkg->y1; i < pkg->y2; i++) \
395 type *row = (type*)plugin->input->get_rows()[i]; \
397 for(int j = 0; j < w; j++) \
401 /* Test for value in range */ \
402 if(plugin->config.use_value) \
404 float current_value; \
407 float r = (float)row[0] / max; \
412 float r = (float)row[0] / max; \
413 float g = (float)row[1] / max; \
414 float b = (float)row[2] / max; \
415 current_value = RGB_TO_VALUE(r, g, b); \
418 /* Full transparency if in range */ \
419 if(current_value >= min_v && current_value < max_v) \
424 /* Phased out if below or above range */ \
425 if(current_value < min_v) \
427 if(min_v - current_value < run) \
428 a = (min_v - current_value) / run; \
431 if(current_value - max_v < run) \
432 a = (current_value - max_v) / run; \
435 /* Use color cube */ \
443 difference = sqrt(SQR(y - y_key) + \
445 SQR(v - v_key)) / max; \
449 float r = (float)row[0] / max; \
450 float g = (float)row[1] / max; \
451 float b = (float)row[2] / max; \
452 difference = sqrt(SQR(r - r_key) + \
456 if(difference < threshold) \
461 if(difference < threshold_run) \
463 a = (difference - threshold) / run; \
468 /* Multiply alpha and put back in frame */ \
469 if(components == 4) \
471 row[3] = MIN((type)(a * max), row[3]); \
476 row[0] = (type)(a * row[0]); \
477 row[1] = (type)(a * (row[1] - (max / 2 + 1)) + max / 2 + 1); \
478 row[2] = (type)(a * (row[2] - (max / 2 + 1)) + max / 2 + 1); \
482 row[0] = (type)(a * row[0]); \
483 row[1] = (type)(a * row[1]); \
484 row[2] = (type)(a * row[2]); \
495 switch(plugin->input->get_color_model())
498 CHROMAKEY(float, 3, 1.0, 0);
501 CHROMAKEY(float, 4, 1.0, 0);
504 CHROMAKEY(unsigned char, 3, 0xff, 0);
507 CHROMAKEY(unsigned char, 4, 0xff, 0);
510 CHROMAKEY(unsigned char, 3, 0xff, 1);
513 CHROMAKEY(unsigned char, 4, 0xff, 1);
516 CHROMAKEY(uint16_t, 3, 0xffff, 1);
518 case BC_YUVA16161616:
519 CHROMAKEY(uint16_t, 4, 0xffff, 1);
529 REGISTER_PLUGIN(ChromaKey)
533 ChromaKey::ChromaKey(PluginServer *server)
534 : PluginVClient(server)
540 ChromaKey::~ChromaKey()
547 int ChromaKey::process_buffer(VFrame *frame,
548 int64_t start_position,
553 load_configuration();
555 this->output = frame;
563 if(EQUIV(config.threshold, 0))
569 if(get_use_opengl()) return run_opengl();
571 if(!engine) engine = new ChromaKeyServer(this);
572 engine->process_packages();
579 const char* ChromaKey::plugin_title() { return _("Chroma key"); }
580 int ChromaKey::is_realtime() { return 1; }
582 NEW_WINDOW_MACRO(ChromaKey, ChromaKeyWindow)
584 LOAD_CONFIGURATION_MACRO(ChromaKey, ChromaKeyConfig)
587 void ChromaKey::save_data(KeyFrame *keyframe)
590 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
591 output.tag.set_title("CHROMAKEY");
592 output.tag.set_property("RED", config.red);
593 output.tag.set_property("GREEN", config.green);
594 output.tag.set_property("BLUE", config.blue);
595 output.tag.set_property("THRESHOLD", config.threshold);
596 output.tag.set_property("SLOPE", config.slope);
597 output.tag.set_property("USE_VALUE", config.use_value);
599 output.tag.set_title("/CHROMAKEY");
601 output.append_newline();
602 output.terminate_string();
605 void ChromaKey::read_data(KeyFrame *keyframe)
609 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
611 while(!input.read_tag())
613 if(input.tag.title_is("CHROMAKEY"))
615 config.red = input.tag.get_property("RED", config.red);
616 config.green = input.tag.get_property("GREEN", config.green);
617 config.blue = input.tag.get_property("BLUE", config.blue);
618 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
619 config.slope = input.tag.get_property("SLOPE", config.slope);
620 config.use_value = input.tag.get_property("USE_VALUE", config.use_value);
627 void ChromaKey::update_gui()
631 load_configuration();
632 thread->window->lock_window();
633 ((ChromaKeyWindow*)thread->window)->threshold->update(config.threshold);
634 ((ChromaKeyWindow*)thread->window)->slope->update(config.slope);
635 ((ChromaKeyWindow*)thread->window)->use_value->update(config.use_value);
636 ((ChromaKeyWindow*)thread->window)->update_sample();
638 thread->window->unlock_window();
642 int ChromaKey::handle_opengl()
645 OUTER_VARIABLES(this)
649 static const char *uniform_frag =
650 "uniform sampler2D tex;\n"
651 "uniform float min_v;\n"
652 "uniform float max_v;\n"
653 "uniform float run;\n"
654 "uniform float threshold;\n"
655 "uniform float threshold_run;\n"
656 "uniform vec3 key;\n";
658 static const char *get_yuvvalue_frag =
659 "float get_value(vec4 color)\n"
661 " return abs(color.r);\n"
664 static const char *get_rgbvalue_frag =
665 "float get_value(vec4 color)\n"
667 " return dot(color.rgb, vec3(0.29900, 0.58700, 0.11400));\n"
670 static const char *value_frag =
673 " vec4 color = texture2D(tex, gl_TexCoord[0].st);\n"
674 " float value = get_value(color);\n"
675 " float alpha = 1.0;\n"
677 " if(value >= min_v && value < max_v)\n"
680 " if(value < min_v)\n"
682 " if(min_v - value < run)\n"
683 " alpha = (min_v - value) / run;\n"
686 " if(value - max_v < run)\n"
687 " alpha = (value - max_v) / run;\n"
689 " gl_FragColor = vec4(color.rgb, alpha);\n"
692 static const char *cube_frag =
695 " vec4 color = texture2D(tex, gl_TexCoord[0].st);\n"
696 " float difference = length(color.rgb - key);\n"
697 " float alpha = 1.0;\n"
698 " if(difference < threshold)\n"
701 " if(difference < threshold_run)\n"
702 " alpha = (difference - threshold) / run;\n"
703 " gl_FragColor = vec4(color.rgb, min(color.a, alpha));\n"
708 get_output()->to_texture();
709 get_output()->enable_opengl();
710 get_output()->init_screen();
711 const char *shader_stack[] = { 0, 0, 0, 0, 0 };
712 int current_shader = 0;
714 shader_stack[current_shader++] = uniform_frag;
715 switch(get_output()->get_color_model())
721 shader_stack[current_shader++] = get_yuvvalue_frag;
722 shader_stack[current_shader++] = value_frag;
726 shader_stack[current_shader++] = cube_frag;
733 shader_stack[current_shader++] = get_rgbvalue_frag;
734 shader_stack[current_shader++] = value_frag;
738 shader_stack[current_shader++] = cube_frag;
744 unsigned int frag = VFrame::make_shader(0,
750 get_output()->bind_texture(0);
755 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
756 glUniform1f(glGetUniformLocation(frag, "min_v"), min_v);
757 glUniform1f(glGetUniformLocation(frag, "max_v"), max_v);
758 glUniform1f(glGetUniformLocation(frag, "run"), run);
759 glUniform1f(glGetUniformLocation(frag, "threshold"), threshold);
760 glUniform1f(glGetUniformLocation(frag, "threshold_run"), threshold_run);
761 if(get_output()->get_color_model() != BC_YUV888 &&
762 get_output()->get_color_model() != BC_YUVA8888)
763 glUniform3f(glGetUniformLocation(frag, "key"),
764 r_key, g_key, b_key);
766 glUniform3f(glGetUniformLocation(frag, "key"),
767 (float)y_key / 0xff, (float)u_key / 0xff, (float)v_key / 0xff);
772 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
773 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
775 if(BC_CModels::components(get_output()->get_color_model()) == 3)
778 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
779 get_output()->clear_pbuffer();
783 get_output()->draw_texture();
786 get_output()->set_opengl_state(VFrame::SCREEN);
787 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
788 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);