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
25 #include "colorbalance.h"
28 #include "playback3d.h"
30 #include "aggregated.h"
31 #include "../interpolate/aggregated.h"
32 #include "../gamma/aggregated.h"
37 // 1000 corresponds to (1.0 + MAX_COLOR) * input
40 REGISTER_PLUGIN(ColorBalanceMain)
44 ColorBalanceConfig::ColorBalanceConfig()
49 void ColorBalanceConfig::reset(int clear)
52 case RESET_CYAN : cyan = 0;
54 case RESET_MAGENTA : magenta = 0;
56 case RESET_YELLOW : yellow = 0;
59 case RESET_DEFAULT_SETTINGS :
70 int ColorBalanceConfig::equivalent(ColorBalanceConfig &that)
72 return (cyan == that.cyan &&
73 magenta == that.magenta &&
74 yellow == that.yellow &&
75 lock_params == that.lock_params &&
76 preserve == that.preserve);
79 void ColorBalanceConfig::copy_from(ColorBalanceConfig &that)
82 magenta = that.magenta;
84 lock_params = that.lock_params;
85 preserve = that.preserve;
88 void ColorBalanceConfig::interpolate(ColorBalanceConfig &prev,
89 ColorBalanceConfig &next,
92 int64_t current_frame)
94 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
95 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
97 this->cyan = prev.cyan * prev_scale + next.cyan * next_scale;
98 this->magenta = prev.magenta * prev_scale + next.magenta * next_scale;
99 this->yellow = prev.yellow * prev_scale + next.yellow * next_scale;
100 this->preserve = prev.preserve;
101 this->lock_params = prev.lock_params;
113 ColorBalanceEngine::ColorBalanceEngine(ColorBalanceMain *plugin)
116 this->plugin = plugin;
121 ColorBalanceEngine::~ColorBalanceEngine()
129 int ColorBalanceEngine::start_process_frame(VFrame *output, VFrame *input, int row_start, int row_end)
131 this->output = output;
133 this->row_start = row_start;
134 this->row_end = row_end;
140 int ColorBalanceEngine::wait_process_frame()
142 output_lock.lock("ColorBalanceEngine::wait_process_frame");
146 void ColorBalanceEngine::run()
150 input_lock.lock("ColorBalanceEngine::run");
153 output_lock.unlock();
157 #define PROCESS(yuvtorgb, \
168 int y, cr, cb, r, g, b, r_n, g_n, b_n; \
169 float h, s, v, h_old, s_old, r_f, g_f, b_f; \
170 type **input_rows, **output_rows; \
171 input_rows = (type**)input->get_rows(); \
172 output_rows = (type**)output->get_rows(); \
174 for(j = row_start; j < row_end; j++) \
176 for(k = 0; k < input->get_w() * components; k += components) \
180 y = input_rows[j][k]; \
181 cb = input_rows[j][k + 1]; \
182 cr = input_rows[j][k + 2]; \
183 yuvtorgb(r, g, b, y, cb, cr); \
187 r = input_rows[j][k]; \
188 g = input_rows[j][k + 1]; \
189 b = input_rows[j][k + 2]; \
192 r_n = plugin->r_lookup[r]; \
193 g_n = plugin->g_lookup[g]; \
194 b_n = plugin->b_lookup[b]; \
196 if(plugin->config.preserve) \
198 HSV::rgb_to_hsv((float)r_n, (float)g_n, (float)b_n, h, s, v); \
199 HSV::rgb_to_hsv((float)r, (float)g, (float)b, h_old, s_old, v); \
200 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
214 rgbtoyuv(CLAMP(r, 0, max), CLAMP(g, 0, max), CLAMP(b, 0, max), y, cb, cr); \
215 output_rows[j][k] = y; \
216 output_rows[j][k + 1] = cb; \
217 output_rows[j][k + 2] = cr; \
221 output_rows[j][k] = CLAMP(r, 0, max); \
222 output_rows[j][k + 1] = CLAMP(g, 0, max); \
223 output_rows[j][k + 2] = CLAMP(b, 0, max); \
229 #define PROCESS_F(components) \
232 float r, g, b, r_n, g_n, b_n; \
233 float h, s, v, h_old, s_old, r_f, g_f, b_f; \
234 float **input_rows, **output_rows; \
235 input_rows = (float**)input->get_rows(); \
236 output_rows = (float**)output->get_rows(); \
237 cyan_f = plugin->calculate_transfer(plugin->config.cyan); \
238 magenta_f = plugin->calculate_transfer(plugin->config.magenta); \
239 yellow_f = plugin->calculate_transfer(plugin->config.yellow); \
241 printf("PROCESS_F %f\n", cyan_f); \
242 for(j = row_start; j < row_end; j++) \
244 for(k = 0; k < input->get_w() * components; k += components) \
246 r = input_rows[j][k]; \
247 g = input_rows[j][k + 1]; \
248 b = input_rows[j][k + 2]; \
251 g_n = g * magenta_f; \
252 b_n = b * yellow_f; \
254 if(plugin->config.preserve) \
256 HSV::rgb_to_hsv(r_n, g_n, b_n, h, s, v); \
257 HSV::rgb_to_hsv(r, g, b, h_old, s_old, v); \
258 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
270 output_rows[j][k] = r; \
271 output_rows[j][k + 1] = g; \
272 output_rows[j][k + 2] = b; \
277 switch(input->get_color_model())
280 PROCESS(YUV::yuv.yuv_to_rgb_8, YUV::yuv.rgb_to_yuv_8,
281 r_lookup_8, g_lookup_8, b_lookup_8,
282 unsigned char, 0xff, 3, 0);
290 PROCESS(YUV::yuv.yuv_to_rgb_8, YUV::yuv.rgb_to_yuv_8,
291 r_lookup_8, g_lookup_8, b_lookup_8,
292 unsigned char, 0xff, 3, 1);
300 PROCESS(YUV::yuv.yuv_to_rgb_8, YUV::yuv.rgb_to_yuv_8,
301 r_lookup_8, g_lookup_8, b_lookup_8,
302 unsigned char, 0xff, 4, 0);
306 PROCESS(YUV::yuv.yuv_to_rgb_8, YUV::yuv.rgb_to_yuv_8,
307 r_lookup_8, g_lookup_8, b_lookup_8,
308 unsigned char, 0xff, 4, 1);
312 PROCESS(YUV::yuv.yuv_to_rgb_16, YUV::yuv.rgb_to_yuv_16,
313 r_lookup_16, g_lookup_16, b_lookup_16,
314 u_int16_t, 0xffff, 3, 1);
317 case BC_YUVA16161616:
318 PROCESS(YUV::yuv.yuv_to_rgb_16, YUV::yuv.rgb_to_yuv_16,
319 r_lookup_16, g_lookup_16, b_lookup_16,
320 u_int16_t, 0xffff, 4, 1);
326 output_lock.unlock();
333 ColorBalanceMain::ColorBalanceMain(PluginServer *server)
334 : PluginVClient(server)
336 need_reconfigure = 1;
341 ColorBalanceMain::~ColorBalanceMain()
348 for(int i = 0; i < total_engines; i++)
356 const char* ColorBalanceMain::plugin_title() { return N_("Color Balance"); }
357 int ColorBalanceMain::is_realtime() { return 1; }
360 int ColorBalanceMain::reconfigure()
362 float r_scale = calculate_transfer(config.cyan);
363 float g_scale = calculate_transfer(config.magenta);
364 float b_scale = calculate_transfer(config.yellow);
366 #define RECONFIGURE(r_lookup, g_lookup, b_lookup, max) \
367 for(int i = 0; i <= max; i++) \
369 r_lookup[i] = CLIP((int)(r_scale * i), 0, max); \
370 g_lookup[i] = CLIP((int)(g_scale * i), 0, max); \
371 b_lookup[i] = CLIP((int)(b_scale * i), 0, max); \
374 RECONFIGURE(r_lookup_8, g_lookup_8, b_lookup_8, 0xff);
375 RECONFIGURE(r_lookup_16, g_lookup_16, b_lookup_16, 0xffff);
380 int64_t ColorBalanceMain::calculate_slider(float in)
384 return (int64_t)(in * 1000 - 1000.0);
389 return (int64_t)(1000 * (in - 1.0) / MAX_COLOR);
395 float ColorBalanceMain::calculate_transfer(float in)
399 return (1000.0 + in) / 1000.0;
404 return 1.0 + in / 1000.0 * MAX_COLOR;
413 int ColorBalanceMain::test_boundary(float &value)
416 if(value < -1000) value = -1000;
417 if(value > 1000) value = 1000;
421 int ColorBalanceMain::synchronize_params(ColorBalanceSlider *slider, float difference)
423 if(thread && config.lock_params) {
424 if(slider != ((ColorBalanceWindow*)thread->window)->cyan) {
425 config.cyan += difference;
426 test_boundary(config.cyan);
427 ((ColorBalanceWindow*)thread->window)->cyan->update((int64_t)config.cyan);
429 if(slider != ((ColorBalanceWindow*)thread->window)->magenta) {
430 config.magenta += difference;
431 test_boundary(config.magenta);
432 ((ColorBalanceWindow*)thread->window)->magenta->update((int64_t)config.magenta);
434 if(slider != ((ColorBalanceWindow*)thread->window)->yellow) {
435 config.yellow += difference;
436 test_boundary(config.yellow);
437 ((ColorBalanceWindow*)thread->window)->yellow->update((int64_t)config.yellow);
448 LOAD_CONFIGURATION_MACRO(ColorBalanceMain, ColorBalanceConfig)
449 NEW_WINDOW_MACRO(ColorBalanceMain, ColorBalanceWindow)
455 int ColorBalanceMain::process_buffer(VFrame *frame,
456 int64_t start_position,
459 need_reconfigure |= load_configuration();
461 //printf("ColorBalanceMain::process_realtime 1 %d\n", need_reconfigure);
466 total_engines = PluginClient::smp + 1;
467 engine = new ColorBalanceEngine*[total_engines];
468 for(int i = 0; i < total_engines; i++)
470 engine[i] = new ColorBalanceEngine(this);
476 need_reconfigure = 0;
479 frame->get_params()->update("COLORBALANCE_PRESERVE", config.preserve);
480 frame->get_params()->update("COLORBALANCE_CYAN", calculate_transfer(config.cyan));
481 frame->get_params()->update("COLORBALANCE_MAGENTA", calculate_transfer(config.magenta));
482 frame->get_params()->update("COLORBALANCE_YELLOW", calculate_transfer(config.yellow));
487 get_source_position(),
491 int aggregate_interpolate = 0;
492 int aggregate_gamma = 0;
493 get_aggregation(&aggregate_interpolate,
496 if(!EQUIV(config.cyan, 0) ||
497 !EQUIV(config.magenta, 0) ||
498 !EQUIV(config.yellow, 0) ||
500 (aggregate_interpolate ||
505 //get_output()->dump_stacks();
507 if(next_effect_is(_("Histogram"))) return 0;
511 for(int i = 0; i < total_engines; i++)
513 engine[i]->start_process_frame(frame,
515 frame->get_h() * i / total_engines,
516 frame->get_h() * (i + 1) / total_engines);
519 for(int i = 0; i < total_engines; i++)
521 engine[i]->wait_process_frame();
530 void ColorBalanceMain::update_gui()
534 load_configuration();
535 ((ColorBalanceWindow*)thread->window)->lock_window("ColorBalanceMain::update_gui");
536 ((ColorBalanceWindow*)thread->window)->cyan->update((int64_t)config.cyan);
537 ((ColorBalanceWindow*)thread->window)->magenta->update((int64_t)config.magenta);
538 ((ColorBalanceWindow*)thread->window)->yellow->update((int64_t)config.yellow);
539 ((ColorBalanceWindow*)thread->window)->preserve->update(config.preserve);
540 ((ColorBalanceWindow*)thread->window)->lock_params->update(config.lock_params);
541 ((ColorBalanceWindow*)thread->window)->unlock_window();
548 void ColorBalanceMain::save_data(KeyFrame *keyframe)
552 // cause data to be stored directly in text
553 output.set_shared_output(keyframe->xbuf);
554 output.tag.set_title("COLORBALANCE");
555 output.tag.set_property("CYAN", config.cyan);
556 output.tag.set_property("MAGENTA", config.magenta);
557 output.tag.set_property("YELLOW", config.yellow);
558 output.tag.set_property("PRESERVELUMINOSITY", config.preserve);
559 output.tag.set_property("LOCKPARAMS", config.lock_params);
561 output.tag.set_title("/COLORBALANCE");
563 output.append_newline();
564 output.terminate_string();
567 void ColorBalanceMain::read_data(KeyFrame *keyframe)
571 input.set_shared_input(keyframe->xbuf);
577 result = input.read_tag();
581 if(input.tag.title_is("COLORBALANCE"))
583 config.cyan = input.tag.get_property("CYAN", config.cyan);
584 config.magenta = input.tag.get_property("MAGENTA", config.magenta);
585 config.yellow = input.tag.get_property("YELLOW", config.yellow);
586 config.preserve = input.tag.get_property("PRESERVELUMINOSITY", config.preserve);
587 config.lock_params = input.tag.get_property("LOCKPARAMS", config.lock_params);
593 void ColorBalanceMain::get_aggregation(int *aggregate_interpolate,
594 int *aggregate_gamma)
596 if(!strcmp(get_output()->get_prev_effect(1), _("Interpolate Pixels")) &&
597 !strcmp(get_output()->get_prev_effect(0), _("Gamma")))
599 *aggregate_interpolate = 1;
600 *aggregate_gamma = 1;
603 if(!strcmp(get_output()->get_prev_effect(0), _("Interpolate Pixels")))
605 *aggregate_interpolate = 1;
608 if(!strcmp(get_output()->get_prev_effect(0), _("Gamma")))
610 *aggregate_gamma = 1;
614 int ColorBalanceMain::handle_opengl()
618 get_output()->to_texture();
619 get_output()->enable_opengl();
621 const char *shader_stack[16];
622 memset(shader_stack,0, sizeof(shader_stack));
623 int current_shader = 0;
625 int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
626 if( need_color_matrix )
627 shader_stack[current_shader++] = bc_gl_colors;
629 int aggregate_interpolate = 0;
630 int aggregate_gamma = 0;
632 get_aggregation(&aggregate_interpolate,
635 //printf("ColorBalanceMain::handle_opengl %d %d\n", aggregate_interpolate, aggregate_gamma);
636 if(aggregate_interpolate)
637 INTERPOLATE_COMPILE(shader_stack, current_shader);
640 GAMMA_COMPILE(shader_stack, current_shader,
641 aggregate_interpolate);
643 COLORBALANCE_COMPILE(shader_stack, current_shader,
644 aggregate_gamma || aggregate_interpolate);
646 shader_stack[current_shader] = 0;
647 unsigned int shader = VFrame::make_shader(shader_stack);
649 glUseProgram(shader);
650 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
652 if(aggregate_interpolate) INTERPOLATE_UNIFORMS(shader);
653 if(aggregate_gamma) GAMMA_UNIFORMS(shader);
655 COLORBALANCE_UNIFORMS(shader);
656 if( need_color_matrix ) BC_GL_COLORS(shader);
659 get_output()->init_screen();
660 get_output()->bind_texture(0);
661 get_output()->draw_texture();
663 get_output()->set_opengl_state(VFrame::SCREEN);