00001 #include "bcdisplayinfo.h"
00002 #include "clip.h"
00003 #include "bchash.h"
00004 #include "filexml.h"
00005 #include "guicast.h"
00006 #include "keyframe.h"
00007 #include "language.h"
00008 #include "loadbalance.h"
00009 #include "picon_png.h"
00010 #include "pluginvclient.h"
00011 #include "vframe.h"
00012
00013
00014
00015 #include <stdint.h>
00016 #include <string.h>
00017
00018 class WhirlEffect;
00019 class WhirlWindow;
00020 class WhirlEngine;
00021
00022 #define MAXRADIUS 100
00023 #define MAXPINCH 100
00024
00025
00026
00027
00028
00029
00030 class WhirlConfig
00031 {
00032 public:
00033 WhirlConfig();
00034
00035 void copy_from(WhirlConfig &src);
00036 int equivalent(WhirlConfig &src);
00037 void interpolate(WhirlConfig &prev,
00038 WhirlConfig &next,
00039 long prev_frame,
00040 long next_frame,
00041 long current_frame);
00042
00043 float angle;
00044 float pinch;
00045 float radius;
00046 };
00047
00048
00049 class WhirlAngle : public BC_FSlider
00050 {
00051 public:
00052 WhirlAngle(WhirlEffect *plugin, int x, int y);
00053 int handle_event();
00054 WhirlEffect *plugin;
00055 };
00056
00057
00058
00059 class WhirlPinch : public BC_FSlider
00060 {
00061 public:
00062 WhirlPinch(WhirlEffect *plugin, int x, int y);
00063 int handle_event();
00064 WhirlEffect *plugin;
00065 };
00066
00067
00068
00069 class WhirlRadius : public BC_FSlider
00070 {
00071 public:
00072 WhirlRadius(WhirlEffect *plugin, int x, int y);
00073 int handle_event();
00074 WhirlEffect *plugin;
00075 };
00076
00077 class WhirlWindow : public BC_Window
00078 {
00079 public:
00080 WhirlWindow(WhirlEffect *plugin, int x, int y);
00081 void create_objects();
00082 int close_event();
00083 WhirlEffect *plugin;
00084 WhirlRadius *radius;
00085 WhirlPinch *pinch;
00086 WhirlAngle *angle;
00087 };
00088
00089
00090 PLUGIN_THREAD_HEADER(WhirlEffect, WhirlThread, WhirlWindow)
00091
00092
00093 class WhirlPackage : public LoadPackage
00094 {
00095 public:
00096 WhirlPackage();
00097 int row1, row2;
00098 };
00099
00100 class WhirlUnit : public LoadClient
00101 {
00102 public:
00103 WhirlUnit(WhirlEffect *plugin, WhirlEngine *server);
00104 void process_package(LoadPackage *package);
00105 WhirlEngine *server;
00106 WhirlEffect *plugin;
00107
00108 };
00109
00110
00111 class WhirlEngine : public LoadServer
00112 {
00113 public:
00114 WhirlEngine(WhirlEffect *plugin, int cpus);
00115 void init_packages();
00116 LoadClient* new_client();
00117 LoadPackage* new_package();
00118 WhirlEffect *plugin;
00119 };
00120
00121
00122
00123 class WhirlEffect : public PluginVClient
00124 {
00125 public:
00126 WhirlEffect(PluginServer *server);
00127 ~WhirlEffect();
00128
00129 int process_realtime(VFrame *input, VFrame *output);
00130 int is_realtime();
00131 char* plugin_title();
00132 VFrame* new_picon();
00133 int show_gui();
00134 void raise_window();
00135 void update_gui();
00136 int set_string();
00137 int load_configuration();
00138 int load_defaults();
00139 int save_defaults();
00140 void save_data(KeyFrame *keyframe);
00141 void read_data(KeyFrame *keyframe);
00142
00143 WhirlEngine *engine;
00144 VFrame *temp_frame;
00145 VFrame *input, *output;
00146 WhirlConfig config;
00147 BC_Hash *defaults;
00148 WhirlThread *thread;
00149 int need_reconfigure;
00150 };
00151
00152
00153
00154
00155 PLUGIN_THREAD_OBJECT(WhirlEffect, WhirlThread, WhirlWindow)
00156
00157
00158 REGISTER_PLUGIN(WhirlEffect)
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175 WhirlConfig::WhirlConfig()
00176 {
00177 angle = 0.0;
00178 pinch = 0.0;
00179 radius = 0.0;
00180 }
00181
00182 void WhirlConfig::copy_from(WhirlConfig &src)
00183 {
00184 this->angle = src.angle;
00185 this->pinch = src.pinch;
00186 this->radius = src.radius;
00187 }
00188
00189 int WhirlConfig::equivalent(WhirlConfig &src)
00190 {
00191 return EQUIV(this->angle, src.angle) &&
00192 EQUIV(this->pinch, src.pinch) &&
00193 EQUIV(this->radius, src.radius);
00194 }
00195
00196 void WhirlConfig::interpolate(WhirlConfig &prev,
00197 WhirlConfig &next,
00198 long prev_frame,
00199 long next_frame,
00200 long current_frame)
00201 {
00202 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
00203 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
00204
00205 this->angle = prev.angle * prev_scale + next.angle * next_scale;
00206 this->pinch = prev.pinch * prev_scale + next.pinch * next_scale;
00207 this->radius = prev.radius * prev_scale + next.radius * next_scale;
00208 }
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219 WhirlWindow::WhirlWindow(WhirlEffect *plugin, int x, int y)
00220 : BC_Window(plugin->gui_string,
00221 x,
00222 y,
00223 220,
00224 200,
00225 220,
00226 200,
00227 0,
00228 0,
00229 1)
00230 {
00231 this->plugin = plugin;
00232 }
00233
00234
00235
00236 void WhirlWindow::create_objects()
00237 {
00238 int x = 10, y = 10;
00239 add_subwindow(new BC_Title(x, y, _("Radius")));
00240 y += 20;
00241 add_subwindow(radius = new WhirlRadius(plugin, x, y));
00242 y += 40;
00243 add_subwindow(new BC_Title(x, y, _("Pinch")));
00244 y += 20;
00245 add_subwindow(pinch = new WhirlPinch(plugin, x, y));
00246 y += 40;
00247 add_subwindow(new BC_Title(x, y, _("Angle")));
00248 y += 20;
00249 add_subwindow(angle = new WhirlAngle(plugin, x, y));
00250
00251 show_window();
00252 flush();
00253 }
00254
00255 int WhirlWindow::close_event()
00256 {
00257 set_done(1);
00258 return 1;
00259 }
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273 WhirlAngle::WhirlAngle(WhirlEffect *plugin, int x, int y)
00274 : BC_FSlider(x,
00275 y,
00276 0,
00277 200,
00278 200,
00279 (float)0,
00280 (float)360,
00281 plugin->config.angle)
00282 {
00283 this->plugin = plugin;
00284 }
00285 int WhirlAngle::handle_event()
00286 {
00287 plugin->config.angle = get_value();
00288 plugin->send_configure_change();
00289 return 1;
00290 }
00291
00292
00293
00294
00295 WhirlPinch::WhirlPinch(WhirlEffect *plugin, int x, int y)
00296 : BC_FSlider(x,
00297 y,
00298 0,
00299 200,
00300 200,
00301 (float)0,
00302 (float)MAXPINCH,
00303 plugin->config.pinch)
00304 {
00305 this->plugin = plugin;
00306 }
00307 int WhirlPinch::handle_event()
00308 {
00309 plugin->config.pinch = get_value();
00310 plugin->send_configure_change();
00311 return 1;
00312 }
00313
00314
00315
00316
00317 WhirlRadius::WhirlRadius(WhirlEffect *plugin, int x, int y)
00318 : BC_FSlider(x,
00319 y,
00320 0,
00321 200,
00322 200,
00323 (float)0,
00324 (float)MAXRADIUS,
00325 plugin->config.radius)
00326 {
00327 this->plugin = plugin;
00328 }
00329 int WhirlRadius::handle_event()
00330 {
00331 plugin->config.radius = get_value();
00332 plugin->send_configure_change();
00333 return 1;
00334 }
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346 WhirlEffect::WhirlEffect(PluginServer *server)
00347 : PluginVClient(server)
00348 {
00349 need_reconfigure = 1;
00350 engine = 0;
00351 temp_frame = 0;
00352 PLUGIN_CONSTRUCTOR_MACRO
00353 }
00354
00355 WhirlEffect::~WhirlEffect()
00356 {
00357 PLUGIN_DESTRUCTOR_MACRO
00358 if(engine) delete engine;
00359 if(temp_frame) delete temp_frame;
00360 }
00361
00362
00363
00364
00365
00366
00367 char* WhirlEffect::plugin_title() { return N_("Whirl"); }
00368 int WhirlEffect::is_realtime() { return 1; }
00369
00370 NEW_PICON_MACRO(WhirlEffect)
00371
00372 SHOW_GUI_MACRO(WhirlEffect, WhirlThread)
00373
00374 RAISE_WINDOW_MACRO(WhirlEffect)
00375
00376 SET_STRING_MACRO(WhirlEffect)
00377
00378 void WhirlEffect::update_gui()
00379 {
00380 if(thread)
00381 {
00382 load_configuration();
00383 thread->window->lock_window();
00384 thread->window->angle->update(config.angle);
00385 thread->window->pinch->update(config.pinch);
00386 thread->window->radius->update(config.radius);
00387 thread->window->unlock_window();
00388 }
00389 }
00390
00391 LOAD_CONFIGURATION_MACRO(WhirlEffect, WhirlConfig)
00392
00393
00394
00395
00396 int WhirlEffect::load_defaults()
00397 {
00398 char directory[1024], string[1024];
00399
00400 sprintf(directory, "%swhirl.rc", BCASTDIR);
00401
00402
00403 defaults = new BC_Hash(directory);
00404 defaults->load();
00405
00406 config.angle = defaults->get("ANGLE", config.angle);
00407 config.pinch = defaults->get("PINCH", config.pinch);
00408 config.radius = defaults->get("RADIUS", config.radius);
00409 }
00410
00411 int WhirlEffect::save_defaults()
00412 {
00413 defaults->update("ANGLE", config.angle);
00414 defaults->update("PINCH", config.pinch);
00415 defaults->update("RADIUS", config.radius);
00416 defaults->save();
00417 }
00418
00419 void WhirlEffect::save_data(KeyFrame *keyframe)
00420 {
00421 FileXML output;
00422
00423
00424 output.set_shared_string(keyframe->data, MESSAGESIZE);
00425
00426 output.tag.set_title("WHIRL");
00427 output.tag.set_property("ANGLE", config.angle);
00428 output.tag.set_property("PINCH", config.pinch);
00429 output.tag.set_property("RADIUS", config.radius);
00430 output.append_tag();
00431 output.tag.set_title("/WHIRL");
00432 output.append_tag();
00433 output.terminate_string();
00434
00435 }
00436
00437 void WhirlEffect::read_data(KeyFrame *keyframe)
00438 {
00439 FileXML input;
00440
00441 input.set_shared_string(keyframe->data, strlen(keyframe->data));
00442
00443 int result = 0;
00444
00445 while(!result)
00446 {
00447 result = input.read_tag();
00448
00449 if(!result)
00450 {
00451 if(input.tag.title_is("WHIRL"))
00452 {
00453 config.angle = input.tag.get_property("ANGLE", config.angle);
00454 config.pinch = input.tag.get_property("PINCH", config.pinch);
00455 config.radius = input.tag.get_property("RADIUS", config.radius);
00456 }
00457 }
00458 }
00459 }
00460
00461 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
00462 {
00463 need_reconfigure |= load_configuration();
00464 this->input = input;
00465 this->output = output;
00466
00467 if(EQUIV(config.angle, 0) ||
00468 (EQUIV(config.radius, 0) && EQUIV(config.pinch, 0)))
00469 {
00470 if(input->get_rows()[0] != output->get_rows()[0])
00471 output->copy_from(input);
00472 }
00473 else
00474 {
00475 if(input->get_rows()[0] == output->get_rows()[0])
00476 {
00477 if(!temp_frame) temp_frame = new VFrame(0,
00478 input->get_w(),
00479 input->get_h(),
00480 input->get_color_model());
00481 temp_frame->copy_from(input);
00482 this->input = temp_frame;
00483
00484 }
00485
00486 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
00487
00488 engine->process_packages();
00489 }
00490 }
00491
00492
00493
00494
00495
00496
00497
00498
00499 WhirlPackage::WhirlPackage()
00500 : LoadPackage()
00501 {
00502 }
00503
00504
00505
00506 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
00507 : LoadClient(server)
00508 {
00509 this->plugin = plugin;
00510 }
00511
00512
00513
00514 static int calc_undistorted_coords(double cen_x,
00515 double cen_y,
00516 double scale_x,
00517 double scale_y,
00518 double radius,
00519 double radius2,
00520 double radius3,
00521 double pinch,
00522 double wx,
00523 double wy,
00524 double &whirl,
00525 double &x,
00526 double &y)
00527 {
00528 double dx, dy;
00529 double d, factor;
00530 double dist;
00531 double ang, sina, cosa;
00532 int inside;
00533
00534
00535
00536 dx = (wx - cen_x) * scale_x;
00537 dy = (wy - cen_y) * scale_y;
00538
00539
00540
00541 d = dx * dx + dy * dy;
00542
00543
00544
00545
00546
00547 inside = (d < radius2);
00548
00549 if(inside)
00550 {
00551 dist = sqrt(d / radius3) / radius;
00552
00553
00554
00555 factor = pow(sin(M_PI / 2 * dist), -pinch);
00556
00557 dx *= factor;
00558 dy *= factor;
00559
00560
00561
00562 factor = 1.0 - dist;
00563
00564 ang = whirl * factor * factor;
00565
00566 sina = sin(ang);
00567 cosa = cos(ang);
00568
00569 x = (cosa * dx - sina * dy) / scale_x + cen_x;
00570 y = (sina * dx + cosa * dy) / scale_y + cen_y;
00571 }
00572
00573 return inside;
00574 }
00575
00576
00577
00578 #define GET_PIXEL(components, x, y, input_rows) \
00579 input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
00580
00581
00582
00583
00584
00585
00586 static float bilinear(double x, double y, double *values)
00587 {
00588 double m0, m1;
00589 x = fmod(x, 1.0);
00590 y = fmod(y, 1.0);
00591
00592 if(x < 0.0) x += 1.0;
00593 if(y < 0.0) y += 1.0;
00594
00595 m0 = (double)values[0] + x * ((double)values[1] - values[0]);
00596 m1 = (double)values[2] + x * ((double)values[3] - values[2]);
00597 return m0 + y * (m1 - m0);
00598 }
00599
00600
00601
00602
00603
00604 #define WHIRL_MACRO(type, max, components) \
00605 { \
00606 type **input_rows = (type**)plugin->input->get_rows(); \
00607 double values[components]; \
00608 for(int row = pkg->row1; row <= (pkg->row2 + pkg->row1) / 2; row++) \
00609 { \
00610 type *top_row = (type*)plugin->output->get_rows()[row]; \
00611 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
00612 type *top_p = top_row; \
00613 type *bot_p = bot_row + components * w - components; \
00614 \
00615 for(int col = 0; col < w; col++) \
00616 { \
00617 if(calc_undistorted_coords(cen_x, \
00618 cen_y, \
00619 scale_x, \
00620 scale_y, \
00621 radius, \
00622 radius2, \
00623 radius3, \
00624 pinch, \
00625 col, \
00626 row, \
00627 whirl, \
00628 cx, \
00629 cy)) \
00630 { \
00631 \
00632 \
00633 if(cx >= 0.0) \
00634 ix = (int)cx; \
00635 else \
00636 ix = -((int)-cx + 1); \
00637 \
00638 if(cy >= 0.0) \
00639 iy = (int)cy; \
00640 else \
00641 iy = -((int)-cy + 1); \
00642 \
00643 type *pixel1 = GET_PIXEL(components, ix, iy, input_rows); \
00644 type *pixel2 = GET_PIXEL(components, ix + 1, iy, input_rows); \
00645 type *pixel3 = GET_PIXEL(components, ix, iy + 1, input_rows); \
00646 type *pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
00647 \
00648 values[0] = pixel1[0]; \
00649 values[1] = pixel2[0]; \
00650 values[2] = pixel3[0]; \
00651 values[3] = pixel4[0]; \
00652 top_p[0] = (type)bilinear(cx, cy, values); \
00653 \
00654 values[0] = pixel1[1]; \
00655 values[1] = pixel2[1]; \
00656 values[2] = pixel3[1]; \
00657 values[3] = pixel4[1]; \
00658 top_p[1] = (type)bilinear(cx, cy, values); \
00659 \
00660 values[0] = pixel1[2]; \
00661 values[1] = pixel2[2]; \
00662 values[2] = pixel3[2]; \
00663 values[3] = pixel4[2]; \
00664 top_p[2] = (type)bilinear(cx, cy, values); \
00665 \
00666 if(components == 4) \
00667 { \
00668 values[0] = pixel1[3]; \
00669 values[1] = pixel2[3]; \
00670 values[2] = pixel3[3]; \
00671 values[3] = pixel4[3]; \
00672 top_p[3] = (type)bilinear(cx, cy, values); \
00673 } \
00674 \
00675 top_p += components; \
00676 \
00677 \
00678 cx = cen_x + (cen_x - cx); \
00679 cy = cen_y + (cen_y - cy); \
00680 \
00681 if(cx >= 0.0) \
00682 ix = (int)cx; \
00683 else \
00684 ix = -((int)-cx + 1); \
00685 \
00686 if(cy >= 0.0) \
00687 iy = (int)cy; \
00688 else \
00689 iy = -((int)-cy + 1); \
00690 \
00691 pixel1 = GET_PIXEL(components, ix, iy, input_rows); \
00692 pixel2 = GET_PIXEL(components, ix + 1, iy, input_rows); \
00693 pixel3 = GET_PIXEL(components, ix, iy + 1, input_rows); \
00694 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
00695 \
00696 \
00697 \
00698 values[0] = pixel1[0]; \
00699 values[1] = pixel2[0]; \
00700 values[2] = pixel3[0]; \
00701 values[3] = pixel4[0]; \
00702 bot_p[0] = (type)bilinear(cx, cy, values); \
00703 \
00704 values[0] = pixel1[1]; \
00705 values[1] = pixel2[1]; \
00706 values[2] = pixel3[1]; \
00707 values[3] = pixel4[1]; \
00708 bot_p[1] = (type)bilinear(cx, cy, values); \
00709 \
00710 values[0] = pixel1[2]; \
00711 values[1] = pixel2[2]; \
00712 values[2] = pixel3[2]; \
00713 values[3] = pixel4[2]; \
00714 bot_p[2] = (type)bilinear(cx, cy, values); \
00715 \
00716 if(components == 4) \
00717 { \
00718 values[0] = pixel1[3]; \
00719 values[1] = pixel2[3]; \
00720 values[2] = pixel3[3]; \
00721 values[3] = pixel4[3]; \
00722 bot_p[3] = (type)bilinear(cx, cy, values); \
00723 } \
00724 \
00725 bot_p -= components; \
00726 \
00727 \
00728 } \
00729 else \
00730 { \
00731 \
00732 \
00733 top_p[0] = input_rows[row][components * col + 0]; \
00734 top_p[1] = input_rows[row][components * col + 1]; \
00735 top_p[2] = input_rows[row][components * col + 2]; \
00736 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
00737 \
00738 \
00739 top_p += components; \
00740 \
00741 \
00742 int bot_offset = w * components - col * components - components; \
00743 int bot_row = h - 1 - row; \
00744 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
00745 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
00746 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
00747 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
00748 bot_p -= components; \
00749 } \
00750 } \
00751 } \
00752 }
00753
00754 void WhirlUnit::process_package(LoadPackage *package)
00755 {
00756 WhirlPackage *pkg = (WhirlPackage*)package;
00757 int w = plugin->input->get_w();
00758 int h = plugin->input->get_h();
00759 double whirl = plugin->config.angle * M_PI / 180;
00760 double pinch = plugin->config.pinch / MAXPINCH;
00761 double cx, cy;
00762 int ix, iy;
00763 double cen_x = (double)(w - 1) / 2.0;
00764 double cen_y = (double)(h - 1) / 2.0;
00765 double radius = MAX(w, h);
00766 double radius3 = plugin->config.radius / MAXRADIUS;
00767 double radius2 = radius * radius * radius3;
00768 double scale_x;
00769 double scale_y;
00770
00771
00772
00773
00774 if(w < h)
00775 {
00776 scale_x = (double)h / w;
00777 scale_y = 1.0;
00778 }
00779 else
00780 if(w > h)
00781 {
00782 scale_x = 1.0;
00783 scale_y = (double)w / h;
00784 }
00785 else
00786 {
00787 scale_x = 1.0;
00788 scale_y = 1.0;
00789 }
00790
00791
00792
00793 switch(plugin->input->get_color_model())
00794 {
00795 case BC_RGB_FLOAT:
00796 WHIRL_MACRO(float, 1, 3);
00797 break;
00798 case BC_RGB888:
00799 case BC_YUV888:
00800 WHIRL_MACRO(unsigned char, 0xff, 3);
00801 break;
00802 case BC_RGBA_FLOAT:
00803 WHIRL_MACRO(float, 1, 4);
00804 break;
00805 case BC_RGBA8888:
00806 case BC_YUVA8888:
00807 WHIRL_MACRO(unsigned char, 0xff, 4);
00808 break;
00809 case BC_RGB161616:
00810 case BC_YUV161616:
00811 WHIRL_MACRO(uint16_t, 0xffff, 3);
00812 break;
00813 case BC_RGBA16161616:
00814 case BC_YUVA16161616:
00815 WHIRL_MACRO(uint16_t, 0xffff, 4);
00816 break;
00817
00818 }
00819 }
00820
00821
00822
00823
00824
00825
00826
00827 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
00828 : LoadServer(cpus, cpus)
00829 {
00830 this->plugin = plugin;
00831 }
00832 void WhirlEngine::init_packages()
00833 {
00834 for(int i = 0; i < LoadServer::get_total_packages(); i++)
00835 {
00836 WhirlPackage *pkg = (WhirlPackage*)get_package(i);
00837 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
00838 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
00839 }
00840
00841 }
00842
00843 LoadClient* WhirlEngine::new_client()
00844 {
00845 return new WhirlUnit(plugin, this);
00846 }
00847
00848 LoadPackage* WhirlEngine::new_package()
00849 {
00850 return new WhirlPackage;
00851 }
00852