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(int clear)
63 int BlurConfig::equivalent(BlurConfig &that)
65 return (vertical == that.vertical &&
66 horizontal == that.horizontal &&
67 radius == that.radius &&
68 a_key == that.a_key &&
75 void BlurConfig::copy_from(BlurConfig &that)
77 vertical = that.vertical;
78 horizontal = that.horizontal;
87 void BlurConfig::interpolate(BlurConfig &prev,
91 int64_t current_frame)
93 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
94 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
97 //printf("BlurConfig::interpolate %d %d %d\n", prev_frame, next_frame, current_frame);
98 this->vertical = prev.vertical;
99 this->horizontal = prev.horizontal;
100 this->radius = round(prev.radius * prev_scale + next.radius * next_scale);
113 REGISTER_PLUGIN(BlurMain)
122 BlurMain::BlurMain(PluginServer *server)
123 : PluginVClient(server)
125 need_reconfigure = 1;
130 BlurMain::~BlurMain()
132 //printf("BlurMain::~BlurMain 1\n");
136 for(int i = 0; i < (get_project_smp() + 1); i++)
141 if(overlayer) delete overlayer;
144 const char* BlurMain::plugin_title() { return N_("Blur"); }
145 int BlurMain::is_realtime() { return 1; }
148 NEW_WINDOW_MACRO(BlurMain, BlurWindow)
150 LOAD_CONFIGURATION_MACRO(BlurMain, BlurConfig)
155 int BlurMain::process_buffer(VFrame *frame,
156 int64_t start_position,
161 need_reconfigure |= load_configuration();
169 // Create temp based on alpha keying.
170 // Alpha keying needs 2x oversampling.
174 PluginVClient::new_temp(frame->get_w() * 2,
176 frame->get_color_model());
179 overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
182 overlayer->overlay(PluginVClient::get_temp(),
190 PluginVClient::get_temp()->get_w(),
191 PluginVClient::get_temp()->get_h(),
195 input_frame = PluginVClient::get_temp();
199 PluginVClient::new_temp(frame->get_w(),
201 frame->get_color_model());
206 //printf("BlurMain::process_realtime 1 %d %d\n", need_reconfigure, config.radius);
211 engine = new BlurEngine*[(get_project_smp() + 1)];
212 for(i = 0; i < (get_project_smp() + 1); i++)
214 engine[i] = new BlurEngine(this);
219 for(i = 0; i < (get_project_smp() + 1); i++)
221 engine[i]->reconfigure(&engine[i]->forward_constants, config.radius);
222 engine[i]->reconfigure(&engine[i]->reverse_constants, config.radius);
224 need_reconfigure = 0;
228 if(config.radius < MIN_RADIUS ||
229 (!config.vertical && !config.horizontal))
231 // Data never processed
236 // Need to blur vertically to a temp and
237 // horizontally to the output in 2 discrete passes.
239 for(i = 0; i < get_project_smp() + 1; i++)
241 engine[i]->set_range(
242 input_frame->get_h() * i / (get_project_smp() + 1),
243 input_frame->get_h() * (i + 1) / (get_project_smp() + 1),
244 input_frame->get_w() * i / (get_project_smp() + 1),
245 input_frame->get_w() * (i + 1) / (get_project_smp() + 1));
248 for(i = 0; i < (get_project_smp() + 1); i++)
250 engine[i]->do_horizontal = 0;
251 engine[i]->start_process_frame(input_frame);
254 for(i = 0; i < (get_project_smp() + 1); i++)
256 engine[i]->wait_process_frame();
259 for(i = 0; i < (get_project_smp() + 1); i++)
261 engine[i]->do_horizontal = 1;
262 engine[i]->start_process_frame(input_frame);
265 for(i = 0; i < (get_project_smp() + 1); i++)
267 engine[i]->wait_process_frame();
275 overlayer->overlay(frame,
276 PluginVClient::get_temp(),
279 PluginVClient::get_temp()->get_w(),
280 PluginVClient::get_temp()->get_h(),
294 void BlurMain::update_gui()
298 int reconfigure = load_configuration();
301 ((BlurWindow*)thread->window)->lock_window("BlurMain::update_gui");
302 ((BlurWindow*)thread->window)->horizontal->update(config.horizontal);
303 ((BlurWindow*)thread->window)->vertical->update(config.vertical);
304 ((BlurWindow*)thread->window)->radius_slider->update(config.radius);
305 ((BlurWindow*)thread->window)->radius_text->update((int64_t)config.radius);
306 ((BlurWindow*)thread->window)->a_key->update(config.a_key);
307 ((BlurWindow*)thread->window)->a->update(config.a);
308 ((BlurWindow*)thread->window)->r->update(config.r);
309 ((BlurWindow*)thread->window)->g->update(config.g);
310 ((BlurWindow*)thread->window)->b->update(config.b);
311 ((BlurWindow*)thread->window)->unlock_window();
320 void BlurMain::save_data(KeyFrame *keyframe)
324 // cause data to be stored directly in text
325 output.set_shared_output(keyframe->xbuf);
326 output.tag.set_title("BLUR");
327 output.tag.set_property("VERTICAL", config.vertical);
328 output.tag.set_property("HORIZONTAL", config.horizontal);
329 output.tag.set_property("RADIUS", config.radius);
330 output.tag.set_property("R", config.r);
331 output.tag.set_property("G", config.g);
332 output.tag.set_property("B", config.b);
333 output.tag.set_property("A", config.a);
334 output.tag.set_property("A_KEY", config.a_key);
336 output.tag.set_title("/BLUR");
338 output.append_newline();
339 output.terminate_string();
342 void BlurMain::read_data(KeyFrame *keyframe)
346 input.set_shared_input(keyframe->xbuf);
352 result = input.read_tag();
356 if(input.tag.title_is("BLUR"))
358 config.vertical = input.tag.get_property("VERTICAL", config.vertical);
359 config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
360 config.radius = input.tag.get_property("RADIUS", config.radius);
361 //printf("BlurMain::read_data 1 %d %d %s\n", get_source_position(), keyframe->position, keyframe->get_data());
362 config.r = input.tag.get_property("R", config.r);
363 config.g = input.tag.get_property("G", config.g);
364 config.b = input.tag.get_property("B", config.b);
365 config.a = input.tag.get_property("A", config.a);
366 config.a_key = input.tag.get_property("A_KEY", config.a_key);
380 BlurEngine::BlurEngine(BlurMain *plugin)
383 this->plugin = plugin;
387 int size = plugin->get_input()->get_w() > plugin->get_input()->get_h() ?
388 plugin->get_input()->get_w() : plugin->get_input()->get_h();
389 // Prepare for oversampling
391 val_p = new pixel_f[size];
392 val_m = new pixel_f[size];
393 radius = new double[size];
394 src = new pixel_f[size];
395 dst = new pixel_f[size];
402 BlurEngine::~BlurEngine()
414 void BlurEngine::set_range(int start_y,
419 this->start_y = start_y;
421 this->start_x = start_x;
425 int BlurEngine::start_process_frame(VFrame *frame)
432 int BlurEngine::wait_process_frame()
438 void BlurEngine::run()
448 output_lock.unlock();
456 color_model = frame->get_color_model();
457 int w = frame->get_w();
458 int h = frame->get_h();
459 // Force recalculation of filter
460 prev_forward_radius = -65536;
461 prev_reverse_radius = -65536;
466 #define BLUR(type, max, components) \
468 type **input_rows = (type **)frame->get_rows(); \
469 type **output_rows = (type **)frame->get_rows(); \
470 type **current_input = input_rows; \
471 type **current_output = output_rows; \
474 if(!do_horizontal && plugin->config.vertical) \
476 /* Vertical pass */ \
477 /* Render to temp if a horizontal pass comes next */ \
478 /* if(plugin->config.horizontal) */ \
480 /* current_output = (type **)plugin->get_temp()->get_rows(); */ \
483 for(j = start_x; j < end_x; j++) \
485 bzero(val_p, sizeof(pixel_f) * h); \
486 bzero(val_m, sizeof(pixel_f) * h); \
488 for(k = 0; k < h; k++) \
490 if(plugin->config.r) src[k].r = (double)current_input[k][j * components]; \
491 if(plugin->config.g) src[k].g = (double)current_input[k][j * components + 1]; \
492 if(plugin->config.b) src[k].b = (double)current_input[k][j * components + 2]; \
493 if(components == 4) \
495 if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[k][j * components + 3]; \
499 if(components == 4) \
504 for(k = 0; k < h; k++) \
506 if(plugin->config.r) current_output[k][j * components] = (type)dst[k].r; \
507 if(plugin->config.g) current_output[k][j * components + 1] = (type)dst[k].g; \
508 if(plugin->config.b) current_output[k][j * components + 2] = (type)dst[k].b; \
509 if(components == 4) \
510 if(plugin->config.a || plugin->config.a_key) current_output[k][j * components + 3] = (type)dst[k].a; \
514 /* current_input = current_output; */ \
515 /* current_output = output_rows; */ \
519 if(do_horizontal && plugin->config.horizontal) \
521 /* Vertical pass */ \
522 /* if(plugin->config.vertical) */ \
524 /* current_input = (type **)plugin->get_temp()->get_rows(); */ \
527 /* Horizontal pass */ \
528 for(j = start_y; j < end_y; j++) \
530 bzero(val_p, sizeof(pixel_f) * w); \
531 bzero(val_m, sizeof(pixel_f) * w); \
533 for(k = 0; k < w; k++) \
535 if(plugin->config.r) src[k].r = (double)current_input[j][k * components]; \
536 if(plugin->config.g) src[k].g = (double)current_input[j][k * components + 1]; \
537 if(plugin->config.b) src[k].b = (double)current_input[j][k * components + 2]; \
538 if(components == 4) \
539 if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[j][k * components + 3]; \
542 if(components == 4) \
547 for(k = 0; k < w; k++) \
549 if(plugin->config.r) current_output[j][k * components] = (type)dst[k].r; \
550 if(plugin->config.g) current_output[j][k * components + 1] = (type)dst[k].g; \
551 if(plugin->config.b) current_output[j][k * components + 2] = (type)dst[k].b; \
552 if(components == 4) \
554 if(plugin->config.a && !plugin->config.a_key) \
555 current_output[j][k * components + 3] = (type)dst[k].a; \
556 else if(plugin->config.a_key) \
557 current_output[j][k * components + 3] = max; \
570 BLUR(unsigned char, 0xff, 3);
579 BLUR(unsigned char, 0xff, 4);
588 BLUR(uint16_t, 0xffff, 3);
591 case BC_RGBA16161616:
592 case BC_YUVA16161616:
593 BLUR(uint16_t, 0xffff, 4);
597 output_lock.unlock();
601 int BlurEngine::reconfigure(BlurConstants *constants, double radius)
603 // Blurring an oversampled temp
604 if(plugin->config.a_key) radius *= 2;
605 double std_dev = sqrt(-(double)(radius * radius) /
606 (2 * log (1.0 / 255.0)));
607 get_constants(constants, std_dev);
611 int BlurEngine::get_constants(BlurConstants *ptr, double std_dev)
617 div = sqrt(2 * M_PI) * std_dev;
618 constants[0] = -1.783 / std_dev;
619 constants[1] = -1.723 / std_dev;
620 constants[2] = 0.6318 / std_dev;
621 constants[3] = 1.997 / std_dev;
622 constants[4] = 1.6803 / div;
623 constants[5] = 3.735 / div;
624 constants[6] = -0.6803 / div;
625 constants[7] = -0.2598 / div;
627 ptr->n_p[0] = constants[4] + constants[6];
628 ptr->n_p[1] = exp(constants[1]) *
629 (constants[7] * sin(constants[3]) -
630 (constants[6] + 2 * constants[4]) * cos(constants[3])) +
632 (constants[5] * sin(constants[2]) -
633 (2 * constants[6] + constants[4]) * cos(constants[2]));
635 ptr->n_p[2] = 2 * exp(constants[0] + constants[1]) *
636 ((constants[4] + constants[6]) * cos(constants[3]) *
637 cos(constants[2]) - constants[5] *
638 cos(constants[3]) * sin(constants[2]) -
639 constants[7] * cos(constants[2]) * sin(constants[3])) +
640 constants[6] * exp(2 * constants[0]) +
641 constants[4] * exp(2 * constants[1]);
643 ptr->n_p[3] = exp(constants[1] + 2 * constants[0]) *
644 (constants[7] * sin(constants[3]) -
645 constants[6] * cos(constants[3])) +
646 exp(constants[0] + 2 * constants[1]) *
647 (constants[5] * sin(constants[2]) - constants[4] *
652 ptr->d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
653 2 * exp(constants[0]) * cos(constants[2]);
655 ptr->d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) *
656 exp(constants[0] + constants[1]) +
657 exp(2 * constants[1]) + exp (2 * constants[0]);
659 ptr->d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
660 2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
662 ptr->d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
664 for(i = 0; i < 5; i++) ptr->d_m[i] = ptr->d_p[i];
667 for(i = 1; i <= 4; i++)
668 ptr->n_m[i] = ptr->n_p[i] - ptr->d_p[i] * ptr->n_p[0];
670 double sum_n_p, sum_n_m, sum_d;
676 for(i = 0; i < 5; i++)
678 sum_n_p += ptr->n_p[i];
679 sum_n_m += ptr->n_m[i];
680 sum_d += ptr->d_p[i];
683 a = sum_n_p / (1 + sum_d);
684 b = sum_n_m / (1 + sum_d);
686 for (i = 0; i < 5; i++)
688 ptr->bd_p[i] = ptr->d_p[i] * a;
689 ptr->bd_m[i] = ptr->d_m[i] * b;
695 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
697 int BlurEngine::transfer_pixels(pixel_f *src1,
707 // printf("BlurEngine::transfer_pixels %d %d %d %d\n",
711 // plugin->config.a);
713 for(i = 0; i < size; i++)
715 sum = src1[i].r + src2[i].r;
719 sum = src1[i].g + src2[i].g;
723 sum = src1[i].b + src2[i].b;
727 sum = src1[i].a + src2[i].a;
733 // double scale = 2.0 - (radius[i] * radius[i] - 2.0);
734 // dest[i].r /= scale;
735 // dest[i].g /= scale;
736 // dest[i].b /= scale;
737 // dest[i].a /= scale;
744 int BlurEngine::multiply_alpha(pixel_f *row, int size)
749 // for(i = 0; i < size; i++)
751 // alpha = (double)row[i].a / vmax;
752 // row[i].r *= alpha;
753 // row[i].g *= alpha;
754 // row[i].b *= alpha;
759 int BlurEngine::separate_alpha(pixel_f *row, int size)
765 // for(i = 0; i < size; i++)
767 // if(row[i].a > 0 && row[i].a < vmax)
769 // alpha = (double)row[i].a / vmax;
770 // result = (double)row[i].r / alpha;
771 // row[i].r = (result > vmax ? vmax : result);
772 // result = (double)row[i].g / alpha;
773 // row[i].g = (result > vmax ? vmax : result);
774 // result = (double)row[i].b / alpha;
775 // row[i].b = (result > vmax ? vmax : result);
781 int BlurEngine::blur_strip3(int &size)
783 // multiply_alpha(src, size);
786 pixel_f *sp_m = src + size - 1;
788 pixel_f *vm = val_m + size - 1;
794 for(int k = 0; k < size; k++)
796 terms = (k < 4) ? k : 4;
798 radius[k] = plugin->config.radius;
800 for(l = 0; l <= terms; l++)
804 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
805 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
809 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
810 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
814 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
815 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
823 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
824 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
828 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
829 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
833 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
834 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
843 transfer_pixels(val_p, val_m, src, radius, dst, size);
844 // separate_alpha(dst, size);
849 int BlurEngine::blur_strip4(int &size)
852 // multiply_alpha(src, size);
855 pixel_f *sp_m = src + size - 1;
857 pixel_f *vm = val_m + size - 1;
863 for(int k = 0; k < size; k++)
865 if(plugin->config.a_key)
866 radius[k] = (double)plugin->config.radius * src[k].a / vmax;
868 radius[k] = plugin->config.radius;
871 for(int k = 0; k < size; k++)
873 terms = (k < 4) ? k : 4;
875 if(plugin->config.a_key)
877 if(radius[k] != prev_forward_radius)
879 prev_forward_radius = radius[k];
880 if(radius[k] >= MIN_RADIUS / 2)
882 reconfigure(&forward_constants, radius[k]);
886 reconfigure(&forward_constants, (double)MIN_RADIUS / 2);
890 if(radius[size - 1 - k] != prev_reverse_radius)
892 //printf("BlurEngine::blur_strip4 %f\n", sp_m->a);
893 prev_reverse_radius = radius[size - 1 - k];
894 if(radius[size - 1 - k] >= MIN_RADIUS / 2)
896 reconfigure(&reverse_constants, radius[size - 1 - k]);
900 reconfigure(&reverse_constants, (double)MIN_RADIUS / 2);
904 // Force alpha to be copied regardless of alpha blur enabled
910 for(l = 0; l <= terms; l++)
914 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
915 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
919 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
920 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
924 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
925 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
927 if(plugin->config.a && !plugin->config.a_key)
929 vp->a += forward_constants.n_p[l] * sp_p[-l].a - forward_constants.d_p[l] * vp[-l].a;
930 vm->a += reverse_constants.n_m[l] * sp_m[l].a - reverse_constants.d_m[l] * vm[l].a;
938 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
939 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
943 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
944 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
948 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
949 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
951 if(plugin->config.a && !plugin->config.a_key)
953 vp->a += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.a;
954 vm->a += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.a;
964 transfer_pixels(val_p, val_m, src, radius, dst, size);
965 // separate_alpha(dst, size);