00001 #include "bcsignals.h"
00002 #include "filexml.h"
00003 #include "gamma.h"
00004 #include "bchash.h"
00005 #include "language.h"
00006 #include "picon_png.h"
00007 #include "plugincolors.h"
00008 #include "../interpolate/aggregated.h"
00009 #include "playback3d.h"
00010 #include "workarounds.h"
00011
00012
00013 #include <stdio.h>
00014 #include <string.h>
00015
00016 #include "aggregated.h"
00017
00018 #define SQR(a) ((a) * (a))
00019
00020 REGISTER_PLUGIN(GammaMain)
00021
00022
00023
00024 GammaConfig::GammaConfig()
00025 {
00026 max = 1;
00027 gamma = 0.6;
00028 automatic = 1;
00029 plot = 1;
00030 }
00031
00032 int GammaConfig::equivalent(GammaConfig &that)
00033 {
00034 return (EQUIV(max, that.max) &&
00035 EQUIV(gamma, that.gamma) &&
00036 automatic == that.automatic) &&
00037 plot == that.plot;
00038 }
00039
00040 void GammaConfig::copy_from(GammaConfig &that)
00041 {
00042 max = that.max;
00043 gamma = that.gamma;
00044 automatic = that.automatic;
00045 plot = that.plot;
00046 }
00047
00048 void GammaConfig::interpolate(GammaConfig &prev,
00049 GammaConfig &next,
00050 int64_t prev_frame,
00051 int64_t next_frame,
00052 int64_t current_frame)
00053 {
00054 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
00055 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
00056
00057 this->max = prev.max * prev_scale + next.max * next_scale;
00058 this->gamma = prev.gamma * prev_scale + next.gamma * next_scale;
00059 this->automatic = prev.automatic;
00060 this->plot = prev.plot;
00061 }
00062
00063
00064
00065
00066
00067
00068
00069
00070 GammaPackage::GammaPackage()
00071 : LoadPackage()
00072 {
00073 start = end = 0;
00074 }
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085 GammaUnit::GammaUnit(GammaMain *plugin)
00086 {
00087 this->plugin = plugin;
00088 }
00089
00090
00091 void GammaUnit::process_package(LoadPackage *package)
00092 {
00093 GammaPackage *pkg = (GammaPackage*)package;
00094 GammaEngine *engine = (GammaEngine*)get_server();
00095 VFrame *data = engine->data;
00096 int w = data->get_w();
00097 float r, g, b, y, u, v;
00098
00099
00100 if(engine->operation == GammaEngine::HISTOGRAM)
00101 {
00102 #define HISTOGRAM_HEAD(type) \
00103 for(int i = pkg->start; i < pkg->end; i++) \
00104 { \
00105 type *row = (type*)data->get_rows()[i]; \
00106 for(int j = 0; j < w; j++) \
00107 {
00108
00109 #define HISTOGRAM_TAIL(components) \
00110 int slot; \
00111 slot = (int)(r * HISTOGRAM_SIZE); \
00112 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
00113 slot = (int)(g * HISTOGRAM_SIZE); \
00114 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
00115 slot = (int)(b * HISTOGRAM_SIZE); \
00116 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
00117 row += components; \
00118 } \
00119 }
00120
00121
00122 switch(data->get_color_model())
00123 {
00124 case BC_RGB888:
00125 HISTOGRAM_HEAD(unsigned char)
00126 r = (float)row[0] / 0xff;
00127 g = (float)row[1] / 0xff;
00128 b = (float)row[2] / 0xff;
00129 HISTOGRAM_TAIL(3)
00130 break;
00131 case BC_RGBA8888:
00132 HISTOGRAM_HEAD(unsigned char)
00133 r = (float)row[0] / 0xff;
00134 g = (float)row[1] / 0xff;
00135 b = (float)row[2] / 0xff;
00136 HISTOGRAM_TAIL(4)
00137 break;
00138 case BC_RGB_FLOAT:
00139 HISTOGRAM_HEAD(float)
00140 r = row[0];
00141 g = row[1];
00142 b = row[2];
00143 HISTOGRAM_TAIL(3)
00144 break;
00145 case BC_RGBA_FLOAT:
00146 HISTOGRAM_HEAD(float)
00147 r = row[0];
00148 g = row[1];
00149 b = row[2];
00150 HISTOGRAM_TAIL(4)
00151 break;
00152 case BC_YUV888:
00153 HISTOGRAM_HEAD(unsigned char)
00154 y = row[0];
00155 u = row[1];
00156 v = row[2];
00157 y /= 0xff;
00158 u = (float)((u - 0x80) / 0xff);
00159 v = (float)((v - 0x80) / 0xff);
00160 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
00161 HISTOGRAM_TAIL(3)
00162 break;
00163 case BC_YUVA8888:
00164 HISTOGRAM_HEAD(unsigned char)
00165 y = (float)row[0] / 0xff;
00166 u = (float)row[1] / 0xff;
00167 v = (float)row[2] / 0xff;
00168 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
00169 HISTOGRAM_TAIL(4)
00170 break;
00171 }
00172 }
00173 else
00174 {
00175 float max = plugin->config.max;
00176 float scale = 1.0 / max;
00177 float gamma = plugin->config.gamma - 1.0;
00178
00179 #define GAMMA_HEAD(type) \
00180 for(int i = pkg->start; i < pkg->end; i++) \
00181 { \
00182 type *row = (type*)data->get_rows()[i]; \
00183 for(int j = 0; j < w; j++) \
00184 {
00185
00186
00187
00188 #define MY_POW(x, y) ((x > 0.0) ? powf(x * 2 / max, y) : 0.0)
00189
00190 #define GAMMA_MID \
00191 r = r * scale * MY_POW(r, gamma); \
00192 g = g * scale * MY_POW(g, gamma); \
00193 b = b * scale * MY_POW(b, gamma); \
00194
00195 #define GAMMA_TAIL(components) \
00196 row += components; \
00197 } \
00198 }
00199
00200
00201 switch(data->get_color_model())
00202 {
00203 case BC_RGB888:
00204 GAMMA_HEAD(unsigned char)
00205 r = (float)row[0] / 0xff;
00206 g = (float)row[1] / 0xff;
00207 b = (float)row[2] / 0xff;
00208 GAMMA_MID
00209 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
00210 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
00211 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
00212 GAMMA_TAIL(3)
00213 break;
00214 case BC_RGBA8888:
00215 GAMMA_HEAD(unsigned char)
00216 r = (float)row[0] / 0xff;
00217 g = (float)row[1] / 0xff;
00218 b = (float)row[2] / 0xff;
00219 GAMMA_MID
00220 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
00221 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
00222 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
00223 GAMMA_TAIL(4)
00224 break;
00225 case BC_RGB_FLOAT:
00226 GAMMA_HEAD(float)
00227 r = row[0];
00228 g = row[1];
00229 b = row[2];
00230 GAMMA_MID
00231 row[0] = r;
00232 row[1] = g;
00233 row[2] = b;
00234 GAMMA_TAIL(3)
00235 break;
00236 case BC_RGBA_FLOAT:
00237 GAMMA_HEAD(float)
00238 r = row[0];
00239 g = row[1];
00240 b = row[2];
00241 GAMMA_MID
00242 row[0] = r;
00243 row[1] = g;
00244 row[2] = b;
00245 GAMMA_TAIL(4)
00246 break;
00247 case BC_YUV888:
00248 GAMMA_HEAD(unsigned char)
00249 y = row[0];
00250 u = row[1];
00251 v = row[2];
00252 y /= 0xff;
00253 u = (float)((u - 0x80) / 0xff);
00254 v = (float)((v - 0x80) / 0xff);
00255 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
00256 GAMMA_MID
00257 YUV::rgb_to_yuv_f(r, g, b, y, u, v);
00258 y *= 0xff;
00259 u = u * 0xff + 0x80;
00260 v = v * 0xff + 0x80;
00261 row[0] = (int)CLIP(y, 0, 0xff);
00262 row[1] = (int)CLIP(u, 0, 0xff);
00263 row[2] = (int)CLIP(v, 0, 0xff);
00264 GAMMA_TAIL(3)
00265 break;
00266 case BC_YUVA8888:
00267 GAMMA_HEAD(unsigned char)
00268 y = row[0];
00269 u = row[1];
00270 v = row[2];
00271 y /= 0xff;
00272 u = (float)((u - 0x80) / 0xff);
00273 v = (float)((v - 0x80) / 0xff);
00274 YUV::yuv_to_rgb_f(r, g, b, y, u, v);
00275 GAMMA_MID
00276 YUV::rgb_to_yuv_f(r, g, b, y, u, v);
00277 y *= 0xff;
00278 u = u * 0xff + 0x80;
00279 v = v * 0xff + 0x80;
00280 row[0] = (int)CLIP(y, 0, 0xff);
00281 row[1] = (int)CLIP(u, 0, 0xff);
00282 row[2] = (int)CLIP(v, 0, 0xff);
00283 GAMMA_TAIL(4)
00284 break;
00285 }
00286 }
00287 }
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299 GammaEngine::GammaEngine(GammaMain *plugin)
00300 : LoadServer(plugin->get_project_smp() + 1,
00301 plugin->get_project_smp() + 1)
00302 {
00303 this->plugin = plugin;
00304 }
00305
00306 void GammaEngine::init_packages()
00307 {
00308 for(int i = 0; i < get_total_packages(); i++)
00309 {
00310 GammaPackage *package = (GammaPackage*)get_package(i);
00311 package->start = data->get_h() * i / get_total_packages();
00312 package->end = data->get_h() * (i + 1) / get_total_packages();
00313 }
00314
00315
00316 for(int i = 0; i < get_total_clients(); i++)
00317 {
00318 GammaUnit *unit = (GammaUnit*)get_client(i);
00319 bzero(unit->accum, sizeof(int) * HISTOGRAM_SIZE);
00320 }
00321 bzero(accum, sizeof(int) * HISTOGRAM_SIZE);
00322 }
00323
00324 LoadClient* GammaEngine::new_client()
00325 {
00326 return new GammaUnit(plugin);
00327 }
00328
00329 LoadPackage* GammaEngine::new_package()
00330 {
00331 return new GammaPackage;
00332 }
00333
00334 void GammaEngine::process_packages(int operation, VFrame *data)
00335 {
00336 this->data = data;
00337 this->operation = operation;
00338 LoadServer::process_packages();
00339 for(int i = 0; i < get_total_clients(); i++)
00340 {
00341 GammaUnit *unit = (GammaUnit*)get_client(i);
00342 for(int j = 0; j < HISTOGRAM_SIZE; j++)
00343 {
00344 accum[j] += unit->accum[j];
00345 }
00346 }
00347 }
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364 GammaMain::GammaMain(PluginServer *server)
00365 : PluginVClient(server)
00366 {
00367 engine = 0;
00368 PLUGIN_CONSTRUCTOR_MACRO
00369 }
00370
00371 GammaMain::~GammaMain()
00372 {
00373 PLUGIN_DESTRUCTOR_MACRO
00374
00375 delete engine;
00376 }
00377
00378 char* GammaMain::plugin_title() { return N_("Gamma"); }
00379 int GammaMain::is_realtime() { return 1; }
00380
00381
00382
00383
00384
00385 NEW_PICON_MACRO(GammaMain)
00386 LOAD_CONFIGURATION_MACRO(GammaMain, GammaConfig)
00387 SHOW_GUI_MACRO(GammaMain, GammaThread)
00388 RAISE_WINDOW_MACRO(GammaMain)
00389 SET_STRING_MACRO(GammaMain)
00390
00391
00392
00393
00394
00395 int GammaMain::process_buffer(VFrame *frame,
00396 int64_t start_position,
00397 double frame_rate)
00398 {
00399 this->frame = frame;
00400 load_configuration();
00401
00402 frame->get_params()->update("GAMMA_GAMMA", config.gamma);
00403 frame->get_params()->update("GAMMA_MAX", config.max);
00404
00405 int use_opengl = get_use_opengl() &&
00406 !config.automatic &&
00407 (!config.plot || !gui_open());
00408
00409 read_frame(frame,
00410 0,
00411 start_position,
00412 frame_rate,
00413 use_opengl);
00414
00415 if(use_opengl)
00416 {
00417
00418 if(next_effect_is("Histogram"))
00419 return 0;
00420 if(next_effect_is("Color Balance"))
00421 return 0;
00422
00423
00424 return run_opengl();
00425 }
00426 else
00427 if(config.automatic)
00428 {
00429 calculate_max(frame);
00430
00431 send_render_gui(this);
00432 }
00433 else
00434 if(config.plot)
00435 {
00436 send_render_gui(this);
00437 }
00438
00439 if(!engine) engine = new GammaEngine(this);
00440 engine->process_packages(GammaEngine::APPLY, frame);
00441 return 0;
00442 }
00443
00444 void GammaMain::calculate_max(VFrame *frame)
00445 {
00446 if(!engine) engine = new GammaEngine(this);
00447 engine->process_packages(GammaEngine::HISTOGRAM, frame);
00448 int total_pixels = frame->get_w() * frame->get_h() * 3;
00449 int max_fraction = (int)((int64_t)total_pixels * 99 / 100);
00450 int current = 0;
00451 config.max = 1;
00452 for(int i = 0; i < HISTOGRAM_SIZE; i++)
00453 {
00454 current += engine->accum[i];
00455 if(current > max_fraction)
00456 {
00457 config.max = (float)i / HISTOGRAM_SIZE;
00458 break;
00459 }
00460 }
00461 }
00462
00463
00464 void GammaMain::update_gui()
00465 {
00466 if(thread)
00467 {
00468 if(load_configuration())
00469 {
00470 thread->window->lock_window("GammaMain::update_gui");
00471 thread->window->update();
00472 thread->window->unlock_window();
00473 }
00474 }
00475 }
00476
00477 void GammaMain::render_gui(void *data)
00478 {
00479 GammaMain *ptr = (GammaMain*)data;
00480 config.max = ptr->config.max;
00481
00482 if(!engine) engine = new GammaEngine(this);
00483 if(ptr->engine && ptr->config.automatic)
00484 {
00485 memcpy(engine->accum,
00486 ptr->engine->accum,
00487 sizeof(int) * HISTOGRAM_SIZE);
00488 thread->window->lock_window("GammaMain::render_gui");
00489 thread->window->update();
00490 thread->window->unlock_window();
00491 }
00492 else
00493 {
00494 engine->process_packages(GammaEngine::HISTOGRAM,
00495 ptr->frame);
00496 thread->window->lock_window("GammaMain::render_gui");
00497 thread->window->update_histogram();
00498 thread->window->unlock_window();
00499 }
00500
00501
00502 }
00503
00504 int GammaMain::load_defaults()
00505 {
00506 char directory[1024], string[1024];
00507
00508 sprintf(directory, "%sgamma.rc", BCASTDIR);
00509
00510
00511 defaults = new BC_Hash(directory);
00512 defaults->load();
00513
00514 config.max = defaults->get("MAX", config.max);
00515 config.gamma = defaults->get("GAMMA", config.gamma);
00516 config.automatic = defaults->get("AUTOMATIC", config.automatic);
00517 config.plot = defaults->get("PLOT", config.plot);
00518 return 0;
00519 }
00520
00521 int GammaMain::save_defaults()
00522 {
00523 defaults->update("MAX", config.max);
00524 defaults->update("GAMMA", config.gamma);
00525 defaults->update("AUTOMATIC", config.automatic);
00526 defaults->update("PLOT", config.plot);
00527 defaults->save();
00528 return 0;
00529 }
00530
00531 void GammaMain::save_data(KeyFrame *keyframe)
00532 {
00533 FileXML output;
00534
00535
00536 output.set_shared_string(keyframe->data, MESSAGESIZE);
00537 output.tag.set_title("GAMMA");
00538 output.tag.set_property("MAX", config.max);
00539 output.tag.set_property("GAMMA", config.gamma);
00540 output.tag.set_property("AUTOMATIC", config.automatic);
00541 output.tag.set_property("PLOT", config.plot);
00542 output.append_tag();
00543 output.tag.set_title("/GAMMA");
00544 output.append_tag();
00545 output.terminate_string();
00546 }
00547
00548 void GammaMain::read_data(KeyFrame *keyframe)
00549 {
00550 FileXML input;
00551
00552 input.set_shared_string(keyframe->data, strlen(keyframe->data));
00553
00554 int result = 0;
00555
00556 while(!result)
00557 {
00558 result = input.read_tag();
00559
00560 if(!result)
00561 {
00562 if(input.tag.title_is("GAMMA"))
00563 {
00564 config.max = input.tag.get_property("MAX", config.max);
00565 config.gamma = input.tag.get_property("GAMMA", config.gamma);
00566 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
00567 config.plot = input.tag.get_property("PLOT", config.plot);
00568
00569 }
00570 }
00571 }
00572 }
00573
00574 int GammaMain::handle_opengl()
00575 {
00576 #ifdef HAVE_GL
00577
00578
00579 get_output()->to_texture();
00580 get_output()->enable_opengl();
00581
00582
00583 char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
00584 int current_shader = 0;
00585
00586
00587
00588 int aggregate = 0;
00589 if(prev_effect_is("Interpolate Pixels"))
00590 {
00591 aggregate = 1;
00592 INTERPOLATE_COMPILE(shader_stack, current_shader)
00593 }
00594
00595 GAMMA_COMPILE(shader_stack, current_shader, aggregate);
00596
00597 unsigned int shader = VFrame::make_shader(0,
00598 shader_stack[0],
00599 shader_stack[1],
00600 shader_stack[2],
00601 shader_stack[3],
00602 shader_stack[4],
00603 shader_stack[5],
00604 shader_stack[6],
00605 shader_stack[7],
00606 0);
00607
00608
00609 if(shader > 0)
00610 {
00611 glUseProgram(shader);
00612 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
00613
00614 if(aggregate)
00615 {
00616 INTERPOLATE_UNIFORMS(shader)
00617 }
00618 GAMMA_UNIFORMS(shader)
00619 }
00620
00621 get_output()->init_screen();
00622 get_output()->bind_texture(0);
00623 get_output()->draw_texture();
00624 glUseProgram(0);
00625 get_output()->set_opengl_state(VFrame::SCREEN);
00626 #endif
00627 }
00628
00629
00630
00631
00632
00633
00634
00635
00636