bg/clr color tweaks, clear borders rework, fc31 depends
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / bluebanana / bluebanana.C
1 /*
2  * Cinelerra :: Blue Banana - color modification plugin for Cinelerra-CV
3  * Copyright (C) 2012-2013 Monty <monty@xiph.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20
21 #include <math.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include "bcdisplayinfo.h"
27 #include "bcsignals.h"
28 #include "clip.h"
29 #include "bchash.h"
30 #include "filexml.h"
31 #include "bluebanana.h"
32 #include "bluebananaconfig.h"
33 #include "bluebananawindow.h"
34 #include "keyframe.h"
35 #include "language.h"
36 #include "loadbalance.h"
37 #include "playback3d.h"
38 #include "bccolors.h"
39 #include "vframe.h"
40 #include "workarounds.h"
41
42 class BluebananaMain;
43 class BluebananaEngine;
44 class BluebananaWindow;
45
46 REGISTER_PLUGIN(BluebananaMain)
47
48 BluebananaMain::BluebananaMain(PluginServer *server)
49  : PluginVClient(server)
50 {
51   engine = 0;
52
53   /* be sure a lookup update triggers */
54   lookup_cache.Hsel_lo=-999999;
55   lookup_cache.Ssel_lo=-999999;
56   lookup_cache.Vsel_lo=-999999;
57   lookup_cache.Fsel_lo=-999999;
58
59   lookup_cache.Sadj_lo=-999999;
60   lookup_cache.Vadj_lo=-999999;
61   lookup_cache.Radj_lo=-999999;
62   lookup_cache.Gadj_lo=-999999;
63   lookup_cache.Badj_lo=-999999;
64
65   colormodel=-1;
66
67   ants_counter=0;
68
69   memset(red_histogram,0,sizeof(red_histogram));
70   memset(green_histogram,0,sizeof(green_histogram));
71   memset(blue_histogram,0,sizeof(blue_histogram));
72   memset(hue_histogram,0,sizeof(hue_histogram));
73   memset(sat_histogram,0,sizeof(sat_histogram));
74   memset(value_histogram,0,sizeof(value_histogram));
75 }
76
77 BluebananaMain::~BluebananaMain() {
78
79   // if ants are running, run one more pane update to hide them (gui
80   // is already marked as closed)
81   //if(server && server->mwindow && ants_counter>0)
82   //  server->mwindow->sync_parameters();
83
84   delete engine;
85 }
86
87 const char* BluebananaMain::plugin_title() { return N_("Blue Banana"); }
88 int BluebananaMain::is_realtime() { return 1; }
89
90 NEW_WINDOW_MACRO(BluebananaMain, BluebananaWindow)
91 LOAD_CONFIGURATION_MACRO(BluebananaMain, BluebananaConfig)
92
93 void BluebananaMain::render_gui(void *data){
94   if(thread){
95     BluebananaMain *that = (BluebananaMain *)data; // that is server-side
96     BluebananaWindow *window = (BluebananaWindow *)thread->window;
97     window->lock_window("BluebananaMain::render_gui");
98
99     /* push histogram data to gui */
100     window->update_histograms(that);
101
102     /* push any colormodel update to gui */
103     if(that->frame && colormodel != that->frame->get_color_model()){
104       colormodel = that->frame->get_color_model();
105       window->update();
106     }
107
108     window->unlock_window();
109   }
110 }
111
112 void BluebananaMain::update_gui(){
113   if(thread){
114     BluebananaWindow *window = (BluebananaWindow *)thread->window;
115     window->lock_window("BluebananaMain::update_gui");
116     window->flush_config_change();
117     int reconfigure = load_configuration();
118     if(reconfigure)
119       window->update();
120
121     window->unlock_window();
122   }
123 }
124
125 void BluebananaMain::save_data(KeyFrame *keyframe){
126   FileXML output;
127
128   // cause data to be stored directly in text
129   output.set_shared_output(keyframe->xbuf);
130   output.tag.set_title("BLUEBANANA");
131
132   output.tag.set_property("ACTIVE", config.active);
133   output.tag.set_property("OP", config.op);
134   output.tag.set_property("INVERT_SELECTION", config.invert_selection);
135   output.tag.set_property("USE_MASK", config.use_mask);
136   output.tag.set_property("CAPTURE_MASK", config.capture_mask);
137
138   output.tag.set_property("HUE_ACTIVE", config.Hsel_active);
139   output.tag.set_property("HUE_LO", config.Hsel_lo);
140   output.tag.set_property("HUE_HI", config.Hsel_hi);
141   output.tag.set_property("HUE_OVERLAP", config.Hsel_over);
142
143   output.tag.set_property("SATURATION_ACTIVE", config.Ssel_active);
144   output.tag.set_property("SATURATION_LO", config.Ssel_lo);
145   output.tag.set_property("SATURATION_HI", config.Ssel_hi);
146   output.tag.set_property("SATURATION_OVERLAP", config.Ssel_over);
147
148   output.tag.set_property("VALUE_ACTIVE", config.Vsel_active);
149   output.tag.set_property("VALUE_LO", config.Vsel_lo);
150   output.tag.set_property("VALUE_HI", config.Vsel_hi);
151   output.tag.set_property("VALUE_OVERLAP", config.Vsel_over);
152
153   output.tag.set_property("FILL_ACTIVE", config.Fsel_active);
154   output.tag.set_property("FILL_ERODE", config.Fsel_erode);
155   output.tag.set_property("FILL_LO", config.Fsel_lo);
156   output.tag.set_property("FILL_MID", config.Fsel_mid);
157   output.tag.set_property("FILL_HI", config.Fsel_hi);
158   output.tag.set_property("FILL_FEATHER", config.Fsel_over);
159
160   output.tag.set_property("HUE_ADJUST_ACTIVE", config.Hadj_active);
161   output.tag.set_property("HUE_ADJUST", config.Hadj_val);
162
163   output.tag.set_property("SATURATION_ADJUST_ACTIVE", config.Sadj_active);
164   output.tag.set_property("SATURATION_ADJUST_GAMMA", config.Sadj_gamma);
165   output.tag.set_property("SATURATION_ADJUST_LO", config.Sadj_lo);
166   output.tag.set_property("SATURATION_ADJUST_HI", config.Sadj_hi);
167
168   output.tag.set_property("VALUE_ADJUST_ACTIVE", config.Vadj_active);
169   output.tag.set_property("VALUE_ADJUST_GAMMA", config.Vadj_gamma);
170   output.tag.set_property("VALUE_ADJUST_LO", config.Vadj_lo);
171   output.tag.set_property("VALUE_ADJUST_HI", config.Vadj_hi);
172
173   output.tag.set_property("RED_ADJUST_ACTIVE", config.Radj_active);
174   output.tag.set_property("RED_ADJUST_GAMMA", config.Radj_gamma);
175   output.tag.set_property("RED_ADJUST_LO", config.Radj_lo);
176   output.tag.set_property("RED_ADJUST_HI", config.Radj_hi);
177
178   output.tag.set_property("GREEN_ADJUST_ACTIVE", config.Gadj_active);
179   output.tag.set_property("GREEN_ADJUST_GAMMA", config.Gadj_gamma);
180   output.tag.set_property("GREEN_ADJUST_LO", config.Gadj_lo);
181   output.tag.set_property("GREEN_ADJUST_HI", config.Gadj_hi);
182
183   output.tag.set_property("BLUE_ADJUST_ACTIVE", config.Badj_active);
184   output.tag.set_property("BLUE_ADJUST_GAMMA", config.Badj_gamma);
185   output.tag.set_property("BLUE_ADJUST_LO", config.Badj_lo);
186   output.tag.set_property("BLUE_ADJUST_HI", config.Badj_hi);
187
188   output.tag.set_property("OPACITY_ADJUST_ACTIVE", config.Oadj_active);
189   output.tag.set_property("OPACITY_ADJUST", config.Oadj_val);
190   output.tag.set_property("ALPHA_ADJUST_ACTIVE", config.Aadj_active);
191   output.tag.set_property("ALPHA_ADJUST", config.Aadj_val);
192
193   output.append_tag();
194   output.append_newline();
195
196   output.tag.set_title("/BLUEBANANA");
197   output.append_tag();
198   output.append_newline();
199
200   if(keyframe->position==0){
201     /* this will otherwise overwrite the nonauto information as well */
202     output_nonauto(&output);
203   }
204
205   output.terminate_string();
206 }
207
208 // save non-auto data to the default keyframe at zero and does it
209 // without alerting the keyframing mechanism
210 void BluebananaMain::save_nonauto(){
211
212   KeyFrame *default_keyframe=get_prev_keyframe(0);
213   if(default_keyframe){
214     FileXML input;
215     FileXML output;
216     int result = 0;
217
218     input.read_from_string(default_keyframe->get_data());
219     output.set_shared_output(default_keyframe->xbuf);
220
221     while(!result){
222       result = input.read_tag();
223
224       if(!result &&
225          !input.tag.title_is("BLUEBANANA_NONAUTO") &&
226          !input.tag.title_is("/BLUEBANANA_NONAUTO")){
227         input.tag.write_tag(&output);
228         output.append_newline();
229       }
230     }
231
232     output_nonauto(&output);
233   }
234 }
235
236 void BluebananaMain::output_nonauto(FileXML *output){
237   output->tag.set_title("BLUEBANANA_NONAUTO");
238   output->tag.set_property("MARK", config.mark);
239   output->append_tag();
240   output->tag.set_title("/BLUEBANANA_NONAUTO");
241   output->append_tag();
242   output->append_newline();
243   output->terminate_string();
244 }
245
246 void BluebananaMain::load_nonauto(){
247   /* nonauto data stored in the default keyframe at position 0 */
248   KeyFrame *default_keyframe=get_prev_keyframe(0);
249   if(default_keyframe){
250     FileXML input;
251     int result = 0;
252     input.set_shared_input(default_keyframe->xbuf);
253
254     while(!result){
255       result = input.read_tag();
256
257       if(!result && input.tag.title_is("BLUEBANANA_NONAUTO")){
258         config.mark = input.tag.get_property("MARK", config.mark);
259       }
260     }
261   }
262 }
263
264
265 void BluebananaMain::read_data(KeyFrame *keyframe){
266   
267   int result = 0;
268 { FileXML input; // release xbuf-shared_lock before nonaauto
269   input.set_shared_input(keyframe->xbuf);
270
271   while(!result){
272     result = input.read_tag();
273
274     if(!result && input.tag.title_is("BLUEBANANA")){
275       config.active = input.tag.get_property("ACTIVE", config.active);
276       config.op = input.tag.get_property("OP", config.op);
277       config.invert_selection = input.tag.get_property("INVERT_SELECTION", config.invert_selection);
278       config.use_mask = input.tag.get_property("USE_MASK", config.use_mask);
279       config.capture_mask = input.tag.get_property("CAPTURE_MASK", config.capture_mask);
280
281       config.Hsel_active = input.tag.get_property("HUE_ACTIVE", config.Hsel_active);
282       config.Hsel_lo = input.tag.get_property("HUE_LO", config.Hsel_lo);
283       config.Hsel_hi = input.tag.get_property("HUE_HI", config.Hsel_hi);
284       config.Hsel_over = input.tag.get_property("HUE_OVERLAP", config.Hsel_over);
285
286       config.Ssel_active = input.tag.get_property("SATURATION_ACTIVE", config.Ssel_active);
287       config.Ssel_lo = input.tag.get_property("SATURATION_LO", config.Ssel_lo);
288       config.Ssel_hi = input.tag.get_property("SATURATION_HI", config.Ssel_hi);
289       config.Ssel_over = input.tag.get_property("SATURATION_OVERLAP", config.Ssel_over);
290
291       config.Vsel_active = input.tag.get_property("VALUE_ACTIVE", config.Vsel_active);
292       config.Vsel_lo = input.tag.get_property("VALUE_LO", config.Vsel_lo);
293       config.Vsel_hi = input.tag.get_property("VALUE_HI", config.Vsel_hi);
294       config.Vsel_over = input.tag.get_property("VALUE_OVERLAP", config.Vsel_over);
295
296       config.Fsel_active = input.tag.get_property("FILL_ACTIVE", config.Fsel_active);
297       config.Fsel_erode = input.tag.get_property("FILL_ERODE", config.Fsel_erode);
298       config.Fsel_lo = input.tag.get_property("FILL_LO", config.Fsel_lo);
299       config.Fsel_mid = input.tag.get_property("FILL_MID", config.Fsel_mid);
300       config.Fsel_hi = input.tag.get_property("FILL_HI", config.Fsel_hi);
301       config.Fsel_over = input.tag.get_property("FILL_FEATHER", config.Fsel_over);
302
303       config.Hadj_active = input.tag.get_property("HUE_ADJUST_ACTIVE", config.Hadj_active);
304       config.Hadj_val = input.tag.get_property("HUE_ADJUST", config.Hadj_val);
305
306       config.Sadj_active = input.tag.get_property("SATURATION_ADJUST_ACTIVE", config.Sadj_active);
307       config.Sadj_gamma = input.tag.get_property("SATURATION_ADJUST_GAMMA", config.Sadj_gamma);
308       config.Sadj_lo = input.tag.get_property("SATURATION_ADJUST_LO", config.Sadj_lo);
309       config.Sadj_hi = input.tag.get_property("SATURATION_ADJUST_HI", config.Sadj_hi);
310
311       config.Vadj_active = input.tag.get_property("VALUE_ADJUST_ACTIVE", config.Vadj_active);
312       config.Vadj_gamma = input.tag.get_property("VALUE_ADJUST_GAMMA", config.Vadj_gamma);
313       config.Vadj_lo = input.tag.get_property("VALUE_ADJUST_LO", config.Vadj_lo);
314       config.Vadj_hi = input.tag.get_property("VALUE_ADJUST_HI", config.Vadj_hi);
315
316       config.Radj_active = input.tag.get_property("RED_ADJUST_ACTIVE", config.Radj_active);
317       config.Radj_gamma = input.tag.get_property("RED_ADJUST_GAMMA", config.Radj_gamma);
318       config.Radj_lo = input.tag.get_property("RED_ADJUST_LO", config.Radj_lo);
319       config.Radj_hi = input.tag.get_property("RED_ADJUST_HI", config.Radj_hi);
320
321       config.Gadj_active = input.tag.get_property("GREEN_ADJUST_ACTIVE", config.Gadj_active);
322       config.Gadj_gamma = input.tag.get_property("GREEN_ADJUST_GAMMA", config.Gadj_gamma);
323       config.Gadj_lo = input.tag.get_property("GREEN_ADJUST_LO", config.Gadj_lo);
324       config.Gadj_hi = input.tag.get_property("GREEN_ADJUST_HI", config.Gadj_hi);
325
326       config.Badj_active = input.tag.get_property("BLUE_ADJUST_ACTIVE", config.Badj_active);
327       config.Badj_gamma = input.tag.get_property("BLUE_ADJUST_GAMMA", config.Badj_gamma);
328       config.Badj_lo = input.tag.get_property("BLUE_ADJUST_LO", config.Badj_lo);
329       config.Badj_hi = input.tag.get_property("BLUE_ADJUST_HI", config.Badj_hi);
330
331       config.Oadj_active = input.tag.get_property("OPACITY_ADJUST_ACTIVE", config.Oadj_active);
332       config.Oadj_val = input.tag.get_property("OPACITY_ADJUST", config.Oadj_val);
333       config.Aadj_active = input.tag.get_property("ALPHA_ADJUST_ACTIVE", config.Aadj_active);
334       config.Aadj_val = input.tag.get_property("ALPHA_ADJUST", config.Aadj_val);
335
336     }
337   }
338 }
339   load_nonauto();
340 }
341
342 static void select_grow_h(float *hrow, float *vrow, int width){
343   int i;
344
345   /* spread left */
346   for(i=0;i<width-1;i++){
347     if(hrow[i]<hrow[i+1])hrow[i]=hrow[i+1];
348     if(vrow[i]<hrow[i])vrow[i]=hrow[i];
349   }
350   /* spread right */
351   for(i=width-1;i>0;i--){
352     if(hrow[i]<hrow[i-1])hrow[i]=hrow[i-1];
353     if(vrow[i]<hrow[i])vrow[i]=hrow[i];
354   }
355 }
356
357 static void select_shrink_h(float *hrow, float *vrow, int width){
358   int i;
359
360   /* spread left */
361   for(i=0;i<width-1;i++){
362     if(hrow[i]>hrow[i+1])hrow[i]=hrow[i+1];
363     if(vrow[i]>hrow[i])vrow[i]=hrow[i];
364   }
365   /* spread right */
366   for(i=width-1;i>0;i--){
367     if(hrow[i]>hrow[i-1])hrow[i]=hrow[i-1];
368     if(vrow[i]>hrow[i])vrow[i]=hrow[i];
369   }
370 }
371
372 static void select_grow_v(float *row0, float *row1, int width){
373   int i;
374
375   /* spread into 0 */
376   for(i=0;i<width;i++)
377     if(row0[i]<row1[i])row0[i]=row1[i];
378 }
379
380 static void select_shrink_v(float *row0, float *row1, int width){
381   int i;
382
383   /* spread out of 0 */
384   for(i=0;i<width;i++)
385     if(row0[i]>row1[i])row0[i]=row1[i];
386 }
387
388 static void threaded_horizontal(float *in, float *work,
389                                 int width, int height,
390                                 BluebananaEngine *e, int tasks, int passes,
391                                 void(*func)(float *, float *, int)){
392
393   /* these are individually fast operations; here we do in fact make
394      cache region collisions as impossible as we can.  Live with the
395      overhead of the join. */
396   int i,j;
397   e->set_task(tasks,"H_even");
398   j = e->next_task();
399   {
400     int row = (j*2)*height/(tasks*2);
401     int end = (j*2+1)*height/(tasks*2);
402     for(;row<end;row++)
403       for(i=0;i<passes;i++)
404         func(in+row*width,work+row*width,width);
405   }
406   e->wait_task();
407
408   e->set_task(0,"H_odd");
409   {
410     int row = (j*2+1)*height/(tasks*2);
411     int end = (j*2+2)*height/(tasks*2);
412     for(;row<end;row++)
413       for(i=0;i<passes;i++)
414         func(in+row*width,work+row*width,width);
415   }
416   e->wait_task();
417 }
418
419 static void threaded_vertical(float *work, float *temp,
420                               int width, int height,
421                               BluebananaEngine *e, int tasks,
422                               void(*func)(float *, float *, int)){
423
424   /* regions overlap, so this becomes a bit more complex. */
425
426   /* rather than on-demand task allocation, we use the task engine to
427      grab a slot, then reuse this same slot through several joins */
428
429   e->set_task(tasks,"up_odd");
430   int region = e->next_task(); /* grab one slot */
431   int start_row = (region*2)*height/(tasks*2);
432   int mid_row = (region*2+1)*height/(tasks*2);
433   int end_row = (region*2+2)*height/(tasks*2);
434   int row;
435
436   /* spread up, starting at middle row, moving down */
437   /* first task is to save a copy of the un-transformed middle row, as
438      we'll need it for the even pass */
439   memcpy(temp,work+mid_row*width,sizeof(*temp)*width);
440
441   /* odd interleave */
442   for(row=mid_row;row<end_row-1;row++)
443     func(work+row*width,work+(row+1)*width,width);
444   if(end_row<height && row<end_row)
445     func(work+row*width,work+(row+1)*width,width);
446   e->wait_task();
447
448   /* even interleave */
449   e->set_task(0,"up_even");
450   for(row=start_row;row<mid_row-1;row++)
451     func(work+row*width,work+(row+1)*width,width);
452   if(row<mid_row)
453     func(work+row*width,temp,width);
454   e->wait_task();
455
456   /* spread down, starting at mid row and moving up */
457   /* once again, grab a temp vector for the second pass overlap */
458   memcpy(temp,work+mid_row*width,sizeof(*temp)*width);
459
460   /* even interleave */
461   e->set_task(0,"down_even");
462   for(row=mid_row;row>start_row;row--)
463     func(work+row*width,work+(row-1)*width,width);
464   if(start_row>0)
465     func(work+row*width,work+(row-1)*width,width);
466   e->wait_task();
467
468   /* odd interleave */
469   e->set_task(0,"down_odd");
470   for(row=end_row-1;row>mid_row+1;row--)
471     func(work+row*width,work+(row-1)*width,width);
472   if(row>mid_row)
473     func(work+row*width,temp,width);
474   e->wait_task();
475
476   /* done */
477 }
478
479 static float *fill_one(float *in, float *work,
480                       int width, int height,
481                       BluebananaEngine *e, // NULL if we're not threading
482                       char *pattern,
483                       int n){
484   int i,j;
485   int tasks = e?e->get_total_packages():0;
486   float temp[width];
487
488   if(n){
489
490     if(e){
491
492       /* multiple memcpys running at once is occasionally a total
493          cache disaster on Sandy Bridge.  So only one thread gets to
494          copy, the others wait. */
495       e->set_task(1,"fill_memcpy");
496       while((j = e->next_task())>=0){
497         memcpy(work,in,width*height*sizeof(*work));
498       }
499
500     }else{
501       memcpy(work,in,width*height*sizeof(*work));
502     }
503
504
505     for(i=0;i<n;i++){
506       switch(pattern[i]){
507       case 'H': /* grow horizontal */
508         if(e){
509           threaded_horizontal(in, work, width, height, e, tasks, 1, select_grow_h);
510         }else{
511           for(j=0;j<height;j++)
512             select_grow_h(in+j*width,work+j*width,width);
513         }
514         break;
515
516       case 'V': /* grow vertical */
517         if(e){
518           threaded_vertical(work, temp, width, height, e, tasks, select_grow_v);
519         }else{
520           for(j=0;j<height-1;j++)
521             select_grow_v(work+j*width,work+(j+1)*width,width);
522           for(j=height-1;j>0;j--)
523             select_grow_v(work+j*width,work+(j-1)*width,width);
524         }
525         break;
526
527       case 'h': /* shrink horizontal */
528         if(e){
529           threaded_horizontal(in, work, width, height, e, tasks, 1, select_shrink_h);
530         }else{
531           for(j=0;j<height;j++)
532             select_shrink_h(in+j*width,work+j*width,width);
533         }
534         break;
535
536       default: /* shrink vertical */
537
538         if(e){
539           threaded_vertical(work, temp, width, height, e, tasks, select_shrink_v);
540         }else{
541           for(j=0;j<height-1;j++)
542             select_shrink_v(work+j*width,work+(j+1)*width,width);
543           for(j=height-1;j>0;j--)
544             select_shrink_v(work+j*width,work+(j-1)*width,width);
545         }
546         break;
547       }
548     }
549     return work;
550   }else{
551     return in;
552   }
553 }
554
555 static void select_feather_h(float *p, float *dummy, int w){
556   for(int x=0;x<w-1;x++)
557     p[x] = (p[x]+p[x+1])*.5;
558   for(int x=w-1;x>0;x--)
559     p[x] = (p[x]+p[x-1])*.5;
560 }
561
562 static void select_feather_v(float *p0, float *p1, int w){
563   for(int x=0; x<w; x++)
564     p0[x] = (p0[x]+p1[x])*.5;
565 }
566
567 static void feather_one(float *in,
568                         int width, int height,
569                         BluebananaEngine *e, // NULL if we're not threading
570                         int n){
571   int i,j;
572   int tasks = e?e->get_total_packages():0;
573   float temp[width];
574
575   if(e){
576     threaded_horizontal(in, 0, width, height, e, tasks, n, select_feather_h);
577     for(int i=0;i<n;i++)
578       threaded_vertical(in, temp, width, height, e, tasks, select_feather_v);
579   }else{
580     for(j=0;j<height;j++)
581       for(i=0;i<n;i++)
582         select_feather_h(in+j*width,0,width);
583
584     for(i=0;i<n;i++){
585       for(j=0;j<height-1;j++)
586         select_feather_v(in+j*width,in+(j+1)*width,width);
587       for(j=height-1;j>0;j--)
588         select_feather_v(in+j*width,in+(j-1)*width,width);
589     }
590   }
591 }
592
593 /* here and not in the engine as the GUI also uses it (without
594    threading) */
595 float *BluebananaMain::fill_selection(float *in, float *work,
596                                          int width, int height,
597                                          BluebananaEngine *e){
598   float *A=in;
599   float *B=work;
600   float *C;
601
602   C=fill_one(A,B,width,height,e,select_one,select_one_n);
603   C=fill_one(C,(C==A?B:A),width,height,e,select_two,select_two_n);
604   C=fill_one(C,(C==A?B:A),width,height,e,select_three,select_three_n);
605
606   feather_one(C,width,height,e,config.Fsel_over);
607
608   return C;
609 }
610
611
612 int BluebananaMain::process_buffer(VFrame *frame,
613                                    int64_t start_position,
614                                    double frame_rate){
615   ants_counter++;
616   SET_TRACE
617   load_configuration();
618   this->frame = frame;
619
620   SET_TRACE
621   update_lookups(1);
622
623   SET_TRACE
624   read_frame(frame, 0, start_position, frame_rate, 0);
625
626   if(!engine)
627     engine = new BluebananaEngine(this, get_project_smp() + 1,
628                                   get_project_smp() + 1);
629   SET_TRACE
630   engine->process_packages(frame);
631
632   // push final histograms to UI if it's up
633   SET_TRACE
634   send_render_gui(this);
635
636   return 0;
637 }
638