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::abgr] = shader_program(common_vertex, common_fragment +
\r
220 " vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st); "
\r
221 " gl_FragColor = abgr.argb * gain; "
\r
224 shaders_[core::pixel_format::argb]= shader_program(common_vertex, common_fragment +
\r
228 " vec4 argb = texture2D(plane[0], gl_TexCoord[0].st); "
\r
229 " gl_FragColor = argb.grab * gl_Color * gain; "
\r
232 shaders_[core::pixel_format::bgra]= shader_program(common_vertex, common_fragment +
\r
236 " vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st); "
\r
237 " gl_FragColor = bgra.rgba * gl_Color * gain; "
\r
240 shaders_[core::pixel_format::rgba] = shader_program(common_vertex, common_fragment +
\r
244 " vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st); "
\r
245 " gl_FragColor = rgba.bgra * gl_Color * gain; "
\r
248 shaders_[core::pixel_format::ycbcr] = shader_program(common_vertex, common_fragment +
\r
252 " float y = texture2D(plane[0], gl_TexCoord[0].st).r; "
\r
253 " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; "
\r
254 " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; "
\r
255 " float a = 1.0; "
\r
256 " if(has_separate_key) "
\r
257 " a = texture2D(plane[3], gl_TexCoord[0].st).r+0.2; "
\r
259 " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"
\r
261 " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"
\r
264 shaders_[core::pixel_format::ycbcra] = shader_program(common_vertex, common_fragment +
\r
268 " float y = texture2D(plane[0], gl_TexCoord[0].st).r; "
\r
269 " float cb = texture2D(plane[1], gl_TexCoord[0].st).r; "
\r
270 " float cr = texture2D(plane[2], gl_TexCoord[0].st).r; "
\r
271 " float a = texture2D(plane[3], gl_TexCoord[0].st).r; "
\r
273 " gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"
\r
275 " gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"
\r
282 image_kernel::image_kernel() : impl_(new implementation()){}
\r
284 void image_kernel::apply(const core::pixel_format_desc& pix_desc, const core::image_transform& transform, bool has_separate_key)
\r
286 impl_->shaders()[pix_desc.pix_fmt].use();
\r
288 GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast<GLfloat>(transform.get_gain())));
\r
289 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0));
\r
290 GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("has_separate_key"), has_separate_key ? 1 : 0));
\r
292 if(transform.get_mode() == core::video_mode::upper)
\r
293 glPolygonStipple(upper_pattern);
\r
294 else if(transform.get_mode() == core::video_mode::lower)
\r
295 glPolygonStipple(lower_pattern);
\r
297 glPolygonStipple(progressive_pattern);
\r