Credit Andrew - updating patches for FFmpeg 7.0 as needed since 6.1, now at 7.0,...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / chromakey / chromakey.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 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 "bccolors.h"
23 #include "bcdisplayinfo.h"
24 #include "bcsignals.h"
25 #include "chromakey.h"
26 #include "clip.h"
27 #include "bchash.h"
28 #include "filexml.h"
29 #include "guicast.h"
30 #include "keyframe.h"
31 #include "language.h"
32 #include "loadbalance.h"
33 #include "playback3d.h"
34 #include "bccolors.h"
35 #include "pluginvclient.h"
36 #include "vframe.h"
37
38 #include <stdint.h>
39 #include <string.h>
40
41
42
43 ChromaKeyConfig::ChromaKeyConfig()
44 {
45        reset(RESET_DEFAULT_SETTINGS);
46 }
47
48 void ChromaKeyConfig::reset(int clear)
49
50 {
51         switch(clear) {
52                 case RESET_ALL :
53                         red = 0.0;
54                         green = 0.0;
55                         blue = 0.0;
56                         threshold = 0.0;
57                         use_value = 0;
58                         slope = 0.0;
59                         break;
60                 case RESET_RGB :
61                         red = 0.0;
62                         green = 0.0;
63                         blue = 0.0;
64                         break;
65                 case RESET_SLOPE :
66                         slope = 0.0;
67                         break;
68                 case RESET_THRESHOLD :
69                         threshold = 0.0;
70                         break;
71                 case RESET_DEFAULT_SETTINGS :
72                 default:
73                         red = 0.0;
74                         green = 0.0;
75                         blue = 0.0;
76                         threshold = 10.0;
77                         use_value = 0;
78                         slope = 0.0;
79                         break;
80         }
81 }
82
83
84 void ChromaKeyConfig::copy_from(ChromaKeyConfig &src)
85 {
86         red = src.red;
87         green = src.green;
88         blue = src.blue;
89         threshold = src.threshold;
90         use_value = src.use_value;
91         slope = src.slope;
92 }
93
94 int ChromaKeyConfig::equivalent(ChromaKeyConfig &src)
95 {
96         return (EQUIV(red, src.red) &&
97                 EQUIV(green, src.green) &&
98                 EQUIV(blue, src.blue) &&
99                 EQUIV(threshold, src.threshold) &&
100                 EQUIV(slope, src.slope) &&
101                 use_value == src.use_value);
102 }
103
104 void ChromaKeyConfig::interpolate(ChromaKeyConfig &prev,
105         ChromaKeyConfig &next,
106         int64_t prev_frame,
107         int64_t next_frame,
108         int64_t current_frame)
109 {
110         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
111         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
112
113         this->red = prev.red * prev_scale + next.red * next_scale;
114         this->green = prev.green * prev_scale + next.green * next_scale;
115         this->blue = prev.blue * prev_scale + next.blue * next_scale;
116         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
117         this->slope = prev.slope * prev_scale + next.slope * next_scale;
118         this->use_value = prev.use_value;
119 }
120
121 int ChromaKeyConfig::get_color()
122 {
123         int red = (int)(CLIP(this->red, 0, 1) * 0xff);
124         int green = (int)(CLIP(this->green, 0, 1) * 0xff);
125         int blue = (int)(CLIP(this->blue, 0, 1) * 0xff);
126         return (red << 16) | (green << 8) | blue;
127 }
128
129
130
131
132
133
134
135 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin)
136  : PluginClientWindow(plugin,
137         xS(420),
138         yS(250),
139         xS(420),
140         yS(250),
141         0)
142 {
143         this->plugin = plugin;
144         color_thread = 0;
145 }
146
147 ChromaKeyWindow::~ChromaKeyWindow()
148 {
149         delete color_thread;
150 }
151
152 void ChromaKeyWindow::create_objects()
153 {
154         int xs10 = xS(10), xs20 = xS(20), xs100 = xS(100), xs200 = xS(200);
155         int ys10 = yS(10), ys20 = yS(20), ys30 = yS(30), ys40 = yS(40), ys50 = yS(50);
156         int x = xs10, y = ys10, x2 = xS(80), x3 = xS(180);
157         int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
158         int defaultBtn_w = xs100;
159
160         BC_Title *title;
161         BC_TitleBar *title_bar;
162         BC_Bar *bar;
163
164 // Color section
165         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Color")));
166         y += ys20;
167         add_subwindow(color = new ChromaKeyColor(plugin, this, x, y));
168         // Info for Sample rectangle:       x_slider w_slider w_sample
169         //                                        \       |      /    y,   w,     h
170         add_subwindow(sample = new BC_SubWindow(x3 + xs200 - xs100, y, xs100, ys50));
171         y += ys30;
172         add_subwindow(use_colorpicker = new ChromaKeyUseColorPicker(plugin, this, x, y));
173
174 // Key parameters section
175         y += ys30;
176         add_subwindow(title_bar = new BC_TitleBar(x, y, get_w()-2*x, xs20, xs10, _("Key parameters")));
177         y += ys20;
178         add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
179         threshold_text = new ChromaKeyFText(plugin, this,
180                 0, &(plugin->config.threshold), (x + x2), y, MIN_VALUE, MAX_VALUE);
181         threshold_text->create_objects();
182         threshold_slider = new ChromaKeyFSlider(plugin,
183                 threshold_text, &(plugin->config.threshold), x3, y, MIN_VALUE, MAX_VALUE, xs200);
184         add_subwindow(threshold_slider);
185         threshold_text->slider = threshold_slider;
186         add_subwindow(threshold_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_THRESHOLD));
187         y += ys30;
188
189         add_subwindow(title = new BC_Title(x, y, _("Slope:")));
190         slope_text = new ChromaKeyFText(plugin, this,
191                 0, &(plugin->config.slope), (x + x2), y, MIN_VALUE, MAX_VALUE);
192         slope_text->create_objects();
193         slope_slider = new ChromaKeyFSlider(plugin,
194                 slope_text, &(plugin->config.slope), x3, y, MIN_VALUE, MAX_VALUE, xs200);
195         add_subwindow(slope_slider);
196         slope_text->slider = slope_slider;
197         add_subwindow(slope_Clr = new ChromaKeyClr(plugin, this, clr_x, y, RESET_SLOPE));
198         y += ys30;
199
200         add_subwindow(use_value = new ChromaKeyUseValue(plugin, x, y));
201         y += ys40;
202
203 // Reset section
204         add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
205         y += ys10;
206         add_subwindow(reset = new ChromaKeyReset(plugin, this, x, y));
207         add_subwindow(default_settings = new ChromaKeyDefaultSettings(plugin, this,
208                 (get_w() - xs10 - defaultBtn_w), y, defaultBtn_w));
209
210         color_thread = new ChromaKeyColorThread(plugin, this);
211
212         update_sample();
213         show_window();
214         flush();
215 }
216
217
218 void ChromaKeyWindow::update_sample()
219 {
220         sample->set_color(plugin->config.get_color());
221         sample->draw_box(0,
222                 0,
223                 sample->get_w(),
224                 sample->get_h());
225         sample->set_color(BLACK);
226         sample->draw_rectangle(0,
227                 0,
228                 sample->get_w(),
229                 sample->get_h());
230         sample->flash();
231 }
232
233 void ChromaKeyWindow::done_event(int result)
234 {
235         color_thread->close_window();
236 }
237
238
239
240
241
242
243
244
245
246 ChromaKeyColor::ChromaKeyColor(ChromaKey *plugin,
247         ChromaKeyWindow *gui,
248         int x,
249         int y)
250  : BC_GenericButton(x,
251         y,
252         _("Color..."))
253 {
254         this->plugin = plugin;
255         this->gui = gui;
256 }
257 int ChromaKeyColor::handle_event()
258 {
259         gui->color_thread->start_window(
260                 plugin->config.get_color(),
261                 0xff);
262         return 1;
263 }
264
265
266
267 ChromaKeyFText::ChromaKeyFText(ChromaKey *plugin, ChromaKeyWindow *gui,
268         ChromaKeyFSlider *slider, float *output, int x, int y, float min, float max)
269  : BC_TumbleTextBox(gui, *output,
270         min, max, x, y, xS(60), 2)
271 {
272         this->plugin = plugin;
273         this->gui = gui;
274         this->output = output;
275         this->slider = slider;
276         this->min = min;
277         this->max = max;
278         set_increment(0.01);
279 }
280
281 ChromaKeyFText::~ChromaKeyFText()
282 {
283 }
284
285 int ChromaKeyFText::handle_event()
286 {
287         *output = atof(get_text());
288         if(*output > max) *output = max;
289         else if(*output < min) *output = min;
290         slider->update(*output);
291         plugin->send_configure_change();
292         return 1;
293 }
294
295 ChromaKeyFSlider::ChromaKeyFSlider(ChromaKey *plugin,
296         ChromaKeyFText *text, float *output, int x, int y,
297         float min, float max, int w)
298  : BC_FSlider(x, y, 0, w, w, min, max, *output)
299 {
300         this->plugin = plugin;
301         this->output = output;
302         this->text = text;
303         set_precision (0.01);
304         enable_show_value(0); // Hide caption
305 }
306
307 ChromaKeyFSlider::~ChromaKeyFSlider()
308 {
309 }
310
311 int ChromaKeyFSlider::handle_event()
312 {
313         *output = get_value();
314         text->update(*output);
315         plugin->send_configure_change();
316         return 1;
317 }
318
319 ChromaKeyClr::ChromaKeyClr(ChromaKey *plugin, ChromaKeyWindow *gui, int x, int y, int clear)
320  : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
321 {
322         this->plugin = plugin;
323         this->gui = gui;
324         this->clear = clear;
325 }
326
327 ChromaKeyClr::~ChromaKeyClr()
328 {
329 }
330
331 int ChromaKeyClr::handle_event()
332 {
333         plugin->config.reset(clear);
334         gui->update_gui(clear);
335         plugin->send_configure_change();
336         return 1;
337 }
338
339
340
341 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
342  : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
343 {
344         this->plugin = plugin;
345 }
346 int ChromaKeyUseValue::handle_event()
347 {
348         plugin->config.use_value = get_value();
349         plugin->send_configure_change();
350         return 1;
351 }
352
353 ChromaKeyReset::ChromaKeyReset(ChromaKey *plugin, ChromaKeyWindow *gui, int x, int y)
354  : BC_GenericButton(x, y, _("Reset"))
355 {
356         this->plugin = plugin;
357         this->gui = gui;
358 }
359
360 int ChromaKeyReset::handle_event()
361 {
362         plugin->config.reset(RESET_ALL);
363         gui->update_gui(RESET_ALL);
364         plugin->send_configure_change();
365         return 1;
366 }
367
368 ChromaKeyDefaultSettings::ChromaKeyDefaultSettings(ChromaKey *plugin, ChromaKeyWindow *gui,
369         int x, int y, int w)
370  : BC_GenericButton(x, y, w, _("Default"))
371 {
372         this->plugin = plugin;
373         this->gui = gui;
374 }
375 ChromaKeyDefaultSettings::~ChromaKeyDefaultSettings()
376 {
377 }
378 int ChromaKeyDefaultSettings::handle_event()
379 {
380         plugin->config.reset(RESET_DEFAULT_SETTINGS);
381         gui->update_gui(RESET_DEFAULT_SETTINGS);
382         plugin->send_configure_change();
383         return 1;
384 }
385
386 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker(ChromaKey *plugin,
387         ChromaKeyWindow *gui,
388         int x,
389         int y)
390  : BC_GenericButton(x, y, _("Use color picker"))
391 {
392         this->plugin = plugin;
393         this->gui = gui;
394 }
395
396 int ChromaKeyUseColorPicker::handle_event()
397 {
398         plugin->config.red = plugin->get_red();
399         plugin->config.green = plugin->get_green();
400         plugin->config.blue = plugin->get_blue();
401         gui->update_sample();
402         plugin->send_configure_change();
403         return 1;
404 }
405
406
407
408
409 ChromaKeyColorThread::ChromaKeyColorThread(ChromaKey *plugin, ChromaKeyWindow *gui)
410  : ColorPicker(1, _("Inner color"))
411 {
412         this->plugin = plugin;
413         this->gui = gui;
414 }
415
416 int ChromaKeyColorThread::handle_new_color(int output, int alpha)
417 {
418         plugin->config.red = (float)(output & 0xff0000) / 0xff0000;
419         plugin->config.green = (float)(output & 0xff00) / 0xff00;
420         plugin->config.blue = (float)(output & 0xff) / 0xff;
421         gui->lock_window("ChromaKeyColorThread::handle_new_color");
422         gui->update_sample();
423         gui->unlock_window();
424         plugin->send_configure_change();
425         return 1;
426 }
427
428
429
430
431
432
433
434
435
436
437 ChromaKeyServer::ChromaKeyServer(ChromaKey *plugin)
438  : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
439 {
440         this->plugin = plugin;
441 }
442 void ChromaKeyServer::init_packages()
443 {
444         for(int i = 0; i < get_total_packages(); i++)
445         {
446                 ChromaKeyPackage *pkg = (ChromaKeyPackage*)get_package(i);
447                 pkg->y1 = plugin->input->get_h() * i / get_total_packages();
448                 pkg->y2 = plugin->input->get_h() * (i + 1) / get_total_packages();
449         }
450
451 }
452 LoadClient* ChromaKeyServer::new_client()
453 {
454         return new ChromaKeyUnit(plugin, this);
455 }
456 LoadPackage* ChromaKeyServer::new_package()
457 {
458         return new ChromaKeyPackage;
459 }
460
461
462
463 ChromaKeyPackage::ChromaKeyPackage()
464  : LoadPackage()
465 {
466 }
467
468 ChromaKeyUnit::ChromaKeyUnit(ChromaKey *plugin, ChromaKeyServer *server)
469  : LoadClient(server)
470 {
471         this->plugin = plugin;
472 }
473
474
475 void ChromaKeyUnit::process_package(LoadPackage *package)
476 {
477         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
478
479         int w = plugin->input->get_w();
480
481         float h, s, v;
482         HSV::rgb_to_hsv(plugin->config.red,
483                 plugin->config.green,
484                 plugin->config.blue,
485                 h,
486                 s,
487                 v);
488         //float min_hue = h - plugin->config.threshold * 360 / 100;
489         //float max_hue = h + plugin->config.threshold * 360 / 100;
490
491
492 #define RGB_TO_VALUE(r, g, b) YUV::yuv.rgb_to_y_f((r),(g),(b))
493
494 #define OUTER_VARIABLES(plugin) \
495         float value = RGB_TO_VALUE(plugin->config.red, \
496                 plugin->config.green, \
497                 plugin->config.blue); \
498         float threshold = plugin->config.threshold / 100; \
499         float min_v = value - threshold; \
500         float max_v = value + threshold; \
501         float r_key = plugin->config.red; \
502         float g_key = plugin->config.green; \
503         float b_key = plugin->config.blue; \
504         int y_key, u_key, v_key; \
505         YUV::yuv.rgb_to_yuv_8( \
506                 (int)(r_key * 0xff), (int)(g_key * 0xff), (int)(b_key * 0xff), \
507                 y_key, u_key, v_key); \
508         float run = plugin->config.slope / 100; \
509         float threshold_run = threshold + run;
510
511         OUTER_VARIABLES(plugin)
512
513
514
515 #define CHROMAKEY(type, components, max, use_yuv) \
516 { \
517         for(int i = pkg->y1; i < pkg->y2; i++) \
518         { \
519                 type *row = (type*)plugin->input->get_rows()[i]; \
520  \
521                 for(int j = 0; j < w; j++) \
522                 { \
523                         float a = 1; \
524  \
525 /* Test for value in range */ \
526                         if(plugin->config.use_value) \
527                         { \
528                                 float current_value; \
529                                 if(use_yuv) \
530                                 { \
531                                         float r = (float)row[0] / max; \
532                                         current_value = r; \
533                                 } \
534                                 else \
535                                 { \
536                                         float r = (float)row[0] / max; \
537                                         float g = (float)row[1] / max; \
538                                         float b = (float)row[2] / max; \
539                                         current_value = RGB_TO_VALUE(r, g, b); \
540                                 } \
541  \
542 /* Full transparency if in range */ \
543                                 if(current_value >= min_v && current_value < max_v) \
544                                 { \
545                                         a = 0; \
546                                 } \
547                                 else \
548 /* Phased out if below or above range */ \
549                                 if(current_value < min_v) \
550                                 { \
551                                         if(min_v - current_value < run) \
552                                                 a = (min_v - current_value) / run; \
553                                 } \
554                                 else \
555                                 if(current_value - max_v < run) \
556                                         a = (current_value - max_v) / run; \
557                         } \
558                         else \
559 /* Use color cube */ \
560                         { \
561                                 float difference; \
562                                 if(use_yuv) \
563                                 { \
564                                         type y = row[0]; \
565                                         type u = row[1]; \
566                                         type v = row[2]; \
567                                         difference = sqrt(SQR(y - y_key) + \
568                                                 SQR(u - u_key) + \
569                                                 SQR(v - v_key)) / max; \
570                                 } \
571                                 else \
572                                 { \
573                                         float r = (float)row[0] / max; \
574                                         float g = (float)row[1] / max; \
575                                         float b = (float)row[2] / max; \
576                                         difference = sqrt(SQR(r - r_key) +  \
577                                                 SQR(g - g_key) + \
578                                                 SQR(b - b_key)); \
579                                 } \
580                                 if(difference < threshold) \
581                                 { \
582                                         a = 0; \
583                                 } \
584                                 else \
585                                 if(difference < threshold_run) \
586                                 { \
587                                         a = (difference - threshold) / run; \
588                                 } \
589  \
590                         } \
591  \
592 /* Multiply alpha and put back in frame */ \
593                         if(components == 4) \
594                         { \
595                                 row[3] = MIN((type)(a * max), row[3]); \
596                         } \
597                         else \
598                         if(use_yuv) \
599                         { \
600                                 row[0] = (type)(a * row[0]); \
601                                 row[1] = (type)(a * (row[1] - (max / 2 + 1)) + max / 2 + 1); \
602                                 row[2] = (type)(a * (row[2] - (max / 2 + 1)) + max / 2 + 1); \
603                         } \
604                         else \
605                         { \
606                                 row[0] = (type)(a * row[0]); \
607                                 row[1] = (type)(a * row[1]); \
608                                 row[2] = (type)(a * row[2]); \
609                         } \
610  \
611                         row += components; \
612                 } \
613         } \
614 }
615
616
617
618
619         switch(plugin->input->get_color_model())
620         {
621                 case BC_RGB_FLOAT:
622                         CHROMAKEY(float, 3, 1.0, 0);
623                         break;
624                 case BC_RGBA_FLOAT:
625                         CHROMAKEY(float, 4, 1.0, 0);
626                         break;
627                 case BC_RGB888:
628                         CHROMAKEY(unsigned char, 3, 0xff, 0);
629                         break;
630                 case BC_RGBA8888:
631                         CHROMAKEY(unsigned char, 4, 0xff, 0);
632                         break;
633                 case BC_YUV888:
634                         CHROMAKEY(unsigned char, 3, 0xff, 1);
635                         break;
636                 case BC_YUVA8888:
637                         CHROMAKEY(unsigned char, 4, 0xff, 1);
638                         break;
639                 case BC_YUV161616:
640                         CHROMAKEY(uint16_t, 3, 0xffff, 1);
641                         break;
642                 case BC_YUVA16161616:
643                         CHROMAKEY(uint16_t, 4, 0xffff, 1);
644                         break;
645         }
646
647 }
648
649
650
651
652
653 REGISTER_PLUGIN(ChromaKey)
654
655
656
657 ChromaKey::ChromaKey(PluginServer *server)
658  : PluginVClient(server)
659 {
660
661         engine = 0;
662 }
663
664 ChromaKey::~ChromaKey()
665 {
666
667         delete engine;
668 }
669
670
671 int ChromaKey::process_buffer(VFrame *frame,
672                 int64_t start_position,
673                 double frame_rate)
674 {
675 SET_TRACE
676
677         load_configuration();
678         this->input = frame;
679         this->output = frame;
680
681         read_frame(frame,
682                 0,
683                 start_position,
684                 frame_rate,
685                 get_use_opengl());
686
687         if(EQUIV(config.threshold, 0))
688         {
689                 return 1;
690         }
691         else
692         {
693                 if(get_use_opengl()) return run_opengl();
694
695                 if(!engine) engine = new ChromaKeyServer(this);
696                 engine->process_packages();
697         }
698 SET_TRACE
699
700         return 1;
701 }
702
703 const char* ChromaKey::plugin_title() { return N_("Chroma key"); }
704 int ChromaKey::is_realtime() { return 1; }
705
706 NEW_WINDOW_MACRO(ChromaKey, ChromaKeyWindow)
707
708 LOAD_CONFIGURATION_MACRO(ChromaKey, ChromaKeyConfig)
709
710
711 void ChromaKey::save_data(KeyFrame *keyframe)
712 {
713         FileXML output;
714         output.set_shared_output(keyframe->xbuf);
715         output.tag.set_title("CHROMAKEY");
716         output.tag.set_property("RED", config.red);
717         output.tag.set_property("GREEN", config.green);
718         output.tag.set_property("BLUE", config.blue);
719         output.tag.set_property("THRESHOLD", config.threshold);
720         output.tag.set_property("SLOPE", config.slope);
721         output.tag.set_property("USE_VALUE", config.use_value);
722         output.append_tag();
723         output.tag.set_title("/CHROMAKEY");
724         output.append_tag();
725         output.append_newline();
726         output.terminate_string();
727 }
728
729 void ChromaKey::read_data(KeyFrame *keyframe)
730 {
731         FileXML input;
732
733         input.set_shared_input(keyframe->xbuf);
734
735         while(!input.read_tag())
736         {
737                 if(input.tag.title_is("CHROMAKEY"))
738                 {
739                         config.red = input.tag.get_property("RED", config.red);
740                         config.green = input.tag.get_property("GREEN", config.green);
741                         config.blue = input.tag.get_property("BLUE", config.blue);
742                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
743                         config.slope = input.tag.get_property("SLOPE", config.slope);
744                         config.use_value = input.tag.get_property("USE_VALUE", config.use_value);
745                 }
746         }
747 }
748
749
750
751 void ChromaKey::update_gui()
752 {
753         if(thread)
754         {
755                 load_configuration();
756                 thread->window->lock_window();
757                 ((ChromaKeyWindow *)(thread->window))->update_gui(RESET_ALL);
758                 thread->window->unlock_window();
759         }
760 }
761
762 void ChromaKeyWindow::update_gui(int clear)
763 {
764         ChromaKeyConfig &config = plugin->config;
765         switch(clear) {
766                 case RESET_RGB :
767                         update_sample();
768                         break;
769                 case RESET_SLOPE :
770                         slope_text->update(config.slope);
771                         slope_slider->update(config.slope);
772                         break;
773                 case RESET_THRESHOLD :
774                         threshold_text->update(config.threshold);
775                         threshold_slider->update(config.threshold);
776                         break;
777                 case RESET_ALL :
778                 case RESET_DEFAULT_SETTINGS :
779                 default:
780                         update_sample();
781                         slope_text->update(config.slope);
782                         slope_slider->update(config.slope);
783                         threshold_text->update(config.threshold);
784                         threshold_slider->update(config.threshold);
785                         use_value->update(config.use_value);
786                         break;
787         }
788 }
789
790 int ChromaKey::handle_opengl()
791 {
792 #ifdef HAVE_GL
793         OUTER_VARIABLES(this)
794
795
796
797         static const char *uniform_frag =
798                 "uniform sampler2D tex;\n"
799                 "uniform float min_v;\n"
800                 "uniform float max_v;\n"
801                 "uniform float run;\n"
802                 "uniform float threshold;\n"
803                 "uniform float threshold_run;\n"
804                 "uniform vec3 key;\n";
805
806         static const char *get_yuvvalue_frag =
807                 "float get_value(vec4 color)\n"
808                 "{\n"
809                 "       return abs(color.r);\n"
810                 "}\n";
811
812         static const char *get_rgbvalue_frag =
813                 "uniform vec3 rgb_to_y_vector;\n"
814                 "uniform float yminf;\n"
815                 "float get_value(vec4 color)\n"
816                 "{\n"
817                 "       return dot(color.rgb, rgb_to_y_vector) + yminf;\n"
818                 "}\n";
819
820         static const char *value_frag =
821                 "void main()\n"
822                 "{\n"
823                 "       vec4 color = texture2D(tex, gl_TexCoord[0].st);\n"
824                 "       float value = get_value(color);\n"
825                 "       float alpha = 1.0;\n"
826                 "\n"
827                 "       if(value >= min_v && value < max_v)\n"
828                 "               alpha = 0.0;\n"
829                 "       else\n"
830                 "       if(value < min_v)\n"
831                 "       {\n"
832                 "               if(min_v - value < run)\n"
833                 "                       alpha = (min_v - value) / run;\n"
834                 "       }\n"
835                 "       else\n"
836                 "       if(value - max_v < run)\n"
837                 "               alpha = (value - max_v) / run;\n"
838                 "\n"
839                 "       gl_FragColor = vec4(color.rgb, alpha);\n"
840                 "}\n";
841
842         static const char *cube_frag =
843                 "void main()\n"
844                 "{\n"
845                 "       vec4 color = texture2D(tex, gl_TexCoord[0].st);\n"
846                 "       float difference = length(color.rgb - key);\n"
847                 "       float alpha = 1.0;\n"
848                 "       if(difference < threshold)\n"
849                 "               alpha = 0.0;\n"
850                 "       else\n"
851                 "       if(difference < threshold_run)\n"
852                 "               alpha = (difference - threshold) / run;\n"
853                 "       gl_FragColor = vec4(color.rgb, min(color.a, alpha));\n"
854                 "}\n";
855
856
857
858         get_output()->to_texture();
859         get_output()->enable_opengl();
860         get_output()->init_screen();
861
862         const char *shader_stack[16];
863         memset(shader_stack,0, sizeof(shader_stack));
864         int current_shader = 0;
865         shader_stack[current_shader++] = uniform_frag;
866
867         switch(get_output()->get_color_model()) {
868         case BC_YUV888:
869         case BC_YUVA8888:
870                 if( config.use_value ) {
871                         shader_stack[current_shader++] = get_yuvvalue_frag;
872                         shader_stack[current_shader++] = value_frag;
873                 }
874                 else {
875                         shader_stack[current_shader++] = cube_frag;
876                 }
877                 break;
878
879         default:
880                 if(config.use_value) {
881                         shader_stack[current_shader++] = get_rgbvalue_frag;
882                         shader_stack[current_shader++] = value_frag;
883                 }
884                 else {
885                         shader_stack[current_shader++] = cube_frag;
886                 }
887                 break;
888         }
889 SET_TRACE
890
891         shader_stack[current_shader] = 0;
892         unsigned int shader = VFrame::make_shader(shader_stack);
893         if( shader > 0 ) {
894                 get_output()->bind_texture(0);
895                 glUseProgram(shader);
896                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
897                 glUniform1f(glGetUniformLocation(shader, "min_v"), min_v);
898                 glUniform1f(glGetUniformLocation(shader, "max_v"), max_v);
899                 glUniform1f(glGetUniformLocation(shader, "run"), run);
900                 glUniform1f(glGetUniformLocation(shader, "threshold"), threshold);
901                 glUniform1f(glGetUniformLocation(shader, "threshold_run"), threshold_run);
902                 if(get_output()->get_color_model() != BC_YUV888 &&
903                         get_output()->get_color_model() != BC_YUVA8888)
904                         glUniform3f(glGetUniformLocation(shader, "key"),
905                                 r_key, g_key, b_key);
906                 else
907                         glUniform3f(glGetUniformLocation(shader, "key"),
908                                 (float)y_key / 0xff, (float)u_key / 0xff, (float)v_key / 0xff);
909                 if(config.use_value)
910                         BC_GL_RGB_TO_Y(shader);
911         }
912 SET_TRACE
913
914         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
915         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
916
917         if(BC_CModels::components(get_output()->get_color_model()) == 3)
918         {
919                 glEnable(GL_BLEND);
920                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
921                 get_output()->clear_pbuffer();
922         }
923 SET_TRACE
924
925         get_output()->draw_texture();
926
927         glUseProgram(0);
928         get_output()->set_opengl_state(VFrame::SCREEN);
929         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
930         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
931         glDisable(GL_BLEND);
932 SET_TRACE
933 #endif
934         return 0;
935 }
936