2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>
\r
4 * This file is part of CasparCG.
\r
6 * CasparCG is free software: you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation, either version 3 of the License, or
\r
9 * (at your option) any later version.
\r
11 * CasparCG is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with CasparCG. If not, see <http://www.gnu.org/licenses/>.
\r
20 #include "../../stdafx.h"
\r
22 #include "image_kernel.h"
\r
23 #include "blending_glsl.h"
\r
25 #include <common/exception/exceptions.h>
\r
26 #include <common/gl/gl_check.h>
\r
28 #include <core/video_format.h>
\r
29 #include <core/producer/frame/pixel_format.h>
\r
30 #include <core/producer/frame/image_transform.h>
\r
32 #include <boost/noncopyable.hpp>
\r
34 #include <unordered_map>
\r
36 namespace caspar { namespace core {
\r
38 class shader_program : boost::noncopyable
\r
43 shader_program() : program_(0) {}
\r
44 shader_program(shader_program&& other) : program_(other.program_){other.program_ = 0;}
\r
45 shader_program(const std::string& vertex_source_str, const std::string& fragment_source_str) : program_(0)
\r
49 const char* vertex_source = vertex_source_str.c_str();
\r
51 auto vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
\r
53 GL(glShaderSourceARB(vertex_shader, 1, &vertex_source, NULL));
\r
54 GL(glCompileShaderARB(vertex_shader));
\r
56 GL(glGetObjectParameterivARB(vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success));
\r
57 if (success == GL_FALSE)
\r
60 GL(glGetInfoLogARB(vertex_shader, sizeof(info), 0, info));
\r
61 GL(glDeleteObjectARB(vertex_shader));
\r
62 std::stringstream str;
\r
63 str << "Failed to compile vertex shader:" << std::endl << info << std::endl;
\r
64 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(str.str()));
\r
67 const char* fragment_source = fragment_source_str.c_str();
\r
69 auto fragmemt_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
\r
71 GL(glShaderSourceARB(fragmemt_shader, 1, &fragment_source, NULL));
\r
72 GL(glCompileShaderARB(fragmemt_shader));
\r
74 GL(glGetObjectParameterivARB(fragmemt_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success));
\r
75 if (success == GL_FALSE)
\r
78 GL(glGetInfoLogARB(fragmemt_shader, sizeof(info), 0, info));
\r
79 GL(glDeleteObjectARB(fragmemt_shader));
\r
80 std::stringstream str;
\r
81 str << "Failed to compile fragment shader:" << std::endl << info << std::endl;
\r
82 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(str.str()));
\r
85 program_ = glCreateProgramObjectARB();
\r
87 GL(glAttachObjectARB(program_, vertex_shader));
\r
88 GL(glAttachObjectARB(program_, fragmemt_shader));
\r
90 GL(glLinkProgramARB(program_));
\r
92 GL(glDeleteObjectARB(vertex_shader));
\r
93 GL(glDeleteObjectARB(fragmemt_shader));
\r
95 GL(glGetObjectParameterivARB(program_, GL_OBJECT_LINK_STATUS_ARB, &success));
\r
96 if (success == GL_FALSE)
\r
99 GL(glGetInfoLogARB(program_, sizeof(info), 0, info));
\r
100 GL(glDeleteObjectARB(program_));
\r
101 std::stringstream str;
\r
102 str << "Failed to link shader program:" << std::endl << info << std::endl;
\r
103 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(str.str()));
\r
105 GL(glUseProgramObjectARB(program_));
\r
106 glUniform1i(glGetUniformLocation(program_, "plane[0]"), 0);
\r
107 glUniform1i(glGetUniformLocation(program_, "plane[1]"), 1);
\r
108 glUniform1i(glGetUniformLocation(program_, "plane[2]"), 2);
\r
109 glUniform1i(glGetUniformLocation(program_, "plane[3]"), 3);
\r
110 glUniform1i(glGetUniformLocation(program_, "plane[4]"), 4);
\r
111 glUniform1i(glGetUniformLocation(program_, "plane[5]"), 5);
\r
114 GLint get_location(const char* name)
\r
116 GLint loc = glGetUniformLocation(program_, name);
\r
120 shader_program& operator=(shader_program&& other)
\r
122 program_ = other.program_;
\r
123 other.program_ = 0;
\r
129 glDeleteProgram(program_);
\r
134 GL(glUseProgramObjectARB(program_));
\r
138 GLubyte progressive_pattern[] = {
\r
139 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
\r
140 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
\r
141 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
\r
142 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xFF, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
\r
144 GLubyte upper_pattern[] = {
\r
145 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
\r
146 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
\r
147 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
\r
148 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00};
\r
150 GLubyte lower_pattern[] = {
\r
151 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
\r
152 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
\r
153 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
\r
154 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff};
\r
156 struct image_kernel::implementation : boost::noncopyable
\r
158 std::unordered_map<core::pixel_format::type, shader_program> shaders_;
\r
161 std::unordered_map<core::pixel_format::type, shader_program>& shaders()
\r
163 GL(glEnable(GL_POLYGON_STIPPLE));
\r
165 if(shaders_.empty())
\r
167 std::string common_vertex =
\r
170 " gl_TexCoord[0] = gl_MultiTexCoord0; "
\r
171 " gl_TexCoord[1] = gl_MultiTexCoord1; "
\r
172 " gl_FrontColor = gl_Color; "
\r
173 " gl_Position = ftransform(); "
\r
176 std::string common_fragment = std::string() +
\r
177 "uniform sampler2D plane[6]; "
\r
178 "uniform float gain; "
\r
179 "uniform bool HD; "
\r
180 "uniform bool local_key; "
\r
181 "uniform bool layer_key; "
\r
182 "uniform int blend_mode; "
\r
190 "\nvec3 get_blend_color(vec3 back, vec3 fore) "
\r
192 "\n switch(blend_mode) "
\r
194 "\n case 0: return BlendNormal(back, fore); "
\r
195 "\n case 1: return BlendLighten(back, fore); "
\r
196 "\n case 2: return BlendDarken(back, fore); "
\r
197 "\n case 3: return BlendMultiply(back, fore); "
\r
198 "\n case 4: return BlendAverage(back, fore); "
\r
199 "\n case 5: return BlendAdd(back, fore); "
\r
200 "\n case 6: return BlendSubstract(back, fore); "
\r
201 "\n case 7: return BlendDifference(back, fore); "
\r
202 "\n case 8: return BlendNegation(back, fore); "
\r
203 "\n case 9: return BlendExclusion(back, fore); "
\r
204 "\n case 10: return BlendScreen(back, fore); "
\r
205 "\n case 11: return BlendOverlay(back, fore); "
\r
206 //"\n case 12: return BlendSoftLight(back, fore); "
\r
207 //"\n case 13: return BlendHardLight(back, fore); "
\r
208 "\n case 14: return BlendColorDodge(back, fore); "
\r
209 "\n case 15: return BlendColorBurn(back, fore); "
\r
210 "\n case 16: return BlendLinearDodge(back, fore); "
\r
211 "\n case 17: return BlendLinearBurn(back, fore); "
\r
212 "\n case 18: return BlendLinearLight(back, fore); "
\r
213 "\n case 19: return BlendVividLight(back, fore); "
\r
214 "\n case 20: return BlendPinLight(back, fore); "
\r
215 "\n case 21: return BlendHardMix(back, fore); "
\r
216 "\n case 22: return BlendReflect(back, fore); "
\r
217 "\n case 23: return BlendGlow(back, fore); "
\r
218 "\n case 24: return BlendPhoenix(back, fore); "
\r
219 "\n case 25: return BlendHue(back, fore); "
\r
220 "\n case 26: return BlendSaturation(back, fore); "
\r
221 "\n case 27: return BlendColor(back, fore); "
\r
222 "\n case 28: return BlendLuminosity(back, fore); "
\r
225 "\n return BlendNormal(back, fore); "
\r
228 "vec4 get_color(vec4 fore) "
\r
230 " vec4 back = texture2D(plane[5], gl_TexCoord[1].st); "
\r
231 " fore.rgb = get_blend_color(back.rgb, fore.rgb);"
\r
232 " return vec4(fore.rgb * fore.a + back.rgb * (1.0-fore.a), back.a + fore.a);"
\r
235 // NOTE: YCbCr, ITU-R, http://www.intersil.com/data/an/an9717.pdf
\r
236 // TODO: Support for more yuv formats might be needed.
\r
237 "vec4 ycbcra_to_bgra_sd(float y, float cb, float cr, float a) "
\r
241 " y = 1.164*(y-0.0625); "
\r
244 " color.r = y + 1.596 * cr; "
\r
245 " color.g = y - 0.813 * cr - 0.391 * cb; "
\r
246 " color.b = y + 2.018 * cb; "
\r
253 "vec4 ycbcra_to_bgra_hd(float y, float cb, float cr, float a) "
\r
257 " y = 1.164*(y-0.0625); "
\r
260 " color.r = y + 1.793 * cr; "
\r
261 " color.g = y - 0.534 * cr - 0.213 * cb; "
\r
262 " color.b = y + 2.115 * cb; "
\r
269 shaders_[core::pixel_format::gray] = shader_program(common_vertex, common_fragment +
\r
273 " vec4 rgba = vec4(texture2D(plane[0], gl_TexCoord[0].st).rrr, 1.0); "
\r
275 " rgba.a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
277 " rgba.a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
278 " gl_FragColor = get_color(rgba * gain); "
\r
281 shaders_[core::pixel_format::abgr] = shader_program(common_vertex, common_fragment +
\r
285 " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st); "
\r
287 " abgr.b *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
289 " abgr.b *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
290 " gl_FragColor = get_color(abgr.argb * gain); "
\r
293 shaders_[core::pixel_format::argb]= shader_program(common_vertex, common_fragment +
\r
297 " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st); "
\r
299 " argb.b *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
301 " argb.b *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
302 " gl_FragColor = get_color(argb.grab * gl_Color * gain); "
\r
305 shaders_[core::pixel_format::bgra]= shader_program(common_vertex, common_fragment +
\r
309 " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st); "
\r
311 " bgra.a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
313 " bgra.a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
314 " gl_FragColor = get_color(bgra.rgba * gl_Color * gain); "
\r
317 shaders_[core::pixel_format::rgba] = shader_program(common_vertex, common_fragment +
\r
321 " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); "
\r
323 " rgba.a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
325 " rgba.a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
326 " gl_FragColor = get_color(rgba.bgra * gl_Color * gain); "
\r
329 shaders_[core::pixel_format::ycbcr] = shader_program(common_vertex, common_fragment +
\r
333 " float y = texture2D(plane[0], gl_TexCoord[0].st).r; "
\r
334 " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; "
\r
335 " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; "
\r
336 " float a = 1.0; "
\r
338 " a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
340 " a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
342 " gl_FragColor = get_color(ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain);"
\r
344 " gl_FragColor = get_color(ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain);"
\r
347 shaders_[core::pixel_format::ycbcra] = shader_program(common_vertex, common_fragment +
\r
351 " float y = texture2D(plane[0], gl_TexCoord[0].st).r; "
\r
352 " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; "
\r
353 " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; "
\r
354 " float a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
356 " a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
358 " a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
360 " gl_FragColor = get_color(ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain);"
\r
362 " gl_FragColor = get_color(ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain);"
\r
369 image_kernel::image_kernel() : impl_(new implementation()){}
\r
371 void image_kernel::draw(size_t width, size_t height, const core::pixel_format_desc& pix_desc, const core::image_transform& transform, bool local_key, bool layer_key)
\r
373 GL(glEnable(GL_TEXTURE_2D));
\r
374 GL(glDisable(GL_DEPTH_TEST));
\r
376 if(transform.get_mode() == core::video_mode::upper)
\r
377 glPolygonStipple(upper_pattern);
\r
378 else if(transform.get_mode() == core::video_mode::lower)
\r
379 glPolygonStipple(lower_pattern);
\r
381 glPolygonStipple(progressive_pattern);
\r
383 impl_->shaders()[pix_desc.pix_fmt].use();
\r
385 GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast<GLfloat>(transform.get_gain())));
\r
386 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0));
\r
387 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("local_key"), local_key ? 1 : 0));
\r
388 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("layer_key"), layer_key ? 1 : 0));
\r
389 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("blend_mode"), transform.get_blend_mode()));
\r
391 GL(glColor4d(1.0, 1.0, 1.0, transform.get_opacity()));
\r
392 GL(glViewport(0, 0, width, height));
\r
394 auto m_p = transform.get_clip_translation();
\r
395 auto m_s = transform.get_clip_scale();
\r
396 double w = static_cast<double>(width);
\r
397 double h = static_cast<double>(height);
\r
399 GL(glEnable(GL_SCISSOR_TEST));
\r
400 GL(glScissor(static_cast<size_t>(m_p[0]*w), static_cast<size_t>(m_p[1]*h), static_cast<size_t>(m_s[0]*w), static_cast<size_t>(m_s[1]*h)));
\r
402 auto f_p = transform.get_fill_translation();
\r
403 auto f_s = transform.get_fill_scale();
\r
406 glMultiTexCoord2d(GL_TEXTURE0, 0.0, 0.0); glMultiTexCoord2d(GL_TEXTURE1, f_p[0] , f_p[1] ); glVertex2d( f_p[0] *2.0-1.0, f_p[1] *2.0-1.0);
\r
407 glMultiTexCoord2d(GL_TEXTURE0, 1.0, 0.0); glMultiTexCoord2d(GL_TEXTURE1, (f_p[0]+f_s[0]), f_p[1] ); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, f_p[1] *2.0-1.0);
\r
408 glMultiTexCoord2d(GL_TEXTURE0, 1.0, 1.0); glMultiTexCoord2d(GL_TEXTURE1, (f_p[0]+f_s[0]), (f_p[1]+f_s[1])); glVertex2d((f_p[0]+f_s[0])*2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0);
\r
409 glMultiTexCoord2d(GL_TEXTURE0, 0.0, 1.0); glMultiTexCoord2d(GL_TEXTURE1, f_p[0] , (f_p[1]+f_s[1])); glVertex2d( f_p[0] *2.0-1.0, (f_p[1]+f_s[1])*2.0-1.0);
\r
411 GL(glDisable(GL_SCISSOR_TEST));
\r