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