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 "threshold.h"
30 #include "histogramengine.h"
32 #include "playback3d.h"
33 #include "thresholdwindow.h"
40 ThresholdConfig::ThresholdConfig()
45 int ThresholdConfig::equivalent(ThresholdConfig &that)
47 return EQUIV(min, that.min) &&
48 EQUIV(max, that.max) &&
50 low_color == that.low_color &&
51 mid_color == that.mid_color &&
52 high_color == that.high_color;
55 void ThresholdConfig::copy_from(ThresholdConfig &that)
60 low_color = that.low_color;
61 mid_color = that.mid_color;
62 high_color = that.high_color;
65 // General purpose scale function.
67 T interpolate(const T & prev, const double & prev_scale, const T & next, const double & next_scale)
69 return static_cast<T>(prev * prev_scale + next * next_scale);
72 void ThresholdConfig::interpolate(ThresholdConfig &prev,
73 ThresholdConfig &next,
76 int64_t current_frame)
78 double next_scale = (double)(current_frame - prev_frame) /
79 (next_frame - prev_frame);
80 double prev_scale = (double)(next_frame - current_frame) /
81 (next_frame - prev_frame);
83 min = ::interpolate(prev.min, prev_scale, next.min, next_scale);
84 max = ::interpolate(prev.max, prev_scale, next.max, next_scale);
87 low_color = ::interpolate(prev.low_color, prev_scale, next.low_color, next_scale);
88 mid_color = ::interpolate(prev.mid_color, prev_scale, next.mid_color, next_scale);
89 high_color = ::interpolate(prev.high_color, prev_scale, next.high_color, next_scale);
92 void ThresholdConfig::reset()
97 low_color.set (0x0, 0x0, 0x0, 0xff);
98 mid_color.set (0xff, 0xff, 0xff, 0xff);
99 high_color.set(0x0, 0x0, 0x0, 0xff);
102 void ThresholdConfig::boundaries()
104 CLAMP(min, HISTOGRAM_MIN, max);
105 CLAMP(max, min, HISTOGRAM_MAX);
115 REGISTER_PLUGIN(ThresholdMain)
117 ThresholdMain::ThresholdMain(PluginServer *server)
118 : PluginVClient(server)
121 threshold_engine = 0;
124 ThresholdMain::~ThresholdMain()
127 delete threshold_engine;
130 const char* ThresholdMain::plugin_title() { return _("Threshold"); }
131 int ThresholdMain::is_realtime() { return 1; }
135 LOAD_CONFIGURATION_MACRO(ThresholdMain, ThresholdConfig)
143 int ThresholdMain::process_buffer(VFrame *frame,
144 int64_t start_position,
147 load_configuration();
149 int use_opengl = get_use_opengl() &&
150 (!config.plot || !gui_open());
154 get_source_position(),
158 if(use_opengl) return run_opengl();
160 send_render_gui(frame);
162 if(!threshold_engine)
163 threshold_engine = new ThresholdEngine(this);
164 threshold_engine->process_packages(frame);
169 void ThresholdMain::save_data(KeyFrame *keyframe)
172 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
173 output.tag.set_title("THRESHOLD");
174 output.tag.set_property("MIN", config.min);
175 output.tag.set_property("MAX", config.max);
176 output.tag.set_property("PLOT", config.plot);
177 config.low_color.set_property(output.tag, "LOW_COLOR");
178 config.mid_color.set_property(output.tag, "MID_COLOR");
179 config.high_color.set_property(output.tag, "HIGH_COLOR");
181 output.tag.set_title("/THRESHOLD");
183 output.append_newline();
184 output.terminate_string();
187 void ThresholdMain::read_data(KeyFrame *keyframe)
190 const char *data = keyframe->get_data();
191 input.set_shared_input((char *)data, strlen(data));
195 result = input.read_tag();
198 config.min = input.tag.get_property("MIN", config.min);
199 config.max = input.tag.get_property("MAX", config.max);
200 config.plot = input.tag.get_property("PLOT", config.plot);
201 config.low_color = config.low_color.get_property(input.tag, "LOW_COLOR");
202 config.mid_color = config.mid_color.get_property(input.tag, "MID_COLOR");
203 config.high_color = config.high_color.get_property(input.tag, "HIGH_COLOR");
209 NEW_WINDOW_MACRO(ThresholdMain,ThresholdWindow);
211 void ThresholdMain::update_gui()
215 thread->window->lock_window("ThresholdMain::update_gui");
216 if(load_configuration())
218 ThresholdWindow *window = (ThresholdWindow*) thread->window;
219 window->min->update(config.min);
220 window->max->update(config.max);
221 window->plot->update(config.plot);
222 window->update_low_color();
223 window->update_mid_color();
224 window->update_high_color();
225 window->low_color_thread->update_gui(config.low_color.getRGB(), config.low_color.a);
226 window->mid_color_thread->update_gui(config.mid_color.getRGB(), config.mid_color.a);
227 window->high_color_thread->update_gui(config.high_color.getRGB(), config.high_color.a);
229 thread->window->unlock_window();
233 void ThresholdMain::render_gui(void *data)
237 calculate_histogram((VFrame*)data);
238 ThresholdWindow *window = (ThresholdWindow*) thread->window;
239 window->lock_window("ThresholdMain::render_gui");
240 window->canvas->draw();
241 window->unlock_window();
245 void ThresholdMain::calculate_histogram(VFrame *frame)
247 if(!engine) engine = new HistogramEngine(get_project_smp() + 1,
248 get_project_smp() + 1);
249 engine->process_packages(frame);
252 int ThresholdMain::handle_opengl()
255 static const char *rgb_shader =
256 "uniform sampler2D tex;\n"
257 "uniform float min;\n"
258 "uniform float max;\n"
259 "uniform vec4 low_color;\n"
260 "uniform vec4 mid_color;\n"
261 "uniform vec4 high_color;\n"
262 "uniform vec3 rgb_to_y_vector;\n"
263 "uniform float yminf;\n"
266 " vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
267 " float v = dot(pixel.rgb, rgb_to_y_vector) + yminf;\n"
269 " pixel = low_color;\n"
270 " else if(v < max)\n"
271 " pixel = mid_color;\n"
273 " pixel = high_color;\n"
274 " gl_FragColor = pixel;\n"
277 static const char *yuv_shader =
278 "uniform sampler2D tex;\n"
279 "uniform float min;\n"
280 "uniform float max;\n"
281 "uniform vec4 low_color;\n"
282 "uniform vec4 mid_color;\n"
283 "uniform vec4 high_color;\n"
286 " vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
287 " if(pixel.r < min)\n"
288 " pixel = low_color;\n"
289 " else if(pixel.r < max)\n"
290 " pixel = mid_color;\n"
292 " pixel = high_color;\n"
293 " gl_FragColor = pixel;\n"
296 get_output()->to_texture();
297 get_output()->enable_opengl();
299 int color_model = get_output()->get_color_model();
300 bool is_yuv = BC_CModels::is_yuv(color_model);
301 bool has_alpha = BC_CModels::has_alpha(color_model);
302 unsigned int shader = VFrame::make_shader(0, is_yuv ? yuv_shader : rgb_shader, 0);
304 glUseProgram(shader);
305 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
306 glUniform1f(glGetUniformLocation(shader, "min"), config.min);
307 glUniform1f(glGetUniformLocation(shader, "max"), config.max);
310 float y_low, u_low, v_low;
311 float y_mid, u_mid, v_mid;
312 float y_high, u_high, v_high;
314 YUV::yuv.rgb_to_yuv_f(
315 (float)config.low_color.r / 0xff,
316 (float)config.low_color.g / 0xff,
317 (float)config.low_color.b / 0xff,
318 y_low, u_low, v_low);
319 u_low += 0.5; v_low += 0.5;
320 YUV::yuv.rgb_to_yuv_f(
321 (float)config.mid_color.r / 0xff,
322 (float)config.mid_color.g / 0xff,
323 (float)config.mid_color.b / 0xff,
324 y_mid, u_mid, v_mid);
325 u_mid += 0.5; v_mid += 0.5;
326 YUV::yuv.rgb_to_yuv_f(
327 (float)config.high_color.r / 0xff,
328 (float)config.high_color.g / 0xff,
329 (float)config.high_color.b / 0xff,
330 y_high, u_high, v_high);
331 u_high += 0.5; v_high += 0.5;
333 glUniform4f(glGetUniformLocation(shader, "low_color"),
335 has_alpha ? (float)config.low_color.a / 0xff : 1.0);
336 glUniform4f(glGetUniformLocation(shader, "mid_color"),
338 has_alpha ? (float)config.mid_color.a / 0xff : 1.0);
339 glUniform4f(glGetUniformLocation(shader, "high_color"),
340 y_high, u_high, v_high,
341 has_alpha ? (float)config.high_color.a / 0xff : 1.0);
344 glUniform4f(glGetUniformLocation(shader, "low_color"),
345 (float)config.low_color.r / 0xff,
346 (float)config.low_color.g / 0xff,
347 (float)config.low_color.b / 0xff,
348 has_alpha ? (float)config.low_color.a / 0xff : 1.0);
349 glUniform4f(glGetUniformLocation(shader, "mid_color"),
350 (float)config.mid_color.r / 0xff,
351 (float)config.mid_color.g / 0xff,
352 (float)config.mid_color.b / 0xff,
353 has_alpha ? (float)config.mid_color.a / 0xff : 1.0);
354 glUniform4f(glGetUniformLocation(shader, "high_color"),
355 (float)config.high_color.r / 0xff,
356 (float)config.high_color.g / 0xff,
357 (float)config.high_color.b / 0xff,
358 has_alpha ? (float)config.high_color.a / 0xff : 1.0);
359 BC_GL_RGB_TO_Y(shader);
363 get_output()->init_screen();
364 get_output()->bind_texture(0);
365 get_output()->draw_texture();
367 get_output()->set_opengl_state(VFrame::SCREEN);
393 ThresholdPackage::ThresholdPackage()
409 ThresholdUnit::ThresholdUnit(ThresholdEngine *server)
412 this->server = server;
415 // Coerces pixel component to int.
416 static inline int get_component(unsigned char v)
421 static inline int get_component(float v)
423 return (int)(v * 0xffff);
426 static inline int get_component(uint16_t v)
431 // Rescales value in range [0, 255] to range appropriate to TYPE.
432 template<typename TYPE>
433 static TYPE scale_to_range(int v)
435 return v; // works for unsigned char, override for the rest.
439 inline float scale_to_range(int v)
441 return (float) v / 0xff;
445 inline uint16_t scale_to_range(int v)
450 static inline void rgb_to_yuv(unsigned char r, unsigned char g, unsigned char b,
451 unsigned char & y, unsigned char & u, unsigned char & v)
453 YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v);
456 static inline void rgb_to_yuv(float r, float g, float b,
457 float & y, float & u, float & v)
459 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v);
462 static inline void rgb_to_yuv(uint16_t r, uint16_t g, uint16_t b,
463 uint16_t & y, uint16_t & u, uint16_t & v)
465 YUV::yuv.rgb_to_yuv_16(r, g, b, y, u, v);
468 template<typename TYPE, int COMPONENTS, bool USE_YUV>
469 void ThresholdUnit::render_data(LoadPackage *package)
471 const ThresholdPackage *pkg = (ThresholdPackage*)package;
472 const ThresholdConfig *config = & server->plugin->config;
473 VFrame *data = server->data;
474 const int min = (int)(config->min * 0xffff);
475 const int max = (int)(config->max * 0xffff);
476 const int w = data->get_w();
477 //const int h = data->get_h();
479 const TYPE r_low = scale_to_range<TYPE>(config->low_color.r);
480 const TYPE g_low = scale_to_range<TYPE>(config->low_color.g);
481 const TYPE b_low = scale_to_range<TYPE>(config->low_color.b);
482 const TYPE a_low = scale_to_range<TYPE>(config->low_color.a);
484 const TYPE r_mid = scale_to_range<TYPE>(config->mid_color.r);
485 const TYPE g_mid = scale_to_range<TYPE>(config->mid_color.g);
486 const TYPE b_mid = scale_to_range<TYPE>(config->mid_color.b);
487 const TYPE a_mid = scale_to_range<TYPE>(config->mid_color.a);
489 const TYPE r_high = scale_to_range<TYPE>(config->high_color.r);
490 const TYPE g_high = scale_to_range<TYPE>(config->high_color.g);
491 const TYPE b_high = scale_to_range<TYPE>(config->high_color.b);
492 const TYPE a_high = scale_to_range<TYPE>(config->high_color.a);
494 TYPE y_low, u_low, v_low;
495 TYPE y_mid, u_mid, v_mid;
496 TYPE y_high, u_high, v_high;
500 rgb_to_yuv(r_low, g_low, b_low, y_low, u_low, v_low);
501 rgb_to_yuv(r_mid, g_mid, b_mid, y_mid, u_mid, v_mid);
502 rgb_to_yuv(r_high, g_high, b_high, y_high, u_high, v_high);
505 for(int i = pkg->start; i < pkg->end; i++)
507 TYPE *in_row = (TYPE*)data->get_rows()[i];
508 TYPE *out_row = in_row;
509 for(int j = 0; j < w; j++)
513 const int y = get_component(in_row[0]);
519 if(COMPONENTS == 4) *out_row++ = a_low;
526 if(COMPONENTS == 4) *out_row++ = a_mid;
533 if(COMPONENTS == 4) *out_row++ = a_high;
538 const int r = get_component(in_row[0]);
539 const int g = get_component(in_row[1]);
540 const int b = get_component(in_row[2]);
541 const int y = (r * 76 + g * 150 + b * 29) >> 8;
547 if(COMPONENTS == 4) *out_row++ = a_low;
554 if(COMPONENTS == 4) *out_row++ = a_mid;
561 if(COMPONENTS == 4) *out_row++ = a_high;
564 in_row += COMPONENTS;
569 void ThresholdUnit::process_package(LoadPackage *package)
571 switch(server->data->get_color_model())
574 render_data<unsigned char, 3, false>(package);
578 render_data<float, 3, false>(package);
582 render_data<unsigned char, 4, false>(package);
586 render_data<float, 4, false>(package);
590 render_data<unsigned char, 3, true>(package);
594 render_data<unsigned char, 4, true>(package);
598 render_data<uint16_t, 3, true>(package);
601 case BC_YUVA16161616:
602 render_data<uint16_t, 4, true>(package);
617 ThresholdEngine::ThresholdEngine(ThresholdMain *plugin)
618 : LoadServer(plugin->get_project_smp() + 1,
619 plugin->get_project_smp() + 1)
621 this->plugin = plugin;
624 ThresholdEngine::~ThresholdEngine()
628 void ThresholdEngine::process_packages(VFrame *data)
631 LoadServer::process_packages();
634 void ThresholdEngine::init_packages()
636 for(int i = 0; i < get_total_packages(); i++)
638 ThresholdPackage *package = (ThresholdPackage*)get_package(i);
639 package->start = data->get_h() * i / get_total_packages();
640 package->end = data->get_h() * (i + 1) / get_total_packages();
644 LoadClient* ThresholdEngine::new_client()
646 return (LoadClient*)new ThresholdUnit(this);
649 LoadPackage* ThresholdEngine::new_package()
651 return (LoadPackage*)new HistogramPackage;
666 RGBA::RGBA(int r, int g, int b, int a)
674 void RGBA::set(int r, int g, int b, int a)
682 void RGBA::set(int rgb, int alpha)
684 r = (rgb & 0xff0000) >> 16;
685 g = (rgb & 0xff00) >> 8;
690 int RGBA::getRGB() const
692 return r << 16 | g << 8 | b;
695 static void init_RGBA_keys(const char * prefix,
712 void RGBA::set_property(XMLTag & tag, const char * prefix) const
714 string r_s, g_s, b_s, a_s;
715 init_RGBA_keys(prefix, r_s, g_s, b_s, a_s);
717 tag.set_property(const_cast<char *>(r_s.c_str()), r);
718 tag.set_property(const_cast<char *>(g_s.c_str()), g);
719 tag.set_property(const_cast<char *>(b_s.c_str()), b);
720 tag.set_property(const_cast<char *>(a_s.c_str()), a);
723 RGBA RGBA::get_property(XMLTag & tag, const char * prefix) const
725 string r_s, g_s, b_s, a_s;
726 init_RGBA_keys(prefix, r_s, g_s, b_s, a_s);
728 return RGBA(tag.get_property(const_cast<char *>(r_s.c_str()), r),
729 tag.get_property(const_cast<char *>(g_s.c_str()), g),
730 tag.get_property(const_cast<char *>(b_s.c_str()), b),
731 tag.get_property(const_cast<char *>(a_s.c_str()), a));
734 bool operator==(const RGBA & a, const RGBA & b)
743 RGBA interpolate(const RGBA & prev_color, const double & prev_scale, const RGBA &next_color, const double & next_scale)
745 return RGBA(interpolate(prev_color.r, prev_scale, next_color.r, next_scale),
746 interpolate(prev_color.g, prev_scale, next_color.g, next_scale),
747 interpolate(prev_color.b, prev_scale, next_color.b, next_scale),
748 interpolate(prev_color.a, prev_scale, next_color.a, next_scale));