00001 #include "filexml.h"
00002 #include "colorbalance.h"
00003 #include "bchash.h"
00004 #include "language.h"
00005 #include "picon_png.h"
00006 #include "playback3d.h"
00007
00008 #include "aggregated.h"
00009 #include "../interpolate/aggregated.h"
00010 #include "../gamma/aggregated.h"
00011
00012 #include <stdio.h>
00013 #include <string.h>
00014
00015
00016 #define MAX_COLOR 1.0
00017 #define SQR(a) ((a) * (a))
00018
00019 REGISTER_PLUGIN(ColorBalanceMain)
00020
00021
00022
00023 ColorBalanceConfig::ColorBalanceConfig()
00024 {
00025 cyan = 0;
00026 magenta = 0;
00027 yellow = 0;
00028 lock_params = 0;
00029 preserve = 0;
00030 }
00031
00032 int ColorBalanceConfig::equivalent(ColorBalanceConfig &that)
00033 {
00034 return (cyan == that.cyan &&
00035 magenta == that.magenta &&
00036 yellow == that.yellow &&
00037 lock_params == that.lock_params &&
00038 preserve == that.preserve);
00039 }
00040
00041 void ColorBalanceConfig::copy_from(ColorBalanceConfig &that)
00042 {
00043 cyan = that.cyan;
00044 magenta = that.magenta;
00045 yellow = that.yellow;
00046 lock_params = that.lock_params;
00047 preserve = that.preserve;
00048 }
00049
00050 void ColorBalanceConfig::interpolate(ColorBalanceConfig &prev,
00051 ColorBalanceConfig &next,
00052 int64_t prev_frame,
00053 int64_t next_frame,
00054 int64_t current_frame)
00055 {
00056 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
00057 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
00058
00059 this->cyan = prev.cyan * prev_scale + next.cyan * next_scale;
00060 this->magenta = prev.magenta * prev_scale + next.magenta * next_scale;
00061 this->yellow = prev.yellow * prev_scale + next.yellow * next_scale;
00062 this->preserve = prev.preserve;
00063 this->lock_params = prev.lock_params;
00064 }
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075 ColorBalanceEngine::ColorBalanceEngine(ColorBalanceMain *plugin)
00076 : Thread()
00077 {
00078 this->plugin = plugin;
00079 last_frame = 0;
00080 set_synchronous(1);
00081 }
00082
00083 ColorBalanceEngine::~ColorBalanceEngine()
00084 {
00085 last_frame = 1;
00086 input_lock.unlock();
00087 Thread::join();
00088 }
00089
00090
00091 int ColorBalanceEngine::start_process_frame(VFrame *output, VFrame *input, int row_start, int row_end)
00092 {
00093 this->output = output;
00094 this->input = input;
00095 this->row_start = row_start;
00096 this->row_end = row_end;
00097 input_lock.unlock();
00098 return 0;
00099 }
00100
00101
00102 int ColorBalanceEngine::wait_process_frame()
00103 {
00104 output_lock.lock("ColorBalanceEngine::wait_process_frame");
00105 return 0;
00106 }
00107
00108 void ColorBalanceEngine::run()
00109 {
00110 while(1)
00111 {
00112 input_lock.lock("ColorBalanceEngine::run");
00113 if(last_frame)
00114 {
00115 output_lock.unlock();
00116 return;
00117 }
00118
00119 #define PROCESS(yuvtorgb, \
00120 rgbtoyuv, \
00121 r_lookup, \
00122 g_lookup, \
00123 b_lookup, \
00124 type, \
00125 max, \
00126 components, \
00127 do_yuv) \
00128 { \
00129 int i, j, k; \
00130 int y, cb, cr, r, g, b, r_n, g_n, b_n; \
00131 float h, s, v, h_old, s_old, r_f, g_f, b_f; \
00132 type **input_rows, **output_rows; \
00133 input_rows = (type**)input->get_rows(); \
00134 output_rows = (type**)output->get_rows(); \
00135 \
00136 for(j = row_start; j < row_end; j++) \
00137 { \
00138 for(k = 0; k < input->get_w() * components; k += components) \
00139 { \
00140 if(do_yuv) \
00141 { \
00142 y = input_rows[j][k]; \
00143 cb = input_rows[j][k + 1]; \
00144 cr = input_rows[j][k + 2]; \
00145 yuvtorgb(r, g, b, y, cb, cr); \
00146 } \
00147 else \
00148 { \
00149 r = input_rows[j][k]; \
00150 g = input_rows[j][k + 1]; \
00151 b = input_rows[j][k + 2]; \
00152 } \
00153 \
00154 r = CLAMP(r, 0, max-1); g = CLAMP(g, 0, max-1); b = CLAMP(b, 0, max-1); \
00155 r_n = plugin->r_lookup[r]; \
00156 g_n = plugin->g_lookup[g]; \
00157 b_n = plugin->b_lookup[b]; \
00158 \
00159 if(plugin->config.preserve) \
00160 { \
00161 HSV::rgb_to_hsv((float)r_n, (float)g_n, (float)b_n, h, s, v); \
00162 HSV::rgb_to_hsv((float)r, (float)g, (float)b, h_old, s_old, v); \
00163 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
00164 r = (type)r_f; \
00165 g = (type)g_f; \
00166 b = (type)b_f; \
00167 } \
00168 else \
00169 { \
00170 r = r_n; \
00171 g = g_n; \
00172 b = b_n; \
00173 } \
00174 \
00175 if(do_yuv) \
00176 { \
00177 rgbtoyuv(CLAMP(r, 0, max), CLAMP(g, 0, max), CLAMP(b, 0, max), y, cb, cr); \
00178 output_rows[j][k] = y; \
00179 output_rows[j][k + 1] = cb; \
00180 output_rows[j][k + 2] = cr; \
00181 } \
00182 else \
00183 { \
00184 output_rows[j][k] = CLAMP(r, 0, max); \
00185 output_rows[j][k + 1] = CLAMP(g, 0, max); \
00186 output_rows[j][k + 2] = CLAMP(b, 0, max); \
00187 } \
00188 } \
00189 } \
00190 }
00191
00192 #define PROCESS_F(components) \
00193 { \
00194 int i, j, k; \
00195 float y, cb, cr, r, g, b, r_n, g_n, b_n; \
00196 float h, s, v, h_old, s_old, r_f, g_f, b_f; \
00197 float **input_rows, **output_rows; \
00198 input_rows = (float**)input->get_rows(); \
00199 output_rows = (float**)output->get_rows(); \
00200 cyan_f = plugin->calculate_transfer(plugin->config.cyan); \
00201 magenta_f = plugin->calculate_transfer(plugin->config.magenta); \
00202 yellow_f = plugin->calculate_transfer(plugin->config.yellow); \
00203 \
00204 for(j = row_start; j < row_end; j++) \
00205 { \
00206 for(k = 0; k < input->get_w() * components; k += components) \
00207 { \
00208 r = input_rows[j][k]; \
00209 g = input_rows[j][k + 1]; \
00210 b = input_rows[j][k + 2]; \
00211 \
00212 r_n = r * cyan_f; \
00213 g_n = g * magenta_f; \
00214 b_n = b * yellow_f; \
00215 \
00216 if(plugin->config.preserve) \
00217 { \
00218 HSV::rgb_to_hsv(r_n, g_n, b_n, h, s, v); \
00219 HSV::rgb_to_hsv(r, g, b, h_old, s_old, v); \
00220 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
00221 r = (float)r_f; \
00222 g = (float)g_f; \
00223 b = (float)b_f; \
00224 } \
00225 else \
00226 { \
00227 r = r_n; \
00228 g = g_n; \
00229 b = b_n; \
00230 } \
00231 \
00232 output_rows[j][k] = r; \
00233 output_rows[j][k + 1] = g; \
00234 output_rows[j][k + 2] = b; \
00235 } \
00236 } \
00237 }
00238
00239 switch(input->get_color_model())
00240 {
00241 case BC_RGB888:
00242 PROCESS(yuv.yuv_to_rgb_8,
00243 yuv.rgb_to_yuv_8,
00244 r_lookup_8,
00245 g_lookup_8,
00246 b_lookup_8,
00247 unsigned char,
00248 0xff,
00249 3,
00250 0);
00251 break;
00252
00253 case BC_RGB_FLOAT:
00254 PROCESS_F(3);
00255 break;
00256
00257 case BC_YUV888:
00258 PROCESS(yuv.yuv_to_rgb_8,
00259 yuv.rgb_to_yuv_8,
00260 r_lookup_8,
00261 g_lookup_8,
00262 b_lookup_8,
00263 unsigned char,
00264 0xff,
00265 3,
00266 1);
00267 break;
00268
00269 case BC_RGBA_FLOAT:
00270 PROCESS_F(4);
00271 break;
00272
00273 case BC_RGBA8888:
00274 PROCESS(yuv.yuv_to_rgb_8,
00275 yuv.rgb_to_yuv_8,
00276 r_lookup_8,
00277 g_lookup_8,
00278 b_lookup_8,
00279 unsigned char,
00280 0xff,
00281 4,
00282 0);
00283 break;
00284
00285 case BC_YUVA8888:
00286 PROCESS(yuv.yuv_to_rgb_8,
00287 yuv.rgb_to_yuv_8,
00288 r_lookup_8,
00289 g_lookup_8,
00290 b_lookup_8,
00291 unsigned char,
00292 0xff,
00293 4,
00294 1);
00295 break;
00296
00297 case BC_YUV161616:
00298 PROCESS(yuv.yuv_to_rgb_16,
00299 yuv.rgb_to_yuv_16,
00300 r_lookup_16,
00301 g_lookup_16,
00302 b_lookup_16,
00303 u_int16_t,
00304 0xffff,
00305 3,
00306 1);
00307 break;
00308
00309 case BC_YUVA16161616:
00310 PROCESS(yuv.yuv_to_rgb_16,
00311 yuv.rgb_to_yuv_16,
00312 r_lookup_16,
00313 g_lookup_16,
00314 b_lookup_16,
00315 u_int16_t,
00316 0xffff,
00317 4,
00318 1);
00319 break;
00320 }
00321
00322
00323
00324 output_lock.unlock();
00325 }
00326 }
00327
00328
00329
00330
00331 ColorBalanceMain::ColorBalanceMain(PluginServer *server)
00332 : PluginVClient(server)
00333 {
00334 need_reconfigure = 1;
00335 engine = 0;
00336 PLUGIN_CONSTRUCTOR_MACRO
00337 }
00338
00339 ColorBalanceMain::~ColorBalanceMain()
00340 {
00341 PLUGIN_DESTRUCTOR_MACRO
00342
00343
00344 if(engine)
00345 {
00346 for(int i = 0; i < total_engines; i++)
00347 {
00348 delete engine[i];
00349 }
00350 delete [] engine;
00351 }
00352 }
00353
00354 char* ColorBalanceMain::plugin_title() { return N_("Color Balance"); }
00355 int ColorBalanceMain::is_realtime() { return 1; }
00356
00357
00358 int ColorBalanceMain::reconfigure()
00359 {
00360 int r_n, g_n, b_n;
00361 float r_scale = calculate_transfer(config.cyan);
00362 float g_scale = calculate_transfer(config.magenta);
00363 float b_scale = calculate_transfer(config.yellow);
00364
00365
00366
00367 #define RECONFIGURE(r_lookup, g_lookup, b_lookup, max) \
00368 for(int i = 0; i <= max; i++) \
00369 { \
00370 r_lookup[i] = CLIP((int)(r_scale * i), 0, max); \
00371 g_lookup[i] = CLIP((int)(g_scale * i), 0, max); \
00372 b_lookup[i] = CLIP((int)(b_scale * i), 0, max); \
00373 }
00374
00375 RECONFIGURE(r_lookup_8, g_lookup_8, b_lookup_8, 0xff);
00376 RECONFIGURE(r_lookup_16, g_lookup_16, b_lookup_16, 0xffff);
00377
00378 return 0;
00379 }
00380
00381 int64_t ColorBalanceMain::calculate_slider(float in)
00382 {
00383 if(in < 1.0)
00384 {
00385 return (int64_t)(in * 1000 - 1000.0);
00386 }
00387 else
00388 if(in > 1.0)
00389 {
00390 return (int64_t)(1000 * (in - 1.0) / MAX_COLOR);
00391 }
00392 else
00393 return 0;
00394 }
00395
00396 float ColorBalanceMain::calculate_transfer(float in)
00397 {
00398 if(in < 0)
00399 {
00400 return (1000.0 + in) / 1000.0;
00401 }
00402 else
00403 if(in > 0)
00404 {
00405 return 1.0 + in / 1000.0 * MAX_COLOR;
00406 }
00407 else
00408 return 1.0;
00409 }
00410
00411
00412
00413
00414 int ColorBalanceMain::test_boundary(float &value)
00415 {
00416
00417 if(value < -1000) value = -1000;
00418 if(value > 1000) value = 1000;
00419 return 0;
00420 }
00421
00422 int ColorBalanceMain::synchronize_params(ColorBalanceSlider *slider, float difference)
00423 {
00424 if(thread && config.lock_params)
00425 {
00426 if(slider != thread->window->cyan)
00427 {
00428 config.cyan += difference;
00429 test_boundary(config.cyan);
00430 thread->window->cyan->update((int64_t)config.cyan);
00431 }
00432 if(slider != thread->window->magenta)
00433 {
00434 config.magenta += difference;
00435 test_boundary(config.magenta);
00436 thread->window->magenta->update((int64_t)config.magenta);
00437 }
00438 if(slider != thread->window->yellow)
00439 {
00440 config.yellow += difference;
00441 test_boundary(config.yellow);
00442 thread->window->yellow->update((int64_t)config.yellow);
00443 }
00444 }
00445 return 0;
00446 }
00447
00448
00449
00450
00451
00452
00453 NEW_PICON_MACRO(ColorBalanceMain)
00454 LOAD_CONFIGURATION_MACRO(ColorBalanceMain, ColorBalanceConfig)
00455 SHOW_GUI_MACRO(ColorBalanceMain, ColorBalanceThread)
00456 RAISE_WINDOW_MACRO(ColorBalanceMain)
00457 SET_STRING_MACRO(ColorBalanceMain)
00458
00459
00460
00461
00462
00463 int ColorBalanceMain::process_buffer(VFrame *frame,
00464 int64_t start_position,
00465 double frame_rate)
00466 {
00467 need_reconfigure |= load_configuration();
00468
00469
00470 if(need_reconfigure)
00471 {
00472 int i;
00473
00474 if(!engine)
00475 {
00476 total_engines = PluginClient::smp > 1 ? 2 : 1;
00477 engine = new ColorBalanceEngine*[total_engines];
00478 for(int i = 0; i < total_engines; i++)
00479 {
00480 engine[i] = new ColorBalanceEngine(this);
00481 engine[i]->start();
00482 }
00483 }
00484
00485 reconfigure();
00486 need_reconfigure = 0;
00487 }
00488
00489 frame->get_params()->update("COLORBALANCE_PRESERVE", config.preserve);
00490 frame->get_params()->update("COLORBALANCE_CYAN", calculate_transfer(config.cyan));
00491 frame->get_params()->update("COLORBALANCE_MAGENTA", calculate_transfer(config.magenta));
00492 frame->get_params()->update("COLORBALANCE_YELLOW", calculate_transfer(config.yellow));
00493
00494
00495 read_frame(frame,
00496 0,
00497 get_source_position(),
00498 get_framerate(),
00499 get_use_opengl());
00500
00501 int aggregate_interpolate = 0;
00502 int aggregate_gamma = 0;
00503 get_aggregation(&aggregate_interpolate,
00504 &aggregate_gamma);
00505
00506 if(!EQUIV(config.cyan, 0) ||
00507 !EQUIV(config.magenta, 0) ||
00508 !EQUIV(config.yellow, 0) ||
00509 (get_use_opengl() &&
00510 (aggregate_interpolate ||
00511 aggregate_gamma)))
00512 {
00513 if(get_use_opengl())
00514 {
00515 get_output()->dump_stacks();
00516
00517 if(next_effect_is("Histogram")) return 0;
00518 return run_opengl();
00519 }
00520
00521 for(int i = 0; i < total_engines; i++)
00522 {
00523 engine[i]->start_process_frame(frame,
00524 frame,
00525 frame->get_h() * i / total_engines,
00526 frame->get_h() * (i + 1) / total_engines);
00527 }
00528
00529 for(int i = 0; i < total_engines; i++)
00530 {
00531 engine[i]->wait_process_frame();
00532 }
00533 }
00534
00535
00536 return 0;
00537 }
00538
00539
00540 void ColorBalanceMain::update_gui()
00541 {
00542 if(thread)
00543 {
00544 load_configuration();
00545 thread->window->lock_window("ColorBalanceMain::update_gui");
00546 thread->window->cyan->update((int64_t)config.cyan);
00547 thread->window->magenta->update((int64_t)config.magenta);
00548 thread->window->yellow->update((int64_t)config.yellow);
00549 thread->window->preserve->update(config.preserve);
00550 thread->window->lock_params->update(config.lock_params);
00551 thread->window->unlock_window();
00552 }
00553 }
00554
00555
00556
00557 int ColorBalanceMain::load_defaults()
00558 {
00559 char directory[1024], string[1024];
00560
00561 sprintf(directory, "%scolorbalance.rc", BCASTDIR);
00562
00563
00564 defaults = new BC_Hash(directory);
00565 defaults->load();
00566
00567 config.cyan = defaults->get("CYAN", config.cyan);
00568 config.magenta = defaults->get("MAGENTA", config.magenta);
00569 config.yellow = defaults->get("YELLOW", config.yellow);
00570 config.preserve = defaults->get("PRESERVELUMINOSITY", config.preserve);
00571 config.lock_params = defaults->get("LOCKPARAMS", config.lock_params);
00572 return 0;
00573 }
00574
00575 int ColorBalanceMain::save_defaults()
00576 {
00577 defaults->update("CYAN", config.cyan);
00578 defaults->update("MAGENTA", config.magenta);
00579 defaults->update("YELLOW", config.yellow);
00580 defaults->update("PRESERVELUMINOSITY", config.preserve);
00581 defaults->update("LOCKPARAMS", config.lock_params);
00582 defaults->save();
00583 return 0;
00584 }
00585
00586 void ColorBalanceMain::save_data(KeyFrame *keyframe)
00587 {
00588 FileXML output;
00589
00590
00591 output.set_shared_string(keyframe->data, MESSAGESIZE);
00592 output.tag.set_title("COLORBALANCE");
00593 output.tag.set_property("CYAN", config.cyan);
00594 output.tag.set_property("MAGENTA", config.magenta);
00595 output.tag.set_property("YELLOW", config.yellow);
00596 output.tag.set_property("PRESERVELUMINOSITY", config.preserve);
00597 output.tag.set_property("LOCKPARAMS", config.lock_params);
00598 output.append_tag();
00599 output.tag.set_title("/COLORBALANCE");
00600 output.append_tag();
00601 output.terminate_string();
00602 }
00603
00604 void ColorBalanceMain::read_data(KeyFrame *keyframe)
00605 {
00606 FileXML input;
00607
00608 input.set_shared_string(keyframe->data, strlen(keyframe->data));
00609
00610 int result = 0;
00611
00612 while(!result)
00613 {
00614 result = input.read_tag();
00615
00616 if(!result)
00617 {
00618 if(input.tag.title_is("COLORBALANCE"))
00619 {
00620 config.cyan = input.tag.get_property("CYAN", config.cyan);
00621 config.magenta = input.tag.get_property("MAGENTA", config.magenta);
00622 config.yellow = input.tag.get_property("YELLOW", config.yellow);
00623 config.preserve = input.tag.get_property("PRESERVELUMINOSITY", config.preserve);
00624 config.lock_params = input.tag.get_property("LOCKPARAMS", config.lock_params);
00625 }
00626 }
00627 }
00628 }
00629
00630 void ColorBalanceMain::get_aggregation(int *aggregate_interpolate,
00631 int *aggregate_gamma)
00632 {
00633 if(!strcmp(get_output()->get_prev_effect(1), "Interpolate Pixels") &&
00634 !strcmp(get_output()->get_prev_effect(0), "Gamma"))
00635 {
00636 *aggregate_interpolate = 1;
00637 *aggregate_gamma = 1;
00638 }
00639 else
00640 if(!strcmp(get_output()->get_prev_effect(0), "Interpolate Pixels"))
00641 {
00642 *aggregate_interpolate = 1;
00643 }
00644 else
00645 if(!strcmp(get_output()->get_prev_effect(0), "Gamma"))
00646 {
00647 *aggregate_gamma = 1;
00648 }
00649 }
00650
00651 int ColorBalanceMain::handle_opengl()
00652 {
00653 #ifdef HAVE_GL
00654
00655 get_output()->to_texture();
00656 get_output()->enable_opengl();
00657
00658 unsigned int shader = 0;
00659 char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
00660 int current_shader = 0;
00661 int aggregate_interpolate = 0;
00662 int aggregate_gamma = 0;
00663
00664 get_aggregation(&aggregate_interpolate,
00665 &aggregate_gamma);
00666
00667 printf("ColorBalanceMain::handle_opengl %d %d\n", aggregate_interpolate, aggregate_gamma);
00668 if(aggregate_interpolate)
00669 INTERPOLATE_COMPILE(shader_stack, current_shader)
00670
00671 if(aggregate_gamma)
00672 GAMMA_COMPILE(shader_stack, current_shader, aggregate_interpolate)
00673
00674 COLORBALANCE_COMPILE(shader_stack,
00675 current_shader,
00676 aggregate_gamma || aggregate_interpolate)
00677
00678 shader = VFrame::make_shader(0,
00679 shader_stack[0],
00680 shader_stack[1],
00681 shader_stack[2],
00682 shader_stack[3],
00683 shader_stack[4],
00684 shader_stack[5],
00685 shader_stack[6],
00686 shader_stack[7],
00687 0);
00688
00689 if(shader > 0)
00690 {
00691 glUseProgram(shader);
00692 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
00693
00694 if(aggregate_interpolate) INTERPOLATE_UNIFORMS(shader);
00695 if(aggregate_gamma) GAMMA_UNIFORMS(shader);
00696
00697 COLORBALANCE_UNIFORMS(shader);
00698
00699 }
00700
00701 get_output()->init_screen();
00702 get_output()->bind_texture(0);
00703 get_output()->draw_texture();
00704 glUseProgram(0);
00705 get_output()->set_opengl_state(VFrame::SCREEN);
00706 #endif
00707 }
00708
00709
00710
00711
00712
00713
00714