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
23 #include "bcdisplayinfo.h"
27 #include "huesaturation.h"
30 #include "loadbalance.h"
32 #include "playback3d.h"
33 #include "pluginvclient.h"
42 REGISTER_PLUGIN(HueEffect)
48 HueConfig::HueConfig()
53 void HueConfig::reset(int clear)
56 case RESET_HUV : hue = 0;
58 case RESET_SAT : saturation = 0;
60 case RESET_VAL : value = 0;
64 hue = saturation = value = 0;
69 void HueConfig::copy_from(HueConfig &src)
72 saturation = src.saturation;
75 int HueConfig::equivalent(HueConfig &src)
77 return EQUIV(hue, src.hue) &&
78 EQUIV(saturation, src.saturation) &&
79 EQUIV(value, src.value);
81 void HueConfig::interpolate(HueConfig &prev,
87 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
88 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
90 this->hue = prev.hue * prev_scale + next.hue * next_scale;
91 this->saturation = prev.saturation * prev_scale + next.saturation * next_scale;
92 this->value = prev.value * prev_scale + next.value * next_scale;
102 HueSlider::HueSlider(HueEffect *plugin, int x, int y, int w)
112 this->plugin = plugin;
114 int HueSlider::handle_event()
116 plugin->config.hue = get_value();
117 plugin->send_configure_change();
127 SaturationSlider::SaturationSlider(HueEffect *plugin, int x, int y, int w)
133 (float)MINSATURATION,
134 (float)MAXSATURATION,
135 plugin->config.saturation)
137 this->plugin = plugin;
139 int SaturationSlider::handle_event()
141 plugin->config.saturation = get_value();
142 plugin->send_configure_change();
146 char* SaturationSlider::get_caption()
148 float fraction = ((float)plugin->config.saturation - MINSATURATION) /
150 sprintf(string, "%0.4f", fraction);
160 ValueSlider::ValueSlider(HueEffect *plugin, int x, int y, int w)
168 plugin->config.value)
170 this->plugin = plugin;
172 int ValueSlider::handle_event()
174 plugin->config.value = get_value();
175 plugin->send_configure_change();
179 char* ValueSlider::get_caption()
181 float fraction = ((float)plugin->config.value - MINVALUE) / MAXVALUE;
182 sprintf(string, "%0.4f", fraction);
187 HueReset::HueReset(HueEffect *plugin, HueWindow *gui, int x, int y)
188 : BC_GenericButton(x, y, _("Reset"))
190 this->plugin = plugin;
193 HueReset::~HueReset()
196 int HueReset::handle_event()
198 plugin->config.reset(RESET_ALL); // clear=0 ==> reset all
199 gui->update_gui(RESET_ALL);
200 plugin->send_configure_change();
205 HueSliderClr::HueSliderClr(HueEffect *plugin, HueWindow *gui, int x, int y, int w, int clear)
206 : BC_Button(x, y, w, plugin->get_theme()->get_image_set("reset_button"))
208 this->plugin = plugin;
212 HueSliderClr::~HueSliderClr()
215 int HueSliderClr::handle_event()
217 // clear==1 ==> Hue slider
218 // clear==2 ==> Saturation slider
219 // clear==3 ==> Value slider
220 plugin->config.reset(clear);
221 gui->update_gui(clear);
222 plugin->send_configure_change();
229 HueWindow::HueWindow(HueEffect *plugin)
230 : PluginClientWindow(plugin, xS(370), yS(140), xS(370), yS(140), 0)
232 this->plugin = plugin;
234 void HueWindow::create_objects()
236 int xs10 = xS(10), xs50 = xS(50), xs100 = xS(100), xs200 = xS(200);
237 int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40);
238 int x = xs10, y = ys10, x1 = xs100;
239 int x2 = 0; int clrBtn_w = xs50;
241 add_subwindow(new BC_Title(x, y, _("Hue:")));
242 add_subwindow(hue = new HueSlider(plugin, x1, y, xs200));
243 x2 = x1 + hue->get_w() + xs10;
244 add_subwindow(hueClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_HUV));
247 add_subwindow(new BC_Title(x, y, _("Saturation:")));
248 add_subwindow(saturation = new SaturationSlider(plugin, x1, y, xs200));
249 add_subwindow(satClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_SAT));
252 add_subwindow(new BC_Title(x, y, _("Value:")));
253 add_subwindow(value = new ValueSlider(plugin, x1, y, xs200));
254 add_subwindow(valClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_VAL));
257 add_subwindow(reset = new HueReset(plugin, this, x, y));
264 void HueWindow::update_gui(int clear)
267 case RESET_HUV : hue->update(plugin->config.hue);
269 case RESET_SAT : saturation->update(plugin->config.saturation);
271 case RESET_VAL : value->update(plugin->config.value);
275 hue->update(plugin->config.hue);
276 saturation->update(plugin->config.saturation);
277 value->update(plugin->config.value);
289 HueEngine::HueEngine(HueEffect *plugin, int cpus)
290 : LoadServer(cpus, cpus)
292 this->plugin = plugin;
294 void HueEngine::init_packages()
296 for(int i = 0; i < LoadServer::get_total_packages(); i++)
298 HuePackage *pkg = (HuePackage*)get_package(i);
299 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
300 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
303 LoadClient* HueEngine::new_client()
305 return new HueUnit(plugin, this);
307 LoadPackage* HueEngine::new_package()
309 return new HuePackage;
319 HuePackage::HuePackage()
324 HueUnit::HueUnit(HueEffect *plugin, HueEngine *server)
327 this->plugin = plugin;
336 #define HUESATURATION(type, max, components, use_yuv) \
338 float h_offset = plugin->config.hue; \
339 float s_offset = ((float)plugin->config.saturation - MINSATURATION) / MAXSATURATION; \
340 float v_offset = ((float)plugin->config.value - MINVALUE) / MAXVALUE; \
341 for(int i = pkg->row1; i < pkg->row2; i++) \
343 type* in_row = (type*)plugin->input->get_rows()[i]; \
344 type* out_row = (type*)plugin->output->get_rows()[i]; \
346 for(int j = 0; j < w; j++) \
355 y = (int)in_row[0]; \
356 u = (int)in_row[1]; \
357 v = (int)in_row[2]; \
359 YUV::yuv.yuv_to_rgb_16(r_i, g_i, b_i, y, u, v); \
361 YUV::yuv.yuv_to_rgb_8(r_i, g_i, b_i, y, u, v); \
362 HSV::rgb_to_hsv((float)r_i / max, \
371 r = (float)in_row[0] / max; \
372 g = (float)in_row[1] / max; \
373 b = (float)in_row[2] / max; \
374 HSV::rgb_to_hsv(r, g, b, h, s, va); \
382 if(h >= 360) h -= 360; \
383 if(h < 0) h += 360; \
384 if(sizeof(type) < 4) \
394 HSV::hsv_to_yuv(y, u, v, h, s, va, max); \
401 HSV::hsv_to_rgb(r, g, b, h, s, va); \
402 if(sizeof(type) < 4) \
407 out_row[0] = (type)CLIP(r, 0, max); \
408 out_row[1] = (type)CLIP(g, 0, max); \
409 out_row[2] = (type)CLIP(b, 0, max); \
413 out_row[0] = (type)r; \
414 out_row[1] = (type)g; \
415 out_row[2] = (type)b; \
419 in_row += components; \
420 out_row += components; \
426 void HueUnit::process_package(LoadPackage *package)
428 HuePackage *pkg = (HuePackage*)package;
429 int w = plugin->input->get_w();
431 switch(plugin->input->get_color_model())
434 HUESATURATION(unsigned char, 0xff, 3, 0)
438 HUESATURATION(float, 1, 3, 0)
442 HUESATURATION(unsigned char, 0xff, 3, 1)
446 HUESATURATION(uint16_t, 0xffff, 3, 0)
450 HUESATURATION(uint16_t, 0xffff, 3, 1)
454 HUESATURATION(float, 1, 4, 0)
458 HUESATURATION(unsigned char, 0xff, 4, 0)
462 HUESATURATION(unsigned char, 0xff, 4, 1)
465 case BC_RGBA16161616:
466 HUESATURATION(uint16_t, 0xffff, 4, 0)
469 case BC_YUVA16161616:
470 HUESATURATION(uint16_t, 0xffff, 4, 1)
481 HueEffect::HueEffect(PluginServer *server)
482 : PluginVClient(server)
487 HueEffect::~HueEffect()
490 if(engine) delete engine;
493 int HueEffect::process_buffer(VFrame *frame,
494 int64_t start_position,
497 load_configuration();
507 this->output = frame;
508 if(EQUIV(config.hue, 0) && EQUIV(config.saturation, 0) && EQUIV(config.value, 0))
520 if(!engine) engine = new HueEngine(this, PluginClient::smp + 1);
522 engine->process_packages();
527 const char* HueEffect::plugin_title() { return N_("Hue saturation"); }
528 int HueEffect::is_realtime() { return 1; }
530 NEW_WINDOW_MACRO(HueEffect, HueWindow)
531 LOAD_CONFIGURATION_MACRO(HueEffect, HueConfig)
534 void HueEffect::save_data(KeyFrame *keyframe)
537 output.set_shared_output(keyframe->xbuf);
538 output.tag.set_title("HUESATURATION");
539 output.tag.set_property("HUE", config.hue);
540 output.tag.set_property("SATURATION", config.saturation);
541 output.tag.set_property("VALUE", config.value);
543 output.tag.set_title("/HUESATURATION");
545 output.append_newline();
546 output.terminate_string();
548 void HueEffect::read_data(KeyFrame *keyframe)
551 input.set_shared_input(keyframe->xbuf);
552 while(!input.read_tag())
554 if(input.tag.title_is("HUESATURATION"))
556 config.hue = input.tag.get_property("HUE", config.hue);
557 config.saturation = input.tag.get_property("SATURATION", config.saturation);
558 config.value = input.tag.get_property("VALUE", config.value);
562 void HueEffect::update_gui()
566 ((HueWindow*)thread->window)->lock_window();
567 load_configuration();
568 ((HueWindow*)thread->window)->hue->update(config.hue);
569 ((HueWindow*)thread->window)->saturation->update(config.saturation);
570 ((HueWindow*)thread->window)->value->update(config.value);
571 ((HueWindow*)thread->window)->unlock_window();
575 int HueEffect::handle_opengl()
578 const char *yuv_saturation_frag =
579 "uniform sampler2D tex;\n"
580 "uniform float s_offset;\n"
581 "uniform float v_offset;\n"
584 " vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
585 " pixel.r *= v_offset;\n"
586 " pixel.gb -= vec2(0.5, 0.5);\n"
587 " pixel.g *= s_offset;\n"
588 " pixel.b *= s_offset;\n"
589 " pixel.gb += vec2(0.5, 0.5);\n"
590 " gl_FragColor = pixel;\n"
594 const char *yuv_frag =
595 "uniform sampler2D tex;\n"
596 "uniform float h_offset;\n"
597 "uniform float s_offset;\n"
598 "uniform float v_offset;\n"
601 " vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
602 YUV_TO_RGB_FRAG("pixel")
603 RGB_TO_HSV_FRAG("pixel")
604 " pixel.r += h_offset;\n"
605 " pixel.g *= s_offset;\n"
606 " pixel.b *= v_offset;\n"
607 " if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
608 " if(pixel.r < 0.0) pixel.r += 360.0;\n"
609 HSV_TO_RGB_FRAG("pixel")
610 RGB_TO_YUV_FRAG("pixel")
611 " gl_FragColor = pixel;\n"
614 const char *rgb_frag =
615 "uniform sampler2D tex;\n"
616 "uniform float h_offset;\n"
617 "uniform float s_offset;\n"
618 "uniform float v_offset;\n"
621 " vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
622 RGB_TO_HSV_FRAG("pixel")
623 " pixel.r += h_offset;\n"
624 " pixel.g *= s_offset;\n"
625 " pixel.b *= v_offset;\n"
626 " if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
627 " if(pixel.r < 0.0) pixel.r += 360.0;\n"
628 HSV_TO_RGB_FRAG("pixel")
629 " gl_FragColor = pixel;\n"
633 get_output()->to_texture();
634 get_output()->enable_opengl();
636 const char *shader_stack[16];
637 memset(shader_stack,0, sizeof(shader_stack));
638 int current_shader = 0;
640 int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
641 if( need_color_matrix ) shader_stack[current_shader++] = bc_gl_colors;
642 shader_stack[current_shader++] = !need_color_matrix ? rgb_frag :
643 EQUIV(config.hue, 0) ? yuv_saturation_frag: yuv_frag ;
645 shader_stack[current_shader] = 0;
646 unsigned int shader = VFrame::make_shader(shader_stack);
648 glUseProgram(shader);
649 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
650 glUniform1f(glGetUniformLocation(shader, "h_offset"), config.hue);
651 glUniform1f(glGetUniformLocation(shader, "s_offset"),
652 ((float)config.saturation - MINSATURATION) / MAXSATURATION);
653 glUniform1f(glGetUniformLocation(shader, "v_offset"),
654 ((float)config.value - MINVALUE) / MAXVALUE);
655 if( need_color_matrix ) BC_GL_COLORS(shader);
658 get_output()->init_screen();
659 get_output()->bind_texture(0);
660 get_output()->draw_texture();
662 get_output()->set_opengl_state(VFrame::SCREEN);