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
26 #include "bcdisplayinfo.h"
32 #include "loadbalance.h"
33 #include "pluginvclient.h"
39 class LinearBlurEngine;
45 class LinearBlurConfig
50 int equivalent(LinearBlurConfig &that);
51 void copy_from(LinearBlurConfig &that);
52 void interpolate(LinearBlurConfig &prev,
53 LinearBlurConfig &next,
69 class LinearBlurSize : public BC_ISlider
72 LinearBlurSize(LinearBlurMain *plugin,
79 LinearBlurMain *plugin;
83 class LinearBlurToggle : public BC_CheckBox
86 LinearBlurToggle(LinearBlurMain *plugin,
92 LinearBlurMain *plugin;
96 class LinearBlurWindow : public PluginClientWindow
99 LinearBlurWindow(LinearBlurMain *plugin);
102 void create_objects();
104 LinearBlurSize *angle, *steps, *radius;
105 LinearBlurToggle *r, *g, *b, *a;
106 LinearBlurMain *plugin;
113 // Output coords for a layer of blurring
114 // Used for OpenGL only
115 class LinearBlurLayer
118 LinearBlurLayer() {};
122 class LinearBlurMain : public PluginVClient
125 LinearBlurMain(PluginServer *server);
128 int process_buffer(VFrame *frame,
129 int64_t start_position,
132 void save_data(KeyFrame *keyframe);
133 void read_data(KeyFrame *keyframe);
137 PLUGIN_CLASS_MEMBERS(LinearBlurConfig)
139 void delete_tables();
140 VFrame *input, *output, *temp;
141 LinearBlurEngine *engine;
144 LinearBlurLayer *layer_table;
146 int need_reconfigure;
147 // The accumulation buffer is needed because 8 bits isn't precise enough
148 unsigned char *accum;
151 class LinearBlurPackage : public LoadPackage
158 class LinearBlurUnit : public LoadClient
161 LinearBlurUnit(LinearBlurEngine *server, LinearBlurMain *plugin);
162 void process_package(LoadPackage *package);
163 LinearBlurEngine *server;
164 LinearBlurMain *plugin;
167 class LinearBlurEngine : public LoadServer
170 LinearBlurEngine(LinearBlurMain *plugin,
173 void init_packages();
174 LoadClient* new_client();
175 LoadPackage* new_package();
176 LinearBlurMain *plugin;
197 REGISTER_PLUGIN(LinearBlurMain)
201 LinearBlurConfig::LinearBlurConfig()
212 int LinearBlurConfig::equivalent(LinearBlurConfig &that)
215 radius == that.radius &&
216 angle == that.angle &&
217 steps == that.steps &&
224 void LinearBlurConfig::copy_from(LinearBlurConfig &that)
226 radius = that.radius;
235 void LinearBlurConfig::interpolate(LinearBlurConfig &prev,
236 LinearBlurConfig &next,
241 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
242 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
243 this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
244 this->angle = (int)(prev.angle * prev_scale + next.angle * next_scale + 0.5);
245 this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
263 LinearBlurWindow::LinearBlurWindow(LinearBlurMain *plugin)
264 : PluginClientWindow(plugin,
271 this->plugin = plugin;
274 LinearBlurWindow::~LinearBlurWindow()
278 void LinearBlurWindow::create_objects()
282 add_subwindow(new BC_Title(x, y, _("Length:")));
284 add_subwindow(radius = new LinearBlurSize(plugin, x, y, &plugin->config.radius, 0, 100));
286 add_subwindow(new BC_Title(x, y, _("Angle:")));
288 add_subwindow(angle = new LinearBlurSize(plugin, x, y, &plugin->config.angle, -180, 180));
290 add_subwindow(new BC_Title(x, y, _("Steps:")));
292 add_subwindow(steps = new LinearBlurSize(plugin, x, y, &plugin->config.steps, 1, 200));
294 add_subwindow(r = new LinearBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
296 add_subwindow(g = new LinearBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
298 add_subwindow(b = new LinearBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
300 add_subwindow(a = new LinearBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
317 LinearBlurToggle::LinearBlurToggle(LinearBlurMain *plugin,
322 : BC_CheckBox(x, y, *output, string)
324 this->plugin = plugin;
325 this->output = output;
328 int LinearBlurToggle::handle_event()
330 *output = get_value();
331 plugin->send_configure_change();
341 LinearBlurSize::LinearBlurSize(LinearBlurMain *plugin,
347 : BC_ISlider(x, y, 0, 200, 200, min, max, *output)
349 this->plugin = plugin;
350 this->output = output;
352 int LinearBlurSize::handle_event()
354 *output = get_value();
355 plugin->send_configure_change();
368 LinearBlurMain::LinearBlurMain(PluginServer *server)
369 : PluginVClient(server)
377 need_reconfigure = 1;
382 LinearBlurMain::~LinearBlurMain()
385 if(engine) delete engine;
387 if(accum) delete [] accum;
388 if(temp) delete temp;
391 const char* LinearBlurMain::plugin_title() { return N_("Linear Blur"); }
392 int LinearBlurMain::is_realtime() { return 1; }
395 NEW_WINDOW_MACRO(LinearBlurMain, LinearBlurWindow)
396 LOAD_CONFIGURATION_MACRO(LinearBlurMain, LinearBlurConfig)
398 void LinearBlurMain::delete_tables()
402 for(int i = 0; i < table_entries; i++)
403 delete [] scale_x_table[i];
404 delete [] scale_x_table;
409 for(int i = 0; i < table_entries; i++)
410 delete [] scale_y_table[i];
411 delete [] scale_y_table;
413 delete [] layer_table;
420 int LinearBlurMain::process_buffer(VFrame *frame,
421 int64_t start_position,
424 need_reconfigure |= load_configuration();
428 get_source_position(),
432 // Generate tables here. The same table is used by many packages to render
433 // each horizontal stripe. Need to cover the entire output range in each
434 // table to avoid green borders
437 int w = frame->get_w();
438 int h = frame->get_h();
441 int angle = config.angle;
442 int radius = config.radius * MIN(w, h) / 100;
444 while(angle < 0) angle += 360;
465 y_offset = (int)(sin((float)config.angle / 360 * 2 * M_PI) * radius);
466 x_offset = (int)(cos((float)config.angle / 360 * 2 * M_PI) * radius);
472 scale_x_table = new int*[config.steps];
473 scale_y_table = new int*[config.steps];
474 table_entries = config.steps;
475 layer_table = new LinearBlurLayer[table_entries];
477 //printf("LinearBlurMain::process_realtime 1 %d %d %d\n", radius, x_offset, y_offset);
479 for(int i = 0; i < config.steps; i++)
481 float fraction = (float)(i - config.steps / 2) / config.steps;
482 int x = (int)(fraction * x_offset);
483 int y = (int)(fraction * y_offset);
487 scale_y_table[i] = y_table = new int[h];
488 scale_x_table[i] = x_table = new int[w];
490 layer_table[i].x = x;
491 layer_table[i].y = y;
492 for(int j = 0; j < h; j++)
495 CLAMP(y_table[j], 0, h - 1);
497 for(int j = 0; j < w; j++)
500 CLAMP(x_table[j], 0, w - 1);
503 need_reconfigure = 0;
506 if(get_use_opengl()) return run_opengl();
509 if(!engine) engine = new LinearBlurEngine(this,
510 get_project_smp() + 1,
511 get_project_smp() + 1);
512 if(!accum) accum = new unsigned char[frame->get_w() *
514 BC_CModels::components(frame->get_color_model()) *
515 MAX(sizeof(int), sizeof(float))];
518 this->output = frame;
522 temp = new VFrame(frame->get_w(), frame->get_h(),
523 frame->get_color_model(), 0);
524 temp->copy_from(frame);
531 BC_CModels::components(frame->get_color_model()) *
532 MAX(sizeof(int), sizeof(float)));
533 engine->process_packages();
538 void LinearBlurMain::update_gui()
542 load_configuration();
543 ((LinearBlurWindow*)thread->window)->lock_window();
544 ((LinearBlurWindow*)thread->window)->radius->update(config.radius);
545 ((LinearBlurWindow*)thread->window)->angle->update(config.angle);
546 ((LinearBlurWindow*)thread->window)->steps->update(config.steps);
547 ((LinearBlurWindow*)thread->window)->r->update(config.r);
548 ((LinearBlurWindow*)thread->window)->g->update(config.g);
549 ((LinearBlurWindow*)thread->window)->b->update(config.b);
550 ((LinearBlurWindow*)thread->window)->a->update(config.a);
551 ((LinearBlurWindow*)thread->window)->unlock_window();
557 void LinearBlurMain::save_data(KeyFrame *keyframe)
561 // cause data to be stored directly in text
562 output.set_shared_output(keyframe->xbuf);
563 output.tag.set_title("LINEARBLUR");
565 output.tag.set_property("RADIUS", config.radius);
566 output.tag.set_property("ANGLE", config.angle);
567 output.tag.set_property("STEPS", config.steps);
568 output.tag.set_property("R", config.r);
569 output.tag.set_property("G", config.g);
570 output.tag.set_property("B", config.b);
571 output.tag.set_property("A", config.a);
573 output.tag.set_title("/LINEARBLUR");
575 output.append_newline();
576 output.terminate_string();
579 void LinearBlurMain::read_data(KeyFrame *keyframe)
583 input.set_shared_input(keyframe->xbuf);
589 result = input.read_tag();
593 if(input.tag.title_is("LINEARBLUR"))
595 config.radius = input.tag.get_property("RADIUS", config.radius);
596 config.angle = input.tag.get_property("ANGLE", config.angle);
597 config.steps = input.tag.get_property("STEPS", config.steps);
598 config.r = input.tag.get_property("R", config.r);
599 config.g = input.tag.get_property("G", config.g);
600 config.b = input.tag.get_property("B", config.b);
601 config.a = input.tag.get_property("A", config.a);
608 static void draw_box(float x1, float y1, float x2, float y2)
611 glVertex3f(x1, y1, 0.0);
612 glVertex3f(x2, y1, 0.0);
613 glVertex3f(x2, y2, 0.0);
614 glVertex3f(x1, y2, 0.0);
619 int LinearBlurMain::handle_opengl()
622 get_output()->to_texture();
623 get_output()->enable_opengl();
624 get_output()->init_screen();
625 get_output()->bind_texture(0);
627 int is_yuv = BC_CModels::is_yuv(get_output()->get_color_model());
628 glClearColor(0.0, 0.0, 0.0, 0.0);
629 glClear(GL_COLOR_BUFFER_BIT);
631 // Draw unselected channels
633 glBlendFunc(GL_ONE, GL_ONE);
634 glDrawBuffer(GL_BACK);
635 if(!config.r || !config.g || !config.b || !config.a)
637 glColor4f(config.r ? 0 : 1,
641 get_output()->draw_texture();
643 glAccum(GL_LOAD, 1.0);
645 // Blur selected channels
646 float fraction = 1.0 / config.steps;
647 for(int i = 0; i < config.steps; i++)
649 glClear(GL_COLOR_BUFFER_BIT);
650 glColor4f(config.r ? 1 : 0,
655 int w = get_output()->get_w();
656 int h = get_output()->get_h();
657 get_output()->draw_texture(0,
662 get_output()->get_h() - layer_table[i].y,
663 layer_table[i].x + w,
664 get_output()->get_h() - layer_table[i].y - h,
669 glDisable(GL_TEXTURE_2D);
672 glColor4f(config.r ? 0.0 : 0,
676 float center_x1 = 0.0;
677 float center_x2 = get_output()->get_w();
678 float project_x1 = layer_table[i].x;
679 float project_x2 = layer_table[i].x + get_output()->get_w();
680 float project_y1 = layer_table[i].y;
681 float project_y2 = layer_table[i].y + get_output()->get_h();
684 center_x1 = project_x1;
685 draw_box(0, 0, project_x1, -get_output()->get_h());
687 if(project_x2 < get_output()->get_w())
689 center_x2 = project_x2;
690 draw_box(project_x2, 0, get_output()->get_w(), -get_output()->get_h());
695 -get_output()->get_h(),
697 -get_output()->get_h() + project_y1);
699 if(project_y2 < get_output()->get_h())
702 -get_output()->get_h() + project_y2,
711 glAccum(GL_ACCUM, fraction);
712 glEnable(GL_TEXTURE_2D);
713 glColor4f(config.r ? 1 : 0,
720 glDisable(GL_TEXTURE_2D);
721 glReadBuffer(GL_BACK);
722 glAccum(GL_RETURN, 1.0);
724 glColor4f(1, 1, 1, 1);
725 get_output()->set_opengl_state(VFrame::SCREEN);
735 LinearBlurPackage::LinearBlurPackage()
743 LinearBlurUnit::LinearBlurUnit(LinearBlurEngine *server,
744 LinearBlurMain *plugin)
747 this->plugin = plugin;
748 this->server = server;
752 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP, MAX, DO_YUV) \
754 for(int j = pkg->y1; j < pkg->y2; j++) \
756 TEMP *out_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
757 int in_y = y_table[j]; \
760 TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
761 for(int k = 0; k < w; k++) \
763 int in_x = x_table[k]; \
765 int in_offset = in_x * COMPONENTS; \
766 *out_row++ += in_row[in_offset]; \
769 *out_row++ += in_row[in_offset + 1]; \
770 *out_row++ += in_row[in_offset + 2]; \
774 *out_row++ += in_row[in_offset + 1]; \
775 *out_row++ += in_row[in_offset + 2]; \
777 if(COMPONENTS == 4) \
778 *out_row++ += in_row[in_offset + 3]; \
782 /* Copy to output */ \
783 if(i == plugin->config.steps - 1) \
785 for(int j = pkg->y1; j < pkg->y2; j++) \
787 TEMP *in_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
788 TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
789 TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
790 for(int k = 0; k < w; k++) \
794 *out_row++ = (*in_row++ * fraction) / 0x10000; \
799 *out_row++ = *in_backup++; \
807 *out_row++ = (*in_row++ * fraction) / 0x10000; \
812 *out_row++ = *in_backup++; \
818 *out_row++ = (*in_row++ * fraction) / 0x10000; \
823 *out_row++ = *in_backup++; \
831 *out_row++ = (*in_row++ * fraction) / 0x10000; \
836 *out_row++ = *in_backup++; \
842 *out_row++ = (*in_row++ * fraction) / 0x10000; \
847 *out_row++ = *in_backup++; \
852 if(COMPONENTS == 4) \
856 *out_row++ = (*in_row++ * fraction) / 0x10000; \
861 *out_row++ = *in_backup++; \
870 void LinearBlurUnit::process_package(LoadPackage *package)
872 LinearBlurPackage *pkg = (LinearBlurPackage*)package;
873 //int h = plugin->output->get_h();
874 int w = plugin->output->get_w();
876 int fraction = 0x10000 / plugin->config.steps;
877 int do_r = plugin->config.r;
878 int do_g = plugin->config.g;
879 int do_b = plugin->config.b;
880 int do_a = plugin->config.a;
881 for(int i = 0; i < plugin->config.steps; i++)
883 int *x_table = plugin->scale_x_table[i];
884 int *y_table = plugin->scale_y_table[i];
886 switch(plugin->input->get_color_model())
889 BLEND_LAYER(3, float, float, 1, 0)
892 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
895 BLEND_LAYER(4, float, float, 1, 0)
898 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
901 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
903 case BC_RGBA16161616:
904 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
907 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
910 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
913 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
915 case BC_YUVA16161616:
916 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
927 LinearBlurEngine::LinearBlurEngine(LinearBlurMain *plugin,
930 : LoadServer(total_clients, total_packages)
932 this->plugin = plugin;
935 void LinearBlurEngine::init_packages()
937 for(int i = 0; i < get_total_packages(); i++)
939 LinearBlurPackage *package = (LinearBlurPackage*)get_package(i);
940 package->y1 = plugin->output->get_h() * i / get_total_packages();
941 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
945 LoadClient* LinearBlurEngine::new_client()
947 return new LinearBlurUnit(this, plugin);
950 LoadPackage* LinearBlurEngine::new_package()
952 return new LinearBlurPackage;