]> git.sesse.net Git - casparcg/blob - accelerator/ogl/image/image_shader.cpp
[mixer] Merged fixed from 2.0 where contrast adjustment incorrectly worked on premult...
[casparcg] / accelerator / ogl / image / image_shader.cpp
1 /*
2 * Copyright (c) 2011 Sveriges Television AB <info@casparcg.com>
3 *
4 * This file is part of CasparCG (www.casparcg.com).
5 *
6 * CasparCG 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 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CasparCG 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 CasparCG. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Author: Robert Nagy, ronag89@gmail.com
20 */
21
22 #include "../../StdAfx.h"
23
24 #include "image_shader.h"
25
26 #include "../util/shader.h"
27 #include "../util/device.h"
28
29 #include "blending_glsl.h"
30
31 #include <common/gl/gl_check.h>
32 #include <common/env.h>
33
34 #include <tbb/mutex.h>
35
36 namespace caspar { namespace accelerator { namespace ogl {
37
38 std::weak_ptr<shader>   g_shader;
39 tbb::mutex                              g_shader_mutex;
40 bool                                    g_blend_modes           = false;
41 bool                                    g_post_processing       = false;
42
43 std::string get_blend_color_func()
44 {
45         return
46
47                 get_adjustement_glsl()
48
49                 +
50
51                 get_blend_glsl()
52
53                 +
54
55                 R"shader(
56                                 vec3 get_blend_color(vec3 back, vec3 fore)
57                                 {
58                                         switch(blend_mode)
59                                         {
60                                         case  0: return BlendNormal(back, fore);
61                                         case  1: return BlendLighten(back, fore);
62                                         case  2: return BlendDarken(back, fore);
63                                         case  3: return BlendMultiply(back, fore);
64                                         case  4: return BlendAverage(back, fore);
65                                         case  5: return BlendAdd(back, fore);
66                                         case  6: return BlendSubstract(back, fore);
67                                         case  7: return BlendDifference(back, fore);
68                                         case  8: return BlendNegation(back, fore);
69                                         case  9: return BlendExclusion(back, fore);
70                                         case 10: return BlendScreen(back, fore);
71                                         case 11: return BlendOverlay(back, fore);
72                                 //      case 12: return BlendSoftLight(back, fore);
73                                         case 13: return BlendHardLight(back, fore);
74                                         case 14: return BlendColorDodge(back, fore);
75                                         case 15: return BlendColorBurn(back, fore);
76                                         case 16: return BlendLinearDodge(back, fore);
77                                         case 17: return BlendLinearBurn(back, fore);
78                                         case 18: return BlendLinearLight(back, fore);
79                                         case 19: return BlendVividLight(back, fore);
80                                         case 20: return BlendPinLight(back, fore);
81                                         case 21: return BlendHardMix(back, fore);
82                                         case 22: return BlendReflect(back, fore);
83                                         case 23: return BlendGlow(back, fore);
84                                         case 24: return BlendPhoenix(back, fore);
85                                         case 25: return BlendHue(back, fore);
86                                         case 26: return BlendSaturation(back, fore);
87                                         case 27: return BlendColor(back, fore);
88                                         case 28: return BlendLuminosity(back, fore);
89                                         }
90                                         return BlendNormal(back, fore);
91                                 }
92
93                                 vec4 blend(vec4 fore)
94                                 {
95                                    vec4 back = texture2D(background, gl_TexCoord[1].st).bgra;
96                                    if(blend_mode != 0)
97                                                 fore.rgb = get_blend_color(back.rgb/(back.a+0.0000001), fore.rgb/(fore.a+0.0000001))*fore.a;
98                                         switch(keyer)
99                                         {
100                                                 case 1:  return fore + back; // additive
101                                                 default: return fore + (1.0-fore.a)*back; // linear
102                                         }
103                                 }
104                 )shader";
105 }
106
107 std::string get_simple_blend_color_func()
108 {
109         return
110
111                 get_adjustement_glsl()
112
113                 +
114
115                 R"shader(
116                                 vec4 blend(vec4 fore)
117                                 {
118                                         return fore;
119                                 }
120                 )shader";
121 }
122
123 std::string get_chroma_func()
124 {
125         return
126
127                 get_chroma_glsl()
128
129                 +
130
131                 R"shader(
132                                 vec4 chroma_key(vec4 c)
133                                 {
134                                         return ChromaOnCustomColor(c.bgra).bgra;
135                                 }
136                 )shader";
137 }
138
139 std::string get_post_process()
140 {
141         return R"shader(
142                 if (post_processing)
143                 {
144                         gl_FragColor = post_process().bgra;
145                 }
146                 else
147         )shader";
148 }
149
150 std::string get_no_post_process()
151 {
152         return "";
153 }
154
155 std::string get_vertex()
156 {
157         return R"shader(
158                         void main()
159                         {
160                                 gl_TexCoord[0] = gl_MultiTexCoord0;
161                         //      gl_TexCoord[1] = gl_MultiTexCoord1;
162                                 vec4 pos = ftransform();
163                                 gl_TexCoord[1] = vec4(pos.xy, 0.0, 0.0);
164                                 pos.x = pos.x*2.0 - 1.0;
165                                 pos.y = pos.y*2.0 - 1.0;
166                                 gl_Position    = pos;
167                         }
168         )shader";
169 }
170
171 std::string get_fragment(bool blend_modes, bool post_processing)
172 {
173         return R"shader(
174
175                         #version 130
176                         uniform sampler2D       background;
177                         uniform sampler2D       plane[4];
178                         uniform vec2            plane_size[4];
179                         uniform sampler2D       local_key;
180                         uniform sampler2D       layer_key;
181
182                         uniform bool            is_hd;
183                         uniform bool            has_local_key;
184                         uniform bool            has_layer_key;
185                         uniform int                     blend_mode;
186                         uniform int                     keyer;
187                         uniform int                     pixel_format;
188                         uniform int                     deinterlace;
189
190                         uniform float           opacity;
191                         uniform bool            levels;
192                         uniform float           min_input;
193                         uniform float           max_input;
194                         uniform float           gamma;
195                         uniform float           min_output;
196                         uniform float           max_output;
197
198                         uniform bool            csb;
199                         uniform float           brt;
200                         uniform float           sat;
201                         uniform float           con;
202
203                         uniform bool            post_processing;
204                         uniform bool            straighten_alpha;
205
206                         uniform bool            chroma;
207                         uniform bool            chroma_show_mask;
208                         uniform float           chroma_target_hue;
209                         uniform float           chroma_hue_width;
210                         uniform float           chroma_min_saturation;
211                         uniform float           chroma_min_brightness;
212                         uniform float           chroma_softness;
213                         uniform float           chroma_spill_suppress;
214                         uniform float           chroma_spill_suppress_saturation;
215         )shader"
216
217         +
218
219         (blend_modes ? get_blend_color_func() : get_simple_blend_color_func())
220
221         +
222
223         get_chroma_func()
224
225         +
226
227         R"shader(
228
229                         vec4 ycbcra_to_rgba_sd(float Y, float Cb, float Cr, float A)
230                         {
231                                 vec4 rgba;
232                                 rgba.b = (1.164*(Y*255 - 16) + 1.596*(Cr*255 - 128))/255;
233                                 rgba.g = (1.164*(Y*255 - 16) - 0.813*(Cr*255 - 128) - 0.391*(Cb*255 - 128))/255;
234                                 rgba.r = (1.164*(Y*255 - 16) + 2.018*(Cb*255 - 128))/255;
235                                 rgba.a = A;
236                                 return rgba;
237                         }
238
239                         vec4 ycbcra_to_rgba_hd(float Y, float Cb, float Cr, float A)
240                         {
241                                 vec4 rgba;
242                                 rgba.b = (1.164*(Y*255 - 16) + 1.793*(Cr*255 - 128))/255;
243                                 rgba.g = (1.164*(Y*255 - 16) - 0.534*(Cr*255 - 128) - 0.213*(Cb*255 - 128))/255;
244                                 rgba.r = (1.164*(Y*255 - 16) + 2.115*(Cb*255 - 128))/255;
245                                 rgba.a = A;
246                                 return rgba;
247                         }
248
249                         vec4 ycbcra_to_rgba(float y, float cb, float cr, float a)
250                         {
251                                 if(is_hd)
252                                         return ycbcra_to_rgba_hd(y, cb, cr, a);
253                                 else
254                                         return ycbcra_to_rgba_sd(y, cb, cr, a);
255                         }
256
257                         vec4 get_sample(sampler2D sampler, vec2 coords, vec2 size)
258                         {
259                                 switch(deinterlace)
260                                 {
261                                 case 1: // upper
262                                         return texture2D(sampler, coords);
263                                 case 2: // lower
264                                         return texture2D(sampler, coords);
265                                 default:
266                                         return texture2D(sampler, coords);
267                                 }
268                         }
269
270                         vec4 get_rgba_color()
271                         {
272                                 switch(pixel_format)
273                                 {
274                                 case 0:         //gray
275                                         return vec4(get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).rrr, 1.0);
276                                 case 1:         //bgra,
277                                         return get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).bgra;
278                                 case 2:         //rgba,
279                                         return get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).rgba;
280                                 case 3:         //argb,
281                                         return get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).argb;
282                                 case 4:         //abgr,
283                                         return get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).gbar;
284                                 case 5:         //ycbcr,
285                                         {
286                                                 float y  = get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
287                                                 float cb = get_sample(plane[1], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
288                                                 float cr = get_sample(plane[2], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
289                                                 return ycbcra_to_rgba(y, cb, cr, 1.0);
290                                         }
291                                 case 6:         //ycbcra
292                                         {
293                                                 float y  = get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
294                                                 float cb = get_sample(plane[1], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
295                                                 float cr = get_sample(plane[2], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
296                                                 float a  = get_sample(plane[3], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
297                                                 return ycbcra_to_rgba(y, cb, cr, a);
298                                         }
299                                 case 7:         //luma
300                                         {
301                                                 vec3 y3 = get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).rrr;
302                                                 return vec4((y3-0.065)/0.859, 1.0);
303                                         }
304                                 case 8:         //bgr,
305                                         return vec4(get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).bgr, 1.0);
306                                 case 9:         //rgb,
307                                         return vec4(get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).rgb, 1.0);
308                                 }
309                                 return vec4(0.0, 0.0, 0.0, 0.0);
310                         }
311
312                         vec4 post_process()
313                         {
314                                 vec4 color = texture2D(background, gl_TexCoord[0].st).bgra;
315
316                                 if (straighten_alpha)
317                                         color.rgb /= color.a + 0.0000001;
318
319                                 return color;
320                         }
321
322                         void main()
323                         {
324         )shader"
325
326         +
327
328         (post_processing ? get_post_process() : get_no_post_process())
329
330         +
331
332         R"shader(
333                                 {
334                                         vec4 color = get_rgba_color();
335                                         if (chroma)
336                                                 color = chroma_key(color);
337                                         if(levels)
338                                                 color.rgb = LevelsControl(color.rgb, min_input, gamma, max_input, min_output, max_output);
339                                         if(csb)
340                                                 color.rgb = ContrastSaturationBrightness(color, brt, sat, con);
341                                         if(has_local_key)
342                                                 color *= texture2D(local_key, gl_TexCoord[1].st).r;
343                                         if(has_layer_key)
344                                                 color *= texture2D(layer_key, gl_TexCoord[1].st).r;
345                                         color *= opacity;
346                                         color = blend(color);
347                                         gl_FragColor = color.bgra;
348                                 }
349                         }
350         )shader";
351 }
352
353 std::shared_ptr<shader> get_image_shader(
354                 const spl::shared_ptr<device>& ogl,
355                 bool& blend_modes,
356                 bool blend_modes_wanted,
357                 bool& post_processing,
358                 bool straight_alpha_wanted)
359 {
360         tbb::mutex::scoped_lock lock(g_shader_mutex);
361         auto existing_shader = g_shader.lock();
362
363         if(existing_shader)
364         {
365                 blend_modes = g_blend_modes;
366                 post_processing = g_post_processing;
367                 return existing_shader;
368         }
369
370         // The deleter is alive until the weak pointer is destroyed, so we have
371         // to weakly reference ogl, to not keep it alive until atexit
372         std::weak_ptr<device> weak_ogl = ogl;
373
374         auto deleter = [weak_ogl](shader* p)
375         {
376                 auto ogl = weak_ogl.lock();
377
378                 if (ogl)
379                         ogl->invoke([=]
380                         {
381                                 delete p;
382                         });
383         };
384
385         try
386         {
387                 g_blend_modes  = glTextureBarrierNV ? blend_modes_wanted : false;
388                 g_post_processing = straight_alpha_wanted;
389                 existing_shader.reset(new shader(get_vertex(), get_fragment(g_blend_modes, g_post_processing)), deleter);
390         }
391         catch(...)
392         {
393                 CASPAR_LOG_CURRENT_EXCEPTION();
394                 CASPAR_LOG(warning) << "Failed to compile shader. Trying to compile without blend-modes.";
395
396                 g_blend_modes = false;
397                 existing_shader.reset(new shader(get_vertex(), get_fragment(g_blend_modes, g_post_processing)), deleter);
398         }
399
400         //if(!g_blend_modes)
401         //{
402         //      ogl.blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
403         //      CASPAR_LOG(info) << L"[shader] Blend-modes are disabled.";
404         //}
405
406
407         blend_modes = g_blend_modes;
408         post_processing = g_post_processing;
409         g_shader = existing_shader;
410         return existing_shader;
411 }
412
413 }}}