add BC_SCALE env var for hi def monitors, cleanup theme data
[goodguy/cinelerra.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            xS(400),
160            yS(450),
161            xS(400),
162            yS(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 xs5 = xS(5), xs10 = xS(10), xs30 = xS(30), xs100 = xS(100), xs240 = xS(240);
178         int ys5 = yS(5), ys10 = yS(10), ys25 = yS(25), ys50 = yS(50);
179   int y = ys10, y1, x1 = 0, x2 = xs10;
180   int x = xs30;
181
182   BC_Title *title;
183   BC_Bar *bar;
184   int ymargin = get_text_height(MEDIUMFONT) + xs5;
185   int ymargin2 = get_text_height(MEDIUMFONT) + ys10;
186
187   add_subwindow (title = new BC_Title (x2, y, _("Color:")));
188
189   add_subwindow (color = new ChromaKeyColor (plugin, this, x, y + ys25));
190
191   add_subwindow (sample =
192                  new BC_SubWindow (x + color->get_w () + xs10, y, xs100, ys50));
193   y += sample->get_h () + ys10;
194
195   add_subwindow (use_colorpicker =
196                  new ChromaKeyUseColorPicker (plugin, this, x, y));
197   y += use_colorpicker->get_h() + ys10;
198
199   add_subwindow (new ChromaKeyReset (plugin, this, x2+xs240, y));
200   add_subwindow (show_mask = new ChromaKeyShowMask (plugin, x2, y));
201         y += show_mask->get_h() + ys5;
202
203         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
204         y += bar->get_h() + ys5;
205         y1 = y;
206   add_subwindow (new BC_Title (x2, y, _("Key parameters:")));
207   y += ymargin;
208   add_subwindow (title = new BC_Title (x, y, _("Hue Tolerance:")));
209   if(title->get_w() > x1) x1 = title->get_w();
210   y += ymargin;
211   add_subwindow (title = new BC_Title (x, y, _("Min. Brightness:")));
212   if(title->get_w() > x1) x1 = title->get_w();
213   y += ymargin;
214   add_subwindow (title = new BC_Title (x, y, _("Max. Brightness:")));
215   if(title->get_w() > x1) x1 = title->get_w();
216   y += ymargin;
217   add_subwindow (title = new BC_Title (x, y, _("Saturation Offset:")));
218   if(title->get_w() > x1) x1 = title->get_w();
219   y += ymargin;
220   add_subwindow (title = new BC_Title (x, y, _("Min Saturation:")));
221   if(title->get_w() > x1) x1 = title->get_w();
222   y += ymargin2;
223
224         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
225         y += bar->get_h() + ys5;
226   add_subwindow (title = new BC_Title (x2, y, _("Mask tweaking:")));
227   y += ymargin;
228   add_subwindow (title = new BC_Title (x, y, _("In Slope:")));
229   if(title->get_w() > x1) x1 = title->get_w();
230   y += ymargin;
231   add_subwindow (title = new BC_Title (x, y, _("Out Slope:")));
232   if(title->get_w() > x1) x1 = title->get_w();
233   y += ymargin;
234   add_subwindow (title = new BC_Title (x, y, _("Alpha Offset:")));
235   if(title->get_w() > x1) x1 = title->get_w();
236   y += ymargin2;
237
238
239
240         add_subwindow(bar = new BC_Bar(x2, y, get_w() - x2 * 2));
241         y += bar->get_h() + ys5;
242   add_subwindow (title = new BC_Title (x2, y, _("Spill light control:")));
243   y += ymargin;
244   add_subwindow (title = new BC_Title (x, y, _("Spill Threshold:")));
245   if(title->get_w() > x1) x1 = title->get_w();
246   y += ymargin;
247   add_subwindow (title = new BC_Title (x, y, _("Spill Compensation:")));
248   if(title->get_w() > x1) x1 = title->get_w();
249   y += ymargin;
250
251
252         y = y1;
253   y += ymargin;
254         x1 += x;
255
256
257
258   add_subwindow (tolerance = new ChromaKeyTolerance (plugin, x1, y));
259   y += ymargin;
260   add_subwindow (min_brightness = new ChromaKeyMinBrightness (plugin, x1, y));
261   y += ymargin;
262   add_subwindow (max_brightness = new ChromaKeyMaxBrightness (plugin, x1, y));
263   y += ymargin;
264   add_subwindow (saturation = new ChromaKeySaturation (plugin, x1, y));
265   y += ymargin;
266   add_subwindow (min_saturation = new ChromaKeyMinSaturation (plugin, x1, y));
267   y += ymargin;
268
269         y += bar->get_h() + ys5;
270   y += ymargin2;
271   add_subwindow (in_slope = new ChromaKeyInSlope (plugin, x1, y));
272   y += ymargin;
273   add_subwindow (out_slope = new ChromaKeyOutSlope (plugin, x1, y));
274   y += ymargin;
275   add_subwindow (alpha_offset = new ChromaKeyAlphaOffset (plugin, x1, y));
276    y += ymargin;
277
278         y += bar->get_h() + ys5;
279   y += ymargin2;
280   add_subwindow (spill_threshold = new ChromaKeySpillThreshold (plugin, x1, y));
281   y += ymargin;
282   add_subwindow (spill_amount = new ChromaKeySpillAmount (plugin, x1, y));
283
284   color_thread = new ChromaKeyColorThread (plugin, this);
285
286   update_sample ();
287   show_window ();
288 }
289
290 void
291 ChromaKeyWindow::update_sample ()
292 {
293   sample->set_color (plugin->config.get_color ());
294   sample->draw_box (0, 0, sample->get_w (), sample->get_h ());
295   sample->set_color (BLACK);
296   sample->draw_rectangle (0, 0, sample->get_w (), sample->get_h ());
297   sample->flash ();
298 }
299
300 void ChromaKeyWindow::done_event(int result)
301 {
302         color_thread->close_window();
303 }
304
305
306 ChromaKeyColor::ChromaKeyColor (ChromaKeyHSV * plugin,
307                                   ChromaKeyWindow * gui, int x, int y):
308 BC_GenericButton (x, y, _("Color..."))
309 {
310   this->plugin = plugin;
311   this->gui = gui;
312 }
313
314 int
315 ChromaKeyColor::handle_event ()
316 {
317   gui->color_thread->start_window (plugin->config.get_color (), 0xff);
318   return 1;
319 }
320
321
322
323
324 ChromaKeyMinBrightness::ChromaKeyMinBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
325             y,
326             0,
327             xS(200), yS(200), (float) 0, (float) 100, plugin->config.min_brightness)
328 {
329   this->plugin = plugin;
330   set_precision (0.01);
331 }
332
333 int
334 ChromaKeyMinBrightness::handle_event ()
335 {
336   plugin->config.min_brightness = get_value ();
337   plugin->send_configure_change ();
338   return 1;
339 }
340
341 ChromaKeyMaxBrightness::ChromaKeyMaxBrightness (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
342             y,
343             0,
344             xS(200), yS(200), (float) 0, (float) 100, plugin->config.max_brightness)
345 {
346   this->plugin = plugin;
347   set_precision (0.01);
348 }
349
350 int
351 ChromaKeyMaxBrightness::handle_event ()
352 {
353   plugin->config.max_brightness = get_value ();
354   plugin->send_configure_change ();
355   return 1;
356 }
357
358
359 ChromaKeySaturation::ChromaKeySaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
360             y,
361             0, xS(200), yS(200), (float) 0, (float) 100, plugin->config.saturation)
362 {
363   this->plugin = plugin;
364   set_precision (0.01);
365 }
366
367 int
368 ChromaKeySaturation::handle_event ()
369 {
370   plugin->config.saturation = get_value ();
371   plugin->send_configure_change ();
372   return 1;
373 }
374
375 ChromaKeyMinSaturation::ChromaKeyMinSaturation (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
376             y,
377             0,
378             xS(200), yS(200), (float) 0, (float) 100, plugin->config.min_saturation)
379 {
380   this->plugin = plugin;
381   set_precision (0.01);
382 }
383
384 int
385 ChromaKeyMinSaturation::handle_event ()
386 {
387   plugin->config.min_saturation = get_value ();
388   plugin->send_configure_change ();
389   return 1;
390 }
391
392
393 ChromaKeyTolerance::ChromaKeyTolerance (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
394             y,
395             0, xS(200), yS(200), (float) 0, (float) 100, plugin->config.tolerance)
396 {
397   this->plugin = plugin;
398   set_precision (0.01);
399 }
400
401 int
402 ChromaKeyTolerance::handle_event ()
403 {
404   plugin->config.tolerance = get_value ();
405   plugin->send_configure_change ();
406   return 1;
407 }
408
409
410
411 ChromaKeyInSlope::ChromaKeyInSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
412             y,
413             0, xS(200), yS(200), (float) 0, (float) 20, plugin->config.in_slope)
414 {
415   this->plugin = plugin;
416   set_precision (0.01);
417 }
418
419 int
420 ChromaKeyInSlope::handle_event ()
421 {
422   plugin->config.in_slope = get_value ();
423   plugin->send_configure_change ();
424   return 1;
425 }
426
427
428 ChromaKeyOutSlope::ChromaKeyOutSlope (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
429             y,
430             0, xS(200), yS(200), (float) 0, (float) 20, plugin->config.out_slope)
431 {
432   this->plugin = plugin;
433   set_precision (0.01);
434 }
435
436 int
437 ChromaKeyOutSlope::handle_event ()
438 {
439   plugin->config.out_slope = get_value ();
440   plugin->send_configure_change ();
441   return 1;
442 }
443
444
445 ChromaKeyAlphaOffset::ChromaKeyAlphaOffset (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
446             y,
447             0,
448             xS(200), yS(200), (float) -100, (float) 100, plugin->config.alpha_offset)
449 {
450   this->plugin = plugin;
451   set_precision (0.01);
452 }
453
454 int
455 ChromaKeyAlphaOffset::handle_event ()
456 {
457   plugin->config.alpha_offset = get_value ();
458   plugin->send_configure_change ();
459   return 1;
460 }
461
462 ChromaKeyShowMask::ChromaKeyShowMask (ChromaKeyHSV * plugin, int x, int y):BC_CheckBox (x, y, plugin->config.show_mask,
463              _
464              ("Show Mask"))
465 {
466   this->plugin = plugin;
467
468 }
469
470 int
471 ChromaKeyShowMask::handle_event ()
472 {
473   plugin->config.show_mask = get_value ();
474   plugin->send_configure_change ();
475   return 1;
476 }
477
478 ChromaKeyReset::ChromaKeyReset (ChromaKeyHSV *plugin, ChromaKeyWindow *gui, int x, int y)
479  :BC_GenericButton(x, y, _("Reset"))
480 {
481   this->plugin = plugin;
482   this->gui = gui;
483 }
484
485 int ChromaKeyReset::handle_event ()
486 {
487   plugin->config.reset();
488   gui->update_gui();
489   plugin->send_configure_change();
490   return 1;
491 }
492
493 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker (ChromaKeyHSV * plugin, ChromaKeyWindow * gui, int x, int y)
494  : BC_GenericButton (x, y,
495                   _
496                   ("Use color picker"))
497 {
498   this->plugin = plugin;
499   this->gui = gui;
500 }
501
502 int
503 ChromaKeyUseColorPicker::handle_event ()
504 {
505   plugin->config.red = plugin->get_red ();
506   plugin->config.green = plugin->get_green ();
507   plugin->config.blue = plugin->get_blue ();
508
509   gui->update_sample ();
510
511
512   plugin->send_configure_change ();
513   return 1;
514 }
515
516
517
518 ChromaKeySpillThreshold::ChromaKeySpillThreshold (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
519             y,
520             0,
521             xS(200), yS(200), (float) 0, (float) 100, plugin->config.spill_threshold)
522 {
523   this->plugin = plugin;
524   set_precision (0.01);
525 }
526
527 int
528 ChromaKeySpillThreshold::handle_event ()
529 {
530   plugin->config.spill_threshold = get_value ();
531   plugin->send_configure_change ();
532   return 1;
533 }
534
535 ChromaKeySpillAmount::ChromaKeySpillAmount (ChromaKeyHSV * plugin, int x, int y):BC_FSlider (x,
536             y,
537             0, xS(200), yS(200), (float) 0, (float) 100, plugin->config.spill_amount)
538 {
539   this->plugin = plugin;
540   set_precision (0.01);
541 }
542
543 int
544 ChromaKeySpillAmount::handle_event ()
545 {
546   plugin->config.spill_amount = get_value ();
547   plugin->send_configure_change ();
548   return 1;
549 }
550
551
552
553
554
555
556 ChromaKeyColorThread::ChromaKeyColorThread (ChromaKeyHSV * plugin, ChromaKeyWindow * gui)
557  : ColorPicker (1, _("Inner color"))
558 {
559   this->plugin = plugin;
560   this->gui = gui;
561 }
562
563 int
564 ChromaKeyColorThread::handle_new_color (int output, int alpha)
565 {
566   plugin->config.red = (float) (output & 0xff0000) / 0xff0000;
567   plugin->config.green = (float) (output & 0xff00) / 0xff00;
568   plugin->config.blue = (float) (output & 0xff) / 0xff;
569
570   get_gui()->unlock_window();
571   gui->lock_window("ChromaKeyColorThread::handle_new_color");
572   gui->update_sample ();
573   gui->unlock_window();
574   get_gui()->lock_window("ChromaKeyColorThread::handle_new_color");
575
576
577   plugin->send_configure_change ();
578   return 1;
579 }
580
581
582
583
584
585
586
587
588 ChromaKeyServer::ChromaKeyServer (ChromaKeyHSV * plugin):LoadServer (plugin->PluginClient::smp + 1,
589             plugin->PluginClient::smp +
590             1)
591 {
592   this->plugin = plugin;
593 }
594
595 void
596 ChromaKeyServer::init_packages ()
597 {
598   for (int i = 0; i < get_total_packages (); i++)
599     {
600       ChromaKeyPackage *pkg = (ChromaKeyPackage *) get_package (i);
601       pkg->y1 = plugin->input->get_h () * i / get_total_packages ();
602       pkg->y2 = plugin->input->get_h () * (i + 1) / get_total_packages ();
603     }
604
605 }
606 LoadClient *
607 ChromaKeyServer::new_client ()
608 {
609   return new ChromaKeyUnit (plugin, this);
610 }
611
612 LoadPackage *
613 ChromaKeyServer::new_package ()
614 {
615   return new ChromaKeyPackage;
616 }
617
618
619
620 ChromaKeyPackage::ChromaKeyPackage ():LoadPackage ()
621 {
622 }
623
624 ChromaKeyUnit::ChromaKeyUnit (ChromaKeyHSV * plugin, ChromaKeyServer * server):LoadClient
625   (server)
626 {
627   this->plugin = plugin;
628 }
629
630
631
632
633 #define ABS(a) ((a<0)?-(a):a)
634 // Reuse as much as possible in the opengl version
635 #define OUTER_VARIABLES \
636   float red = plugin->config.red; \
637   float green = plugin->config.green; \
638   float blue = plugin->config.blue; \
639  \
640   float in_slope = plugin->config.in_slope / 100; \
641   float out_slope = plugin->config.out_slope / 100; \
642  \
643   float tolerance = plugin->config.tolerance / 100; \
644   float tolerance_in = tolerance - in_slope; \
645   float tolerance_out = tolerance + out_slope; \
646  \
647   float sat = plugin->config.saturation / 100; \
648   float min_s = plugin->config.min_saturation / 100; \
649   float min_s_in = min_s + in_slope; \
650   float min_s_out = min_s - out_slope; \
651  \
652   float min_v = plugin->config.min_brightness / 100; \
653   float min_v_in = min_v + in_slope; \
654   float min_v_out = min_v - out_slope; \
655  \
656   float max_v = plugin->config.max_brightness / 100; \
657   float max_v_in = max_v - in_slope; \
658   float max_v_out = max_v + out_slope; \
659  \
660   float spill_threshold = plugin->config.spill_threshold / 100; \
661   float spill_amount = 1.0 - plugin->config.spill_amount / 100; \
662  \
663   float alpha_offset = plugin->config.alpha_offset / 100; \
664  \
665 /* Convert RGB key to HSV key */ \
666         float hue_key, saturation_key, value_key; \
667         HSV::rgb_to_hsv(red,  \
668                 green, \
669                 blue, \
670                 hue_key, \
671                 saturation_key, \
672                 value_key);
673
674
675 template <typename component_type>
676 void ChromaKeyUnit::process_chromakey(int components,
677         component_type max,
678         bool use_yuv,
679         ChromaKeyPackage *pkg)
680
681         OUTER_VARIABLES
682
683         int w = plugin->input->get_w();
684
685         for (int i = pkg->y1; i < pkg->y2; i++)
686         {
687         component_type *row = (component_type *) plugin->input->get_rows ()[i];
688
689         for (int j = 0; j < w; j++)
690                 {
691                         float r, g, b, a = 1;
692                         if (!use_yuv) {
693                                 r = (float) row[0] / max;
694                                 g = (float) row[1] / max;
695                                 b = (float) row[2] / max;
696                         }
697                         else
698                         YUV::yuv.yuv_to_rgb_f (r, g, b, row[0], row[1], row[2]);
699
700                         float h, s, v;
701
702                         float av = 1, ah = 1, as = 1, avm = 1;
703                         bool has_match = true;
704
705                         HSV::rgb_to_hsv (r, g, b, h, s, v);
706
707 // First, test if the hue is in range
708
709 /* Hue wrap */
710                         if(h <= hue_key - tolerance_in * 180.0)
711                                 h += 360.0;
712                         else
713                         if(h >= hue_key + tolerance_in * 180.0)
714                                 h -= 360.0;
715
716
717                         if (tolerance == 0)
718                     ah = 1.0;
719                         else
720                         if (ABS (h - hue_key) < tolerance_in * 180)
721                     ah = 0;
722                         else
723                         if ((out_slope != 0) && (ABS (h - hue_key) < tolerance * 180))
724 /* we scale alpha between 0 and 1/2 */
725                     ah = ABS (h - hue_key) / tolerance / 360;
726                         else
727                         if (ABS (h - hue_key) < tolerance_out * 180)
728 /* we scale alpha between 1/2 and 1 */
729                     ah = ABS (h - hue_key) / tolerance_out / 360;
730                         else
731                     has_match = false;
732
733 // Check if the saturation matches
734                         if (has_match)
735                 {
736                         if (min_s == 0)
737                                         as = 0;
738                         else if (s - sat >= min_s_in)
739                                         as = 0;
740                         else if ((out_slope != 0) && (s - sat > min_s))
741                                         as = (s - sat - min_s) / (min_s * 2);
742                         else if (s - sat > min_s_out)
743                                         as = (s - sat - min_s_out) / (min_s_out * 2);
744                         else
745                                         has_match = false;
746                 }
747
748 // Check if the value is more than the minimun
749                         if (has_match)
750                 {
751                         if (min_v == 0)
752                                         av = 0;
753                         else if (v >= min_v_in)
754                                         av = 0;
755                         else if ((out_slope != 0) && (v > min_v))
756                                         av = (v - min_v) / (min_v * 2);
757                         else if (v > min_v_out)
758                                         av = (v - min_v_out) / (min_v_out * 2);
759                         else
760                                         has_match = false;
761                 }
762
763 // Check if the value is less than the maximum
764                         if (has_match)
765                 {
766                         if (max_v == 0)
767                                         avm = 1;
768                         else if (v <= max_v_in)
769                                         avm = 0;
770                         else if ((out_slope != 0) && (v < max_v))
771                                         avm = (v - max_v) / (max_v * 2);
772                         else if (v < max_v_out)
773                                         avm = (v - max_v_out) / (max_v_out * 2);
774                         else
775                                         has_match = false;
776                 }
777
778           // If the color is part of the key, update the alpha channel
779           if (has_match)
780             a = MAX (MAX (ah, av), MAX (as, avm));
781
782           // Spill light processing
783           if ((ABS (h - hue_key) < spill_threshold * 180) ||
784               ((ABS (h - hue_key) > 360)
785                && (ABS (h - hue_key) - 360 < spill_threshold * 180)))
786             {
787               s = s * spill_amount * ABS (h - hue_key) / (spill_threshold * 180);
788
789               HSV::hsv_to_rgb (r, g, b, h, s, v);
790
791                 if (!use_yuv) {
792                         row[0] = (component_type) ((float) r * max);
793                         row[1] = (component_type) ((float) g * max);
794                         row[2] = (component_type) ((float) b * max);
795                 }
796                 else
797                         YUV::yuv.rgb_to_yuv_f(r, g, b, row[0], row[1], row[2]);
798           }
799
800           a += alpha_offset;
801           CLAMP (a, 0.0, 1.0);
802
803           if (plugin->config.show_mask)
804             {
805
806               if (use_yuv)
807                 {
808                   row[0] = (component_type) ((float) a * max);
809                   row[1] = (component_type) ((float) max / 2);
810                   row[2] = (component_type) ((float) max / 2);
811                 }
812               else
813                 {
814                   row[0] = (component_type) ((float) a * max);
815                   row[1] = (component_type) ((float) a * max);
816                   row[2] = (component_type) ((float) a * max);
817                 }
818             }
819
820           /* Multiply alpha and put back in frame */
821           if (components == 4)
822             {
823               row[3] = MIN ((component_type) (a * max), row[3]);
824             }
825           else if (use_yuv)
826             {
827               row[0] = (component_type) ((float) a * row[0]);
828               row[1] =
829                 (component_type) ((float) a * (row[1] - (max / 2 + 1)) +
830                                   max / 2 + 1);
831               row[2] =
832                 (component_type) ((float) a * (row[2] - (max / 2 + 1)) +
833                                   max / 2 + 1);
834             }
835           else
836             {
837               row[0] = (component_type) ((float) a * row[0]);
838               row[1] = (component_type) ((float) a * row[1]);
839               row[2] = (component_type) ((float) a * row[2]);
840             }
841
842           row += components;
843         }
844     }
845 }
846
847
848
849
850 void ChromaKeyUnit::process_package(LoadPackage *package)
851 {
852         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
853
854
855         switch(plugin->input->get_color_model())
856         {
857                 case BC_RGB_FLOAT:
858                         process_chromakey<float> (3, 1.0, 0, pkg);
859                         break;
860                 case BC_RGBA_FLOAT:
861                         process_chromakey<float> ( 4, 1.0, 0, pkg);
862                         break;
863                 case BC_RGB888:
864                         process_chromakey<unsigned char> ( 3, 0xff, 0, pkg);
865                         break;
866                 case BC_RGBA8888:
867                         process_chromakey<unsigned char> ( 4, 0xff, 0, pkg);
868                         break;
869                 case BC_YUV888:
870                         process_chromakey<unsigned char> ( 3, 0xff, 1, pkg);
871                         break;
872                 case BC_YUVA8888:
873                         process_chromakey<unsigned char> ( 4, 0xff, 1, pkg);
874                         break;
875                 case BC_YUV161616:
876                         process_chromakey<uint16_t> (3, 0xffff, 1, pkg);
877                         break;
878                 case BC_YUVA16161616:
879                         process_chromakey<uint16_t> (4, 0xffff, 1, pkg);
880                         break;
881         }
882
883 }
884
885
886
887
888
889 REGISTER_PLUGIN(ChromaKeyHSV)
890
891
892
893 ChromaKeyHSV::ChromaKeyHSV(PluginServer *server)
894  : PluginVClient(server)
895 {
896
897         engine = 0;
898 }
899
900 ChromaKeyHSV::~ChromaKeyHSV()
901 {
902
903         if(engine) delete engine;
904 }
905
906
907 int ChromaKeyHSV::process_buffer(VFrame *frame,
908                 int64_t start_position,
909                 double frame_rate)
910 {
911         load_configuration();
912         this->input = frame;
913         this->output = frame;
914
915
916         read_frame(frame,
917                 0,
918                 start_position,
919                 frame_rate,
920                 get_use_opengl());
921         if(get_use_opengl()) return run_opengl();
922
923
924         if(!engine) engine = new ChromaKeyServer(this);
925         engine->process_packages();
926
927         return 0;
928 }
929
930 const char* ChromaKeyHSV::plugin_title() { return N_("Chroma key (HSV)"); }
931 int ChromaKeyHSV::is_realtime() { return 1; }
932
933
934 LOAD_CONFIGURATION_MACRO(ChromaKeyHSV, ChromaKeyConfig)
935
936
937 void ChromaKeyHSV::save_data(KeyFrame * keyframe)
938 {
939         FileXML output;
940         output.set_shared_output(keyframe->xbuf);
941         output.tag.set_title("CHROMAKEY_HSV");
942         output.tag.set_property("RED", config.red);
943         output.tag.set_property("GREEN", config.green);
944         output.tag.set_property("BLUE", config.blue);
945         output.tag.set_property("MIN_BRIGHTNESS", config.min_brightness);
946         output.tag.set_property("MAX_BRIGHTNESS", config.max_brightness);
947         output.tag.set_property("SATURATION", config.saturation);
948         output.tag.set_property("MIN_SATURATION", config.min_saturation);
949         output.tag.set_property("TOLERANCE", config.tolerance);
950         output.tag.set_property("IN_SLOPE", config.in_slope);
951         output.tag.set_property("OUT_SLOPE", config.out_slope);
952         output.tag.set_property("ALPHA_OFFSET", config.alpha_offset);
953         output.tag.set_property("SPILL_THRESHOLD", config.spill_threshold);
954         output.tag.set_property("SPILL_AMOUNT", config.spill_amount);
955         output.tag.set_property("SHOW_MASK", config.show_mask);
956         output.append_tag();
957         output.tag.set_title("/CHROMAKEY_HSV");
958         output.append_tag();
959         output.append_newline();
960         output.terminate_string();
961 }
962
963 void ChromaKeyHSV::read_data(KeyFrame * keyframe)
964 {
965         FileXML input;
966
967         input.set_shared_input(keyframe->xbuf);
968
969         while( !input.read_tag() ) {
970                 if( input.tag.title_is("CHROMAKEY_HSV") ) {
971                         config.red = input.tag.get_property("RED", config.red);
972                         config.green = input.tag.get_property("GREEN", config.green);
973                         config.blue = input.tag.get_property("BLUE", config.blue);
974                         config.min_brightness =
975                                 input.tag.get_property("MIN_BRIGHTNESS", config.min_brightness);
976                         config.max_brightness =
977                                 input.tag.get_property("MAX_BRIGHTNESS", config.max_brightness);
978                         config.saturation =
979                                 input.tag.get_property("SATURATION", config.saturation);
980                         config.min_saturation =
981                                 input.tag.get_property("MIN_SATURATION", config.min_saturation);
982                         config.tolerance =
983                                 input.tag.get_property("TOLERANCE", config.tolerance);
984                         config.in_slope =
985                                 input.tag.get_property("IN_SLOPE", config.in_slope);
986                         config.out_slope =
987                                 input.tag.get_property("OUT_SLOPE", config.out_slope);
988                         config.alpha_offset =
989                                 input.tag.get_property("ALPHA_OFFSET", config.alpha_offset);
990                         config.spill_threshold =
991                                 input.tag.get_property("SPILL_THRESHOLD",
992                                                         config.spill_threshold);
993                         config.spill_amount =
994                                 input.tag.get_property("SPILL_AMOUNT", config.spill_amount);
995                         config.show_mask =
996                                 input.tag.get_property("SHOW_MASK", config.show_mask);
997                 }
998         }
999 }
1000
1001
1002 NEW_WINDOW_MACRO(ChromaKeyHSV, ChromaKeyWindow)
1003
1004 void ChromaKeyHSV::update_gui()
1005 {
1006         if( thread ) {
1007                 load_configuration();
1008                 ChromaKeyWindow *window = (ChromaKeyWindow*)thread->window;
1009                 window->lock_window();
1010                 window->update_gui();
1011                 window->unlock_window();
1012         }
1013 }
1014
1015 void ChromaKeyWindow::update_gui()
1016 {
1017         ChromaKeyConfig &config = plugin->config;
1018         min_brightness->update(config.min_brightness);
1019         max_brightness->update(config.max_brightness);
1020         saturation->update(config.saturation);
1021         min_saturation->update(config.min_saturation);
1022         tolerance->update(config.tolerance);
1023         in_slope->update(config.in_slope);
1024         out_slope->update(config.out_slope);
1025         alpha_offset->update(config.alpha_offset);
1026         spill_threshold->update(config.spill_threshold);
1027         spill_amount->update(config.spill_amount);
1028         show_mask->update(config.show_mask);
1029         update_sample();
1030 }
1031
1032
1033
1034 int ChromaKeyHSV::handle_opengl()
1035 {
1036 #ifdef HAVE_GL
1037 // For macro
1038         ChromaKeyHSV *plugin = this;
1039         OUTER_VARIABLES
1040
1041         static const char *yuv_shader =
1042                 "const vec3 black = vec3(0.0, 0.5, 0.5);\n"
1043                 "uniform mat3 yuv_to_rgb_matrix;\n"
1044                 "uniform mat3 rgb_to_yuv_matrix;\n"
1045                 "uniform float yminf;\n"
1046                 "\n"
1047                 "vec4 yuv_to_rgb(vec4 color)\n"
1048                 "{\n"
1049                 "       color.rgb -= vec3(yminf, 0.5, 0.5);\n"
1050                 "       color.rgb = yuv_to_rgb_matrix * color.rgb;\n"
1051                 "       return color;\n"
1052                 "}\n"
1053                 "\n"
1054                 "vec4 rgb_to_yuv(vec4 color)\n"
1055                 "{\n"
1056                 "       color.rgb = rgb_to_yuv_matrix * color.rgb;\n"
1057                 "       color.rgb += vec3(yminf, 0.5, 0.5);\n"
1058                 "       return color;\n"
1059                 "}\n";
1060
1061         static const char *rgb_shader =
1062                 "const vec3 black = vec3(0.0, 0.0, 0.0);\n"
1063                 "\n"
1064                 "vec4 yuv_to_rgb(vec4 color)\n"
1065                 "{\n"
1066                 "       return color;\n"
1067                 "}\n"
1068                 "vec4 rgb_to_yuv(vec4 color)\n"
1069                 "{\n"
1070                 "       return color;\n"
1071                 "}\n";
1072
1073         static const char *hsv_shader =
1074                 "vec4 rgb_to_hsv(vec4 color)\n"
1075                 "{\n"
1076                         RGB_TO_HSV_FRAG("color")
1077                 "       return color;\n"
1078                 "}\n"
1079                 "\n"
1080                 "vec4 hsv_to_rgb(vec4 color)\n"
1081                 "{\n"
1082                         HSV_TO_RGB_FRAG("color")
1083                 "       return color;\n"
1084                 "}\n"
1085                 "\n";
1086
1087         static const char *show_rgbmask_shader =
1088                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1089                 "{\n"
1090                 "       return vec4(1.0, 1.0, 1.0, min(color.a, color2.a));"
1091                 "}\n";
1092
1093         static const char *show_yuvmask_shader =
1094                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1095                 "{\n"
1096                 "       return vec4(1.0, 0.5, 0.5, min(color.a, color2.a));"
1097                 "}\n";
1098
1099         static const char *nomask_shader =
1100                 "vec4 show_mask(vec4 color, vec4 color2)\n"
1101                 "{\n"
1102                 "       return vec4(color.rgb, min(color.a, color2.a));"
1103                 "}\n";
1104
1105         get_output()->to_texture();
1106         get_output()->enable_opengl();
1107         get_output()->init_screen();
1108
1109         const char *shader_stack[16];
1110         memset(shader_stack,0, sizeof(shader_stack));
1111         int current_shader = 0;
1112
1113         shader_stack[current_shader++] = \
1114                  !BC_CModels::is_yuv(get_output()->get_color_model()) ?
1115                         rgb_shader : yuv_shader;
1116         shader_stack[current_shader++] = hsv_shader;
1117         shader_stack[current_shader++] = !config.show_mask ? nomask_shader :
1118                  !BC_CModels::is_yuv(get_output()->get_color_model()) ?
1119                         show_rgbmask_shader : show_yuvmask_shader ;
1120         extern unsigned char _binary_chromakey_sl_start[];
1121         static const char *shader_frag = (char*)_binary_chromakey_sl_start;
1122         shader_stack[current_shader++] = shader_frag;
1123
1124         shader_stack[current_shader] = 0;
1125         unsigned int shader = VFrame::make_shader(shader_stack);
1126         if( shader > 0 ) {
1127                 glUseProgram(shader);
1128                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
1129                 glUniform1f(glGetUniformLocation(shader, "red"), red);
1130                 glUniform1f(glGetUniformLocation(shader, "green"), green);
1131                 glUniform1f(glGetUniformLocation(shader, "blue"), blue);
1132                 glUniform1f(glGetUniformLocation(shader, "in_slope"), in_slope);
1133                 glUniform1f(glGetUniformLocation(shader, "out_slope"), out_slope);
1134                 glUniform1f(glGetUniformLocation(shader, "tolerance"), tolerance);
1135                 glUniform1f(glGetUniformLocation(shader, "tolerance_in"), tolerance_in);
1136                 glUniform1f(glGetUniformLocation(shader, "tolerance_out"), tolerance_out);
1137                 glUniform1f(glGetUniformLocation(shader, "sat"), sat);
1138                 glUniform1f(glGetUniformLocation(shader, "min_s"), min_s);
1139                 glUniform1f(glGetUniformLocation(shader, "min_s_in"), min_s_in);
1140                 glUniform1f(glGetUniformLocation(shader, "min_s_out"), min_s_out);
1141                 glUniform1f(glGetUniformLocation(shader, "min_v"), min_v);
1142                 glUniform1f(glGetUniformLocation(shader, "min_v_in"), min_v_in);
1143                 glUniform1f(glGetUniformLocation(shader, "min_v_out"), min_v_out);
1144                 glUniform1f(glGetUniformLocation(shader, "max_v"), max_v);
1145                 glUniform1f(glGetUniformLocation(shader, "max_v_in"), max_v_in);
1146                 glUniform1f(glGetUniformLocation(shader, "max_v_out"), max_v_out);
1147                 glUniform1f(glGetUniformLocation(shader, "spill_threshold"), spill_threshold);
1148                 glUniform1f(glGetUniformLocation(shader, "spill_amount"), spill_amount);
1149                 glUniform1f(glGetUniformLocation(shader, "alpha_offset"), alpha_offset);
1150                 glUniform1f(glGetUniformLocation(shader, "hue_key"), hue_key);
1151                 glUniform1f(glGetUniformLocation(shader, "saturation_key"), saturation_key);
1152                 glUniform1f(glGetUniformLocation(shader, "value_key"), value_key);
1153                 if( BC_CModels::is_yuv(get_output()->get_color_model()) )
1154                         BC_GL_COLORS(shader);
1155         }
1156
1157         get_output()->bind_texture(0);
1158         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1159         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1160
1161         if(BC_CModels::components(get_output()->get_color_model()) == 3)
1162         {
1163                 glEnable(GL_BLEND);
1164                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1165                 get_output()->clear_pbuffer();
1166         }
1167         get_output()->draw_texture();
1168
1169         glUseProgram(0);
1170         get_output()->set_opengl_state(VFrame::SCREEN);
1171         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1172         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1173         glDisable(GL_BLEND);
1174
1175
1176 #endif
1177         return 0;
1178 }
1179