Fixed initialisation of tf in file_open(). Without setting the memory to 0,
[cinelerra_cv/mob.git] / plugins / blur / blur.C
bloba7f98e638296ec99bc9ab2d6431e0bb7bce99696
1 #include "filexml.h"
2 #include "blur.h"
3 #include "blurwindow.h"
4 #include "bchash.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 < (get_project_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                         engine = new BlurEngine*[(get_project_smp() + 1)];
143                         for(int i = 0; i < (get_project_smp() + 1); i++)
144                         {
145                                 engine[i] = new BlurEngine(this, 
146                                         input->get_h() * i / (get_project_smp() + 1), 
147                                         input->get_h() * (i + 1) / (get_project_smp() + 1));
148                                 engine[i]->start();
149                         }
150                 }
152                 for(i = 0; i < (get_project_smp() + 1); i++)
153                         engine[i]->reconfigure();
154                 need_reconfigure = 0;
155         }
158         if(temp && 
159                 (temp->get_w() != input_ptr->get_w() ||
160                 temp->get_h() != input_ptr->get_h()))
161         {
162                 delete temp;
163                 temp = 0;
164         }
166         if(!temp)
167                 temp = new VFrame(0,
168                         input_ptr->get_w(),
169                         input_ptr->get_h(),
170                         input_ptr->get_color_model());
172         input_rows = input_ptr->get_rows();
173         output_rows = output_ptr->get_rows();
175         if(config.radius < 2 || 
176                 (!config.vertical && !config.horizontal))
177         {
178 // Data never processed so copy if necessary
179                 if(input_rows[0] != output_rows[0])
180                 {
181                         output_ptr->copy_from(input_ptr);
182                 }
183         }
184         else
185         {
186 // Process blur
187 // TODO
188 // Can't blur recursively.  Need to blur vertically to a temp and 
189 // horizontally to the output in 2 discrete passes.
190                 for(i = 0; i < (get_project_smp() + 1); i++)
191                 {
192                         engine[i]->start_process_frame(output_ptr, input_ptr);
193                 }
195                 for(i = 0; i < (get_project_smp() + 1); i++)
196                 {
197                         engine[i]->wait_process_frame();
198                 }
199         }
201         return 0;
205 void BlurMain::update_gui()
207         if(thread)
208         {
209                 load_configuration();
210                 thread->window->lock_window();
211                 thread->window->horizontal->update(config.horizontal);
212                 thread->window->vertical->update(config.vertical);
213                 thread->window->radius->update(config.radius);
214                 thread->window->a->update(config.a);
215                 thread->window->r->update(config.r);
216                 thread->window->g->update(config.g);
217                 thread->window->b->update(config.b);
218                 thread->window->unlock_window();
219         }
223 int BlurMain::load_defaults()
225         char directory[1024], string[1024];
226 // set the default directory
227         sprintf(directory, "%sblur.rc", BCASTDIR);
229 // load the defaults
230         defaults = new BC_Hash(directory);
231         defaults->load();
233         config.vertical = defaults->get("VERTICAL", config.vertical);
234         config.horizontal = defaults->get("HORIZONTAL", config.horizontal);
235         config.radius = defaults->get("RADIUS", config.radius);
236         config.r = defaults->get("R", config.r);
237         config.g = defaults->get("G", config.g);
238         config.b = defaults->get("B", config.b);
239         config.a = defaults->get("A", config.a);
240         return 0;
244 int BlurMain::save_defaults()
246         defaults->update("VERTICAL", config.vertical);
247         defaults->update("HORIZONTAL", config.horizontal);
248         defaults->update("RADIUS", config.radius);
249         defaults->update("R", config.r);
250         defaults->update("G", config.g);
251         defaults->update("B", config.b);
252         defaults->update("A", config.a);
253         defaults->save();
254         return 0;
259 void BlurMain::save_data(KeyFrame *keyframe)
261         FileXML output;
263 // cause data to be stored directly in text
264         output.set_shared_string(keyframe->data, MESSAGESIZE);
265         output.tag.set_title("BLUR");
266         output.tag.set_property("VERTICAL", config.vertical);
267         output.tag.set_property("HORIZONTAL", config.horizontal);
268         output.tag.set_property("RADIUS", config.radius);
269         output.tag.set_property("R", config.r);
270         output.tag.set_property("G", config.g);
271         output.tag.set_property("B", config.b);
272         output.tag.set_property("A", config.a);
273         output.append_tag();
274         output.terminate_string();
277 void BlurMain::read_data(KeyFrame *keyframe)
279         FileXML input;
281         input.set_shared_string(keyframe->data, strlen(keyframe->data));
283         int result = 0;
285         while(!result)
286         {
287                 result = input.read_tag();
289                 if(!result)
290                 {
291                         if(input.tag.title_is("BLUR"))
292                         {
293                                 config.vertical = input.tag.get_property("VERTICAL", config.vertical);
294                                 config.horizontal = input.tag.get_property("HORIZONTAL", config.horizontal);
295                                 config.radius = input.tag.get_property("RADIUS", config.radius);
296 //printf("BlurMain::read_data 1 %d %d %s\n", get_source_position(), keyframe->position, keyframe->data);
297                                 config.r = input.tag.get_property("R", config.r);
298                                 config.g = input.tag.get_property("G", config.g);
299                                 config.b = input.tag.get_property("B", config.b);
300                                 config.a = input.tag.get_property("A", config.a);
301                         }
302                 }
303         }
314 BlurEngine::BlurEngine(BlurMain *plugin, int start_out, int end_out)
315  : Thread()
317         int size = plugin->input->get_w() > plugin->input->get_h() ? 
318                 plugin->input->get_w() : plugin->input->get_h();
319         this->plugin = plugin;
320         this->start_out = start_out;
321         this->end_out = end_out;
322         last_frame = 0;
323         val_p = new pixel_f[size];
324         val_m = new pixel_f[size];
325         src = new pixel_f[size];
326         dst = new pixel_f[size];
327         set_synchronous(1);
328         input_lock.lock();
329         output_lock.lock();
332 BlurEngine::~BlurEngine()
334         last_frame = 1;
335         input_lock.unlock();
336         join();
339 int BlurEngine::start_process_frame(VFrame *output, VFrame *input)
341         this->output = output;
342         this->input = input;
343         input_lock.unlock();
344         return 0;
347 int BlurEngine::wait_process_frame()
349         output_lock.lock();
350         return 0;
353 void BlurEngine::run()
355         int i, j, k, l;
356         int strip_size;
359         while(1)
360         {
361                 input_lock.lock();
362                 if(last_frame)
363                 {
364                         output_lock.unlock();
365                         return;
366                 }
368                 start_in = start_out - plugin->config.radius;
369                 end_in = end_out + plugin->config.radius;
370                 if(start_in < 0) start_in = 0;
371                 if(end_in > plugin->input->get_h()) end_in = plugin->input->get_h();
372                 strip_size = end_in - start_in;
373                 color_model = input->get_color_model();
374                 int w = input->get_w();
375                 int h = input->get_h();
381 #define BLUR(type, max, components) \
382 { \
383         type **input_rows = (type **)input->get_rows(); \
384         type **output_rows = (type **)output->get_rows(); \
385         type **current_input = input_rows; \
386         type **current_output = output_rows; \
387         vmax = max; \
389         if(plugin->config.vertical) \
390         { \
391 /* Vertical pass */ \
392                 if(plugin->config.horizontal) \
393                 { \
394                         current_output = (type **)plugin->temp->get_rows(); \
395                 } \
397                 for(j = 0; j < w; j++) \
398                 { \
399                         bzero(val_p, sizeof(pixel_f) * (end_in - start_in)); \
400                         bzero(val_m, sizeof(pixel_f) * (end_in - start_in)); \
402                         for(l = 0, k = start_in; k < end_in; l++, k++) \
403                         { \
404                                 if(plugin->config.r) src[l].r = (float)current_input[k][j * components]; \
405                                 if(plugin->config.g) src[l].g = (float)current_input[k][j * components + 1]; \
406                                 if(plugin->config.b) src[l].b = (float)current_input[k][j * components + 2]; \
407                                 if(components == 4) \
408                                         if(plugin->config.a) src[l].a = (float)current_input[k][j * components + 3]; \
409                         } \
411                         if(components == 4) \
412                                 blur_strip4(strip_size); \
413                         else \
414                                 blur_strip3(strip_size); \
416                         for(l = start_out - start_in, k = start_out; k < end_out; l++, k++) \
417                         { \
418                                 if(plugin->config.r) current_output[k][j * components] = (type)dst[l].r; \
419                                 if(plugin->config.g) current_output[k][j * components + 1] = (type)dst[l].g; \
420                                 if(plugin->config.b) current_output[k][j * components + 2] = (type)dst[l].b; \
421                                 if(components == 4) \
422                                         if(plugin->config.a) current_output[k][j * components + 3] = (type)dst[l].a; \
423                         } \
424                 } \
426                 current_input = current_output; \
427                 current_output = output_rows; \
428         } \
431         if(plugin->config.horizontal) \
432         { \
433 /* Horizontal pass */ \
434                 for(j = start_out; j < end_out; j++) \
435                 { \
436                         bzero(val_p, sizeof(pixel_f) * w); \
437                         bzero(val_m, sizeof(pixel_f) * w); \
439                         for(k = 0; k < w; k++) \
440                         { \
441                                 if(plugin->config.r) src[k].r = (float)current_input[j][k * components]; \
442                                 if(plugin->config.g) src[k].g = (float)current_input[j][k * components + 1]; \
443                                 if(plugin->config.b) src[k].b = (float)current_input[j][k * components + 2]; \
444                                 if(components == 4) \
445                                         if(plugin->config.a) src[k].a = (float)current_input[j][k * components + 3]; \
446                         } \
448                         if(components == 4) \
449                                 blur_strip4(w); \
450                         else \
451                                 blur_strip3(w); \
453                         for(k = 0; k < w; k++) \
454                         { \
455                                 if(plugin->config.r) current_output[j][k * components] = (type)dst[k].r; \
456                                 if(plugin->config.g) current_output[j][k * components + 1] = (type)dst[k].g; \
457                                 if(plugin->config.b) current_output[j][k * components + 2] = (type)dst[k].b; \
458                                 if(components == 4) \
459                                         if(plugin->config.a) current_output[j][k * components + 3] = (type)dst[k].a; \
460                         } \
461                 } \
462         } \
467                 switch(color_model)
468                 {
469                         case BC_RGB888:
470                         case BC_YUV888:
471                                 BLUR(unsigned char, 0xff, 3);
472                                 break;
473                         case BC_RGB_FLOAT:
474                                 BLUR(float, 1.0, 3);
475                                 break;
476                         case BC_RGBA8888:
477                         case BC_YUVA8888:
478                                 BLUR(unsigned char, 0xff, 4);
479                                 break;
480                         case BC_RGBA_FLOAT:
481                                 BLUR(float, 1.0, 4);
482                                 break;
483                         case BC_RGB161616:
484                         case BC_YUV161616:
485                                 BLUR(uint16_t, 0xffff, 3);
486                                 break;
487                         case BC_RGBA16161616:
488                         case BC_YUVA16161616:
489                                 BLUR(uint16_t, 0xffff, 4);
490                                 break;
491                 }
493                 output_lock.unlock();
494         }
497 int BlurEngine::reconfigure()
499         std_dev = sqrt(-(double)(plugin->config.radius * plugin->config.radius) / 
500                 (2 * log (1.0 / 255.0)));
501         get_constants();
504 int BlurEngine::get_constants()
506         int i;
507         double constants[8];
508         double div;
510         div = sqrt(2 * M_PI) * std_dev;
511         constants[0] = -1.783 / std_dev;
512         constants[1] = -1.723 / std_dev;
513         constants[2] = 0.6318 / std_dev;
514         constants[3] = 1.997  / std_dev;
515         constants[4] = 1.6803 / div;
516         constants[5] = 3.735 / div;
517         constants[6] = -0.6803 / div;
518         constants[7] = -0.2598 / div;
520         n_p[0] = constants[4] + constants[6];
521         n_p[1] = exp(constants[1]) *
522                                 (constants[7] * sin(constants[3]) -
523                                 (constants[6] + 2 * constants[4]) * cos(constants[3])) +
524                                 exp(constants[0]) *
525                                 (constants[5] * sin(constants[2]) -
526                                 (2 * constants[6] + constants[4]) * cos(constants[2]));
528         n_p[2] = 2 * exp(constants[0] + constants[1]) *
529                                 ((constants[4] + constants[6]) * cos(constants[3]) * 
530                                 cos(constants[2]) - constants[5] * 
531                                 cos(constants[3]) * sin(constants[2]) -
532                                 constants[7] * cos(constants[2]) * sin(constants[3])) +
533                                 constants[6] * exp(2 * constants[0]) +
534                                 constants[4] * exp(2 * constants[1]);
536         n_p[3] = exp(constants[1] + 2 * constants[0]) *
537                                 (constants[7] * sin(constants[3]) - 
538                                 constants[6] * cos(constants[3])) +
539                                 exp(constants[0] + 2 * constants[1]) *
540                                 (constants[5] * sin(constants[2]) - constants[4] * 
541                                 cos(constants[2]));
542         n_p[4] = 0.0;
544         d_p[0] = 0.0;
545         d_p[1] = -2 * exp(constants[1]) * cos(constants[3]) -
546                                 2 * exp(constants[0]) * cos(constants[2]);
548         d_p[2] = 4 * cos(constants[3]) * cos(constants[2]) * 
549                                 exp(constants[0] + constants[1]) +
550                                 exp(2 * constants[1]) + exp (2 * constants[0]);
552         d_p[3] = -2 * cos(constants[2]) * exp(constants[0] + 2 * constants[1]) -
553                                 2 * cos(constants[3]) * exp(constants[1] + 2 * constants[0]);
555         d_p[4] = exp(2 * constants[0] + 2 * constants[1]);
557         for(i = 0; i < 5; i++) d_m[i] = d_p[i];
559         n_m[0] = 0.0;
560         for(i = 1; i <= 4; i++)
561                 n_m[i] = n_p[i] - d_p[i] * n_p[0];
563         double sum_n_p, sum_n_m, sum_d;
564         double a, b;
566         sum_n_p = 0.0;
567         sum_n_m = 0.0;
568         sum_d = 0.0;
569         for(i = 0; i < 5; i++)
570         {
571                 sum_n_p += n_p[i];
572                 sum_n_m += n_m[i];
573                 sum_d += d_p[i];
574         }
576         a = sum_n_p / (1 + sum_d);
577         b = sum_n_m / (1 + sum_d);
579         for (i = 0; i < 5; i++)
580         {
581                 bd_p[i] = d_p[i] * a;
582                 bd_m[i] = d_m[i] * b;
583         }
584         return 0;
587 #define BOUNDARY(x) if((x) > vmax) (x) = vmax; else if((x) < 0) (x) = 0;
589 int BlurEngine::transfer_pixels(pixel_f *src1, pixel_f *src2, pixel_f *dest, int size)
591         int i;
592         float sum;
594 // printf("BlurEngine::transfer_pixels %d %d %d %d\n", 
595 // plugin->config.r, 
596 // plugin->config.g, 
597 // plugin->config.b, 
598 // plugin->config.a);
600         for(i = 0; i < size; i++)
601     {
602                 sum = src1[i].r + src2[i].r;
603                 BOUNDARY(sum);
604                 dest[i].r = sum;
605                 sum = src1[i].g + src2[i].g;
606                 BOUNDARY(sum);
607                 dest[i].g = sum;
608                 sum = src1[i].b + src2[i].b;
609                 BOUNDARY(sum);
610                 dest[i].b = sum;
611                 sum = src1[i].a + src2[i].a;
612                 BOUNDARY(sum);
613                 dest[i].a = sum;
614     }
615         return 0;
619 int BlurEngine::multiply_alpha(pixel_f *row, int size)
621         register int i;
622         register float alpha;
624 //      for(i = 0; i < size; i++)
625 //      {
626 //              alpha = (float)row[i].a / vmax;
627 //              row[i].r *= alpha;
628 //              row[i].g *= alpha;
629 //              row[i].b *= alpha;
630 //      }
631         return 0;
634 int BlurEngine::separate_alpha(pixel_f *row, int size)
636         register int i;
637         register float alpha;
638         register float result;
639         
640 //      for(i = 0; i < size; i++)
641 //      {
642 //              if(row[i].a > 0 && row[i].a < vmax)
643 //              {
644 //                      alpha = (float)row[i].a / vmax;
645 //                      result = (float)row[i].r / alpha;
646 //                      row[i].r = (result > vmax ? vmax : result);
647 //                      result = (float)row[i].g / alpha;
648 //                      row[i].g = (result > vmax ? vmax : result);
649 //                      result = (float)row[i].b / alpha;
650 //                      row[i].b = (result > vmax ? vmax : result);
651 //              }
652 //      }
653         return 0;
656 int BlurEngine::blur_strip3(int &size)
658         multiply_alpha(src, size);
660         sp_p = src;
661         sp_m = src + size - 1;
662         vp = val_p;
663         vm = val_m + size - 1;
665         initial_p = sp_p[0];
666         initial_m = sp_m[0];
668         int l;
669         for(int k = 0; k < size; k++)
670         {
671                 terms = (k < 4) ? k : 4;
672                 for(l = 0; l <= terms; l++)
673                 {
674                         if(plugin->config.r)
675                         {
676                                 vp->r += n_p[l] * sp_p[-l].r - d_p[l] * vp[-l].r;
677                                 vm->r += n_m[l] * sp_m[l].r - d_m[l] * vm[l].r;
678                         }
679                         if(plugin->config.g)
680                         {
681                                 vp->g += n_p[l] * sp_p[-l].g - d_p[l] * vp[-l].g;
682                                 vm->g += n_m[l] * sp_m[l].g - d_m[l] * vm[l].g;
683                         }
684                         if(plugin->config.b)
685                         {
686                                 vp->b += n_p[l] * sp_p[-l].b - d_p[l] * vp[-l].b;
687                                 vm->b += n_m[l] * sp_m[l].b - d_m[l] * vm[l].b;
688                         }
689                 }
690                 for( ; l <= 4; l++)
691                 {
692                         if(plugin->config.r)
693                         {
694                                 vp->r += (n_p[l] - bd_p[l]) * initial_p.r;
695                                 vm->r += (n_m[l] - bd_m[l]) * initial_m.r;
696                         }
697                         if(plugin->config.g)
698                         {
699                                 vp->g += (n_p[l] - bd_p[l]) * initial_p.g;
700                                 vm->g += (n_m[l] - bd_m[l]) * initial_m.g;
701                         }
702                         if(plugin->config.b)
703                         {
704                                 vp->b += (n_p[l] - bd_p[l]) * initial_p.b;
705                                 vm->b += (n_m[l] - bd_m[l]) * initial_m.b;
706                         }
707                 }
708                 sp_p++;
709                 sp_m--;
710                 vp++;
711                 vm--;
712         }
713         transfer_pixels(val_p, val_m, dst, size);
714         separate_alpha(dst, size);
715         return 0;
719 int BlurEngine::blur_strip4(int &size)
721         multiply_alpha(src, size);
723         sp_p = src;
724         sp_m = src + size - 1;
725         vp = val_p;
726         vm = val_m + size - 1;
728         initial_p = sp_p[0];
729         initial_m = sp_m[0];
731         int l;
732         for(int k = 0; k < size; k++)
733         {
734                 terms = (k < 4) ? k : 4;
736                 for(l = 0; l <= terms; l++)
737                 {
738                         if(plugin->config.r)
739                         {
740                                 vp->r += n_p[l] * sp_p[-l].r - d_p[l] * vp[-l].r;
741                                 vm->r += n_m[l] * sp_m[l].r - d_m[l] * vm[l].r;
742                         }
743                         if(plugin->config.g)
744                         {
745                                 vp->g += n_p[l] * sp_p[-l].g - d_p[l] * vp[-l].g;
746                                 vm->g += n_m[l] * sp_m[l].g - d_m[l] * vm[l].g;
747                         }
748                         if(plugin->config.b)
749                         {
750                                 vp->b += n_p[l] * sp_p[-l].b - d_p[l] * vp[-l].b;
751                                 vm->b += n_m[l] * sp_m[l].b - d_m[l] * vm[l].b;
752                         }
753                         if(plugin->config.a)
754                         {
755                                 vp->a += n_p[l] * sp_p[-l].a - d_p[l] * vp[-l].a;
756                                 vm->a += n_m[l] * sp_m[l].a - d_m[l] * vm[l].a;
757                         }
758                 }
760                 for( ; l <= 4; l++)
761                 {
762                         if(plugin->config.r)
763                         {
764                                 vp->r += (n_p[l] - bd_p[l]) * initial_p.r;
765                                 vm->r += (n_m[l] - bd_m[l]) * initial_m.r;
766                         }
767                         if(plugin->config.g)
768                         {
769                                 vp->g += (n_p[l] - bd_p[l]) * initial_p.g;
770                                 vm->g += (n_m[l] - bd_m[l]) * initial_m.g;
771                         }
772                         if(plugin->config.b)
773                         {
774                                 vp->b += (n_p[l] - bd_p[l]) * initial_p.b;
775                                 vm->b += (n_m[l] - bd_m[l]) * initial_m.b;
776                         }
777                         if(plugin->config.a)
778                         {
779                                 vp->a += (n_p[l] - bd_p[l]) * initial_p.a;
780                                 vm->a += (n_m[l] - bd_m[l]) * initial_m.a;
781                         }
782                 }
784                 sp_p++;
785                 sp_m--;
786                 vp++;
787                 vm--;
788         }
789         transfer_pixels(val_p, val_m, dst, size);
790         separate_alpha(dst, size);
791         return 0;