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
25 #include "bcdisplayinfo.h"
31 #include "loadbalance.h"
33 #include "pluginvclient.h"
49 void copy_from(DiffKeyConfig &src);
50 int equivalent(DiffKeyConfig &src);
51 void interpolate(DiffKeyConfig &prev,
55 int64_t current_frame);
63 class DiffKeyThreshold : public BC_FSlider
66 DiffKeyThreshold(DiffKey *plugin, int x, int y);
71 class DiffKeySlope : public BC_FSlider
74 DiffKeySlope(DiffKey *plugin, int x, int y);
79 class DiffKeyDoValue : public BC_CheckBox
82 DiffKeyDoValue(DiffKey *plugin, int x, int y);
89 class DiffKeyGUI : public PluginClientWindow
92 DiffKeyGUI(DiffKey *plugin);
96 void create_objects();
99 DiffKeyThreshold *threshold;
101 DiffKeyDoValue *do_value;
108 class DiffKeyEngine : public LoadServer
111 DiffKeyEngine(DiffKey *plugin);
112 void init_packages();
113 LoadClient* new_client();
114 LoadPackage* new_package();
119 class DiffKeyClient : public LoadClient
122 DiffKeyClient(DiffKeyEngine *engine);
125 void process_package(LoadPackage *pkg);
126 DiffKeyEngine *engine;
129 class DiffKeyPackage : public LoadPackage
139 class DiffKey : public PluginVClient
142 DiffKey(PluginServer *server);
145 int process_buffer(VFrame **frame,
146 int64_t start_position,
149 int is_multichannel();
150 void save_data(KeyFrame *keyframe);
151 void read_data(KeyFrame *keyframe);
157 PLUGIN_CLASS_MEMBERS(DiffKeyConfig)
159 DiffKeyEngine *engine;
161 VFrame *bottom_frame;
171 REGISTER_PLUGIN(DiffKey)
174 DiffKeyConfig::DiffKeyConfig()
181 void DiffKeyConfig::copy_from(DiffKeyConfig &src)
183 this->threshold = src.threshold;
184 this->slope = src.slope;
185 this->do_value = src.do_value;
189 int DiffKeyConfig::equivalent(DiffKeyConfig &src)
191 return EQUIV(threshold, src.threshold) &&
192 EQUIV(slope, src.slope) &&
193 do_value == src.do_value;
196 void DiffKeyConfig::interpolate(DiffKeyConfig &prev,
200 int64_t current_frame)
202 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
203 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
205 this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
206 this->slope = prev.slope * prev_scale + next.slope * next_scale;
207 this->do_value = prev.do_value;
219 DiffKeyThreshold::DiffKeyThreshold(DiffKey *plugin, int x, int y)
220 : BC_FSlider(x, y, 0, 200, 200, 0, 100, plugin->config.threshold)
222 this->plugin = plugin;
225 int DiffKeyThreshold::handle_event()
227 plugin->config.threshold = get_value();
228 plugin->send_configure_change();
239 DiffKeySlope::DiffKeySlope(DiffKey *plugin, int x, int y)
240 : BC_FSlider(x, y, 0, 200, 200, 0, 100, plugin->config.slope)
242 this->plugin = plugin;
245 int DiffKeySlope::handle_event()
247 plugin->config.slope = get_value();
248 plugin->send_configure_change();
254 DiffKeyDoValue::DiffKeyDoValue(DiffKey *plugin, int x, int y)
255 : BC_CheckBox(x, y, plugin->config.do_value, _("Use Value"))
257 this->plugin = plugin;
260 int DiffKeyDoValue::handle_event()
262 plugin->config.do_value = get_value();
263 plugin->send_configure_change();
273 DiffKeyGUI::DiffKeyGUI(DiffKey *plugin)
274 : PluginClientWindow(plugin,
281 this->plugin = plugin;
284 DiffKeyGUI::~DiffKeyGUI()
289 void DiffKeyGUI::create_objects()
293 add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
294 x += title->get_w() + 10;
295 add_subwindow(threshold = new DiffKeyThreshold(plugin, x, y));
297 y += threshold->get_h() + 10;
298 add_subwindow(title = new BC_Title(x, y, _("Slope:")));
299 x += title->get_w() + 10;
300 add_subwindow(slope = new DiffKeySlope(plugin, x, y));
302 y += slope->get_h() + 10;
303 add_subwindow(do_value = new DiffKeyDoValue(plugin, x, y));
313 DiffKey::DiffKey(PluginServer *server)
314 : PluginVClient(server)
326 NEW_WINDOW_MACRO(DiffKey, DiffKeyGUI)
327 LOAD_CONFIGURATION_MACRO(DiffKey, DiffKeyConfig)
329 const char* DiffKey::plugin_title() { return _("Difference key"); }
330 int DiffKey::is_realtime() { return 1; }
331 int DiffKey::is_multichannel() { return 1; }
334 void DiffKey::save_data(KeyFrame *keyframe)
337 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
338 output.tag.set_title("DIFFKEY");
339 output.tag.set_property("THRESHOLD", config.threshold);
340 output.tag.set_property("SLOPE", config.slope);
341 output.tag.set_property("DO_VALUE", config.do_value);
343 output.tag.set_title("/DIFFKEY");
345 output.append_newline();
346 output.terminate_string();
349 void DiffKey::read_data(KeyFrame *keyframe)
353 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
355 while(!input.read_tag())
357 if(input.tag.title_is("DIFFKEY"))
359 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
360 config.slope = input.tag.get_property("SLOPE", config.slope);
361 config.do_value = input.tag.get_property("DO_VALUE", config.do_value);
366 void DiffKey::update_gui()
370 if(load_configuration())
372 thread->window->lock_window("DiffKey::update_gui");
373 ((DiffKeyGUI*)thread->window)->threshold->update(config.threshold);
374 ((DiffKeyGUI*)thread->window)->slope->update(config.slope);
375 ((DiffKeyGUI*)thread->window)->do_value->update(config.do_value);
376 thread->window->unlock_window();
381 int DiffKey::process_buffer(VFrame **frame,
382 int64_t start_position,
385 load_configuration();
387 // Don't process if only 1 layer.
388 if(get_total_buffers() < 2)
398 // Read frames from 2 layers
410 top_frame = frame[0];
411 bottom_frame = frame[1];
418 engine = new DiffKeyEngine(this);
421 engine->process_packages();
426 #define DIFFKEY_VARS(plugin) \
427 float threshold = plugin->config.threshold / 100; \
428 float pad = plugin->config.slope / 100; \
429 float threshold_pad = threshold + pad; \
432 int DiffKey::handle_opengl()
435 static const char *diffkey_head =
436 "uniform sampler2D tex_bg;\n"
437 "uniform sampler2D tex_fg;\n"
438 "uniform float threshold;\n"
439 "uniform float pad;\n"
440 "uniform float threshold_pad;\n"
443 " vec4 foreground = texture2D(tex_fg, gl_TexCoord[0].st);\n"
444 " vec4 background = texture2D(tex_bg, gl_TexCoord[0].st);\n";
446 static const char *colorcube =
447 " float difference = length(foreground.rgb - background.rgb);\n";
449 static const char *yuv_value =
450 " float difference = abs(foreground.r - background.r);\n";
452 static const char *rgb_value =
453 " float difference = abs(dot(foreground.rgb, vec3(0.29900, 0.58700, 0.11400)) - \n"
454 " dot(background.rgb, vec3(0.29900, 0.58700, 0.11400)));\n";
456 static const char *diffkey_tail =
458 " if(difference < threshold)\n"
461 " if(difference < threshold_pad)\n"
462 " result.a = (difference - threshold) / pad;\n"
465 " result.rgb = foreground.rgb;\n"
466 " gl_FragColor = result;\n"
473 top_frame->enable_opengl();
474 top_frame->init_screen();
476 top_frame->to_texture();
477 bottom_frame->to_texture();
479 top_frame->enable_opengl();
480 top_frame->init_screen();
482 unsigned int shader_id = 0;
485 if(BC_CModels::is_yuv(top_frame->get_color_model()))
486 shader_id = VFrame::make_shader(0,
492 shader_id = VFrame::make_shader(0,
500 shader_id = VFrame::make_shader(0,
511 bottom_frame->bind_texture(1);
512 top_frame->bind_texture(0);
516 glUseProgram(shader_id);
517 glUniform1i(glGetUniformLocation(shader_id, "tex_fg"), 0);
518 glUniform1i(glGetUniformLocation(shader_id, "tex_bg"), 1);
519 glUniform1f(glGetUniformLocation(shader_id, "threshold"), threshold);
520 glUniform1f(glGetUniformLocation(shader_id, "pad"), pad);
521 glUniform1f(glGetUniformLocation(shader_id, "threshold_pad"), threshold_pad);
524 if(BC_CModels::components(get_output()->get_color_model()) == 3)
527 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
528 top_frame->clear_pbuffer();
531 top_frame->draw_texture();
533 top_frame->set_opengl_state(VFrame::SCREEN);
534 // Fastest way to discard output
535 bottom_frame->set_opengl_state(VFrame::TEXTURE);
546 DiffKeyEngine::DiffKeyEngine(DiffKey *plugin)
547 : LoadServer(plugin->get_project_smp() + 1, plugin->get_project_smp() + 1)
549 this->plugin = plugin;
552 void DiffKeyEngine::init_packages()
554 int increment = plugin->top_frame->get_h() / get_total_packages() + 1;
556 for(int i = 0; i < get_total_packages(); i++)
558 DiffKeyPackage *pkg = (DiffKeyPackage*)get_package(i);
560 pkg->row2 = MIN(y + increment, plugin->top_frame->get_h());
565 LoadClient* DiffKeyEngine::new_client()
567 return new DiffKeyClient(this);
570 LoadPackage* DiffKeyEngine::new_package()
572 return new DiffKeyPackage;
585 DiffKeyClient::DiffKeyClient(DiffKeyEngine *engine)
588 this->engine = engine;
591 DiffKeyClient::~DiffKeyClient()
595 void DiffKeyClient::process_package(LoadPackage *ptr)
597 DiffKeyPackage *pkg = (DiffKeyPackage*)ptr;
598 DiffKey *plugin = engine->plugin;
599 int w = plugin->top_frame->get_w();
601 #define RGB_TO_VALUE(r, g, b) \
602 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
605 #define DIFFKEY_MACRO(type, components, max, chroma_offset) \
608 for(int i = pkg->row1; i < pkg->row2; i++) \
610 type *top_row = (type*)plugin->top_frame->get_rows()[i]; \
611 type *bottom_row = (type*)plugin->bottom_frame->get_rows()[i]; \
613 for(int j = 0; j < w; j++) \
617 /* Test for value in range */ \
618 if(plugin->config.do_value) \
621 float bottom_value; \
623 /* Convert pixel data into floating point value */ \
626 float top_r = (float)top_row[0] / max; \
627 float bottom_r = (float)bottom_row[0] / max; \
629 bottom_value = bottom_r; \
633 float top_r = (float)top_row[0] / max; \
634 float top_g = (float)top_row[1] / max; \
635 float top_b = (float)top_row[2] / max; \
636 top_g -= (float)chroma_offset / max; \
637 top_b -= (float)chroma_offset / max; \
639 float bottom_r = (float)bottom_row[0] / max; \
640 float bottom_g = (float)bottom_row[1] / max; \
641 float bottom_b = (float)bottom_row[2] / max; \
642 bottom_g -= (float)chroma_offset / max; \
643 bottom_b -= (float)chroma_offset / max; \
645 top_value = RGB_TO_VALUE(top_r, top_g, top_b); \
646 bottom_value = RGB_TO_VALUE(bottom_r, bottom_g, bottom_b); \
649 float min_v = bottom_value - threshold; \
650 float max_v = bottom_value + threshold; \
652 /* Full transparency if in range */ \
653 if(top_value >= min_v && top_value < max_v) \
658 /* Phased out if below or above range */ \
659 if(top_value < min_v) \
661 if(min_v - top_value < pad) \
662 a = (min_v - top_value) / pad; \
665 if(top_value - max_v < pad) \
666 a = (top_value - max_v) / pad; \
669 /* Use color cube */ \
671 float top_r = (float)top_row[0] / max; \
672 float top_g = (float)top_row[1] / max; \
673 float top_b = (float)top_row[2] / max; \
674 top_g -= (float)chroma_offset / max; \
675 top_b -= (float)chroma_offset / max; \
677 float bottom_r = (float)bottom_row[0] / max; \
678 float bottom_g = (float)bottom_row[1] / max; \
679 float bottom_b = (float)bottom_row[2] / max; \
680 bottom_g -= (float)chroma_offset / max; \
681 bottom_b -= (float)chroma_offset / max; \
684 float difference = sqrt(SQR(top_r - bottom_r) + \
685 SQR(top_g - bottom_g) + \
686 SQR(top_b - bottom_b)); \
688 if(difference < threshold) \
693 if(difference < threshold_pad) \
695 a = (difference - threshold) / pad; \
699 /* multiply alpha */ \
700 if(components == 4) \
702 top_row[3] = MIN((type)(a * max), top_row[3]); \
706 top_row[0] = (type)(a * top_row[0]); \
707 top_row[1] = (type)(a * (top_row[1] - chroma_offset) + chroma_offset); \
708 top_row[2] = (type)(a * (top_row[2] - chroma_offset) + chroma_offset); \
711 top_row += components; \
712 bottom_row += components; \
719 switch(plugin->top_frame->get_color_model())
722 DIFFKEY_MACRO(float, 3, 1.0, 0);
725 DIFFKEY_MACRO(float, 4, 1.0, 0);
728 DIFFKEY_MACRO(unsigned char, 3, 0xff, 0);
731 DIFFKEY_MACRO(unsigned char, 4, 0xff, 0);
734 DIFFKEY_MACRO(unsigned char, 3, 0xff, 0x80);
737 DIFFKEY_MACRO(unsigned char, 4, 0xff, 0x80);
748 DiffKeyPackage::DiffKeyPackage()