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;
101 saturation is stored from -100.00 to +100.00
102 saturation_slider goes from -100.00 to +100.00
103 saturation_caption goes from 0.000 to +2.000 (clear to +1.000)
104 saturation_text goes from -100.00 to +100.00
107 value is stored from -100.00 to +100.00
108 value_slider goes from -100.00 to +100.00
109 value_caption goes from 0.000 to +2.000 (clear to +1.000)
110 value_text goes from -100.00 to +100.00
113 HueText::HueText(HueEffect *plugin, HueWindow *gui, int x, int y)
114 : BC_TumbleTextBox(gui, plugin->config.hue,
115 (float)MINHUE, (float)MAXHUE, x, y, xS(60), 2)
118 this->plugin = plugin;
126 int HueText::handle_event()
128 float min = MINHUE, max = MAXHUE;
129 float output = atof(get_text());
130 if(output > max) output = max;
131 if(output < min) output = min;
132 plugin->config.hue = output;
133 gui->hue_slider->update(plugin->config.hue);
134 plugin->send_configure_change();
138 HueSlider::HueSlider(HueEffect *plugin, HueWindow *gui, int x, int y, int w)
139 : BC_FSlider(x, y, 0, w, w,
140 (float)MINHUE, (float)MAXHUE,
143 this->plugin = plugin;
145 enable_show_value(0); // Hide caption
147 int HueSlider::handle_event()
149 plugin->config.hue = get_value();
150 gui->hue_text->update(plugin->config.hue);
151 plugin->send_configure_change();
156 SaturationText::SaturationText(HueEffect *plugin, HueWindow *gui, int x, int y)
157 : BC_TumbleTextBox(gui, plugin->config.saturation,
158 (float)MINSATURATION, (float)MAXSATURATION, x, y, xS(60), 2)
161 this->plugin = plugin;
165 SaturationText::~SaturationText()
169 int SaturationText::handle_event()
171 float min = MINSATURATION, max = MAXSATURATION;
172 float output = atof(get_text());
173 if(output > max) output = max;
174 if(output < min) output = min;
175 plugin->config.saturation = output;
176 gui->sat_slider->update(plugin->config.saturation);
177 plugin->send_configure_change();
181 SaturationSlider::SaturationSlider(HueEffect *plugin, HueWindow *gui, int x, int y, int w)
182 : BC_FSlider(x, y, 0, w, w,
183 (float)MINSATURATION, (float)MAXSATURATION,
184 plugin->config.saturation)
186 this->plugin = plugin;
188 enable_show_value(0); // Hide caption
190 int SaturationSlider::handle_event()
192 plugin->config.saturation = get_value();
193 gui->sat_text->update(plugin->config.saturation);
194 plugin->send_configure_change();
198 char* SaturationSlider::get_caption()
200 float fraction = ((float)plugin->config.saturation - MINSATURATION) /
202 sprintf(string, "%0.4f", fraction);
207 ValueText::ValueText(HueEffect *plugin, HueWindow *gui, int x, int y)
208 : BC_TumbleTextBox(gui, plugin->config.value,
209 (float)MINVALUE, (float)MAXVALUE, x, y, xS(60), 2)
212 this->plugin = plugin;
216 ValueText::~ValueText()
220 int ValueText::handle_event()
222 float min = MINVALUE, max = MAXVALUE;
223 float output = atof(get_text());
224 if(output > max) output = max;
225 if(output < min) output = min;
226 plugin->config.value = output;
227 gui->value_slider->update(plugin->config.value);
228 plugin->send_configure_change();
232 ValueSlider::ValueSlider(HueEffect *plugin, HueWindow *gui, int x, int y, int w)
233 : BC_FSlider(x, y, 0, w, w,
234 (float)MINVALUE, (float)MAXVALUE,
235 plugin->config.value)
237 this->plugin = plugin;
239 enable_show_value(0); // Hide caption
241 int ValueSlider::handle_event()
243 plugin->config.value = get_value();
244 gui->value_text->update(plugin->config.value);
245 plugin->send_configure_change();
249 char* ValueSlider::get_caption()
251 float fraction = ((float)plugin->config.value - MINVALUE) / MAXVALUE;
252 sprintf(string, "%0.4f", fraction);
257 HueReset::HueReset(HueEffect *plugin, HueWindow *gui, int x, int y)
258 : BC_GenericButton(x, y, _("Reset"))
260 this->plugin = plugin;
263 HueReset::~HueReset()
266 int HueReset::handle_event()
268 plugin->config.reset(RESET_ALL); // clear=0 ==> reset all
269 gui->update_gui(RESET_ALL);
270 plugin->send_configure_change();
275 HueClr::HueClr(HueEffect *plugin, HueWindow *gui, int x, int y, int clear)
276 : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
278 this->plugin = plugin;
285 int HueClr::handle_event()
288 // clear==2 ==> Saturation
289 // clear==3 ==> Value
290 plugin->config.reset(clear);
291 gui->update_gui(clear);
292 plugin->send_configure_change();
299 HueWindow::HueWindow(HueEffect *plugin)
300 : PluginClientWindow(plugin, xS(420), yS(160), xS(420), yS(160), 0)
302 this->plugin = plugin;
304 void HueWindow::create_objects()
306 int xs10 = xS(10), xs200 = xS(200);
307 int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40);
308 int x = xs10, y = ys10;
309 int x2 = xS(80), x3 = xS(180);
310 int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
316 add_subwindow(new BC_Title(x, y, _("Hue:")));
317 hue_text = new HueText(plugin, this, (x + x2), y);
318 hue_text->create_objects();
319 add_subwindow(hue_slider = new HueSlider(plugin, this, x3, y, xs200));
320 clr_x = x3 + hue_slider->get_w() + x;
321 add_subwindow(hue_clr = new HueClr(plugin, this, clr_x, y, RESET_HUV));
325 add_subwindow(new BC_Title(x, y, _("Saturation:")));
326 sat_text = new SaturationText(plugin, this, (x + x2), y);
327 sat_text->create_objects();
328 add_subwindow(sat_slider = new SaturationSlider(plugin, this, x3, y, xs200));
329 add_subwindow(sat_clr = new HueClr(plugin, this, clr_x, y, RESET_SAT));
333 add_subwindow(new BC_Title(x, y, _("Value:")));
334 value_text = new ValueText(plugin, this, (x + x2), y);
335 value_text->create_objects();
336 add_subwindow(value_slider = new ValueSlider(plugin, this, x3, y, xs200));
337 add_subwindow(value_clr = new HueClr(plugin, this, clr_x, y, RESET_VAL));
341 add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
343 add_subwindow(reset = new HueReset(plugin, this, x, y));
350 void HueWindow::update_gui(int clear)
354 hue_text->update(plugin->config.hue);
355 hue_slider->update(plugin->config.hue);
358 sat_text->update(plugin->config.saturation);
359 sat_slider->update(plugin->config.saturation);
362 value_text->update(plugin->config.value);
363 value_slider->update(plugin->config.value);
367 hue_text->update(plugin->config.hue);
368 hue_slider->update(plugin->config.hue);
369 sat_text->update(plugin->config.saturation);
370 sat_slider->update(plugin->config.saturation);
371 value_text->update(plugin->config.value);
372 value_slider->update(plugin->config.value);
384 HueEngine::HueEngine(HueEffect *plugin, int cpus)
385 : LoadServer(cpus, cpus)
387 this->plugin = plugin;
389 void HueEngine::init_packages()
391 for(int i = 0; i < LoadServer::get_total_packages(); i++)
393 HuePackage *pkg = (HuePackage*)get_package(i);
394 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
395 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
398 LoadClient* HueEngine::new_client()
400 return new HueUnit(plugin, this);
402 LoadPackage* HueEngine::new_package()
404 return new HuePackage;
414 HuePackage::HuePackage()
419 HueUnit::HueUnit(HueEffect *plugin, HueEngine *server)
422 this->plugin = plugin;
431 #define HUESATURATION(type, max, components, use_yuv) \
433 float h_offset = plugin->config.hue; \
434 float s_offset = ((float)plugin->config.saturation - MINSATURATION) / MAXSATURATION; \
435 float v_offset = ((float)plugin->config.value - MINVALUE) / MAXVALUE; \
436 for(int i = pkg->row1; i < pkg->row2; i++) \
438 type* in_row = (type*)plugin->input->get_rows()[i]; \
439 type* out_row = (type*)plugin->output->get_rows()[i]; \
441 for(int j = 0; j < w; j++) \
450 y = (int)in_row[0]; \
451 u = (int)in_row[1]; \
452 v = (int)in_row[2]; \
454 YUV::yuv.yuv_to_rgb_16(r_i, g_i, b_i, y, u, v); \
456 YUV::yuv.yuv_to_rgb_8(r_i, g_i, b_i, y, u, v); \
457 HSV::rgb_to_hsv((float)r_i / max, \
466 r = (float)in_row[0] / max; \
467 g = (float)in_row[1] / max; \
468 b = (float)in_row[2] / max; \
469 HSV::rgb_to_hsv(r, g, b, h, s, va); \
477 if(h >= 360) h -= 360; \
478 if(h < 0) h += 360; \
479 if(sizeof(type) < 4) \
489 HSV::hsv_to_yuv(y, u, v, h, s, va, max); \
496 HSV::hsv_to_rgb(r, g, b, h, s, va); \
497 if(sizeof(type) < 4) \
502 out_row[0] = (type)CLIP(r, 0, max); \
503 out_row[1] = (type)CLIP(g, 0, max); \
504 out_row[2] = (type)CLIP(b, 0, max); \
508 out_row[0] = (type)r; \
509 out_row[1] = (type)g; \
510 out_row[2] = (type)b; \
514 in_row += components; \
515 out_row += components; \
521 void HueUnit::process_package(LoadPackage *package)
523 HuePackage *pkg = (HuePackage*)package;
524 int w = plugin->input->get_w();
526 switch(plugin->input->get_color_model())
529 HUESATURATION(unsigned char, 0xff, 3, 0)
533 HUESATURATION(float, 1, 3, 0)
537 HUESATURATION(unsigned char, 0xff, 3, 1)
541 HUESATURATION(uint16_t, 0xffff, 3, 0)
545 HUESATURATION(uint16_t, 0xffff, 3, 1)
549 HUESATURATION(float, 1, 4, 0)
553 HUESATURATION(unsigned char, 0xff, 4, 0)
557 HUESATURATION(unsigned char, 0xff, 4, 1)
560 case BC_RGBA16161616:
561 HUESATURATION(uint16_t, 0xffff, 4, 0)
564 case BC_YUVA16161616:
565 HUESATURATION(uint16_t, 0xffff, 4, 1)
576 HueEffect::HueEffect(PluginServer *server)
577 : PluginVClient(server)
582 HueEffect::~HueEffect()
585 if(engine) delete engine;
588 int HueEffect::process_buffer(VFrame *frame,
589 int64_t start_position,
592 load_configuration();
602 this->output = frame;
603 if(EQUIV(config.hue, 0) && EQUIV(config.saturation, 0) && EQUIV(config.value, 0))
615 if(!engine) engine = new HueEngine(this, PluginClient::smp + 1);
617 engine->process_packages();
622 const char* HueEffect::plugin_title() { return N_("Hue saturation"); }
623 int HueEffect::is_realtime() { return 1; }
625 NEW_WINDOW_MACRO(HueEffect, HueWindow)
626 LOAD_CONFIGURATION_MACRO(HueEffect, HueConfig)
629 void HueEffect::save_data(KeyFrame *keyframe)
632 output.set_shared_output(keyframe->xbuf);
633 output.tag.set_title("HUESATURATION");
634 output.tag.set_property("HUE", config.hue);
635 output.tag.set_property("SATURATION", config.saturation);
636 output.tag.set_property("VALUE", config.value);
638 output.tag.set_title("/HUESATURATION");
640 output.append_newline();
641 output.terminate_string();
643 void HueEffect::read_data(KeyFrame *keyframe)
646 input.set_shared_input(keyframe->xbuf);
647 while(!input.read_tag())
649 if(input.tag.title_is("HUESATURATION"))
651 config.hue = input.tag.get_property("HUE", config.hue);
652 config.saturation = input.tag.get_property("SATURATION", config.saturation);
653 config.value = input.tag.get_property("VALUE", config.value);
657 void HueEffect::update_gui()
661 ((HueWindow*)thread->window)->lock_window();
662 load_configuration();
663 ((HueWindow*)thread->window)->hue_text->update(config.hue);
664 ((HueWindow*)thread->window)->hue_slider->update(config.hue);
665 ((HueWindow*)thread->window)->sat_text->update(config.saturation);
666 ((HueWindow*)thread->window)->sat_slider->update(config.saturation);
667 ((HueWindow*)thread->window)->value_text->update(config.value);
668 ((HueWindow*)thread->window)->value_slider->update(config.value);
669 ((HueWindow*)thread->window)->unlock_window();
673 int HueEffect::handle_opengl()
676 const char *yuv_saturation_frag =
677 "uniform sampler2D tex;\n"
678 "uniform float s_offset;\n"
679 "uniform float v_offset;\n"
682 " vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
683 " pixel.r *= v_offset;\n"
684 " pixel.gb -= vec2(0.5, 0.5);\n"
685 " pixel.g *= s_offset;\n"
686 " pixel.b *= s_offset;\n"
687 " pixel.gb += vec2(0.5, 0.5);\n"
688 " gl_FragColor = pixel;\n"
692 const char *yuv_frag =
693 "uniform sampler2D tex;\n"
694 "uniform float h_offset;\n"
695 "uniform float s_offset;\n"
696 "uniform float v_offset;\n"
699 " vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
700 YUV_TO_RGB_FRAG("pixel")
701 RGB_TO_HSV_FRAG("pixel")
702 " pixel.r += h_offset;\n"
703 " pixel.g *= s_offset;\n"
704 " pixel.b *= v_offset;\n"
705 " if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
706 " if(pixel.r < 0.0) pixel.r += 360.0;\n"
707 HSV_TO_RGB_FRAG("pixel")
708 RGB_TO_YUV_FRAG("pixel")
709 " gl_FragColor = pixel;\n"
712 const char *rgb_frag =
713 "uniform sampler2D tex;\n"
714 "uniform float h_offset;\n"
715 "uniform float s_offset;\n"
716 "uniform float v_offset;\n"
719 " vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
720 RGB_TO_HSV_FRAG("pixel")
721 " pixel.r += h_offset;\n"
722 " pixel.g *= s_offset;\n"
723 " pixel.b *= v_offset;\n"
724 " if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
725 " if(pixel.r < 0.0) pixel.r += 360.0;\n"
726 HSV_TO_RGB_FRAG("pixel")
727 " gl_FragColor = pixel;\n"
731 get_output()->to_texture();
732 get_output()->enable_opengl();
734 const char *shader_stack[16];
735 memset(shader_stack,0, sizeof(shader_stack));
736 int current_shader = 0;
738 int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
739 if( need_color_matrix ) shader_stack[current_shader++] = bc_gl_colors;
740 shader_stack[current_shader++] = !need_color_matrix ? rgb_frag :
741 EQUIV(config.hue, 0) ? yuv_saturation_frag: yuv_frag ;
743 shader_stack[current_shader] = 0;
744 unsigned int shader = VFrame::make_shader(shader_stack);
746 glUseProgram(shader);
747 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
748 glUniform1f(glGetUniformLocation(shader, "h_offset"), config.hue);
749 glUniform1f(glGetUniformLocation(shader, "s_offset"),
750 ((float)config.saturation - MINSATURATION) / MAXSATURATION);
751 glUniform1f(glGetUniformLocation(shader, "v_offset"),
752 ((float)config.value - MINVALUE) / MAXVALUE);
753 if( need_color_matrix ) BC_GL_COLORS(shader);
756 get_output()->init_screen();
757 get_output()->bind_texture(0);
758 get_output()->draw_texture();
760 get_output()->set_opengl_state(VFrame::SCREEN);