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
26 #include "bcdisplayinfo.h"
32 #include "loadbalance.h"
34 #include "pluginvclient.h"
35 #include "playback3d.h"
50 void copy_from(DiffKeyConfig &src);
51 int equivalent(DiffKeyConfig &src);
52 void interpolate(DiffKeyConfig &prev,
56 int64_t current_frame);
64 class DiffKeyThreshold : public BC_FSlider
67 DiffKeyThreshold(DiffKey *plugin, int x, int y);
72 class DiffKeySlope : public BC_FSlider
75 DiffKeySlope(DiffKey *plugin, int x, int y);
80 class DiffKeyDoValue : public BC_CheckBox
83 DiffKeyDoValue(DiffKey *plugin, int x, int y);
90 class DiffKeyGUI : public PluginClientWindow
93 DiffKeyGUI(DiffKey *plugin);
97 void create_objects();
100 DiffKeyThreshold *threshold;
102 DiffKeyDoValue *do_value;
109 class DiffKeyEngine : public LoadServer
112 DiffKeyEngine(DiffKey *plugin);
113 void init_packages();
114 LoadClient* new_client();
115 LoadPackage* new_package();
120 class DiffKeyClient : public LoadClient
123 DiffKeyClient(DiffKeyEngine *engine);
126 void process_package(LoadPackage *pkg);
127 DiffKeyEngine *engine;
130 class DiffKeyPackage : public LoadPackage
140 class DiffKey : public PluginVClient
143 DiffKey(PluginServer *server);
146 int process_buffer(VFrame **frame,
147 int64_t start_position,
150 int is_multichannel();
151 void save_data(KeyFrame *keyframe);
152 void read_data(KeyFrame *keyframe);
158 PLUGIN_CLASS_MEMBERS(DiffKeyConfig)
160 DiffKeyEngine *engine;
162 VFrame *bottom_frame;
172 REGISTER_PLUGIN(DiffKey)
175 DiffKeyConfig::DiffKeyConfig()
182 void DiffKeyConfig::copy_from(DiffKeyConfig &src)
184 this->threshold = src.threshold;
185 this->slope = src.slope;
186 this->do_value = src.do_value;
190 int DiffKeyConfig::equivalent(DiffKeyConfig &src)
192 return EQUIV(threshold, src.threshold) &&
193 EQUIV(slope, src.slope) &&
194 do_value == src.do_value;
197 void DiffKeyConfig::interpolate(DiffKeyConfig &prev,
201 int64_t current_frame)
203 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
204 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
206 this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
207 this->slope = prev.slope * prev_scale + next.slope * next_scale;
208 this->do_value = prev.do_value;
220 DiffKeyThreshold::DiffKeyThreshold(DiffKey *plugin, int x, int y)
221 : BC_FSlider(x, y, 0, xS(200), yS(200), 0, 100, plugin->config.threshold)
223 this->plugin = plugin;
226 int DiffKeyThreshold::handle_event()
228 plugin->config.threshold = get_value();
229 plugin->send_configure_change();
240 DiffKeySlope::DiffKeySlope(DiffKey *plugin, int x, int y)
241 : BC_FSlider(x, y, 0, xS(200), yS(200), 0, 100, plugin->config.slope)
243 this->plugin = plugin;
246 int DiffKeySlope::handle_event()
248 plugin->config.slope = get_value();
249 plugin->send_configure_change();
255 DiffKeyDoValue::DiffKeyDoValue(DiffKey *plugin, int x, int y)
256 : BC_CheckBox(x, y, plugin->config.do_value, _("Use Value"))
258 this->plugin = plugin;
261 int DiffKeyDoValue::handle_event()
263 plugin->config.do_value = get_value();
264 plugin->send_configure_change();
274 DiffKeyGUI::DiffKeyGUI(DiffKey *plugin)
275 : PluginClientWindow(plugin,
282 this->plugin = plugin;
285 DiffKeyGUI::~DiffKeyGUI()
290 void DiffKeyGUI::create_objects()
292 int xs10 = xS(10), xs38 = xS(38);
294 int x = xs10, y = ys10;
296 add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
297 x += title->get_w() + xs10;
298 add_subwindow(threshold = new DiffKeyThreshold(plugin, x, y));
300 y += threshold->get_h() + ys10;
301 add_subwindow(title = new BC_Title(x, y, _("Slope:")));
302 x += title->get_w() + xs38;
303 add_subwindow(slope = new DiffKeySlope(plugin, x, y));
305 y += slope->get_h() + ys10;
306 add_subwindow(do_value = new DiffKeyDoValue(plugin, x, y));
316 DiffKey::DiffKey(PluginServer *server)
317 : PluginVClient(server)
329 NEW_WINDOW_MACRO(DiffKey, DiffKeyGUI)
330 LOAD_CONFIGURATION_MACRO(DiffKey, DiffKeyConfig)
332 const char* DiffKey::plugin_title() { return N_("Difference key"); }
333 int DiffKey::is_realtime() { return 1; }
334 int DiffKey::is_multichannel() { return 1; }
337 void DiffKey::save_data(KeyFrame *keyframe)
340 output.set_shared_output(keyframe->xbuf);
341 output.tag.set_title("DIFFKEY");
342 output.tag.set_property("THRESHOLD", config.threshold);
343 output.tag.set_property("SLOPE", config.slope);
344 output.tag.set_property("DO_VALUE", config.do_value);
346 output.tag.set_title("/DIFFKEY");
348 output.append_newline();
349 output.terminate_string();
352 void DiffKey::read_data(KeyFrame *keyframe)
356 input.set_shared_input(keyframe->xbuf);
358 while(!input.read_tag())
360 if(input.tag.title_is("DIFFKEY"))
362 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
363 config.slope = input.tag.get_property("SLOPE", config.slope);
364 config.do_value = input.tag.get_property("DO_VALUE", config.do_value);
369 void DiffKey::update_gui()
373 if(load_configuration())
375 thread->window->lock_window("DiffKey::update_gui");
376 ((DiffKeyGUI*)thread->window)->threshold->update(config.threshold);
377 ((DiffKeyGUI*)thread->window)->slope->update(config.slope);
378 ((DiffKeyGUI*)thread->window)->do_value->update(config.do_value);
379 thread->window->unlock_window();
384 int DiffKey::process_buffer(VFrame **frame,
385 int64_t start_position,
388 load_configuration();
390 // Don't process if only 1 layer.
391 if(get_total_buffers() < 2)
401 // Read frames from 2 layers
413 top_frame = frame[0];
414 bottom_frame = frame[1];
421 engine = new DiffKeyEngine(this);
424 engine->process_packages();
429 #define DIFFKEY_VARS(plugin) \
430 float threshold = plugin->config.threshold / 100; \
431 float pad = plugin->config.slope / 100; \
432 float threshold_pad = threshold + pad; \
435 int DiffKey::handle_opengl()
438 static const char *diffkey_head =
439 "uniform sampler2D tex_bg;\n"
440 "uniform sampler2D tex_fg;\n"
441 "uniform float threshold;\n"
442 "uniform float pad;\n"
443 "uniform float threshold_pad;\n"
446 " vec4 foreground = texture2D(tex_fg, gl_TexCoord[0].st);\n"
447 " vec4 background = texture2D(tex_bg, gl_TexCoord[0].st);\n";
449 static const char *colorcube =
450 " float difference = length(foreground.rgb - background.rgb);\n";
452 static const char *yuv_value =
453 " float difference = abs(foreground.r - background.r);\n";
455 static const char *rgb_value =
456 " float difference = abs("
457 " dot(foreground.rgb, rgb_to_y_vector) - "
458 " dot(background.rgb, rgb_to_y_vector));\n";
460 static const char *diffkey_tail =
462 " if(difference < threshold)\n"
465 " if(difference < threshold_pad)\n"
466 " result.a = (difference - threshold) / pad;\n"
469 " result.rgb = foreground.rgb;\n"
470 " gl_FragColor = result;\n"
473 top_frame->to_texture();
474 bottom_frame->to_texture();
476 top_frame->enable_opengl();
477 top_frame->init_screen();
479 const char *shader_stack[16];
480 memset(shader_stack,0, sizeof(shader_stack));
481 int current_shader = 0;
483 int need_rgb_to_y = 0;
484 const char *shader_frag = !config.do_value ? colorcube :
485 BC_CModels::is_yuv(top_frame->get_color_model()) ?
486 yuv_value : (need_rgb_to_y = 1, rgb_value);
488 shader_stack[current_shader++] = bc_gl_rgb_to_y;
489 shader_stack[current_shader++] = diffkey_head;
490 shader_stack[current_shader++] = shader_frag;
491 shader_stack[current_shader++] = diffkey_tail;
493 shader_stack[current_shader] = 0;
494 unsigned int shader = VFrame::make_shader(shader_stack);
497 bottom_frame->bind_texture(1);
498 top_frame->bind_texture(0);
501 glUseProgram(shader);
502 glUniform1i(glGetUniformLocation(shader, "tex_fg"), 0);
503 glUniform1i(glGetUniformLocation(shader, "tex_bg"), 1);
504 glUniform1f(glGetUniformLocation(shader, "threshold"), threshold);
505 glUniform1f(glGetUniformLocation(shader, "pad"), pad);
506 glUniform1f(glGetUniformLocation(shader, "threshold_pad"), threshold_pad);
508 BC_GL_MATRIX(shader, rgb_to_y_vector);
511 if(BC_CModels::components(get_output()->get_color_model()) == 3)
514 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
515 top_frame->clear_pbuffer();
518 top_frame->draw_texture();
520 top_frame->set_opengl_state(VFrame::SCREEN);
522 // Fastest way to discard output
523 bottom_frame->set_opengl_state(VFrame::UNKNOWN);
526 top_frame->screen_to_ram();
534 DiffKeyEngine::DiffKeyEngine(DiffKey *plugin)
535 : LoadServer(plugin->get_project_smp() + 1, plugin->get_project_smp() + 1)
537 this->plugin = plugin;
540 void DiffKeyEngine::init_packages()
542 int increment = plugin->top_frame->get_h() / get_total_packages() + 1;
544 for(int i = 0; i < get_total_packages(); i++)
546 DiffKeyPackage *pkg = (DiffKeyPackage*)get_package(i);
548 pkg->row2 = MIN(y + increment, plugin->top_frame->get_h());
553 LoadClient* DiffKeyEngine::new_client()
555 return new DiffKeyClient(this);
558 LoadPackage* DiffKeyEngine::new_package()
560 return new DiffKeyPackage;
573 DiffKeyClient::DiffKeyClient(DiffKeyEngine *engine)
576 this->engine = engine;
579 DiffKeyClient::~DiffKeyClient()
583 void DiffKeyClient::process_package(LoadPackage *ptr)
585 DiffKeyPackage *pkg = (DiffKeyPackage*)ptr;
586 DiffKey *plugin = engine->plugin;
587 int w = plugin->top_frame->get_w();
589 #define RGB_TO_VALUE(r, g, b) YUV::yuv.rgb_to_y_f((r),(g),(b))
592 #define DIFFKEY_MACRO(type, components, max, chroma_offset) \
595 for(int i = pkg->row1; i < pkg->row2; i++) \
597 type *top_row = (type*)plugin->top_frame->get_rows()[i]; \
598 type *bottom_row = (type*)plugin->bottom_frame->get_rows()[i]; \
600 for(int j = 0; j < w; j++) \
604 /* Test for value in range */ \
605 if(plugin->config.do_value) \
608 float bottom_value; \
610 /* Convert pixel data into floating point value */ \
613 float top_r = (float)top_row[0] / max; \
614 float bottom_r = (float)bottom_row[0] / max; \
616 bottom_value = bottom_r; \
620 float top_r = (float)top_row[0] / max; \
621 float top_g = (float)top_row[1] / max; \
622 float top_b = (float)top_row[2] / max; \
623 top_g -= (float)chroma_offset / max; \
624 top_b -= (float)chroma_offset / max; \
626 float bottom_r = (float)bottom_row[0] / max; \
627 float bottom_g = (float)bottom_row[1] / max; \
628 float bottom_b = (float)bottom_row[2] / max; \
629 bottom_g -= (float)chroma_offset / max; \
630 bottom_b -= (float)chroma_offset / max; \
632 top_value = RGB_TO_VALUE(top_r, top_g, top_b); \
633 bottom_value = RGB_TO_VALUE(bottom_r, bottom_g, bottom_b); \
636 float min_v = bottom_value - threshold; \
637 float max_v = bottom_value + threshold; \
639 /* Full transparency if in range */ \
640 if(top_value >= min_v && top_value < max_v) \
645 /* Phased out if below or above range */ \
646 if(top_value < min_v) \
648 if(min_v - top_value < pad) \
649 a = (min_v - top_value) / pad; \
652 if(top_value - max_v < pad) \
653 a = (top_value - max_v) / pad; \
656 /* Use color cube */ \
658 float top_r = (float)top_row[0] / max; \
659 float top_g = (float)top_row[1] / max; \
660 float top_b = (float)top_row[2] / max; \
661 top_g -= (float)chroma_offset / max; \
662 top_b -= (float)chroma_offset / max; \
664 float bottom_r = (float)bottom_row[0] / max; \
665 float bottom_g = (float)bottom_row[1] / max; \
666 float bottom_b = (float)bottom_row[2] / max; \
667 bottom_g -= (float)chroma_offset / max; \
668 bottom_b -= (float)chroma_offset / max; \
671 float difference = sqrt(SQR(top_r - bottom_r) + \
672 SQR(top_g - bottom_g) + \
673 SQR(top_b - bottom_b)); \
675 if(difference < threshold) \
680 if(difference < threshold_pad) \
682 a = (difference - threshold) / pad; \
686 /* multiply alpha */ \
687 if(components == 4) \
689 top_row[3] = MIN((type)(a * max), top_row[3]); \
693 top_row[0] = (type)(a * top_row[0]); \
694 top_row[1] = (type)(a * (top_row[1] - chroma_offset) + chroma_offset); \
695 top_row[2] = (type)(a * (top_row[2] - chroma_offset) + chroma_offset); \
698 top_row += components; \
699 bottom_row += components; \
706 switch(plugin->top_frame->get_color_model())
709 DIFFKEY_MACRO(float, 3, 1.0, 0);
712 DIFFKEY_MACRO(float, 4, 1.0, 0);
715 DIFFKEY_MACRO(unsigned char, 3, 0xff, 0);
718 DIFFKEY_MACRO(unsigned char, 4, 0xff, 0);
721 DIFFKEY_MACRO(unsigned char, 3, 0xff, 0x80);
724 DIFFKEY_MACRO(unsigned char, 4, 0xff, 0x80);
735 DiffKeyPackage::DiffKeyPackage()