4 * Copyright (C) 2010 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
24 #include "blurwindow.h"
41 BlurConfig::BlurConfig()
46 void BlurConfig::reset()
55 int BlurConfig::equivalent(BlurConfig &that)
57 return (vertical == that.vertical &&
58 horizontal == that.horizontal &&
59 radius == that.radius &&
60 a_key == that.a_key &&
67 void BlurConfig::copy_from(BlurConfig &that)
69 vertical = that.vertical;
70 horizontal = that.horizontal;
79 void BlurConfig::interpolate(BlurConfig &prev,
83 int64_t current_frame)
85 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
86 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
89 //printf("BlurConfig::interpolate %d %d %d\n", prev_frame, next_frame, current_frame);
90 this->vertical = prev.vertical;
91 this->horizontal = prev.horizontal;
92 this->radius = round(prev.radius * prev_scale + next.radius * next_scale);
105 REGISTER_PLUGIN(BlurMain)
114 BlurMain::BlurMain(PluginServer *server)
115 : PluginVClient(server)
117 need_reconfigure = 1;
122 BlurMain::~BlurMain()
124 //printf("BlurMain::~BlurMain 1\n");
128 for(int i = 0; i < (get_project_smp() + 1); i++)
133 if(overlayer) delete overlayer;
136 const char* BlurMain::plugin_title() { return N_("Blur"); }
137 int BlurMain::is_realtime() { return 1; }
140 NEW_WINDOW_MACRO(BlurMain, BlurWindow)
142 LOAD_CONFIGURATION_MACRO(BlurMain, BlurConfig)
147 int BlurMain::process_buffer(VFrame *frame,
148 int64_t start_position,
153 need_reconfigure |= load_configuration();
161 // Create temp based on alpha keying.
162 // Alpha keying needs 2x oversampling.
166 PluginVClient::new_temp(frame->get_w() * 2,
168 frame->get_color_model());
171 overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
174 overlayer->overlay(PluginVClient::get_temp(),
182 PluginVClient::get_temp()->get_w(),
183 PluginVClient::get_temp()->get_h(),
187 input_frame = PluginVClient::get_temp();
191 PluginVClient::new_temp(frame->get_w(),
193 frame->get_color_model());
198 //printf("BlurMain::process_realtime 1 %d %d\n", need_reconfigure, config.radius);
203 engine = new BlurEngine*[(get_project_smp() + 1)];
204 for(i = 0; i < (get_project_smp() + 1); i++)
206 engine[i] = new BlurEngine(this);
211 for(i = 0; i < (get_project_smp() + 1); i++)
213 engine[i]->reconfigure(&engine[i]->forward_constants, config.radius);
214 engine[i]->reconfigure(&engine[i]->reverse_constants, config.radius);
216 need_reconfigure = 0;
220 if(config.radius < MIN_RADIUS ||
221 (!config.vertical && !config.horizontal))
223 // Data never processed
228 // Need to blur vertically to a temp and
229 // horizontally to the output in 2 discrete passes.
231 for(i = 0; i < get_project_smp() + 1; i++)
233 engine[i]->set_range(
234 input_frame->get_h() * i / (get_project_smp() + 1),
235 input_frame->get_h() * (i + 1) / (get_project_smp() + 1),
236 input_frame->get_w() * i / (get_project_smp() + 1),
237 input_frame->get_w() * (i + 1) / (get_project_smp() + 1));
240 for(i = 0; i < (get_project_smp() + 1); i++)
242 engine[i]->do_horizontal = 0;
243 engine[i]->start_process_frame(input_frame);
246 for(i = 0; i < (get_project_smp() + 1); i++)
248 engine[i]->wait_process_frame();
251 for(i = 0; i < (get_project_smp() + 1); i++)
253 engine[i]->do_horizontal = 1;
254 engine[i]->start_process_frame(input_frame);
257 for(i = 0; i < (get_project_smp() + 1); i++)
259 engine[i]->wait_process_frame();
267 overlayer->overlay(frame,
268 PluginVClient::get_temp(),
271 PluginVClient::get_temp()->get_w(),
272 PluginVClient::get_temp()->get_h(),
286 void BlurMain::update_gui()
290 int reconfigure = load_configuration();
293 ((BlurWindow*)thread->window)->lock_window("BlurMain::update_gui");
294 ((BlurWindow*)thread->window)->horizontal->update(config.horizontal);
295 ((BlurWindow*)thread->window)->vertical->update(config.vertical);
296 ((BlurWindow*)thread->window)->radius->update(config.radius);
297 ((BlurWindow*)thread->window)->radius_text->update((int64_t)config.radius);
298 ((BlurWindow*)thread->window)->a_key->update(config.a_key);
299 ((BlurWindow*)thread->window)->a->update(config.a);
300 ((BlurWindow*)thread->window)->r->update(config.r);
301 ((BlurWindow*)thread->window)->g->update(config.g);
302 ((BlurWindow*)thread->window)->b->update(config.b);
303 ((BlurWindow*)thread->window)->unlock_window();
312 void BlurMain::save_data(KeyFrame *keyframe)
316 // cause data to be stored directly in text
317 output.set_shared_output(keyframe->xbuf);
318 output.tag.set_title("BLUR");
319 output.tag.set_property("VERTICAL", config.vertical);
320 output.tag.set_property("HORIZONTAL", config.horizontal);
321 output.tag.set_property("RADIUS", config.radius);
322 output.tag.set_property("R", config.r);
323 output.tag.set_property("G", config.g);
324 output.tag.set_property("B", config.b);
325 output.tag.set_property("A", config.a);
326 output.tag.set_property("A_KEY", config.a_key);
328 output.tag.set_title("/BLUR");
330 output.append_newline();
331 output.terminate_string();
334 void BlurMain::read_data(KeyFrame *keyframe)
338 input.set_shared_input(keyframe->xbuf);
344 result = input.read_tag();
348 if(input.tag.title_is("BLUR"))
350 config.vertical = input.tag.get_property("VERTICAL", config.vertical);
351 config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
352 config.radius = input.tag.get_property("RADIUS", config.radius);
353 //printf("BlurMain::read_data 1 %d %d %s\n", get_source_position(), keyframe->position, keyframe->get_data());
354 config.r = input.tag.get_property("R", config.r);
355 config.g = input.tag.get_property("G", config.g);
356 config.b = input.tag.get_property("B", config.b);
357 config.a = input.tag.get_property("A", config.a);
358 config.a_key = input.tag.get_property("A_KEY", config.a_key);
372 BlurEngine::BlurEngine(BlurMain *plugin)
375 this->plugin = plugin;
379 int size = plugin->get_input()->get_w() > plugin->get_input()->get_h() ?
380 plugin->get_input()->get_w() : plugin->get_input()->get_h();
381 // Prepare for oversampling
383 val_p = new pixel_f[size];
384 val_m = new pixel_f[size];
385 radius = new double[size];
386 src = new pixel_f[size];
387 dst = new pixel_f[size];
394 BlurEngine::~BlurEngine()
406 void BlurEngine::set_range(int start_y,
411 this->start_y = start_y;
413 this->start_x = start_x;
417 int BlurEngine::start_process_frame(VFrame *frame)
424 int BlurEngine::wait_process_frame()
430 void BlurEngine::run()
440 output_lock.unlock();
448 color_model = frame->get_color_model();
449 int w = frame->get_w();
450 int h = frame->get_h();
451 // Force recalculation of filter
452 prev_forward_radius = -65536;
453 prev_reverse_radius = -65536;
458 #define BLUR(type, max, components) \
460 type **input_rows = (type **)frame->get_rows(); \
461 type **output_rows = (type **)frame->get_rows(); \
462 type **current_input = input_rows; \
463 type **current_output = output_rows; \
466 if(!do_horizontal && plugin->config.vertical) \
468 /* Vertical pass */ \
469 /* Render to temp if a horizontal pass comes next */ \
470 /* if(plugin->config.horizontal) */ \
472 /* current_output = (type **)plugin->get_temp()->get_rows(); */ \
475 for(j = start_x; j < end_x; j++) \
477 bzero(val_p, sizeof(pixel_f) * h); \
478 bzero(val_m, sizeof(pixel_f) * h); \
480 for(k = 0; k < h; k++) \
482 if(plugin->config.r) src[k].r = (double)current_input[k][j * components]; \
483 if(plugin->config.g) src[k].g = (double)current_input[k][j * components + 1]; \
484 if(plugin->config.b) src[k].b = (double)current_input[k][j * components + 2]; \
485 if(components == 4) \
487 if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[k][j * components + 3]; \
491 if(components == 4) \
496 for(k = 0; k < h; k++) \
498 if(plugin->config.r) current_output[k][j * components] = (type)dst[k].r; \
499 if(plugin->config.g) current_output[k][j * components + 1] = (type)dst[k].g; \
500 if(plugin->config.b) current_output[k][j * components + 2] = (type)dst[k].b; \
501 if(components == 4) \
502 if(plugin->config.a || plugin->config.a_key) current_output[k][j * components + 3] = (type)dst[k].a; \
506 /* current_input = current_output; */ \
507 /* current_output = output_rows; */ \
511 if(do_horizontal && plugin->config.horizontal) \
513 /* Vertical pass */ \
514 /* if(plugin->config.vertical) */ \
516 /* current_input = (type **)plugin->get_temp()->get_rows(); */ \
519 /* Horizontal pass */ \
520 for(j = start_y; j < end_y; j++) \
522 bzero(val_p, sizeof(pixel_f) * w); \
523 bzero(val_m, sizeof(pixel_f) * w); \
525 for(k = 0; k < w; k++) \
527 if(plugin->config.r) src[k].r = (double)current_input[j][k * components]; \
528 if(plugin->config.g) src[k].g = (double)current_input[j][k * components + 1]; \
529 if(plugin->config.b) src[k].b = (double)current_input[j][k * components + 2]; \
530 if(components == 4) \
531 if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[j][k * components + 3]; \
534 if(components == 4) \
539 for(k = 0; k < w; k++) \
541 if(plugin->config.r) current_output[j][k * components] = (type)dst[k].r; \
542 if(plugin->config.g) current_output[j][k * components + 1] = (type)dst[k].g; \
543 if(plugin->config.b) current_output[j][k * components + 2] = (type)dst[k].b; \
544 if(components == 4) \
546 if(plugin->config.a && !plugin->config.a_key) \
547 current_output[j][k * components + 3] = (type)dst[k].a; \
548 else if(plugin->config.a_key) \
549 current_output[j][k * components + 3] = max; \
562 BLUR(unsigned char, 0xff, 3);
571 BLUR(unsigned char, 0xff, 4);
580 BLUR(uint16_t, 0xffff, 3);
583 case BC_RGBA16161616:
584 case BC_YUVA16161616:
585 BLUR(uint16_t, 0xffff, 4);
589 output_lock.unlock();
593 int BlurEngine::reconfigure(BlurConstants *constants, double radius)
595 // Blurring an oversampled temp
596 if(plugin->config.a_key) radius *= 2;
597 double std_dev = sqrt(-(double)(radius * radius) /
598 (2 * log (1.0 / 255.0)));
599 get_constants(constants, std_dev);
603 int BlurEngine::get_constants(BlurConstants *ptr, double std_dev)
609 div = sqrt(2 * M_PI) * std_dev;
610 constants[0] = -1.783 / std_dev;
611 constants[1] = -1.723 / std_dev;
612 constants[2] = 0.6318 / std_dev;
613 constants[3] = 1.997 / std_dev;
614 constants[4] = 1.6803 / div;
615 constants[5] = 3.735 / div;
616 constants[6] = -0.6803 / div;
617 constants[7] = -0.2598 / div;
619 ptr->n_p[0] = constants[4] + constants[6];
620 ptr->n_p[1] = exp(constants[1]) *
621 (constants[7] * sin(constants[3]) -
622 (constants[6] + 2 * constants[4]) * cos(constants[3])) +
624 (constants[5] * sin(constants[2]) -
625 (2 * constants[6] + constants[4]) * cos(constants[2]));
627 ptr->n_p[2] = 2 * exp(constants[0] + constants[1]) *
628 ((constants[4] + constants[6]) * cos(constants[3]) *
629 cos(constants[2]) - constants[5] *
630 cos(constants[3]) * sin(constants[2]) -
631 constants[7] * cos(constants[2]) * sin(constants[3])) +
632 constants[6] * exp(2 * constants[0]) +
633 constants[4] * exp(2 * constants[1]);
635 ptr->n_p[3] = exp(constants[1] + 2 * constants[0]) *
636 (constants[7] * sin(constants[3]) -
637 constants[6] * cos(constants[3])) +
638 exp(constants[0] + 2 * constants[1]) *
639 (constants[5] * sin(constants[2]) - constants[4] *
644 ptr->d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
645 2 * exp(constants[0]) * cos(constants[2]);
647 ptr->d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) *
648 exp(constants[0] + constants[1]) +
649 exp(2 * constants[1]) + exp (2 * constants[0]);
651 ptr->d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
652 2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
654 ptr->d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
656 for(i = 0; i < 5; i++) ptr->d_m[i] = ptr->d_p[i];
659 for(i = 1; i <= 4; i++)
660 ptr->n_m[i] = ptr->n_p[i] - ptr->d_p[i] * ptr->n_p[0];
662 double sum_n_p, sum_n_m, sum_d;
668 for(i = 0; i < 5; i++)
670 sum_n_p += ptr->n_p[i];
671 sum_n_m += ptr->n_m[i];
672 sum_d += ptr->d_p[i];
675 a = sum_n_p / (1 + sum_d);
676 b = sum_n_m / (1 + sum_d);
678 for (i = 0; i < 5; i++)
680 ptr->bd_p[i] = ptr->d_p[i] * a;
681 ptr->bd_m[i] = ptr->d_m[i] * b;
687 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
689 int BlurEngine::transfer_pixels(pixel_f *src1,
699 // printf("BlurEngine::transfer_pixels %d %d %d %d\n",
703 // plugin->config.a);
705 for(i = 0; i < size; i++)
707 sum = src1[i].r + src2[i].r;
711 sum = src1[i].g + src2[i].g;
715 sum = src1[i].b + src2[i].b;
719 sum = src1[i].a + src2[i].a;
725 // double scale = 2.0 - (radius[i] * radius[i] - 2.0);
726 // dest[i].r /= scale;
727 // dest[i].g /= scale;
728 // dest[i].b /= scale;
729 // dest[i].a /= scale;
736 int BlurEngine::multiply_alpha(pixel_f *row, int size)
741 // for(i = 0; i < size; i++)
743 // alpha = (double)row[i].a / vmax;
744 // row[i].r *= alpha;
745 // row[i].g *= alpha;
746 // row[i].b *= alpha;
751 int BlurEngine::separate_alpha(pixel_f *row, int size)
757 // for(i = 0; i < size; i++)
759 // if(row[i].a > 0 && row[i].a < vmax)
761 // alpha = (double)row[i].a / vmax;
762 // result = (double)row[i].r / alpha;
763 // row[i].r = (result > vmax ? vmax : result);
764 // result = (double)row[i].g / alpha;
765 // row[i].g = (result > vmax ? vmax : result);
766 // result = (double)row[i].b / alpha;
767 // row[i].b = (result > vmax ? vmax : result);
773 int BlurEngine::blur_strip3(int &size)
775 // multiply_alpha(src, size);
778 pixel_f *sp_m = src + size - 1;
780 pixel_f *vm = val_m + size - 1;
786 for(int k = 0; k < size; k++)
788 terms = (k < 4) ? k : 4;
790 radius[k] = plugin->config.radius;
792 for(l = 0; l <= terms; l++)
796 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
797 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
801 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
802 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
806 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
807 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
815 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
816 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
820 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
821 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
825 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
826 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
835 transfer_pixels(val_p, val_m, src, radius, dst, size);
836 // separate_alpha(dst, size);
841 int BlurEngine::blur_strip4(int &size)
844 // multiply_alpha(src, size);
847 pixel_f *sp_m = src + size - 1;
849 pixel_f *vm = val_m + size - 1;
855 for(int k = 0; k < size; k++)
857 if(plugin->config.a_key)
858 radius[k] = (double)plugin->config.radius * src[k].a / vmax;
860 radius[k] = plugin->config.radius;
863 for(int k = 0; k < size; k++)
865 terms = (k < 4) ? k : 4;
867 if(plugin->config.a_key)
869 if(radius[k] != prev_forward_radius)
871 prev_forward_radius = radius[k];
872 if(radius[k] >= MIN_RADIUS / 2)
874 reconfigure(&forward_constants, radius[k]);
878 reconfigure(&forward_constants, (double)MIN_RADIUS / 2);
882 if(radius[size - 1 - k] != prev_reverse_radius)
884 //printf("BlurEngine::blur_strip4 %f\n", sp_m->a);
885 prev_reverse_radius = radius[size - 1 - k];
886 if(radius[size - 1 - k] >= MIN_RADIUS / 2)
888 reconfigure(&reverse_constants, radius[size - 1 - k]);
892 reconfigure(&reverse_constants, (double)MIN_RADIUS / 2);
896 // Force alpha to be copied regardless of alpha blur enabled
902 for(l = 0; l <= terms; l++)
906 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
907 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
911 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
912 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
916 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
917 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
919 if(plugin->config.a && !plugin->config.a_key)
921 vp->a += forward_constants.n_p[l] * sp_p[-l].a - forward_constants.d_p[l] * vp[-l].a;
922 vm->a += reverse_constants.n_m[l] * sp_m[l].a - reverse_constants.d_m[l] * vm[l].a;
930 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
931 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
935 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
936 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
940 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
941 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
943 if(plugin->config.a && !plugin->config.a_key)
945 vp->a += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.a;
946 vm->a += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.a;
956 transfer_pixels(val_p, val_m, src, radius, dst, size);
957 // separate_alpha(dst, size);