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