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