initial commit
[goodguy/history.git] / cinelerra-5.0 / 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         vertical = 1;
44         horizontal = 1;
45         radius = 5;
46         a_key = 0;
47         a = r = g = b = 1;
48 }
49
50 int BlurConfig::equivalent(BlurConfig &that)
51 {
52         return (vertical == that.vertical && 
53                 horizontal == that.horizontal && 
54                 radius == that.radius &&
55                 a_key == that.a_key &&
56                 a == that.a &&
57                 r == that.r &&
58                 g == that.g &&
59                 b == that.b);
60 }
61
62 void BlurConfig::copy_from(BlurConfig &that)
63 {
64         vertical = that.vertical;
65         horizontal = that.horizontal;
66         radius = that.radius;
67         a_key = that.a_key;
68         a = that.a;
69         r = that.r;
70         g = that.g;
71         b = that.b;
72 }
73
74 void BlurConfig::interpolate(BlurConfig &prev, 
75         BlurConfig &next, 
76         int64_t prev_frame, 
77         int64_t next_frame, 
78         int64_t current_frame)
79 {
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);
82
83
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);
88         a_key = prev.a_key;
89         a = prev.a;
90         r = prev.r;
91         g = prev.g;
92         b = prev.b;
93 }
94
95
96
97
98
99
100 REGISTER_PLUGIN(BlurMain)
101
102
103
104
105
106
107
108
109 BlurMain::BlurMain(PluginServer *server)
110  : PluginVClient(server)
111 {
112         need_reconfigure = 1;
113         engine = 0;
114         overlayer = 0;
115 }
116
117 BlurMain::~BlurMain()
118 {
119 //printf("BlurMain::~BlurMain 1\n");
120
121         if(engine)
122         {
123                 for(int i = 0; i < (get_project_smp() + 1); i++)
124                         delete engine[i];
125                 delete [] engine;
126         }
127
128         if(overlayer) delete overlayer;
129 }
130
131 const char* BlurMain::plugin_title() { return N_("Blur"); }
132 int BlurMain::is_realtime() { return 1; }
133
134
135 NEW_WINDOW_MACRO(BlurMain, BlurWindow)
136
137 LOAD_CONFIGURATION_MACRO(BlurMain, BlurConfig)
138
139
140
141
142 int BlurMain::process_buffer(VFrame *frame,
143         int64_t start_position,
144         double frame_rate)
145 {
146         int i;
147
148         need_reconfigure |= load_configuration();
149
150         read_frame(frame, 
151                 0, 
152                 start_position, 
153                 frame_rate,
154                 0);
155
156 // Create temp based on alpha keying.
157 // Alpha keying needs 2x oversampling.
158
159         if(config.a_key)
160         {
161                 PluginVClient::new_temp(frame->get_w() * 2,
162                                 frame->get_h() * 2,
163                                 frame->get_color_model());
164                 if(!overlayer)
165                 {
166                         overlayer = new OverlayFrame(PluginClient::get_project_smp() + 1);
167                 }
168
169                 overlayer->overlay(PluginVClient::get_temp(), 
170                         frame, 
171                         0, 
172                         0, 
173                         frame->get_w(), 
174                         frame->get_h(), 
175                         0, 
176                         0, 
177                         PluginVClient::get_temp()->get_w(), 
178                         PluginVClient::get_temp()->get_h(), 
179                         1,        // 0 - 1
180                         TRANSFER_REPLACE,
181                         NEAREST_NEIGHBOR);
182                 input_frame = PluginVClient::get_temp();
183         }
184         else
185         {
186                 PluginVClient::new_temp(frame->get_w(),
187                                 frame->get_h(),
188                                 frame->get_color_model());
189                 input_frame = frame;
190         }
191
192
193 //printf("BlurMain::process_realtime 1 %d %d\n", need_reconfigure, config.radius);
194         if(need_reconfigure)
195         {
196                 if(!engine)
197                 {
198                         engine = new BlurEngine*[(get_project_smp() + 1)];
199                         for(i = 0; i < (get_project_smp() + 1); i++)
200                         {
201                                 engine[i] = new BlurEngine(this);
202                                 engine[i]->start();
203                         }
204                 }
205
206                 for(i = 0; i < (get_project_smp() + 1); i++)
207                 {
208                         engine[i]->reconfigure(&engine[i]->forward_constants, config.radius);
209                         engine[i]->reconfigure(&engine[i]->reverse_constants, config.radius);
210                 }
211                 need_reconfigure = 0;
212         }
213
214
215         if(config.radius < MIN_RADIUS || 
216                 (!config.vertical && !config.horizontal))
217         {
218 // Data never processed
219         }
220         else
221         {
222 // Process blur
223 // Need to blur vertically to a temp and 
224 // horizontally to the output in 2 discrete passes.
225
226                 for(i = 0; i < get_project_smp() + 1; i++)
227                 {
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));
233                 }
234
235                 for(i = 0; i < (get_project_smp() + 1); i++)
236                 {
237                         engine[i]->do_horizontal = 0;
238                         engine[i]->start_process_frame(input_frame);
239                 }
240
241                 for(i = 0; i < (get_project_smp() + 1); i++)
242                 {
243                         engine[i]->wait_process_frame();
244                 }
245
246                 for(i = 0; i < (get_project_smp() + 1); i++)
247                 {
248                         engine[i]->do_horizontal = 1;
249                         engine[i]->start_process_frame(input_frame);
250                 }
251
252                 for(i = 0; i < (get_project_smp() + 1); i++)
253                 {
254                         engine[i]->wait_process_frame();
255                 }
256         }
257
258
259 // Downsample
260         if(config.a_key)
261         {
262                 overlayer->overlay(frame, 
263                         PluginVClient::get_temp(), 
264                         0, 
265                         0, 
266                         PluginVClient::get_temp()->get_w(), 
267                         PluginVClient::get_temp()->get_h(), 
268                         0, 
269                         0, 
270                         frame->get_w(), 
271                         frame->get_h(), 
272                         1,        // 0 - 1
273                         TRANSFER_REPLACE,
274                         NEAREST_NEIGHBOR);
275         }
276
277         return 0;
278 }
279
280
281 void BlurMain::update_gui()
282 {
283         if(thread)
284         {
285                 int reconfigure = load_configuration();
286                 if(reconfigure) 
287                 {
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();
299                 }
300         }
301 }
302
303
304
305
306
307 void BlurMain::save_data(KeyFrame *keyframe)
308 {
309         FileXML output;
310
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);
322         output.append_tag();
323         output.terminate_string();
324 }
325
326 void BlurMain::read_data(KeyFrame *keyframe)
327 {
328         FileXML input;
329
330         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
331
332         int result = 0;
333
334         while(!result)
335         {
336                 result = input.read_tag();
337
338                 if(!result)
339                 {
340                         if(input.tag.title_is("BLUR"))
341                         {
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);
351                         }
352                 }
353         }
354 }
355
356
357
358
359
360
361
362
363
364 BlurEngine::BlurEngine(BlurMain *plugin)
365  : Thread(1, 0, 0)
366 {
367         this->plugin = plugin;
368         last_frame = 0;
369
370 // Strip size
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
374         size *= 2;
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];
380
381         set_synchronous(1);
382         input_lock.lock();
383         output_lock.lock();
384 }
385
386 BlurEngine::~BlurEngine()
387 {
388         last_frame = 1;
389         input_lock.unlock();
390         join();
391         delete [] val_p;
392         delete [] val_m;
393         delete [] src;
394         delete [] dst;
395         delete [] radius;
396 }
397
398 void BlurEngine::set_range(int start_y, 
399         int end_y,
400         int start_x,
401         int end_x)
402 {
403         this->start_y = start_y;
404         this->end_y = end_y;
405         this->start_x = start_x;
406         this->end_x = end_x;
407 }
408
409 int BlurEngine::start_process_frame(VFrame *frame)
410 {
411         this->frame = frame;
412         input_lock.unlock();
413         return 0;
414 }
415
416 int BlurEngine::wait_process_frame()
417 {
418         output_lock.lock();
419         return 0;
420 }
421
422 void BlurEngine::run()
423 {
424         int j, k;
425
426
427         while(1)
428         {
429                 input_lock.lock();
430                 if(last_frame)
431                 {
432                         output_lock.unlock();
433                         return;
434                 }
435
436
437
438
439
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;
446
447
448
449
450 #define BLUR(type, max, components) \
451 { \
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; \
456         vmax = max; \
457  \
458         if(!do_horizontal && plugin->config.vertical) \
459         { \
460 /* Vertical pass */ \
461 /* Render to temp if a horizontal pass comes next */ \
462 /*              if(plugin->config.horizontal) */ \
463 /*              { */ \
464 /*                      current_output = (type **)plugin->get_temp()->get_rows(); */ \
465 /*              } */ \
466  \
467                 for(j = start_x; j < end_x; j++) \
468                 { \
469                         bzero(val_p, sizeof(pixel_f) * h); \
470                         bzero(val_m, sizeof(pixel_f) * h); \
471  \
472                         for(k = 0; k < h; k++) \
473                         { \
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) \
478                                 { \
479                                         if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[k][j * components + 3]; \
480                                 } \
481                         } \
482  \
483                         if(components == 4) \
484                                 blur_strip4(h); \
485                         else \
486                                 blur_strip3(h); \
487  \
488                         for(k = 0; k < h; k++) \
489                         { \
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; \
495                         } \
496                 } \
497  \
498 /*              current_input = current_output; */ \
499 /*              current_output = output_rows; */ \
500         } \
501  \
502  \
503         if(do_horizontal && plugin->config.horizontal) \
504         { \
505 /* Vertical pass */ \
506 /*              if(plugin->config.vertical) */ \
507 /*              { */ \
508 /*                      current_input = (type **)plugin->get_temp()->get_rows(); */ \
509 /*              } */ \
510  \
511 /* Horizontal pass */ \
512                 for(j = start_y; j < end_y; j++) \
513                 { \
514                         bzero(val_p, sizeof(pixel_f) * w); \
515                         bzero(val_m, sizeof(pixel_f) * w); \
516  \
517                         for(k = 0; k < w; k++) \
518                         { \
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]; \
524                         } \
525  \
526                         if(components == 4) \
527                                 blur_strip4(w); \
528                         else \
529                                 blur_strip3(w); \
530  \
531                         for(k = 0; k < w; k++) \
532                         { \
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) \
537                                 { \
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; \
542                                 } \
543                         } \
544                 } \
545         } \
546 }
547
548
549
550                 switch(color_model)
551                 {
552                         case BC_RGB888:
553                         case BC_YUV888:
554                                 BLUR(unsigned char, 0xff, 3);
555                                 break;
556
557                         case BC_RGB_FLOAT:
558                                 BLUR(float, 1.0, 3);
559                                 break;
560
561                         case BC_RGBA8888:
562                         case BC_YUVA8888:
563                                 BLUR(unsigned char, 0xff, 4);
564                                 break;
565
566                         case BC_RGBA_FLOAT:
567                                 BLUR(float, 1.0, 4);
568                                 break;
569
570                         case BC_RGB161616:
571                         case BC_YUV161616:
572                                 BLUR(uint16_t, 0xffff, 3);
573                                 break;
574
575                         case BC_RGBA16161616:
576                         case BC_YUVA16161616:
577                                 BLUR(uint16_t, 0xffff, 4);
578                                 break;
579                 }
580
581                 output_lock.unlock();
582         }
583 }
584
585 int BlurEngine::reconfigure(BlurConstants *constants, double radius)
586 {
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);
592         return 0;
593 }
594
595 int BlurEngine::get_constants(BlurConstants *ptr, double std_dev)
596 {
597         int i;
598         double constants[8];
599         double div;
600
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;
610
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])) +
615                                 exp(constants[0]) *
616                                 (constants[5] * sin(constants[2]) -
617                                 (2 * constants[6] + constants[4]) * cos(constants[2]));
618
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]);
626
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] * 
632                                 cos(constants[2]));
633         ptr->n_p[4] = 0.0;
634
635         ptr->d_p[0] = 0.0;
636         ptr->d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
637                                 2 * exp(constants[0]) * cos(constants[2]);
638
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]);
642
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]);
645
646         ptr->d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
647
648         for(i = 0; i < 5; i++) ptr->d_m[i] = ptr->d_p[i];
649
650         ptr->n_m[0] = 0.0;
651         for(i = 1; i <= 4; i++)
652                 ptr->n_m[i] = ptr->n_p[i] - ptr->d_p[i] * ptr->n_p[0];
653
654         double sum_n_p, sum_n_m, sum_d;
655         double a, b;
656
657         sum_n_p = 0.0;
658         sum_n_m = 0.0;
659         sum_d = 0.0;
660         for(i = 0; i < 5; i++)
661         {
662                 sum_n_p += ptr->n_p[i];
663                 sum_n_m += ptr->n_m[i];
664                 sum_d += ptr->d_p[i];
665         }
666
667         a = sum_n_p / (1 + sum_d);
668         b = sum_n_m / (1 + sum_d);
669
670         for (i = 0; i < 5; i++)
671         {
672                 ptr->bd_p[i] = ptr->d_p[i] * a;
673                 ptr->bd_m[i] = ptr->d_m[i] * b;
674         }
675
676         return 0;
677 }
678
679 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
680
681 int BlurEngine::transfer_pixels(pixel_f *src1, 
682         pixel_f *src2, 
683         pixel_f *src,
684         double *radius,
685         pixel_f *dest, 
686         int size)
687 {
688         int i;
689         double sum;
690
691 // printf("BlurEngine::transfer_pixels %d %d %d %d\n", 
692 // plugin->config.r, 
693 // plugin->config.g, 
694 // plugin->config.b, 
695 // plugin->config.a);
696
697         for(i = 0; i < size; i++)
698     {
699                 sum = src1[i].r + src2[i].r;
700                 BOUNDARY(sum);
701                 dest[i].r = sum;
702
703                 sum = src1[i].g + src2[i].g;
704                 BOUNDARY(sum);
705                 dest[i].g = sum;
706
707                 sum = src1[i].b + src2[i].b;
708                 BOUNDARY(sum);
709                 dest[i].b = sum;
710
711                 sum = src1[i].a + src2[i].a;
712                 BOUNDARY(sum);
713                 dest[i].a = sum;
714
715 //              if(radius[i] < 2)
716 //              {
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;
722 //              }
723     }
724         return 0;
725 }
726
727
728 int BlurEngine::multiply_alpha(pixel_f *row, int size)
729 {
730 //      register int i;
731 //      register double alpha;
732
733 //      for(i = 0; i < size; i++)
734 //      {
735 //              alpha = (double)row[i].a / vmax;
736 //              row[i].r *= alpha;
737 //              row[i].g *= alpha;
738 //              row[i].b *= alpha;
739 //      }
740         return 0;
741 }
742
743 int BlurEngine::separate_alpha(pixel_f *row, int size)
744 {
745 //      register int i;
746 //      register double alpha;
747 //      register double result;
748         
749 //      for(i = 0; i < size; i++)
750 //      {
751 //              if(row[i].a > 0 && row[i].a < vmax)
752 //              {
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);
760 //              }
761 //      }
762         return 0;
763 }
764
765 int BlurEngine::blur_strip3(int &size)
766 {
767 //      multiply_alpha(src, size);
768
769         pixel_f *sp_p = src;
770         pixel_f *sp_m = src + size - 1;
771         pixel_f *vp = val_p;
772         pixel_f *vm = val_m + size - 1;
773
774         initial_p = sp_p[0];
775         initial_m = sp_m[0];
776
777         int l;
778         for(int k = 0; k < size; k++)
779         {
780                 terms = (k < 4) ? k : 4;
781
782                 radius[k] = plugin->config.radius;
783
784                 for(l = 0; l <= terms; l++)
785                 {
786                         if(plugin->config.r)
787                         {
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;
790                         }
791                         if(plugin->config.g)
792                         {
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;
795                         }
796                         if(plugin->config.b)
797                         {
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;
800                         }
801                 }
802
803                 for( ; l <= 4; l++)
804                 {
805                         if(plugin->config.r)
806                         {
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;
809                         }
810                         if(plugin->config.g)
811                         {
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;
814                         }
815                         if(plugin->config.b)
816                         {
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;
819                         }
820                 }
821                 sp_p++;
822                 sp_m--;
823                 vp++;
824                 vm--;
825         }
826
827         transfer_pixels(val_p, val_m, src, radius, dst, size);
828 //      separate_alpha(dst, size);
829         return 0;
830 }
831
832
833 int BlurEngine::blur_strip4(int &size)
834 {
835         
836 //      multiply_alpha(src, size);
837
838         pixel_f *sp_p = src;
839         pixel_f *sp_m = src + size - 1;
840         pixel_f *vp = val_p;
841         pixel_f *vm = val_m + size - 1;
842
843         initial_p = sp_p[0];
844         initial_m = sp_m[0];
845
846         int l;
847         for(int k = 0; k < size; k++)
848         {
849                 if(plugin->config.a_key)
850                         radius[k] = (double)plugin->config.radius * src[k].a / vmax;
851                 else
852                         radius[k] = plugin->config.radius;
853         }
854
855         for(int k = 0; k < size; k++)
856         {
857                 terms = (k < 4) ? k : 4;
858                 
859                 if(plugin->config.a_key)
860                 {
861                         if(radius[k] != prev_forward_radius)
862                         {
863                                 prev_forward_radius = radius[k];
864                                 if(radius[k] >= MIN_RADIUS / 2)
865                                 {
866                                         reconfigure(&forward_constants, radius[k]);
867                                 }
868                                 else
869                                 {
870                                         reconfigure(&forward_constants, (double)MIN_RADIUS / 2);
871                                 }
872                         }
873
874                         if(radius[size - 1 - k] != prev_reverse_radius)
875                         {
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)
879                                 {
880                                         reconfigure(&reverse_constants, radius[size - 1 - k]);
881                                 }
882                                 else
883                                 {
884                                         reconfigure(&reverse_constants, (double)MIN_RADIUS / 2);
885                                 }
886                         }
887
888 // Force alpha to be copied regardless of alpha blur enabled
889                         vp->a = sp_p->a;
890                         vm->a = sp_m->a;
891                 }
892
893
894                 for(l = 0; l <= terms; l++)
895                 {
896                         if(plugin->config.r)
897                         {
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;
900                         }
901                         if(plugin->config.g)
902                         {
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;
905                         }
906                         if(plugin->config.b)
907                         {
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;
910                         }
911                         if(plugin->config.a && !plugin->config.a_key)
912                         {
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;
915                         }
916                 }
917
918                 for( ; l <= 4; l++)
919                 {
920                         if(plugin->config.r)
921                         {
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;
924                         }
925                         if(plugin->config.g)
926                         {
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;
929                         }
930                         if(plugin->config.b)
931                         {
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;
934                         }
935                         if(plugin->config.a && !plugin->config.a_key)
936                         {
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;
939                         }
940                 }
941
942                 sp_p++;
943                 sp_m--;
944                 vp++;
945                 vm--;
946         }
947
948         transfer_pixels(val_p, val_m, src, radius, dst, size);
949 //      separate_alpha(dst, size);
950         return 0;
951 }
952
953
954
955
956
957