Fixed initialisation of tf in file_open(). Without setting the memory to 0,
[cinelerra_cv/mob.git] / plugins / chromakeyhsv / chromakey.C
blob7c6ef53f2f63c74b70e545a75c7ef6f58ee2dfed
1 #include "bcdisplayinfo.h"
2 #include "bcsignals.h"
3 #include "chromakey.h"
4 #include "clip.h"
5 #include "bchash.h"
6 #include "filexml.h"
7 #include "guicast.h"
8 #include "keyframe.h"
9 #include "language.h"
10 #include "loadbalance.h"
11 #include "picon_png.h"
12 #include "playback3d.h"
13 #include "plugincolors.h"
14 #include "pluginvclient.h"
15 #include "vframe.h"
17 #include <stdint.h>
18 #include <string.h>
19 #include <unistd.h>
23 ChromaKeyConfig::ChromaKeyConfig ()
25   red = 0.0;
26   green = 1.0;
27   blue = 0.0;
29   min_brightness = 50.0;
30   max_brightness = 100.0;
31   tolerance = 15.0;
32   saturation = 0.0;
33   min_saturation = 50.0;
35   in_slope = 2;
36   out_slope = 2;
37   alpha_offset = 0;
39   spill_threshold = 0.0;
40   spill_amount = 90.0;
42   show_mask = 0;
47 void
48 ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
50   red = src.red;
51   green = src.green;
52   blue = src.blue;
53   spill_threshold = src.spill_threshold;
54   spill_amount = src.spill_amount;
55   min_brightness = src.min_brightness;
56   max_brightness = src.max_brightness;
57   saturation = src.saturation;
58   min_saturation = src.min_saturation;
59   tolerance = src.tolerance;
60   in_slope = src.in_slope;
61   out_slope = src.out_slope;
62   alpha_offset = src.alpha_offset;
63   show_mask = src.show_mask;
66 int
67 ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
69   return (EQUIV (red, src.red) &&
70           EQUIV (green, src.green) &&
71           EQUIV (blue, src.blue) &&
72           EQUIV (spill_threshold, src.spill_threshold) &&
73           EQUIV (spill_amount, src.spill_amount) &&
74           EQUIV (min_brightness, src.min_brightness) &&
75           EQUIV (max_brightness, src.max_brightness) &&
76           EQUIV (saturation, src.saturation) &&
77           EQUIV (min_saturation, src.min_saturation) &&
78           EQUIV (tolerance, src.tolerance) &&
79           EQUIV (in_slope, src.in_slope) &&
80           EQUIV (out_slope, src.out_slope) &&
81           EQUIV (show_mask, src.show_mask) &&
82           EQUIV (alpha_offset, src.alpha_offset));
85 void
86 ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
87                               ChromaKeyConfig & next,
88                               int64_t prev_frame,
89                               int64_t next_frame, int64_t current_frame)
91   double next_scale =
92     (double) (current_frame - prev_frame) / (next_frame - prev_frame);
93   double prev_scale =
94     (double) (next_frame - current_frame) / (next_frame - prev_frame);
96   this->red = prev.red * prev_scale + next.red * next_scale;
97   this->green = prev.green * prev_scale + next.green * next_scale;
98   this->blue = prev.blue * prev_scale + next.blue * next_scale;
99   this->spill_threshold =
100     prev.spill_threshold * prev_scale + next.spill_threshold * next_scale;
101   this->spill_amount =
102     prev.spill_amount * prev_scale + next.tolerance * next_scale;
103   this->min_brightness =
104     prev.min_brightness * prev_scale + next.min_brightness * next_scale;
105   this->max_brightness =
106     prev.max_brightness * prev_scale + next.max_brightness * next_scale;
107   this->saturation =
108     prev.saturation * prev_scale + next.saturation * next_scale;
109   this->min_saturation =
110     prev.min_saturation * prev_scale + next.min_saturation * next_scale;
111   this->tolerance = prev.tolerance * prev_scale + next.tolerance * next_scale;
112   this->in_slope = prev.in_slope * prev_scale + next.in_slope * next_scale;
113   this->out_slope = prev.out_slope * prev_scale + next.out_slope * next_scale;
114   this->alpha_offset =
115     prev.alpha_offset * prev_scale + next.alpha_offset * next_scale;
116   this->show_mask = next.show_mask;
121 ChromaKeyConfig::get_color ()
123   int red = (int) (CLIP (this->red, 0, 1) * 0xff);
124   int green = (int) (CLIP (this->green, 0, 1) * 0xff);
125   int blue = (int) (CLIP (this->blue, 0, 1) * 0xff);
126   return (red << 16) | (green << 8) | blue;
131 ChromaKeyWindow::ChromaKeyWindow (ChromaKeyHSV * plugin, int x, int y):BC_Window (plugin->gui_string,
132            x,
133            y, 
134            400, 
135            450, 
136            400, 
137            450, 
138            0, 
139            0, 
140            1)
142   this->plugin = plugin;
143   color_thread = 0;
146 ChromaKeyWindow::~ChromaKeyWindow ()
148   delete color_thread;
151 void
152 ChromaKeyWindow::create_objects ()
154   int y = 10, y1, x1 = 0, x2 = 10;
155   int x = 30;
157   BC_Title *title;
158   BC_Bar *bar;
159   int ymargin = get_text_height(MEDIUMFONT) + 5;
160   int ymargin2 = get_text_height(MEDIUMFONT) + 10;
162   add_subwindow (title = new BC_Title (x2, y, _("Color:")));
164   add_subwindow (color = new ChromaKeyColor (plugin, this, x, y + 25));
166   add_subwindow (sample =
167                  new BC_SubWindow (x + color->get_w () + 10, y, 100, 50));
168   y += sample->get_h () + 10;
170   add_subwindow (use_colorpicker =
171                  new ChromaKeyUseColorPicker (plugin, this, x, y));
172   y += use_colorpicker->get_h() + 10;
173   add_subwindow (show_mask = new ChromaKeyShowMask (plugin, x2, y));
174         y += show_mask->get_h() + 5;
176         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
177         y += bar->get_h() + 5;
178         y1 = y;
179   add_subwindow (new BC_Title (x2, y, _("Key parameters:")));
180   y += ymargin;
181   add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
182   if(title->get_w() > x1) x1 = title->get_w();
183   y += ymargin;
184   add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
185   if(title->get_w() > x1) x1 = title->get_w();
186   y += ymargin;
187   add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
188   if(title->get_w() > x1) x1 = title->get_w();
189   y += ymargin;
190   add_subwindow (title = new BC_Title (x, y, _("Saturation Offset:")));
191   if(title->get_w() > x1) x1 = title->get_w();
192   y += ymargin;
193   add_subwindow (title = new BC_Title (x, y, _("Min Saturation:")));
194   if(title->get_w() > x1) x1 = title->get_w();
195   y += ymargin2;
197         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
198         y += bar->get_h() + 5;
199   add_subwindow (title = new BC_Title (x2, y, _("Mask tweaking:")));
200   y += ymargin;
201   add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
202   if(title->get_w() > x1) x1 = title->get_w();
203   y += ymargin;
204   add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
205   if(title->get_w() > x1) x1 = title->get_w();
206   y += ymargin;
207   add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
208   if(title->get_w() > x1) x1 = title->get_w();
209   y += ymargin2;
213         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
214         y += bar->get_h() + 5;
215   add_subwindow (title = new BC_Title (x2, y, _("Spill light control:")));
216   y += ymargin;
217   add_subwindow (title = new BC_Title (x, y, _("Spill Threshold:")));
218   if(title->get_w() > x1) x1 = title->get_w();
219   y += ymargin;
220   add_subwindow (title = new BC_Title (x, y, _("Spill Compensation:")));
221   if(title->get_w() > x1) x1 = title->get_w();
222   y += ymargin;
225         y = y1;
226   y += ymargin;
227         x1 += x;
231   add_subwindow (tolerance = new ChromaKeyTolerance (plugin, x1, y));
232   y += ymargin;
233   add_subwindow (min_brightness = new ChromaKeyMinBrightness (plugin, x1, y));
234   y += ymargin;
235   add_subwindow (max_brightness = new ChromaKeyMaxBrightness (plugin, x1, y));
236   y += ymargin;
237   add_subwindow (saturation = new ChromaKeySaturation (plugin, x1, y));
238   y += ymargin;
239   add_subwindow (min_saturation = new ChromaKeyMinSaturation (plugin, x1, y));
240   y += ymargin;
242         y += bar->get_h() + 5;
243   y += ymargin2;
244   add_subwindow (in_slope = new ChromaKeyInSlope (plugin, x1, y));
245   y += ymargin;
246   add_subwindow (out_slope = new ChromaKeyOutSlope (plugin, x1, y));
247   y += ymargin;
248   add_subwindow (alpha_offset = new ChromaKeyAlphaOffset (plugin, x1, y));
249    y += ymargin;
251         y += bar->get_h() + 5;
252   y += ymargin2;
253   add_subwindow (spill_threshold = new ChromaKeySpillThreshold (plugin, x1, y));
254   y += ymargin;
255   add_subwindow (spill_amount = new ChromaKeySpillAmount (plugin, x1, y));
257   color_thread = new ChromaKeyColorThread (plugin, this);
259   update_sample ();
260   show_window ();
261   flush ();
264 void
265 ChromaKeyWindow::update_sample ()
267   sample->set_color (plugin->config.get_color ());
268   sample->draw_box (0, 0, sample->get_w (), sample->get_h ());
269   sample->set_color (BLACK);
270   sample->draw_rectangle (0, 0, sample->get_w (), sample->get_h ());
271   sample->flash ();
276 WINDOW_CLOSE_EVENT (ChromaKeyWindow)
277   ChromaKeyColor::ChromaKeyColor (ChromaKeyHSV * plugin,
278                                   ChromaKeyWindow * gui, int x, int y):
279 BC_GenericButton (x, y, _("Color..."))
281   this->plugin = plugin;
282   this->gui = gui;
286 ChromaKeyColor::handle_event ()
288   gui->color_thread->start_window (plugin->config.get_color (), 0xff);
289   return 1;
295 ChromaKeyMinBrightness::ChromaKeyMinBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
296             y,
297             0,
298             200, 200, (float) 0, (float) 100, plugin->config.min_brightness)
300   this->plugin = plugin;
301   set_precision (0.01);
305 ChromaKeyMinBrightness::handle_event ()
307   plugin->config.min_brightness = get_value ();
308   plugin->send_configure_change ();
309   return 1;
312 ChromaKeyMaxBrightness::ChromaKeyMaxBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
313             y,
314             0,
315             200, 200, (float) 0, (float) 100, plugin->config.max_brightness)
317   this->plugin = plugin;
318   set_precision (0.01);
322 ChromaKeyMaxBrightness::handle_event ()
324   plugin->config.max_brightness = get_value ();
325   plugin->send_configure_change ();
326   return 1;
330 ChromaKeySaturation::ChromaKeySaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
331             y,
332             0, 200, 200, (float) 0, (float) 100, plugin->config.saturation)
334   this->plugin = plugin;
335   set_precision (0.01);
339 ChromaKeySaturation::handle_event ()
341   plugin->config.saturation = get_value ();
342   plugin->send_configure_change ();
343   return 1;
346 ChromaKeyMinSaturation::ChromaKeyMinSaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
347             y,
348             0,
349             200, 200, (float) 0, (float) 100, plugin->config.min_saturation)
351   this->plugin = plugin;
352   set_precision (0.01);
356 ChromaKeyMinSaturation::handle_event ()
358   plugin->config.min_saturation = get_value ();
359   plugin->send_configure_change ();
360   return 1;
364 ChromaKeyTolerance::ChromaKeyTolerance (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
365             y,
366             0, 200, 200, (float) 0, (float) 100, plugin->config.tolerance)
368   this->plugin = plugin;
369   set_precision (0.01);
373 ChromaKeyTolerance::handle_event ()
375   plugin->config.tolerance = get_value ();
376   plugin->send_configure_change ();
377   return 1;
382 ChromaKeyInSlope::ChromaKeyInSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
383             y,
384             0, 200, 200, (float) 0, (float) 20, plugin->config.in_slope)
386   this->plugin = plugin;
387   set_precision (0.01);
391 ChromaKeyInSlope::handle_event ()
393   plugin->config.in_slope = get_value ();
394   plugin->send_configure_change ();
395   return 1;
399 ChromaKeyOutSlope::ChromaKeyOutSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
400             y,
401             0, 200, 200, (float) 0, (float) 20, plugin->config.out_slope)
403   this->plugin = plugin;
404   set_precision (0.01);
408 ChromaKeyOutSlope::handle_event ()
410   plugin->config.out_slope = get_value ();
411   plugin->send_configure_change ();
412   return 1;
416 ChromaKeyAlphaOffset::ChromaKeyAlphaOffset (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
417             y,
418             0,
419             200, 200, (float) -100, (float) 100, plugin->config.alpha_offset)
421   this->plugin = plugin;
422   set_precision (0.01);
426 ChromaKeyAlphaOffset::handle_event ()
428   plugin->config.alpha_offset = get_value ();
429   plugin->send_configure_change ();
430   return 1;
433 ChromaKeyShowMask::ChromaKeyShowMask (ChromaKeyHSV * plugin, int x, int y):BC_CheckBox (x, y, plugin->config.show_mask,
434              _
435              ("Show Mask"))
437   this->plugin = plugin;
442 ChromaKeyShowMask::handle_event ()
444   plugin->config.show_mask = get_value ();
445   plugin->send_configure_change ();
446   return 1;
449 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyHSV * plugin, ChromaKeyWindow * gui, int x, int y):BC_GenericButton (x, y,
450                   _
451                   ("Use color picker"))
453   this->plugin = plugin;
454   this->gui = gui;
458 ChromaKeyUseColorPicker::handle_event ()
460   plugin->config.red = plugin->get_red ();
461   plugin->config.green = plugin->get_green ();
462   plugin->config.blue = plugin->get_blue ();
463   gui->update_sample ();
464   plugin->send_configure_change ();
465   return 1;
470 ChromaKeySpillThreshold::ChromaKeySpillThreshold (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
471             y,
472             0,
473             200, 200, (float) 0, (float) 100, plugin->config.spill_threshold)
475   this->plugin = plugin;
476   set_precision (0.01);
480 ChromaKeySpillThreshold::handle_event ()
482   plugin->config.spill_threshold = get_value ();
483   plugin->send_configure_change ();
484   return 1;
487 ChromaKeySpillAmount::ChromaKeySpillAmount (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
488             y,
489             0, 200, 200, (float) 0, (float) 100, plugin->config.spill_amount)
491   this->plugin = plugin;
492   set_precision (0.01);
496 ChromaKeySpillAmount::handle_event ()
498   plugin->config.spill_amount = get_value ();
499   plugin->send_configure_change ();
500   return 1;
508 ChromaKeyColorThread::ChromaKeyColorThread (ChromaKeyHSV * plugin, ChromaKeyWindow * gui):ColorThread (1,
509              _
510              ("Inner color"))
512   this->plugin = plugin;
513   this->gui = gui;
517 ChromaKeyColorThread::handle_new_color (int output, int alpha)
519   plugin->config.red = (float) (output & 0xff0000) / 0xff0000;
520   plugin->config.green = (float) (output & 0xff00) / 0xff00;
521   plugin->config.blue = (float) (output & 0xff) / 0xff;
522   gui->update_sample ();
523   plugin->send_configure_change ();
524   return 1;
534 PLUGIN_THREAD_OBJECT (ChromaKeyHSV, ChromaKeyThread, ChromaKeyWindow) ChromaKeyServer::ChromaKeyServer (ChromaKeyHSV * plugin):LoadServer (plugin->PluginClient::smp + 1,
535             plugin->PluginClient::smp +
536             1)
538   this->plugin = plugin;
541 void
542 ChromaKeyServer::init_packages ()
544   for (int i = 0; i < get_total_packages (); i++)
545     {
546       ChromaKeyPackage *pkg = (ChromaKeyPackage *) get_package (i);
547       pkg->y1 = plugin->input->get_h () * i / get_total_packages ();
548       pkg->y2 = plugin->input->get_h () * (i + 1) / get_total_packages ();
549     }
552 LoadClient *
553 ChromaKeyServer::new_client ()
555   return new ChromaKeyUnit (plugin, this);
558 LoadPackage *
559 ChromaKeyServer::new_package ()
561   return new ChromaKeyPackage;
566 ChromaKeyPackage::ChromaKeyPackage ():LoadPackage ()
570 ChromaKeyUnit::ChromaKeyUnit (ChromaKeyHSV * plugin, ChromaKeyServer * server):LoadClient
571   (server)
573   this->plugin = plugin;
579 #define ABS(a) ((a<0)?-(a):a)
580 // Reuse as much as possible in the opengl version
581 #define OUTER_VARIABLES \
582   float red = plugin->config.red; \
583   float green = plugin->config.green; \
584   float blue = plugin->config.blue; \
586   float in_slope = plugin->config.in_slope / 100; \
587   float out_slope = plugin->config.out_slope / 100; \
589   float tolerance = plugin->config.tolerance / 100; \
590   float tolerance_in = tolerance - in_slope; \
591   float tolerance_out = tolerance + out_slope; \
593   float sat = plugin->config.saturation / 100; \
594   float min_s = plugin->config.min_saturation / 100; \
595   float min_s_in = min_s + in_slope; \
596   float min_s_out = min_s - out_slope; \
598   float min_v = plugin->config.min_brightness / 100; \
599   float min_v_in = min_v + in_slope; \
600   float min_v_out = min_v - out_slope; \
602   float max_v = plugin->config.max_brightness / 100; \
603   float max_v_in = max_v - in_slope; \
604   float max_v_out = max_v + out_slope; \
606   float spill_threshold = plugin->config.spill_threshold / 100; \
607   float spill_amount = 1.0 - plugin->config.spill_amount / 100; \
609   float alpha_offset = plugin->config.alpha_offset / 100; \
611 /* Convert RGB key to HSV key */ \
612         float hue_key, saturation_key, value_key; \
613         HSV::rgb_to_hsv(red,  \
614                 green, \
615                 blue, \
616                 hue_key, \
617                 saturation_key, \
618                 value_key);
621 template <typename component_type> 
622 void ChromaKeyUnit::process_chromakey(int components, 
623         component_type max, 
624         bool use_yuv, 
625         ChromaKeyPackage *pkg) 
626 {       
627         OUTER_VARIABLES
629         int w = plugin->input->get_w();
631         for (int i = pkg->y1; i < pkg->y2; i++)
632         {
633         component_type *row = (component_type *) plugin->input->get_rows ()[i];
635         for (int j = 0; j < w; j++)
636                 {
637                         float a = 1;
639                         float r = (float) row[0] / max;
640                         float g = (float) row[1] / max;
641                         float b = (float) row[2] / max;
643                         float h, s, v;
645                         float av = 1, ah = 1, as = 1, avm = 1;
646                         bool has_match = true;
648                         if (use_yuv)
649                 {
650 /* Convert pixel to RGB float */
651                         float y = r;
652                         float u = g;
653                         float v = b;
654                         YUV::yuv_to_rgb_f (r, g, b, y, u - 0.5, v - 0.5);
655                 }
657                         HSV::rgb_to_hsv (r, g, b, h, s, v);
659 // First, test if the hue is in range
660                         if (tolerance == 0)
661                     ah = 1.0;
662                         else 
663                         if (ABS (h - hue_key) < tolerance_in * 180)
664                     ah = 0;
665                         else 
666                         if ((out_slope != 0) && (ABS (h - hue_key) < tolerance * 180))
667 /* we scale alpha between 0 and 1/2 */
668                     ah = ABS (h - hue_key) / tolerance / 360;   
669                         else 
670                         if (ABS (h - hue_key) < tolerance_out * 180)
671 /* we scale alpha between 1/2 and 1 */
672                     ah = ABS (h - hue_key) / tolerance_out / 360;       
673                         else
674                     has_match = false;
676 // Check if the saturation matches
677                         if (has_match)
678                 {
679                         if (min_s == 0)
680                                         as = 0;
681                         else if (s - sat >= min_s_in)
682                                         as = 0;
683                         else if ((out_slope != 0) && (s - sat > min_s))
684                                         as = (s - sat - min_s) / (min_s * 2);
685                         else if (s - sat > min_s_out)
686                                         as = (s - sat - min_s_out) / (min_s_out * 2);
687                         else
688                                         has_match = false;
689                 }
691 // Check if the value is more than the minimun
692                         if (has_match)
693                 {
694                         if (min_v == 0)
695                                         av = 0;
696                         else if (v >= min_v_in)
697                                         av = 0;
698                         else if ((out_slope != 0) && (v > min_v))
699                                         av = (v - min_v) / (min_v * 2);
700                         else if (v > min_v_out)
701                                         av = (v - min_v_out) / (min_v_out * 2);
702                         else
703                                         has_match = false;
704                 }
706 // Check if the value is less than the maximum
707                         if (has_match)
708                 {
709                         if (max_v == 0)
710                                         avm = 1;
711                         else if (v <= max_v_in)
712                                         avm = 0;
713                         else if ((out_slope != 0) && (v < max_v))
714                                         avm = (v - max_v) / (max_v * 2);
715                         else if (v < max_v_out)
716                                         avm = (v - max_v_out) / (max_v_out * 2);
717                         else
718                                         has_match = false;
719                 }
721           // If the color is part of the key, update the alpha channel
722           if (has_match)
723             a = MAX (MAX (ah, av), MAX (as, avm));
725           // Spill light processing       
726           if ((ABS (h - hue_key) < spill_threshold * 180) ||
727               ((ABS (h - hue_key) > 360)
728                && (ABS (h - hue_key) - 360 < spill_threshold * 180)))
729             {
730               s = s * spill_amount * ABS (h - hue_key) / (spill_threshold * 180);
732               HSV::hsv_to_rgb (r, g, b, h, s, v);
734               if (use_yuv)
735                 {
736                   float y;
737                   float u;
738                   float v;
739                   YUV::rgb_to_yuv_f (r, g, b, y, u, v);
740               CLAMP (y, 0, 1.0);
741               CLAMP (u, 0, 1.0);
742               CLAMP (v, 0, 1.0);
743                   row[0] = (component_type) ((float) y * max);
744                   row[1] = (component_type) ((float) (u + 0.5) * max);
745                   row[2] = (component_type) ((float) (v + 0.5) * max);
746                 }
747               else
748                 {
749               CLAMP (r, 0, 1.0);
750               CLAMP (g, 0, 1.0);
751               CLAMP (b, 0, 1.0);
752                   row[0] = (component_type) ((float) r * max);
753                   row[1] = (component_type) ((float) g * max);
754                   row[2] = (component_type) ((float) b * max);
755                 }
756             }
758           a += alpha_offset;
759           CLAMP (a, 0.0, 1.0);
761           if (plugin->config.show_mask)
762             {
764               if (use_yuv)
765                 {
766                   row[0] = (component_type) ((float) a * max);
767                   row[1] = (component_type) ((float) max / 2);
768                   row[2] = (component_type) ((float) max / 2);
769                 }
770               else
771                 {
772                   row[0] = (component_type) ((float) a * max);
773                   row[1] = (component_type) ((float) a * max);
774                   row[2] = (component_type) ((float) a * max);
775                 }
776             }
778           /* Multiply alpha and put back in frame */
779           if (components == 4)
780             {
781               row[3] = MIN ((component_type) (a * max), row[3]);
782             }
783           else if (use_yuv)
784             {
785               row[0] = (component_type) ((float) a * row[0]);
786               row[1] =
787                 (component_type) ((float) a * (row[1] - (max / 2 + 1)) +
788                                   max / 2 + 1);
789               row[2] =
790                 (component_type) ((float) a * (row[2] - (max / 2 + 1)) +
791                                   max / 2 + 1);
792             }
793           else
794             {
795               row[0] = (component_type) ((float) a * row[0]);
796               row[1] = (component_type) ((float) a * row[1]);
797               row[2] = (component_type) ((float) a * row[2]);
798             }
800           row += components;
801         }
802     }
808 void ChromaKeyUnit::process_package(LoadPackage *package)
810         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
813         switch(plugin->input->get_color_model())
814         {
815                 case BC_RGB_FLOAT:
816                         process_chromakey<float> (3, 1.0, 0, pkg);
817                         break;
818                 case BC_RGBA_FLOAT:
819                         process_chromakey<float> ( 4, 1.0, 0, pkg);
820                         break;
821                 case BC_RGB888:
822                         process_chromakey<unsigned char> ( 3, 0xff, 0, pkg);
823                         break;
824                 case BC_RGBA8888:
825                         process_chromakey<unsigned char> ( 4, 0xff, 0, pkg);
826                         break;
827                 case BC_YUV888:
828                         process_chromakey<unsigned char> ( 3, 0xff, 1, pkg);
829                         break;
830                 case BC_YUVA8888:
831                         process_chromakey<unsigned char> ( 4, 0xff, 1, pkg);
832                         break;
833                 case BC_YUV161616:
834                         process_chromakey<uint16_t> (3, 0xffff, 1, pkg);
835                         break;
836                 case BC_YUVA16161616:
837                         process_chromakey<uint16_t> (4, 0xffff, 1, pkg);
838                         break;
839         }
847 REGISTER_PLUGIN(ChromaKeyHSV)
851 ChromaKeyHSV::ChromaKeyHSV(PluginServer *server)
852  : PluginVClient(server)
854         PLUGIN_CONSTRUCTOR_MACRO
855         engine = 0;
858 ChromaKeyHSV::~ChromaKeyHSV()
860         PLUGIN_DESTRUCTOR_MACRO
861         if(engine) delete engine;
865 int ChromaKeyHSV::process_buffer(VFrame *frame,
866                 int64_t start_position,
867                 double frame_rate)
869         load_configuration();
870         this->input = frame;
871         this->output = frame;
874         read_frame(frame, 
875                 0, 
876                 start_position, 
877                 frame_rate,
878                 get_use_opengl());
879         if(get_use_opengl()) return run_opengl();
882         if(!engine) engine = new ChromaKeyServer(this);
883         engine->process_packages();
885         return 0;
888 char* ChromaKeyHSV::plugin_title() { return N_("Chroma key (HSV)"); }
889 int ChromaKeyHSV::is_realtime() { return 1; }
891 NEW_PICON_MACRO(ChromaKeyHSV)
893 LOAD_CONFIGURATION_MACRO(ChromaKeyHSV, ChromaKeyConfig)
895 int ChromaKeyHSV::load_defaults ()
897   char directory[BCTEXTLEN];
898 // set the default directory
899   sprintf (directory, "%schromakey-hsv.rc", BCASTDIR);
901 // load the defaults
902   defaults = new BC_Hash (directory);
903   defaults->load ();
905   config.red = defaults->get ("RED", config.red);
906   config.green = defaults->get ("GREEN", config.green);
907   config.blue = defaults->get ("BLUE", config.blue);
908   config.min_brightness =
909     defaults->get ("MIN_BRIGHTNESS", config.min_brightness);
910   config.max_brightness =
911     defaults->get ("MAX_BRIGHTNESS", config.max_brightness);
912   config.saturation = defaults->get ("SATURATION", config.saturation);
913   config.min_saturation =
914     defaults->get ("MIN_SATURATION", config.min_saturation);
915   config.tolerance = defaults->get ("TOLERANCE", config.tolerance);
916   config.spill_threshold =
917     defaults->get ("SPILL_THRESHOLD", config.spill_threshold);
918   config.spill_amount = defaults->get ("SPILL_AMOUNT", config.spill_amount);
919   config.in_slope = defaults->get ("IN_SLOPE", config.in_slope);
920   config.out_slope = defaults->get ("OUT_SLOPE", config.out_slope);
921   config.alpha_offset = defaults->get ("ALPHA_OFFSET", config.alpha_offset);
922   config.show_mask = defaults->get ("SHOW_MASK", config.show_mask);
923   return 0;
927 ChromaKeyHSV::save_defaults ()
929   defaults->update ("RED", config.red);
930   defaults->update ("GREEN", config.green);
931   defaults->update ("BLUE", config.blue);
932   defaults->update ("MIN_BRIGHTNESS", config.min_brightness);
933   defaults->update ("MAX_BRIGHTNESS", config.max_brightness);
934   defaults->update ("SATURATION", config.saturation);
935   defaults->update ("MIN_SATURATION", config.min_saturation);
936   defaults->update ("TOLERANCE", config.tolerance);
937   defaults->update ("IN_SLOPE", config.in_slope);
938   defaults->update ("OUT_SLOPE", config.out_slope);
939   defaults->update ("ALPHA_OFFSET", config.alpha_offset);
940   defaults->update ("SPILL_THRESHOLD", config.spill_threshold);
941   defaults->update ("SPILL_AMOUNT", config.spill_amount);
942   defaults->update ("SHOW_MASK", config.show_mask);
943   defaults->save ();
944   return 0;
947 void
948 ChromaKeyHSV::save_data (KeyFrame * keyframe)
950   FileXML output;
951   output.set_shared_string (keyframe->data, MESSAGESIZE);
952   output.tag.set_title ("CHROMAKEY_HSV");
953   output.tag.set_property ("RED", config.red);
954   output.tag.set_property ("GREEN", config.green);
955   output.tag.set_property ("BLUE", config.blue);
956   output.tag.set_property ("MIN_BRIGHTNESS", config.min_brightness);
957   output.tag.set_property ("MAX_BRIGHTNESS", config.max_brightness);
958   output.tag.set_property ("SATURATION", config.saturation);
959   output.tag.set_property ("MIN_SATURATION", config.min_saturation);
960   output.tag.set_property ("TOLERANCE", config.tolerance);
961   output.tag.set_property ("IN_SLOPE", config.in_slope);
962   output.tag.set_property ("OUT_SLOPE", config.out_slope);
963   output.tag.set_property ("ALPHA_OFFSET", config.alpha_offset);
964   output.tag.set_property ("SPILL_THRESHOLD", config.spill_threshold);
965   output.tag.set_property ("SPILL_AMOUNT", config.spill_amount);
966   output.tag.set_property ("SHOW_MASK", config.show_mask);
967   output.append_tag ();
968   output.terminate_string ();
971 void
972 ChromaKeyHSV::read_data (KeyFrame * keyframe)
974   FileXML input;
976   input.set_shared_string (keyframe->data, strlen (keyframe->data));
978   while (!input.read_tag ())
979     {
980       if (input.tag.title_is ("CHROMAKEY_HSV"))
981         {
982           config.red = input.tag.get_property ("RED", config.red);
983           config.green = input.tag.get_property ("GREEN", config.green);
984           config.blue = input.tag.get_property ("BLUE", config.blue);
985           config.min_brightness =
986             input.tag.get_property ("MIN_BRIGHTNESS", config.min_brightness);
987           config.max_brightness =
988             input.tag.get_property ("MAX_BRIGHTNESS", config.max_brightness);
989           config.saturation =
990             input.tag.get_property ("SATURATION", config.saturation);
991           config.min_saturation =
992             input.tag.get_property ("MIN_SATURATION", config.min_saturation);
993           config.tolerance =
994             input.tag.get_property ("TOLERANCE", config.tolerance);
995           config.in_slope =
996             input.tag.get_property ("IN_SLOPE", config.in_slope);
997           config.out_slope =
998             input.tag.get_property ("OUT_SLOPE", config.out_slope);
999           config.alpha_offset =
1000             input.tag.get_property ("ALPHA_OFFSET", config.alpha_offset);
1001           config.spill_threshold =
1002             input.tag.get_property ("SPILL_THRESHOLD",
1003                                     config.spill_threshold);
1004           config.spill_amount =
1005             input.tag.get_property ("SPILL_AMOUNT", config.spill_amount);
1006           config.show_mask =
1007             input.tag.get_property ("SHOW_MASK", config.show_mask);
1008         }
1009     }
1013 SHOW_GUI_MACRO(ChromaKeyHSV, ChromaKeyThread)
1015 SET_STRING_MACRO(ChromaKeyHSV)
1017 RAISE_WINDOW_MACRO(ChromaKeyHSV)
1019 void ChromaKeyHSV::update_gui ()
1021   if (thread)
1022     {
1023       load_configuration ();
1024       thread->window->lock_window ();
1025       thread->window->min_brightness->update (config.min_brightness);
1026       thread->window->max_brightness->update (config.max_brightness);
1027       thread->window->saturation->update (config.saturation);
1028       thread->window->min_saturation->update (config.min_saturation);
1029       thread->window->tolerance->update (config.tolerance);
1030       thread->window->in_slope->update (config.in_slope);
1031       thread->window->out_slope->update (config.out_slope);
1032       thread->window->alpha_offset->update (config.alpha_offset);
1033       thread->window->spill_threshold->update (config.spill_threshold);
1034       thread->window->spill_amount->update (config.spill_amount);
1035       thread->window->show_mask->update (config.show_mask);
1036       thread->window->update_sample ();
1037       thread->window->unlock_window ();
1038     }
1044 int ChromaKeyHSV::handle_opengl()
1046 #ifdef HAVE_GL
1047 // For macro
1048         ChromaKeyHSV *plugin = this;
1049         OUTER_VARIABLES
1051         static char *yuv_shader = 
1052                 "const vec3 black = vec3(0.0, 0.5, 0.5);\n"
1053                 "\n"
1054                 "vec4 yuv_to_rgb(vec4 color)\n"
1055                 "{\n"
1056                         YUV_TO_RGB_FRAG("color")
1057                 "       return color;\n"
1058                 "}\n"
1059                 "\n"
1060                 "vec4 rgb_to_yuv(vec4 color)\n"
1061                 "{\n"
1062                         RGB_TO_YUV_FRAG("color")
1063                 "       return color;\n"
1064                 "}\n";
1066         static char *rgb_shader = 
1067                 "const vec3 black = vec3(0.0, 0.0, 0.0);\n"
1068                 "\n"
1069                 "vec4 yuv_to_rgb(vec4 color)\n"
1070                 "{\n"
1071                 "       return color;\n"
1072                 "}\n"
1073                 "vec4 rgb_to_yuv(vec4 color)\n"
1074                 "{\n"
1075                 "       return color;\n"
1076                 "}\n";
1078         static char *hsv_shader = 
1079                 "vec4 rgb_to_hsv(vec4 color)\n"
1080                 "{\n"
1081                         RGB_TO_HSV_FRAG("color")
1082                 "       return color;\n"
1083                 "}\n"
1084                 "\n"
1085                 "vec4 hsv_to_rgb(vec4 color)\n"
1086                 "{\n"
1087                         HSV_TO_RGB_FRAG("color")
1088                 "       return color;\n"
1089                 "}\n"
1090                 "\n";
1092         static char *show_rgbmask_shader = 
1093                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1094                 "{\n"
1095                 "       return vec4(1.0, 1.0, 1.0, min(color.a, color2.a));"
1096                 "}\n";
1098         static char *show_yuvmask_shader = 
1099                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1100                 "{\n"
1101                 "       return vec4(1.0, 0.5, 0.5, min(color.a, color2.a));"
1102                 "}\n";
1104         static char *nomask_shader = 
1105                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1106                 "{\n"
1107                 "       return vec4(color.rgb, min(color.a, color2.a));"
1108                 "}\n";
1110         extern unsigned char _binary_chromakey_sl_start[];
1111         static char *shader = (char*)_binary_chromakey_sl_start;
1112 SET_TRACE
1114         get_output()->to_texture();
1115         get_output()->enable_opengl();
1116         get_output()->init_screen();
1118 SET_TRACE
1119         char *shader_stack[] = { 0, 0, 0, 0, 0 };
1121 SET_TRACE
1122         switch(get_output()->get_color_model())
1123         {
1124                 case BC_YUV888:
1125                 case BC_YUVA8888:
1126                         shader_stack[0] = yuv_shader;
1127                         shader_stack[1] = hsv_shader;
1128                         if(config.show_mask) 
1129                                 shader_stack[2] = show_yuvmask_shader;
1130                         else
1131                                 shader_stack[2] = nomask_shader;
1132                         shader_stack[3] = shader;
1133                         break;
1135                 default:
1136                         shader_stack[0] = rgb_shader;
1137                         shader_stack[1] = hsv_shader;
1138                         if(config.show_mask) 
1139                                 shader_stack[2] = show_rgbmask_shader;
1140                         else
1141                                 shader_stack[2] = nomask_shader;
1142                         shader_stack[3] = shader;
1143                         break;
1144         }
1147 SET_TRACE
1148         unsigned int frag = VFrame::make_shader(0, 
1149                 shader_stack[0], 
1150                 shader_stack[1], 
1151                 shader_stack[2], 
1152                 shader_stack[3], 
1153                 0);
1155 SET_TRACE
1156         if(frag)
1157         {
1158                 glUseProgram(frag);
1159                 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
1160                 glUniform1f(glGetUniformLocation(frag, "red"), red);
1161                 glUniform1f(glGetUniformLocation(frag, "green"), green);
1162                 glUniform1f(glGetUniformLocation(frag, "blue"), blue);
1163                 glUniform1f(glGetUniformLocation(frag, "in_slope"), in_slope);
1164                 glUniform1f(glGetUniformLocation(frag, "out_slope"), out_slope);
1165                 glUniform1f(glGetUniformLocation(frag, "tolerance"), tolerance);
1166                 glUniform1f(glGetUniformLocation(frag, "tolerance_in"), tolerance_in);
1167                 glUniform1f(glGetUniformLocation(frag, "tolerance_out"), tolerance_out);
1168                 glUniform1f(glGetUniformLocation(frag, "sat"), sat);
1169                 glUniform1f(glGetUniformLocation(frag, "min_s"), min_s);
1170                 glUniform1f(glGetUniformLocation(frag, "min_s_in"), min_s_in);
1171                 glUniform1f(glGetUniformLocation(frag, "min_s_out"), min_s_out);
1172                 glUniform1f(glGetUniformLocation(frag, "min_v"), min_v);
1173                 glUniform1f(glGetUniformLocation(frag, "min_v_in"), min_v_in);
1174                 glUniform1f(glGetUniformLocation(frag, "min_v_out"), min_v_out);
1175                 glUniform1f(glGetUniformLocation(frag, "max_v"), max_v);
1176                 glUniform1f(glGetUniformLocation(frag, "max_v_in"), max_v_in);
1177                 glUniform1f(glGetUniformLocation(frag, "max_v_out"), max_v_out);
1178                 glUniform1f(glGetUniformLocation(frag, "spill_threshold"), spill_threshold);
1179                 glUniform1f(glGetUniformLocation(frag, "spill_amount"), spill_amount);
1180                 glUniform1f(glGetUniformLocation(frag, "alpha_offset"), alpha_offset);
1181                 glUniform1f(glGetUniformLocation(frag, "hue_key"), hue_key);
1182                 glUniform1f(glGetUniformLocation(frag, "saturation_key"), saturation_key);
1183                 glUniform1f(glGetUniformLocation(frag, "value_key"), value_key);
1184         }
1186 SET_TRACE
1188         get_output()->bind_texture(0);
1189         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1190         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1192         if(cmodel_components(get_output()->get_color_model()) == 3)
1193         {
1194                 glEnable(GL_BLEND);
1195                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1196                 get_output()->clear_pbuffer();
1197         }
1198         get_output()->draw_texture();
1200 SET_TRACE
1201         glUseProgram(0);
1202         get_output()->set_opengl_state(VFrame::SCREEN);
1203         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1204         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1205         glDisable(GL_BLEND);
1207 SET_TRACE
1209 #endif