00001 #include <math.h>
00002 #include <stdint.h>
00003 #include <string.h>
00004
00005 #include "bcdisplayinfo.h"
00006 #include "clip.h"
00007 #include "bchash.h"
00008 #include "filexml.h"
00009 #include "keyframe.h"
00010 #include "language.h"
00011 #include "loadbalance.h"
00012 #include "picon_png.h"
00013 #include "pluginvclient.h"
00014 #include "vframe.h"
00015
00016
00017 class MotionBlurMain;
00018 class MotionBlurEngine;
00019
00020
00021
00022
00023
00024 class MotionBlurConfig
00025 {
00026 public:
00027 MotionBlurConfig();
00028
00029 int equivalent(MotionBlurConfig &that);
00030 void copy_from(MotionBlurConfig &that);
00031 void interpolate(MotionBlurConfig &prev,
00032 MotionBlurConfig &next,
00033 long prev_frame,
00034 long next_frame,
00035 long current_frame);
00036
00037 int radius;
00038 int steps;
00039 int r;
00040 int g;
00041 int b;
00042 int a;
00043 };
00044
00045
00046
00047 class MotionBlurSize : public BC_ISlider
00048 {
00049 public:
00050 MotionBlurSize(MotionBlurMain *plugin,
00051 int x,
00052 int y,
00053 int *output,
00054 int min,
00055 int max);
00056 int handle_event();
00057 MotionBlurMain *plugin;
00058 int *output;
00059 };
00060
00061
00062 class MotionBlurWindow : public BC_Window
00063 {
00064 public:
00065 MotionBlurWindow(MotionBlurMain *plugin, int x, int y);
00066 ~MotionBlurWindow();
00067
00068 int create_objects();
00069 int close_event();
00070
00071 MotionBlurSize *steps, *radius;
00072 MotionBlurMain *plugin;
00073 };
00074
00075
00076
00077 PLUGIN_THREAD_HEADER(MotionBlurMain, MotionBlurThread, MotionBlurWindow)
00078
00079
00080 class MotionBlurMain : public PluginVClient
00081 {
00082 public:
00083 MotionBlurMain(PluginServer *server);
00084 ~MotionBlurMain();
00085
00086 int process_realtime(VFrame *input_ptr, VFrame *output_ptr);
00087 int is_realtime();
00088 int load_defaults();
00089 int save_defaults();
00090 void save_data(KeyFrame *keyframe);
00091 void read_data(KeyFrame *keyframe);
00092 void update_gui();
00093
00094 PLUGIN_CLASS_MEMBERS(MotionBlurConfig, MotionBlurThread)
00095
00096 void delete_tables();
00097 VFrame *input, *output, *temp;
00098 MotionBlurEngine *engine;
00099 int **scale_y_table;
00100 int **scale_x_table;
00101 int table_entries;
00102 unsigned char *accum;
00103 };
00104
00105 class MotionBlurPackage : public LoadPackage
00106 {
00107 public:
00108 MotionBlurPackage();
00109 int y1, y2;
00110 };
00111
00112 class MotionBlurUnit : public LoadClient
00113 {
00114 public:
00115 MotionBlurUnit(MotionBlurEngine *server, MotionBlurMain *plugin);
00116 void process_package(LoadPackage *package);
00117 MotionBlurEngine *server;
00118 MotionBlurMain *plugin;
00119 };
00120
00121 class MotionBlurEngine : public LoadServer
00122 {
00123 public:
00124 MotionBlurEngine(MotionBlurMain *plugin,
00125 int total_clients,
00126 int total_packages);
00127 void init_packages();
00128 LoadClient* new_client();
00129 LoadPackage* new_package();
00130 MotionBlurMain *plugin;
00131 };
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151 REGISTER_PLUGIN(MotionBlurMain)
00152
00153
00154
00155 MotionBlurConfig::MotionBlurConfig()
00156 {
00157 radius = 10;
00158 steps = 10;
00159 r = 1;
00160 g = 1;
00161 b = 1;
00162 a = 1;
00163 }
00164
00165 int MotionBlurConfig::equivalent(MotionBlurConfig &that)
00166 {
00167 return
00168 radius == that.radius &&
00169 steps == that.steps &&
00170 r == that.r &&
00171 g == that.g &&
00172 b == that.b &&
00173 a == that.a;
00174 }
00175
00176 void MotionBlurConfig::copy_from(MotionBlurConfig &that)
00177 {
00178 radius = that.radius;
00179 steps = that.steps;
00180 r = that.r;
00181 g = that.g;
00182 b = that.b;
00183 a = that.a;
00184 }
00185
00186 void MotionBlurConfig::interpolate(MotionBlurConfig &prev,
00187 MotionBlurConfig &next,
00188 long prev_frame,
00189 long next_frame,
00190 long current_frame)
00191 {
00192 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
00193 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
00194 this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
00195 this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
00196 r = prev.r;
00197 g = prev.g;
00198 b = prev.b;
00199 a = prev.a;
00200 }
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210 PLUGIN_THREAD_OBJECT(MotionBlurMain, MotionBlurThread, MotionBlurWindow)
00211
00212
00213
00214 MotionBlurWindow::MotionBlurWindow(MotionBlurMain *plugin, int x, int y)
00215 : BC_Window(plugin->gui_string,
00216 x,
00217 y,
00218 260,
00219 120,
00220 260,
00221 120,
00222 0,
00223 1)
00224 {
00225 this->plugin = plugin;
00226 }
00227
00228 MotionBlurWindow::~MotionBlurWindow()
00229 {
00230 }
00231
00232 int MotionBlurWindow::create_objects()
00233 {
00234 int x = 10, y = 10;
00235
00236 add_subwindow(new BC_Title(x, y, _("Length:")));
00237 y += 20;
00238 add_subwindow(radius = new MotionBlurSize(plugin, x, y, &plugin->config.radius, 0, 100));
00239 y += 30;
00240 add_subwindow(new BC_Title(x, y, _("Steps:")));
00241 y += 20;
00242 add_subwindow(steps = new MotionBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
00243
00244 show_window();
00245 flush();
00246 return 0;
00247 }
00248
00249 WINDOW_CLOSE_EVENT(MotionBlurWindow)
00250
00251
00252
00253 MotionBlurSize::MotionBlurSize(MotionBlurMain *plugin,
00254 int x,
00255 int y,
00256 int *output,
00257 int min,
00258 int max)
00259 : BC_ISlider(x, y, 0, 240, 240, min, max, *output)
00260 {
00261 this->plugin = plugin;
00262 this->output = output;
00263 }
00264 int MotionBlurSize::handle_event()
00265 {
00266 *output = get_value();
00267 plugin->send_configure_change();
00268 return 1;
00269 }
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280 MotionBlurMain::MotionBlurMain(PluginServer *server)
00281 : PluginVClient(server)
00282 {
00283 PLUGIN_CONSTRUCTOR_MACRO
00284 engine = 0;
00285 scale_x_table = 0;
00286 scale_y_table = 0;
00287 table_entries = 0;
00288 accum = 0;
00289 temp = 0;
00290 }
00291
00292 MotionBlurMain::~MotionBlurMain()
00293 {
00294 PLUGIN_DESTRUCTOR_MACRO
00295 if(engine) delete engine;
00296 delete_tables();
00297 if(accum) delete [] accum;
00298 if(temp) delete temp;
00299 }
00300
00301 char* MotionBlurMain::plugin_title() { return N_("Motion Blur"); }
00302 int MotionBlurMain::is_realtime() { return 1; }
00303
00304
00305 NEW_PICON_MACRO(MotionBlurMain)
00306
00307 SHOW_GUI_MACRO(MotionBlurMain, MotionBlurThread)
00308
00309 SET_STRING_MACRO(MotionBlurMain)
00310
00311 RAISE_WINDOW_MACRO(MotionBlurMain)
00312
00313 LOAD_CONFIGURATION_MACRO(MotionBlurMain, MotionBlurConfig)
00314
00315 void MotionBlurMain::delete_tables()
00316 {
00317 if(scale_x_table)
00318 {
00319 for(int i = 0; i < table_entries; i++)
00320 delete [] scale_x_table[i];
00321 delete [] scale_x_table;
00322 }
00323
00324 if(scale_y_table)
00325 {
00326 for(int i = 0; i < table_entries; i++)
00327 delete [] scale_y_table[i];
00328 delete [] scale_y_table;
00329 }
00330 scale_x_table = 0;
00331 scale_y_table = 0;
00332 table_entries = 0;
00333 }
00334
00335 int MotionBlurMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
00336 {
00337 float xa,ya,za,xb,yb,zb,xd,yd,zd;
00338 if (get_source_position() == 0)
00339 get_camera(&xa, &ya, &za, get_source_position());
00340 else
00341 get_camera(&xa, &ya, &za, get_source_position()-1);
00342 get_camera(&xb, &yb, &zb, get_source_position());
00343
00344 xd = xb - xa;
00345 yd = yb - ya;
00346 zd = zb - za;
00347
00348
00349 load_configuration();
00350
00351
00352 if(!engine) engine = new MotionBlurEngine(this,
00353 get_project_smp() + 1,
00354 get_project_smp() + 1);
00355 if(!accum) accum = new unsigned char[input_ptr->get_w() *
00356 input_ptr->get_h() *
00357 cmodel_components(input_ptr->get_color_model()) *
00358 MAX(sizeof(int), sizeof(float))];
00359
00360 this->input = input_ptr;
00361 this->output = output_ptr;
00362
00363
00364 if(input_ptr->get_rows()[0] == output_ptr->get_rows()[0])
00365 {
00366 if(!temp) temp = new VFrame(0,
00367 input_ptr->get_w(),
00368 input_ptr->get_h(),
00369 input_ptr->get_color_model());
00370 temp->copy_from(input_ptr);
00371 this->input = temp;
00372 }
00373
00374
00375
00376
00377 float w = input->get_w();
00378 float h = input->get_h();
00379 int x_offset;
00380 int y_offset;
00381
00382 float fradius = config.radius * 0.5;
00383 float zradius = (float)(zd * fradius / 4 + 1);
00384 float center_x = w/2;
00385 float center_y = h/2;
00386
00387 float min_w, min_h;
00388 float max_w, max_h;
00389 float min_x1, min_y1, min_x2, min_y2;
00390 float max_x1, max_y1, max_x2, max_y2;
00391
00392 int steps = config.steps ? config.steps : 1;
00393
00394 x_offset = (int)(xd * fradius);
00395 y_offset = (int)(yd * fradius);
00396
00397 min_w = w * zradius;
00398 min_h = h * zradius;
00399 max_w = w;
00400 max_h = h;
00401 min_x1 = center_x - min_w / 2;
00402 min_y1 = center_y - min_h / 2;
00403 min_x2 = center_x + min_w / 2;
00404 min_y2 = center_y + min_h / 2;
00405 max_x1 = 0;
00406 max_y1 = 0;
00407 max_x2 = w;
00408 max_y2 = h;
00409
00410 delete_tables();
00411 scale_x_table = new int*[config.steps];
00412 scale_y_table = new int*[config.steps];
00413 table_entries = config.steps;
00414
00415 for(int i = 0; i < config.steps; i++)
00416 {
00417 float fraction = (float)(i - config.steps / 2) / config.steps;
00418 float inv_fraction = 1.0 - fraction;
00419
00420 int x = (int)(fraction * x_offset);
00421 int y = (int)(fraction * y_offset);
00422 float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
00423 float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
00424 float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
00425 float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
00426 float out_w = out_x2 - out_x1;
00427 float out_h = out_y2 - out_y1;
00428 if(out_w < 0) out_w = 0;
00429 if(out_h < 0) out_h = 0;
00430 float scale_x = (float)w / out_w;
00431 float scale_y = (float)h / out_h;
00432
00433 int *x_table;
00434 int *y_table;
00435 scale_y_table[i] = y_table = new int[(int)(h + 1)];
00436 scale_x_table[i] = x_table = new int[(int)(w + 1)];
00437
00438 for(int j = 0; j < h; j++)
00439 {
00440 y_table[j] = (int)((j - out_y1) * scale_y) + y;
00441 }
00442 for(int j = 0; j < w; j++)
00443 {
00444 x_table[j] = (int)((j - out_x1) * scale_x) + x;
00445 }
00446 }
00447
00448 bzero(accum,
00449 input_ptr->get_w() *
00450 input_ptr->get_h() *
00451 cmodel_components(input_ptr->get_color_model()) *
00452 MAX(sizeof(int), sizeof(float)));
00453 engine->process_packages();
00454 return 0;
00455 }
00456
00457
00458 void MotionBlurMain::update_gui()
00459 {
00460 if(thread)
00461 {
00462 load_configuration();
00463 thread->window->lock_window();
00464 thread->window->radius->update(config.radius);
00465 thread->window->steps->update(config.steps);
00466 thread->window->unlock_window();
00467 }
00468 }
00469
00470
00471 int MotionBlurMain::load_defaults()
00472 {
00473 char directory[1024], string[1024];
00474
00475 sprintf(directory, "%smotionblur.rc", BCASTDIR);
00476
00477
00478 defaults = new BC_Hash(directory);
00479 defaults->load();
00480
00481 config.radius = defaults->get("RADIUS", config.radius);
00482 config.steps = defaults->get("STEPS", config.steps);
00483 return 0;
00484 }
00485
00486
00487 int MotionBlurMain::save_defaults()
00488 {
00489 defaults->update("RADIUS", config.radius);
00490 defaults->update("STEPS", config.steps);
00491 defaults->save();
00492 return 0;
00493 }
00494
00495
00496
00497 void MotionBlurMain::save_data(KeyFrame *keyframe)
00498 {
00499 FileXML output;
00500
00501
00502 output.set_shared_string(keyframe->data, MESSAGESIZE);
00503 output.tag.set_title("MOTIONBLUR");
00504
00505 output.tag.set_property("RADIUS", config.radius);
00506 output.tag.set_property("STEPS", config.steps);
00507 output.append_tag();
00508 output.tag.set_title("/MOTIONBLUR");
00509 output.append_tag();
00510 output.terminate_string();
00511 }
00512
00513 void MotionBlurMain::read_data(KeyFrame *keyframe)
00514 {
00515 FileXML input;
00516
00517 input.set_shared_string(keyframe->data, strlen(keyframe->data));
00518
00519 int result = 0;
00520
00521 while(!result)
00522 {
00523 result = input.read_tag();
00524
00525 if(!result)
00526 {
00527 if(input.tag.title_is("MOTIONBLUR"))
00528 {
00529 config.radius = input.tag.get_property("RADIUS", config.radius);
00530 config.steps = input.tag.get_property("STEPS", config.steps);
00531 }
00532 }
00533 }
00534 }
00535
00536
00537
00538
00539
00540
00541 MotionBlurPackage::MotionBlurPackage()
00542 : LoadPackage()
00543 {
00544 }
00545
00546
00547
00548
00549 MotionBlurUnit::MotionBlurUnit(MotionBlurEngine *server,
00550 MotionBlurMain *plugin)
00551 : LoadClient(server)
00552 {
00553 this->plugin = plugin;
00554 this->server = server;
00555 }
00556
00557
00558 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP, MAX, DO_YUV) \
00559 { \
00560 const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
00561 for(int j = pkg->y1; j < pkg->y2; j++) \
00562 { \
00563 TEMP *out_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
00564 int in_y = y_table[j]; \
00565 \
00566 \
00567 if(in_y >= 0 && in_y < h) \
00568 { \
00569 TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
00570 for(int k = 0; k < w; k++) \
00571 { \
00572 int in_x = x_table[k]; \
00573 \
00574 if(in_x >= 0 && in_x < w) \
00575 { \
00576 int in_offset = in_x * COMPONENTS; \
00577 *out_row++ += in_row[in_offset]; \
00578 if(DO_YUV) \
00579 { \
00580 *out_row++ += in_row[in_offset + 1]; \
00581 *out_row++ += in_row[in_offset + 2]; \
00582 } \
00583 else \
00584 { \
00585 *out_row++ += in_row[in_offset + 1]; \
00586 *out_row++ += in_row[in_offset + 2]; \
00587 } \
00588 if(COMPONENTS == 4) \
00589 *out_row++ += in_row[in_offset + 3]; \
00590 } \
00591 \
00592 else \
00593 { \
00594 out_row++; \
00595 if(DO_YUV) \
00596 { \
00597 *out_row++ += chroma_offset; \
00598 *out_row++ += chroma_offset; \
00599 } \
00600 else \
00601 { \
00602 out_row += 2; \
00603 } \
00604 if(COMPONENTS == 4) out_row++; \
00605 } \
00606 } \
00607 } \
00608 else \
00609 if(DO_YUV) \
00610 { \
00611 for(int k = 0; k < w; k++) \
00612 { \
00613 out_row++; \
00614 *out_row++ += chroma_offset; \
00615 *out_row++ += chroma_offset; \
00616 if(COMPONENTS == 4) out_row++; \
00617 } \
00618 } \
00619 } \
00620 \
00621 \
00622 if(i == plugin->config.steps - 1) \
00623 { \
00624 for(int j = pkg->y1; j < pkg->y2; j++) \
00625 { \
00626 TEMP *in_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
00627 TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
00628 TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
00629 for(int k = 0; k < w; k++) \
00630 { \
00631 *out_row++ = (*in_row++ * fraction) / 0x10000; \
00632 in_backup++; \
00633 \
00634 if(DO_YUV) \
00635 { \
00636 *out_row++ = (*in_row++ * fraction) / 0x10000; \
00637 in_backup++; \
00638 \
00639 *out_row++ = (*in_row++ * fraction) / 0x10000; \
00640 in_backup++; \
00641 } \
00642 else \
00643 { \
00644 *out_row++ = (*in_row++ * fraction) / 0x10000; \
00645 in_backup++; \
00646 *out_row++ = (*in_row++ * fraction) / 0x10000; \
00647 in_backup++; \
00648 } \
00649 \
00650 if(COMPONENTS == 4) \
00651 { \
00652 *out_row++ = (*in_row++ * fraction) / 0x10000; \
00653 in_backup++; \
00654 } \
00655 } \
00656 } \
00657 } \
00658 }
00659
00660 void MotionBlurUnit::process_package(LoadPackage *package)
00661 {
00662 MotionBlurPackage *pkg = (MotionBlurPackage*)package;
00663 int h = plugin->output->get_h();
00664 int w = plugin->output->get_w();
00665
00666 int fraction = 0x10000 / plugin->config.steps;
00667 for(int i = 0; i < plugin->config.steps; i++)
00668 {
00669 int *x_table = plugin->scale_x_table[i];
00670 int *y_table = plugin->scale_y_table[i];
00671
00672 switch(plugin->input->get_color_model())
00673 {
00674 case BC_RGB_FLOAT:
00675 BLEND_LAYER(3, float, float, 1, 0)
00676 break;
00677 case BC_RGB888:
00678 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
00679 break;
00680 case BC_RGBA_FLOAT:
00681 BLEND_LAYER(4, float, float, 1, 0)
00682 break;
00683 case BC_RGBA8888:
00684 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
00685 break;
00686 case BC_RGB161616:
00687 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
00688 break;
00689 case BC_RGBA16161616:
00690 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
00691 break;
00692 case BC_YUV888:
00693 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
00694 break;
00695 case BC_YUVA8888:
00696 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
00697 break;
00698 case BC_YUV161616:
00699 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
00700 break;
00701 case BC_YUVA16161616:
00702 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
00703 break;
00704 }
00705 }
00706 }
00707
00708
00709
00710
00711
00712
00713 MotionBlurEngine::MotionBlurEngine(MotionBlurMain *plugin,
00714 int total_clients,
00715 int total_packages)
00716 : LoadServer(total_clients, total_packages)
00717 {
00718 this->plugin = plugin;
00719 }
00720
00721 void MotionBlurEngine::init_packages()
00722 {
00723 for(int i = 0; i < get_total_packages(); i++)
00724 {
00725 MotionBlurPackage *package = (MotionBlurPackage*)get_package(i);
00726 package->y1 = plugin->output->get_h() * i / get_total_packages();
00727 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
00728 }
00729 }
00730
00731 LoadClient* MotionBlurEngine::new_client()
00732 {
00733 return new MotionBlurUnit(this, plugin);
00734 }
00735
00736 LoadPackage* MotionBlurEngine::new_package()
00737 {
00738 return new MotionBlurPackage;
00739 }
00740
00741
00742
00743
00744