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