3 * Copyright (C) 2024 Adam Williams <broadcast at earthling dot net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 // draw a color swatch for a given brightness or saturation
23 // does not visualize but draws output to be processed
29 #include "bcdisplayinfo.h"
36 #include "playback3d.h"
43 REGISTER_PLUGIN(SwatchMain)
50 SwatchConfig::SwatchConfig()
52 brightness = MAX_VALUE;
53 saturation = MAX_VALUE;
59 int SwatchConfig::equivalent(SwatchConfig &that)
61 return brightness == that.brightness &&
62 saturation == that.saturation &&
63 fix_brightness == that.fix_brightness &&
64 angle == that.angle &&
65 draw_src == that.draw_src;
68 void SwatchConfig::copy_from(SwatchConfig &that)
70 brightness = that.brightness;
71 saturation = that.saturation;
72 fix_brightness = that.fix_brightness;
74 draw_src = that.draw_src;
77 void SwatchConfig::interpolate(SwatchConfig &prev,
83 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
84 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
87 this->brightness = (int)(prev.brightness * prev_scale + next.brightness * next_scale);
88 this->saturation = (int)(prev.saturation * prev_scale + next.saturation * next_scale);
89 this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale);
90 fix_brightness = prev.fix_brightness;
91 draw_src = prev.draw_src;
97 SwatchSlider::SwatchSlider(SwatchMain *plugin,
107 gui->get_w() - xS(10) - x,
108 gui->get_w() - xS(10) - x,
113 this->plugin = plugin;
114 this->output = output;
117 int SwatchSlider::handle_event ()
119 *output = get_value();
120 plugin->send_configure_change();
124 SwatchRadial::SwatchRadial(SwatchMain *plugin,
132 (plugin->config.fix_brightness && fix_brightness),
135 this->plugin = plugin;
137 this->fix_brightness = fix_brightness;
140 int SwatchRadial::handle_event()
142 plugin->config.fix_brightness = fix_brightness;
144 plugin->send_configure_change();
149 SwatchCheck::SwatchCheck(SwatchMain *plugin,
159 this->plugin = plugin;
160 this->output = output;
163 int SwatchCheck::handle_event()
165 *output = get_value();
166 plugin->send_configure_change();
173 SwatchWindow::SwatchWindow(SwatchMain *plugin)
174 : PluginClientWindow(plugin,
181 this->plugin = plugin;
184 SwatchWindow::~SwatchWindow()
188 void SwatchWindow::create_objects()
191 int x = margin, y = margin;
194 add_subwindow(brightness_title = new BC_Title(x, y, _("Brightness:")));
195 y += brightness_title->get_h() + margin;
196 add_subwindow (brightness = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.brightness));
197 y += brightness->get_h() + margin;
199 add_subwindow(saturation_title = new BC_Title(x, y, _("Saturation:")));
200 y += saturation_title->get_h() + margin;
201 add_subwindow (saturation = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.saturation));
202 y += saturation->get_h() + margin;
204 add_subwindow(title = new BC_Title(x, y, _("Angle:")));
205 y += title->get_h() + margin;
206 add_subwindow (angle = new SwatchSlider(plugin, this, x, y, -180, 180, &plugin->config.angle));
207 y += saturation->get_h() + margin;
209 add_subwindow(fix_brightness = new SwatchRadial(plugin,
213 _("Constant Brightness"),
215 y += fix_brightness->get_h() + margin;
216 add_subwindow(fix_saturation = new SwatchRadial(plugin,
220 _("Constant Saturation"),
222 y += fix_saturation->get_h() + margin;
225 add_subwindow(draw_src = new SwatchCheck(plugin,
229 &plugin->config.draw_src));
235 void SwatchWindow::update_fixed()
237 fix_brightness->update(plugin->config.fix_brightness);
238 fix_saturation->update(!plugin->config.fix_brightness);
239 if(plugin->config.fix_brightness)
241 saturation_title->set_color(BC_WindowBase::get_resources()->disabled_text_color);
242 brightness_title->set_color(BC_WindowBase::get_resources()->default_text_color);
243 saturation->disable();
244 brightness->enable();
248 saturation_title->set_color(BC_WindowBase::get_resources()->default_text_color);
249 brightness_title->set_color(BC_WindowBase::get_resources()->disabled_text_color);
250 saturation->enable();
251 brightness->disable();
256 SwatchMain::SwatchMain(PluginServer *server)
257 : PluginVClient(server)
259 need_reconfigure = 1;
265 SwatchMain::~SwatchMain()
267 if(engine) delete engine;
268 if(temp) delete temp;
269 if(src_temp) delete src_temp;
272 const char* SwatchMain::plugin_title() { return N_("Color Swatch"); }
273 int SwatchMain::is_realtime() { return 1; }
276 NEW_WINDOW_MACRO(SwatchMain, SwatchWindow)
278 LOAD_CONFIGURATION_MACRO(SwatchMain, SwatchConfig)
280 int SwatchMain::is_synthesis()
285 int SwatchMain::process_buffer(VFrame *frame,
286 int64_t start_position,
289 need_reconfigure |= load_configuration();
290 int use_opengl = get_use_opengl();
292 // have to draw output pixels out of order
293 if(config.draw_src) use_opengl = 0;
295 if(use_opengl) return run_opengl();
297 if(!engine) engine = new SwatchEngine(this,
298 get_project_smp() + 1,
299 get_project_smp() + 1);
303 src_temp = new VFrame(0,
307 frame->get_color_model(),
318 if(!temp) temp = new VFrame(0,
322 frame->get_color_model(),
327 engine->draw_pattern();
329 // draw the pattern on the output
330 frame->copy_from(temp);
331 // draw input on the pattern
335 //printf("SwatchMain::process_buffer %d %d\n", __LINE__, config.draw_src);
340 void SwatchMain::update_gui()
344 if(load_configuration())
346 ((SwatchWindow*)thread->window)->lock_window("SwatchMain::update_gui");
347 ((SwatchWindow*)thread->window)->brightness->update(config.brightness);
348 ((SwatchWindow*)thread->window)->saturation->update(config.saturation);
349 ((SwatchWindow*)thread->window)->angle->update(config.angle);
350 ((SwatchWindow*)thread->window)->draw_src->update(config.draw_src);
351 ((SwatchWindow*)thread->window)->update_fixed();
352 ((SwatchWindow*)thread->window)->unlock_window();
361 void SwatchMain::save_data(KeyFrame *keyframe)
365 // cause data to be stored directly in text
366 output.set_shared_output(keyframe->xbuf);
367 output.tag.set_title("SWATCH");
369 output.tag.set_property("BRIGHTNESS", config.brightness);
370 output.tag.set_property("SATURATION", config.saturation);
371 output.tag.set_property("ANGLE", config.angle);
372 output.tag.set_property("FIX_BRIGHTNESS", config.fix_brightness);
373 output.tag.set_property("DRAW_SRC", config.draw_src);
375 output.tag.set_title("/SWATCH");
377 output.append_newline();
378 output.terminate_string();
379 //printf("SwatchMain::save_data %d %s\n", __LINE__, output.string);
382 void SwatchMain::read_data(KeyFrame *keyframe)
386 input.set_shared_input(keyframe->xbuf);
388 while( !input.read_tag() )
390 if(input.tag.title_is("SWATCH"))
393 input.tag.get_property("BRIGHTNESS", config.brightness);
395 input.tag.get_property("SATURATION", config.saturation);
397 input.tag.get_property("ANGLE", config.angle);
398 config.fix_brightness =
399 input.tag.get_property("FIX_BRIGHTNESS", config.fix_brightness);
401 input.tag.get_property("DRAW_SRC", config.draw_src);
406 int SwatchMain::handle_opengl()
409 const char *head_frag =
410 "uniform mat3 yuv_to_rgb_matrix;\n"
411 "uniform mat3 rgb_to_yuv_matrix;\n"
412 "uniform float yminf;\n"
413 "uniform sampler2D tex;\n"
414 "uniform vec2 texture_extents;\n"
415 "uniform vec2 center_coord;\n"
416 "uniform float value;\n"
417 "uniform float saturation;\n"
418 "uniform float angle;\n"
419 "uniform bool fix_value;\n"
423 " vec2 out_coord = gl_TexCoord[0].st * texture_extents;\n"
424 " vec2 in_coord = vec2(out_coord.x - center_coord.x, out_coord.y - center_coord.y);\n"
425 " float max_s = center_coord.x;\n"
426 " if(center_coord.y < max_s) max_s = center_coord.y;\n"
429 " pixel.r = atan(in_coord.x, in_coord.y) / 2.0 / 3.14159 * 360.0 + angle; // hue\n"
430 " if(pixel.r < 0.0) pixel.r += 360.0;\n"
433 " pixel.g = length(vec2(in_coord.x, in_coord.y)) / max_s; // saturation\n"
434 " if(pixel.g > 1.0) pixel.g = 1.0; \n"
435 " pixel.b = value;\n"
439 " pixel.g = saturation;\n"
440 " pixel.b = length(vec2(in_coord.x, in_coord.y)) / max_s; // value\n"
441 " if(pixel.b > 1.0) pixel.b = 1.0; \n"
443 HSV_TO_RGB_FRAG("pixel");
445 static const char *put_yuv_frag =
446 RGB_TO_YUV_FRAG("pixel")
447 " gl_FragColor = pixel;\n"
450 static const char *put_rgb_frag =
451 " gl_FragColor = pixel;\n"
457 const char *shader_stack[5] = { 0, 0, 0, 0, 0 };
458 shader_stack[0] = head_frag;
459 if(BC_CModels::is_yuv(get_output()->get_color_model()))
460 shader_stack[1] = put_yuv_frag;
462 shader_stack[1] = put_rgb_frag;
464 get_output()->set_opengl_state(VFrame::TEXTURE);
465 get_output()->to_texture();
466 get_output()->enable_opengl();
467 get_output()->init_screen();
468 get_output()->bind_texture(0);
470 unsigned int frag = VFrame::make_shader(0,
478 float w = get_output()->get_w();
479 float h = get_output()->get_h();
480 float texture_w = get_output()->get_texture_w();
481 float texture_h = get_output()->get_texture_h();
482 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
483 glUniform2f(glGetUniformLocation(frag, "center_coord"),
486 glUniform2f(glGetUniformLocation(frag, "texture_extents"),
490 glUniform1f(glGetUniformLocation(frag, "value"), (float)config.brightness / MAX_VALUE);
491 glUniform1f(glGetUniformLocation(frag, "saturation"), (float)config.saturation / MAX_VALUE);
492 glUniform1f(glGetUniformLocation(frag, "angle"), (float)config.angle);
493 glUniform1i(glGetUniformLocation(frag, "fix_value"), config.fix_brightness);
494 if(BC_CModels::is_yuv(get_output()->get_color_model()))
498 get_output()->draw_texture();
500 get_output()->set_opengl_state(VFrame::SCREEN);
516 SwatchPackage::SwatchPackage()
524 SwatchUnit::SwatchUnit(SwatchEngine *server, SwatchMain *plugin)
527 this->plugin = plugin;
528 this->server = server;
534 #define CREATE_SWATCH(type, components, max, is_yuv) \
536 for(int i = pkg->y1; i < pkg->y2; i++) \
538 type *out_row = (type*)plugin->temp->get_rows()[i]; \
539 for(int j = 0; j < w; j++) \
541 float hue = atan2(j - center_x, i - center_y) * 360 / 2 / M_PI + angle; \
544 saturation = hypot(j - center_x, i - center_y) / max_s; \
545 if(saturation > 1) saturation = 1; \
549 value = hypot(j - center_x, i - center_y) / max_s; \
550 if(value > 1) value = 1; \
552 if(hue < 0) hue += 360; \
556 HSV::hsv_to_yuv(y, u, v, hue, saturation, value, max); \
564 HSV::hsv_to_rgb(r, g, b, hue, saturation, value); \
565 out_row[0] = (type)(r * max); \
566 out_row[1] = (type)(g * max); \
567 out_row[2] = (type)(b * max); \
571 if(components == 4) out_row[3] = max; \
572 out_row += components; \
578 #define DRAW_SRC(type, components, max, is_yuv) \
580 type **dst_rows = (type**)plugin->get_output()->get_rows(); \
581 for(int i = pkg->y1; i < pkg->y2; i++) \
583 type *src_row = (type*)plugin->src_temp->get_rows()[i]; \
584 for(int j = 0; j < w; j++) \
586 /* the source values */ \
591 YUV::yuv.yuv_to_rgb_f (r2, g2, b2, src_row[0], src_row[1], src_row[2]); \
598 r2 = (float)r / max; \
599 g2 = (float)g / max; \
600 b2 = (float)b / max; \
602 float hue, s, value; \
603 HSV::rgb_to_hsv(r2, g2, b2, hue, s, value); \
604 float h_rad = TO_RAD(hue - angle); \
605 /* get coordinate of color in output */ \
609 x_out = center_x + (int)(sin(h_rad) * s * max_s); \
610 y_out = center_y + (int)(cos(h_rad) * s * max_s); \
614 x_out = center_x + (int)(sin(h_rad) * value * max_s); \
615 y_out = center_y + (int)(cos(h_rad) * value * max_s); \
617 if(x_out >= 0 && x_out < w && y_out >= 0 && y_out < h) \
619 type *dst = dst_rows[y_out] + x_out * components; \
622 dst[0] = src_row[0]; \
623 dst[1] = src_row[1]; \
624 dst[2] = src_row[2]; \
634 src_row += components; \
639 #define DRAW_PATTERN_MODE 0
640 #define DRAW_SRC_MODE 1
642 void SwatchUnit::process_package(LoadPackage *package)
644 SwatchPackage *pkg = (SwatchPackage*)package;
645 int h = plugin->temp->get_h();
646 int w = plugin->temp->get_w();
647 int center_x = w / 2;
648 int center_y = h / 2;
649 int cmodel = plugin->temp->get_color_model();
650 // maximum saturation
651 float max_s = center_x;
652 if(center_y < max_s) max_s = center_y;
653 int fix_brightness = plugin->config.fix_brightness;
654 float saturation = (float)plugin->config.saturation / MAX_VALUE;
655 float value = (float)plugin->config.brightness / MAX_VALUE;
656 float angle = plugin->config.angle;
658 if(server->mode == DRAW_PATTERN_MODE)
663 CREATE_SWATCH(unsigned char, 3, 0xff, 0)
666 CREATE_SWATCH(unsigned char, 4, 0xff, 0)
669 CREATE_SWATCH(float, 3, 1.0, 0)
672 CREATE_SWATCH(float, 4, 1.0, 0)
675 CREATE_SWATCH(unsigned char, 3, 0xff, 1)
678 CREATE_SWATCH(unsigned char, 4, 0xff, 1)
687 DRAW_SRC(unsigned char, 3, 0xff, 0)
690 DRAW_SRC(unsigned char, 4, 0xff, 0)
693 DRAW_SRC(float, 3, 1.0, 0)
696 DRAW_SRC(float, 4, 1.0, 0)
699 DRAW_SRC(unsigned char, 3, 0xff, 1)
702 DRAW_SRC(unsigned char, 4, 0xff, 1)
713 SwatchEngine::SwatchEngine(SwatchMain *plugin,
716 : LoadServer(total_clients, total_packages)
718 this->plugin = plugin;
721 void SwatchEngine::draw_pattern()
723 mode = DRAW_PATTERN_MODE;
727 void SwatchEngine::draw_src()
729 mode = DRAW_SRC_MODE;
733 void SwatchEngine::init_packages()
735 for(int i = 0; i < get_total_packages(); i++)
737 SwatchPackage *package = (SwatchPackage*)get_package(i);
738 package->y1 = plugin->temp->get_h() *
740 get_total_packages();
741 package->y2 = plugin->temp->get_h() *
743 get_total_packages();
747 LoadClient* SwatchEngine::new_client()
749 return new SwatchUnit(this, plugin);
752 LoadPackage* SwatchEngine::new_package()
754 return new SwatchPackage;