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