1 #include "bcdisplayinfo.h"
7 #include "loadbalance.h"
9 #include "pluginvclient.h"
13 #define _(String) gettext(String)
14 #define gettext_noop(String) String
15 #define N_(String) gettext_noop (String)
39 void copy_from(WhirlConfig &src);
40 int equivalent(WhirlConfig &src);
41 void interpolate(WhirlConfig &prev,
53 class WhirlAngle : public BC_FSlider
56 WhirlAngle(WhirlEffect *plugin, int x, int y);
63 class WhirlPinch : public BC_FSlider
66 WhirlPinch(WhirlEffect *plugin, int x, int y);
73 class WhirlRadius : public BC_FSlider
76 WhirlRadius(WhirlEffect *plugin, int x, int y);
81 class WhirlWindow : public BC_Window
84 WhirlWindow(WhirlEffect *plugin, int x, int y);
85 void create_objects();
94 PLUGIN_THREAD_HEADER(WhirlEffect, WhirlThread, WhirlWindow)
97 class WhirlPackage : public LoadPackage
104 class WhirlUnit : public LoadClient
107 WhirlUnit(WhirlEffect *plugin, WhirlEngine *server);
108 void process_package(LoadPackage *package);
115 class WhirlEngine : public LoadServer
118 WhirlEngine(WhirlEffect *plugin, int cpus);
119 void init_packages();
120 LoadClient* new_client();
121 LoadPackage* new_package();
127 class WhirlEffect : public PluginVClient
130 WhirlEffect(PluginServer *server);
133 int process_realtime(VFrame *input, VFrame *output);
135 char* plugin_title();
141 int load_configuration();
144 void save_data(KeyFrame *keyframe);
145 void read_data(KeyFrame *keyframe);
149 VFrame *input, *output;
153 int need_reconfigure;
159 PLUGIN_THREAD_OBJECT(WhirlEffect, WhirlThread, WhirlWindow)
162 REGISTER_PLUGIN(WhirlEffect)
179 WhirlConfig::WhirlConfig()
186 void WhirlConfig::copy_from(WhirlConfig &src)
188 this->angle = src.angle;
189 this->pinch = src.pinch;
190 this->radius = src.radius;
193 int WhirlConfig::equivalent(WhirlConfig &src)
195 return EQUIV(this->angle, src.angle) &&
196 EQUIV(this->pinch, src.pinch) &&
197 EQUIV(this->radius, src.radius);
200 void WhirlConfig::interpolate(WhirlConfig &prev,
206 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
207 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
209 this->angle = prev.angle * prev_scale + next.angle * next_scale;
210 this->pinch = prev.pinch * prev_scale + next.pinch * next_scale;
211 this->radius = prev.radius * prev_scale + next.radius * next_scale;
223 WhirlWindow::WhirlWindow(WhirlEffect *plugin, int x, int y)
224 : BC_Window(plugin->gui_string,
235 this->plugin = plugin;
240 void WhirlWindow::create_objects()
243 add_subwindow(new BC_Title(x, y, _("Radius")));
245 add_subwindow(radius = new WhirlRadius(plugin, x, y));
247 add_subwindow(new BC_Title(x, y, _("Pinch")));
249 add_subwindow(pinch = new WhirlPinch(plugin, x, y));
251 add_subwindow(new BC_Title(x, y, _("Angle")));
253 add_subwindow(angle = new WhirlAngle(plugin, x, y));
259 int WhirlWindow::close_event()
277 WhirlAngle::WhirlAngle(WhirlEffect *plugin, int x, int y)
285 plugin->config.angle)
287 this->plugin = plugin;
289 int WhirlAngle::handle_event()
291 plugin->config.angle = get_value();
292 plugin->send_configure_change();
299 WhirlPinch::WhirlPinch(WhirlEffect *plugin, int x, int y)
307 plugin->config.pinch)
309 this->plugin = plugin;
311 int WhirlPinch::handle_event()
313 plugin->config.pinch = get_value();
314 plugin->send_configure_change();
321 WhirlRadius::WhirlRadius(WhirlEffect *plugin, int x, int y)
329 plugin->config.radius)
331 this->plugin = plugin;
333 int WhirlRadius::handle_event()
335 plugin->config.radius = get_value();
336 plugin->send_configure_change();
350 WhirlEffect::WhirlEffect(PluginServer *server)
351 : PluginVClient(server)
353 need_reconfigure = 1;
356 PLUGIN_CONSTRUCTOR_MACRO
359 WhirlEffect::~WhirlEffect()
361 PLUGIN_DESTRUCTOR_MACRO
362 if(engine) delete engine;
363 if(temp_frame) delete temp_frame;
370 int WhirlEffect::is_realtime()
375 char* WhirlEffect::plugin_title()
380 NEW_PICON_MACRO(WhirlEffect)
382 SHOW_GUI_MACRO(WhirlEffect, WhirlThread)
384 RAISE_WINDOW_MACRO(WhirlEffect)
386 SET_STRING_MACRO(WhirlEffect)
388 void WhirlEffect::update_gui()
392 load_configuration();
393 thread->window->lock_window();
394 thread->window->angle->update(config.angle);
395 thread->window->pinch->update(config.pinch);
396 thread->window->radius->update(config.radius);
397 thread->window->unlock_window();
401 LOAD_CONFIGURATION_MACRO(WhirlEffect, WhirlConfig)
406 int WhirlEffect::load_defaults()
408 char directory[1024], string[1024];
409 // set the default directory
410 sprintf(directory, "%swhirl.rc", BCASTDIR);
413 defaults = new Defaults(directory);
416 config.angle = defaults->get("ANGLE", config.angle);
417 config.pinch = defaults->get("PINCH", config.pinch);
418 config.radius = defaults->get("RADIUS", config.radius);
421 int WhirlEffect::save_defaults()
423 defaults->update("ANGLE", config.angle);
424 defaults->update("PINCH", config.pinch);
425 defaults->update("RADIUS", config.radius);
429 void WhirlEffect::save_data(KeyFrame *keyframe)
433 // cause data to be stored directly in text
434 output.set_shared_string(keyframe->data, MESSAGESIZE);
436 output.tag.set_title("WHIRL");
437 output.tag.set_property("ANGLE", config.angle);
438 output.tag.set_property("PINCH", config.pinch);
439 output.tag.set_property("RADIUS", config.radius);
441 output.terminate_string();
442 // data is now in *text
445 void WhirlEffect::read_data(KeyFrame *keyframe)
449 input.set_shared_string(keyframe->data, strlen(keyframe->data));
455 result = input.read_tag();
459 if(input.tag.title_is("WHIRL"))
461 config.angle = input.tag.get_property("ANGLE", config.angle);
462 config.pinch = input.tag.get_property("PINCH", config.pinch);
463 config.radius = input.tag.get_property("RADIUS", config.radius);
469 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
471 need_reconfigure |= load_configuration();
473 this->output = output;
475 if(EQUIV(config.angle, 0) || EQUIV(config.radius, 0))
477 if(input->get_rows()[0] != output->get_rows()[0])
478 output->copy_from(input);
482 if(input->get_rows()[0] == output->get_rows()[0])
484 if(!temp_frame) temp_frame = new VFrame(0,
487 input->get_color_model());
488 temp_frame->copy_from(input);
489 this->input = temp_frame;
490 //printf("WhirlEffect::process_realtime 1\n");
493 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
495 engine->process_packages();
506 WhirlPackage::WhirlPackage()
513 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
516 this->plugin = plugin;
521 static int calc_undistorted_coords(double cen_x,
538 double ang, sina, cosa;
541 /* Distances to center, scaled */
543 dx = (wx - cen_x) * scale_x;
544 dy = (wy - cen_y) * scale_y;
546 /* Distance^2 to center of *circle* (scaled ellipse) */
548 d = dx * dx + dy * dy;
550 /* If we are inside circle, then distort.
551 * Else, just return the same position
554 inside = (d < radius2);
558 dist = sqrt(d / radius3) / radius;
562 factor = pow(sin(M_PI / 2 * dist), -pinch);
571 ang = whirl * factor * factor;
576 x = (cosa * dx - sina * dy) / scale_x + cen_x;
577 y = (sina * dx + cosa * dy) / scale_y + cen_y;
585 #define GET_PIXEL(components, x, y, input_rows) \
586 input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
593 static float bilinear(double x, double y, double *values)
599 if(x < 0.0) x += 1.0;
600 if(y < 0.0) y += 1.0;
602 m0 = (double)values[0] + x * ((double)values[1] - values[0]);
603 m1 = (double)values[2] + x * ((double)values[3] - values[2]);
604 return m0 + y * (m1 - m0);
611 #define WHIRL_MACRO(type, max, components) \
613 type **input_rows = (type**)plugin->input->get_rows(); \
614 double values[components]; \
615 for(int row = pkg->row1; row <= (pkg->row2 + pkg->row1) / 2; row++) \
617 type *top_row = (type*)plugin->output->get_rows()[row]; \
618 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
619 type *top_p = top_row; \
620 type *bot_p = bot_row + components * w - components; \
622 for(int col = 0; col < w; col++) \
624 if(calc_undistorted_coords(cen_x, \
638 /* Inside distortion area */ \
643 ix = -((int)-cx + 1); \
648 iy = -((int)-cy + 1); \
650 type *pixel1 = GET_PIXEL(components, ix, iy, input_rows); \
651 type *pixel2 = GET_PIXEL(components, ix + 1, iy, input_rows); \
652 type *pixel3 = GET_PIXEL(components, ix, iy + 1, input_rows); \
653 type *pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
655 values[0] = pixel1[0]; \
656 values[1] = pixel2[0]; \
657 values[2] = pixel3[0]; \
658 values[3] = pixel4[0]; \
659 top_p[0] = (type)bilinear(cx, cy, values); \
661 values[0] = pixel1[1]; \
662 values[1] = pixel2[1]; \
663 values[2] = pixel3[1]; \
664 values[3] = pixel4[1]; \
665 top_p[1] = (type)bilinear(cx, cy, values); \
667 values[0] = pixel1[2]; \
668 values[1] = pixel2[2]; \
669 values[2] = pixel3[2]; \
670 values[3] = pixel4[2]; \
671 top_p[2] = (type)bilinear(cx, cy, values); \
673 if(components == 4) \
675 values[0] = pixel1[3]; \
676 values[1] = pixel2[3]; \
677 values[2] = pixel3[3]; \
678 values[3] = pixel4[3]; \
679 top_p[3] = (type)bilinear(cx, cy, values); \
682 top_p += components; \
685 cx = cen_x + (cen_x - cx); \
686 cy = cen_y + (cen_y - cy); \
691 ix = -((int)-cx + 1); \
696 iy = -((int)-cy + 1); \
698 pixel1 = GET_PIXEL(components, ix, iy, input_rows); \
699 pixel2 = GET_PIXEL(components, ix + 1, iy, input_rows); \
700 pixel3 = GET_PIXEL(components, ix, iy + 1, input_rows); \
701 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
705 values[0] = pixel1[0]; \
706 values[1] = pixel2[0]; \
707 values[2] = pixel3[0]; \
708 values[3] = pixel4[0]; \
709 bot_p[0] = (type)bilinear(cx, cy, values); \
711 values[0] = pixel1[1]; \
712 values[1] = pixel2[1]; \
713 values[2] = pixel3[1]; \
714 values[3] = pixel4[1]; \
715 bot_p[1] = (type)bilinear(cx, cy, values); \
717 values[0] = pixel1[2]; \
718 values[1] = pixel2[2]; \
719 values[2] = pixel3[2]; \
720 values[3] = pixel4[2]; \
721 bot_p[2] = (type)bilinear(cx, cy, values); \
723 if(components == 4) \
725 values[0] = pixel1[3]; \
726 values[1] = pixel2[3]; \
727 values[2] = pixel3[3]; \
728 values[3] = pixel4[3]; \
729 bot_p[3] = (type)bilinear(cx, cy, values); \
732 bot_p -= components; \
738 /* Outside distortion area */ \
740 top_p[0] = input_rows[row][components * col + 0]; \
741 top_p[1] = input_rows[row][components * col + 1]; \
742 top_p[2] = input_rows[row][components * col + 2]; \
743 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
746 top_p += components; \
749 int bot_offset = w * components - col * components - components; \
750 int bot_row = h - 1 - row; \
751 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
752 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
753 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
754 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
755 bot_p -= components; \
761 void WhirlUnit::process_package(LoadPackage *package)
763 WhirlPackage *pkg = (WhirlPackage*)package;
764 int w = plugin->input->get_w();
765 int h = plugin->input->get_h();
766 double whirl = plugin->config.angle * M_PI / 180;
767 double pinch = plugin->config.pinch / MAXPINCH;
770 double cen_x = (double)(w - 1) / 2.0;
771 double cen_y = (double)(h - 1) / 2.0;
772 double radius = MAX(w, h);
773 double radius3 = plugin->config.radius / MAXRADIUS;
774 double radius2 = radius * radius * radius3;
779 //printf("WhirlUnit::process_package 1 %f %f %f\n",
780 // plugin->config.angle, plugin->config.pinch, plugin->config.radius);
783 scale_x = (double)h / w;
790 scale_y = (double)w / h;
800 switch(plugin->input->get_color_model())
804 WHIRL_MACRO(unsigned char, 0xff, 3);
808 WHIRL_MACRO(unsigned char, 0xff, 4);
812 WHIRL_MACRO(uint16_t, 0xffff, 3);
814 case BC_RGBA16161616:
815 case BC_YUVA16161616:
816 WHIRL_MACRO(uint16_t, 0xffff, 4);
828 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
829 : LoadServer(cpus, cpus)
831 this->plugin = plugin;
833 void WhirlEngine::init_packages()
835 int increment = plugin->input->get_h() / LoadServer::total_packages;
837 for(int i = 0; i < LoadServer::total_packages; i++)
839 WhirlPackage *pkg = (WhirlPackage*)packages[i];
841 pkg->row2 = y + increment;
842 if(i == LoadServer::total_packages - 1) pkg->row2 = plugin->input->get_h();
848 LoadClient* WhirlEngine::new_client()
850 return new WhirlUnit(plugin, this);
853 LoadPackage* WhirlEngine::new_package()
855 return new WhirlPackage;