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
24 #include <common/exception/exceptions.h>
\r
25 #include <common/gl/gl_check.h>
\r
27 #include <core/video_format.h>
\r
28 #include <core/producer/frame/pixel_format.h>
\r
29 #include <core/producer/frame/image_transform.h>
\r
31 #include <boost/noncopyable.hpp>
\r
33 #include <unordered_map>
\r
35 namespace caspar { namespace core {
\r
37 class shader_program : boost::noncopyable
\r
42 shader_program() : program_(0) {}
\r
43 shader_program(shader_program&& other) : program_(other.program_){other.program_ = 0;}
\r
44 shader_program(const std::string& vertex_source_str, const std::string& fragment_source_str) : program_(0)
\r
48 const char* vertex_source = vertex_source_str.c_str();
\r
50 auto vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
\r
52 GL(glShaderSourceARB(vertex_shader, 1, &vertex_source, NULL));
\r
53 GL(glCompileShaderARB(vertex_shader));
\r
55 GL(glGetObjectParameterivARB(vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success));
\r
56 if (success == GL_FALSE)
\r
59 GL(glGetInfoLogARB(vertex_shader, sizeof(info), 0, info));
\r
60 GL(glDeleteObjectARB(vertex_shader));
\r
61 std::stringstream str;
\r
62 str << "Failed to compile vertex shader:" << std::endl << info << std::endl;
\r
63 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(str.str()));
\r
66 const char* fragment_source = fragment_source_str.c_str();
\r
68 auto fragmemt_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
\r
70 GL(glShaderSourceARB(fragmemt_shader, 1, &fragment_source, NULL));
\r
71 GL(glCompileShaderARB(fragmemt_shader));
\r
73 GL(glGetObjectParameterivARB(fragmemt_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success));
\r
74 if (success == GL_FALSE)
\r
77 GL(glGetInfoLogARB(fragmemt_shader, sizeof(info), 0, info));
\r
78 GL(glDeleteObjectARB(fragmemt_shader));
\r
79 std::stringstream str;
\r
80 str << "Failed to compile fragment shader:" << std::endl << info << std::endl;
\r
81 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(str.str()));
\r
84 program_ = glCreateProgramObjectARB();
\r
86 GL(glAttachObjectARB(program_, vertex_shader));
\r
87 GL(glAttachObjectARB(program_, fragmemt_shader));
\r
89 GL(glLinkProgramARB(program_));
\r
91 GL(glDeleteObjectARB(vertex_shader));
\r
92 GL(glDeleteObjectARB(fragmemt_shader));
\r
94 GL(glGetObjectParameterivARB(program_, GL_OBJECT_LINK_STATUS_ARB, &success));
\r
95 if (success == GL_FALSE)
\r
98 GL(glGetInfoLogARB(program_, sizeof(info), 0, info));
\r
99 GL(glDeleteObjectARB(program_));
\r
100 std::stringstream str;
\r
101 str << "Failed to link shader program:" << std::endl << info << std::endl;
\r
102 BOOST_THROW_EXCEPTION(caspar_exception() << msg_info(str.str()));
\r
104 GL(glUseProgramObjectARB(program_));
\r
105 glUniform1i(glGetUniformLocation(program_, "plane[0]"), 0);
\r
106 glUniform1i(glGetUniformLocation(program_, "plane[1]"), 1);
\r
107 glUniform1i(glGetUniformLocation(program_, "plane[2]"), 2);
\r
108 glUniform1i(glGetUniformLocation(program_, "plane[3]"), 3);
\r
109 glUniform1i(glGetUniformLocation(program_, "plane[4]"), 4);
\r
112 GLint get_location(const char* name)
\r
114 GLint loc = glGetUniformLocation(program_, name);
\r
118 shader_program& operator=(shader_program&& other)
\r
120 program_ = other.program_;
\r
121 other.program_ = 0;
\r
127 glDeleteProgram(program_);
\r
132 GL(glUseProgramObjectARB(program_));
\r
136 GLubyte progressive_pattern[] = {
\r
137 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
138 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
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
142 GLubyte upper_pattern[] = {
\r
143 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
144 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
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
148 GLubyte lower_pattern[] = {
\r
149 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
150 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
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
154 struct image_kernel::implementation : boost::noncopyable
\r
156 std::unordered_map<core::pixel_format::type, shader_program> shaders_;
\r
159 std::unordered_map<core::pixel_format::type, shader_program>& shaders()
\r
161 GL(glEnable(GL_POLYGON_STIPPLE));
\r
162 GL(glEnable(GL_BLEND));
\r
163 GL(glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE));
\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 =
\r
177 "uniform sampler2D plane[5]; "
\r
178 "uniform float gain; "
\r
179 "uniform bool HD; "
\r
180 "uniform bool local_key; "
\r
181 "uniform bool layer_key; "
\r
183 // NOTE: YCbCr, ITU-R, http://www.intersil.com/data/an/an9717.pdf
\r
184 // TODO: Support for more yuv formats might be needed.
\r
185 "vec4 ycbcra_to_bgra_sd(float y, float cb, float cr, float a) "
\r
189 " y = 1.164*(y-0.0625); "
\r
192 " color.r = y + 1.596 * cr; "
\r
193 " color.g = y - 0.813 * cr - 0.391 * cb; "
\r
194 " color.b = y + 2.018 * cb; "
\r
201 "vec4 ycbcra_to_bgra_hd(float y, float cb, float cr, float a) "
\r
205 " y = 1.164*(y-0.0625); "
\r
208 " color.r = y + 1.793 * cr; "
\r
209 " color.g = y - 0.534 * cr - 0.213 * cb; "
\r
210 " color.b = y + 2.115 * cb; "
\r
217 shaders_[core::pixel_format::gray] = shader_program(common_vertex, common_fragment +
\r
221 " vec4 rgba = vec4(texture2D(plane[0], gl_TexCoord[0].st).rrr, 1.0); "
\r
223 " rgba.a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
225 " rgba.a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
226 " gl_FragColor = rgba * gain; "
\r
229 shaders_[core::pixel_format::abgr] = shader_program(common_vertex, common_fragment +
\r
233 " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st); "
\r
235 " abgr.b *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
237 " abgr.b *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
238 " gl_FragColor = abgr.argb * gain; "
\r
241 shaders_[core::pixel_format::argb]= shader_program(common_vertex, common_fragment +
\r
245 " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st); "
\r
247 " argb.b *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
249 " argb.b *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
250 " gl_FragColor = argb.grab * gl_Color * gain; "
\r
253 shaders_[core::pixel_format::bgra]= shader_program(common_vertex, common_fragment +
\r
257 " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st); "
\r
259 " bgra.a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
261 " bgra.a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
262 " gl_FragColor = bgra.rgba * gl_Color * gain; "
\r
265 shaders_[core::pixel_format::rgba] = shader_program(common_vertex, common_fragment +
\r
269 " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); "
\r
271 " rgba.a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
273 " rgba.a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
274 " gl_FragColor = rgba.bgra * gl_Color * gain; "
\r
277 shaders_[core::pixel_format::ycbcr] = shader_program(common_vertex, common_fragment +
\r
281 " float y = texture2D(plane[0], gl_TexCoord[0].st).r; "
\r
282 " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; "
\r
283 " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; "
\r
284 " float a = 1.0; "
\r
286 " a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
288 " a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
290 " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"
\r
292 " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"
\r
295 shaders_[core::pixel_format::ycbcra] = shader_program(common_vertex, common_fragment +
\r
299 " float y = texture2D(plane[0], gl_TexCoord[0].st).r; "
\r
300 " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; "
\r
301 " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; "
\r
302 " float a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
304 " a *= texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
306 " a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
308 " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"
\r
310 " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"
\r
317 image_kernel::image_kernel() : impl_(new implementation()){}
\r
319 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
321 if(transform.get_opacity() < 0.001)
\r
324 GL(glEnable(GL_TEXTURE_2D));
\r
325 GL(glDisable(GL_DEPTH_TEST));
\r
327 impl_->shaders()[pix_desc.pix_fmt].use();
\r
329 GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast<GLfloat>(transform.get_gain())));
\r
330 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0));
\r
331 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("local_key"), local_key ? 1 : 0));
\r
332 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("layer_key"), layer_key ? 1 : 0));
\r
334 if(transform.get_mode() == core::video_mode::upper)
\r
335 glPolygonStipple(upper_pattern);
\r
336 else if(transform.get_mode() == core::video_mode::lower)
\r
337 glPolygonStipple(lower_pattern);
\r
339 glPolygonStipple(progressive_pattern);
\r
341 GL(glColor4d(1.0, 1.0, 1.0, transform.get_opacity()));
\r
342 GL(glViewport(0, 0, width, height));
\r
344 auto m_p = transform.get_clip_translation();
\r
345 auto m_s = transform.get_clip_scale();
\r
346 double w = static_cast<double>(width);
\r
347 double h = static_cast<double>(height);
\r
349 GL(glEnable(GL_SCISSOR_TEST));
\r
350 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
352 auto f_p = transform.get_fill_translation();
\r
353 auto f_s = transform.get_fill_scale();
\r
356 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
357 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
358 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
359 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
361 GL(glDisable(GL_SCISSOR_TEST));
\r