no longer need ffmpeg patch0 which was for Termux
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / huesaturation / huesaturation.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 "clip.h"
25 #include "bchash.h"
26 #include "filexml.h"
27 #include "huesaturation.h"
28 #include "guicast.h"
29 #include "language.h"
30 #include "loadbalance.h"
31 #include "bccolors.h"
32 #include "playback3d.h"
33 #include "pluginvclient.h"
34 #include "theme.h"
35 #include "vframe.h"
36
37 #include <stdint.h>
38 #include <string.h>
39
40
41
42 REGISTER_PLUGIN(HueEffect)
43
44
45
46
47
48 HueConfig::HueConfig()
49 {
50         reset(RESET_ALL);
51 }
52
53 void HueConfig::reset(int clear)
54 {
55         switch(clear) {
56                 case RESET_HUV : hue = 0;
57                         break;
58                 case RESET_SAT : saturation = 0;
59                         break;
60                 case RESET_VAL : value = 0;
61                         break;
62                 case RESET_ALL :
63                 default:
64                         hue = saturation = value = 0;
65                         break;
66         }
67 }
68
69 void HueConfig::copy_from(HueConfig &src)
70 {
71         hue = src.hue;
72         saturation = src.saturation;
73         value = src.value;
74 }
75 int HueConfig::equivalent(HueConfig &src)
76 {
77         return EQUIV(hue, src.hue) &&
78                 EQUIV(saturation, src.saturation) &&
79                 EQUIV(value, src.value);
80 }
81 void HueConfig::interpolate(HueConfig &prev,
82         HueConfig &next,
83         long prev_frame,
84         long next_frame,
85         long current_frame)
86 {
87         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
88         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
89
90         this->hue = prev.hue * prev_scale + next.hue * next_scale;
91         this->saturation = prev.saturation * prev_scale + next.saturation * next_scale;
92         this->value = prev.value * prev_scale + next.value * next_scale;
93 }
94
95
96
97
98
99
100 /* SATURATION VALUES
101   saturation is stored    from -100.00  to +100.00
102   saturation_slider  goes from -100.00  to +100.00
103   saturation_caption goes from    0.000 to   +2.000 (clear to +1.000)
104   saturation_text    goes from -100.00  to +100.00
105 */
106 /* VALUE VALUES
107   value is stored    from -100.00  to +100.00
108   value_slider  goes from -100.00  to +100.00
109   value_caption goes from    0.000 to   +2.000 (clear to +1.000)
110   value_text    goes from -100.00  to +100.00
111 */
112
113 HueText::HueText(HueEffect *plugin, HueWindow *gui, int x, int y)
114  : BC_TumbleTextBox(gui, plugin->config.hue,
115         (float)MINHUE, (float)MAXHUE, x, y, xS(60), 2)
116 {
117         this->gui = gui;
118         this->plugin = plugin;
119         set_increment(0.01);
120 }
121
122 HueText::~HueText()
123 {
124 }
125
126 int HueText::handle_event()
127 {
128         float min = MINHUE, max = MAXHUE;
129         float output = atof(get_text());
130         if(output > max) output = max;
131         if(output < min) output = min;
132         plugin->config.hue = output;
133         gui->hue_slider->update(plugin->config.hue);
134         plugin->send_configure_change();
135         return 1;
136 }
137
138 HueSlider::HueSlider(HueEffect *plugin, HueWindow *gui, int x, int y, int w)
139  : BC_FSlider(x, y, 0, w, w,
140         (float)MINHUE, (float)MAXHUE,
141         plugin->config.hue)
142 {
143         this->plugin = plugin;
144         this->gui = gui;
145         enable_show_value(0); // Hide caption
146 }
147 int HueSlider::handle_event()
148 {
149         plugin->config.hue = get_value();
150         gui->hue_text->update(plugin->config.hue);
151         plugin->send_configure_change();
152         return 1;
153 }
154
155
156 SaturationText::SaturationText(HueEffect *plugin, HueWindow *gui, int x, int y)
157  : BC_TumbleTextBox(gui, plugin->config.saturation,
158         (float)MINSATURATION, (float)MAXSATURATION, x, y, xS(60), 2)
159 {
160         this->gui = gui;
161         this->plugin = plugin;
162         set_increment(0.01);
163 }
164
165 SaturationText::~SaturationText()
166 {
167 }
168
169 int SaturationText::handle_event()
170 {
171         float min = MINSATURATION, max = MAXSATURATION;
172         float output = atof(get_text());
173         if(output > max) output = max;
174         if(output < min) output = min;
175         plugin->config.saturation = output;
176         gui->sat_slider->update(plugin->config.saturation);
177         plugin->send_configure_change();
178         return 1;
179 }
180
181 SaturationSlider::SaturationSlider(HueEffect *plugin, HueWindow *gui, int x, int y, int w)
182  : BC_FSlider(x, y, 0, w, w,
183         (float)MINSATURATION, (float)MAXSATURATION,
184         plugin->config.saturation)
185 {
186         this->plugin = plugin;
187         this->gui = gui;
188         enable_show_value(0); // Hide caption
189 }
190 int SaturationSlider::handle_event()
191 {
192         plugin->config.saturation = get_value();
193         gui->sat_text->update(plugin->config.saturation);
194         plugin->send_configure_change();
195         return 1;
196 }
197
198 char* SaturationSlider::get_caption()
199 {
200         float fraction = ((float)plugin->config.saturation - MINSATURATION) /
201                 MAXSATURATION;
202         sprintf(string, "%0.4f", fraction);
203         return string;
204 }
205
206
207 ValueText::ValueText(HueEffect *plugin, HueWindow *gui, int x, int y)
208  : BC_TumbleTextBox(gui, plugin->config.value,
209         (float)MINVALUE, (float)MAXVALUE, x, y, xS(60), 2)
210 {
211         this->gui = gui;
212         this->plugin = plugin;
213         set_increment(0.01);
214 }
215
216 ValueText::~ValueText()
217 {
218 }
219
220 int ValueText::handle_event()
221 {
222         float min = MINVALUE, max = MAXVALUE;
223         float output = atof(get_text());
224         if(output > max) output = max;
225         if(output < min) output = min;
226         plugin->config.value = output;
227         gui->value_slider->update(plugin->config.value);
228         plugin->send_configure_change();
229         return 1;
230 }
231
232 ValueSlider::ValueSlider(HueEffect *plugin, HueWindow *gui, int x, int y, int w)
233  : BC_FSlider(x, y, 0, w, w,
234         (float)MINVALUE, (float)MAXVALUE,
235         plugin->config.value)
236 {
237         this->plugin = plugin;
238         this->gui = gui;
239         enable_show_value(0); // Hide caption
240 }
241 int ValueSlider::handle_event()
242 {
243         plugin->config.value = get_value();
244         gui->value_text->update(plugin->config.value);
245         plugin->send_configure_change();
246         return 1;
247 }
248
249 char* ValueSlider::get_caption()
250 {
251         float fraction = ((float)plugin->config.value - MINVALUE) / MAXVALUE;
252         sprintf(string, "%0.4f", fraction);
253         return string;
254 }
255
256
257 HueReset::HueReset(HueEffect *plugin, HueWindow *gui, int x, int y)
258  : BC_GenericButton(x, y, _("Reset"))
259 {
260         this->plugin = plugin;
261         this->gui = gui;
262 }
263 HueReset::~HueReset()
264 {
265 }
266 int HueReset::handle_event()
267 {
268         plugin->config.reset(RESET_ALL); // clear=0 ==> reset all
269         gui->update_gui(RESET_ALL);
270         plugin->send_configure_change();
271         return 1;
272 }
273
274
275 HueClr::HueClr(HueEffect *plugin, HueWindow *gui, int x, int y, int clear)
276  : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
277 {
278         this->plugin = plugin;
279         this->gui = gui;
280         this->clear = clear;
281 }
282 HueClr::~HueClr()
283 {
284 }
285 int HueClr::handle_event()
286 {
287         // clear==1 ==> Hue
288         // clear==2 ==> Saturation
289         // clear==3 ==> Value
290         plugin->config.reset(clear);
291         gui->update_gui(clear);
292         plugin->send_configure_change();
293         return 1;
294 }
295
296
297
298
299 HueWindow::HueWindow(HueEffect *plugin)
300  : PluginClientWindow(plugin, xS(420), yS(160), xS(420), yS(160), 0)
301 {
302         this->plugin = plugin;
303 }
304 void HueWindow::create_objects()
305 {
306         int xs10 = xS(10), xs200 = xS(200);
307         int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40);
308         int x = xs10, y = ys10;
309         int x2 = xS(80), x3 = xS(180);
310         int clr_x = get_w()-x - xS(22); // note: clrBtn_w = 22
311
312         BC_Bar *bar;
313
314 // Hue
315         y += ys10;
316         add_subwindow(new BC_Title(x, y, _("Hue:")));
317         hue_text = new HueText(plugin, this, (x + x2), y);
318         hue_text->create_objects();
319         add_subwindow(hue_slider = new HueSlider(plugin, this, x3, y, xs200));
320         clr_x = x3 + hue_slider->get_w() + x;
321         add_subwindow(hue_clr = new HueClr(plugin, this, clr_x, y, RESET_HUV));
322         y += ys30;
323
324 // Saturation
325         add_subwindow(new BC_Title(x, y, _("Saturation:")));
326         sat_text = new SaturationText(plugin, this, (x + x2), y);
327         sat_text->create_objects();
328         add_subwindow(sat_slider = new SaturationSlider(plugin, this, x3, y, xs200));
329         add_subwindow(sat_clr = new HueClr(plugin, this, clr_x, y, RESET_SAT));
330         y += ys30;
331
332 // Value
333         add_subwindow(new BC_Title(x, y, _("Value:")));
334         value_text = new ValueText(plugin, this, (x + x2), y);
335         value_text->create_objects();
336         add_subwindow(value_slider = new ValueSlider(plugin, this, x3, y, xs200));
337         add_subwindow(value_clr = new HueClr(plugin, this, clr_x, y, RESET_VAL));
338         y += ys40;
339
340 // Reset section
341         add_subwindow(bar = new BC_Bar(x, y, get_w()-2*x));
342         y += ys10;
343         add_subwindow(reset = new HueReset(plugin, this, x, y));
344         show_window();
345         flush();
346 }
347
348
349 // for Reset button
350 void HueWindow::update_gui(int clear)
351 {
352         switch(clear) {
353                 case RESET_HUV :
354                         hue_text->update(plugin->config.hue);
355                         hue_slider->update(plugin->config.hue);
356                         break;
357                 case RESET_SAT :
358                         sat_text->update(plugin->config.saturation);
359                         sat_slider->update(plugin->config.saturation);
360                         break;
361                 case RESET_VAL :
362                         value_text->update(plugin->config.value);
363                         value_slider->update(plugin->config.value);
364                         break;
365                 case RESET_ALL :
366                 default:
367                         hue_text->update(plugin->config.hue);
368                         hue_slider->update(plugin->config.hue);
369                         sat_text->update(plugin->config.saturation);
370                         sat_slider->update(plugin->config.saturation);
371                         value_text->update(plugin->config.value);
372                         value_slider->update(plugin->config.value);
373                         break;
374         }
375 }
376
377
378
379
380
381
382
383
384 HueEngine::HueEngine(HueEffect *plugin, int cpus)
385  : LoadServer(cpus, cpus)
386 {
387         this->plugin = plugin;
388 }
389 void HueEngine::init_packages()
390 {
391         for(int i = 0; i < LoadServer::get_total_packages(); i++)
392         {
393                 HuePackage *pkg = (HuePackage*)get_package(i);
394                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
395                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
396         }
397 }
398 LoadClient* HueEngine::new_client()
399 {
400         return new HueUnit(plugin, this);
401 }
402 LoadPackage* HueEngine::new_package()
403 {
404         return new HuePackage;
405 }
406
407
408
409
410
411
412
413
414 HuePackage::HuePackage()
415  : LoadPackage()
416 {
417 }
418
419 HueUnit::HueUnit(HueEffect *plugin, HueEngine *server)
420  : LoadClient(server)
421 {
422         this->plugin = plugin;
423 }
424
425
426
427
428
429
430
431 #define HUESATURATION(type, max, components, use_yuv) \
432 { \
433         float h_offset = plugin->config.hue; \
434         float s_offset = ((float)plugin->config.saturation - MINSATURATION) / MAXSATURATION; \
435         float v_offset = ((float)plugin->config.value - MINVALUE) / MAXVALUE; \
436         for(int i = pkg->row1; i < pkg->row2; i++) \
437         { \
438                 type* in_row = (type*)plugin->input->get_rows()[i]; \
439                 type* out_row = (type*)plugin->output->get_rows()[i]; \
440  \
441                 for(int j = 0; j < w; j++) \
442                 { \
443                         float h, s, va; \
444                         int y, u, v; \
445                         float r, g, b; \
446                         int r_i, g_i, b_i; \
447  \
448                         if(use_yuv) \
449                         { \
450                                 y = (int)in_row[0]; \
451                                 u = (int)in_row[1]; \
452                                 v = (int)in_row[2]; \
453                                 if(max == 0xffff) \
454                                         YUV::yuv.yuv_to_rgb_16(r_i, g_i, b_i, y, u, v); \
455                                 else \
456                                         YUV::yuv.yuv_to_rgb_8(r_i, g_i, b_i, y, u, v); \
457                                 HSV::rgb_to_hsv((float)r_i / max, \
458                                         (float)g_i / max, \
459                                         (float)b_i / max, \
460                                         h, \
461                                         s, \
462                                         va); \
463                         } \
464                         else \
465                         { \
466                                 r = (float)in_row[0] / max; \
467                                 g = (float)in_row[1] / max; \
468                                 b = (float)in_row[2] / max; \
469                                 HSV::rgb_to_hsv(r, g, b, h, s, va); \
470                         } \
471  \
472  \
473                         h += h_offset; \
474                         s *= s_offset; \
475                         va *= v_offset; \
476  \
477                         if(h >= 360) h -= 360; \
478                         if(h < 0) h += 360; \
479                         if(sizeof(type) < 4) \
480                         { \
481                                 if(s > 1) s = 1; \
482                                 if(va > 1) va = 1; \
483                                 if(s < 0) s = 0; \
484                                 if(va < 0) va = 0; \
485                         } \
486  \
487                         if(use_yuv) \
488                         { \
489                                 HSV::hsv_to_yuv(y, u, v, h, s, va, max); \
490                                 out_row[0] = y; \
491                                 out_row[1] = u; \
492                                 out_row[2] = v; \
493                         } \
494                         else \
495                         { \
496                                 HSV::hsv_to_rgb(r, g, b, h, s, va); \
497                                 if(sizeof(type) < 4) \
498                                 { \
499                                         r *= max; \
500                                         g *= max; \
501                                         b *= max; \
502                                         out_row[0] = (type)CLIP(r, 0, max); \
503                                         out_row[1] = (type)CLIP(g, 0, max); \
504                                         out_row[2] = (type)CLIP(b, 0, max); \
505                                 } \
506                                 else \
507                                 { \
508                                         out_row[0] = (type)r; \
509                                         out_row[1] = (type)g; \
510                                         out_row[2] = (type)b; \
511                                 } \
512                         } \
513  \
514                         in_row += components; \
515                         out_row += components; \
516                 } \
517         } \
518 }
519
520
521 void HueUnit::process_package(LoadPackage *package)
522 {
523         HuePackage *pkg = (HuePackage*)package;
524         int w = plugin->input->get_w();
525
526         switch(plugin->input->get_color_model())
527         {
528                 case BC_RGB888:
529                         HUESATURATION(unsigned char, 0xff, 3, 0)
530                         break;
531
532                 case BC_RGB_FLOAT:
533                         HUESATURATION(float, 1, 3, 0)
534                         break;
535
536                 case BC_YUV888:
537                         HUESATURATION(unsigned char, 0xff, 3, 1)
538                         break;
539
540                 case BC_RGB161616:
541                         HUESATURATION(uint16_t, 0xffff, 3, 0)
542                         break;
543
544                 case BC_YUV161616:
545                         HUESATURATION(uint16_t, 0xffff, 3, 1)
546                         break;
547
548                 case BC_RGBA_FLOAT:
549                         HUESATURATION(float, 1, 4, 0)
550                         break;
551
552                 case BC_RGBA8888:
553                         HUESATURATION(unsigned char, 0xff, 4, 0)
554                         break;
555
556                 case BC_YUVA8888:
557                         HUESATURATION(unsigned char, 0xff, 4, 1)
558                         break;
559
560                 case BC_RGBA16161616:
561                         HUESATURATION(uint16_t, 0xffff, 4, 0)
562                         break;
563
564                 case BC_YUVA16161616:
565                         HUESATURATION(uint16_t, 0xffff, 4, 1)
566                         break;
567
568         }
569 }
570
571
572
573
574
575
576 HueEffect::HueEffect(PluginServer *server)
577  : PluginVClient(server)
578 {
579         engine = 0;
580
581 }
582 HueEffect::~HueEffect()
583 {
584
585         if(engine) delete engine;
586 }
587
588 int HueEffect::process_buffer(VFrame *frame,
589         int64_t start_position,
590         double frame_rate)
591 {
592         load_configuration();
593
594         read_frame(frame,
595                 0,
596                 start_position,
597                 frame_rate,
598                 get_use_opengl());
599
600
601         this->input = frame;
602         this->output = frame;
603         if(EQUIV(config.hue, 0) && EQUIV(config.saturation, 0) && EQUIV(config.value, 0))
604         {
605                 return 0;
606         }
607         else
608         {
609                 if(get_use_opengl())
610                 {
611                         run_opengl();
612                         return 0;
613                 }
614
615                 if(!engine) engine = new HueEngine(this, PluginClient::smp + 1);
616
617                 engine->process_packages();
618         }
619         return 0;
620 }
621
622 const char* HueEffect::plugin_title() { return N_("Hue saturation"); }
623 int HueEffect::is_realtime() { return 1; }
624
625 NEW_WINDOW_MACRO(HueEffect, HueWindow)
626 LOAD_CONFIGURATION_MACRO(HueEffect, HueConfig)
627
628
629 void HueEffect::save_data(KeyFrame *keyframe)
630 {
631         FileXML output;
632         output.set_shared_output(keyframe->xbuf);
633         output.tag.set_title("HUESATURATION");
634         output.tag.set_property("HUE", config.hue);
635         output.tag.set_property("SATURATION", config.saturation);
636         output.tag.set_property("VALUE", config.value);
637         output.append_tag();
638         output.tag.set_title("/HUESATURATION");
639         output.append_tag();
640         output.append_newline();
641         output.terminate_string();
642 }
643 void HueEffect::read_data(KeyFrame *keyframe)
644 {
645         FileXML input;
646         input.set_shared_input(keyframe->xbuf);
647         while(!input.read_tag())
648         {
649                 if(input.tag.title_is("HUESATURATION"))
650                 {
651                         config.hue = input.tag.get_property("HUE", config.hue);
652                         config.saturation = input.tag.get_property("SATURATION", config.saturation);
653                         config.value = input.tag.get_property("VALUE", config.value);
654                 }
655         }
656 }
657 void HueEffect::update_gui()
658 {
659         if(thread)
660         {
661                 ((HueWindow*)thread->window)->lock_window();
662                 load_configuration();
663                 ((HueWindow*)thread->window)->hue_text->update(config.hue);
664                 ((HueWindow*)thread->window)->hue_slider->update(config.hue);
665                 ((HueWindow*)thread->window)->sat_text->update(config.saturation);
666                 ((HueWindow*)thread->window)->sat_slider->update(config.saturation);
667                 ((HueWindow*)thread->window)->value_text->update(config.value);
668                 ((HueWindow*)thread->window)->value_slider->update(config.value);
669                 ((HueWindow*)thread->window)->unlock_window();
670         }
671 }
672
673 int HueEffect::handle_opengl()
674 {
675 #ifdef HAVE_GL
676         const char *yuv_saturation_frag =
677                 "uniform sampler2D tex;\n"
678                 "uniform float s_offset;\n"
679                 "uniform float v_offset;\n"
680                 "void main()\n"
681                 "{\n"
682                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
683                 "       pixel.r *= v_offset;\n"
684                 "       pixel.gb -= vec2(0.5, 0.5);\n"
685                 "       pixel.g *= s_offset;\n"
686                 "       pixel.b *= s_offset;\n"
687                 "       pixel.gb += vec2(0.5, 0.5);\n"
688                 "       gl_FragColor = pixel;\n"
689                 "}\n";
690
691
692         const char *yuv_frag =
693                 "uniform sampler2D tex;\n"
694                 "uniform float h_offset;\n"
695                 "uniform float s_offset;\n"
696                 "uniform float v_offset;\n"
697                 "void main()\n"
698                 "{\n"
699                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
700                         YUV_TO_RGB_FRAG("pixel")
701                         RGB_TO_HSV_FRAG("pixel")
702                 "       pixel.r += h_offset;\n"
703                 "       pixel.g *= s_offset;\n"
704                 "       pixel.b *= v_offset;\n"
705                 "       if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
706                 "       if(pixel.r < 0.0) pixel.r += 360.0;\n"
707                         HSV_TO_RGB_FRAG("pixel")
708                         RGB_TO_YUV_FRAG("pixel")
709                 "       gl_FragColor = pixel;\n"
710                 "}\n";
711
712         const char *rgb_frag =
713                 "uniform sampler2D tex;\n"
714                 "uniform float h_offset;\n"
715                 "uniform float s_offset;\n"
716                 "uniform float v_offset;\n"
717                 "void main()\n"
718                 "{\n"
719                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
720                         RGB_TO_HSV_FRAG("pixel")
721                 "       pixel.r += h_offset;\n"
722                 "       pixel.g *= s_offset;\n"
723                 "       pixel.b *= v_offset;\n"
724                 "       if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
725                 "       if(pixel.r < 0.0) pixel.r += 360.0;\n"
726                         HSV_TO_RGB_FRAG("pixel")
727                 "       gl_FragColor = pixel;\n"
728                 "}\n";
729
730
731         get_output()->to_texture();
732         get_output()->enable_opengl();
733
734         const char *shader_stack[16];
735         memset(shader_stack,0, sizeof(shader_stack));
736         int current_shader = 0;
737
738         int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
739         if( need_color_matrix ) shader_stack[current_shader++] = bc_gl_colors;
740         shader_stack[current_shader++] = !need_color_matrix ? rgb_frag :
741                 EQUIV(config.hue, 0) ? yuv_saturation_frag: yuv_frag ;
742
743         shader_stack[current_shader] = 0;
744         unsigned int shader = VFrame::make_shader(shader_stack);
745         if(shader > 0) {
746                 glUseProgram(shader);
747                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
748                 glUniform1f(glGetUniformLocation(shader, "h_offset"), config.hue);
749                 glUniform1f(glGetUniformLocation(shader, "s_offset"),
750                         ((float)config.saturation - MINSATURATION) / MAXSATURATION);
751                 glUniform1f(glGetUniformLocation(shader, "v_offset"),
752                         ((float)config.value - MINVALUE) / MAXVALUE);
753                 if( need_color_matrix ) BC_GL_COLORS(shader);
754         }
755
756         get_output()->init_screen();
757         get_output()->bind_texture(0);
758         get_output()->draw_texture();
759         glUseProgram(0);
760         get_output()->set_opengl_state(VFrame::SCREEN);
761 #endif
762         return 0;
763 }
764
765
766
767
768