b0a09757891033a0f96476aedc2b26accc8ff602
[goodguy/history.git] / cinelerra-5.1 / plugins / chromakeyhsv / chromakey.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2012 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
24 #include "chromakey.h"
25 #include "clip.h"
26 #include "bchash.h"
27 #include "filexml.h"
28 #include "guicast.h"
29 #include "keyframe.h"
30 #include "language.h"
31 #include "loadbalance.h"
32 #include "playback3d.h"
33 #include "bccolors.h"
34 #include "pluginvclient.h"
35 #include "vframe.h"
36
37 #include <stdint.h>
38 #include <string.h>
39 #include <unistd.h>
40
41
42
43 ChromaKeyConfig::ChromaKeyConfig ()
44 {
45         reset();
46 }
47
48 void ChromaKeyConfig::reset()
49
50 {
51   red = 0.0;
52   green = 1.0;
53   blue = 0.0;
54
55   min_brightness = 50.0;
56   max_brightness = 100.0;
57   tolerance = 15.0;
58   saturation = 0.0;
59   min_saturation = 50.0;
60
61   in_slope = 2;
62   out_slope = 2;
63   alpha_offset = 0;
64
65   spill_threshold = 0.0;
66   spill_amount = 90.0;
67
68   show_mask = 0;
69
70 }
71
72
73 void
74 ChromaKeyConfig::copy_from (ChromaKeyConfig & src)
75 {
76   red = src.red;
77   green = src.green;
78   blue = src.blue;
79   spill_threshold = src.spill_threshold;
80   spill_amount = src.spill_amount;
81   min_brightness = src.min_brightness;
82   max_brightness = src.max_brightness;
83   saturation = src.saturation;
84   min_saturation = src.min_saturation;
85   tolerance = src.tolerance;
86   in_slope = src.in_slope;
87   out_slope = src.out_slope;
88   alpha_offset = src.alpha_offset;
89   show_mask = src.show_mask;
90 }
91
92 int
93 ChromaKeyConfig::equivalent (ChromaKeyConfig & src)
94 {
95   return (EQUIV (red, src.red) &&
96           EQUIV (green, src.green) &&
97           EQUIV (blue, src.blue) &&
98           EQUIV (spill_threshold, src.spill_threshold) &&
99           EQUIV (spill_amount, src.spill_amount) &&
100           EQUIV (min_brightness, src.min_brightness) &&
101           EQUIV (max_brightness, src.max_brightness) &&
102           EQUIV (saturation, src.saturation) &&
103           EQUIV (min_saturation, src.min_saturation) &&
104           EQUIV (tolerance, src.tolerance) &&
105           EQUIV (in_slope, src.in_slope) &&
106           EQUIV (out_slope, src.out_slope) &&
107           EQUIV (show_mask, src.show_mask) &&
108           EQUIV (alpha_offset, src.alpha_offset));
109 }
110
111 void
112 ChromaKeyConfig::interpolate (ChromaKeyConfig & prev,
113                               ChromaKeyConfig & next,
114                               int64_t prev_frame,
115                               int64_t next_frame, int64_t current_frame)
116 {
117   double next_scale =
118     (double) (current_frame - prev_frame) / (next_frame - prev_frame);
119   double prev_scale =
120     (double) (next_frame - current_frame) / (next_frame - prev_frame);
121
122   this->red = prev.red * prev_scale + next.red * next_scale;
123   this->green = prev.green * prev_scale + next.green * next_scale;
124   this->blue = prev.blue * prev_scale + next.blue * next_scale;
125   this->spill_threshold =
126     prev.spill_threshold * prev_scale + next.spill_threshold * next_scale;
127   this->spill_amount =
128     prev.spill_amount * prev_scale + next.tolerance * next_scale;
129   this->min_brightness =
130     prev.min_brightness * prev_scale + next.min_brightness * next_scale;
131   this->max_brightness =
132     prev.max_brightness * prev_scale + next.max_brightness * next_scale;
133   this->saturation =
134     prev.saturation * prev_scale + next.saturation * next_scale;
135   this->min_saturation =
136     prev.min_saturation * prev_scale + next.min_saturation * next_scale;
137   this->tolerance = prev.tolerance * prev_scale + next.tolerance * next_scale;
138   this->in_slope = prev.in_slope * prev_scale + next.in_slope * next_scale;
139   this->out_slope = prev.out_slope * prev_scale + next.out_slope * next_scale;
140   this->alpha_offset =
141     prev.alpha_offset * prev_scale + next.alpha_offset * next_scale;
142   this->show_mask = next.show_mask;
143
144 }
145
146 int
147 ChromaKeyConfig::get_color ()
148 {
149   int red = (int) (CLIP (this->red, 0, 1) * 0xff);
150   int green = (int) (CLIP (this->green, 0, 1) * 0xff);
151   int blue = (int) (CLIP (this->blue, 0, 1) * 0xff);
152   return (red << 16) | (green << 8) | blue;
153 }
154
155
156
157 ChromaKeyWindow::ChromaKeyWindow (ChromaKeyHSV * plugin)
158  : PluginClientWindow(plugin,
159            400,
160            450,
161            400,
162            450,
163            0)
164 {
165   this->plugin = plugin;
166   color_thread = 0;
167 }
168
169 ChromaKeyWindow::~ChromaKeyWindow ()
170 {
171   delete color_thread;
172 }
173
174 void
175 ChromaKeyWindow::create_objects ()
176 {
177   int y = 10, y1, x1 = 0, x2 = 10;
178   int x = 30;
179
180   BC_Title *title;
181   BC_Bar *bar;
182   int ymargin = get_text_height(MEDIUMFONT) + 5;
183   int ymargin2 = get_text_height(MEDIUMFONT) + 10;
184
185   add_subwindow (title = new BC_Title (x2, y, _("Color:")));
186
187   add_subwindow (color = new ChromaKeyColor (plugin, this, x, y + 25));
188
189   add_subwindow (sample =
190                  new BC_SubWindow (x + color->get_w () + 10, y, 100, 50));
191   y += sample->get_h () + 10;
192
193   add_subwindow (use_colorpicker =
194                  new ChromaKeyUseColorPicker (plugin, this, x, y));
195   y += use_colorpicker->get_h() + 10;
196
197   add_subwindow (new ChromaKeyReset (plugin, this, x2+240, y));
198   add_subwindow (show_mask = new ChromaKeyShowMask (plugin, x2, y));
199         y += show_mask->get_h() + 5;
200
201         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
202         y += bar->get_h() + 5;
203         y1 = y;
204   add_subwindow (new BC_Title (x2, y, _("Key parameters:")));
205   y += ymargin;
206   add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
207   if(title->get_w() > x1) x1 = title->get_w();
208   y += ymargin;
209   add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
210   if(title->get_w() > x1) x1 = title->get_w();
211   y += ymargin;
212   add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
213   if(title->get_w() > x1) x1 = title->get_w();
214   y += ymargin;
215   add_subwindow (title = new BC_Title (x, y, _("Saturation Offset:")));
216   if(title->get_w() > x1) x1 = title->get_w();
217   y += ymargin;
218   add_subwindow (title = new BC_Title (x, y, _("Min Saturation:")));
219   if(title->get_w() > x1) x1 = title->get_w();
220   y += ymargin2;
221
222         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
223         y += bar->get_h() + 5;
224   add_subwindow (title = new BC_Title (x2, y, _("Mask tweaking:")));
225   y += ymargin;
226   add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
227   if(title->get_w() > x1) x1 = title->get_w();
228   y += ymargin;
229   add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
230   if(title->get_w() > x1) x1 = title->get_w();
231   y += ymargin;
232   add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
233   if(title->get_w() > x1) x1 = title->get_w();
234   y += ymargin2;
235
236
237
238         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
239         y += bar->get_h() + 5;
240   add_subwindow (title = new BC_Title (x2, y, _("Spill light control:")));
241   y += ymargin;
242   add_subwindow (title = new BC_Title (x, y, _("Spill Threshold:")));
243   if(title->get_w() > x1) x1 = title->get_w();
244   y += ymargin;
245   add_subwindow (title = new BC_Title (x, y, _("Spill Compensation:")));
246   if(title->get_w() > x1) x1 = title->get_w();
247   y += ymargin;
248
249
250         y = y1;
251   y += ymargin;
252         x1 += x;
253
254
255
256   add_subwindow (tolerance = new ChromaKeyTolerance (plugin, x1, y));
257   y += ymargin;
258   add_subwindow (min_brightness = new ChromaKeyMinBrightness (plugin, x1, y));
259   y += ymargin;
260   add_subwindow (max_brightness = new ChromaKeyMaxBrightness (plugin, x1, y));
261   y += ymargin;
262   add_subwindow (saturation = new ChromaKeySaturation (plugin, x1, y));
263   y += ymargin;
264   add_subwindow (min_saturation = new ChromaKeyMinSaturation (plugin, x1, y));
265   y += ymargin;
266
267         y += bar->get_h() + 5;
268   y += ymargin2;
269   add_subwindow (in_slope = new ChromaKeyInSlope (plugin, x1, y));
270   y += ymargin;
271   add_subwindow (out_slope = new ChromaKeyOutSlope (plugin, x1, y));
272   y += ymargin;
273   add_subwindow (alpha_offset = new ChromaKeyAlphaOffset (plugin, x1, y));
274    y += ymargin;
275
276         y += bar->get_h() + 5;
277   y += ymargin2;
278   add_subwindow (spill_threshold = new ChromaKeySpillThreshold (plugin, x1, y));
279   y += ymargin;
280   add_subwindow (spill_amount = new ChromaKeySpillAmount (plugin, x1, y));
281
282   color_thread = new ChromaKeyColorThread (plugin, this);
283
284   update_sample ();
285   show_window ();
286 }
287
288 void
289 ChromaKeyWindow::update_sample ()
290 {
291   sample->set_color (plugin->config.get_color ());
292   sample->draw_box (0, 0, sample->get_w (), sample->get_h ());
293   sample->set_color (BLACK);
294   sample->draw_rectangle (0, 0, sample->get_w (), sample->get_h ());
295   sample->flash ();
296 }
297
298 void ChromaKeyWindow::done_event(int result)
299 {
300         color_thread->close_window();
301 }
302
303
304 ChromaKeyColor::ChromaKeyColor (ChromaKeyHSV * plugin,
305                                   ChromaKeyWindow * gui, int x, int y):
306 BC_GenericButton (x, y, _("Color..."))
307 {
308   this->plugin = plugin;
309   this->gui = gui;
310 }
311
312 int
313 ChromaKeyColor::handle_event ()
314 {
315   gui->color_thread->start_window (plugin->config.get_color (), 0xff);
316   return 1;
317 }
318
319
320
321
322 ChromaKeyMinBrightness::ChromaKeyMinBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
323             y,
324             0,
325             200, 200, (float) 0, (float) 100, plugin->config.min_brightness)
326 {
327   this->plugin = plugin;
328   set_precision (0.01);
329 }
330
331 int
332 ChromaKeyMinBrightness::handle_event ()
333 {
334   plugin->config.min_brightness = get_value ();
335   plugin->send_configure_change ();
336   return 1;
337 }
338
339 ChromaKeyMaxBrightness::ChromaKeyMaxBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
340             y,
341             0,
342             200, 200, (float) 0, (float) 100, plugin->config.max_brightness)
343 {
344   this->plugin = plugin;
345   set_precision (0.01);
346 }
347
348 int
349 ChromaKeyMaxBrightness::handle_event ()
350 {
351   plugin->config.max_brightness = get_value ();
352   plugin->send_configure_change ();
353   return 1;
354 }
355
356
357 ChromaKeySaturation::ChromaKeySaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
358             y,
359             0, 200, 200, (float) 0, (float) 100, plugin->config.saturation)
360 {
361   this->plugin = plugin;
362   set_precision (0.01);
363 }
364
365 int
366 ChromaKeySaturation::handle_event ()
367 {
368   plugin->config.saturation = get_value ();
369   plugin->send_configure_change ();
370   return 1;
371 }
372
373 ChromaKeyMinSaturation::ChromaKeyMinSaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
374             y,
375             0,
376             200, 200, (float) 0, (float) 100, plugin->config.min_saturation)
377 {
378   this->plugin = plugin;
379   set_precision (0.01);
380 }
381
382 int
383 ChromaKeyMinSaturation::handle_event ()
384 {
385   plugin->config.min_saturation = get_value ();
386   plugin->send_configure_change ();
387   return 1;
388 }
389
390
391 ChromaKeyTolerance::ChromaKeyTolerance (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
392             y,
393             0, 200, 200, (float) 0, (float) 100, plugin->config.tolerance)
394 {
395   this->plugin = plugin;
396   set_precision (0.01);
397 }
398
399 int
400 ChromaKeyTolerance::handle_event ()
401 {
402   plugin->config.tolerance = get_value ();
403   plugin->send_configure_change ();
404   return 1;
405 }
406
407
408
409 ChromaKeyInSlope::ChromaKeyInSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
410             y,
411             0, 200, 200, (float) 0, (float) 20, plugin->config.in_slope)
412 {
413   this->plugin = plugin;
414   set_precision (0.01);
415 }
416
417 int
418 ChromaKeyInSlope::handle_event ()
419 {
420   plugin->config.in_slope = get_value ();
421   plugin->send_configure_change ();
422   return 1;
423 }
424
425
426 ChromaKeyOutSlope::ChromaKeyOutSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
427             y,
428             0, 200, 200, (float) 0, (float) 20, plugin->config.out_slope)
429 {
430   this->plugin = plugin;
431   set_precision (0.01);
432 }
433
434 int
435 ChromaKeyOutSlope::handle_event ()
436 {
437   plugin->config.out_slope = get_value ();
438   plugin->send_configure_change ();
439   return 1;
440 }
441
442
443 ChromaKeyAlphaOffset::ChromaKeyAlphaOffset (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
444             y,
445             0,
446             200, 200, (float) -100, (float) 100, plugin->config.alpha_offset)
447 {
448   this->plugin = plugin;
449   set_precision (0.01);
450 }
451
452 int
453 ChromaKeyAlphaOffset::handle_event ()
454 {
455   plugin->config.alpha_offset = get_value ();
456   plugin->send_configure_change ();
457   return 1;
458 }
459
460 ChromaKeyShowMask::ChromaKeyShowMask (ChromaKeyHSV * plugin, int x, int y):BC_CheckBox (x, y, plugin->config.show_mask,
461              _
462              ("Show Mask"))
463 {
464   this->plugin = plugin;
465
466 }
467
468 int
469 ChromaKeyShowMask::handle_event ()
470 {
471   plugin->config.show_mask = get_value ();
472   plugin->send_configure_change ();
473   return 1;
474 }
475
476 ChromaKeyReset::ChromaKeyReset (ChromaKeyHSV *plugin, ChromaKeyWindow *gui, int x, int y)
477  :BC_GenericButton(x, y, _("Reset"))
478 {
479   this->plugin = plugin;
480   this->gui = gui;
481 }
482
483 int ChromaKeyReset::handle_event ()
484 {
485   plugin->config.reset();
486   gui->update_gui();
487   plugin->send_configure_change();
488   return 1;
489 }
490
491 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyHSV * plugin, ChromaKeyWindow * gui, int x, int y)
492  : BC_GenericButton (x, y,
493                   _
494                   ("Use color picker"))
495 {
496   this->plugin = plugin;
497   this->gui = gui;
498 }
499
500 int
501 ChromaKeyUseColorPicker::handle_event ()
502 {
503   plugin->config.red = plugin->get_red ();
504   plugin->config.green = plugin->get_green ();
505   plugin->config.blue = plugin->get_blue ();
506
507   gui->update_sample ();
508
509
510   plugin->send_configure_change ();
511   return 1;
512 }
513
514
515
516 ChromaKeySpillThreshold::ChromaKeySpillThreshold (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
517             y,
518             0,
519             200, 200, (float) 0, (float) 100, plugin->config.spill_threshold)
520 {
521   this->plugin = plugin;
522   set_precision (0.01);
523 }
524
525 int
526 ChromaKeySpillThreshold::handle_event ()
527 {
528   plugin->config.spill_threshold = get_value ();
529   plugin->send_configure_change ();
530   return 1;
531 }
532
533 ChromaKeySpillAmount::ChromaKeySpillAmount (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
534             y,
535             0, 200, 200, (float) 0, (float) 100, plugin->config.spill_amount)
536 {
537   this->plugin = plugin;
538   set_precision (0.01);
539 }
540
541 int
542 ChromaKeySpillAmount::handle_event ()
543 {
544   plugin->config.spill_amount = get_value ();
545   plugin->send_configure_change ();
546   return 1;
547 }
548
549
550
551
552
553
554 ChromaKeyColorThread::ChromaKeyColorThread (ChromaKeyHSV * plugin, ChromaKeyWindow * gui)
555  : ColorPicker (1, _("Inner color"))
556 {
557   this->plugin = plugin;
558   this->gui = gui;
559 }
560
561 int
562 ChromaKeyColorThread::handle_new_color (int output, int alpha)
563 {
564   plugin->config.red = (float) (output & 0xff0000) / 0xff0000;
565   plugin->config.green = (float) (output & 0xff00) / 0xff00;
566   plugin->config.blue = (float) (output & 0xff) / 0xff;
567
568   get_gui()->unlock_window();
569   gui->lock_window("ChromaKeyColorThread::handle_new_color");
570   gui->update_sample ();
571   gui->unlock_window();
572   get_gui()->lock_window("ChromaKeyColorThread::handle_new_color");
573
574
575   plugin->send_configure_change ();
576   return 1;
577 }
578
579
580
581
582
583
584
585
586 ChromaKeyServer::ChromaKeyServer (ChromaKeyHSV * plugin):LoadServer (plugin->PluginClient::smp + 1,
587             plugin->PluginClient::smp +
588             1)
589 {
590   this->plugin = plugin;
591 }
592
593 void
594 ChromaKeyServer::init_packages ()
595 {
596   for (int i = 0; i < get_total_packages (); i++)
597     {
598       ChromaKeyPackage *pkg = (ChromaKeyPackage *) get_package (i);
599       pkg->y1 = plugin->input->get_h () * i / get_total_packages ();
600       pkg->y2 = plugin->input->get_h () * (i + 1) / get_total_packages ();
601     }
602
603 }
604 LoadClient *
605 ChromaKeyServer::new_client ()
606 {
607   return new ChromaKeyUnit (plugin, this);
608 }
609
610 LoadPackage *
611 ChromaKeyServer::new_package ()
612 {
613   return new ChromaKeyPackage;
614 }
615
616
617
618 ChromaKeyPackage::ChromaKeyPackage ():LoadPackage ()
619 {
620 }
621
622 ChromaKeyUnit::ChromaKeyUnit (ChromaKeyHSV * plugin, ChromaKeyServer * server):LoadClient
623   (server)
624 {
625   this->plugin = plugin;
626 }
627
628
629
630
631 #define ABS(a) ((a<0)?-(a):a)
632 // Reuse as much as possible in the opengl version
633 #define OUTER_VARIABLES \
634   float red = plugin->config.red; \
635   float green = plugin->config.green; \
636   float blue = plugin->config.blue; \
637  \
638   float in_slope = plugin->config.in_slope / 100; \
639   float out_slope = plugin->config.out_slope / 100; \
640  \
641   float tolerance = plugin->config.tolerance / 100; \
642   float tolerance_in = tolerance - in_slope; \
643   float tolerance_out = tolerance + out_slope; \
644  \
645   float sat = plugin->config.saturation / 100; \
646   float min_s = plugin->config.min_saturation / 100; \
647   float min_s_in = min_s + in_slope; \
648   float min_s_out = min_s - out_slope; \
649  \
650   float min_v = plugin->config.min_brightness / 100; \
651   float min_v_in = min_v + in_slope; \
652   float min_v_out = min_v - out_slope; \
653  \
654   float max_v = plugin->config.max_brightness / 100; \
655   float max_v_in = max_v - in_slope; \
656   float max_v_out = max_v + out_slope; \
657  \
658   float spill_threshold = plugin->config.spill_threshold / 100; \
659   float spill_amount = 1.0 - plugin->config.spill_amount / 100; \
660  \
661   float alpha_offset = plugin->config.alpha_offset / 100; \
662  \
663 /* Convert RGB key to HSV key */ \
664         float hue_key, saturation_key, value_key; \
665         HSV::rgb_to_hsv(red,  \
666                 green, \
667                 blue, \
668                 hue_key, \
669                 saturation_key, \
670                 value_key);
671
672
673 template <typename component_type>
674 void ChromaKeyUnit::process_chromakey(int components,
675         component_type max,
676         bool use_yuv,
677         ChromaKeyPackage *pkg)
678
679         OUTER_VARIABLES
680
681         int w = plugin->input->get_w();
682
683         for (int i = pkg->y1; i < pkg->y2; i++)
684         {
685         component_type *row = (component_type *) plugin->input->get_rows ()[i];
686
687         for (int j = 0; j < w; j++)
688                 {
689                         float a = 1;
690
691                         float r = (float) row[0] / max;
692                         float g = (float) row[1] / max;
693                         float b = (float) row[2] / max;
694
695                         float h, s, v;
696
697                         float av = 1, ah = 1, as = 1, avm = 1;
698                         bool has_match = true;
699
700                         if (use_yuv)
701                 {
702 /* Convert pixel to RGB float */
703                         float y = r;
704                         float u = g;
705                         float v = b;
706                         YUV::yuv_to_rgb_f (r, g, b, y, u - 0.5, v - 0.5);
707                 }
708
709                         HSV::rgb_to_hsv (r, g, b, h, s, v);
710
711 // First, test if the hue is in range
712
713 /* Hue wrap */
714                         if(h <= hue_key - tolerance_in * 180.0)
715                                 h += 360;
716                         else
717                         if(h >= hue_key + tolerance_in * 180.0)
718                                 h -= 360;
719
720
721                         if (tolerance == 0)
722                     ah = 1.0;
723                         else
724                         if (ABS (h - hue_key) < tolerance_in * 180)
725                     ah = 0;
726                         else
727                         if ((out_slope != 0) && (ABS (h - hue_key) < tolerance * 180))
728 /* we scale alpha between 0 and 1/2 */
729                     ah = ABS (h - hue_key) / tolerance / 360;
730                         else
731                         if (ABS (h - hue_key) < tolerance_out * 180)
732 /* we scale alpha between 1/2 and 1 */
733                     ah = ABS (h - hue_key) / tolerance_out / 360;
734                         else
735                     has_match = false;
736
737 // Check if the saturation matches
738                         if (has_match)
739                 {
740                         if (min_s == 0)
741                                         as = 0;
742                         else if (s - sat >= min_s_in)
743                                         as = 0;
744                         else if ((out_slope != 0) && (s - sat > min_s))
745                                         as = (s - sat - min_s) / (min_s * 2);
746                         else if (s - sat > min_s_out)
747                                         as = (s - sat - min_s_out) / (min_s_out * 2);
748                         else
749                                         has_match = false;
750                 }
751
752 // Check if the value is more than the minimun
753                         if (has_match)
754                 {
755                         if (min_v == 0)
756                                         av = 0;
757                         else if (v >= min_v_in)
758                                         av = 0;
759                         else if ((out_slope != 0) && (v > min_v))
760                                         av = (v - min_v) / (min_v * 2);
761                         else if (v > min_v_out)
762                                         av = (v - min_v_out) / (min_v_out * 2);
763                         else
764                                         has_match = false;
765                 }
766
767 // Check if the value is less than the maximum
768                         if (has_match)
769                 {
770                         if (max_v == 0)
771                                         avm = 1;
772                         else if (v <= max_v_in)
773                                         avm = 0;
774                         else if ((out_slope != 0) && (v < max_v))
775                                         avm = (v - max_v) / (max_v * 2);
776                         else if (v < max_v_out)
777                                         avm = (v - max_v_out) / (max_v_out * 2);
778                         else
779                                         has_match = false;
780                 }
781
782           // If the color is part of the key, update the alpha channel
783           if (has_match)
784             a = MAX (MAX (ah, av), MAX (as, avm));
785
786           // Spill light processing
787           if ((ABS (h - hue_key) < spill_threshold * 180) ||
788               ((ABS (h - hue_key) > 360)
789                && (ABS (h - hue_key) - 360 < spill_threshold * 180)))
790             {
791               s = s * spill_amount * ABS (h - hue_key) / (spill_threshold * 180);
792
793               HSV::hsv_to_rgb (r, g, b, h, s, v);
794
795               if (use_yuv)
796                 {
797                   float y;
798                   float u;
799                   float v;
800                   YUV::rgb_to_yuv_f (r, g, b, y, u, v);
801               CLAMP (y, 0, 1.0);
802               CLAMP (u, 0, 1.0);
803               CLAMP (v, 0, 1.0);
804                   row[0] = (component_type) ((float) y * max);
805                   row[1] = (component_type) ((float) (u + 0.5) * max);
806                   row[2] = (component_type) ((float) (v + 0.5) * max);
807                 }
808               else
809                 {
810               CLAMP (r, 0, 1.0);
811               CLAMP (g, 0, 1.0);
812               CLAMP (b, 0, 1.0);
813                   row[0] = (component_type) ((float) r * max);
814                   row[1] = (component_type) ((float) g * max);
815                   row[2] = (component_type) ((float) b * max);
816                 }
817             }
818
819           a += alpha_offset;
820           CLAMP (a, 0.0, 1.0);
821
822           if (plugin->config.show_mask)
823             {
824
825               if (use_yuv)
826                 {
827                   row[0] = (component_type) ((float) a * max);
828                   row[1] = (component_type) ((float) max / 2);
829                   row[2] = (component_type) ((float) max / 2);
830                 }
831               else
832                 {
833                   row[0] = (component_type) ((float) a * max);
834                   row[1] = (component_type) ((float) a * max);
835                   row[2] = (component_type) ((float) a * max);
836                 }
837             }
838
839           /* Multiply alpha and put back in frame */
840           if (components == 4)
841             {
842               row[3] = MIN ((component_type) (a * max), row[3]);
843             }
844           else if (use_yuv)
845             {
846               row[0] = (component_type) ((float) a * row[0]);
847               row[1] =
848                 (component_type) ((float) a * (row[1] - (max / 2 + 1)) +
849                                   max / 2 + 1);
850               row[2] =
851                 (component_type) ((float) a * (row[2] - (max / 2 + 1)) +
852                                   max / 2 + 1);
853             }
854           else
855             {
856               row[0] = (component_type) ((float) a * row[0]);
857               row[1] = (component_type) ((float) a * row[1]);
858               row[2] = (component_type) ((float) a * row[2]);
859             }
860
861           row += components;
862         }
863     }
864 }
865
866
867
868
869 void ChromaKeyUnit::process_package(LoadPackage *package)
870 {
871         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
872
873
874         switch(plugin->input->get_color_model())
875         {
876                 case BC_RGB_FLOAT:
877                         process_chromakey<float> (3, 1.0, 0, pkg);
878                         break;
879                 case BC_RGBA_FLOAT:
880                         process_chromakey<float> ( 4, 1.0, 0, pkg);
881                         break;
882                 case BC_RGB888:
883                         process_chromakey<unsigned char> ( 3, 0xff, 0, pkg);
884                         break;
885                 case BC_RGBA8888:
886                         process_chromakey<unsigned char> ( 4, 0xff, 0, pkg);
887                         break;
888                 case BC_YUV888:
889                         process_chromakey<unsigned char> ( 3, 0xff, 1, pkg);
890                         break;
891                 case BC_YUVA8888:
892                         process_chromakey<unsigned char> ( 4, 0xff, 1, pkg);
893                         break;
894                 case BC_YUV161616:
895                         process_chromakey<uint16_t> (3, 0xffff, 1, pkg);
896                         break;
897                 case BC_YUVA16161616:
898                         process_chromakey<uint16_t> (4, 0xffff, 1, pkg);
899                         break;
900         }
901
902 }
903
904
905
906
907
908 REGISTER_PLUGIN(ChromaKeyHSV)
909
910
911
912 ChromaKeyHSV::ChromaKeyHSV(PluginServer *server)
913  : PluginVClient(server)
914 {
915
916         engine = 0;
917 }
918
919 ChromaKeyHSV::~ChromaKeyHSV()
920 {
921
922         if(engine) delete engine;
923 }
924
925
926 int ChromaKeyHSV::process_buffer(VFrame *frame,
927                 int64_t start_position,
928                 double frame_rate)
929 {
930         load_configuration();
931         this->input = frame;
932         this->output = frame;
933
934
935         read_frame(frame,
936                 0,
937                 start_position,
938                 frame_rate,
939                 get_use_opengl());
940         if(get_use_opengl()) return run_opengl();
941
942
943         if(!engine) engine = new ChromaKeyServer(this);
944         engine->process_packages();
945
946         return 0;
947 }
948
949 const char* ChromaKeyHSV::plugin_title() { return _("Chroma key (HSV)"); }
950 int ChromaKeyHSV::is_realtime() { return 1; }
951
952
953 LOAD_CONFIGURATION_MACRO(ChromaKeyHSV, ChromaKeyConfig)
954
955
956 void ChromaKeyHSV::save_data(KeyFrame * keyframe)
957 {
958         FileXML output;
959         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
960         output.tag.set_title("CHROMAKEY_HSV");
961         output.tag.set_property("RED", config.red);
962         output.tag.set_property("GREEN", config.green);
963         output.tag.set_property("BLUE", config.blue);
964         output.tag.set_property("MIN_BRIGHTNESS", config.min_brightness);
965         output.tag.set_property("MAX_BRIGHTNESS", config.max_brightness);
966         output.tag.set_property("SATURATION", config.saturation);
967         output.tag.set_property("MIN_SATURATION", config.min_saturation);
968         output.tag.set_property("TOLERANCE", config.tolerance);
969         output.tag.set_property("IN_SLOPE", config.in_slope);
970         output.tag.set_property("OUT_SLOPE", config.out_slope);
971         output.tag.set_property("ALPHA_OFFSET", config.alpha_offset);
972         output.tag.set_property("SPILL_THRESHOLD", config.spill_threshold);
973         output.tag.set_property("SPILL_AMOUNT", config.spill_amount);
974         output.tag.set_property("SHOW_MASK", config.show_mask);
975         output.append_tag();
976         output.tag.set_title("/CHROMAKEY_HSV");
977         output.append_tag();
978         output.append_newline();
979         output.terminate_string();
980 }
981
982 void ChromaKeyHSV::read_data(KeyFrame * keyframe)
983 {
984         FileXML input;
985
986         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
987
988         while( !input.read_tag() ) {
989                 if( input.tag.title_is("CHROMAKEY_HSV") ) {
990                         config.red = input.tag.get_property("RED", config.red);
991                         config.green = input.tag.get_property("GREEN", config.green);
992                         config.blue = input.tag.get_property("BLUE", config.blue);
993                         config.min_brightness =
994                                 input.tag.get_property("MIN_BRIGHTNESS", config.min_brightness);
995                         config.max_brightness =
996                                 input.tag.get_property("MAX_BRIGHTNESS", config.max_brightness);
997                         config.saturation =
998                                 input.tag.get_property("SATURATION", config.saturation);
999                         config.min_saturation =
1000                                 input.tag.get_property("MIN_SATURATION", config.min_saturation);
1001                         config.tolerance =
1002                                 input.tag.get_property("TOLERANCE", config.tolerance);
1003                         config.in_slope =
1004                                 input.tag.get_property("IN_SLOPE", config.in_slope);
1005                         config.out_slope =
1006                                 input.tag.get_property("OUT_SLOPE", config.out_slope);
1007                         config.alpha_offset =
1008                                 input.tag.get_property("ALPHA_OFFSET", config.alpha_offset);
1009                         config.spill_threshold =
1010                                 input.tag.get_property("SPILL_THRESHOLD",
1011                                                         config.spill_threshold);
1012                         config.spill_amount =
1013                                 input.tag.get_property("SPILL_AMOUNT", config.spill_amount);
1014                         config.show_mask =
1015                                 input.tag.get_property("SHOW_MASK", config.show_mask);
1016                 }
1017         }
1018 }
1019
1020
1021 NEW_WINDOW_MACRO(ChromaKeyHSV, ChromaKeyWindow)
1022
1023 void ChromaKeyHSV::update_gui()
1024 {
1025         if( thread ) {
1026                 load_configuration();
1027                 ChromaKeyWindow *window = (ChromaKeyWindow*)thread->window;
1028                 window->lock_window();
1029                 window->update_gui();
1030                 window->unlock_window();
1031         }
1032 }
1033
1034 void ChromaKeyWindow::update_gui()
1035 {
1036         ChromaKeyConfig &config = plugin->config;
1037         min_brightness->update(config.min_brightness);
1038         max_brightness->update(config.max_brightness);
1039         saturation->update(config.saturation);
1040         min_saturation->update(config.min_saturation);
1041         tolerance->update(config.tolerance);
1042         in_slope->update(config.in_slope);
1043         out_slope->update(config.out_slope);
1044         alpha_offset->update(config.alpha_offset);
1045         spill_threshold->update(config.spill_threshold);
1046         spill_amount->update(config.spill_amount);
1047         show_mask->update(config.show_mask);
1048         update_sample();
1049 }
1050
1051
1052
1053 int ChromaKeyHSV::handle_opengl()
1054 {
1055 #ifdef HAVE_GL
1056 // For macro
1057         ChromaKeyHSV *plugin = this;
1058         OUTER_VARIABLES
1059
1060         static const char *yuv_shader =
1061                 "const vec3 black = vec3(0.0, 0.5, 0.5);\n"
1062                 "\n"
1063                 "vec4 yuv_to_rgb(vec4 color)\n"
1064                 "{\n"
1065                         YUV_TO_RGB_FRAG("color")
1066                 "       return color;\n"
1067                 "}\n"
1068                 "\n"
1069                 "vec4 rgb_to_yuv(vec4 color)\n"
1070                 "{\n"
1071                         RGB_TO_YUV_FRAG("color")
1072                 "       return color;\n"
1073                 "}\n";
1074
1075         static const char *rgb_shader =
1076                 "const vec3 black = vec3(0.0, 0.0, 0.0);\n"
1077                 "\n"
1078                 "vec4 yuv_to_rgb(vec4 color)\n"
1079                 "{\n"
1080                 "       return color;\n"
1081                 "}\n"
1082                 "vec4 rgb_to_yuv(vec4 color)\n"
1083                 "{\n"
1084                 "       return color;\n"
1085                 "}\n";
1086
1087         static const char *hsv_shader =
1088                 "vec4 rgb_to_hsv(vec4 color)\n"
1089                 "{\n"
1090                         RGB_TO_HSV_FRAG("color")
1091                 "       return color;\n"
1092                 "}\n"
1093                 "\n"
1094                 "vec4 hsv_to_rgb(vec4 color)\n"
1095                 "{\n"
1096                         HSV_TO_RGB_FRAG("color")
1097                 "       return color;\n"
1098                 "}\n"
1099                 "\n";
1100
1101         static const char *show_rgbmask_shader =
1102                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1103                 "{\n"
1104                 "       return vec4(1.0, 1.0, 1.0, min(color.a, color2.a));"
1105                 "}\n";
1106
1107         static const char *show_yuvmask_shader =
1108                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1109                 "{\n"
1110                 "       return vec4(1.0, 0.5, 0.5, min(color.a, color2.a));"
1111                 "}\n";
1112
1113         static const char *nomask_shader =
1114                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1115                 "{\n"
1116                 "       return vec4(color.rgb, min(color.a, color2.a));"
1117                 "}\n";
1118
1119         extern unsigned char _binary_chromakey_sl_start[];
1120         static const char *shader = (char*)_binary_chromakey_sl_start;
1121
1122         get_output()->to_texture();
1123         get_output()->enable_opengl();
1124         get_output()->init_screen();
1125
1126         const char* shader_stack[] = { 0, 0, 0, 0, 0 };
1127
1128         switch(get_output()->get_color_model())
1129         {
1130                 case BC_YUV888:
1131                 case BC_YUVA8888:
1132                         shader_stack[0] = yuv_shader;
1133                         shader_stack[1] = hsv_shader;
1134                         if(config.show_mask)
1135                                 shader_stack[2] = show_yuvmask_shader;
1136                         else
1137                                 shader_stack[2] = nomask_shader;
1138                         shader_stack[3] = shader;
1139                         break;
1140
1141                 default:
1142                         shader_stack[0] = rgb_shader;
1143                         shader_stack[1] = hsv_shader;
1144                         if(config.show_mask)
1145                                 shader_stack[2] = show_rgbmask_shader;
1146                         else
1147                                 shader_stack[2] = nomask_shader;
1148                         shader_stack[3] = shader;
1149                         break;
1150         }
1151
1152
1153         unsigned int frag = VFrame::make_shader(0,
1154                 shader_stack[0],
1155                 shader_stack[1],
1156                 shader_stack[2],
1157                 shader_stack[3],
1158                 0);
1159
1160         if(frag)
1161         {
1162                 glUseProgram(frag);
1163                 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
1164                 glUniform1f(glGetUniformLocation(frag, "red"), red);
1165                 glUniform1f(glGetUniformLocation(frag, "green"), green);
1166                 glUniform1f(glGetUniformLocation(frag, "blue"), blue);
1167                 glUniform1f(glGetUniformLocation(frag, "in_slope"), in_slope);
1168                 glUniform1f(glGetUniformLocation(frag, "out_slope"), out_slope);
1169                 glUniform1f(glGetUniformLocation(frag, "tolerance"), tolerance);
1170                 glUniform1f(glGetUniformLocation(frag, "tolerance_in"), tolerance_in);
1171                 glUniform1f(glGetUniformLocation(frag, "tolerance_out"), tolerance_out);
1172                 glUniform1f(glGetUniformLocation(frag, "sat"), sat);
1173                 glUniform1f(glGetUniformLocation(frag, "min_s"), min_s);
1174                 glUniform1f(glGetUniformLocation(frag, "min_s_in"), min_s_in);
1175                 glUniform1f(glGetUniformLocation(frag, "min_s_out"), min_s_out);
1176                 glUniform1f(glGetUniformLocation(frag, "min_v"), min_v);
1177                 glUniform1f(glGetUniformLocation(frag, "min_v_in"), min_v_in);
1178                 glUniform1f(glGetUniformLocation(frag, "min_v_out"), min_v_out);
1179                 glUniform1f(glGetUniformLocation(frag, "max_v"), max_v);
1180                 glUniform1f(glGetUniformLocation(frag, "max_v_in"), max_v_in);
1181                 glUniform1f(glGetUniformLocation(frag, "max_v_out"), max_v_out);
1182                 glUniform1f(glGetUniformLocation(frag, "spill_threshold"), spill_threshold);
1183                 glUniform1f(glGetUniformLocation(frag, "spill_amount"), spill_amount);
1184                 glUniform1f(glGetUniformLocation(frag, "alpha_offset"), alpha_offset);
1185                 glUniform1f(glGetUniformLocation(frag, "hue_key"), hue_key);
1186                 glUniform1f(glGetUniformLocation(frag, "saturation_key"), saturation_key);
1187                 glUniform1f(glGetUniformLocation(frag, "value_key"), value_key);
1188         }
1189
1190
1191         get_output()->bind_texture(0);
1192         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1193         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1194
1195         if(BC_CModels::components(get_output()->get_color_model()) == 3)
1196         {
1197                 glEnable(GL_BLEND);
1198                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1199                 get_output()->clear_pbuffer();
1200         }
1201         get_output()->draw_texture();
1202
1203         glUseProgram(0);
1204         get_output()->set_opengl_state(VFrame::SCREEN);
1205         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1206         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1207         glDisable(GL_BLEND);
1208
1209
1210 #endif
1211         return 0;
1212 }
1213