]> git.sesse.net Git - casparcg/blob - accelerator/ogl/image/image_shader.cpp
* Merged straight alpha support to 2.1.0
[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                                         switch (chroma_mode)
135                                         {
136                                         case 0: return c;
137                                         case 1: return ChromaOnGreen(c.bgra).bgra;
138                                         case 2: return ChromaOnBlue(c.bgra).bgra;
139                                         }
140
141                                         return c;
142                                 }
143                 )shader";
144 }
145
146 std::string get_post_process()
147 {
148         return R"shader(
149                 if (post_processing)
150                 {
151                         gl_FragColor = post_process().bgra;
152                 }
153                 else
154         )shader";
155 }
156
157 std::string get_no_post_process()
158 {
159         return "";
160 }
161
162 std::string get_vertex()
163 {
164         return R"shader(
165                         void main()
166                         {
167                                 gl_TexCoord[0] = gl_MultiTexCoord0;
168                         //      gl_TexCoord[1] = gl_MultiTexCoord1;
169                                 vec4 pos = ftransform();
170                                 gl_TexCoord[1] = vec4(pos.xy, 0.0, 0.0);
171                                 pos.x = pos.x*2.0 - 1.0;
172                                 pos.y = pos.y*2.0 - 1.0;
173                                 gl_Position    = pos;
174                         }
175         )shader";
176 }
177
178 std::string get_fragment(bool blend_modes, bool post_processing)
179 {
180         return R"shader(
181
182                         #version 130
183                         uniform sampler2D       background;
184                         uniform sampler2D       plane[4];
185                         uniform vec2            plane_size[4];
186                         uniform sampler2D       local_key;
187                         uniform sampler2D       layer_key;
188
189                         uniform bool            is_hd;
190                         uniform bool            has_local_key;
191                         uniform bool            has_layer_key;
192                         uniform int                     blend_mode;
193                         uniform int                     keyer;
194                         uniform int                     pixel_format;
195                         uniform int                     deinterlace;
196
197                         uniform float           opacity;
198                         uniform bool            levels;
199                         uniform float           min_input;
200                         uniform float           max_input;
201                         uniform float           gamma;
202                         uniform float           min_output;
203                         uniform float           max_output;
204
205                         uniform bool            csb;
206                         uniform float           brt;
207                         uniform float           sat;
208                         uniform float           con;
209
210                         uniform bool            post_processing;
211                         uniform bool            straighten_alpha;
212
213                         uniform bool            chroma;
214                         uniform int                     chroma_mode;
215                         uniform vec2            chroma_blend;
216                         uniform float           chroma_spill;
217         )shader"
218
219         +
220
221         (blend_modes ? get_blend_color_func() : get_simple_blend_color_func())
222
223         +
224
225         get_chroma_func()
226
227         +
228
229         R"shader(
230
231                         vec4 ycbcra_to_rgba_sd(float Y, float Cb, float Cr, float A)
232                         {
233                                 vec4 rgba;
234                                 rgba.b = (1.164*(Y*255 - 16) + 1.596*(Cr*255 - 128))/255;
235                                 rgba.g = (1.164*(Y*255 - 16) - 0.813*(Cr*255 - 128) - 0.391*(Cb*255 - 128))/255;
236                                 rgba.r = (1.164*(Y*255 - 16) + 2.018*(Cb*255 - 128))/255;
237                                 rgba.a = A;
238                                 return rgba;
239                         }
240
241                         vec4 ycbcra_to_rgba_hd(float Y, float Cb, float Cr, float A)
242                         {
243                                 vec4 rgba;
244                                 rgba.b = (1.164*(Y*255 - 16) + 1.793*(Cr*255 - 128))/255;
245                                 rgba.g = (1.164*(Y*255 - 16) - 0.534*(Cr*255 - 128) - 0.213*(Cb*255 - 128))/255;
246                                 rgba.r = (1.164*(Y*255 - 16) + 2.115*(Cb*255 - 128))/255;
247                                 rgba.a = A;
248                                 return rgba;
249                         }
250
251                         vec4 ycbcra_to_rgba(float y, float cb, float cr, float a)
252                         {
253                                 if(is_hd)
254                                         return ycbcra_to_rgba_hd(y, cb, cr, a);
255                                 else
256                                         return ycbcra_to_rgba_sd(y, cb, cr, a);
257                         }
258
259                         vec4 get_sample(sampler2D sampler, vec2 coords, vec2 size)
260                         {
261                                 switch(deinterlace)
262                                 {
263                                 case 1: // upper
264                                         return texture2D(sampler, coords);
265                                 case 2: // lower
266                                         return texture2D(sampler, coords);
267                                 default:
268                                         return texture2D(sampler, coords);
269                                 }
270                         }
271
272                         vec4 get_rgba_color()
273                         {
274                                 switch(pixel_format)
275                                 {
276                                 case 0:         //gray
277                                         return vec4(get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).rrr, 1.0);
278                                 case 1:         //bgra,
279                                         return get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).bgra;
280                                 case 2:         //rgba,
281                                         return get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).rgba;
282                                 case 3:         //argb,
283                                         return get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).argb;
284                                 case 4:         //abgr,
285                                         return get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).gbar;
286                                 case 5:         //ycbcr,
287                                         {
288                                                 float y  = get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
289                                                 float cb = get_sample(plane[1], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
290                                                 float cr = get_sample(plane[2], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
291                                                 return ycbcra_to_rgba(y, cb, cr, 1.0);
292                                         }
293                                 case 6:         //ycbcra
294                                         {
295                                                 float y  = get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
296                                                 float cb = get_sample(plane[1], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
297                                                 float cr = get_sample(plane[2], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
298                                                 float a  = get_sample(plane[3], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).r;
299                                                 return ycbcra_to_rgba(y, cb, cr, a);
300                                         }
301                                 case 7:         //luma
302                                         {
303                                                 vec3 y3 = get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).rrr;
304                                                 return vec4((y3-0.065)/0.859, 1.0);
305                                         }
306                                 case 8:         //bgr,
307                                         return vec4(get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).bgr, 1.0);
308                                 case 9:         //rgb,
309                                         return vec4(get_sample(plane[0], gl_TexCoord[0].st / gl_TexCoord[0].q, plane_size[0]).rgb, 1.0);
310                                 }
311                                 return vec4(0.0, 0.0, 0.0, 0.0);
312                         }
313
314                         vec4 post_process()
315                         {
316                                 vec4 color = texture2D(background, gl_TexCoord[0].st).bgra;
317
318                                 if (straighten_alpha)
319                                         color.rgb /= color.a + 0.0000001;
320
321                                 return color;
322                         }       
323
324                         void main()
325                         {
326         )shader"
327
328         +
329
330         (post_processing ? get_post_process() : get_no_post_process())
331
332         +
333
334         R"shader(
335                                 {
336                                         vec4 color = get_rgba_color();
337                                         if (chroma)
338                                                 color = chroma_key(color);
339                                         if(levels)
340                                                 color.rgb = LevelsControl(color.rgb, min_input, gamma, max_input, min_output, max_output);
341                                         if(csb)
342                                                 color.rgb = ContrastSaturationBrightness(color.rgb, brt, sat, con);
343                                         if(has_local_key)
344                                                 color *= texture2D(local_key, gl_TexCoord[1].st).r;
345                                         if(has_layer_key)
346                                                 color *= texture2D(layer_key, gl_TexCoord[1].st).r;
347                                         color *= opacity;
348                                         color = blend(color);
349                                         gl_FragColor = color.bgra;
350                                 }
351                         }
352         )shader";
353 }
354
355 std::shared_ptr<shader> get_image_shader(
356                 const spl::shared_ptr<device>& ogl,
357                 bool& blend_modes,
358                 bool blend_modes_wanted,
359                 bool& post_processing,
360                 bool straight_alpha_wanted)
361 {
362         tbb::mutex::scoped_lock lock(g_shader_mutex);
363         auto existing_shader = g_shader.lock();
364
365         if(existing_shader)
366         {
367                 blend_modes = g_blend_modes;
368                 post_processing = g_post_processing;
369                 return existing_shader;
370         }
371
372         // The deleter is alive until the weak pointer is destroyed, so we have
373         // to weakly reference ogl, to not keep it alive until atexit
374         std::weak_ptr<device> weak_ogl = ogl;
375
376         auto deleter = [weak_ogl](shader* p)
377         {
378                 auto ogl = weak_ogl.lock();
379
380                 if (ogl)
381                         ogl->invoke([=]
382                         {
383                                 delete p;
384                         });
385         };
386                 
387         try
388         {                               
389                 g_blend_modes  = glTextureBarrierNV ? blend_modes_wanted : false;
390                 g_post_processing = straight_alpha_wanted;
391                 existing_shader.reset(new shader(get_vertex(), get_fragment(g_blend_modes, g_post_processing)), deleter);
392         }
393         catch(...)
394         {
395                 CASPAR_LOG_CURRENT_EXCEPTION();
396                 CASPAR_LOG(warning) << "Failed to compile shader. Trying to compile without blend-modes.";
397                                 
398                 g_blend_modes = false;
399                 existing_shader.reset(new shader(get_vertex(), get_fragment(g_blend_modes, g_post_processing)), deleter);
400         }
401
402         //if(!g_blend_modes)
403         //{
404         //      ogl.blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
405         //      CASPAR_LOG(info) << L"[shader] Blend-modes are disabled.";
406         //}
407
408
409         blend_modes = g_blend_modes;
410         post_processing = g_post_processing;
411         g_shader = existing_shader;
412         return existing_shader;
413 }
414
415 }}}