X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fcolorbalance%2Fcolorbalance.C;fp=cinelerra-5.1%2Fplugins%2Fcolorbalance%2Fcolorbalance.C;h=13d5e629ace83b514f6b05e934f97ca9331d2ed5;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/plugins/colorbalance/colorbalance.C b/cinelerra-5.1/plugins/colorbalance/colorbalance.C new file mode 100644 index 00000000..13d5e629 --- /dev/null +++ b/cinelerra-5.1/plugins/colorbalance/colorbalance.C @@ -0,0 +1,664 @@ + +/* + * CINELERRA + * Copyright (C) 2008 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 + * + */ + +#include "filexml.h" +#include "clip.h" +#include "colorbalance.h" +#include "bchash.h" +#include "language.h" +#include "playback3d.h" + +#include "aggregated.h" +#include "../interpolate/aggregated.h" +#include "../gamma/aggregated.h" + +#include +#include + +// 1000 corresponds to (1.0 + MAX_COLOR) * input +#define MAX_COLOR 1.0 + +REGISTER_PLUGIN(ColorBalanceMain) + + + +ColorBalanceConfig::ColorBalanceConfig() +{ + cyan = 0; + magenta = 0; + yellow = 0; + lock_params = 0; + preserve = 0; +} + +int ColorBalanceConfig::equivalent(ColorBalanceConfig &that) +{ + return (cyan == that.cyan && + magenta == that.magenta && + yellow == that.yellow && + lock_params == that.lock_params && + preserve == that.preserve); +} + +void ColorBalanceConfig::copy_from(ColorBalanceConfig &that) +{ + cyan = that.cyan; + magenta = that.magenta; + yellow = that.yellow; + lock_params = that.lock_params; + preserve = that.preserve; +} + +void ColorBalanceConfig::interpolate(ColorBalanceConfig &prev, + ColorBalanceConfig &next, + int64_t prev_frame, + int64_t next_frame, + int64_t 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->cyan = prev.cyan * prev_scale + next.cyan * next_scale; + this->magenta = prev.magenta * prev_scale + next.magenta * next_scale; + this->yellow = prev.yellow * prev_scale + next.yellow * next_scale; + this->preserve = prev.preserve; + this->lock_params = prev.lock_params; +} + + + + + + + + + + +ColorBalanceEngine::ColorBalanceEngine(ColorBalanceMain *plugin) + : Thread() +{ + this->plugin = plugin; + last_frame = 0; + set_synchronous(1); +} + +ColorBalanceEngine::~ColorBalanceEngine() +{ + last_frame = 1; + input_lock.unlock(); + Thread::join(); +} + + +int ColorBalanceEngine::start_process_frame(VFrame *output, VFrame *input, int row_start, int row_end) +{ + this->output = output; + this->input = input; + this->row_start = row_start; + this->row_end = row_end; + input_lock.unlock(); + return 0; +} + + +int ColorBalanceEngine::wait_process_frame() +{ + output_lock.lock("ColorBalanceEngine::wait_process_frame"); + return 0; +} + +void ColorBalanceEngine::run() +{ + while(1) + { + input_lock.lock("ColorBalanceEngine::run"); + if(last_frame) + { + output_lock.unlock(); + return; + } + +#define PROCESS(yuvtorgb, \ + rgbtoyuv, \ + r_lookup, \ + g_lookup, \ + b_lookup, \ + type, \ + max, \ + components, \ + do_yuv) \ +{ \ + int j, k; \ + int y, cr, cb, r, g, b, r_n, g_n, b_n; \ + float h, s, v, h_old, s_old, r_f, g_f, b_f; \ + type **input_rows, **output_rows; \ + input_rows = (type**)input->get_rows(); \ + output_rows = (type**)output->get_rows(); \ + \ + for(j = row_start; j < row_end; j++) \ + { \ + for(k = 0; k < input->get_w() * components; k += components) \ + { \ + if(do_yuv) \ + { \ + y = input_rows[j][k]; \ + cb = input_rows[j][k + 1]; \ + cr = input_rows[j][k + 2]; \ + yuvtorgb(r, g, b, y, cb, cr); \ + } \ + else \ + { \ + r = input_rows[j][k]; \ + g = input_rows[j][k + 1]; \ + b = input_rows[j][k + 2]; \ + } \ + \ + r_n = plugin->r_lookup[r]; \ + g_n = plugin->g_lookup[g]; \ + b_n = plugin->b_lookup[b]; \ + \ + if(plugin->config.preserve) \ + { \ + HSV::rgb_to_hsv((float)r_n, (float)g_n, (float)b_n, h, s, v); \ + HSV::rgb_to_hsv((float)r, (float)g, (float)b, h_old, s_old, v); \ + HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \ + r = (type)r_f; \ + g = (type)g_f; \ + b = (type)b_f; \ + } \ + else \ + { \ + r = r_n; \ + g = g_n; \ + b = b_n; \ + } \ + \ + if(do_yuv) \ + { \ + rgbtoyuv(CLAMP(r, 0, max), CLAMP(g, 0, max), CLAMP(b, 0, max), y, cb, cr); \ + output_rows[j][k] = y; \ + output_rows[j][k + 1] = cb; \ + output_rows[j][k + 2] = cr; \ + } \ + else \ + { \ + output_rows[j][k] = CLAMP(r, 0, max); \ + output_rows[j][k + 1] = CLAMP(g, 0, max); \ + output_rows[j][k + 2] = CLAMP(b, 0, max); \ + } \ + } \ + } \ +} + +#define PROCESS_F(components) \ +{ \ + int j, k; \ + float r, g, b, r_n, g_n, b_n; \ + float h, s, v, h_old, s_old, r_f, g_f, b_f; \ + float **input_rows, **output_rows; \ + input_rows = (float**)input->get_rows(); \ + output_rows = (float**)output->get_rows(); \ + cyan_f = plugin->calculate_transfer(plugin->config.cyan); \ + magenta_f = plugin->calculate_transfer(plugin->config.magenta); \ + yellow_f = plugin->calculate_transfer(plugin->config.yellow); \ + \ +printf("PROCESS_F %f\n", cyan_f); \ + for(j = row_start; j < row_end; j++) \ + { \ + for(k = 0; k < input->get_w() * components; k += components) \ + { \ + r = input_rows[j][k]; \ + g = input_rows[j][k + 1]; \ + b = input_rows[j][k + 2]; \ + \ + r_n = r * cyan_f; \ + g_n = g * magenta_f; \ + b_n = b * yellow_f; \ + \ + if(plugin->config.preserve) \ + { \ + HSV::rgb_to_hsv(r_n, g_n, b_n, h, s, v); \ + HSV::rgb_to_hsv(r, g, b, h_old, s_old, v); \ + HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \ + r = (float)r_f; \ + g = (float)g_f; \ + b = (float)b_f; \ + } \ + else \ + { \ + r = r_n; \ + g = g_n; \ + b = b_n; \ + } \ + \ + output_rows[j][k] = r; \ + output_rows[j][k + 1] = g; \ + output_rows[j][k + 2] = b; \ + } \ + } \ +} + + switch(input->get_color_model()) + { + case BC_RGB888: + PROCESS(yuv.yuv_to_rgb_8, yuv.rgb_to_yuv_8, + r_lookup_8, g_lookup_8, b_lookup_8, + unsigned char, 0xff, 3, 0); + break; + + case BC_RGB_FLOAT: + PROCESS_F(3); + break; + + case BC_YUV888: + PROCESS(yuv.yuv_to_rgb_8, yuv.rgb_to_yuv_8, + r_lookup_8, g_lookup_8, b_lookup_8, + unsigned char, 0xff, 3, 1); + break; + + case BC_RGBA_FLOAT: + PROCESS_F(4); + break; + + case BC_RGBA8888: + PROCESS(yuv.yuv_to_rgb_8, yuv.rgb_to_yuv_8, + r_lookup_8, g_lookup_8, b_lookup_8, + unsigned char, 0xff, 4, 0); + break; + + case BC_YUVA8888: + PROCESS(yuv.yuv_to_rgb_8, yuv.rgb_to_yuv_8, + r_lookup_8, g_lookup_8, b_lookup_8, + unsigned char, 0xff, 4, 1); + break; + + case BC_YUV161616: + PROCESS(yuv.yuv_to_rgb_16, yuv.rgb_to_yuv_16, + r_lookup_16, g_lookup_16, b_lookup_16, + u_int16_t, 0xffff, 3, 1); + break; + + case BC_YUVA16161616: + PROCESS(yuv.yuv_to_rgb_16, yuv.rgb_to_yuv_16, + r_lookup_16, g_lookup_16, b_lookup_16, + u_int16_t, 0xffff, 4, 1); + break; + } + + + + output_lock.unlock(); + } +} + + + + +ColorBalanceMain::ColorBalanceMain(PluginServer *server) + : PluginVClient(server) +{ + need_reconfigure = 1; + engine = 0; + +} + +ColorBalanceMain::~ColorBalanceMain() +{ + + + + if(engine) + { + for(int i = 0; i < total_engines; i++) + { + delete engine[i]; + } + delete [] engine; + } +} + +const char* ColorBalanceMain::plugin_title() { return _("Color Balance"); } +int ColorBalanceMain::is_realtime() { return 1; } + + +int ColorBalanceMain::reconfigure() +{ + float r_scale = calculate_transfer(config.cyan); + float g_scale = calculate_transfer(config.magenta); + float b_scale = calculate_transfer(config.yellow); + +#define RECONFIGURE(r_lookup, g_lookup, b_lookup, max) \ + for(int i = 0; i <= max; i++) \ + { \ + r_lookup[i] = CLIP((int)(r_scale * i), 0, max); \ + g_lookup[i] = CLIP((int)(g_scale * i), 0, max); \ + b_lookup[i] = CLIP((int)(b_scale * i), 0, max); \ + } + + RECONFIGURE(r_lookup_8, g_lookup_8, b_lookup_8, 0xff); + RECONFIGURE(r_lookup_16, g_lookup_16, b_lookup_16, 0xffff); + + return 0; +} + +int64_t ColorBalanceMain::calculate_slider(float in) +{ + if(in < 1.0) + { + return (int64_t)(in * 1000 - 1000.0); + } + else + if(in > 1.0) + { + return (int64_t)(1000 * (in - 1.0) / MAX_COLOR); + } + else + return 0; +} + +float ColorBalanceMain::calculate_transfer(float in) +{ + if(in < 0) + { + return (1000.0 + in) / 1000.0; + } + else + if(in > 0) + { + return 1.0 + in / 1000.0 * MAX_COLOR; + } + else + return 1.0; +} + + + + +int ColorBalanceMain::test_boundary(float &value) +{ + + if(value < -1000) value = -1000; + if(value > 1000) value = 1000; + return 0; +} + +int ColorBalanceMain::synchronize_params(ColorBalanceSlider *slider, float difference) +{ + if(thread && config.lock_params) + { + if(slider != ((ColorBalanceWindow*)thread->window)->cyan) + { + config.cyan += difference; + test_boundary(config.cyan); + ((ColorBalanceWindow*)thread->window)->cyan->update((int64_t)config.cyan); + } + if(slider != ((ColorBalanceWindow*)thread->window)->magenta) + { + config.magenta += difference; + test_boundary(config.magenta); + ((ColorBalanceWindow*)thread->window)->magenta->update((int64_t)config.magenta); + } + if(slider != ((ColorBalanceWindow*)thread->window)->yellow) + { + config.yellow += difference; + test_boundary(config.yellow); + ((ColorBalanceWindow*)thread->window)->yellow->update((int64_t)config.yellow); + } + } + return 0; +} + + + + + + +LOAD_CONFIGURATION_MACRO(ColorBalanceMain, ColorBalanceConfig) +NEW_WINDOW_MACRO(ColorBalanceMain, ColorBalanceWindow) + + + + + +int ColorBalanceMain::process_buffer(VFrame *frame, + int64_t start_position, + double frame_rate) +{ + need_reconfigure |= load_configuration(); + +//printf("ColorBalanceMain::process_realtime 1 %d\n", need_reconfigure); + if(need_reconfigure) + { + if(!engine) + { + total_engines = PluginClient::smp + 1; + engine = new ColorBalanceEngine*[total_engines]; + for(int i = 0; i < total_engines; i++) + { + engine[i] = new ColorBalanceEngine(this); + engine[i]->start(); + } + } + + reconfigure(); + need_reconfigure = 0; + } + + frame->get_params()->update("COLORBALANCE_PRESERVE", config.preserve); + frame->get_params()->update("COLORBALANCE_CYAN", calculate_transfer(config.cyan)); + frame->get_params()->update("COLORBALANCE_MAGENTA", calculate_transfer(config.magenta)); + frame->get_params()->update("COLORBALANCE_YELLOW", calculate_transfer(config.yellow)); + + + read_frame(frame, + 0, + get_source_position(), + get_framerate(), + get_use_opengl()); + + int aggregate_interpolate = 0; + int aggregate_gamma = 0; + get_aggregation(&aggregate_interpolate, + &aggregate_gamma); + + if(!EQUIV(config.cyan, 0) || + !EQUIV(config.magenta, 0) || + !EQUIV(config.yellow, 0) || + (get_use_opengl() && + (aggregate_interpolate || + aggregate_gamma))) + { + if(get_use_opengl()) + { +//get_output()->dump_stacks(); +// Aggregate + if(next_effect_is(_("Histogram"))) return 0; + return run_opengl(); + } + + for(int i = 0; i < total_engines; i++) + { + engine[i]->start_process_frame(frame, + frame, + frame->get_h() * i / total_engines, + frame->get_h() * (i + 1) / total_engines); + } + + for(int i = 0; i < total_engines; i++) + { + engine[i]->wait_process_frame(); + } + } + + + return 0; +} + + +void ColorBalanceMain::update_gui() +{ + if(thread) + { + load_configuration(); + ((ColorBalanceWindow*)thread->window)->lock_window("ColorBalanceMain::update_gui"); + ((ColorBalanceWindow*)thread->window)->cyan->update((int64_t)config.cyan); + ((ColorBalanceWindow*)thread->window)->magenta->update((int64_t)config.magenta); + ((ColorBalanceWindow*)thread->window)->yellow->update((int64_t)config.yellow); + ((ColorBalanceWindow*)thread->window)->preserve->update(config.preserve); + ((ColorBalanceWindow*)thread->window)->lock_params->update(config.lock_params); + ((ColorBalanceWindow*)thread->window)->unlock_window(); + } +} + + + + +void ColorBalanceMain::save_data(KeyFrame *keyframe) +{ + FileXML output; + +// cause data to be stored directly in text + output.set_shared_output(keyframe->get_data(), MESSAGESIZE); + output.tag.set_title("COLORBALANCE"); + output.tag.set_property("CYAN", config.cyan); + output.tag.set_property("MAGENTA", config.magenta); + output.tag.set_property("YELLOW", config.yellow); + output.tag.set_property("PRESERVELUMINOSITY", config.preserve); + output.tag.set_property("LOCKPARAMS", config.lock_params); + output.append_tag(); + output.tag.set_title("/COLORBALANCE"); + output.append_tag(); + output.append_newline(); + output.terminate_string(); +} + +void ColorBalanceMain::read_data(KeyFrame *keyframe) +{ + FileXML input; + + input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data())); + + int result = 0; + + while(!result) + { + result = input.read_tag(); + + if(!result) + { + if(input.tag.title_is("COLORBALANCE")) + { + config.cyan = input.tag.get_property("CYAN", config.cyan); + config.magenta = input.tag.get_property("MAGENTA", config.magenta); + config.yellow = input.tag.get_property("YELLOW", config.yellow); + config.preserve = input.tag.get_property("PRESERVELUMINOSITY", config.preserve); + config.lock_params = input.tag.get_property("LOCKPARAMS", config.lock_params); + } + } + } +} + +void ColorBalanceMain::get_aggregation(int *aggregate_interpolate, + int *aggregate_gamma) +{ + if(!strcmp(get_output()->get_prev_effect(1), _("Interpolate Pixels")) && + !strcmp(get_output()->get_prev_effect(0), _("Gamma"))) + { + *aggregate_interpolate = 1; + *aggregate_gamma = 1; + } + else + if(!strcmp(get_output()->get_prev_effect(0), _("Interpolate Pixels"))) + { + *aggregate_interpolate = 1; + } + else + if(!strcmp(get_output()->get_prev_effect(0), _("Gamma"))) + { + *aggregate_gamma = 1; + } +} + +int ColorBalanceMain::handle_opengl() +{ +#ifdef HAVE_GL + + get_output()->to_texture(); + get_output()->enable_opengl(); + + unsigned int shader = 0; + const char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int current_shader = 0; + int aggregate_interpolate = 0; + int aggregate_gamma = 0; + + get_aggregation(&aggregate_interpolate, + &aggregate_gamma); + +//printf("ColorBalanceMain::handle_opengl %d %d\n", aggregate_interpolate, aggregate_gamma); + if(aggregate_interpolate) + INTERPOLATE_COMPILE(shader_stack, current_shader) + + if(aggregate_gamma) + GAMMA_COMPILE(shader_stack, current_shader, aggregate_interpolate) + + COLORBALANCE_COMPILE(shader_stack, + current_shader, + aggregate_gamma || aggregate_interpolate) + + shader = VFrame::make_shader(0, + shader_stack[0], + shader_stack[1], + shader_stack[2], + shader_stack[3], + shader_stack[4], + shader_stack[5], + shader_stack[6], + shader_stack[7], + 0); + + if(shader > 0) + { + glUseProgram(shader); + glUniform1i(glGetUniformLocation(shader, "tex"), 0); + + if(aggregate_interpolate) INTERPOLATE_UNIFORMS(shader); + if(aggregate_gamma) GAMMA_UNIFORMS(shader); + + COLORBALANCE_UNIFORMS(shader); + + } + + get_output()->init_screen(); + get_output()->bind_texture(0); + get_output()->draw_texture(); + glUseProgram(0); + get_output()->set_opengl_state(VFrame::SCREEN); +#endif + return 0; +} + + + + + + +