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 = xS(50);
51 case RESET_YSLIDER : y = yS(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()
142 int xs10 = xS(10), xs50 = xS(50), xs100 = xS(100);
143 int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40);
144 int x = xs10, y = ys10;
145 int x1 = 0; int clrBtn_w = xs50;
146 int defaultBtn_w = xs100;
148 add_subwindow(new BC_Title(x, y, _("X:")));
150 add_subwindow(this->x = new ZoomBlurSize(plugin, x, y, &plugin->config.x, 0, 100));
151 x1 = x + this->x->get_w() + xs10;
152 add_subwindow(xClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_XSLIDER));
155 add_subwindow(new BC_Title(x, y, _("Y:")));
157 add_subwindow(this->y = new ZoomBlurSize(plugin, x, y, &plugin->config.y, 0, 100));
158 add_subwindow(yClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_YSLIDER));
161 add_subwindow(new BC_Title(x, y, _("Radius:")));
163 add_subwindow(radius = new ZoomBlurSize(plugin, x, y, &plugin->config.radius, -100, 100));
164 add_subwindow(radiusClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_RADIUS));
167 add_subwindow(new BC_Title(x, y, _("Steps:")));
169 add_subwindow(steps = new ZoomBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
170 add_subwindow(stepsClr = new ZoomBlurSliderClr(plugin, this, x1, y, clrBtn_w, RESET_STEPS));
173 add_subwindow(r = new ZoomBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
175 add_subwindow(g = new ZoomBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
177 add_subwindow(b = new ZoomBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
179 add_subwindow(a = new ZoomBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
181 add_subwindow(reset = new ZoomBlurReset(plugin, this, x, y));
182 add_subwindow(default_settings = new ZoomBlurDefaultSettings(plugin, this,
183 (xS(280) - xS(10) - defaultBtn_w), y, defaultBtn_w));
190 void ZoomBlurWindow::update_gui(int clear)
193 case RESET_XSLIDER : this->x->update(plugin->config.x);
195 case RESET_YSLIDER : this->y->update(plugin->config.y);
197 case RESET_RADIUS : radius->update(plugin->config.radius);
199 case RESET_STEPS : steps->update(plugin->config.steps);
202 case RESET_DEFAULT_SETTINGS :
204 this->x->update(plugin->config.x);
205 this->y->update(plugin->config.x);
206 radius->update(plugin->config.radius);
207 steps->update(plugin->config.steps);
208 r->update(plugin->config.r);
209 g->update(plugin->config.g);
210 b->update(plugin->config.b);
211 a->update(plugin->config.a);
225 ZoomBlurToggle::ZoomBlurToggle(ZoomBlurMain *plugin,
230 : BC_CheckBox(x, y, *output, string)
232 this->plugin = plugin;
233 this->output = output;
236 int ZoomBlurToggle::handle_event()
238 *output = get_value();
239 plugin->send_configure_change();
249 ZoomBlurSize::ZoomBlurSize(ZoomBlurMain *plugin,
255 : BC_ISlider(x, y, 0, xS(200), yS(200), min, max, *output)
257 this->plugin = plugin;
258 this->output = output;
260 int ZoomBlurSize::handle_event()
262 *output = get_value();
263 plugin->send_configure_change();
268 ZoomBlurReset::ZoomBlurReset(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y)
269 : BC_GenericButton(x, y, _("Reset"))
271 this->plugin = plugin;
272 this->window = window;
274 ZoomBlurReset::~ZoomBlurReset()
277 int ZoomBlurReset::handle_event()
279 plugin->config.reset(RESET_ALL);
280 window->update_gui(RESET_ALL);
281 plugin->send_configure_change();
286 ZoomBlurDefaultSettings::ZoomBlurDefaultSettings(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y, int w)
287 : BC_GenericButton(x, y, w, _("Default"))
289 this->plugin = plugin;
290 this->window = window;
292 ZoomBlurDefaultSettings::~ZoomBlurDefaultSettings()
295 int ZoomBlurDefaultSettings::handle_event()
297 plugin->config.reset(RESET_DEFAULT_SETTINGS);
298 window->update_gui(RESET_DEFAULT_SETTINGS);
299 plugin->send_configure_change();
304 ZoomBlurSliderClr::ZoomBlurSliderClr(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y, int w, int clear)
305 : BC_Button(x, y, w, plugin->get_theme()->get_image_set("reset_button"))
307 this->plugin = plugin;
308 this->window = window;
311 ZoomBlurSliderClr::~ZoomBlurSliderClr()
314 int ZoomBlurSliderClr::handle_event()
316 // clear==1 ==> X slider
317 // clear==2 ==> Y slider
318 // clear==3 ==> Radius slider
319 // clear==4 ==> Steps slider
320 plugin->config.reset(clear);
321 window->update_gui(clear);
322 plugin->send_configure_change();
329 ZoomBlurMain::ZoomBlurMain(PluginServer *server)
330 : PluginVClient(server)
339 need_reconfigure = 1;
343 ZoomBlurMain::~ZoomBlurMain()
346 if(engine) delete engine;
348 if(accum) delete [] accum;
349 if(temp) delete temp;
352 const char* ZoomBlurMain::plugin_title() { return N_("Zoom Blur"); }
353 int ZoomBlurMain::is_realtime() { return 1; }
357 NEW_WINDOW_MACRO(ZoomBlurMain, ZoomBlurWindow)
359 LOAD_CONFIGURATION_MACRO(ZoomBlurMain, ZoomBlurConfig)
361 void ZoomBlurMain::delete_tables()
365 for(int i = 0; i < table_entries; i++)
366 delete [] scale_x_table[i];
367 delete [] scale_x_table;
372 for(int i = 0; i < table_entries; i++)
373 delete [] scale_y_table[i];
374 delete [] scale_y_table;
377 delete [] layer_table;
384 int ZoomBlurMain::process_buffer(VFrame *frame,
385 int64_t start_position,
388 need_reconfigure |= load_configuration();
394 get_source_position(),
400 // Generate tables here. The same table is used by many packages to render
401 // each horizontal stripe. Need to cover the entire output range in each
402 // table to avoid green borders
406 float w = frame->get_w();
407 float h = frame->get_h();
408 float center_x = (float)config.x / 100 * w;
409 float center_y = (float)config.y / 100 * h;
410 float radius = (float)(100 + config.radius) / 100;
412 //float max_w, max_h;
413 int steps = config.steps ? config.steps : 1;
425 // printf("ZoomBlurMain::process_realtime 1 %d %d\n",
429 center_x = (center_x - w / 2) * (1.0 - radius) + w / 2;
430 center_y = (center_y - h / 2) * (1.0 - radius) + h / 2;
435 min_x1 = center_x - min_w / 2;
436 min_y1 = center_y - min_h / 2;
437 min_x2 = center_x + min_w / 2;
438 min_y2 = center_y + min_h / 2;
445 // printf("ZoomBlurMain::process_realtime 2 w=%f radius=%f center_x=%f\n",
451 // Dimensions of outermost rectangle
454 table_entries = steps;
455 scale_x_table = new int*[steps];
456 scale_y_table = new int*[steps];
457 layer_table = new ZoomBlurLayer[table_entries];
460 for(int i = 0; i < steps; i++)
462 float fraction = (float)i / steps;
463 float inv_fraction = 1.0 - fraction;
464 float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
465 float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
466 float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
467 float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
468 float out_w = out_x2 - out_x1;
469 float out_h = out_y2 - out_y1;
470 if(out_w < 0) out_w = 0;
471 if(out_h < 0) out_h = 0;
472 float scale_x = (float)w / out_w;
473 float scale_y = (float)h / out_h;
477 scale_y_table[i] = y_table = new int[(int)(h + 1)];
478 scale_x_table[i] = x_table = new int[(int)(w + 1)];
480 layer_table[i].x1 = out_x1;
481 layer_table[i].y1 = out_y1;
482 layer_table[i].x2 = out_x2;
483 layer_table[i].y2 = out_y2;
486 for(int j = 0; j < h; j++)
488 y_table[j] = (int)((j - out_y1) * scale_y);
490 for(int j = 0; j < w; j++)
492 x_table[j] = (int)((j - out_x1) * scale_x);
493 //printf("ZoomBlurMain::process_realtime %d %d\n", j, x_table[j]);
497 need_reconfigure = 0;
501 if(get_use_opengl()) return run_opengl();
507 if(!engine) engine = new ZoomBlurEngine(this,
508 get_project_smp() + 1,
509 get_project_smp() + 1);
510 if(!accum) accum = new unsigned char[frame->get_w() *
512 BC_CModels::components(frame->get_color_model()) *
513 MAX(sizeof(int), sizeof(float))];
516 this->output = frame;
519 if(!temp) temp = new VFrame(frame->get_w(), frame->get_h(),
520 frame->get_color_model(), 0);
521 temp->copy_from(frame);
527 BC_CModels::components(frame->get_color_model()) *
528 MAX(sizeof(int), sizeof(float)));
529 engine->process_packages();
534 void ZoomBlurMain::update_gui()
538 load_configuration();
539 thread->window->lock_window();
540 ((ZoomBlurWindow*)thread->window)->x->update(config.x);
541 ((ZoomBlurWindow*)thread->window)->y->update(config.y);
542 ((ZoomBlurWindow*)thread->window)->radius->update(config.radius);
543 ((ZoomBlurWindow*)thread->window)->steps->update(config.steps);
544 ((ZoomBlurWindow*)thread->window)->r->update(config.r);
545 ((ZoomBlurWindow*)thread->window)->g->update(config.g);
546 ((ZoomBlurWindow*)thread->window)->b->update(config.b);
547 ((ZoomBlurWindow*)thread->window)->a->update(config.a);
548 thread->window->unlock_window();
556 void ZoomBlurMain::save_data(KeyFrame *keyframe)
560 // cause data to be stored directly in text
561 output.set_shared_output(keyframe->xbuf);
562 output.tag.set_title("ZOOMBLUR");
564 output.tag.set_property("X", config.x);
565 output.tag.set_property("Y", config.y);
566 output.tag.set_property("RADIUS", config.radius);
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("/ZOOMBLUR");
575 output.append_newline();
576 output.terminate_string();
579 void ZoomBlurMain::read_data(KeyFrame *keyframe)
583 input.set_shared_input(keyframe->xbuf);
589 result = input.read_tag();
593 if(input.tag.title_is("ZOOMBLUR"))
595 config.x = input.tag.get_property("X", config.x);
596 config.y = input.tag.get_property("Y", config.y);
597 config.radius = input.tag.get_property("RADIUS", config.radius);
598 config.steps = input.tag.get_property("STEPS", config.steps);
599 config.r = input.tag.get_property("R", config.r);
600 config.g = input.tag.get_property("G", config.g);
601 config.b = input.tag.get_property("B", config.b);
602 config.a = input.tag.get_property("A", config.a);
609 static void draw_box(float x1, float y1, float x2, float y2)
612 glVertex3f(x1, y1, 0.0);
613 glVertex3f(x2, y1, 0.0);
614 glVertex3f(x2, y2, 0.0);
615 glVertex3f(x1, y2, 0.0);
620 int ZoomBlurMain::handle_opengl()
623 get_output()->to_texture();
624 get_output()->enable_opengl();
625 get_output()->init_screen();
626 get_output()->bind_texture(0);
628 //int is_yuv = BC_CModels::is_yuv(get_output()->get_color_model());
629 glClearColor(0.0, 0.0, 0.0, 0.0);
630 glClear(GL_COLOR_BUFFER_BIT);
632 // Draw unselected channels
634 glBlendFunc(GL_ONE, GL_ONE);
635 glDrawBuffer(GL_BACK);
637 if(!config.r || !config.g || !config.b || !config.a)
639 glColor4f(config.r ? 0 : 1,
643 get_output()->draw_texture();
645 glAccum(GL_LOAD, 1.0);
647 // Blur selected channels
648 float fraction = 1.0 / config.steps;
649 for(int i = 0; i < config.steps; i++)
651 glClear(GL_COLOR_BUFFER_BIT);
652 glColor4f(config.r ? 1 : 0,
657 get_output()->draw_texture(0,
659 get_output()->get_w(),
660 get_output()->get_h(),
662 get_output()->get_h() - layer_table[i].y1,
664 get_output()->get_h() - layer_table[i].y2,
668 glDisable(GL_TEXTURE_2D);
669 if(BC_CModels::is_yuv(get_output()->get_color_model()))
671 glColor4f(config.r ? 0.0 : 0,
675 float center_x1 = 0.0;
676 float center_x2 = get_output()->get_w();
677 if(layer_table[i].x1 > 0)
679 center_x1 = layer_table[i].x1;
680 draw_box(0, 0, layer_table[i].x1, -get_output()->get_h());
682 if(layer_table[i].x2 < get_output()->get_w())
684 center_x2 = layer_table[i].x2;
685 draw_box(layer_table[i].x2, 0, get_output()->get_w(), -get_output()->get_h());
687 if(layer_table[i].y1 > 0)
690 -get_output()->get_h(),
692 -get_output()->get_h() + layer_table[i].y1);
694 if(layer_table[i].y2 < get_output()->get_h())
697 -get_output()->get_h() + layer_table[i].y2,
704 glAccum(GL_ACCUM, fraction);
705 glEnable(GL_TEXTURE_2D);
706 glColor4f(config.r ? 1 : 0,
713 glReadBuffer(GL_BACK);
714 glDisable(GL_TEXTURE_2D);
715 glAccum(GL_RETURN, 1.0);
717 glColor4f(1, 1, 1, 1);
718 get_output()->set_opengl_state(VFrame::SCREEN);
736 ZoomBlurPackage::ZoomBlurPackage()
744 ZoomBlurUnit::ZoomBlurUnit(ZoomBlurEngine *server,
745 ZoomBlurMain *plugin)
748 this->plugin = plugin;
749 this->server = server;
753 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP_TYPE, MAX, DO_YUV) \
755 const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
756 for(int j = pkg->y1; j < pkg->y2; j++) \
758 TEMP_TYPE *out_row = (TEMP_TYPE*)plugin->accum + COMPONENTS * w * j; \
759 int in_y = y_table[j]; \
762 if(in_y >= 0 && in_y < h) \
764 TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
765 for(int k = 0; k < w; k++) \
767 int in_x = x_table[k]; \
769 if(in_x >= 0 && in_x < w) \
771 int in_offset = in_x * COMPONENTS; \
772 *out_row++ += in_row[in_offset]; \
775 *out_row++ += in_row[in_offset + 1]; \
776 *out_row++ += in_row[in_offset + 2]; \
780 *out_row++ += (TEMP_TYPE)in_row[in_offset + 1]; \
781 *out_row++ += (TEMP_TYPE)in_row[in_offset + 2]; \
783 if(COMPONENTS == 4) \
784 *out_row++ += in_row[in_offset + 3]; \
786 /* Blend nothing */ \
792 *out_row++ += chroma_offset; \
793 *out_row++ += chroma_offset; \
799 if(COMPONENTS == 4) out_row++; \
806 for(int k = 0; k < w; k++) \
809 *out_row++ += chroma_offset; \
810 *out_row++ += chroma_offset; \
811 if(COMPONENTS == 4) out_row++; \
816 /* Copy just selected blurred channels to output and combine with original \
817 unblurred channels */ \
818 if(i == plugin->config.steps - 1) \
820 for(int j = pkg->y1; j < pkg->y2; j++) \
822 TEMP_TYPE *in_row = (TEMP_TYPE*)plugin->accum + COMPONENTS * w * j; \
823 TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
824 TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
825 for(int k = 0; k < w; k++) \
829 *out_row++ = (*in_row++ * fraction) / 0x10000; \
834 *out_row++ = *in_backup++; \
842 *out_row++ = ((*in_row++ * fraction) / 0x10000); \
847 *out_row++ = *in_backup++; \
853 *out_row++ = ((*in_row++ * fraction) / 0x10000); \
858 *out_row++ = *in_backup++; \
866 *out_row++ = (*in_row++ * fraction) / 0x10000; \
871 *out_row++ = *in_backup++; \
877 *out_row++ = (*in_row++ * fraction) / 0x10000; \
882 *out_row++ = *in_backup++; \
887 if(COMPONENTS == 4) \
891 *out_row++ = (*in_row++ * fraction) / 0x10000; \
896 *out_row++ = *in_backup++; \
905 void ZoomBlurUnit::process_package(LoadPackage *package)
907 ZoomBlurPackage *pkg = (ZoomBlurPackage*)package;
908 int h = plugin->output->get_h();
909 int w = plugin->output->get_w();
910 int do_r = plugin->config.r;
911 int do_g = plugin->config.g;
912 int do_b = plugin->config.b;
913 int do_a = plugin->config.a;
915 int fraction = 0x10000 / plugin->config.steps;
916 for(int i = 0; i < plugin->config.steps; i++)
918 int *x_table = plugin->scale_x_table[i];
919 int *y_table = plugin->scale_y_table[i];
921 switch(plugin->input->get_color_model())
924 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
927 BLEND_LAYER(3, float, float, 1, 0)
930 BLEND_LAYER(4, float, float, 1, 0)
933 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
936 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
938 case BC_RGBA16161616:
939 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
942 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
945 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
948 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
950 case BC_YUVA16161616:
951 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
962 ZoomBlurEngine::ZoomBlurEngine(ZoomBlurMain *plugin,
965 : LoadServer(total_clients, total_packages)
967 this->plugin = plugin;
970 void ZoomBlurEngine::init_packages()
972 for(int i = 0; i < get_total_packages(); i++)
974 ZoomBlurPackage *package = (ZoomBlurPackage*)get_package(i);
975 package->y1 = plugin->output->get_h() * i / get_total_packages();
976 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
980 LoadClient* ZoomBlurEngine::new_client()
982 return new ZoomBlurUnit(this, plugin);
985 LoadPackage* ZoomBlurEngine::new_package()
987 return new ZoomBlurPackage;