154e28489cf9330f3eee962efad6e63d3dfa9965
[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 r, g, b, a = 1;
690                         if (!use_yuv) {
691                                 r = (float) row[0] / max;
692                                 g = (float) row[1] / max;
693                                 b = (float) row[2] / max;
694                         }
695                         else
696                         YUV::yuv.yuv_to_rgb_f (r, g, b, row[0], row[1], row[2]);
697
698                         float h, s, v;
699
700                         float av = 1, ah = 1, as = 1, avm = 1;
701                         bool has_match = true;
702
703                         HSV::rgb_to_hsv (r, g, b, h, s, v);
704
705 // First, test if the hue is in range
706
707 /* Hue wrap */
708                         if(h <= hue_key - tolerance_in * 180.0)
709                                 h += 360.0;
710                         else
711                         if(h >= hue_key + tolerance_in * 180.0)
712                                 h -= 360.0;
713
714
715                         if (tolerance == 0)
716                     ah = 1.0;
717                         else
718                         if (ABS (h - hue_key) < tolerance_in * 180)
719                     ah = 0;
720                         else
721                         if ((out_slope != 0) && (ABS (h - hue_key) < tolerance * 180))
722 /* we scale alpha between 0 and 1/2 */
723                     ah = ABS (h - hue_key) / tolerance / 360;
724                         else
725                         if (ABS (h - hue_key) < tolerance_out * 180)
726 /* we scale alpha between 1/2 and 1 */
727                     ah = ABS (h - hue_key) / tolerance_out / 360;
728                         else
729                     has_match = false;
730
731 // Check if the saturation matches
732                         if (has_match)
733                 {
734                         if (min_s == 0)
735                                         as = 0;
736                         else if (s - sat >= min_s_in)
737                                         as = 0;
738                         else if ((out_slope != 0) && (s - sat > min_s))
739                                         as = (s - sat - min_s) / (min_s * 2);
740                         else if (s - sat > min_s_out)
741                                         as = (s - sat - min_s_out) / (min_s_out * 2);
742                         else
743                                         has_match = false;
744                 }
745
746 // Check if the value is more than the minimun
747                         if (has_match)
748                 {
749                         if (min_v == 0)
750                                         av = 0;
751                         else if (v >= min_v_in)
752                                         av = 0;
753                         else if ((out_slope != 0) && (v > min_v))
754                                         av = (v - min_v) / (min_v * 2);
755                         else if (v > min_v_out)
756                                         av = (v - min_v_out) / (min_v_out * 2);
757                         else
758                                         has_match = false;
759                 }
760
761 // Check if the value is less than the maximum
762                         if (has_match)
763                 {
764                         if (max_v == 0)
765                                         avm = 1;
766                         else if (v <= max_v_in)
767                                         avm = 0;
768                         else if ((out_slope != 0) && (v < max_v))
769                                         avm = (v - max_v) / (max_v * 2);
770                         else if (v < max_v_out)
771                                         avm = (v - max_v_out) / (max_v_out * 2);
772                         else
773                                         has_match = false;
774                 }
775
776           // If the color is part of the key, update the alpha channel
777           if (has_match)
778             a = MAX (MAX (ah, av), MAX (as, avm));
779
780           // Spill light processing
781           if ((ABS (h - hue_key) < spill_threshold * 180) ||
782               ((ABS (h - hue_key) > 360)
783                && (ABS (h - hue_key) - 360 < spill_threshold * 180)))
784             {
785               s = s * spill_amount * ABS (h - hue_key) / (spill_threshold * 180);
786
787               HSV::hsv_to_rgb (r, g, b, h, s, v);
788
789                 if (!use_yuv) {
790                         row[0] = (component_type) ((float) r * max);
791                         row[1] = (component_type) ((float) g * max);
792                         row[2] = (component_type) ((float) b * max);
793                 }
794                 else
795                         YUV::yuv.rgb_to_yuv_f(r, g, b, row[0], row[1], row[2]);
796           }
797
798           a += alpha_offset;
799           CLAMP (a, 0.0, 1.0);
800
801           if (plugin->config.show_mask)
802             {
803
804               if (use_yuv)
805                 {
806                   row[0] = (component_type) ((float) a * max);
807                   row[1] = (component_type) ((float) max / 2);
808                   row[2] = (component_type) ((float) max / 2);
809                 }
810               else
811                 {
812                   row[0] = (component_type) ((float) a * max);
813                   row[1] = (component_type) ((float) a * max);
814                   row[2] = (component_type) ((float) a * max);
815                 }
816             }
817
818           /* Multiply alpha and put back in frame */
819           if (components == 4)
820             {
821               row[3] = MIN ((component_type) (a * max), row[3]);
822             }
823           else if (use_yuv)
824             {
825               row[0] = (component_type) ((float) a * row[0]);
826               row[1] =
827                 (component_type) ((float) a * (row[1] - (max / 2 + 1)) +
828                                   max / 2 + 1);
829               row[2] =
830                 (component_type) ((float) a * (row[2] - (max / 2 + 1)) +
831                                   max / 2 + 1);
832             }
833           else
834             {
835               row[0] = (component_type) ((float) a * row[0]);
836               row[1] = (component_type) ((float) a * row[1]);
837               row[2] = (component_type) ((float) a * row[2]);
838             }
839
840           row += components;
841         }
842     }
843 }
844
845
846
847
848 void ChromaKeyUnit::process_package(LoadPackage *package)
849 {
850         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
851
852
853         switch(plugin->input->get_color_model())
854         {
855                 case BC_RGB_FLOAT:
856                         process_chromakey<float> (3, 1.0, 0, pkg);
857                         break;
858                 case BC_RGBA_FLOAT:
859                         process_chromakey<float> ( 4, 1.0, 0, pkg);
860                         break;
861                 case BC_RGB888:
862                         process_chromakey<unsigned char> ( 3, 0xff, 0, pkg);
863                         break;
864                 case BC_RGBA8888:
865                         process_chromakey<unsigned char> ( 4, 0xff, 0, pkg);
866                         break;
867                 case BC_YUV888:
868                         process_chromakey<unsigned char> ( 3, 0xff, 1, pkg);
869                         break;
870                 case BC_YUVA8888:
871                         process_chromakey<unsigned char> ( 4, 0xff, 1, pkg);
872                         break;
873                 case BC_YUV161616:
874                         process_chromakey<uint16_t> (3, 0xffff, 1, pkg);
875                         break;
876                 case BC_YUVA16161616:
877                         process_chromakey<uint16_t> (4, 0xffff, 1, pkg);
878                         break;
879         }
880
881 }
882
883
884
885
886
887 REGISTER_PLUGIN(ChromaKeyHSV)
888
889
890
891 ChromaKeyHSV::ChromaKeyHSV(PluginServer *server)
892  : PluginVClient(server)
893 {
894
895         engine = 0;
896 }
897
898 ChromaKeyHSV::~ChromaKeyHSV()
899 {
900
901         if(engine) delete engine;
902 }
903
904
905 int ChromaKeyHSV::process_buffer(VFrame *frame,
906                 int64_t start_position,
907                 double frame_rate)
908 {
909         load_configuration();
910         this->input = frame;
911         this->output = frame;
912
913
914         read_frame(frame,
915                 0,
916                 start_position,
917                 frame_rate,
918                 get_use_opengl());
919         if(get_use_opengl()) return run_opengl();
920
921
922         if(!engine) engine = new ChromaKeyServer(this);
923         engine->process_packages();
924
925         return 0;
926 }
927
928 const char* ChromaKeyHSV::plugin_title() { return N_("Chroma key (HSV)"); }
929 int ChromaKeyHSV::is_realtime() { return 1; }
930
931
932 LOAD_CONFIGURATION_MACRO(ChromaKeyHSV, ChromaKeyConfig)
933
934
935 void ChromaKeyHSV::save_data(KeyFrame * keyframe)
936 {
937         FileXML output;
938         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
939         output.tag.set_title("CHROMAKEY_HSV");
940         output.tag.set_property("RED", config.red);
941         output.tag.set_property("GREEN", config.green);
942         output.tag.set_property("BLUE", config.blue);
943         output.tag.set_property("MIN_BRIGHTNESS", config.min_brightness);
944         output.tag.set_property("MAX_BRIGHTNESS", config.max_brightness);
945         output.tag.set_property("SATURATION", config.saturation);
946         output.tag.set_property("MIN_SATURATION", config.min_saturation);
947         output.tag.set_property("TOLERANCE", config.tolerance);
948         output.tag.set_property("IN_SLOPE", config.in_slope);
949         output.tag.set_property("OUT_SLOPE", config.out_slope);
950         output.tag.set_property("ALPHA_OFFSET", config.alpha_offset);
951         output.tag.set_property("SPILL_THRESHOLD", config.spill_threshold);
952         output.tag.set_property("SPILL_AMOUNT", config.spill_amount);
953         output.tag.set_property("SHOW_MASK", config.show_mask);
954         output.append_tag();
955         output.tag.set_title("/CHROMAKEY_HSV");
956         output.append_tag();
957         output.append_newline();
958         output.terminate_string();
959 }
960
961 void ChromaKeyHSV::read_data(KeyFrame * keyframe)
962 {
963         FileXML input;
964
965         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
966
967         while( !input.read_tag() ) {
968                 if( input.tag.title_is("CHROMAKEY_HSV") ) {
969                         config.red = input.tag.get_property("RED", config.red);
970                         config.green = input.tag.get_property("GREEN", config.green);
971                         config.blue = input.tag.get_property("BLUE", config.blue);
972                         config.min_brightness =
973                                 input.tag.get_property("MIN_BRIGHTNESS", config.min_brightness);
974                         config.max_brightness =
975                                 input.tag.get_property("MAX_BRIGHTNESS", config.max_brightness);
976                         config.saturation =
977                                 input.tag.get_property("SATURATION", config.saturation);
978                         config.min_saturation =
979                                 input.tag.get_property("MIN_SATURATION", config.min_saturation);
980                         config.tolerance =
981                                 input.tag.get_property("TOLERANCE", config.tolerance);
982                         config.in_slope =
983                                 input.tag.get_property("IN_SLOPE", config.in_slope);
984                         config.out_slope =
985                                 input.tag.get_property("OUT_SLOPE", config.out_slope);
986                         config.alpha_offset =
987                                 input.tag.get_property("ALPHA_OFFSET", config.alpha_offset);
988                         config.spill_threshold =
989                                 input.tag.get_property("SPILL_THRESHOLD",
990                                                         config.spill_threshold);
991                         config.spill_amount =
992                                 input.tag.get_property("SPILL_AMOUNT", config.spill_amount);
993                         config.show_mask =
994                                 input.tag.get_property("SHOW_MASK", config.show_mask);
995                 }
996         }
997 }
998
999
1000 NEW_WINDOW_MACRO(ChromaKeyHSV, ChromaKeyWindow)
1001
1002 void ChromaKeyHSV::update_gui()
1003 {
1004         if( thread ) {
1005                 load_configuration();
1006                 ChromaKeyWindow *window = (ChromaKeyWindow*)thread->window;
1007                 window->lock_window();
1008                 window->update_gui();
1009                 window->unlock_window();
1010         }
1011 }
1012
1013 void ChromaKeyWindow::update_gui()
1014 {
1015         ChromaKeyConfig &config = plugin->config;
1016         min_brightness->update(config.min_brightness);
1017         max_brightness->update(config.max_brightness);
1018         saturation->update(config.saturation);
1019         min_saturation->update(config.min_saturation);
1020         tolerance->update(config.tolerance);
1021         in_slope->update(config.in_slope);
1022         out_slope->update(config.out_slope);
1023         alpha_offset->update(config.alpha_offset);
1024         spill_threshold->update(config.spill_threshold);
1025         spill_amount->update(config.spill_amount);
1026         show_mask->update(config.show_mask);
1027         update_sample();
1028 }
1029
1030
1031
1032 int ChromaKeyHSV::handle_opengl()
1033 {
1034 #ifdef HAVE_GL
1035 // For macro
1036         ChromaKeyHSV *plugin = this;
1037         OUTER_VARIABLES
1038
1039         static const char *yuv_shader =
1040                 "const vec3 black = vec3(0.0, 0.5, 0.5);\n"
1041                 "uniform mat3 yuv_to_rgb_matrix;\n"
1042                 "uniform mat3 rgb_to_yuv_matrix;\n"
1043                 "uniform float yminf;\n"
1044                 "\n"
1045                 "vec4 yuv_to_rgb(vec4 color)\n"
1046                 "{\n"
1047                 "       color.rgb -= vec3(yminf, 0.5, 0.5);\n"
1048                 "       color.rgb = yuv_to_rgb_matrix * color.rgb;\n"
1049                 "       return color;\n"
1050                 "}\n"
1051                 "\n"
1052                 "vec4 rgb_to_yuv(vec4 color)\n"
1053                 "{\n"
1054                 "       color.rgb = rgb_to_yuv_matrix * color.rgb;\n"
1055                 "       color.rgb += vec3(yminf, 0.5, 0.5);\n"
1056                 "       return color;\n"
1057                 "}\n";
1058
1059         static const char *rgb_shader =
1060                 "const vec3 black = vec3(0.0, 0.0, 0.0);\n"
1061                 "\n"
1062                 "vec4 yuv_to_rgb(vec4 color)\n"
1063                 "{\n"
1064                 "       return color;\n"
1065                 "}\n"
1066                 "vec4 rgb_to_yuv(vec4 color)\n"
1067                 "{\n"
1068                 "       return color;\n"
1069                 "}\n";
1070
1071         static const char *hsv_shader =
1072                 "vec4 rgb_to_hsv(vec4 color)\n"
1073                 "{\n"
1074                         RGB_TO_HSV_FRAG("color")
1075                 "       return color;\n"
1076                 "}\n"
1077                 "\n"
1078                 "vec4 hsv_to_rgb(vec4 color)\n"
1079                 "{\n"
1080                         HSV_TO_RGB_FRAG("color")
1081                 "       return color;\n"
1082                 "}\n"
1083                 "\n";
1084
1085         static const char *show_rgbmask_shader =
1086                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1087                 "{\n"
1088                 "       return vec4(1.0, 1.0, 1.0, min(color.a, color2.a));"
1089                 "}\n";
1090
1091         static const char *show_yuvmask_shader =
1092                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1093                 "{\n"
1094                 "       return vec4(1.0, 0.5, 0.5, min(color.a, color2.a));"
1095                 "}\n";
1096
1097         static const char *nomask_shader =
1098                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1099                 "{\n"
1100                 "       return vec4(color.rgb, min(color.a, color2.a));"
1101                 "}\n";
1102
1103         get_output()->to_texture();
1104         get_output()->enable_opengl();
1105         get_output()->init_screen();
1106
1107         const char *shader_stack[16];
1108         memset(shader_stack,0, sizeof(shader_stack));
1109         int current_shader = 0;
1110
1111         shader_stack[current_shader++] = \
1112                  !BC_CModels::is_yuv(get_output()->get_color_model()) ?
1113                         rgb_shader : yuv_shader;
1114         shader_stack[current_shader++] = hsv_shader;
1115         shader_stack[current_shader++] = !config.show_mask ? nomask_shader :
1116                  !BC_CModels::is_yuv(get_output()->get_color_model()) ?
1117                         show_rgbmask_shader : show_yuvmask_shader ;
1118         extern unsigned char _binary_chromakey_sl_start[];
1119         static const char *shader_frag = (char*)_binary_chromakey_sl_start;
1120         shader_stack[current_shader++] = shader_frag;
1121
1122         shader_stack[current_shader] = 0;
1123         unsigned int shader = VFrame::make_shader(shader_stack);
1124         if( shader > 0 ) {
1125                 glUseProgram(shader);
1126                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
1127                 glUniform1f(glGetUniformLocation(shader, "red"), red);
1128                 glUniform1f(glGetUniformLocation(shader, "green"), green);
1129                 glUniform1f(glGetUniformLocation(shader, "blue"), blue);
1130                 glUniform1f(glGetUniformLocation(shader, "in_slope"), in_slope);
1131                 glUniform1f(glGetUniformLocation(shader, "out_slope"), out_slope);
1132                 glUniform1f(glGetUniformLocation(shader, "tolerance"), tolerance);
1133                 glUniform1f(glGetUniformLocation(shader, "tolerance_in"), tolerance_in);
1134                 glUniform1f(glGetUniformLocation(shader, "tolerance_out"), tolerance_out);
1135                 glUniform1f(glGetUniformLocation(shader, "sat"), sat);
1136                 glUniform1f(glGetUniformLocation(shader, "min_s"), min_s);
1137                 glUniform1f(glGetUniformLocation(shader, "min_s_in"), min_s_in);
1138                 glUniform1f(glGetUniformLocation(shader, "min_s_out"), min_s_out);
1139                 glUniform1f(glGetUniformLocation(shader, "min_v"), min_v);
1140                 glUniform1f(glGetUniformLocation(shader, "min_v_in"), min_v_in);
1141                 glUniform1f(glGetUniformLocation(shader, "min_v_out"), min_v_out);
1142                 glUniform1f(glGetUniformLocation(shader, "max_v"), max_v);
1143                 glUniform1f(glGetUniformLocation(shader, "max_v_in"), max_v_in);
1144                 glUniform1f(glGetUniformLocation(shader, "max_v_out"), max_v_out);
1145                 glUniform1f(glGetUniformLocation(shader, "spill_threshold"), spill_threshold);
1146                 glUniform1f(glGetUniformLocation(shader, "spill_amount"), spill_amount);
1147                 glUniform1f(glGetUniformLocation(shader, "alpha_offset"), alpha_offset);
1148                 glUniform1f(glGetUniformLocation(shader, "hue_key"), hue_key);
1149                 glUniform1f(glGetUniformLocation(shader, "saturation_key"), saturation_key);
1150                 glUniform1f(glGetUniformLocation(shader, "value_key"), value_key);
1151                 if( BC_CModels::is_yuv(get_output()->get_color_model()) )
1152                         BC_GL_COLORS(shader);
1153         }
1154
1155         get_output()->bind_texture(0);
1156         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1157         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1158
1159         if(BC_CModels::components(get_output()->get_color_model()) == 3)
1160         {
1161                 glEnable(GL_BLEND);
1162                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1163                 get_output()->clear_pbuffer();
1164         }
1165         get_output()->draw_texture();
1166
1167         glUseProgram(0);
1168         get_output()->set_opengl_state(VFrame::SCREEN);
1169         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1170         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1171         glDisable(GL_BLEND);
1172
1173
1174 #endif
1175         return 0;
1176 }
1177