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()
50 int BlurConfig::equivalent(BlurConfig &that)
52 return (vertical == that.vertical &&
53 horizontal == that.horizontal &&
54 radius == that.radius &&
55 a_key == that.a_key &&
62 void BlurConfig::copy_from(BlurConfig &that)
64 vertical = that.vertical;
65 horizontal = that.horizontal;
74 void BlurConfig::interpolate(BlurConfig &prev,
78 int64_t current_frame)
80 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
81 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
84 //printf("BlurConfig::interpolate %d %d %d\n", prev_frame, next_frame, current_frame);
85 this->vertical = prev.vertical;
86 this->horizontal = prev.horizontal;
87 this->radius = round(prev.radius * prev_scale + next.radius * next_scale);
100 REGISTER_PLUGIN(BlurMain)
109 BlurMain::BlurMain(PluginServer *server)
110 : PluginVClient(server)
112 need_reconfigure = 1;
117 BlurMain::~BlurMain()
119 //printf("BlurMain::~BlurMain 1\n");
123 for(int i = 0; i < (get_project_smp() + 1); i++)
128 if(overlayer) delete overlayer;
131 const char* BlurMain::plugin_title() { return _("Blur"); }
132 int BlurMain::is_realtime() { return 1; }
135 NEW_WINDOW_MACRO(BlurMain, BlurWindow)
137 LOAD_CONFIGURATION_MACRO(BlurMain, BlurConfig)
142 int BlurMain::process_buffer(VFrame *frame,
143 int64_t start_position,
148 need_reconfigure |= load_configuration();
156 // Create temp based on alpha keying.
157 // Alpha keying needs 2x oversampling.
161 PluginVClient::new_temp(frame->get_w() * 2,
163 frame->get_color_model());
166 overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
169 overlayer->overlay(PluginVClient::get_temp(),
177 PluginVClient::get_temp()->get_w(),
178 PluginVClient::get_temp()->get_h(),
182 input_frame = PluginVClient::get_temp();
186 PluginVClient::new_temp(frame->get_w(),
188 frame->get_color_model());
193 //printf("BlurMain::process_realtime 1 %d %d\n", need_reconfigure, config.radius);
198 engine = new BlurEngine*[(get_project_smp() + 1)];
199 for(i = 0; i < (get_project_smp() + 1); i++)
201 engine[i] = new BlurEngine(this);
206 for(i = 0; i < (get_project_smp() + 1); i++)
208 engine[i]->reconfigure(&engine[i]->forward_constants, config.radius);
209 engine[i]->reconfigure(&engine[i]->reverse_constants, config.radius);
211 need_reconfigure = 0;
215 if(config.radius < MIN_RADIUS ||
216 (!config.vertical && !config.horizontal))
218 // Data never processed
223 // Need to blur vertically to a temp and
224 // horizontally to the output in 2 discrete passes.
226 for(i = 0; i < get_project_smp() + 1; i++)
228 engine[i]->set_range(
229 input_frame->get_h() * i / (get_project_smp() + 1),
230 input_frame->get_h() * (i + 1) / (get_project_smp() + 1),
231 input_frame->get_w() * i / (get_project_smp() + 1),
232 input_frame->get_w() * (i + 1) / (get_project_smp() + 1));
235 for(i = 0; i < (get_project_smp() + 1); i++)
237 engine[i]->do_horizontal = 0;
238 engine[i]->start_process_frame(input_frame);
241 for(i = 0; i < (get_project_smp() + 1); i++)
243 engine[i]->wait_process_frame();
246 for(i = 0; i < (get_project_smp() + 1); i++)
248 engine[i]->do_horizontal = 1;
249 engine[i]->start_process_frame(input_frame);
252 for(i = 0; i < (get_project_smp() + 1); i++)
254 engine[i]->wait_process_frame();
262 overlayer->overlay(frame,
263 PluginVClient::get_temp(),
266 PluginVClient::get_temp()->get_w(),
267 PluginVClient::get_temp()->get_h(),
281 void BlurMain::update_gui()
285 int reconfigure = load_configuration();
288 ((BlurWindow*)thread->window)->lock_window("BlurMain::update_gui");
289 ((BlurWindow*)thread->window)->horizontal->update(config.horizontal);
290 ((BlurWindow*)thread->window)->vertical->update(config.vertical);
291 ((BlurWindow*)thread->window)->radius->update(config.radius);
292 ((BlurWindow*)thread->window)->radius_text->update((int64_t)config.radius);
293 ((BlurWindow*)thread->window)->a_key->update(config.a_key);
294 ((BlurWindow*)thread->window)->a->update(config.a);
295 ((BlurWindow*)thread->window)->r->update(config.r);
296 ((BlurWindow*)thread->window)->g->update(config.g);
297 ((BlurWindow*)thread->window)->b->update(config.b);
298 ((BlurWindow*)thread->window)->unlock_window();
307 void BlurMain::save_data(KeyFrame *keyframe)
311 // cause data to be stored directly in text
312 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
313 output.tag.set_title("BLUR");
314 output.tag.set_property("VERTICAL", config.vertical);
315 output.tag.set_property("HORIZONTAL", config.horizontal);
316 output.tag.set_property("RADIUS", config.radius);
317 output.tag.set_property("R", config.r);
318 output.tag.set_property("G", config.g);
319 output.tag.set_property("B", config.b);
320 output.tag.set_property("A", config.a);
321 output.tag.set_property("A_KEY", config.a_key);
323 output.tag.set_title("/BLUR");
325 output.append_newline();
326 output.terminate_string();
329 void BlurMain::read_data(KeyFrame *keyframe)
333 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
339 result = input.read_tag();
343 if(input.tag.title_is("BLUR"))
345 config.vertical = input.tag.get_property("VERTICAL", config.vertical);
346 config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
347 config.radius = input.tag.get_property("RADIUS", config.radius);
348 //printf("BlurMain::read_data 1 %d %d %s\n", get_source_position(), keyframe->position, keyframe->get_data());
349 config.r = input.tag.get_property("R", config.r);
350 config.g = input.tag.get_property("G", config.g);
351 config.b = input.tag.get_property("B", config.b);
352 config.a = input.tag.get_property("A", config.a);
353 config.a_key = input.tag.get_property("A_KEY", config.a_key);
367 BlurEngine::BlurEngine(BlurMain *plugin)
370 this->plugin = plugin;
374 int size = plugin->get_input()->get_w() > plugin->get_input()->get_h() ?
375 plugin->get_input()->get_w() : plugin->get_input()->get_h();
376 // Prepare for oversampling
378 val_p = new pixel_f[size];
379 val_m = new pixel_f[size];
380 radius = new double[size];
381 src = new pixel_f[size];
382 dst = new pixel_f[size];
389 BlurEngine::~BlurEngine()
401 void BlurEngine::set_range(int start_y,
406 this->start_y = start_y;
408 this->start_x = start_x;
412 int BlurEngine::start_process_frame(VFrame *frame)
419 int BlurEngine::wait_process_frame()
425 void BlurEngine::run()
435 output_lock.unlock();
443 color_model = frame->get_color_model();
444 int w = frame->get_w();
445 int h = frame->get_h();
446 // Force recalculation of filter
447 prev_forward_radius = -65536;
448 prev_reverse_radius = -65536;
453 #define BLUR(type, max, components) \
455 type **input_rows = (type **)frame->get_rows(); \
456 type **output_rows = (type **)frame->get_rows(); \
457 type **current_input = input_rows; \
458 type **current_output = output_rows; \
461 if(!do_horizontal && plugin->config.vertical) \
463 /* Vertical pass */ \
464 /* Render to temp if a horizontal pass comes next */ \
465 /* if(plugin->config.horizontal) */ \
467 /* current_output = (type **)plugin->get_temp()->get_rows(); */ \
470 for(j = start_x; j < end_x; j++) \
472 bzero(val_p, sizeof(pixel_f) * h); \
473 bzero(val_m, sizeof(pixel_f) * h); \
475 for(k = 0; k < h; k++) \
477 if(plugin->config.r) src[k].r = (double)current_input[k][j * components]; \
478 if(plugin->config.g) src[k].g = (double)current_input[k][j * components + 1]; \
479 if(plugin->config.b) src[k].b = (double)current_input[k][j * components + 2]; \
480 if(components == 4) \
482 if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[k][j * components + 3]; \
486 if(components == 4) \
491 for(k = 0; k < h; k++) \
493 if(plugin->config.r) current_output[k][j * components] = (type)dst[k].r; \
494 if(plugin->config.g) current_output[k][j * components + 1] = (type)dst[k].g; \
495 if(plugin->config.b) current_output[k][j * components + 2] = (type)dst[k].b; \
496 if(components == 4) \
497 if(plugin->config.a || plugin->config.a_key) current_output[k][j * components + 3] = (type)dst[k].a; \
501 /* current_input = current_output; */ \
502 /* current_output = output_rows; */ \
506 if(do_horizontal && plugin->config.horizontal) \
508 /* Vertical pass */ \
509 /* if(plugin->config.vertical) */ \
511 /* current_input = (type **)plugin->get_temp()->get_rows(); */ \
514 /* Horizontal pass */ \
515 for(j = start_y; j < end_y; j++) \
517 bzero(val_p, sizeof(pixel_f) * w); \
518 bzero(val_m, sizeof(pixel_f) * w); \
520 for(k = 0; k < w; k++) \
522 if(plugin->config.r) src[k].r = (double)current_input[j][k * components]; \
523 if(plugin->config.g) src[k].g = (double)current_input[j][k * components + 1]; \
524 if(plugin->config.b) src[k].b = (double)current_input[j][k * components + 2]; \
525 if(components == 4) \
526 if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[j][k * components + 3]; \
529 if(components == 4) \
534 for(k = 0; k < w; k++) \
536 if(plugin->config.r) current_output[j][k * components] = (type)dst[k].r; \
537 if(plugin->config.g) current_output[j][k * components + 1] = (type)dst[k].g; \
538 if(plugin->config.b) current_output[j][k * components + 2] = (type)dst[k].b; \
539 if(components == 4) \
541 if(plugin->config.a && !plugin->config.a_key) \
542 current_output[j][k * components + 3] = (type)dst[k].a; \
543 else if(plugin->config.a_key) \
544 current_output[j][k * components + 3] = max; \
557 BLUR(unsigned char, 0xff, 3);
566 BLUR(unsigned char, 0xff, 4);
575 BLUR(uint16_t, 0xffff, 3);
578 case BC_RGBA16161616:
579 case BC_YUVA16161616:
580 BLUR(uint16_t, 0xffff, 4);
584 output_lock.unlock();
588 int BlurEngine::reconfigure(BlurConstants *constants, double radius)
590 // Blurring an oversampled temp
591 if(plugin->config.a_key) radius *= 2;
592 double std_dev = sqrt(-(double)(radius * radius) /
593 (2 * log (1.0 / 255.0)));
594 get_constants(constants, std_dev);
598 int BlurEngine::get_constants(BlurConstants *ptr, double std_dev)
604 div = sqrt(2 * M_PI) * std_dev;
605 constants[0] = -1.783 / std_dev;
606 constants[1] = -1.723 / std_dev;
607 constants[2] = 0.6318 / std_dev;
608 constants[3] = 1.997 / std_dev;
609 constants[4] = 1.6803 / div;
610 constants[5] = 3.735 / div;
611 constants[6] = -0.6803 / div;
612 constants[7] = -0.2598 / div;
614 ptr->n_p[0] = constants[4] + constants[6];
615 ptr->n_p[1] = exp(constants[1]) *
616 (constants[7] * sin(constants[3]) -
617 (constants[6] + 2 * constants[4]) * cos(constants[3])) +
619 (constants[5] * sin(constants[2]) -
620 (2 * constants[6] + constants[4]) * cos(constants[2]));
622 ptr->n_p[2] = 2 * exp(constants[0] + constants[1]) *
623 ((constants[4] + constants[6]) * cos(constants[3]) *
624 cos(constants[2]) - constants[5] *
625 cos(constants[3]) * sin(constants[2]) -
626 constants[7] * cos(constants[2]) * sin(constants[3])) +
627 constants[6] * exp(2 * constants[0]) +
628 constants[4] * exp(2 * constants[1]);
630 ptr->n_p[3] = exp(constants[1] + 2 * constants[0]) *
631 (constants[7] * sin(constants[3]) -
632 constants[6] * cos(constants[3])) +
633 exp(constants[0] + 2 * constants[1]) *
634 (constants[5] * sin(constants[2]) - constants[4] *
639 ptr->d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
640 2 * exp(constants[0]) * cos(constants[2]);
642 ptr->d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) *
643 exp(constants[0] + constants[1]) +
644 exp(2 * constants[1]) + exp (2 * constants[0]);
646 ptr->d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
647 2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
649 ptr->d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
651 for(i = 0; i < 5; i++) ptr->d_m[i] = ptr->d_p[i];
654 for(i = 1; i <= 4; i++)
655 ptr->n_m[i] = ptr->n_p[i] - ptr->d_p[i] * ptr->n_p[0];
657 double sum_n_p, sum_n_m, sum_d;
663 for(i = 0; i < 5; i++)
665 sum_n_p += ptr->n_p[i];
666 sum_n_m += ptr->n_m[i];
667 sum_d += ptr->d_p[i];
670 a = sum_n_p / (1 + sum_d);
671 b = sum_n_m / (1 + sum_d);
673 for (i = 0; i < 5; i++)
675 ptr->bd_p[i] = ptr->d_p[i] * a;
676 ptr->bd_m[i] = ptr->d_m[i] * b;
682 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
684 int BlurEngine::transfer_pixels(pixel_f *src1,
694 // printf("BlurEngine::transfer_pixels %d %d %d %d\n",
698 // plugin->config.a);
700 for(i = 0; i < size; i++)
702 sum = src1[i].r + src2[i].r;
706 sum = src1[i].g + src2[i].g;
710 sum = src1[i].b + src2[i].b;
714 sum = src1[i].a + src2[i].a;
720 // double scale = 2.0 - (radius[i] * radius[i] - 2.0);
721 // dest[i].r /= scale;
722 // dest[i].g /= scale;
723 // dest[i].b /= scale;
724 // dest[i].a /= scale;
731 int BlurEngine::multiply_alpha(pixel_f *row, int size)
734 // register double alpha;
736 // for(i = 0; i < size; i++)
738 // alpha = (double)row[i].a / vmax;
739 // row[i].r *= alpha;
740 // row[i].g *= alpha;
741 // row[i].b *= alpha;
746 int BlurEngine::separate_alpha(pixel_f *row, int size)
749 // register double alpha;
750 // register double result;
752 // for(i = 0; i < size; i++)
754 // if(row[i].a > 0 && row[i].a < vmax)
756 // alpha = (double)row[i].a / vmax;
757 // result = (double)row[i].r / alpha;
758 // row[i].r = (result > vmax ? vmax : result);
759 // result = (double)row[i].g / alpha;
760 // row[i].g = (result > vmax ? vmax : result);
761 // result = (double)row[i].b / alpha;
762 // row[i].b = (result > vmax ? vmax : result);
768 int BlurEngine::blur_strip3(int &size)
770 // multiply_alpha(src, size);
773 pixel_f *sp_m = src + size - 1;
775 pixel_f *vm = val_m + size - 1;
781 for(int k = 0; k < size; k++)
783 terms = (k < 4) ? k : 4;
785 radius[k] = plugin->config.radius;
787 for(l = 0; l <= terms; l++)
791 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
792 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
796 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
797 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
801 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
802 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
810 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
811 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
815 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
816 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
820 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
821 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
830 transfer_pixels(val_p, val_m, src, radius, dst, size);
831 // separate_alpha(dst, size);
836 int BlurEngine::blur_strip4(int &size)
839 // multiply_alpha(src, size);
842 pixel_f *sp_m = src + size - 1;
844 pixel_f *vm = val_m + size - 1;
850 for(int k = 0; k < size; k++)
852 if(plugin->config.a_key)
853 radius[k] = (double)plugin->config.radius * src[k].a / vmax;
855 radius[k] = plugin->config.radius;
858 for(int k = 0; k < size; k++)
860 terms = (k < 4) ? k : 4;
862 if(plugin->config.a_key)
864 if(radius[k] != prev_forward_radius)
866 prev_forward_radius = radius[k];
867 if(radius[k] >= MIN_RADIUS / 2)
869 reconfigure(&forward_constants, radius[k]);
873 reconfigure(&forward_constants, (double)MIN_RADIUS / 2);
877 if(radius[size - 1 - k] != prev_reverse_radius)
879 //printf("BlurEngine::blur_strip4 %f\n", sp_m->a);
880 prev_reverse_radius = radius[size - 1 - k];
881 if(radius[size - 1 - k] >= MIN_RADIUS / 2)
883 reconfigure(&reverse_constants, radius[size - 1 - k]);
887 reconfigure(&reverse_constants, (double)MIN_RADIUS / 2);
891 // Force alpha to be copied regardless of alpha blur enabled
897 for(l = 0; l <= terms; l++)
901 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
902 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
906 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
907 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
911 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
912 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
914 if(plugin->config.a && !plugin->config.a_key)
916 vp->a += forward_constants.n_p[l] * sp_p[-l].a - forward_constants.d_p[l] * vp[-l].a;
917 vm->a += reverse_constants.n_m[l] * sp_m[l].a - reverse_constants.d_m[l] * vm[l].a;
925 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
926 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
930 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
931 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
935 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
936 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
938 if(plugin->config.a && !plugin->config.a_key)
940 vp->a += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.a;
941 vm->a += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.a;
951 transfer_pixels(val_p, val_m, src, radius, dst, size);
952 // separate_alpha(dst, size);