add lv2 plugin interface
[goodguy/history.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         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.tag.set_title("/BLUR");
324         output.append_tag();
325         output.append_newline();
326         output.terminate_string();
327 }
328
329 void BlurMain::read_data(KeyFrame *keyframe)
330 {
331         FileXML input;
332
333         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
334
335         int result = 0;
336
337         while(!result)
338         {
339                 result = input.read_tag();
340
341                 if(!result)
342                 {
343                         if(input.tag.title_is("BLUR"))
344                         {
345                                 config.vertical = input.tag.get_property("VERTICAL", config.vertical);
346                                 config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
347                                 config.radius = input.tag.get_property("RADIUS", config.radius);
348 //printf("BlurMain::read_data 1 %d %d %s\n", get_source_position(), keyframe->position, keyframe->get_data());
349                                 config.r = input.tag.get_property("R", config.r);
350                                 config.g = input.tag.get_property("G", config.g);
351                                 config.b = input.tag.get_property("B", config.b);
352                                 config.a = input.tag.get_property("A", config.a);
353                                 config.a_key = input.tag.get_property("A_KEY", config.a_key);
354                         }
355                 }
356         }
357 }
358
359
360
361
362
363
364
365
366
367 BlurEngine::BlurEngine(BlurMain *plugin)
368  : Thread(1, 0, 0)
369 {
370         this->plugin = plugin;
371         last_frame = 0;
372
373 // Strip size
374         int size = plugin->get_input()->get_w() > plugin->get_input()->get_h() ?
375                 plugin->get_input()->get_w() : plugin->get_input()->get_h();
376 // Prepare for oversampling
377         size *= 2;
378         val_p = new pixel_f[size];
379         val_m = new pixel_f[size];
380         radius = new double[size];
381         src = new pixel_f[size];
382         dst = new pixel_f[size];
383
384         set_synchronous(1);
385         input_lock.lock();
386         output_lock.lock();
387 }
388
389 BlurEngine::~BlurEngine()
390 {
391         last_frame = 1;
392         input_lock.unlock();
393         join();
394         delete [] val_p;
395         delete [] val_m;
396         delete [] src;
397         delete [] dst;
398         delete [] radius;
399 }
400
401 void BlurEngine::set_range(int start_y,
402         int end_y,
403         int start_x,
404         int end_x)
405 {
406         this->start_y = start_y;
407         this->end_y = end_y;
408         this->start_x = start_x;
409         this->end_x = end_x;
410 }
411
412 int BlurEngine::start_process_frame(VFrame *frame)
413 {
414         this->frame = frame;
415         input_lock.unlock();
416         return 0;
417 }
418
419 int BlurEngine::wait_process_frame()
420 {
421         output_lock.lock();
422         return 0;
423 }
424
425 void BlurEngine::run()
426 {
427         int j, k;
428
429
430         while(1)
431         {
432                 input_lock.lock();
433                 if(last_frame)
434                 {
435                         output_lock.unlock();
436                         return;
437                 }
438
439
440
441
442
443                 color_model = frame->get_color_model();
444                 int w = frame->get_w();
445                 int h = frame->get_h();
446 // Force recalculation of filter
447                 prev_forward_radius = -65536;
448                 prev_reverse_radius = -65536;
449
450
451
452
453 #define BLUR(type, max, components) \
454 { \
455         type **input_rows = (type **)frame->get_rows(); \
456         type **output_rows = (type **)frame->get_rows(); \
457         type **current_input = input_rows; \
458         type **current_output = output_rows; \
459         vmax = max; \
460  \
461         if(!do_horizontal && plugin->config.vertical) \
462         { \
463 /* Vertical pass */ \
464 /* Render to temp if a horizontal pass comes next */ \
465 /*              if(plugin->config.horizontal) */ \
466 /*              { */ \
467 /*                      current_output = (type **)plugin->get_temp()->get_rows(); */ \
468 /*              } */ \
469  \
470                 for(j = start_x; j < end_x; j++) \
471                 { \
472                         bzero(val_p, sizeof(pixel_f) * h); \
473                         bzero(val_m, sizeof(pixel_f) * h); \
474  \
475                         for(k = 0; k < h; k++) \
476                         { \
477                                 if(plugin->config.r) src[k].r = (double)current_input[k][j * components]; \
478                                 if(plugin->config.g) src[k].g = (double)current_input[k][j * components + 1]; \
479                                 if(plugin->config.b) src[k].b = (double)current_input[k][j * components + 2]; \
480                                 if(components == 4) \
481                                 { \
482                                         if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[k][j * components + 3]; \
483                                 } \
484                         } \
485  \
486                         if(components == 4) \
487                                 blur_strip4(h); \
488                         else \
489                                 blur_strip3(h); \
490  \
491                         for(k = 0; k < h; k++) \
492                         { \
493                                 if(plugin->config.r) current_output[k][j * components] = (type)dst[k].r; \
494                                 if(plugin->config.g) current_output[k][j * components + 1] = (type)dst[k].g; \
495                                 if(plugin->config.b) current_output[k][j * components + 2] = (type)dst[k].b; \
496                                 if(components == 4) \
497                                         if(plugin->config.a || plugin->config.a_key) current_output[k][j * components + 3] = (type)dst[k].a; \
498                         } \
499                 } \
500  \
501 /*              current_input = current_output; */ \
502 /*              current_output = output_rows; */ \
503         } \
504  \
505  \
506         if(do_horizontal && plugin->config.horizontal) \
507         { \
508 /* Vertical pass */ \
509 /*              if(plugin->config.vertical) */ \
510 /*              { */ \
511 /*                      current_input = (type **)plugin->get_temp()->get_rows(); */ \
512 /*              } */ \
513  \
514 /* Horizontal pass */ \
515                 for(j = start_y; j < end_y; j++) \
516                 { \
517                         bzero(val_p, sizeof(pixel_f) * w); \
518                         bzero(val_m, sizeof(pixel_f) * w); \
519  \
520                         for(k = 0; k < w; k++) \
521                         { \
522                                 if(plugin->config.r) src[k].r = (double)current_input[j][k * components]; \
523                                 if(plugin->config.g) src[k].g = (double)current_input[j][k * components + 1]; \
524                                 if(plugin->config.b) src[k].b = (double)current_input[j][k * components + 2]; \
525                                 if(components == 4) \
526                                         if(plugin->config.a || plugin->config.a_key) src[k].a = (double)current_input[j][k * components + 3]; \
527                         } \
528  \
529                         if(components == 4) \
530                                 blur_strip4(w); \
531                         else \
532                                 blur_strip3(w); \
533  \
534                         for(k = 0; k < w; k++) \
535                         { \
536                                 if(plugin->config.r) current_output[j][k * components] = (type)dst[k].r; \
537                                 if(plugin->config.g) current_output[j][k * components + 1] = (type)dst[k].g; \
538                                 if(plugin->config.b) current_output[j][k * components + 2] = (type)dst[k].b; \
539                                 if(components == 4) \
540                                 { \
541                                         if(plugin->config.a && !plugin->config.a_key) \
542                                                 current_output[j][k * components + 3] = (type)dst[k].a; \
543                                         else if(plugin->config.a_key) \
544                                                 current_output[j][k * components + 3] = max; \
545                                 } \
546                         } \
547                 } \
548         } \
549 }
550
551
552
553                 switch(color_model)
554                 {
555                         case BC_RGB888:
556                         case BC_YUV888:
557                                 BLUR(unsigned char, 0xff, 3);
558                                 break;
559
560                         case BC_RGB_FLOAT:
561                                 BLUR(float, 1.0, 3);
562                                 break;
563
564                         case BC_RGBA8888:
565                         case BC_YUVA8888:
566                                 BLUR(unsigned char, 0xff, 4);
567                                 break;
568
569                         case BC_RGBA_FLOAT:
570                                 BLUR(float, 1.0, 4);
571                                 break;
572
573                         case BC_RGB161616:
574                         case BC_YUV161616:
575                                 BLUR(uint16_t, 0xffff, 3);
576                                 break;
577
578                         case BC_RGBA16161616:
579                         case BC_YUVA16161616:
580                                 BLUR(uint16_t, 0xffff, 4);
581                                 break;
582                 }
583
584                 output_lock.unlock();
585         }
586 }
587
588 int BlurEngine::reconfigure(BlurConstants *constants, double radius)
589 {
590 // Blurring an oversampled temp
591         if(plugin->config.a_key) radius *= 2;
592         double std_dev = sqrt(-(double)(radius * radius) /
593                 (2 * log (1.0 / 255.0)));
594         get_constants(constants, std_dev);
595         return 0;
596 }
597
598 int BlurEngine::get_constants(BlurConstants *ptr, double std_dev)
599 {
600         int i;
601         double constants[8];
602         double div;
603
604         div = sqrt(2 * M_PI) * std_dev;
605         constants[0] = -1.783 / std_dev;
606         constants[1] = -1.723 / std_dev;
607         constants[2] = 0.6318 / std_dev;
608         constants[3] = 1.997  / std_dev;
609         constants[4] = 1.6803 / div;
610         constants[5] = 3.735 / div;
611         constants[6] = -0.6803 / div;
612         constants[7] = -0.2598 / div;
613
614         ptr->n_p[0] = constants[4] + constants[6];
615         ptr->n_p[1] = exp(constants[1]) *
616                                 (constants[7] * sin(constants[3]) -
617                                 (constants[6] + 2 * constants[4]) * cos(constants[3])) +
618                                 exp(constants[0]) *
619                                 (constants[5] * sin(constants[2]) -
620                                 (2 * constants[6] + constants[4]) * cos(constants[2]));
621
622         ptr->n_p[2] = 2 * exp(constants[0] + constants[1]) *
623                                 ((constants[4] + constants[6]) * cos(constants[3]) *
624                                 cos(constants[2]) - constants[5] *
625                                 cos(constants[3]) * sin(constants[2]) -
626                                 constants[7] * cos(constants[2]) * sin(constants[3])) +
627                                 constants[6] * exp(2 * constants[0]) +
628                                 constants[4] * exp(2 * constants[1]);
629
630         ptr->n_p[3] = exp(constants[1] + 2 * constants[0]) *
631                                 (constants[7] * sin(constants[3]) -
632                                 constants[6] * cos(constants[3])) +
633                                 exp(constants[0] + 2 * constants[1]) *
634                                 (constants[5] * sin(constants[2]) - constants[4] *
635                                 cos(constants[2]));
636         ptr->n_p[4] = 0.0;
637
638         ptr->d_p[0] = 0.0;
639         ptr->d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
640                                 2 * exp(constants[0]) * cos(constants[2]);
641
642         ptr->d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) *
643                                 exp(constants[0] + constants[1]) +
644                                 exp(2 * constants[1]) + exp (2 * constants[0]);
645
646         ptr->d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
647                                 2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
648
649         ptr->d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
650
651         for(i = 0; i < 5; i++) ptr->d_m[i] = ptr->d_p[i];
652
653         ptr->n_m[0] = 0.0;
654         for(i = 1; i <= 4; i++)
655                 ptr->n_m[i] = ptr->n_p[i] - ptr->d_p[i] * ptr->n_p[0];
656
657         double sum_n_p, sum_n_m, sum_d;
658         double a, b;
659
660         sum_n_p = 0.0;
661         sum_n_m = 0.0;
662         sum_d = 0.0;
663         for(i = 0; i < 5; i++)
664         {
665                 sum_n_p += ptr->n_p[i];
666                 sum_n_m += ptr->n_m[i];
667                 sum_d += ptr->d_p[i];
668         }
669
670         a = sum_n_p / (1 + sum_d);
671         b = sum_n_m / (1 + sum_d);
672
673         for (i = 0; i < 5; i++)
674         {
675                 ptr->bd_p[i] = ptr->d_p[i] * a;
676                 ptr->bd_m[i] = ptr->d_m[i] * b;
677         }
678
679         return 0;
680 }
681
682 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
683
684 int BlurEngine::transfer_pixels(pixel_f *src1,
685         pixel_f *src2,
686         pixel_f *src,
687         double *radius,
688         pixel_f *dest,
689         int size)
690 {
691         int i;
692         double sum;
693
694 // printf("BlurEngine::transfer_pixels %d %d %d %d\n",
695 // plugin->config.r,
696 // plugin->config.g,
697 // plugin->config.b,
698 // plugin->config.a);
699
700         for(i = 0; i < size; i++)
701     {
702                 sum = src1[i].r + src2[i].r;
703                 BOUNDARY(sum);
704                 dest[i].r = sum;
705
706                 sum = src1[i].g + src2[i].g;
707                 BOUNDARY(sum);
708                 dest[i].g = sum;
709
710                 sum = src1[i].b + src2[i].b;
711                 BOUNDARY(sum);
712                 dest[i].b = sum;
713
714                 sum = src1[i].a + src2[i].a;
715                 BOUNDARY(sum);
716                 dest[i].a = sum;
717
718 //              if(radius[i] < 2)
719 //              {
720 //                      double scale = 2.0 - (radius[i] * radius[i] - 2.0);
721 //                      dest[i].r /= scale;
722 //                      dest[i].g /= scale;
723 //                      dest[i].b /= scale;
724 //                      dest[i].a /= scale;
725 //              }
726     }
727         return 0;
728 }
729
730
731 int BlurEngine::multiply_alpha(pixel_f *row, int size)
732 {
733 //      register int i;
734 //      register double alpha;
735
736 //      for(i = 0; i < size; i++)
737 //      {
738 //              alpha = (double)row[i].a / vmax;
739 //              row[i].r *= alpha;
740 //              row[i].g *= alpha;
741 //              row[i].b *= alpha;
742 //      }
743         return 0;
744 }
745
746 int BlurEngine::separate_alpha(pixel_f *row, int size)
747 {
748 //      register int i;
749 //      register double alpha;
750 //      register double result;
751
752 //      for(i = 0; i < size; i++)
753 //      {
754 //              if(row[i].a > 0 && row[i].a < vmax)
755 //              {
756 //                      alpha = (double)row[i].a / vmax;
757 //                      result = (double)row[i].r / alpha;
758 //                      row[i].r = (result > vmax ? vmax : result);
759 //                      result = (double)row[i].g / alpha;
760 //                      row[i].g = (result > vmax ? vmax : result);
761 //                      result = (double)row[i].b / alpha;
762 //                      row[i].b = (result > vmax ? vmax : result);
763 //              }
764 //      }
765         return 0;
766 }
767
768 int BlurEngine::blur_strip3(int &size)
769 {
770 //      multiply_alpha(src, size);
771
772         pixel_f *sp_p = src;
773         pixel_f *sp_m = src + size - 1;
774         pixel_f *vp = val_p;
775         pixel_f *vm = val_m + size - 1;
776
777         initial_p = sp_p[0];
778         initial_m = sp_m[0];
779
780         int l;
781         for(int k = 0; k < size; k++)
782         {
783                 terms = (k < 4) ? k : 4;
784
785                 radius[k] = plugin->config.radius;
786
787                 for(l = 0; l <= terms; l++)
788                 {
789                         if(plugin->config.r)
790                         {
791                                 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
792                                 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
793                         }
794                         if(plugin->config.g)
795                         {
796                                 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
797                                 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
798                         }
799                         if(plugin->config.b)
800                         {
801                                 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
802                                 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
803                         }
804                 }
805
806                 for( ; l <= 4; l++)
807                 {
808                         if(plugin->config.r)
809                         {
810                                 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
811                                 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
812                         }
813                         if(plugin->config.g)
814                         {
815                                 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
816                                 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
817                         }
818                         if(plugin->config.b)
819                         {
820                                 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
821                                 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
822                         }
823                 }
824                 sp_p++;
825                 sp_m--;
826                 vp++;
827                 vm--;
828         }
829
830         transfer_pixels(val_p, val_m, src, radius, dst, size);
831 //      separate_alpha(dst, size);
832         return 0;
833 }
834
835
836 int BlurEngine::blur_strip4(int &size)
837 {
838
839 //      multiply_alpha(src, size);
840
841         pixel_f *sp_p = src;
842         pixel_f *sp_m = src + size - 1;
843         pixel_f *vp = val_p;
844         pixel_f *vm = val_m + size - 1;
845
846         initial_p = sp_p[0];
847         initial_m = sp_m[0];
848
849         int l;
850         for(int k = 0; k < size; k++)
851         {
852                 if(plugin->config.a_key)
853                         radius[k] = (double)plugin->config.radius * src[k].a / vmax;
854                 else
855                         radius[k] = plugin->config.radius;
856         }
857
858         for(int k = 0; k < size; k++)
859         {
860                 terms = (k < 4) ? k : 4;
861
862                 if(plugin->config.a_key)
863                 {
864                         if(radius[k] != prev_forward_radius)
865                         {
866                                 prev_forward_radius = radius[k];
867                                 if(radius[k] >= MIN_RADIUS / 2)
868                                 {
869                                         reconfigure(&forward_constants, radius[k]);
870                                 }
871                                 else
872                                 {
873                                         reconfigure(&forward_constants, (double)MIN_RADIUS / 2);
874                                 }
875                         }
876
877                         if(radius[size - 1 - k] != prev_reverse_radius)
878                         {
879 //printf("BlurEngine::blur_strip4 %f\n", sp_m->a);
880                                 prev_reverse_radius = radius[size - 1 - k];
881                                 if(radius[size - 1 - k] >= MIN_RADIUS / 2)
882                                 {
883                                         reconfigure(&reverse_constants, radius[size - 1 - k]);
884                                 }
885                                 else
886                                 {
887                                         reconfigure(&reverse_constants, (double)MIN_RADIUS / 2);
888                                 }
889                         }
890
891 // Force alpha to be copied regardless of alpha blur enabled
892                         vp->a = sp_p->a;
893                         vm->a = sp_m->a;
894                 }
895
896
897                 for(l = 0; l <= terms; l++)
898                 {
899                         if(plugin->config.r)
900                         {
901                                 vp->r += forward_constants.n_p[l] * sp_p[-l].r - forward_constants.d_p[l] * vp[-l].r;
902                                 vm->r += reverse_constants.n_m[l] * sp_m[l].r - reverse_constants.d_m[l] * vm[l].r;
903                         }
904                         if(plugin->config.g)
905                         {
906                                 vp->g += forward_constants.n_p[l] * sp_p[-l].g - forward_constants.d_p[l] * vp[-l].g;
907                                 vm->g += reverse_constants.n_m[l] * sp_m[l].g - reverse_constants.d_m[l] * vm[l].g;
908                         }
909                         if(plugin->config.b)
910                         {
911                                 vp->b += forward_constants.n_p[l] * sp_p[-l].b - forward_constants.d_p[l] * vp[-l].b;
912                                 vm->b += reverse_constants.n_m[l] * sp_m[l].b - reverse_constants.d_m[l] * vm[l].b;
913                         }
914                         if(plugin->config.a && !plugin->config.a_key)
915                         {
916                                 vp->a += forward_constants.n_p[l] * sp_p[-l].a - forward_constants.d_p[l] * vp[-l].a;
917                                 vm->a += reverse_constants.n_m[l] * sp_m[l].a - reverse_constants.d_m[l] * vm[l].a;
918                         }
919                 }
920
921                 for( ; l <= 4; l++)
922                 {
923                         if(plugin->config.r)
924                         {
925                                 vp->r += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.r;
926                                 vm->r += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.r;
927                         }
928                         if(plugin->config.g)
929                         {
930                                 vp->g += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.g;
931                                 vm->g += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.g;
932                         }
933                         if(plugin->config.b)
934                         {
935                                 vp->b += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.b;
936                                 vm->b += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.b;
937                         }
938                         if(plugin->config.a && !plugin->config.a_key)
939                         {
940                                 vp->a += (forward_constants.n_p[l] - forward_constants.bd_p[l]) * initial_p.a;
941                                 vm->a += (reverse_constants.n_m[l] - reverse_constants.bd_m[l]) * initial_m.a;
942                         }
943                 }
944
945                 sp_p++;
946                 sp_m--;
947                 vp++;
948                 vm--;
949         }
950
951         transfer_pixels(val_p, val_m, src, radius, dst, size);
952 //      separate_alpha(dst, size);
953         return 0;
954 }
955
956
957
958
959
960