X-Git-Url: https://cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fswatch%2Fswatch.C;fp=cinelerra-5.1%2Fplugins%2Fswatch%2Fswatch.C;h=2e9142eba8a2d3629b61db5ac57fe9506c7b93cc;hb=5dd11b748e5409f8376376f51d0a18d111cbcfdc;hp=0000000000000000000000000000000000000000;hpb=30cad318c058b80002aea28a0968c56485635c7b;p=goodguy%2Fcinelerra.git diff --git a/cinelerra-5.1/plugins/swatch/swatch.C b/cinelerra-5.1/plugins/swatch/swatch.C new file mode 100644 index 00000000..2e9142eb --- /dev/null +++ b/cinelerra-5.1/plugins/swatch/swatch.C @@ -0,0 +1,760 @@ +/* + * CINELERRA + * Copyright (C) 2024 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +// draw a color swatch for a given brightness or saturation +// does not visualize but draws output to be processed + +#include +#include +#include + +#include "bcdisplayinfo.h" +#include "bccolors.h" +#include "clip.h" +#include "bchash.h" +#include "filexml.h" +#include "keyframe.h" +#include "language.h" +#include "playback3d.h" +#include "swatch.h" +#include "vframe.h" + + + + +REGISTER_PLUGIN(SwatchMain) + + +#define MAX_VALUE 100 + + + +SwatchConfig::SwatchConfig() +{ + brightness = MAX_VALUE; + saturation = MAX_VALUE; + fix_brightness = 0; + angle = 0; + draw_src = 0; +} + +int SwatchConfig::equivalent(SwatchConfig &that) +{ + return brightness == that.brightness && + saturation == that.saturation && + fix_brightness == that.fix_brightness && + angle == that.angle && + draw_src == that.draw_src; +} + +void SwatchConfig::copy_from(SwatchConfig &that) +{ + brightness = that.brightness; + saturation = that.saturation; + fix_brightness = that.fix_brightness; + angle = that.angle; + draw_src = that.draw_src; +} + +void SwatchConfig::interpolate(SwatchConfig &prev, + SwatchConfig &next, + long prev_frame, + long next_frame, + long current_frame) +{ + double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame); + double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame); + + + this->brightness = (int)(prev.brightness * prev_scale + next.brightness * next_scale); + this->saturation = (int)(prev.saturation * prev_scale + next.saturation * next_scale); + this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale); + fix_brightness = prev.fix_brightness; + draw_src = prev.draw_src; +} + + + + +SwatchSlider::SwatchSlider(SwatchMain *plugin, + SwatchWindow *gui, + int x, + int y, + int min, + int max, + int *output) + : BC_ISlider(x, + y, + 0, + gui->get_w() - xS(10) - x, + gui->get_w() - xS(10) - x, + min, + max, + *output) +{ + this->plugin = plugin; + this->output = output; +} + +int SwatchSlider::handle_event () +{ + *output = get_value(); + plugin->send_configure_change(); + return 1; +} + +SwatchRadial::SwatchRadial(SwatchMain *plugin, + SwatchWindow *gui, + int x, + int y, + const char *text, + int fix_brightness) + : BC_Radial(x, + y, + (plugin->config.fix_brightness && fix_brightness), + text) +{ + this->plugin = plugin; + this->gui = gui; + this->fix_brightness = fix_brightness; +} + +int SwatchRadial::handle_event() +{ + plugin->config.fix_brightness = fix_brightness; + gui->update_fixed(); + plugin->send_configure_change(); + return 1; +} + + +SwatchCheck::SwatchCheck(SwatchMain *plugin, + int x, + int y, + const char *text, + int *output) + : BC_CheckBox(x, + y, + *output, + text) +{ + this->plugin = plugin; + this->output = output; +} + +int SwatchCheck::handle_event() +{ + *output = get_value(); + plugin->send_configure_change(); + return 1; +} + + + + +SwatchWindow::SwatchWindow(SwatchMain *plugin) + : PluginClientWindow(plugin, + xS(350), + yS(300), + xS(350), + yS(300), + 0) +{ + this->plugin = plugin; +} + +SwatchWindow::~SwatchWindow() +{ +} + +void SwatchWindow::create_objects() +{ + int margin = yS(10); + int x = margin, y = margin; + BC_Title *title; + + add_subwindow(brightness_title = new BC_Title(x, y, _("Brightness:"))); + y += brightness_title->get_h() + margin; + add_subwindow (brightness = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.brightness)); + y += brightness->get_h() + margin; + + add_subwindow(saturation_title = new BC_Title(x, y, _("Saturation:"))); + y += saturation_title->get_h() + margin; + add_subwindow (saturation = new SwatchSlider(plugin, this, x, y, 0, MAX_VALUE, &plugin->config.saturation)); + y += saturation->get_h() + margin; + + add_subwindow(title = new BC_Title(x, y, _("Angle:"))); + y += title->get_h() + margin; + add_subwindow (angle = new SwatchSlider(plugin, this, x, y, -180, 180, &plugin->config.angle)); + y += saturation->get_h() + margin; + + add_subwindow(fix_brightness = new SwatchRadial(plugin, + this, + x, + y, + _("Constant Brightness"), + 1)); + y += fix_brightness->get_h() + margin; + add_subwindow(fix_saturation = new SwatchRadial(plugin, + this, + x, + y, + _("Constant Saturation"), + 0)); + y += fix_saturation->get_h() + margin; + update_fixed(); + + add_subwindow(draw_src = new SwatchCheck(plugin, + x, + y, + _("Draw source"), + &plugin->config.draw_src)); + + + show_window(); +} + +void SwatchWindow::update_fixed() +{ + fix_brightness->update(plugin->config.fix_brightness); + fix_saturation->update(!plugin->config.fix_brightness); + if(plugin->config.fix_brightness) + { + saturation_title->set_color(BC_WindowBase::get_resources()->disabled_text_color); + brightness_title->set_color(BC_WindowBase::get_resources()->default_text_color); + saturation->disable(); + brightness->enable(); + } + else + { + saturation_title->set_color(BC_WindowBase::get_resources()->default_text_color); + brightness_title->set_color(BC_WindowBase::get_resources()->disabled_text_color); + saturation->enable(); + brightness->disable(); + } +} + + +SwatchMain::SwatchMain(PluginServer *server) + : PluginVClient(server) +{ + need_reconfigure = 1; + engine = 0; + temp = 0; + src_temp = 0; +} + +SwatchMain::~SwatchMain() +{ + if(engine) delete engine; + if(temp) delete temp; + if(src_temp) delete src_temp; +} + +const char* SwatchMain::plugin_title() { return N_("Color Swatch"); } +int SwatchMain::is_realtime() { return 1; } + + +NEW_WINDOW_MACRO(SwatchMain, SwatchWindow) + +LOAD_CONFIGURATION_MACRO(SwatchMain, SwatchConfig) + +int SwatchMain::is_synthesis() +{ + return 1; +} + +int SwatchMain::process_buffer(VFrame *frame, + int64_t start_position, + double frame_rate) +{ + need_reconfigure |= load_configuration(); + int use_opengl = get_use_opengl(); + +// have to draw output pixels out of order + if(config.draw_src) use_opengl = 0; + + if(use_opengl) return run_opengl(); + + if(!engine) engine = new SwatchEngine(this, + get_project_smp() + 1, + get_project_smp() + 1); + if(config.draw_src) + { + if(!src_temp) + src_temp = new VFrame(0, + -1, + frame->get_w(), + frame->get_h(), + frame->get_color_model(), + -1); + + read_frame(src_temp, + 0, + start_position, + frame_rate, + use_opengl); + } + + + if(!temp) temp = new VFrame(0, + -1, + frame->get_w(), + frame->get_h(), + frame->get_color_model(), + -1); + +// draw pattern once + if(need_reconfigure) + engine->draw_pattern(); + +// draw the pattern on the output + frame->copy_from(temp); +// draw input on the pattern + if(config.draw_src) + engine->draw_src(); + +//printf("SwatchMain::process_buffer %d %d\n", __LINE__, config.draw_src); + return 0; +} + + +void SwatchMain::update_gui() +{ + if(thread) + { + if(load_configuration()) + { + ((SwatchWindow*)thread->window)->lock_window("SwatchMain::update_gui"); + ((SwatchWindow*)thread->window)->brightness->update(config.brightness); + ((SwatchWindow*)thread->window)->saturation->update(config.saturation); + ((SwatchWindow*)thread->window)->angle->update(config.angle); + ((SwatchWindow*)thread->window)->draw_src->update(config.draw_src); + ((SwatchWindow*)thread->window)->update_fixed(); + ((SwatchWindow*)thread->window)->unlock_window(); + } + } +} + + + + + +void SwatchMain::save_data(KeyFrame *keyframe) +{ + FileXML output; + +// cause data to be stored directly in text + output.set_shared_output(keyframe->xbuf); + output.tag.set_title("SWATCH"); + + output.tag.set_property("BRIGHTNESS", config.brightness); + output.tag.set_property("SATURATION", config.saturation); + output.tag.set_property("ANGLE", config.angle); + output.tag.set_property("FIX_BRIGHTNESS", config.fix_brightness); + output.tag.set_property("DRAW_SRC", config.draw_src); + output.append_tag(); + output.tag.set_title("/SWATCH"); + output.append_tag(); + output.append_newline(); + output.terminate_string(); +//printf("SwatchMain::save_data %d %s\n", __LINE__, output.string); +} + +void SwatchMain::read_data(KeyFrame *keyframe) +{ + FileXML input; + + input.set_shared_input(keyframe->xbuf); + + while( !input.read_tag() ) + { + if(input.tag.title_is("SWATCH")) + { + config.brightness = + input.tag.get_property("BRIGHTNESS", config.brightness); + config.saturation = + input.tag.get_property("SATURATION", config.saturation); + config.angle = + input.tag.get_property("ANGLE", config.angle); + config.fix_brightness = + input.tag.get_property("FIX_BRIGHTNESS", config.fix_brightness); + config.draw_src = + input.tag.get_property("DRAW_SRC", config.draw_src); + } + } +} + +int SwatchMain::handle_opengl() +{ +#ifdef HAVE_GL + const char *head_frag = + "uniform mat3 yuv_to_rgb_matrix;\n" + "uniform mat3 rgb_to_yuv_matrix;\n" + "uniform float yminf;\n" + "uniform sampler2D tex;\n" + "uniform vec2 texture_extents;\n" + "uniform vec2 center_coord;\n" + "uniform float value;\n" + "uniform float saturation;\n" + "uniform float angle;\n" + "uniform bool fix_value;\n" + "\n" + "void main()\n" + "{\n" + " vec2 out_coord = gl_TexCoord[0].st * texture_extents;\n" + " vec2 in_coord = vec2(out_coord.x - center_coord.x, out_coord.y - center_coord.y);\n" + " float max_s = center_coord.x;\n" + " if(center_coord.y < max_s) max_s = center_coord.y;\n" + " vec4 pixel;\n" + " pixel.a = 1.0;\n" + " pixel.r = atan(in_coord.x, in_coord.y) / 2.0 / 3.14159 * 360.0 + angle; // hue\n" + " if(pixel.r < 0.0) pixel.r += 360.0;\n" + " if(fix_value)\n" + " {\n" + " pixel.g = length(vec2(in_coord.x, in_coord.y)) / max_s; // saturation\n" + " if(pixel.g > 1.0) pixel.g = 1.0; \n" + " pixel.b = value;\n" + " }\n" + " else\n" + " {\n" + " pixel.g = saturation;\n" + " pixel.b = length(vec2(in_coord.x, in_coord.y)) / max_s; // value\n" + " if(pixel.b > 1.0) pixel.b = 1.0; \n" + " }\n" + HSV_TO_RGB_FRAG("pixel"); + + static const char *put_yuv_frag = + RGB_TO_YUV_FRAG("pixel") + " gl_FragColor = pixel;\n" + "}\n"; + + static const char *put_rgb_frag = + " gl_FragColor = pixel;\n" + "}\n"; + + + + + const char *shader_stack[5] = { 0, 0, 0, 0, 0 }; + shader_stack[0] = head_frag; + if(BC_CModels::is_yuv(get_output()->get_color_model())) + shader_stack[1] = put_yuv_frag; + else + shader_stack[1] = put_rgb_frag; + + get_output()->set_opengl_state(VFrame::TEXTURE); + get_output()->to_texture(); + get_output()->enable_opengl(); + get_output()->init_screen(); + get_output()->bind_texture(0); + + unsigned int frag = VFrame::make_shader(0, + shader_stack[0], + shader_stack[1], + 0); + + if(frag) + { + glUseProgram(frag); + float w = get_output()->get_w(); + float h = get_output()->get_h(); + float texture_w = get_output()->get_texture_w(); + float texture_h = get_output()->get_texture_h(); + glUniform1i(glGetUniformLocation(frag, "tex"), 0); + glUniform2f(glGetUniformLocation(frag, "center_coord"), + (GLfloat)w / 2, + (GLfloat)h / 2); + glUniform2f(glGetUniformLocation(frag, "texture_extents"), + (GLfloat)texture_w, + (GLfloat)texture_h); + + glUniform1f(glGetUniformLocation(frag, "value"), (float)config.brightness / MAX_VALUE); + glUniform1f(glGetUniformLocation(frag, "saturation"), (float)config.saturation / MAX_VALUE); + glUniform1f(glGetUniformLocation(frag, "angle"), (float)config.angle); + glUniform1i(glGetUniformLocation(frag, "fix_value"), config.fix_brightness); + if(BC_CModels::is_yuv(get_output()->get_color_model())) + BC_GL_COLORS(frag); + } + + get_output()->draw_texture(); + glUseProgram(0); + get_output()->set_opengl_state(VFrame::SCREEN); + +#endif + return 0; +} + + + + + + + + + + + +SwatchPackage::SwatchPackage() + : LoadPackage() +{ +} + + + + +SwatchUnit::SwatchUnit(SwatchEngine *server, SwatchMain *plugin) + : LoadClient(server) +{ + this->plugin = plugin; + this->server = server; +} + + + + +#define CREATE_SWATCH(type, components, max, is_yuv) \ +{ \ + for(int i = pkg->y1; i < pkg->y2; i++) \ + { \ + type *out_row = (type*)plugin->temp->get_rows()[i]; \ + for(int j = 0; j < w; j++) \ + { \ + float hue = atan2(j - center_x, i - center_y) * 360 / 2 / M_PI + angle; \ + if(fix_brightness) \ + { \ + saturation = hypot(j - center_x, i - center_y) / max_s; \ + if(saturation > 1) saturation = 1; \ + } \ + else \ + { \ + value = hypot(j - center_x, i - center_y) / max_s; \ + if(value > 1) value = 1; \ + } \ + if(hue < 0) hue += 360; \ + if(is_yuv) \ + { \ + int y, u, v; \ + HSV::hsv_to_yuv(y, u, v, hue, saturation, value, max); \ + out_row[0] = y; \ + out_row[1] = u; \ + out_row[2] = v; \ + } \ + else \ + { \ + float r, g, b; \ + HSV::hsv_to_rgb(r, g, b, hue, saturation, value); \ + out_row[0] = (type)(r * max); \ + out_row[1] = (type)(g * max); \ + out_row[2] = (type)(b * max); \ + } \ + \ +/* the alpha */ \ + if(components == 4) out_row[3] = max; \ + out_row += components; \ + } \ + } \ +} + + +#define DRAW_SRC(type, components, max, is_yuv) \ +{ \ + type **dst_rows = (type**)plugin->get_output()->get_rows(); \ + for(int i = pkg->y1; i < pkg->y2; i++) \ + { \ + type *src_row = (type*)plugin->src_temp->get_rows()[i]; \ + for(int j = 0; j < w; j++) \ + { \ +/* the source values */ \ + type r, g, b; \ + float r2, g2, b2; \ + if(is_yuv) \ + { \ + YUV::yuv.yuv_to_rgb_f (r2, g2, b2, src_row[0], src_row[1], src_row[2]); \ + } \ + else \ + { \ + r = src_row[0]; \ + g = src_row[1]; \ + b = src_row[2]; \ + r2 = (float)r / max; \ + g2 = (float)g / max; \ + b2 = (float)b / max; \ + } \ + float hue, s, value; \ + HSV::rgb_to_hsv(r2, g2, b2, hue, s, value); \ + float h_rad = TO_RAD(hue - angle); \ +/* get coordinate of color in output */ \ + int x_out, y_out; \ + if(fix_brightness) \ + { \ + x_out = center_x + (int)(sin(h_rad) * s * max_s); \ + y_out = center_y + (int)(cos(h_rad) * s * max_s); \ + } \ + else \ + { \ + x_out = center_x + (int)(sin(h_rad) * value * max_s); \ + y_out = center_y + (int)(cos(h_rad) * value * max_s); \ + } \ + if(x_out >= 0 && x_out < w && y_out >= 0 && y_out < h) \ + { \ + type *dst = dst_rows[y_out] + x_out * components; \ + if(is_yuv) \ + { \ + dst[0] = src_row[0]; \ + dst[1] = src_row[1]; \ + dst[2] = src_row[2]; \ + } \ + else \ + { \ + dst[0] = r; \ + dst[1] = g; \ + dst[2] = b; \ + } \ + } \ + \ + src_row += components; \ + } \ + } \ +} + +#define DRAW_PATTERN_MODE 0 +#define DRAW_SRC_MODE 1 + +void SwatchUnit::process_package(LoadPackage *package) +{ + SwatchPackage *pkg = (SwatchPackage*)package; + int h = plugin->temp->get_h(); + int w = plugin->temp->get_w(); + int center_x = w / 2; + int center_y = h / 2; + int cmodel = plugin->temp->get_color_model(); +// maximum saturation + float max_s = center_x; + if(center_y < max_s) max_s = center_y; + int fix_brightness = plugin->config.fix_brightness; + float saturation = (float)plugin->config.saturation / MAX_VALUE; + float value = (float)plugin->config.brightness / MAX_VALUE; + float angle = plugin->config.angle; + + if(server->mode == DRAW_PATTERN_MODE) + { + switch(cmodel) + { + case BC_RGB888: + CREATE_SWATCH(unsigned char, 3, 0xff, 0) + break; + case BC_RGBA8888: + CREATE_SWATCH(unsigned char, 4, 0xff, 0) + break; + case BC_RGB_FLOAT: + CREATE_SWATCH(float, 3, 1.0, 0) + break; + case BC_RGBA_FLOAT: + CREATE_SWATCH(float, 4, 1.0, 0) + break; + case BC_YUV888: + CREATE_SWATCH(unsigned char, 3, 0xff, 1) + break; + case BC_YUVA8888: + CREATE_SWATCH(unsigned char, 4, 0xff, 1) + break; + } + } + else + { + switch(cmodel) + { + case BC_RGB888: + DRAW_SRC(unsigned char, 3, 0xff, 0) + break; + case BC_RGBA8888: + DRAW_SRC(unsigned char, 4, 0xff, 0) + break; + case BC_RGB_FLOAT: + DRAW_SRC(float, 3, 1.0, 0) + break; + case BC_RGBA_FLOAT: + DRAW_SRC(float, 4, 1.0, 0) + break; + case BC_YUV888: + DRAW_SRC(unsigned char, 3, 0xff, 1) + break; + case BC_YUVA8888: + DRAW_SRC(unsigned char, 4, 0xff, 1) + break; + } + } +} + + + + + + +SwatchEngine::SwatchEngine(SwatchMain *plugin, + int total_clients, + int total_packages) + : LoadServer(total_clients, total_packages) +{ + this->plugin = plugin; +} + +void SwatchEngine::draw_pattern() +{ + mode = DRAW_PATTERN_MODE; + process_packages(); +} + +void SwatchEngine::draw_src() +{ + mode = DRAW_SRC_MODE; + process_packages(); +} + +void SwatchEngine::init_packages() +{ + for(int i = 0; i < get_total_packages(); i++) + { + SwatchPackage *package = (SwatchPackage*)get_package(i); + package->y1 = plugin->temp->get_h() * + i / + get_total_packages(); + package->y2 = plugin->temp->get_h() * + (i + 1) / + get_total_packages(); + } +} + +LoadClient* SwatchEngine::new_client() +{ + return new SwatchUnit(this, plugin); +} + +LoadPackage* SwatchEngine::new_package() +{ + return new SwatchPackage; +} + + + + +