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
33 #include <boost/noncopyable.hpp>
\r
35 #include <unordered_map>
\r
37 namespace caspar { namespace mixer {
\r
39 class shader_program : boost::noncopyable
\r
44 shader_program() : program_(0) {}
\r
45 shader_program(shader_program&& other) : program_(other.program_){other.program_ = 0;}
\r
46 shader_program(const std::string& vertex_source_str, const std::string& fragment_source_str) : program_(0)
\r
50 const char* vertex_source = vertex_source_str.c_str();
\r
52 auto vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
\r
54 GL(glShaderSourceARB(vertex_shader, 1, &vertex_source, NULL));
\r
55 GL(glCompileShaderARB(vertex_shader));
\r
57 GL(glGetObjectParameterivARB(vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success));
\r
58 if (success == GL_FALSE)
\r
61 GL(glGetInfoLogARB(vertex_shader, sizeof(info), 0, info));
\r
62 GL(glDeleteObjectARB(vertex_shader));
\r
63 std::stringstream str;
\r
64 str << "Failed to compile vertex shader:" << std::endl << info << std::endl;
\r
65 BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str()));
\r
68 const char* fragment_source = fragment_source_str.c_str();
\r
70 auto fragmemt_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
\r
72 GL(glShaderSourceARB(fragmemt_shader, 1, &fragment_source, NULL));
\r
73 GL(glCompileShaderARB(fragmemt_shader));
\r
75 GL(glGetObjectParameterivARB(fragmemt_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success));
\r
76 if (success == GL_FALSE)
\r
79 GL(glGetInfoLogARB(fragmemt_shader, sizeof(info), 0, info));
\r
80 GL(glDeleteObjectARB(fragmemt_shader));
\r
81 std::stringstream str;
\r
82 str << "Failed to compile fragment shader:" << std::endl << info << std::endl;
\r
83 BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str()));
\r
86 program_ = glCreateProgramObjectARB();
\r
88 GL(glAttachObjectARB(program_, vertex_shader));
\r
89 GL(glAttachObjectARB(program_, fragmemt_shader));
\r
91 GL(glLinkProgramARB(program_));
\r
93 GL(glDeleteObjectARB(vertex_shader));
\r
94 GL(glDeleteObjectARB(fragmemt_shader));
\r
96 GL(glGetObjectParameterivARB(program_, GL_OBJECT_LINK_STATUS_ARB, &success));
\r
97 if (success == GL_FALSE)
\r
100 GL(glGetInfoLogARB(program_, sizeof(info), 0, info));
\r
101 GL(glDeleteObjectARB(program_));
\r
102 std::stringstream str;
\r
103 str << "Failed to link shader program:" << std::endl << info << std::endl;
\r
104 BOOST_THROW_EXCEPTION(gl::gl_error() << msg_info(str.str()));
\r
106 GL(glUseProgramObjectARB(program_));
\r
107 glUniform1i(glGetUniformLocation(program_, "plane[0]"), 0);
\r
108 glUniform1i(glGetUniformLocation(program_, "plane[1]"), 1);
\r
109 glUniform1i(glGetUniformLocation(program_, "plane[2]"), 2);
\r
110 glUniform1i(glGetUniformLocation(program_, "plane[3]"), 3);
\r
113 GLint get_location(const char* name)
\r
115 GLint loc = glGetUniformLocation(program_, name);
\r
119 shader_program& operator=(shader_program&& other)
\r
121 program_ = other.program_;
\r
122 other.program_ = 0;
\r
128 glDeleteProgram(program_);
\r
133 GL(glUseProgramObjectARB(program_));
\r
137 GLubyte progressive_pattern[] = {
\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
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
143 GLubyte upper_pattern[] = {
\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
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
149 GLubyte lower_pattern[] = {
\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
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
155 struct image_kernel::implementation : boost::noncopyable
\r
157 std::unordered_map<core::pixel_format::type, shader_program> shaders_;
\r
160 std::unordered_map<core::pixel_format::type, shader_program>& shaders()
\r
162 GL(glEnable(GL_POLYGON_STIPPLE));
\r
163 GL(glEnable(GL_BLEND));
\r
164 GL(glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE));
\r
166 if(shaders_.empty())
\r
168 std::string common_vertex =
\r
171 " gl_TexCoord[0] = gl_MultiTexCoord0; "
\r
172 " gl_FrontColor = gl_Color; "
\r
173 " gl_Position = ftransform(); "
\r
176 std::string common_fragment =
\r
177 "uniform sampler2D plane[4]; "
\r
178 "uniform float gain; "
\r
179 "uniform bool HD; "
\r
180 "uniform bool has_separate_key; "
\r
182 // NOTE: YCbCr, ITU-R, http://www.intersil.com/data/an/an9717.pdf
\r
183 // TODO: Support for more yuv formats might be needed.
\r
184 "vec4 ycbcra_to_bgra_sd(float y, float cb, float cr, float a) "
\r
188 " y = 1.164*(y-0.0625); "
\r
191 " color.r = y + 1.596 * cr; "
\r
192 " color.g = y - 0.813 * cr - 0.391 * cb; "
\r
193 " color.b = y + 2.018 * cb; "
\r
200 "vec4 ycbcra_to_bgra_hd(float y, float cb, float cr, float a) "
\r
204 " y = 1.164*(y-0.0625); "
\r
207 " color.r = y + 1.793 * cr; "
\r
208 " color.g = y - 0.534 * cr - 0.213 * cb; "
\r
209 " color.b = y + 2.115 * cb; "
\r
216 shaders_[core::pixel_format::gray] = shader_program(common_vertex, common_fragment +
\r
220 " vec4 rgba = vec4(texture2D(plane[0], gl_TexCoord[0].st).rrr, 1.0); "
\r
221 " if(has_separate_key) "
\r
222 " rgba.a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
223 " gl_FragColor = rgba * gain; "
\r
226 shaders_[core::pixel_format::abgr] = shader_program(common_vertex, common_fragment +
\r
230 " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st); "
\r
231 " if(has_separate_key) "
\r
232 " abgr.b = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
233 " gl_FragColor = abgr.argb * gain; "
\r
236 shaders_[core::pixel_format::argb]= shader_program(common_vertex, common_fragment +
\r
240 " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st); "
\r
241 " if(has_separate_key) "
\r
242 " argb.b = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
243 " gl_FragColor = argb.grab * gl_Color * gain; "
\r
246 shaders_[core::pixel_format::bgra]= shader_program(common_vertex, common_fragment +
\r
250 " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st); "
\r
251 " if(has_separate_key) "
\r
252 " bgra.a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
253 " gl_FragColor = bgra.rgba * gl_Color * gain; "
\r
256 shaders_[core::pixel_format::rgba] = shader_program(common_vertex, common_fragment +
\r
260 " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); "
\r
261 " if(has_separate_key) "
\r
262 " rgba.a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
263 " gl_FragColor = rgba.bgra * gl_Color * gain; "
\r
266 shaders_[core::pixel_format::ycbcr] = shader_program(common_vertex, common_fragment +
\r
270 " float y = texture2D(plane[0], gl_TexCoord[0].st).r; "
\r
271 " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; "
\r
272 " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; "
\r
273 " float a = 1.0; "
\r
274 " if(has_separate_key) "
\r
275 " a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
277 " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"
\r
279 " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"
\r
282 shaders_[core::pixel_format::ycbcra] = shader_program(common_vertex, common_fragment +
\r
286 " float y = texture2D(plane[0], gl_TexCoord[0].st).r; "
\r
287 " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; "
\r
288 " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; "
\r
289 " float a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
291 " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"
\r
293 " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"
\r
300 image_kernel::image_kernel() : impl_(new implementation()){}
\r
302 void image_kernel::apply(const core::pixel_format_desc& pix_desc, const core::image_transform& transform, bool has_separate_key)
\r
304 impl_->shaders()[pix_desc.pix_fmt].use();
\r
306 GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast<GLfloat>(transform.get_gain())));
\r
307 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0));
\r
308 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("has_separate_key"), has_separate_key ? 1 : 0));
\r
310 if(transform.get_mode() == core::video_mode::upper)
\r
311 glPolygonStipple(upper_pattern);
\r
312 else if(transform.get_mode() == core::video_mode::lower)
\r
313 glPolygonStipple(lower_pattern);
\r
315 glPolygonStipple(progressive_pattern);
\r