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
27 REGISTER_PLUGIN(ZoomBlurMain)
31 ZoomBlurConfig::ZoomBlurConfig()
33 reset(RESET_DEFAULT_SETTINGS);
36 void ZoomBlurConfig::reset(int clear)
49 case RESET_XSLIDER : x = 50;
51 case RESET_YSLIDER : y = 50;
53 case RESET_RADIUS : radius = 0;
55 case RESET_STEPS : steps = 1;
57 case RESET_DEFAULT_SETTINGS :
71 int ZoomBlurConfig::equivalent(ZoomBlurConfig &that)
76 radius == that.radius &&
77 steps == that.steps &&
84 void ZoomBlurConfig::copy_from(ZoomBlurConfig &that)
96 void ZoomBlurConfig::interpolate(ZoomBlurConfig &prev,
102 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
103 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
104 this->x = (int)(prev.x * prev_scale + next.x * next_scale + 0.5);
105 this->y = (int)(prev.y * prev_scale + next.y * next_scale + 0.5);
106 this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
107 this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
125 ZoomBlurWindow::ZoomBlurWindow(ZoomBlurMain *plugin)
126 : PluginClientWindow(plugin,
133 this->plugin = plugin;
136 ZoomBlurWindow::~ZoomBlurWindow()
140 void ZoomBlurWindow::create_objects()
143 int x1 = 0; int clrBtn_w = 50;
144 int defaultBtn_w = 100;
146 add_subwindow(new BC_Title(x, y, _("X:")));
148 add_subwindow(this->x = new ZoomBlurSize(plugin, x, y, &plugin->config.x, 0, 100));
149 x1 = x + this->x->get_w() + 10;
150 add_subwindow(xClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_XSLIDER));
153 add_subwindow(new BC_Title(x, y, _("Y:")));
155 add_subwindow(this->y = new ZoomBlurSize(plugin, x, y, &plugin->config.y, 0, 100));
156 add_subwindow(yClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_YSLIDER));
159 add_subwindow(new BC_Title(x, y, _("Radius:")));
161 add_subwindow(radius = new ZoomBlurSize(plugin, x, y, &plugin->config.radius, -100, 100));
162 add_subwindow(radiusClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_RADIUS));
165 add_subwindow(new BC_Title(x, y, _("Steps:")));
167 add_subwindow(steps = new ZoomBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
168 add_subwindow(stepsClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_STEPS));
171 add_subwindow(r = new ZoomBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
173 add_subwindow(g = new ZoomBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
175 add_subwindow(b = new ZoomBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
177 add_subwindow(a = new ZoomBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
179 add_subwindow(reset = new ZoomBlurReset(plugin, this, x, y));
180 add_subwindow(default_settings = new ZoomBlurDefaultSettings(plugin, this,
181 (280 - 10 - defaultBtn_w), y, defaultBtn_w));
188 void ZoomBlurWindow::update_gui(int clear)
191 case RESET_XSLIDER : this->x->update(plugin->config.x);
193 case RESET_YSLIDER : this->y->update(plugin->config.y);
195 case RESET_RADIUS : radius->update(plugin->config.radius);
197 case RESET_STEPS : steps->update(plugin->config.steps);
200 case RESET_DEFAULT_SETTINGS :
202 this->x->update(plugin->config.x);
203 this->y->update(plugin->config.x);
204 radius->update(plugin->config.radius);
205 steps->update(plugin->config.steps);
206 r->update(plugin->config.r);
207 g->update(plugin->config.g);
208 b->update(plugin->config.b);
209 a->update(plugin->config.a);
223 ZoomBlurToggle::ZoomBlurToggle(ZoomBlurMain *plugin,
228 : BC_CheckBox(x, y, *output, string)
230 this->plugin = plugin;
231 this->output = output;
234 int ZoomBlurToggle::handle_event()
236 *output = get_value();
237 plugin->send_configure_change();
247 ZoomBlurSize::ZoomBlurSize(ZoomBlurMain *plugin,
253 : BC_ISlider(x, y, 0, 200, 200, min, max, *output)
255 this->plugin = plugin;
256 this->output = output;
258 int ZoomBlurSize::handle_event()
260 *output = get_value();
261 plugin->send_configure_change();
266 ZoomBlurReset::ZoomBlurReset(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y)
267 : BC_GenericButton(x, y, _("Reset"))
269 this->plugin = plugin;
270 this->window = window;
272 ZoomBlurReset::~ZoomBlurReset()
275 int ZoomBlurReset::handle_event()
277 plugin->config.reset(RESET_ALL);
278 window->update_gui(RESET_ALL);
279 plugin->send_configure_change();
284 ZoomBlurDefaultSettings::ZoomBlurDefaultSettings(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y, int w)
285 : BC_GenericButton(x, y, w, _("Default"))
287 this->plugin = plugin;
288 this->window = window;
290 ZoomBlurDefaultSettings::~ZoomBlurDefaultSettings()
293 int ZoomBlurDefaultSettings::handle_event()
295 plugin->config.reset(RESET_DEFAULT_SETTINGS);
296 window->update_gui(RESET_DEFAULT_SETTINGS);
297 plugin->send_configure_change();
302 ZoomBlurSliderClr::ZoomBlurSliderClr(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y, int w, int clear)
303 : BC_Button(x, y, w, plugin->get_theme()->get_image_set("reset_button"))
305 this->plugin = plugin;
306 this->window = window;
309 ZoomBlurSliderClr::~ZoomBlurSliderClr()
312 int ZoomBlurSliderClr::handle_event()
314 // clear==1 ==> X slider
315 // clear==2 ==> Y slider
316 // clear==3 ==> Radius slider
317 // clear==4 ==> Steps slider
318 plugin->config.reset(clear);
319 window->update_gui(clear);
320 plugin->send_configure_change();
327 ZoomBlurMain::ZoomBlurMain(PluginServer *server)
328 : PluginVClient(server)
337 need_reconfigure = 1;
341 ZoomBlurMain::~ZoomBlurMain()
344 if(engine) delete engine;
346 if(accum) delete [] accum;
347 if(temp) delete temp;
350 const char* ZoomBlurMain::plugin_title() { return N_("Zoom Blur"); }
351 int ZoomBlurMain::is_realtime() { return 1; }
355 NEW_WINDOW_MACRO(ZoomBlurMain, ZoomBlurWindow)
357 LOAD_CONFIGURATION_MACRO(ZoomBlurMain, ZoomBlurConfig)
359 void ZoomBlurMain::delete_tables()
363 for(int i = 0; i < table_entries; i++)
364 delete [] scale_x_table[i];
365 delete [] scale_x_table;
370 for(int i = 0; i < table_entries; i++)
371 delete [] scale_y_table[i];
372 delete [] scale_y_table;
375 delete [] layer_table;
382 int ZoomBlurMain::process_buffer(VFrame *frame,
383 int64_t start_position,
386 need_reconfigure |= load_configuration();
392 get_source_position(),
398 // Generate tables here. The same table is used by many packages to render
399 // each horizontal stripe. Need to cover the entire output range in each
400 // table to avoid green borders
404 float w = frame->get_w();
405 float h = frame->get_h();
406 float center_x = (float)config.x / 100 * w;
407 float center_y = (float)config.y / 100 * h;
408 float radius = (float)(100 + config.radius) / 100;
410 //float max_w, max_h;
411 int steps = config.steps ? config.steps : 1;
423 // printf("ZoomBlurMain::process_realtime 1 %d %d\n",
427 center_x = (center_x - w / 2) * (1.0 - radius) + w / 2;
428 center_y = (center_y - h / 2) * (1.0 - radius) + h / 2;
433 min_x1 = center_x - min_w / 2;
434 min_y1 = center_y - min_h / 2;
435 min_x2 = center_x + min_w / 2;
436 min_y2 = center_y + min_h / 2;
443 // printf("ZoomBlurMain::process_realtime 2 w=%f radius=%f center_x=%f\n",
449 // Dimensions of outermost rectangle
452 table_entries = steps;
453 scale_x_table = new int*[steps];
454 scale_y_table = new int*[steps];
455 layer_table = new ZoomBlurLayer[table_entries];
458 for(int i = 0; i < steps; i++)
460 float fraction = (float)i / steps;
461 float inv_fraction = 1.0 - fraction;
462 float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
463 float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
464 float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
465 float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
466 float out_w = out_x2 - out_x1;
467 float out_h = out_y2 - out_y1;
468 if(out_w < 0) out_w = 0;
469 if(out_h < 0) out_h = 0;
470 float scale_x = (float)w / out_w;
471 float scale_y = (float)h / out_h;
475 scale_y_table[i] = y_table = new int[(int)(h + 1)];
476 scale_x_table[i] = x_table = new int[(int)(w + 1)];
478 layer_table[i].x1 = out_x1;
479 layer_table[i].y1 = out_y1;
480 layer_table[i].x2 = out_x2;
481 layer_table[i].y2 = out_y2;
484 for(int j = 0; j < h; j++)
486 y_table[j] = (int)((j - out_y1) * scale_y);
488 for(int j = 0; j < w; j++)
490 x_table[j] = (int)((j - out_x1) * scale_x);
491 //printf("ZoomBlurMain::process_realtime %d %d\n", j, x_table[j]);
495 need_reconfigure = 0;
499 if(get_use_opengl()) return run_opengl();
505 if(!engine) engine = new ZoomBlurEngine(this,
506 get_project_smp() + 1,
507 get_project_smp() + 1);
508 if(!accum) accum = new unsigned char[frame->get_w() *
510 BC_CModels::components(frame->get_color_model()) *
511 MAX(sizeof(int), sizeof(float))];
514 this->output = frame;
517 if(!temp) temp = new VFrame(frame->get_w(), frame->get_h(),
518 frame->get_color_model(), 0);
519 temp->copy_from(frame);
525 BC_CModels::components(frame->get_color_model()) *
526 MAX(sizeof(int), sizeof(float)));
527 engine->process_packages();
532 void ZoomBlurMain::update_gui()
536 load_configuration();
537 thread->window->lock_window();
538 ((ZoomBlurWindow*)thread->window)->x->update(config.x);
539 ((ZoomBlurWindow*)thread->window)->y->update(config.y);
540 ((ZoomBlurWindow*)thread->window)->radius->update(config.radius);
541 ((ZoomBlurWindow*)thread->window)->steps->update(config.steps);
542 ((ZoomBlurWindow*)thread->window)->r->update(config.r);
543 ((ZoomBlurWindow*)thread->window)->g->update(config.g);
544 ((ZoomBlurWindow*)thread->window)->b->update(config.b);
545 ((ZoomBlurWindow*)thread->window)->a->update(config.a);
546 thread->window->unlock_window();
554 void ZoomBlurMain::save_data(KeyFrame *keyframe)
558 // cause data to be stored directly in text
559 output.set_shared_output(keyframe->xbuf);
560 output.tag.set_title("ZOOMBLUR");
562 output.tag.set_property("X", config.x);
563 output.tag.set_property("Y", config.y);
564 output.tag.set_property("RADIUS", config.radius);
565 output.tag.set_property("STEPS", config.steps);
566 output.tag.set_property("R", config.r);
567 output.tag.set_property("G", config.g);
568 output.tag.set_property("B", config.b);
569 output.tag.set_property("A", config.a);
571 output.tag.set_title("/ZOOMBLUR");
573 output.append_newline();
574 output.terminate_string();
577 void ZoomBlurMain::read_data(KeyFrame *keyframe)
581 input.set_shared_input(keyframe->xbuf);
587 result = input.read_tag();
591 if(input.tag.title_is("ZOOMBLUR"))
593 config.x = input.tag.get_property("X", config.x);
594 config.y = input.tag.get_property("Y", config.y);
595 config.radius = input.tag.get_property("RADIUS", config.radius);
596 config.steps = input.tag.get_property("STEPS", config.steps);
597 config.r = input.tag.get_property("R", config.r);
598 config.g = input.tag.get_property("G", config.g);
599 config.b = input.tag.get_property("B", config.b);
600 config.a = input.tag.get_property("A", config.a);
607 static void draw_box(float x1, float y1, float x2, float y2)
610 glVertex3f(x1, y1, 0.0);
611 glVertex3f(x2, y1, 0.0);
612 glVertex3f(x2, y2, 0.0);
613 glVertex3f(x1, y2, 0.0);
618 int ZoomBlurMain::handle_opengl()
621 get_output()->to_texture();
622 get_output()->enable_opengl();
623 get_output()->init_screen();
624 get_output()->bind_texture(0);
626 //int is_yuv = BC_CModels::is_yuv(get_output()->get_color_model());
627 glClearColor(0.0, 0.0, 0.0, 0.0);
628 glClear(GL_COLOR_BUFFER_BIT);
630 // Draw unselected channels
632 glBlendFunc(GL_ONE, GL_ONE);
633 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 get_output()->draw_texture(0,
657 get_output()->get_w(),
658 get_output()->get_h(),
660 get_output()->get_h() - layer_table[i].y1,
662 get_output()->get_h() - layer_table[i].y2,
666 glDisable(GL_TEXTURE_2D);
667 if(BC_CModels::is_yuv(get_output()->get_color_model()))
669 glColor4f(config.r ? 0.0 : 0,
673 float center_x1 = 0.0;
674 float center_x2 = get_output()->get_w();
675 if(layer_table[i].x1 > 0)
677 center_x1 = layer_table[i].x1;
678 draw_box(0, 0, layer_table[i].x1, -get_output()->get_h());
680 if(layer_table[i].x2 < get_output()->get_w())
682 center_x2 = layer_table[i].x2;
683 draw_box(layer_table[i].x2, 0, get_output()->get_w(), -get_output()->get_h());
685 if(layer_table[i].y1 > 0)
688 -get_output()->get_h(),
690 -get_output()->get_h() + layer_table[i].y1);
692 if(layer_table[i].y2 < get_output()->get_h())
695 -get_output()->get_h() + layer_table[i].y2,
702 glAccum(GL_ACCUM, fraction);
703 glEnable(GL_TEXTURE_2D);
704 glColor4f(config.r ? 1 : 0,
711 glReadBuffer(GL_BACK);
712 glDisable(GL_TEXTURE_2D);
713 glAccum(GL_RETURN, 1.0);
715 glColor4f(1, 1, 1, 1);
716 get_output()->set_opengl_state(VFrame::SCREEN);
734 ZoomBlurPackage::ZoomBlurPackage()
742 ZoomBlurUnit::ZoomBlurUnit(ZoomBlurEngine *server,
743 ZoomBlurMain *plugin)
746 this->plugin = plugin;
747 this->server = server;
751 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP_TYPE, MAX, DO_YUV) \
753 const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
754 for(int j = pkg->y1; j < pkg->y2; j++) \
756 TEMP_TYPE *out_row = (TEMP_TYPE*)plugin->accum + COMPONENTS * w * j; \
757 int in_y = y_table[j]; \
760 if(in_y >= 0 && in_y < h) \
762 TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
763 for(int k = 0; k < w; k++) \
765 int in_x = x_table[k]; \
767 if(in_x >= 0 && in_x < w) \
769 int in_offset = in_x * COMPONENTS; \
770 *out_row++ += in_row[in_offset]; \
773 *out_row++ += in_row[in_offset + 1]; \
774 *out_row++ += in_row[in_offset + 2]; \
778 *out_row++ += (TEMP_TYPE)in_row[in_offset + 1]; \
779 *out_row++ += (TEMP_TYPE)in_row[in_offset + 2]; \
781 if(COMPONENTS == 4) \
782 *out_row++ += in_row[in_offset + 3]; \
784 /* Blend nothing */ \
790 *out_row++ += chroma_offset; \
791 *out_row++ += chroma_offset; \
797 if(COMPONENTS == 4) out_row++; \
804 for(int k = 0; k < w; k++) \
807 *out_row++ += chroma_offset; \
808 *out_row++ += chroma_offset; \
809 if(COMPONENTS == 4) out_row++; \
814 /* Copy just selected blurred channels to output and combine with original \
815 unblurred channels */ \
816 if(i == plugin->config.steps - 1) \
818 for(int j = pkg->y1; j < pkg->y2; j++) \
820 TEMP_TYPE *in_row = (TEMP_TYPE*)plugin->accum + COMPONENTS * w * j; \
821 TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
822 TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
823 for(int k = 0; k < w; k++) \
827 *out_row++ = (*in_row++ * fraction) / 0x10000; \
832 *out_row++ = *in_backup++; \
840 *out_row++ = ((*in_row++ * fraction) / 0x10000); \
845 *out_row++ = *in_backup++; \
851 *out_row++ = ((*in_row++ * fraction) / 0x10000); \
856 *out_row++ = *in_backup++; \
864 *out_row++ = (*in_row++ * fraction) / 0x10000; \
869 *out_row++ = *in_backup++; \
875 *out_row++ = (*in_row++ * fraction) / 0x10000; \
880 *out_row++ = *in_backup++; \
885 if(COMPONENTS == 4) \
889 *out_row++ = (*in_row++ * fraction) / 0x10000; \
894 *out_row++ = *in_backup++; \
903 void ZoomBlurUnit::process_package(LoadPackage *package)
905 ZoomBlurPackage *pkg = (ZoomBlurPackage*)package;
906 int h = plugin->output->get_h();
907 int w = plugin->output->get_w();
908 int do_r = plugin->config.r;
909 int do_g = plugin->config.g;
910 int do_b = plugin->config.b;
911 int do_a = plugin->config.a;
913 int fraction = 0x10000 / plugin->config.steps;
914 for(int i = 0; i < plugin->config.steps; i++)
916 int *x_table = plugin->scale_x_table[i];
917 int *y_table = plugin->scale_y_table[i];
919 switch(plugin->input->get_color_model())
922 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
925 BLEND_LAYER(3, float, float, 1, 0)
928 BLEND_LAYER(4, float, float, 1, 0)
931 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
934 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
936 case BC_RGBA16161616:
937 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
940 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
943 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
946 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
948 case BC_YUVA16161616:
949 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
960 ZoomBlurEngine::ZoomBlurEngine(ZoomBlurMain *plugin,
963 : LoadServer(total_clients, total_packages)
965 this->plugin = plugin;
968 void ZoomBlurEngine::init_packages()
970 for(int i = 0; i < get_total_packages(); i++)
972 ZoomBlurPackage *package = (ZoomBlurPackage*)get_package(i);
973 package->y1 = plugin->output->get_h() * i / get_total_packages();
974 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
978 LoadClient* ZoomBlurEngine::new_client()
980 return new ZoomBlurUnit(this, plugin);
983 LoadPackage* ZoomBlurEngine::new_package()
985 return new ZoomBlurPackage;