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()
36 void ZoomBlurConfig::reset()
48 int ZoomBlurConfig::equivalent(ZoomBlurConfig &that)
53 radius == that.radius &&
54 steps == that.steps &&
61 void ZoomBlurConfig::copy_from(ZoomBlurConfig &that)
73 void ZoomBlurConfig::interpolate(ZoomBlurConfig &prev,
79 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
80 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
81 this->x = (int)(prev.x * prev_scale + next.x * next_scale + 0.5);
82 this->y = (int)(prev.y * prev_scale + next.y * next_scale + 0.5);
83 this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
84 this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
102 ZoomBlurWindow::ZoomBlurWindow(ZoomBlurMain *plugin)
103 : PluginClientWindow(plugin,
110 this->plugin = plugin;
113 ZoomBlurWindow::~ZoomBlurWindow()
117 void ZoomBlurWindow::create_objects()
121 add_subwindow(new BC_Title(x, y, _("X:")));
123 add_subwindow(this->x = new ZoomBlurSize(plugin, x, y, &plugin->config.x, 0, 100));
125 add_subwindow(new BC_Title(x, y, _("Y:")));
127 add_subwindow(this->y = new ZoomBlurSize(plugin, x, y, &plugin->config.y, 0, 100));
129 add_subwindow(new BC_Title(x, y, _("Radius:")));
131 add_subwindow(radius = new ZoomBlurSize(plugin, x, y, &plugin->config.radius, -100, 100));
133 add_subwindow(new BC_Title(x, y, _("Steps:")));
135 add_subwindow(steps = new ZoomBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
137 add_subwindow(r = new ZoomBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
139 add_subwindow(g = new ZoomBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
141 add_subwindow(b = new ZoomBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
143 add_subwindow(a = new ZoomBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
145 add_subwindow(reset = new ZoomBlurReset(plugin, this, x, y));
152 void ZoomBlurWindow::update()
154 this->x->update(plugin->config.x);
155 this->y->update(plugin->config.x);
156 radius->update(plugin->config.radius);
157 steps->update(plugin->config.steps);
158 r->update(plugin->config.r);
159 g->update(plugin->config.g);
160 b->update(plugin->config.b);
161 a->update(plugin->config.a);
173 ZoomBlurToggle::ZoomBlurToggle(ZoomBlurMain *plugin,
178 : BC_CheckBox(x, y, *output, string)
180 this->plugin = plugin;
181 this->output = output;
184 int ZoomBlurToggle::handle_event()
186 *output = get_value();
187 plugin->send_configure_change();
197 ZoomBlurSize::ZoomBlurSize(ZoomBlurMain *plugin,
203 : BC_ISlider(x, y, 0, 200, 200, min, max, *output)
205 this->plugin = plugin;
206 this->output = output;
208 int ZoomBlurSize::handle_event()
210 *output = get_value();
211 plugin->send_configure_change();
216 ZoomBlurReset::ZoomBlurReset(ZoomBlurMain *plugin, ZoomBlurWindow *window, int x, int y)
217 : BC_GenericButton(x, y, _("Reset"))
219 this->plugin = plugin;
220 this->window = window;
222 ZoomBlurReset::~ZoomBlurReset()
225 int ZoomBlurReset::handle_event()
227 plugin->config.reset();
229 plugin->send_configure_change();
238 ZoomBlurMain::ZoomBlurMain(PluginServer *server)
239 : PluginVClient(server)
248 need_reconfigure = 1;
252 ZoomBlurMain::~ZoomBlurMain()
255 if(engine) delete engine;
257 if(accum) delete [] accum;
258 if(temp) delete temp;
261 const char* ZoomBlurMain::plugin_title() { return N_("Zoom Blur"); }
262 int ZoomBlurMain::is_realtime() { return 1; }
266 NEW_WINDOW_MACRO(ZoomBlurMain, ZoomBlurWindow)
268 LOAD_CONFIGURATION_MACRO(ZoomBlurMain, ZoomBlurConfig)
270 void ZoomBlurMain::delete_tables()
274 for(int i = 0; i < table_entries; i++)
275 delete [] scale_x_table[i];
276 delete [] scale_x_table;
281 for(int i = 0; i < table_entries; i++)
282 delete [] scale_y_table[i];
283 delete [] scale_y_table;
286 delete [] layer_table;
293 int ZoomBlurMain::process_buffer(VFrame *frame,
294 int64_t start_position,
297 need_reconfigure |= load_configuration();
303 get_source_position(),
309 // Generate tables here. The same table is used by many packages to render
310 // each horizontal stripe. Need to cover the entire output range in each
311 // table to avoid green borders
315 float w = frame->get_w();
316 float h = frame->get_h();
317 float center_x = (float)config.x / 100 * w;
318 float center_y = (float)config.y / 100 * h;
319 float radius = (float)(100 + config.radius) / 100;
321 //float max_w, max_h;
322 int steps = config.steps ? config.steps : 1;
334 // printf("ZoomBlurMain::process_realtime 1 %d %d\n",
338 center_x = (center_x - w / 2) * (1.0 - radius) + w / 2;
339 center_y = (center_y - h / 2) * (1.0 - radius) + h / 2;
344 min_x1 = center_x - min_w / 2;
345 min_y1 = center_y - min_h / 2;
346 min_x2 = center_x + min_w / 2;
347 min_y2 = center_y + min_h / 2;
354 // printf("ZoomBlurMain::process_realtime 2 w=%f radius=%f center_x=%f\n",
360 // Dimensions of outermost rectangle
363 table_entries = steps;
364 scale_x_table = new int*[steps];
365 scale_y_table = new int*[steps];
366 layer_table = new ZoomBlurLayer[table_entries];
369 for(int i = 0; i < steps; i++)
371 float fraction = (float)i / steps;
372 float inv_fraction = 1.0 - fraction;
373 float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
374 float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
375 float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
376 float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
377 float out_w = out_x2 - out_x1;
378 float out_h = out_y2 - out_y1;
379 if(out_w < 0) out_w = 0;
380 if(out_h < 0) out_h = 0;
381 float scale_x = (float)w / out_w;
382 float scale_y = (float)h / out_h;
386 scale_y_table[i] = y_table = new int[(int)(h + 1)];
387 scale_x_table[i] = x_table = new int[(int)(w + 1)];
389 layer_table[i].x1 = out_x1;
390 layer_table[i].y1 = out_y1;
391 layer_table[i].x2 = out_x2;
392 layer_table[i].y2 = out_y2;
395 for(int j = 0; j < h; j++)
397 y_table[j] = (int)((j - out_y1) * scale_y);
399 for(int j = 0; j < w; j++)
401 x_table[j] = (int)((j - out_x1) * scale_x);
402 //printf("ZoomBlurMain::process_realtime %d %d\n", j, x_table[j]);
406 need_reconfigure = 0;
410 if(get_use_opengl()) return run_opengl();
416 if(!engine) engine = new ZoomBlurEngine(this,
417 get_project_smp() + 1,
418 get_project_smp() + 1);
419 if(!accum) accum = new unsigned char[frame->get_w() *
421 BC_CModels::components(frame->get_color_model()) *
422 MAX(sizeof(int), sizeof(float))];
425 this->output = frame;
428 if(!temp) temp = new VFrame(frame->get_w(), frame->get_h(),
429 frame->get_color_model(), 0);
430 temp->copy_from(frame);
436 BC_CModels::components(frame->get_color_model()) *
437 MAX(sizeof(int), sizeof(float)));
438 engine->process_packages();
443 void ZoomBlurMain::update_gui()
447 load_configuration();
448 thread->window->lock_window();
449 ((ZoomBlurWindow*)thread->window)->x->update(config.x);
450 ((ZoomBlurWindow*)thread->window)->y->update(config.y);
451 ((ZoomBlurWindow*)thread->window)->radius->update(config.radius);
452 ((ZoomBlurWindow*)thread->window)->steps->update(config.steps);
453 ((ZoomBlurWindow*)thread->window)->r->update(config.r);
454 ((ZoomBlurWindow*)thread->window)->g->update(config.g);
455 ((ZoomBlurWindow*)thread->window)->b->update(config.b);
456 ((ZoomBlurWindow*)thread->window)->a->update(config.a);
457 thread->window->unlock_window();
465 void ZoomBlurMain::save_data(KeyFrame *keyframe)
469 // cause data to be stored directly in text
470 output.set_shared_output(keyframe->xbuf);
471 output.tag.set_title("ZOOMBLUR");
473 output.tag.set_property("X", config.x);
474 output.tag.set_property("Y", config.y);
475 output.tag.set_property("RADIUS", config.radius);
476 output.tag.set_property("STEPS", config.steps);
477 output.tag.set_property("R", config.r);
478 output.tag.set_property("G", config.g);
479 output.tag.set_property("B", config.b);
480 output.tag.set_property("A", config.a);
482 output.tag.set_title("/ZOOMBLUR");
484 output.append_newline();
485 output.terminate_string();
488 void ZoomBlurMain::read_data(KeyFrame *keyframe)
492 input.set_shared_input(keyframe->xbuf);
498 result = input.read_tag();
502 if(input.tag.title_is("ZOOMBLUR"))
504 config.x = input.tag.get_property("X", config.x);
505 config.y = input.tag.get_property("Y", config.y);
506 config.radius = input.tag.get_property("RADIUS", config.radius);
507 config.steps = input.tag.get_property("STEPS", config.steps);
508 config.r = input.tag.get_property("R", config.r);
509 config.g = input.tag.get_property("G", config.g);
510 config.b = input.tag.get_property("B", config.b);
511 config.a = input.tag.get_property("A", config.a);
518 static void draw_box(float x1, float y1, float x2, float y2)
521 glVertex3f(x1, y1, 0.0);
522 glVertex3f(x2, y1, 0.0);
523 glVertex3f(x2, y2, 0.0);
524 glVertex3f(x1, y2, 0.0);
529 int ZoomBlurMain::handle_opengl()
532 get_output()->to_texture();
533 get_output()->enable_opengl();
534 get_output()->init_screen();
535 get_output()->bind_texture(0);
537 //int is_yuv = BC_CModels::is_yuv(get_output()->get_color_model());
538 glClearColor(0.0, 0.0, 0.0, 0.0);
539 glClear(GL_COLOR_BUFFER_BIT);
541 // Draw unselected channels
543 glBlendFunc(GL_ONE, GL_ONE);
544 glDrawBuffer(GL_BACK);
546 if(!config.r || !config.g || !config.b || !config.a)
548 glColor4f(config.r ? 0 : 1,
552 get_output()->draw_texture();
554 glAccum(GL_LOAD, 1.0);
556 // Blur selected channels
557 float fraction = 1.0 / config.steps;
558 for(int i = 0; i < config.steps; i++)
560 glClear(GL_COLOR_BUFFER_BIT);
561 glColor4f(config.r ? 1 : 0,
566 get_output()->draw_texture(0,
568 get_output()->get_w(),
569 get_output()->get_h(),
571 get_output()->get_h() - layer_table[i].y1,
573 get_output()->get_h() - layer_table[i].y2,
577 glDisable(GL_TEXTURE_2D);
578 if(BC_CModels::is_yuv(get_output()->get_color_model()))
580 glColor4f(config.r ? 0.0 : 0,
584 float center_x1 = 0.0;
585 float center_x2 = get_output()->get_w();
586 if(layer_table[i].x1 > 0)
588 center_x1 = layer_table[i].x1;
589 draw_box(0, 0, layer_table[i].x1, -get_output()->get_h());
591 if(layer_table[i].x2 < get_output()->get_w())
593 center_x2 = layer_table[i].x2;
594 draw_box(layer_table[i].x2, 0, get_output()->get_w(), -get_output()->get_h());
596 if(layer_table[i].y1 > 0)
599 -get_output()->get_h(),
601 -get_output()->get_h() + layer_table[i].y1);
603 if(layer_table[i].y2 < get_output()->get_h())
606 -get_output()->get_h() + layer_table[i].y2,
613 glAccum(GL_ACCUM, fraction);
614 glEnable(GL_TEXTURE_2D);
615 glColor4f(config.r ? 1 : 0,
622 glReadBuffer(GL_BACK);
623 glDisable(GL_TEXTURE_2D);
624 glAccum(GL_RETURN, 1.0);
626 glColor4f(1, 1, 1, 1);
627 get_output()->set_opengl_state(VFrame::SCREEN);
645 ZoomBlurPackage::ZoomBlurPackage()
653 ZoomBlurUnit::ZoomBlurUnit(ZoomBlurEngine *server,
654 ZoomBlurMain *plugin)
657 this->plugin = plugin;
658 this->server = server;
662 #define BLEND_LAYER(COMPONENTS, TYPE, TEMP_TYPE, MAX, DO_YUV) \
664 const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
665 for(int j = pkg->y1; j < pkg->y2; j++) \
667 TEMP_TYPE *out_row = (TEMP_TYPE*)plugin->accum + COMPONENTS * w * j; \
668 int in_y = y_table[j]; \
671 if(in_y >= 0 && in_y < h) \
673 TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
674 for(int k = 0; k < w; k++) \
676 int in_x = x_table[k]; \
678 if(in_x >= 0 && in_x < w) \
680 int in_offset = in_x * COMPONENTS; \
681 *out_row++ += in_row[in_offset]; \
684 *out_row++ += in_row[in_offset + 1]; \
685 *out_row++ += in_row[in_offset + 2]; \
689 *out_row++ += (TEMP_TYPE)in_row[in_offset + 1]; \
690 *out_row++ += (TEMP_TYPE)in_row[in_offset + 2]; \
692 if(COMPONENTS == 4) \
693 *out_row++ += in_row[in_offset + 3]; \
695 /* Blend nothing */ \
701 *out_row++ += chroma_offset; \
702 *out_row++ += chroma_offset; \
708 if(COMPONENTS == 4) out_row++; \
715 for(int k = 0; k < w; k++) \
718 *out_row++ += chroma_offset; \
719 *out_row++ += chroma_offset; \
720 if(COMPONENTS == 4) out_row++; \
725 /* Copy just selected blurred channels to output and combine with original \
726 unblurred channels */ \
727 if(i == plugin->config.steps - 1) \
729 for(int j = pkg->y1; j < pkg->y2; j++) \
731 TEMP_TYPE *in_row = (TEMP_TYPE*)plugin->accum + COMPONENTS * w * j; \
732 TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
733 TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
734 for(int k = 0; k < w; k++) \
738 *out_row++ = (*in_row++ * fraction) / 0x10000; \
743 *out_row++ = *in_backup++; \
751 *out_row++ = ((*in_row++ * fraction) / 0x10000); \
756 *out_row++ = *in_backup++; \
762 *out_row++ = ((*in_row++ * fraction) / 0x10000); \
767 *out_row++ = *in_backup++; \
775 *out_row++ = (*in_row++ * fraction) / 0x10000; \
780 *out_row++ = *in_backup++; \
786 *out_row++ = (*in_row++ * fraction) / 0x10000; \
791 *out_row++ = *in_backup++; \
796 if(COMPONENTS == 4) \
800 *out_row++ = (*in_row++ * fraction) / 0x10000; \
805 *out_row++ = *in_backup++; \
814 void ZoomBlurUnit::process_package(LoadPackage *package)
816 ZoomBlurPackage *pkg = (ZoomBlurPackage*)package;
817 int h = plugin->output->get_h();
818 int w = plugin->output->get_w();
819 int do_r = plugin->config.r;
820 int do_g = plugin->config.g;
821 int do_b = plugin->config.b;
822 int do_a = plugin->config.a;
824 int fraction = 0x10000 / plugin->config.steps;
825 for(int i = 0; i < plugin->config.steps; i++)
827 int *x_table = plugin->scale_x_table[i];
828 int *y_table = plugin->scale_y_table[i];
830 switch(plugin->input->get_color_model())
833 BLEND_LAYER(3, uint8_t, int, 0xff, 0)
836 BLEND_LAYER(3, float, float, 1, 0)
839 BLEND_LAYER(4, float, float, 1, 0)
842 BLEND_LAYER(4, uint8_t, int, 0xff, 0)
845 BLEND_LAYER(3, uint16_t, int, 0xffff, 0)
847 case BC_RGBA16161616:
848 BLEND_LAYER(4, uint16_t, int, 0xffff, 0)
851 BLEND_LAYER(3, uint8_t, int, 0xff, 1)
854 BLEND_LAYER(4, uint8_t, int, 0xff, 1)
857 BLEND_LAYER(3, uint16_t, int, 0xffff, 1)
859 case BC_YUVA16161616:
860 BLEND_LAYER(4, uint16_t, int, 0xffff, 1)
871 ZoomBlurEngine::ZoomBlurEngine(ZoomBlurMain *plugin,
874 : LoadServer(total_clients, total_packages)
876 this->plugin = plugin;
879 void ZoomBlurEngine::init_packages()
881 for(int i = 0; i < get_total_packages(); i++)
883 ZoomBlurPackage *package = (ZoomBlurPackage*)get_package(i);
884 package->y1 = plugin->output->get_h() * i / get_total_packages();
885 package->y2 = plugin->output->get_h() * (i + 1) / get_total_packages();
889 LoadClient* ZoomBlurEngine::new_client()
891 return new ZoomBlurUnit(this, plugin);
894 LoadPackage* ZoomBlurEngine::new_package()
896 return new ZoomBlurPackage;