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 _("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;
521 if(!temp) temp = new VFrame(0,
525 frame->get_color_model(),
527 temp->copy_from(frame);
534 BC_CModels::components(frame->get_color_model()) *
535 MAX(sizeof(int), sizeof(float)));
536 engine->process_packages();
541 void LinearBlurMain::update_gui()
545 load_configuration();
546 ((LinearBlurWindow*)thread->window)->lock_window();
547 ((LinearBlurWindow*)thread->window)->radius->update(config.radius);
548 ((LinearBlurWindow*)thread->window)->angle->update(config.angle);
549 ((LinearBlurWindow*)thread->window)->steps->update(config.steps);
550 ((LinearBlurWindow*)thread->window)->r->update(config.r);
551 ((LinearBlurWindow*)thread->window)->g->update(config.g);
552 ((LinearBlurWindow*)thread->window)->b->update(config.b);
553 ((LinearBlurWindow*)thread->window)->a->update(config.a);
554 ((LinearBlurWindow*)thread->window)->unlock_window();
560 void LinearBlurMain::save_data(KeyFrame *keyframe)
564 // cause data to be stored directly in text
565 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
566 output.tag.set_title("LINEARBLUR");
568 output.tag.set_property("RADIUS", config.radius);
569 output.tag.set_property("ANGLE", config.angle);
570 output.tag.set_property("STEPS", config.steps);
571 output.tag.set_property("R", config.r);
572 output.tag.set_property("G", config.g);
573 output.tag.set_property("B", config.b);
574 output.tag.set_property("A", config.a);
576 output.tag.set_title("/LINEARBLUR");
578 output.append_newline();
579 output.terminate_string();
582 void LinearBlurMain::read_data(KeyFrame *keyframe)
586 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
592 result = input.read_tag();
596 if(input.tag.title_is("LINEARBLUR"))
598 config.radius = input.tag.get_property("RADIUS", config.radius);
599 config.angle = input.tag.get_property("ANGLE", config.angle);
600 config.steps = input.tag.get_property("STEPS", config.steps);
601 config.r = input.tag.get_property("R", config.r);
602 config.g = input.tag.get_property("G", config.g);
603 config.b = input.tag.get_property("B", config.b);
604 config.a = input.tag.get_property("A", config.a);
611 static void draw_box(float x1, float y1, float x2, float y2)
614 glVertex3f(x1, y1, 0.0);
615 glVertex3f(x2, y1, 0.0);
616 glVertex3f(x2, y2, 0.0);
617 glVertex3f(x1, y2, 0.0);
622 int LinearBlurMain::handle_opengl()
625 get_output()->to_texture();
626 get_output()->enable_opengl();
627 get_output()->init_screen();
628 get_output()->bind_texture(0);
630 int is_yuv = BC_CModels::is_yuv(get_output()->get_color_model());
631 glClearColor(0.0, 0.0, 0.0, 0.0);
632 glClear(GL_COLOR_BUFFER_BIT);
634 // Draw unselected channels
636 glBlendFunc(GL_ONE, GL_ONE);
637 glDrawBuffer(GL_BACK);
638 if(!config.r || !config.g || !config.b || !config.a)
640 glColor4f(config.r ? 0 : 1,
644 get_output()->draw_texture();
646 glAccum(GL_LOAD, 1.0);
648 // Blur selected channels
649 float fraction = 1.0 / config.steps;
650 for(int i = 0; i < config.steps; i++)
652 glClear(GL_COLOR_BUFFER_BIT);
653 glColor4f(config.r ? 1 : 0,
658 int w = get_output()->get_w();
659 int h = get_output()->get_h();
660 get_output()->draw_texture(0,
665 get_output()->get_h() - layer_table[i].y,
666 layer_table[i].x + w,
667 get_output()->get_h() - layer_table[i].y - h,
672 glDisable(GL_TEXTURE_2D);
675 glColor4f(config.r ? 0.0 : 0,
679 float center_x1 = 0.0;
680 float center_x2 = get_output()->get_w();
681 float project_x1 = layer_table[i].x;
682 float project_x2 = layer_table[i].x + get_output()->get_w();
683 float project_y1 = layer_table[i].y;
684 float project_y2 = layer_table[i].y + get_output()->get_h();
687 center_x1 = project_x1;
688 draw_box(0, 0, project_x1, -get_output()->get_h());
690 if(project_x2 < get_output()->get_w())
692 center_x2 = project_x2;
693 draw_box(project_x2, 0, get_output()->get_w(), -get_output()->get_h());
698 -get_output()->get_h(),
700 -get_output()->get_h() + project_y1);
702 if(project_y2 < get_output()->get_h())
705 -get_output()->get_h() + project_y2,
714 glAccum(GL_ACCUM, fraction);
715 glEnable(GL_TEXTURE_2D);
716 glColor4f(config.r ? 1 : 0,
723 glDisable(GL_TEXTURE_2D);
724 glReadBuffer(GL_BACK);
725 glAccum(GL_RETURN, 1.0);
727 glColor4f(1, 1, 1, 1);
728 get_output()->set_opengl_state(VFrame::SCREEN);
738 LinearBlurPackage::LinearBlurPackage()
746 LinearBlurUnit::LinearBlurUnit(LinearBlurEngine *server,
747 LinearBlurMain *plugin)
750 this->plugin = plugin;
751 this->server = server;
755 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP, MAX, DO_YUV) \
757 for(int j = pkg->y1; j < pkg->y2; j++) \
759 TEMP *out_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
760 int in_y = y_table[j]; \
763 TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
764 for(int k = 0; k < w; k++) \
766 int in_x = x_table[k]; \
768 int in_offset = in_x * COMPONENTS; \
769 *out_row++ += in_row[in_offset]; \
772 *out_row++ += in_row[in_offset + 1]; \
773 *out_row++ += in_row[in_offset + 2]; \
777 *out_row++ += in_row[in_offset + 1]; \
778 *out_row++ += in_row[in_offset + 2]; \
780 if(COMPONENTS == 4) \
781 *out_row++ += in_row[in_offset + 3]; \
785 /* Copy to output */ \
786 if(i == plugin->config.steps - 1) \
788 for(int j = pkg->y1; j < pkg->y2; j++) \
790 TEMP *in_row = (TEMP*)plugin->accum + COMPONENTS * w * j; \
791 TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
792 TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
793 for(int k = 0; k < w; k++) \
797 *out_row++ = (*in_row++ * fraction) / 0x10000; \
802 *out_row++ = *in_backup++; \
810 *out_row++ = (*in_row++ * fraction) / 0x10000; \
815 *out_row++ = *in_backup++; \
821 *out_row++ = (*in_row++ * fraction) / 0x10000; \
826 *out_row++ = *in_backup++; \
834 *out_row++ = (*in_row++ * fraction) / 0x10000; \
839 *out_row++ = *in_backup++; \
845 *out_row++ = (*in_row++ * fraction) / 0x10000; \
850 *out_row++ = *in_backup++; \
855 if(COMPONENTS == 4) \
859 *out_row++ = (*in_row++ * fraction) / 0x10000; \
864 *out_row++ = *in_backup++; \
873 void LinearBlurUnit::process_package(LoadPackage *package)
875 LinearBlurPackage *pkg = (LinearBlurPackage*)package;
876 //int h = plugin->output->get_h();
877 int w = plugin->output->get_w();
879 int fraction = 0x10000 / plugin->config.steps;
880 int do_r = plugin->config.r;
881 int do_g = plugin->config.g;
882 int do_b = plugin->config.b;
883 int do_a = plugin->config.a;
884 for(int i = 0; i < plugin->config.steps; i++)
886 int *x_table = plugin->scale_x_table[i];
887 int *y_table = plugin->scale_y_table[i];
889 switch(plugin->input->get_color_model())
892 BLEND_LAYER(3, float, float, 1, 0)
895 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
898 BLEND_LAYER(4, float, float, 1, 0)
901 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
904 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
906 case BC_RGBA16161616:
907 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
910 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
913 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
916 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
918 case BC_YUVA16161616:
919 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
930 LinearBlurEngine::LinearBlurEngine(LinearBlurMain *plugin,
933 : LoadServer(total_clients, total_packages)
935 this->plugin = plugin;
938 void LinearBlurEngine::init_packages()
940 for(int i = 0; i < get_total_packages(); i++)
942 LinearBlurPackage *package = (LinearBlurPackage*)get_package(i);
943 package->y1 = plugin->output->get_h() * i / get_total_packages();
944 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
948 LoadClient* LinearBlurEngine::new_client()
950 return new LinearBlurUnit(this, plugin);
953 LoadPackage* LinearBlurEngine::new_package()
955 return new LinearBlurPackage;