r370: Heroine Virutal's official release 1.2.1
[cinelerra_cv/mob.git] / hvirtual / plugins / blur / blur.C
blob70413d428d92788460f7cb7369e037cb4f66f64d
1 #include "filexml.h"
2 #include "blur.h"
3 #include "blurwindow.h"
4 #include "defaults.h"
5 #include "keyframe.h"
6 #include "language.h"
7 #include "picon_png.h"
8 #include "vframe.h"
10 #include <math.h>
11 #include <stdint.h>
12 #include <string.h>
20 BlurConfig::BlurConfig()
22         vertical = 1;
23         horizontal = 1;
24         radius = 5;
25         a = r = g = b = 1;
28 int BlurConfig::equivalent(BlurConfig &that)
30         return (vertical == that.vertical && 
31                 horizontal == that.horizontal && 
32                 radius == that.radius &&
33                 a == that.a &&
34                 r == that.r &&
35                 g == that.g &&
36                 b == that.b);
39 void BlurConfig::copy_from(BlurConfig &that)
41         vertical = that.vertical;
42         horizontal = that.horizontal;
43         radius = that.radius;
44         a = that.a;
45         r = that.r;
46         g = that.g;
47         b = that.b;
50 void BlurConfig::interpolate(BlurConfig &prev, 
51         BlurConfig &next, 
52         int64_t prev_frame, 
53         int64_t next_frame, 
54         int64_t current_frame)
56         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
57         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
60 //printf("BlurConfig::interpolate %d %d %d\n", prev_frame, next_frame, current_frame);
61         this->vertical = (int)(prev.vertical * prev_scale + next.vertical * next_scale);
62         this->horizontal = (int)(prev.horizontal * prev_scale + next.horizontal * next_scale);
63         this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale);
64         a = prev.a;
65         r = prev.r;
66         g = prev.g;
67         b = prev.b;
75 REGISTER_PLUGIN(BlurMain)
84 BlurMain::BlurMain(PluginServer *server)
85  : PluginVClient(server)
87         defaults = 0;
88         temp = 0;
89         need_reconfigure = 1;
90         engine = 0;
91         PLUGIN_CONSTRUCTOR_MACRO
94 BlurMain::~BlurMain()
96 //printf("BlurMain::~BlurMain 1\n");
97         PLUGIN_DESTRUCTOR_MACRO
99         if(temp) delete temp;
100         if(engine)
101         {
102                 for(int i = 0; i < (PluginClient::smp + 1); i++)
103                         delete engine[i];
104                 delete [] engine;
105         }
108 char* BlurMain::plugin_title() { return N_("Blur"); }
109 int BlurMain::is_realtime() { return 1; }
112 NEW_PICON_MACRO(BlurMain)
114 SHOW_GUI_MACRO(BlurMain, BlurThread)
116 SET_STRING_MACRO(BlurMain)
118 RAISE_WINDOW_MACRO(BlurMain)
120 LOAD_CONFIGURATION_MACRO(BlurMain, BlurConfig)
125 int BlurMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
127         int i, j, k, l;
128         unsigned char **input_rows, **output_rows;
130         this->input = input_ptr;
131         this->output = output_ptr;
132         need_reconfigure |= load_configuration();
135 //printf("BlurMain::process_realtime 1 %d %d\n", need_reconfigure, config.radius);
136         if(need_reconfigure)
137         {
138                 int y1, y2, y_increment;
140                 if(!engine)
141                 {
142                         y_increment = input->get_h() / (smp + 1);
143                         y1 = 0;
145                         engine = new BlurEngine*[(PluginClient::smp + 1)];
146                         for(int i = 0; i < (PluginClient::smp + 1); i++)
147                         {
148                                 y2 = y1 + y_increment;
149                                 if(i == (PluginClient::smp + 1) - 1 && 
150                                         y2 < input->get_h() - 1) 
151                                         y2 = input->get_h() - 1;
153                                 engine[i] = new BlurEngine(this, y1, y2);
154                                 engine[i]->start();
155                                 y1 += y_increment;
156                         }
157                 }
159                 for(i = 0; i < (PluginClient::smp + 1); i++)
160                         engine[i]->reconfigure();
161                 need_reconfigure = 0;
162         }
165         if(temp && 
166                 (temp->get_w() != input_ptr->get_w() ||
167                 temp->get_h() != input_ptr->get_h()))
168         {
169                 delete temp;
170                 temp = 0;
171         }
173         if(!temp)
174                 temp = new VFrame(0,
175                         input_ptr->get_w(),
176                         input_ptr->get_h(),
177                         input_ptr->get_color_model());
179         input_rows = input_ptr->get_rows();
180         output_rows = output_ptr->get_rows();
182         if(config.radius < 2 || 
183                 (!config.vertical && !config.horizontal))
184         {
185 // Data never processed so copy if necessary
186 //printf("BlurMain::process_realtime 2 %d\n", radius);
187                 if(input_rows[0] != output_rows[0])
188                 {
189                         output_ptr->copy_from(input_ptr);
190                 }
191         }
192         else
193         {
194 // Process blur
195 // TODO
196 // Can't blur recursively.  Need to blur to a temp and 
197 // horizontally to the output in 2 discrete passes.
198 //printf("BlurMain::process_realtime 3 %d\n", (smp + 1));
199                 for(i = 0; i < (smp + 1); i++)
200                 {
201                         engine[i]->start_process_frame(output_ptr, input_ptr);
202                 }
204                 for(i = 0; i < (smp + 1); i++)
205                 {
206                         engine[i]->wait_process_frame();
207                 }
208         }
210         return 0;
214 void BlurMain::update_gui()
216         if(thread)
217         {
218                 load_configuration();
219                 thread->window->lock_window();
220                 thread->window->horizontal->update(config.horizontal);
221                 thread->window->vertical->update(config.vertical);
222                 thread->window->radius->update(config.radius);
223                 thread->window->a->update(config.a);
224                 thread->window->r->update(config.r);
225                 thread->window->g->update(config.g);
226                 thread->window->b->update(config.b);
227                 thread->window->unlock_window();
228         }
232 int BlurMain::load_defaults()
234         char directory[1024], string[1024];
235 // set the default directory
236         sprintf(directory, "%sblur.rc", BCASTDIR);
238 // load the defaults
239         defaults = new Defaults(directory);
240         defaults->load();
242         config.vertical = defaults->get("VERTICAL", config.vertical);
243         config.horizontal = defaults->get("HORIZONTAL", config.horizontal);
244         config.radius = defaults->get("RADIUS", config.radius);
245         config.r = defaults->get("R", config.r);
246         config.g = defaults->get("G", config.g);
247         config.b = defaults->get("B", config.b);
248         config.a = defaults->get("A", config.a);
249         return 0;
253 int BlurMain::save_defaults()
255         defaults->update("VERTICAL", config.vertical);
256         defaults->update("HORIZONTAL", config.horizontal);
257         defaults->update("RADIUS", config.radius);
258         defaults->update("R", config.r);
259         defaults->update("G", config.g);
260         defaults->update("B", config.b);
261         defaults->update("A", config.a);
262         defaults->save();
263         return 0;
268 void BlurMain::save_data(KeyFrame *keyframe)
270         FileXML output;
272 // cause data to be stored directly in text
273         output.set_shared_string(keyframe->data, MESSAGESIZE);
274         output.tag.set_title("BLUR");
275         output.tag.set_property("VERTICAL", config.vertical);
276         output.tag.set_property("HORIZONTAL", config.horizontal);
277         output.tag.set_property("RADIUS", config.radius);
278         output.tag.set_property("R", config.r);
279         output.tag.set_property("G", config.g);
280         output.tag.set_property("B", config.b);
281         output.tag.set_property("A", config.a);
282         output.append_tag();
283         output.terminate_string();
286 void BlurMain::read_data(KeyFrame *keyframe)
288         FileXML input;
290         input.set_shared_string(keyframe->data, strlen(keyframe->data));
292         int result = 0;
294         while(!result)
295         {
296                 result = input.read_tag();
298                 if(!result)
299                 {
300                         if(input.tag.title_is("BLUR"))
301                         {
302                                 config.vertical = input.tag.get_property("VERTICAL", config.vertical);
303                                 config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
304                                 config.radius = input.tag.get_property("RADIUS", config.radius);
305 //printf("BlurMain::read_data 1 %d %d %s\n", get_source_position(), keyframe->position, keyframe->data);
306                                 config.r = input.tag.get_property("R", config.r);
307                                 config.g = input.tag.get_property("G", config.g);
308                                 config.b = input.tag.get_property("B", config.b);
309                                 config.a = input.tag.get_property("A", config.a);
310                         }
311                 }
312         }
323 BlurEngine::BlurEngine(BlurMain *plugin, int start_out, int end_out)
324  : Thread()
326         int size = plugin->input->get_w() > plugin->input->get_h() ? 
327                 plugin->input->get_w() : plugin->input->get_h();
328         this->plugin = plugin;
329         this->start_out = start_out;
330         this->end_out = end_out;
331         last_frame = 0;
332         val_p = new pixel_f[size];
333         val_m = new pixel_f[size];
334         src = new pixel_f[size];
335         dst = new pixel_f[size];
336         set_synchronous(1);
337         input_lock.lock();
338         output_lock.lock();
341 BlurEngine::~BlurEngine()
343         last_frame = 1;
344         input_lock.unlock();
345         join();
348 int BlurEngine::start_process_frame(VFrame *output, VFrame *input)
350         this->output = output;
351         this->input = input;
352         input_lock.unlock();
353         return 0;
356 int BlurEngine::wait_process_frame()
358         output_lock.lock();
359         return 0;
362 void BlurEngine::run()
364         int i, j, k, l;
365         int strip_size;
367         while(1)
368         {
369                 input_lock.lock();
370                 if(last_frame)
371                 {
372                         output_lock.unlock();
373                         return;
374                 }
376                 start_in = start_out - plugin->config.radius;
377                 end_in = end_out + plugin->config.radius;
378                 if(start_in < 0) start_in = 0;
379                 if(end_in > plugin->input->get_h()) end_in = plugin->input->get_h();
380                 strip_size = end_in - start_in;
381                 color_model = input->get_color_model();
382                 int w = input->get_w();
383                 int h = input->get_h();
389 #define BLUR(type, max, components) \
390 { \
391         type **input_rows = (type **)input->get_rows(); \
392         type **output_rows = (type **)output->get_rows(); \
393         type **current_input = input_rows; \
394         type **current_output = output_rows; \
395         vmax = max; \
397         if(plugin->config.vertical) \
398         { \
399 /* Vertical pass */ \
400                 if(plugin->config.horizontal) \
401                 { \
402                         current_output = (type **)plugin->temp->get_rows(); \
403                 } \
405                 for(j = 0; j < w; j++) \
406                 { \
407                         bzero(val_p, sizeof(pixel_f) * (end_in - start_in)); \
408                         bzero(val_m, sizeof(pixel_f) * (end_in - start_in)); \
410                         for(l = 0, k = start_in; k < end_in; l++, k++) \
411                         { \
412                                 if(plugin->config.r) src[l].r = (float)current_input[k][j * components]; \
413                                 if(plugin->config.g) src[l].g = (float)current_input[k][j * components + 1]; \
414                                 if(plugin->config.b) src[l].b = (float)current_input[k][j * components + 2]; \
415                                 if(components == 4) \
416                                         if(plugin->config.a) src[l].a = (float)current_input[k][j * components + 3]; \
417                         } \
419                         if(components == 4) \
420                                 blur_strip4(strip_size); \
421                         else \
422                                 blur_strip3(strip_size); \
424                         for(l = start_out - start_in, k = start_out; k < end_out; l++, k++) \
425                         { \
426                                 if(plugin->config.r) current_output[k][j * components] = (type)dst[l].r; \
427                                 if(plugin->config.g) current_output[k][j * components + 1] = (type)dst[l].g; \
428                                 if(plugin->config.b) current_output[k][j * components + 2] = (type)dst[l].b; \
429                                 if(components == 4) \
430                                         if(plugin->config.a) current_output[k][j * components + 3] = (type)dst[l].a; \
431                         } \
432                 } \
434                 current_input = current_output; \
435                 current_output = output_rows; \
436         } \
439         if(plugin->config.horizontal) \
440         { \
441 /* Horizontal pass */ \
442                 for(j = start_out; j < end_out; j++) \
443                 { \
444                         bzero(val_p, sizeof(pixel_f) * w); \
445                         bzero(val_m, sizeof(pixel_f) * w); \
447                         for(k = 0; k < w; k++) \
448                         { \
449                                 if(plugin->config.r) src[k].r = (float)current_input[j][k * components]; \
450                                 if(plugin->config.g) src[k].g = (float)current_input[j][k * components + 1]; \
451                                 if(plugin->config.b) src[k].b = (float)current_input[j][k * components + 2]; \
452                                 if(components == 4) \
453                                         if(plugin->config.a) src[k].a = (float)current_input[j][k * components + 3]; \
454                         } \
456                         if(components == 4) \
457                                 blur_strip4(w); \
458                         else \
459                                 blur_strip3(w); \
461                         for(k = 0; k < w; k++) \
462                         { \
463                                 if(plugin->config.r) current_output[j][k * components] = (type)dst[k].r; \
464                                 if(plugin->config.g) current_output[j][k * components + 1] = (type)dst[k].g; \
465                                 if(plugin->config.b) current_output[j][k * components + 2] = (type)dst[k].b; \
466                                 if(components == 4) \
467                                         if(plugin->config.a) current_output[j][k * components + 3] = (type)dst[k].a; \
468                         } \
469                 } \
470         } \
475                 switch(color_model)
476                 {
477                         case BC_RGB888:
478                         case BC_YUV888:
479                                 BLUR(unsigned char, 0xff, 3);
480                                 break;
481                         case BC_RGB_FLOAT:
482                                 BLUR(float, 1.0, 3);
483                                 break;
484                         case BC_RGBA8888:
485                         case BC_YUVA8888:
486                                 BLUR(unsigned char, 0xff, 4);
487                                 break;
488                         case BC_RGBA_FLOAT:
489                                 BLUR(float, 1.0, 4);
490                                 break;
491                         case BC_RGB161616:
492                         case BC_YUV161616:
493                                 BLUR(uint16_t, 0xffff, 3);
494                                 break;
495                         case BC_RGBA16161616:
496                         case BC_YUVA16161616:
497                                 BLUR(uint16_t, 0xffff, 4);
498                                 break;
499                 }
501                 output_lock.unlock();
502         }
505 int BlurEngine::reconfigure()
507         std_dev = sqrt(-(double)(plugin->config.radius * plugin->config.radius) / 
508                 (2 * log (1.0 / 255.0)));
509         get_constants();
512 int BlurEngine::get_constants()
514         int i;
515         double constants[8];
516         double div;
518         div = sqrt(2 * M_PI) * std_dev;
519         constants[0] = -1.783 / std_dev;
520         constants[1] = -1.723 / std_dev;
521         constants[2] = 0.6318 / std_dev;
522         constants[3] = 1.997  / std_dev;
523         constants[4] = 1.6803 / div;
524         constants[5] = 3.735 / div;
525         constants[6] = -0.6803 / div;
526         constants[7] = -0.2598 / div;
528         n_p[0] = constants[4] + constants[6];
529         n_p[1] = exp(constants[1]) *
530                                 (constants[7] * sin(constants[3]) -
531                                 (constants[6] + 2 * constants[4]) * cos(constants[3])) +
532                                 exp(constants[0]) *
533                                 (constants[5] * sin(constants[2]) -
534                                 (2 * constants[6] + constants[4]) * cos(constants[2]));
536         n_p[2] = 2 * exp(constants[0] + constants[1]) *
537                                 ((constants[4] + constants[6]) * cos(constants[3]) * 
538                                 cos(constants[2]) - constants[5] * 
539                                 cos(constants[3]) * sin(constants[2]) -
540                                 constants[7] * cos(constants[2]) * sin(constants[3])) +
541                                 constants[6] * exp(2 * constants[0]) +
542                                 constants[4] * exp(2 * constants[1]);
544         n_p[3] = exp(constants[1] + 2 * constants[0]) *
545                                 (constants[7] * sin(constants[3]) - 
546                                 constants[6] * cos(constants[3])) +
547                                 exp(constants[0] + 2 * constants[1]) *
548                                 (constants[5] * sin(constants[2]) - constants[4] * 
549                                 cos(constants[2]));
550         n_p[4] = 0.0;
552         d_p[0] = 0.0;
553         d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
554                                 2 * exp(constants[0]) * cos(constants[2]);
556         d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) * 
557                                 exp(constants[0] + constants[1]) +
558                                 exp(2 * constants[1]) + exp (2 * constants[0]);
560         d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
561                                 2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
563         d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
565         for(i = 0; i < 5; i++) d_m[i] = d_p[i];
567         n_m[0] = 0.0;
568         for(i = 1; i <= 4; i++)
569                 n_m[i] = n_p[i] - d_p[i] * n_p[0];
571         double sum_n_p, sum_n_m, sum_d;
572         double a, b;
574         sum_n_p = 0.0;
575         sum_n_m = 0.0;
576         sum_d = 0.0;
577         for(i = 0; i < 5; i++)
578         {
579                 sum_n_p += n_p[i];
580                 sum_n_m += n_m[i];
581                 sum_d += d_p[i];
582         }
584         a = sum_n_p / (1 + sum_d);
585         b = sum_n_m / (1 + sum_d);
587         for (i = 0; i < 5; i++)
588         {
589                 bd_p[i] = d_p[i] * a;
590                 bd_m[i] = d_m[i] * b;
591         }
592         return 0;
595 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
597 int BlurEngine::transfer_pixels(pixel_f *src1, pixel_f *src2, pixel_f *dest, int size)
599         int i;
600         float sum;
602 // printf("BlurEngine::transfer_pixels %d %d %d %d\n", 
603 // plugin->config.r, 
604 // plugin->config.g, 
605 // plugin->config.b, 
606 // plugin->config.a);
608         for(i = 0; i < size; i++)
609     {
610                 sum = src1[i].r + src2[i].r;
611                 BOUNDARY(sum);
612                 dest[i].r = sum;
613                 sum = src1[i].g + src2[i].g;
614                 BOUNDARY(sum);
615                 dest[i].g = sum;
616                 sum = src1[i].b + src2[i].b;
617                 BOUNDARY(sum);
618                 dest[i].b = sum;
619                 sum = src1[i].a + src2[i].a;
620                 BOUNDARY(sum);
621                 dest[i].a = sum;
622     }
623         return 0;
627 int BlurEngine::multiply_alpha(pixel_f *row, int size)
629         register int i;
630         register float alpha;
632 //      for(i = 0; i < size; i++)
633 //      {
634 //              alpha = (float)row[i].a / vmax;
635 //              row[i].r *= alpha;
636 //              row[i].g *= alpha;
637 //              row[i].b *= alpha;
638 //      }
639         return 0;
642 int BlurEngine::separate_alpha(pixel_f *row, int size)
644         register int i;
645         register float alpha;
646         register float result;
647         
648 //      for(i = 0; i < size; i++)
649 //      {
650 //              if(row[i].a > 0 && row[i].a < vmax)
651 //              {
652 //                      alpha = (float)row[i].a / vmax;
653 //                      result = (float)row[i].r / alpha;
654 //                      row[i].r = (result > vmax ? vmax : result);
655 //                      result = (float)row[i].g / alpha;
656 //                      row[i].g = (result > vmax ? vmax : result);
657 //                      result = (float)row[i].b / alpha;
658 //                      row[i].b = (result > vmax ? vmax : result);
659 //              }
660 //      }
661         return 0;
664 int BlurEngine::blur_strip3(int &size)
666         multiply_alpha(src, size);
668         sp_p = src;
669         sp_m = src + size - 1;
670         vp = val_p;
671         vm = val_m + size - 1;
673         initial_p = sp_p[0];
674         initial_m = sp_m[0];
676         int l;
677         for(int k = 0; k < size; k++)
678         {
679                 terms = (k < 4) ? k : 4;
680                 for(l = 0; l <= terms; l++)
681                 {
682                         if(plugin->config.r)
683                         {
684                                 vp->r += n_p[l] * sp_p[-l].r - d_p[l] * vp[-l].r;
685                                 vm->r += n_m[l] * sp_m[l].r - d_m[l] * vm[l].r;
686                         }
687                         if(plugin->config.g)
688                         {
689                                 vp->g += n_p[l] * sp_p[-l].g - d_p[l] * vp[-l].g;
690                                 vm->g += n_m[l] * sp_m[l].g - d_m[l] * vm[l].g;
691                         }
692                         if(plugin->config.b)
693                         {
694                                 vp->b += n_p[l] * sp_p[-l].b - d_p[l] * vp[-l].b;
695                                 vm->b += n_m[l] * sp_m[l].b - d_m[l] * vm[l].b;
696                         }
697                 }
698                 for( ; l <= 4; l++)
699                 {
700                         if(plugin->config.r)
701                         {
702                                 vp->r += (n_p[l] - bd_p[l]) * initial_p.r;
703                                 vm->r += (n_m[l] - bd_m[l]) * initial_m.r;
704                         }
705                         if(plugin->config.g)
706                         {
707                                 vp->g += (n_p[l] - bd_p[l]) * initial_p.g;
708                                 vm->g += (n_m[l] - bd_m[l]) * initial_m.g;
709                         }
710                         if(plugin->config.b)
711                         {
712                                 vp->b += (n_p[l] - bd_p[l]) * initial_p.b;
713                                 vm->b += (n_m[l] - bd_m[l]) * initial_m.b;
714                         }
715                 }
716                 sp_p++;
717                 sp_m--;
718                 vp++;
719                 vm--;
720         }
721         transfer_pixels(val_p, val_m, dst, size);
722         separate_alpha(dst, size);
723         return 0;
727 int BlurEngine::blur_strip4(int &size)
729         multiply_alpha(src, size);
731         sp_p = src;
732         sp_m = src + size - 1;
733         vp = val_p;
734         vm = val_m + size - 1;
736         initial_p = sp_p[0];
737         initial_m = sp_m[0];
739         int l;
740         for(int k = 0; k < size; k++)
741         {
742                 terms = (k < 4) ? k : 4;
744                 for(l = 0; l <= terms; l++)
745                 {
746                         if(plugin->config.r)
747                         {
748                                 vp->r += n_p[l] * sp_p[-l].r - d_p[l] * vp[-l].r;
749                                 vm->r += n_m[l] * sp_m[l].r - d_m[l] * vm[l].r;
750                         }
751                         if(plugin->config.g)
752                         {
753                                 vp->g += n_p[l] * sp_p[-l].g - d_p[l] * vp[-l].g;
754                                 vm->g += n_m[l] * sp_m[l].g - d_m[l] * vm[l].g;
755                         }
756                         if(plugin->config.b)
757                         {
758                                 vp->b += n_p[l] * sp_p[-l].b - d_p[l] * vp[-l].b;
759                                 vm->b += n_m[l] * sp_m[l].b - d_m[l] * vm[l].b;
760                         }
761                         if(plugin->config.a)
762                         {
763                                 vp->a += n_p[l] * sp_p[-l].a - d_p[l] * vp[-l].a;
764                                 vm->a += n_m[l] * sp_m[l].a - d_m[l] * vm[l].a;
765                         }
766                 }
768                 for( ; l <= 4; l++)
769                 {
770                         if(plugin->config.r)
771                         {
772                                 vp->r += (n_p[l] - bd_p[l]) * initial_p.r;
773                                 vm->r += (n_m[l] - bd_m[l]) * initial_m.r;
774                         }
775                         if(plugin->config.g)
776                         {
777                                 vp->g += (n_p[l] - bd_p[l]) * initial_p.g;
778                                 vm->g += (n_m[l] - bd_m[l]) * initial_m.g;
779                         }
780                         if(plugin->config.b)
781                         {
782                                 vp->b += (n_p[l] - bd_p[l]) * initial_p.b;
783                                 vm->b += (n_m[l] - bd_m[l]) * initial_m.b;
784                         }
785                         if(plugin->config.a)
786                         {
787                                 vp->a += (n_p[l] - bd_p[l]) * initial_p.a;
788                                 vm->a += (n_m[l] - bd_m[l]) * initial_m.a;
789                         }
790                 }
792                 sp_p++;
793                 sp_m--;
794                 vp++;
795                 vm--;
796         }
797         transfer_pixels(val_p, val_m, dst, size);
798         separate_alpha(dst, size);
799         return 0;