1 #include "bcdisplayinfo.h"
9 #include "loadbalance.h"
10 #include "picon_png.h"
11 #include "plugincolors.h"
12 #include "pluginvclient.h"
22 ChromaKeyConfig::ChromaKeyConfig()
33 void ChromaKeyConfig::copy_from(ChromaKeyConfig &src)
38 threshold = src.threshold;
39 use_value = src.use_value;
43 int ChromaKeyConfig::equivalent(ChromaKeyConfig &src)
45 return (EQUIV(red, src.red) &&
46 EQUIV(green, src.green) &&
47 EQUIV(blue, src.blue) &&
48 EQUIV(threshold, src.threshold) &&
49 EQUIV(slope, src.slope) &&
50 use_value == src.use_value);
53 void ChromaKeyConfig::interpolate(ChromaKeyConfig &prev,
54 ChromaKeyConfig &next,
57 int64_t current_frame)
59 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
60 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
62 this->red = prev.red * prev_scale + next.red * next_scale;
63 this->green = prev.green * prev_scale + next.green * next_scale;
64 this->blue = prev.blue * prev_scale + next.blue * next_scale;
65 this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
66 this->slope = prev.slope * prev_scale + next.slope * next_scale;
67 this->use_value = prev.use_value;
70 int ChromaKeyConfig::get_color()
72 int red = (int)(CLIP(this->red, 0, 1) * 0xff);
73 int green = (int)(CLIP(this->green, 0, 1) * 0xff);
74 int blue = (int)(CLIP(this->blue, 0, 1) * 0xff);
75 return (red << 16) | (green << 8) | blue;
84 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin, int x, int y)
85 : BC_Window(plugin->gui_string,
96 this->plugin = plugin;
100 ChromaKeyWindow::~ChromaKeyWindow()
105 void ChromaKeyWindow::create_objects()
107 int x = 10, y = 10, x1 = 100;
110 add_subwindow(title = new BC_Title(x, y, _("Color:")));
111 x += title->get_w() + 10;
112 add_subwindow(color = new ChromaKeyColor(plugin, this, x, y));
113 x += color->get_w() + 10;
114 add_subwindow(sample = new BC_SubWindow(x, y, 100, 50));
115 y += sample->get_h() + 10;
118 add_subwindow(new BC_Title(x, y, _("Slope:")));
119 add_subwindow(slope = new ChromaKeySlope(plugin, x1, y));
122 add_subwindow(new BC_Title(x, y, _("Threshold:")));
123 add_subwindow(threshold = new ChromaKeyThreshold(plugin, x1, y));
127 add_subwindow(use_value = new ChromaKeyUseValue(plugin, x1, y));
130 add_subwindow(use_colorpicker = new ChromaKeyUseColorPicker(plugin, this, x1, y));
132 color_thread = new ChromaKeyColorThread(plugin, this);
139 void ChromaKeyWindow::update_sample()
141 sample->set_color(plugin->config.get_color());
146 sample->set_color(BLACK);
147 sample->draw_rectangle(0,
156 WINDOW_CLOSE_EVENT(ChromaKeyWindow)
163 ChromaKeyColor::ChromaKeyColor(ChromaKey *plugin,
164 ChromaKeyWindow *gui,
167 : BC_GenericButton(x,
171 this->plugin = plugin;
174 int ChromaKeyColor::handle_event()
176 gui->color_thread->start_window(
177 plugin->config.get_color(),
185 ChromaKeyThreshold::ChromaKeyThreshold(ChromaKey *plugin, int x, int y)
193 plugin->config.threshold)
195 this->plugin = plugin;
198 int ChromaKeyThreshold::handle_event()
200 plugin->config.threshold = get_value();
201 plugin->send_configure_change();
206 ChromaKeySlope::ChromaKeySlope(ChromaKey *plugin, int x, int y)
214 plugin->config.slope)
216 this->plugin = plugin;
220 int ChromaKeySlope::handle_event()
222 plugin->config.slope = get_value();
223 plugin->send_configure_change();
228 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
229 : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
231 this->plugin = plugin;
233 int ChromaKeyUseValue::handle_event()
235 plugin->config.use_value = get_value();
236 plugin->send_configure_change();
241 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker(ChromaKey *plugin,
242 ChromaKeyWindow *gui,
245 : BC_GenericButton(x, y, _("Use color picker"))
247 this->plugin = plugin;
251 int ChromaKeyUseColorPicker::handle_event()
253 plugin->config.red = plugin->get_red();
254 plugin->config.green = plugin->get_green();
255 plugin->config.blue = plugin->get_blue();
256 gui->update_sample();
257 plugin->send_configure_change();
264 ChromaKeyColorThread::ChromaKeyColorThread(ChromaKey *plugin, ChromaKeyWindow *gui)
265 : ColorThread(1, _("Inner color"))
267 this->plugin = plugin;
271 int ChromaKeyColorThread::handle_new_color(int output, int alpha)
273 plugin->config.red = (float)(output & 0xff0000) / 0xff0000;
274 plugin->config.green = (float)(output & 0xff00) / 0xff00;
275 plugin->config.blue = (float)(output & 0xff) / 0xff;
276 gui->update_sample();
277 plugin->send_configure_change();
288 PLUGIN_THREAD_OBJECT(ChromaKey, ChromaKeyThread, ChromaKeyWindow)
291 ChromaKeyServer::ChromaKeyServer(ChromaKey *plugin)
292 : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
294 this->plugin = plugin;
296 void ChromaKeyServer::init_packages()
298 for(int i = 0; i < get_total_packages(); i++)
300 ChromaKeyPackage *pkg = (ChromaKeyPackage*)get_package(i);
301 pkg->y1 = plugin->input->get_h() * i / get_total_packages();
302 pkg->y2 = plugin->input->get_h() * (i + 1) / get_total_packages();
306 LoadClient* ChromaKeyServer::new_client()
308 return new ChromaKeyUnit(plugin, this);
310 LoadPackage* ChromaKeyServer::new_package()
312 return new ChromaKeyPackage;
317 ChromaKeyPackage::ChromaKeyPackage()
322 ChromaKeyUnit::ChromaKeyUnit(ChromaKey *plugin, ChromaKeyServer *server)
325 this->plugin = plugin;
329 void ChromaKeyUnit::process_package(LoadPackage *package)
331 ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
333 int w = plugin->input->get_w();
336 HSV::rgb_to_hsv(plugin->config.red,
337 plugin->config.green,
342 float min_hue = h - plugin->config.threshold * 360 / 100;
343 float max_hue = h + plugin->config.threshold * 360 / 100;
346 #define RGB_TO_VALUE(r, g, b) \
347 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
349 #define SQR(x) ((x) * (x))
351 float value = RGB_TO_VALUE(plugin->config.red,
352 plugin->config.green,
353 plugin->config.blue);
354 float min_v = value - plugin->config.threshold / 100;
355 float max_v = value + plugin->config.threshold / 100;
356 float threshold = plugin->config.threshold / 100;
357 float red = plugin->config.red;
358 float green = plugin->config.green;
359 float blue = plugin->config.blue;
361 float run = plugin->config.slope / 100;
362 float threshold_run = threshold + run;
365 #define CHROMAKEY(type, components, max, use_yuv) \
367 for(int i = pkg->y1; i < pkg->y2; i++) \
369 type *row = (type*)plugin->input->get_rows()[i]; \
371 for(int j = 0; j < w; j++) \
375 /* Test for value in range */ \
376 if(plugin->config.use_value) \
378 float current_value; \
381 float r = (float)row[0] / max; \
386 float r = (float)row[0] / max; \
387 float g = (float)row[1] / max; \
388 float b = (float)row[2] / max; \
389 current_value = RGB_TO_VALUE(r, g, b); \
392 /* Full transparency if in range */ \
393 if(current_value >= min_v && current_value < max_v) \
398 /* Phased out if below or above range */ \
399 if(current_value < min_v) \
401 if(min_v - current_value < run) \
402 a = (min_v - current_value) / run; \
405 if(current_value - max_v < run) \
406 a = (current_value - max_v) / run; \
409 /* Use color cube */ \
411 float r = (float)row[0] / max; \
412 float g = (float)row[1] / max; \
413 float b = (float)row[2] / max; \
416 /* Convert pixel to RGB float */ \
420 YUV::yuv_to_rgb_f(r, g, b, y, u - 0.5, v - 0.5); \
423 float difference = sqrt(SQR(r - red) + \
427 if(difference < threshold) \
432 if(difference < threshold_run) \
434 a = (difference - threshold) / run; \
439 /* Multiply alpha and put back in frame */ \
440 if(components == 4) \
442 row[3] = MIN((type)(a * max), row[3]); \
447 row[0] = (type)(a * row[0]); \
448 row[1] = (type)(a * (row[1] - (max / 2 + 1)) + max / 2 + 1); \
449 row[2] = (type)(a * (row[2] - (max / 2 + 1)) + max / 2 + 1); \
453 row[0] = (type)(a * row[0]); \
454 row[1] = (type)(a * row[1]); \
455 row[2] = (type)(a * row[2]); \
466 switch(plugin->input->get_color_model())
469 CHROMAKEY(float, 3, 1.0, 0);
472 CHROMAKEY(float, 4, 1.0, 0);
475 CHROMAKEY(unsigned char, 3, 0xff, 0);
478 CHROMAKEY(unsigned char, 4, 0xff, 0);
481 CHROMAKEY(unsigned char, 3, 0xff, 1);
484 CHROMAKEY(unsigned char, 4, 0xff, 1);
487 CHROMAKEY(uint16_t, 3, 0xffff, 1);
489 case BC_YUVA16161616:
490 CHROMAKEY(uint16_t, 4, 0xffff, 1);
500 REGISTER_PLUGIN(ChromaKey)
504 ChromaKey::ChromaKey(PluginServer *server)
505 : PluginVClient(server)
507 PLUGIN_CONSTRUCTOR_MACRO
511 ChromaKey::~ChromaKey()
513 PLUGIN_DESTRUCTOR_MACRO
514 if(engine) delete engine;
518 int ChromaKey::process_realtime(VFrame *input, VFrame *output)
520 load_configuration();
522 this->output = output;
524 if(EQUIV(config.threshold, 0))
526 if(input->get_rows()[0] != output->get_rows()[0])
527 output->copy_from(input);
531 if(!engine) engine = new ChromaKeyServer(this);
532 engine->process_packages();
538 char* ChromaKey::plugin_title() { return N_("Chroma key"); }
539 int ChromaKey::is_realtime() { return 1; }
541 NEW_PICON_MACRO(ChromaKey)
543 LOAD_CONFIGURATION_MACRO(ChromaKey, ChromaKeyConfig)
545 int ChromaKey::load_defaults()
547 char directory[BCTEXTLEN];
548 // set the default directory
549 sprintf(directory, "%schromakey.rc", BCASTDIR);
552 defaults = new Defaults(directory);
555 config.red = defaults->get("RED", config.red);
556 config.green = defaults->get("GREEN", config.green);
557 config.blue = defaults->get("BLUE", config.blue);
558 config.threshold = defaults->get("THRESHOLD", config.threshold);
559 config.slope = defaults->get("SLOPE", config.slope);
560 config.use_value = defaults->get("USE_VALUE", config.use_value);
564 int ChromaKey::save_defaults()
566 defaults->update("RED", config.red);
567 defaults->update("GREEN", config.green);
568 defaults->update("BLUE", config.blue);
569 defaults->update("THRESHOLD", config.threshold);
570 defaults->update("SLOPE", config.slope);
571 defaults->update("USE_VALUE", config.use_value);
576 void ChromaKey::save_data(KeyFrame *keyframe)
579 output.set_shared_string(keyframe->data, MESSAGESIZE);
580 output.tag.set_title("CHROMAKEY");
581 output.tag.set_property("RED", config.red);
582 output.tag.set_property("GREEN", config.green);
583 output.tag.set_property("BLUE", config.blue);
584 output.tag.set_property("THRESHOLD", config.threshold);
585 output.tag.set_property("SLOPE", config.slope);
586 output.tag.set_property("USE_VALUE", config.use_value);
588 output.terminate_string();
591 void ChromaKey::read_data(KeyFrame *keyframe)
595 input.set_shared_string(keyframe->data, strlen(keyframe->data));
597 while(!input.read_tag())
599 if(input.tag.title_is("CHROMAKEY"))
601 config.red = input.tag.get_property("RED", config.red);
602 config.green = input.tag.get_property("GREEN", config.green);
603 config.blue = input.tag.get_property("BLUE", config.blue);
604 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
605 config.slope = input.tag.get_property("SLOPE", config.slope);
606 config.use_value = input.tag.get_property("USE_VALUE", config.use_value);
612 SHOW_GUI_MACRO(ChromaKey, ChromaKeyThread)
614 SET_STRING_MACRO(ChromaKey)
616 RAISE_WINDOW_MACRO(ChromaKey)
618 void ChromaKey::update_gui()
622 load_configuration();
623 thread->window->lock_window();
624 thread->window->threshold->update(config.threshold);
625 thread->window->slope->update(config.slope);
626 thread->window->use_value->update(config.use_value);
627 thread->window->update_sample();
629 thread->window->unlock_window();