bc404397966ccae30b2e61d9b970a5861c3b4dfb
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / blur / blur.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2010 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "filexml.h"
23 #include "blur.h"
24 #include "blurwindow.h"
25 #include "bchash.h"
26 #include "keyframe.h"
27 #include "language.h"
28 #include "vframe.h"
29
30 #include <math.h>
31 #include <stdint.h>
32 #include <string.h>
33
34
35
36
37
38 #define MIN_RADIUS 2
39
40
41 BlurConfig::BlurConfig()
42 {
43         reset();
44 }
45
46 void BlurConfig::reset()
47 {
48         vertical = 1;
49         horizontal = 1;
50         radius = 5;
51         a_key = 0;
52         a = r = g = b = 1;
53 }
54
55 int BlurConfig::equivalent(BlurConfig &that)
56 {
57         return (vertical == that.vertical &&
58                 horizontal == that.horizontal &&
59                 radius == that.radius &&
60                 a_key == that.a_key &&
61                 a == that.a &&
62                 r == that.r &&
63                 g == that.g &&
64                 b == that.b);
65 }
66
67 void BlurConfig::copy_from(BlurConfig &that)
68 {
69         vertical = that.vertical;
70         horizontal = that.horizontal;
71         radius = that.radius;
72         a_key = that.a_key;
73         a = that.a;
74         r = that.r;
75         g = that.g;
76         b = that.b;
77 }
78
79 void BlurConfig::interpolate(BlurConfig &prev,
80         BlurConfig &next,
81         int64_t prev_frame,
82         int64_t next_frame,
83         int64_t current_frame)
84 {
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);
87
88
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);
93         a_key = prev.a_key;
94         a = prev.a;
95         r = prev.r;
96         g = prev.g;
97         b = prev.b;
98 }
99
100
101
102
103
104
105 REGISTER_PLUGIN(BlurMain)
106
107
108
109
110
111
112
113
114 BlurMain::BlurMain(PluginServer *server)
115  : PluginVClient(server)
116 {
117         need_reconfigure = 1;
118         engine = 0;
119         overlayer = 0;
120 }
121
122 BlurMain::~BlurMain()
123 {
124 //printf("BlurMain::~BlurMain 1\n");
125
126         if(engine)
127         {
128                 for(int i = 0; i < (get_project_smp() + 1); i++)
129                         delete engine[i];
130                 delete [] engine;
131         }
132
133         if(overlayer) delete overlayer;
134 }
135
136 const char* BlurMain::plugin_title() { return N_("Blur"); }
137 int BlurMain::is_realtime() { return 1; }
138
139
140 NEW_WINDOW_MACRO(BlurMain, BlurWindow)
141
142 LOAD_CONFIGURATION_MACRO(BlurMain, BlurConfig)
143
144
145
146
147 int BlurMain::process_buffer(VFrame *frame,
148         int64_t start_position,
149         double frame_rate)
150 {
151         int i;
152
153         need_reconfigure |= load_configuration();
154
155         read_frame(frame,
156                 0,
157                 start_position,
158                 frame_rate,
159                 0);
160
161 // Create temp based on alpha keying.
162 // Alpha keying needs 2x oversampling.
163
164         if(config.a_key)
165         {
166                 PluginVClient::new_temp(frame->get_w() * 2,
167                                 frame->get_h() * 2,
168                                 frame->get_color_model());
169                 if(!overlayer)
170                 {
171                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
172                 }
173
174                 overlayer->overlay(PluginVClient::get_temp(),
175                         frame,
176                         0,
177                         0,
178                         frame->get_w(),
179                         frame->get_h(),
180                         0,
181                         0,
182                         PluginVClient::get_temp()->get_w(),
183                         PluginVClient::get_temp()->get_h(),
184                         1,        // 0 - 1
185                         TRANSFER_REPLACE,
186                         NEAREST_NEIGHBOR);
187                 input_frame = PluginVClient::get_temp();
188         }
189         else
190         {
191                 PluginVClient::new_temp(frame->get_w(),
192                                 frame->get_h(),
193                                 frame->get_color_model());
194                 input_frame = frame;
195         }
196
197
198 //printf("BlurMain::process_realtime 1 %d %d\n", need_reconfigure, config.radius);
199         if(need_reconfigure)
200         {
201                 if(!engine)
202                 {
203                         engine = new BlurEngine*[(get_project_smp() + 1)];
204                         for(i = 0; i < (get_project_smp() + 1); i++)
205                         {
206                                 engine[i] = new BlurEngine(this);
207                                 engine[i]->start();
208                         }
209                 }
210
211                 for(i = 0; i < (get_project_smp() + 1); i++)
212                 {
213                         engine[i]->reconfigure(&engine[i]->forward_constants, config.radius);
214                         engine[i]->reconfigure(&engine[i]->reverse_constants, config.radius);
215                 }
216                 need_reconfigure = 0;
217         }
218
219
220         if(config.radius < MIN_RADIUS ||
221                 (!config.vertical && !config.horizontal))
222         {
223 // Data never processed
224         }
225         else
226         {
227 // Process blur
228 // Need to blur vertically to a temp and
229 // horizontally to the output in 2 discrete passes.
230
231                 for(i = 0; i < get_project_smp() + 1; i++)
232                 {
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));
238                 }
239
240                 for(i = 0; i < (get_project_smp() + 1); i++)
241                 {
242                         engine[i]->do_horizontal = 0;
243                         engine[i]->start_process_frame(input_frame);
244                 }
245
246                 for(i = 0; i < (get_project_smp() + 1); i++)
247                 {
248                         engine[i]->wait_process_frame();
249                 }
250
251                 for(i = 0; i < (get_project_smp() + 1); i++)
252                 {
253                         engine[i]->do_horizontal = 1;
254                         engine[i]->start_process_frame(input_frame);
255                 }
256
257                 for(i = 0; i < (get_project_smp() + 1); i++)
258                 {
259                         engine[i]->wait_process_frame();
260                 }
261         }
262
263
264 // Downsample
265         if(config.a_key)
266         {
267                 overlayer->overlay(frame,
268                         PluginVClient::get_temp(),
269                         0,
270                         0,
271                         PluginVClient::get_temp()->get_w(),
272                         PluginVClient::get_temp()->get_h(),
273                         0,
274                         0,
275                         frame->get_w(),
276                         frame->get_h(),
277                         1,        // 0 - 1
278                         TRANSFER_REPLACE,
279                         NEAREST_NEIGHBOR);
280         }
281
282         return 0;
283 }
284
285
286 void BlurMain::update_gui()
287 {
288         if(thread)
289         {
290                 int reconfigure = load_configuration();
291                 if(reconfigure)
292                 {
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();
304                 }
305         }
306 }
307
308
309
310
311
312 void BlurMain::save_data(KeyFrame *keyframe)
313 {
314         FileXML output;
315
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);
327         output.append_tag();
328         output.tag.set_title("/BLUR");
329         output.append_tag();
330         output.append_newline();
331         output.terminate_string();
332 }
333
334 void BlurMain::read_data(KeyFrame *keyframe)
335 {
336         FileXML input;
337
338         input.set_shared_input(keyframe->xbuf);
339
340         int result = 0;
341
342         while(!result)
343         {
344                 result = input.read_tag();
345
346                 if(!result)
347                 {
348                         if(input.tag.title_is("BLUR"))
349                         {
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);
359                         }
360                 }
361         }
362 }
363
364
365
366
367
368
369
370
371
372 BlurEngine::BlurEngine(BlurMain *plugin)
373  : Thread(1, 0, 0)
374 {
375         this->plugin = plugin;
376         last_frame = 0;
377
378 // Strip size
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
382         size *= 2;
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];
388
389         set_synchronous(1);
390         input_lock.lock();
391         output_lock.lock();
392 }
393
394 BlurEngine::~BlurEngine()
395 {
396         last_frame = 1;
397         input_lock.unlock();
398         join();
399         delete [] val_p;
400         delete [] val_m;
401         delete [] src;
402         delete [] dst;
403         delete [] radius;
404 }
405
406 void BlurEngine::set_range(int start_y,
407         int end_y,
408         int start_x,
409         int end_x)
410 {
411         this->start_y = start_y;
412         this->end_y = end_y;
413         this->start_x = start_x;
414         this->end_x = end_x;
415 }
416
417 int BlurEngine::start_process_frame(VFrame *frame)
418 {
419         this->frame = frame;
420         input_lock.unlock();
421         return 0;
422 }
423
424 int BlurEngine::wait_process_frame()
425 {
426         output_lock.lock();
427         return 0;
428 }
429
430 void BlurEngine::run()
431 {
432         int j, k;
433
434
435         while(1)
436         {
437                 input_lock.lock();
438                 if(last_frame)
439                 {
440                         output_lock.unlock();
441                         return;
442                 }
443
444
445
446
447
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;
454
455
456
457
458 #define BLUR(type, max, components) \
459 { \
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; \
464         vmax = max; \
465  \
466         if(!do_horizontal && plugin->config.vertical) \
467         { \
468 /* Vertical pass */ \
469 /* Render to temp if a horizontal pass comes next */ \
470 /*              if(plugin->config.horizontal) */ \
471 /*              { */ \
472 /*                      current_output = (type **)plugin->get_temp()->get_rows(); */ \
473 /*              } */ \
474  \
475                 for(j = start_x; j < end_x; j++) \
476                 { \
477                         bzero(val_p, sizeof(pixel_f) * h); \
478                         bzero(val_m, sizeof(pixel_f) * h); \
479  \
480                         for(k = 0; k < h; k++) \
481                         { \
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) \
486                                 { \
487                                         if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[k][j * components + 3]; \
488                                 } \
489                         } \
490  \
491                         if(components == 4) \
492                                 blur_strip4(h); \
493                         else \
494                                 blur_strip3(h); \
495  \
496                         for(k = 0; k < h; k++) \
497                         { \
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; \
503                         } \
504                 } \
505  \
506 /*              current_input = current_output; */ \
507 /*              current_output = output_rows; */ \
508         } \
509  \
510  \
511         if(do_horizontal && plugin->config.horizontal) \
512         { \
513 /* Vertical pass */ \
514 /*              if(plugin->config.vertical) */ \
515 /*              { */ \
516 /*                      current_input = (type **)plugin->get_temp()->get_rows(); */ \
517 /*              } */ \
518  \
519 /* Horizontal pass */ \
520                 for(j = start_y; j < end_y; j++) \
521                 { \
522                         bzero(val_p, sizeof(pixel_f) * w); \
523                         bzero(val_m, sizeof(pixel_f) * w); \
524  \
525                         for(k = 0; k < w; k++) \
526                         { \
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]; \
532                         } \
533  \
534                         if(components == 4) \
535                                 blur_strip4(w); \
536                         else \
537                                 blur_strip3(w); \
538  \
539                         for(k = 0; k < w; k++) \
540                         { \
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) \
545                                 { \
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; \
550                                 } \
551                         } \
552                 } \
553         } \
554 }
555
556
557
558                 switch(color_model)
559                 {
560                         case BC_RGB888:
561                         case BC_YUV888:
562                                 BLUR(unsigned char, 0xff, 3);
563                                 break;
564
565                         case BC_RGB_FLOAT:
566                                 BLUR(float, 1.0, 3);
567                                 break;
568
569                         case BC_RGBA8888:
570                         case BC_YUVA8888:
571                                 BLUR(unsigned char, 0xff, 4);
572                                 break;
573
574                         case BC_RGBA_FLOAT:
575                                 BLUR(float, 1.0, 4);
576                                 break;
577
578                         case BC_RGB161616:
579                         case BC_YUV161616:
580                                 BLUR(uint16_t, 0xffff, 3);
581                                 break;
582
583                         case BC_RGBA16161616:
584                         case BC_YUVA16161616:
585                                 BLUR(uint16_t, 0xffff, 4);
586                                 break;
587                 }
588
589                 output_lock.unlock();
590         }
591 }
592
593 int BlurEngine::reconfigure(BlurConstants *constants, double radius)
594 {
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);
600         return 0;
601 }
602
603 int BlurEngine::get_constants(BlurConstants *ptr, double std_dev)
604 {
605         int i;
606         double constants[8];
607         double div;
608
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;
618
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])) +
623                                 exp(constants[0]) *
624                                 (constants[5] * sin(constants[2]) -
625                                 (2 * constants[6] + constants[4]) * cos(constants[2]));
626
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]);
634
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] *
640                                 cos(constants[2]));
641         ptr->n_p[4] = 0.0;
642
643         ptr->d_p[0] = 0.0;
644         ptr->d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
645                                 2 * exp(constants[0]) * cos(constants[2]);
646
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]);
650
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]);
653
654         ptr->d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
655
656         for(i = 0; i < 5; i++) ptr->d_m[i] = ptr->d_p[i];
657
658         ptr->n_m[0] = 0.0;
659         for(i = 1; i <= 4; i++)
660                 ptr->n_m[i] = ptr->n_p[i] - ptr->d_p[i] * ptr->n_p[0];
661
662         double sum_n_p, sum_n_m, sum_d;
663         double a, b;
664
665         sum_n_p = 0.0;
666         sum_n_m = 0.0;
667         sum_d = 0.0;
668         for(i = 0; i < 5; i++)
669         {
670                 sum_n_p += ptr->n_p[i];
671                 sum_n_m += ptr->n_m[i];
672                 sum_d += ptr->d_p[i];
673         }
674
675         a = sum_n_p / (1 + sum_d);
676         b = sum_n_m / (1 + sum_d);
677
678         for (i = 0; i < 5; i++)
679         {
680                 ptr->bd_p[i] = ptr->d_p[i] * a;
681                 ptr->bd_m[i] = ptr->d_m[i] * b;
682         }
683
684         return 0;
685 }
686
687 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
688
689 int BlurEngine::transfer_pixels(pixel_f *src1,
690         pixel_f *src2,
691         pixel_f *src,
692         double *radius,
693         pixel_f *dest,
694         int size)
695 {
696         int i;
697         double sum;
698
699 // printf("BlurEngine::transfer_pixels %d %d %d %d\n",
700 // plugin->config.r,
701 // plugin->config.g,
702 // plugin->config.b,
703 // plugin->config.a);
704
705         for(i = 0; i < size; i++)
706     {
707                 sum = src1[i].r + src2[i].r;
708                 BOUNDARY(sum);
709                 dest[i].r = sum;
710
711                 sum = src1[i].g + src2[i].g;
712                 BOUNDARY(sum);
713                 dest[i].g = sum;
714
715                 sum = src1[i].b + src2[i].b;
716                 BOUNDARY(sum);
717                 dest[i].b = sum;
718
719                 sum = src1[i].a + src2[i].a;
720                 BOUNDARY(sum);
721                 dest[i].a = sum;
722
723 //              if(radius[i] < 2)
724 //              {
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;
730 //              }
731     }
732         return 0;
733 }
734
735
736 int BlurEngine::multiply_alpha(pixel_f *row, int size)
737 {
738 //      int i;
739 //      double alpha;
740
741 //      for(i = 0; i < size; i++)
742 //      {
743 //              alpha = (double)row[i].a / vmax;
744 //              row[i].r *= alpha;
745 //              row[i].g *= alpha;
746 //              row[i].b *= alpha;
747 //      }
748         return 0;
749 }
750
751 int BlurEngine::separate_alpha(pixel_f *row, int size)
752 {
753 //      int i;
754 //      double alpha;
755 //      double result;
756
757 //      for(i = 0; i < size; i++)
758 //      {
759 //              if(row[i].a > 0 && row[i].a < vmax)
760 //              {
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);
768 //              }
769 //      }
770         return 0;
771 }
772
773 int BlurEngine::blur_strip3(int &size)
774 {
775 //      multiply_alpha(src, size);
776
777         pixel_f *sp_p = src;
778         pixel_f *sp_m = src + size - 1;
779         pixel_f *vp = val_p;
780         pixel_f *vm = val_m + size - 1;
781
782         initial_p = sp_p[0];
783         initial_m = sp_m[0];
784
785         int l;
786         for(int k = 0; k < size; k++)
787         {
788                 terms = (k < 4) ? k : 4;
789
790                 radius[k] = plugin->config.radius;
791
792                 for(l = 0; l <= terms; l++)
793                 {
794                         if(plugin->config.r)
795                         {
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;
798                         }
799                         if(plugin->config.g)
800                         {
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;
803                         }
804                         if(plugin->config.b)
805                         {
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;
808                         }
809                 }
810
811                 for( ; l <= 4; l++)
812                 {
813                         if(plugin->config.r)
814                         {
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;
817                         }
818                         if(plugin->config.g)
819                         {
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;
822                         }
823                         if(plugin->config.b)
824                         {
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;
827                         }
828                 }
829                 sp_p++;
830                 sp_m--;
831                 vp++;
832                 vm--;
833         }
834
835         transfer_pixels(val_p, val_m, src, radius, dst, size);
836 //      separate_alpha(dst, size);
837         return 0;
838 }
839
840
841 int BlurEngine::blur_strip4(int &size)
842 {
843
844 //      multiply_alpha(src, size);
845
846         pixel_f *sp_p = src;
847         pixel_f *sp_m = src + size - 1;
848         pixel_f *vp = val_p;
849         pixel_f *vm = val_m + size - 1;
850
851         initial_p = sp_p[0];
852         initial_m = sp_m[0];
853
854         int l;
855         for(int k = 0; k < size; k++)
856         {
857                 if(plugin->config.a_key)
858                         radius[k] = (double)plugin->config.radius * src[k].a / vmax;
859                 else
860                         radius[k] = plugin->config.radius;
861         }
862
863         for(int k = 0; k < size; k++)
864         {
865                 terms = (k < 4) ? k : 4;
866
867                 if(plugin->config.a_key)
868                 {
869                         if(radius[k] != prev_forward_radius)
870                         {
871                                 prev_forward_radius = radius[k];
872                                 if(radius[k] >= MIN_RADIUS / 2)
873                                 {
874                                         reconfigure(&forward_constants, radius[k]);
875                                 }
876                                 else
877                                 {
878                                         reconfigure(&forward_constants, (double)MIN_RADIUS / 2);
879                                 }
880                         }
881
882                         if(radius[size - 1 - k] != prev_reverse_radius)
883                         {
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)
887                                 {
888                                         reconfigure(&reverse_constants, radius[size - 1 - k]);
889                                 }
890                                 else
891                                 {
892                                         reconfigure(&reverse_constants, (double)MIN_RADIUS / 2);
893                                 }
894                         }
895
896 // Force alpha to be copied regardless of alpha blur enabled
897                         vp->a = sp_p->a;
898                         vm->a = sp_m->a;
899                 }
900
901
902                 for(l = 0; l <= terms; l++)
903                 {
904                         if(plugin->config.r)
905                         {
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;
908                         }
909                         if(plugin->config.g)
910                         {
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;
913                         }
914                         if(plugin->config.b)
915                         {
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;
918                         }
919                         if(plugin->config.a && !plugin->config.a_key)
920                         {
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;
923                         }
924                 }
925
926                 for( ; l <= 4; l++)
927                 {
928                         if(plugin->config.r)
929                         {
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;
932                         }
933                         if(plugin->config.g)
934                         {
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;
937                         }
938                         if(plugin->config.b)
939                         {
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;
942                         }
943                         if(plugin->config.a && !plugin->config.a_key)
944                         {
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;
947                         }
948                 }
949
950                 sp_p++;
951                 sp_m--;
952                 vp++;
953                 vm--;
954         }
955
956         transfer_pixels(val_p, val_m, src, radius, dst, size);
957 //      separate_alpha(dst, size);
958         return 0;
959 }
960
961
962
963
964
965