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.terminate_string();
326 void BlurMain::read_data(KeyFrame *keyframe)
330 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
336 result = input.read_tag();
340 if(input.tag.title_is("BLUR"))
342 config.vertical = input.tag.get_property("VERTICAL", config.vertical);
343 config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
344 config.radius = input.tag.get_property("RADIUS", config.radius);
345 //printf("BlurMain::read_data 1 %d %d %s\n", get_source_position(), keyframe->position, keyframe->get_data());
346 config.r = input.tag.get_property("R", config.r);
347 config.g = input.tag.get_property("G", config.g);
348 config.b = input.tag.get_property("B", config.b);
349 config.a = input.tag.get_property("A", config.a);
350 config.a_key = input.tag.get_property("A_KEY", config.a_key);
364 BlurEngine::BlurEngine(BlurMain *plugin)
367 this->plugin = plugin;
371 int size = plugin->get_input()->get_w() > plugin->get_input()->get_h() ?
372 plugin->get_input()->get_w() : plugin->get_input()->get_h();
373 // Prepare for oversampling
375 val_p = new pixel_f[size];
376 val_m = new pixel_f[size];
377 radius = new double[size];
378 src = new pixel_f[size];
379 dst = new pixel_f[size];
386 BlurEngine::~BlurEngine()
398 void BlurEngine::set_range(int start_y,
403 this->start_y = start_y;
405 this->start_x = start_x;
409 int BlurEngine::start_process_frame(VFrame *frame)
416 int BlurEngine::wait_process_frame()
422 void BlurEngine::run()
432 output_lock.unlock();
440 color_model = frame->get_color_model();
441 int w = frame->get_w();
442 int h = frame->get_h();
443 // Force recalculation of filter
444 prev_forward_radius = -65536;
445 prev_reverse_radius = -65536;
450 #define BLUR(type, max, components) \
452 type **input_rows = (type **)frame->get_rows(); \
453 type **output_rows = (type **)frame->get_rows(); \
454 type **current_input = input_rows; \
455 type **current_output = output_rows; \
458 if(!do_horizontal && plugin->config.vertical) \
460 /* Vertical pass */ \
461 /* Render to temp if a horizontal pass comes next */ \
462 /* if(plugin->config.horizontal) */ \
464 /* current_output = (type **)plugin->get_temp()->get_rows(); */ \
467 for(j = start_x; j < end_x; j++) \
469 bzero(val_p, sizeof(pixel_f) * h); \
470 bzero(val_m, sizeof(pixel_f) * h); \
472 for(k = 0; k < h; k++) \
474 if(plugin->config.r) src[k].r = (double)current_input[k][j * components]; \
475 if(plugin->config.g) src[k].g = (double)current_input[k][j * components + 1]; \
476 if(plugin->config.b) src[k].b = (double)current_input[k][j * components + 2]; \
477 if(components == 4) \
479 if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[k][j * components + 3]; \
483 if(components == 4) \
488 for(k = 0; k < h; k++) \
490 if(plugin->config.r) current_output[k][j * components] = (type)dst[k].r; \
491 if(plugin->config.g) current_output[k][j * components + 1] = (type)dst[k].g; \
492 if(plugin->config.b) current_output[k][j * components + 2] = (type)dst[k].b; \
493 if(components == 4) \
494 if(plugin->config.a || plugin->config.a_key) current_output[k][j * components + 3] = (type)dst[k].a; \
498 /* current_input = current_output; */ \
499 /* current_output = output_rows; */ \
503 if(do_horizontal && plugin->config.horizontal) \
505 /* Vertical pass */ \
506 /* if(plugin->config.vertical) */ \
508 /* current_input = (type **)plugin->get_temp()->get_rows(); */ \
511 /* Horizontal pass */ \
512 for(j = start_y; j < end_y; j++) \
514 bzero(val_p, sizeof(pixel_f) * w); \
515 bzero(val_m, sizeof(pixel_f) * w); \
517 for(k = 0; k < w; k++) \
519 if(plugin->config.r) src[k].r = (double)current_input[j][k * components]; \
520 if(plugin->config.g) src[k].g = (double)current_input[j][k * components + 1]; \
521 if(plugin->config.b) src[k].b = (double)current_input[j][k * components + 2]; \
522 if(components == 4) \
523 if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[j][k * components + 3]; \
526 if(components == 4) \
531 for(k = 0; k < w; k++) \
533 if(plugin->config.r) current_output[j][k * components] = (type)dst[k].r; \
534 if(plugin->config.g) current_output[j][k * components + 1] = (type)dst[k].g; \
535 if(plugin->config.b) current_output[j][k * components + 2] = (type)dst[k].b; \
536 if(components == 4) \
538 if(plugin->config.a && !plugin->config.a_key) \
539 current_output[j][k * components + 3] = (type)dst[k].a; \
540 else if(plugin->config.a_key) \
541 current_output[j][k * components + 3] = max; \
554 BLUR(unsigned char, 0xff, 3);
563 BLUR(unsigned char, 0xff, 4);
572 BLUR(uint16_t, 0xffff, 3);
575 case BC_RGBA16161616:
576 case BC_YUVA16161616:
577 BLUR(uint16_t, 0xffff, 4);
581 output_lock.unlock();
585 int BlurEngine::reconfigure(BlurConstants *constants, double radius)
587 // Blurring an oversampled temp
588 if(plugin->config.a_key) radius *= 2;
589 double std_dev = sqrt(-(double)(radius * radius) /
590 (2 * log (1.0 / 255.0)));
591 get_constants(constants, std_dev);
595 int BlurEngine::get_constants(BlurConstants *ptr, double std_dev)
601 div = sqrt(2 * M_PI) * std_dev;
602 constants[0] = -1.783 / std_dev;
603 constants[1] = -1.723 / std_dev;
604 constants[2] = 0.6318 / std_dev;
605 constants[3] = 1.997 / std_dev;
606 constants[4] = 1.6803 / div;
607 constants[5] = 3.735 / div;
608 constants[6] = -0.6803 / div;
609 constants[7] = -0.2598 / div;
611 ptr->n_p[0] = constants[4] + constants[6];
612 ptr->n_p[1] = exp(constants[1]) *
613 (constants[7] * sin(constants[3]) -
614 (constants[6] + 2 * constants[4]) * cos(constants[3])) +
616 (constants[5] * sin(constants[2]) -
617 (2 * constants[6] + constants[4]) * cos(constants[2]));
619 ptr->n_p[2] = 2 * exp(constants[0] + constants[1]) *
620 ((constants[4] + constants[6]) * cos(constants[3]) *
621 cos(constants[2]) - constants[5] *
622 cos(constants[3]) * sin(constants[2]) -
623 constants[7] * cos(constants[2]) * sin(constants[3])) +
624 constants[6] * exp(2 * constants[0]) +
625 constants[4] * exp(2 * constants[1]);
627 ptr->n_p[3] = exp(constants[1] + 2 * constants[0]) *
628 (constants[7] * sin(constants[3]) -
629 constants[6] * cos(constants[3])) +
630 exp(constants[0] + 2 * constants[1]) *
631 (constants[5] * sin(constants[2]) - constants[4] *
636 ptr->d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
637 2 * exp(constants[0]) * cos(constants[2]);
639 ptr->d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) *
640 exp(constants[0] + constants[1]) +
641 exp(2 * constants[1]) + exp (2 * constants[0]);
643 ptr->d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
644 2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
646 ptr->d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
648 for(i = 0; i < 5; i++) ptr->d_m[i] = ptr->d_p[i];
651 for(i = 1; i <= 4; i++)
652 ptr->n_m[i] = ptr->n_p[i] - ptr->d_p[i] * ptr->n_p[0];
654 double sum_n_p, sum_n_m, sum_d;
660 for(i = 0; i < 5; i++)
662 sum_n_p += ptr->n_p[i];
663 sum_n_m += ptr->n_m[i];
664 sum_d += ptr->d_p[i];
667 a = sum_n_p / (1 + sum_d);
668 b = sum_n_m / (1 + sum_d);
670 for (i = 0; i < 5; i++)
672 ptr->bd_p[i] = ptr->d_p[i] * a;
673 ptr->bd_m[i] = ptr->d_m[i] * b;
679 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
681 int BlurEngine::transfer_pixels(pixel_f *src1,
691 // printf("BlurEngine::transfer_pixels %d %d %d %d\n",
695 // plugin->config.a);
697 for(i = 0; i < size; i++)
699 sum = src1[i].r + src2[i].r;
703 sum = src1[i].g + src2[i].g;
707 sum = src1[i].b + src2[i].b;
711 sum = src1[i].a + src2[i].a;
717 // double scale = 2.0 - (radius[i] * radius[i] - 2.0);
718 // dest[i].r /= scale;
719 // dest[i].g /= scale;
720 // dest[i].b /= scale;
721 // dest[i].a /= scale;
728 int BlurEngine::multiply_alpha(pixel_f *row, int size)
731 // register double alpha;
733 // for(i = 0; i < size; i++)
735 // alpha = (double)row[i].a / vmax;
736 // row[i].r *= alpha;
737 // row[i].g *= alpha;
738 // row[i].b *= alpha;
743 int BlurEngine::separate_alpha(pixel_f *row, int size)
746 // register double alpha;
747 // register double result;
749 // for(i = 0; i < size; i++)
751 // if(row[i].a > 0 && row[i].a < vmax)
753 // alpha = (double)row[i].a / vmax;
754 // result = (double)row[i].r / alpha;
755 // row[i].r = (result > vmax ? vmax : result);
756 // result = (double)row[i].g / alpha;
757 // row[i].g = (result > vmax ? vmax : result);
758 // result = (double)row[i].b / alpha;
759 // row[i].b = (result > vmax ? vmax : result);
765 int BlurEngine::blur_strip3(int &size)
767 // multiply_alpha(src, size);
770 pixel_f *sp_m = src + size - 1;
772 pixel_f *vm = val_m + size - 1;
778 for(int k = 0; k < size; k++)
780 terms = (k < 4) ? k : 4;
782 radius[k] = plugin->config.radius;
784 for(l = 0; l <= terms; l++)
788 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
789 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
793 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
794 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
798 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
799 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
807 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
808 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
812 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
813 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
817 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
818 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
827 transfer_pixels(val_p, val_m, src, radius, dst, size);
828 // separate_alpha(dst, size);
833 int BlurEngine::blur_strip4(int &size)
836 // multiply_alpha(src, size);
839 pixel_f *sp_m = src + size - 1;
841 pixel_f *vm = val_m + size - 1;
847 for(int k = 0; k < size; k++)
849 if(plugin->config.a_key)
850 radius[k] = (double)plugin->config.radius * src[k].a / vmax;
852 radius[k] = plugin->config.radius;
855 for(int k = 0; k < size; k++)
857 terms = (k < 4) ? k : 4;
859 if(plugin->config.a_key)
861 if(radius[k] != prev_forward_radius)
863 prev_forward_radius = radius[k];
864 if(radius[k] >= MIN_RADIUS / 2)
866 reconfigure(&forward_constants, radius[k]);
870 reconfigure(&forward_constants, (double)MIN_RADIUS / 2);
874 if(radius[size - 1 - k] != prev_reverse_radius)
876 //printf("BlurEngine::blur_strip4 %f\n", sp_m->a);
877 prev_reverse_radius = radius[size - 1 - k];
878 if(radius[size - 1 - k] >= MIN_RADIUS / 2)
880 reconfigure(&reverse_constants, radius[size - 1 - k]);
884 reconfigure(&reverse_constants, (double)MIN_RADIUS / 2);
888 // Force alpha to be copied regardless of alpha blur enabled
894 for(l = 0; l <= terms; l++)
898 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
899 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
903 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
904 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
908 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
909 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
911 if(plugin->config.a && !plugin->config.a_key)
913 vp->a += forward_constants.n_p[l] * sp_p[-l].a - forward_constants.d_p[l] * vp[-l].a;
914 vm->a += reverse_constants.n_m[l] * sp_m[l].a - reverse_constants.d_m[l] * vm[l].a;
922 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
923 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
927 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
928 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
932 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
933 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
935 if(plugin->config.a && !plugin->config.a_key)
937 vp->a += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.a;
938 vm->a += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.a;
948 transfer_pixels(val_p, val_m, src, radius, dst, size);
949 // separate_alpha(dst, size);