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
304 " a = texture2D(plane[3], gl_TexCoord[1].st).r; "
\r
306 " a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
308 " a *= texture2D(plane[4], gl_TexCoord[1].st).r; "
\r
310 " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"
\r
312 " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"
\r
319 image_kernel::image_kernel() : impl_(new implementation()){}
\r
321 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
323 if(transform.get_opacity() < 0.001)
\r
326 GL(glEnable(GL_TEXTURE_2D));
\r
327 GL(glDisable(GL_DEPTH_TEST));
\r
329 impl_->shaders()[pix_desc.pix_fmt].use();
\r
331 GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast<GLfloat>(transform.get_gain())));
\r
332 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0));
\r
333 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("local_key"), local_key ? 1 : 0));
\r
334 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("layer_key"), layer_key ? 1 : 0));
\r
336 if(transform.get_mode() == core::video_mode::upper)
\r
337 glPolygonStipple(upper_pattern);
\r
338 else if(transform.get_mode() == core::video_mode::lower)
\r
339 glPolygonStipple(lower_pattern);
\r
341 glPolygonStipple(progressive_pattern);
\r
343 GL(glColor4d(1.0, 1.0, 1.0, transform.get_opacity()));
\r
344 GL(glViewport(0, 0, width, height));
\r
346 auto m_p = transform.get_clip_translation();
\r
347 auto m_s = transform.get_clip_scale();
\r
348 double w = static_cast<double>(width);
\r
349 double h = static_cast<double>(height);
\r
351 GL(glEnable(GL_SCISSOR_TEST));
\r
352 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
354 auto f_p = transform.get_fill_translation();
\r
355 auto f_s = transform.get_fill_scale();
\r
358 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
359 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
360 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
361 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
363 GL(glDisable(GL_SCISSOR_TEST));
\r