4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcdisplayinfo.h"
29 #include "loadbalance.h"
30 #include "pluginvclient.h"
55 void copy_from(WhirlConfig &src);
56 int equivalent(WhirlConfig &src);
57 void interpolate(WhirlConfig &prev,
69 class WhirlAngle : public BC_FSlider
72 WhirlAngle(WhirlEffect *plugin, int x, int y);
79 class WhirlPinch : public BC_FSlider
82 WhirlPinch(WhirlEffect *plugin, int x, int y);
89 class WhirlRadius : public BC_FSlider
92 WhirlRadius(WhirlEffect *plugin, int x, int y);
97 class WhirlWindow : public PluginClientWindow
100 WhirlWindow(WhirlEffect *plugin);
101 void create_objects();
112 class WhirlPackage : public LoadPackage
119 class WhirlUnit : public LoadClient
122 WhirlUnit(WhirlEffect *plugin, WhirlEngine *server);
123 void process_package(LoadPackage *package);
130 class WhirlEngine : public LoadServer
133 WhirlEngine(WhirlEffect *plugin, int cpus);
134 void init_packages();
135 LoadClient* new_client();
136 LoadPackage* new_package();
142 class WhirlEffect : public PluginVClient
145 WhirlEffect(PluginServer *server);
148 PLUGIN_CLASS_MEMBERS(WhirlConfig)
149 int process_realtime(VFrame *input, VFrame *output);
152 void save_data(KeyFrame *keyframe);
153 void read_data(KeyFrame *keyframe);
157 VFrame *input, *output;
158 int need_reconfigure;
166 REGISTER_PLUGIN(WhirlEffect)
183 WhirlConfig::WhirlConfig()
190 void WhirlConfig::copy_from(WhirlConfig &src)
192 this->angle = src.angle;
193 this->pinch = src.pinch;
194 this->radius = src.radius;
197 int WhirlConfig::equivalent(WhirlConfig &src)
199 return EQUIV(this->angle, src.angle) &&
200 EQUIV(this->pinch, src.pinch) &&
201 EQUIV(this->radius, src.radius);
204 void WhirlConfig::interpolate(WhirlConfig &prev,
210 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
211 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
213 this->angle = prev.angle * prev_scale + next.angle * next_scale;
214 this->pinch = prev.pinch * prev_scale + next.pinch * next_scale;
215 this->radius = prev.radius * prev_scale + next.radius * next_scale;
227 WhirlWindow::WhirlWindow(WhirlEffect *plugin)
228 : PluginClientWindow(plugin,
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));
272 WhirlAngle::WhirlAngle(WhirlEffect *plugin, int x, int y)
280 plugin->config.angle)
282 this->plugin = plugin;
284 int WhirlAngle::handle_event()
286 plugin->config.angle = get_value();
287 plugin->send_configure_change();
294 WhirlPinch::WhirlPinch(WhirlEffect *plugin, int x, int y)
302 plugin->config.pinch)
304 this->plugin = plugin;
306 int WhirlPinch::handle_event()
308 plugin->config.pinch = get_value();
309 plugin->send_configure_change();
316 WhirlRadius::WhirlRadius(WhirlEffect *plugin, int x, int y)
324 plugin->config.radius)
326 this->plugin = plugin;
328 int WhirlRadius::handle_event()
330 plugin->config.radius = get_value();
331 plugin->send_configure_change();
345 WhirlEffect::WhirlEffect(PluginServer *server)
346 : PluginVClient(server)
348 need_reconfigure = 1;
354 WhirlEffect::~WhirlEffect()
357 if(engine) delete engine;
358 if(temp_frame) delete temp_frame;
366 const char* WhirlEffect::plugin_title() { return N_("Whirl"); }
367 int WhirlEffect::is_realtime() { return 1; }
371 NEW_WINDOW_MACRO(WhirlEffect, WhirlWindow)
374 void WhirlEffect::update_gui()
378 load_configuration();
379 thread->window->lock_window();
380 ((WhirlWindow*)thread->window)->angle->update(config.angle);
381 ((WhirlWindow*)thread->window)->pinch->update(config.pinch);
382 ((WhirlWindow*)thread->window)->radius->update(config.radius);
383 thread->window->unlock_window();
387 LOAD_CONFIGURATION_MACRO(WhirlEffect, WhirlConfig)
392 void WhirlEffect::save_data(KeyFrame *keyframe)
396 // cause data to be stored directly in text
397 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
399 output.tag.set_title("WHIRL");
400 output.tag.set_property("ANGLE", config.angle);
401 output.tag.set_property("PINCH", config.pinch);
402 output.tag.set_property("RADIUS", config.radius);
404 output.tag.set_title("/WHIRL");
406 output.append_newline();
407 output.terminate_string();
408 // data is now in *text
411 void WhirlEffect::read_data(KeyFrame *keyframe)
415 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
421 result = input.read_tag();
425 if(input.tag.title_is("WHIRL"))
427 config.angle = input.tag.get_property("ANGLE", config.angle);
428 config.pinch = input.tag.get_property("PINCH", config.pinch);
429 config.radius = input.tag.get_property("RADIUS", config.radius);
435 int WhirlEffect::process_realtime(VFrame *input, VFrame *output)
437 need_reconfigure |= load_configuration();
439 this->output = output;
441 if(EQUIV(config.angle, 0) ||
442 (EQUIV(config.radius, 0) && EQUIV(config.pinch, 0)))
444 if(input->get_rows()[0] != output->get_rows()[0])
445 output->copy_from(input);
449 if(input->get_rows()[0] == output->get_rows()[0])
451 if(!temp_frame) temp_frame = new VFrame(input->get_w(), input->get_h(),
452 input->get_color_model(), 0);
453 temp_frame->copy_from(input);
454 this->input = temp_frame;
457 if(!engine) engine = new WhirlEngine(this, PluginClient::smp + 1);
459 engine->process_packages();
471 WhirlPackage::WhirlPackage()
478 WhirlUnit::WhirlUnit(WhirlEffect *plugin, WhirlEngine *server)
481 this->plugin = plugin;
486 static int calc_undistorted_coords(double cen_x,
503 double ang, sina, cosa;
506 /* Distances to center, scaled */
508 dx = (wx - cen_x) * scale_x;
509 dy = (wy - cen_y) * scale_y;
511 /* Distance^2 to center of *circle* (scaled ellipse) */
513 d = dx * dx + dy * dy;
515 /* If we are inside circle, then distort.
516 * Else, just return the same position
519 inside = (d < radius2);
523 dist = sqrt(d / radius3) / radius;
527 factor = pow(sin(M_PI / 2 * dist), -pinch);
536 ang = whirl * factor * factor;
541 x = (cosa * dx - sina * dy) / scale_x + cen_x;
542 y = (sina * dx + cosa * dy) / scale_y + cen_y;
550 #define GET_PIXEL(components, x, y, input_rows) \
551 input_rows[CLIP(y, 0, (h - 1))] + components * CLIP(x, 0, (w - 1))
558 static float bilinear(double x, double y, double *values)
564 if(x < 0.0) x += 1.0;
565 if(y < 0.0) y += 1.0;
567 m0 = (double)values[0] + x * ((double)values[1] - values[0]);
568 m1 = (double)values[2] + x * ((double)values[3] - values[2]);
569 return m0 + y * (m1 - m0);
576 #define WHIRL_MACRO(type, max, components) \
578 type **input_rows = (type**)plugin->input->get_rows(); \
579 /* Compiler error requires separate arrays */ \
580 double top_values[4], bot_values[4]; \
581 for( int i=0; i<4; ++i ) top_values[i] = bot_values[i] = 0; \
582 for(int row = pkg->row1 / 2; row <= (pkg->row2 + pkg->row1) / 2; row++) \
584 type *top_row = (type*)plugin->output->get_rows()[row]; \
585 type *bot_row = (type*)plugin->output->get_rows()[h - row - 1]; \
586 type *top_p = top_row; \
587 type *bot_p = bot_row + components * w - components; \
593 for(int col = 0; col < w; col++) \
595 if(calc_undistorted_coords(cen_x, \
609 /* Inside distortion area */ \
614 ix = -((int)-cx + 1); \
619 iy = -((int)-cy + 1); \
621 pixel1 = GET_PIXEL(components, ix, iy, input_rows); \
622 pixel2 = GET_PIXEL(components, ix + 1, iy, input_rows); \
623 pixel3 = GET_PIXEL(components, ix, iy + 1, input_rows); \
624 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
626 top_values[0] = pixel1[0]; \
627 top_values[1] = pixel2[0]; \
628 top_values[2] = pixel3[0]; \
629 top_values[3] = pixel4[0]; \
630 top_p[0] = (type)bilinear(cx, cy, top_values); \
632 top_values[0] = pixel1[1]; \
633 top_values[1] = pixel2[1]; \
634 top_values[2] = pixel3[1]; \
635 top_values[3] = pixel4[1]; \
636 top_p[1] = (type)bilinear(cx, cy, top_values); \
638 top_values[0] = pixel1[2]; \
639 top_values[1] = pixel2[2]; \
640 top_values[2] = pixel3[2]; \
641 top_values[3] = pixel4[2]; \
642 top_p[2] = (type)bilinear(cx, cy, top_values); \
644 if(components == 4) \
646 top_values[0] = pixel1[3]; \
647 top_values[1] = pixel2[3]; \
648 top_values[2] = pixel3[3]; \
649 top_values[3] = pixel4[3]; \
650 top_p[3] = (type)bilinear(cx, cy, top_values); \
653 top_p += components; \
656 cx = cen_x + (cen_x - cx); \
657 cy = cen_y + (cen_y - cy); \
662 ix = -((int)-cx + 1); \
667 iy = -((int)-cy + 1); \
669 pixel1 = GET_PIXEL(components, ix, iy, input_rows); \
670 pixel2 = GET_PIXEL(components, ix + 1, iy, input_rows); \
671 pixel3 = GET_PIXEL(components, ix, iy + 1, input_rows); \
672 pixel4 = GET_PIXEL(components, ix + 1, iy + 1, input_rows); \
676 bot_values[0] = pixel1[0]; \
677 bot_values[1] = pixel2[0]; \
678 bot_values[2] = pixel3[0]; \
679 bot_values[3] = pixel4[0]; \
680 bot_p[0] = (type)bilinear(cx, cy, bot_values); \
682 bot_values[0] = pixel1[1]; \
683 bot_values[1] = pixel2[1]; \
684 bot_values[2] = pixel3[1]; \
685 bot_values[3] = pixel4[1]; \
686 bot_p[1] = (type)bilinear(cx, cy, bot_values); \
688 bot_values[0] = pixel1[2]; \
689 bot_values[1] = pixel2[2]; \
690 bot_values[2] = pixel3[2]; \
691 bot_values[3] = pixel4[2]; \
692 bot_p[2] = (type)bilinear(cx, cy, bot_values); \
694 if(components == 4) \
696 bot_values[0] = pixel1[3]; \
697 bot_values[1] = pixel2[3]; \
698 bot_values[2] = pixel3[3]; \
699 bot_values[3] = pixel4[3]; \
700 bot_p[3] = (type)bilinear(cx, cy, bot_values); \
703 bot_p -= components; \
709 /* Outside distortion area */ \
711 top_p[0] = input_rows[row][components * col + 0]; \
712 top_p[1] = input_rows[row][components * col + 1]; \
713 top_p[2] = input_rows[row][components * col + 2]; \
714 if(components == 4) top_p[3] = input_rows[row][components * col + 3]; \
717 top_p += components; \
720 int bot_offset = w * components - col * components - components; \
721 int bot_row = h - 1 - row; \
722 bot_p[0] = input_rows[bot_row][bot_offset + 0]; \
723 bot_p[1] = input_rows[bot_row][bot_offset + 1]; \
724 bot_p[2] = input_rows[bot_row][bot_offset + 2]; \
725 if(components == 4) bot_p[3] = input_rows[bot_row][bot_offset + 3]; \
726 bot_p -= components; \
732 void WhirlUnit::process_package(LoadPackage *package)
734 WhirlPackage *pkg = (WhirlPackage*)package;
735 int w = plugin->input->get_w();
736 int h = plugin->input->get_h();
737 double whirl = plugin->config.angle * M_PI / 180;
738 double pinch = plugin->config.pinch / MAXPINCH;
741 double cen_x = (double)(w - 1) / 2.0;
742 double cen_y = (double)(h - 1) / 2.0;
743 double radius = MAX(w, h);
744 double radius3 = plugin->config.radius / MAXRADIUS;
745 double radius2 = radius * radius * radius3;
750 //printf("WhirlUnit::process_package 1 %f %f %f\n",
751 // plugin->config.angle, plugin->config.pinch, plugin->config.radius);
754 scale_x = (double)h / w;
761 scale_y = (double)w / h;
771 switch(plugin->input->get_color_model())
774 WHIRL_MACRO(float, 1, 3);
778 WHIRL_MACRO(unsigned char, 0xff, 3);
781 WHIRL_MACRO(float, 1, 4);
785 WHIRL_MACRO(unsigned char, 0xff, 4);
789 WHIRL_MACRO(uint16_t, 0xffff, 3);
791 case BC_RGBA16161616:
792 case BC_YUVA16161616:
793 WHIRL_MACRO(uint16_t, 0xffff, 4);
805 WhirlEngine::WhirlEngine(WhirlEffect *plugin, int cpus)
806 // : LoadServer(1, 1)
807 : LoadServer(cpus, cpus)
809 this->plugin = plugin;
811 void WhirlEngine::init_packages()
813 for(int i = 0; i < LoadServer::get_total_packages(); i++)
815 WhirlPackage *pkg = (WhirlPackage*)get_package(i);
816 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
817 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
822 LoadClient* WhirlEngine::new_client()
824 return new WhirlUnit(plugin, this);
827 LoadPackage* WhirlEngine::new_package()
829 return new WhirlPackage;