]> git.sesse.net Git - casparcg/blob - core/mixer/image/image_kernel.cpp
7da9627e0e41cf26b0395af773930aece2d44969
[casparcg] / core / mixer / image / image_kernel.cpp
1 /*\r
2 * copyright (c) 2010 Sveriges Television AB <info@casparcg.com>\r
3 *\r
4 *  This file is part of CasparCG.\r
5 *\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
10 *\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
15 \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
18 *\r
19 */\r
20 #include "../../stdafx.h"\r
21 \r
22 #include "image_kernel.h"\r
23 \r
24 #include <common/exception/exceptions.h>\r
25 #include <common/gl/gl_check.h>\r
26 \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
30 \r
31 #include <Glee.h>\r
32 \r
33 #include <boost/noncopyable.hpp>\r
34 \r
35 #include <unordered_map>\r
36 \r
37 namespace caspar { namespace mixer {\r
38 \r
39 class shader_program : boost::noncopyable\r
40 {\r
41         GLuint program_;\r
42 public:\r
43 \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
47         {\r
48                 GLint success;\r
49         \r
50                 const char* vertex_source = vertex_source_str.c_str();\r
51                                                 \r
52                 auto vertex_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);\r
53                                         \r
54                 GL(glShaderSourceARB(vertex_shader, 1, &vertex_source, NULL));\r
55                 GL(glCompileShaderARB(vertex_shader));\r
56 \r
57                 GL(glGetObjectParameterivARB(vertex_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success));\r
58                 if (success == GL_FALSE)\r
59                 {\r
60                         char info[2048];\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
66                 }\r
67                         \r
68                 const char* fragment_source = fragment_source_str.c_str();\r
69                                                 \r
70                 auto fragmemt_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);\r
71                                         \r
72                 GL(glShaderSourceARB(fragmemt_shader, 1, &fragment_source, NULL));\r
73                 GL(glCompileShaderARB(fragmemt_shader));\r
74 \r
75                 GL(glGetObjectParameterivARB(fragmemt_shader, GL_OBJECT_COMPILE_STATUS_ARB, &success));\r
76                 if (success == GL_FALSE)\r
77                 {\r
78                         char info[2048];\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
84                 }\r
85                         \r
86                 program_ = glCreateProgramObjectARB();\r
87                         \r
88                 GL(glAttachObjectARB(program_, vertex_shader));\r
89                 GL(glAttachObjectARB(program_, fragmemt_shader));\r
90 \r
91                 GL(glLinkProgramARB(program_));\r
92                         \r
93                 GL(glDeleteObjectARB(vertex_shader));\r
94                 GL(glDeleteObjectARB(fragmemt_shader));\r
95 \r
96                 GL(glGetObjectParameterivARB(program_, GL_OBJECT_LINK_STATUS_ARB, &success));\r
97                 if (success == GL_FALSE)\r
98                 {\r
99                         char info[2048];\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
105                 }\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
111         }\r
112 \r
113         GLint get_location(const char* name)\r
114         {\r
115                 GLint loc = glGetUniformLocation(program_, name);\r
116                 return loc;\r
117         }\r
118 \r
119         shader_program& operator=(shader_program&& other) \r
120         {\r
121                 program_ = other.program_; \r
122                 other.program_ = 0; \r
123                 return *this;\r
124         }\r
125 \r
126         ~shader_program()\r
127         {\r
128                 glDeleteProgram(program_);\r
129         }\r
130 \r
131         void use()\r
132         {       \r
133                 GL(glUseProgramObjectARB(program_));            \r
134         }\r
135 };\r
136 \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
142         \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
148                 \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
154 \r
155 struct image_kernel::implementation : boost::noncopyable\r
156 {       \r
157         std::unordered_map<core::pixel_format::type, shader_program> shaders_;\r
158 \r
159 public:\r
160         std::unordered_map<core::pixel_format::type, shader_program>& shaders()\r
161         {\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
165 \r
166                 if(shaders_.empty())\r
167                 {\r
168                 std::string common_vertex = \r
169                         "void main()                                                                                                                    "\r
170                         "{                                                                                                                                              "\r
171                         "       gl_TexCoord[0] = gl_MultiTexCoord0;                                                                     "\r
172                         "       gl_TexCoord[1] = gl_MultiTexCoord1;                                                                     "\r
173                         "       gl_FrontColor = gl_Color;                                                                                       "\r
174                         "       gl_Position = ftransform();                                                                                     "\r
175                         "}                                                                                                                                              ";\r
176 \r
177                 std::string common_fragment = \r
178                         "uniform sampler2D      plane[4];                                                                                       "\r
179                         "uniform float          gain;                                                                                           "\r
180                         "uniform bool           HD;                                                                                                     "\r
181                         "uniform bool           has_separate_key;                                                                       "\r
182                                                                                                                                                                 \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
186                         "{                                                                                                                                              "\r
187                         "       cb -= 0.5;                                                                                                                      "\r
188                         "       cr -= 0.5;                                                                                                                      "\r
189                         "       y = 1.164*(y-0.0625);                                                                                           "\r
190                         "                                                                                                                                               "\r
191                         "       vec4 color;                                                                                                                     "\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
195                         "       color.a = a;                                                                                                            "\r
196                         "                                                                                                                                               "\r
197                         "       return color;                                                                                                           "\r
198                         "}                                                                                                                                              "                       \r
199                         "                                                                                                                                               "\r
200 \r
201                         "vec4 ycbcra_to_bgra_hd(float y, float cb, float cr, float a)                   "\r
202                         "{                                                                                                                                              "\r
203                         "       cb -= 0.5;                                                                                                                      "\r
204                         "       cr -= 0.5;                                                                                                                      "\r
205                         "       y = 1.164*(y-0.0625);                                                                                           "\r
206                         "                                                                                                                                               "\r
207                         "       vec4 color;                                                                                                                     "\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
211                         "       color.a = a;                                                                                                            "\r
212                         "                                                                                                                                               "\r
213                         "       return color;                                                                                                           "\r
214                         "}                                                                                                                                              "                       \r
215                         "                                                                                                                                               ";\r
216                         \r
217                 shaders_[core::pixel_format::gray] = shader_program(common_vertex, common_fragment +\r
218 \r
219                         "void main()                                                                                                                    "\r
220                         "{                                                                                                                                              "\r
221                         "       vec4 rgba = vec4(texture2D(plane[0], gl_TexCoord[0].st).rrr, 1.0);      "\r
222                         "       if(has_separate_key)                                                                                            "\r
223                         "               rgba.a = texture2D(plane[3], gl_TexCoord[1].st).r;                              "\r
224                         "       gl_FragColor = rgba * gain;                                                                                     "\r
225                         "}                                                                                                                                              ");\r
226 \r
227                 shaders_[core::pixel_format::abgr] = shader_program(common_vertex, common_fragment +\r
228 \r
229                         "void main()                                                                                                                    "\r
230                         "{                                                                                                                                              "\r
231                         "       vec4 abgr = texture2D(plane[0], gl_TexCoord[0].st);                                     "\r
232                         "       if(has_separate_key)                                                                                            "\r
233                         "               abgr.b = texture2D(plane[3], gl_TexCoord[1].st).r;                              "\r
234                         "       gl_FragColor = abgr.argb * gain;                                                                        "\r
235                         "}                                                                                                                                              ");\r
236                 \r
237                 shaders_[core::pixel_format::argb]= shader_program(common_vertex, common_fragment +\r
238 \r
239                         "void main()                                                                                                                    "       \r
240                         "{                                                                                                                                              "\r
241                         "       vec4 argb = texture2D(plane[0], gl_TexCoord[0].st);                                     "\r
242                         "       if(has_separate_key)                                                                                            "\r
243                         "               argb.b = texture2D(plane[3], gl_TexCoord[1].st).r;                              "\r
244                         "       gl_FragColor = argb.grab * gl_Color * gain;                                                     "\r
245                         "}                                                                                                                                              ");\r
246                 \r
247                 shaders_[core::pixel_format::bgra]= shader_program(common_vertex, common_fragment +\r
248 \r
249                         "void main()                                                                                                                    "\r
250                         "{                                                                                                                                              "\r
251                         "       vec4 bgra = texture2D(plane[0], gl_TexCoord[0].st);                                     "\r
252                         "       if(has_separate_key)                                                                                            "\r
253                         "               bgra.a = texture2D(plane[3], gl_TexCoord[1].st).r;                              "\r
254                         "       gl_FragColor = bgra.rgba * gl_Color * gain;                                                     "\r
255                         "}                                                                                                                                              ");\r
256                 \r
257                 shaders_[core::pixel_format::rgba] = shader_program(common_vertex, common_fragment +\r
258 \r
259                         "void main()                                                                                                                    "\r
260                         "{                                                                                                                                              "\r
261                         "       vec4 rgba = texture2D(plane[0], gl_TexCoord[0].st);                                     "\r
262                         "       if(has_separate_key)                                                                                            "\r
263                         "               rgba.a = texture2D(plane[3], gl_TexCoord[1].st).r;                              "\r
264                         "       gl_FragColor = rgba.bgra * gl_Color * gain;                                                     "\r
265                         "}                                                                                                                                              ");\r
266                 \r
267                 shaders_[core::pixel_format::ycbcr] = shader_program(common_vertex, common_fragment +\r
268 \r
269                         "void main()                                                                                                                    "\r
270                         "{                                                                                                                                              "\r
271                         "       float y  = texture2D(plane[0], gl_TexCoord[0].st).r;                            "\r
272                         "       float cb = texture2D(plane[1], gl_TexCoord[0].st).r;                            "\r
273                         "       float cr = texture2D(plane[2], gl_TexCoord[0].st).r;                            "\r
274                         "       float a = 1.0;                                                                                                          "       \r
275                         "       if(has_separate_key)                                                                                            "\r
276                         "               a = texture2D(plane[3], gl_TexCoord[1].st).r;                                   "\r
277                         "       if(HD)                                                                                                                          "\r
278                         "               gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"\r
279                         "       else                                                                                                                            "\r
280                         "               gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"\r
281                         "}                                                                                                                                              ");\r
282                 \r
283                 shaders_[core::pixel_format::ycbcra] = shader_program(common_vertex, common_fragment +\r
284 \r
285                         "void main()                                                                                                                    "\r
286                         "{                                                                                                                                              "\r
287                         "       float y  = texture2D(plane[0], gl_TexCoord[0].st).r;                            "\r
288                         "       float cb = texture2D(plane[1], gl_TexCoord[0].st).r;                            "\r
289                         "       float cr = texture2D(plane[2], gl_TexCoord[0].st).r;                            "\r
290                         "       float a  = texture2D(plane[3], gl_TexCoord[0].st).r;                            "\r
291                         "       if(HD)                                                                                                                          "\r
292                         "               gl_FragColor = ycbcra_to_bgra_hd(y, cb, cr, a) * gl_Color * gain;"\r
293                         "       else                                                                                                                            "\r
294                         "               gl_FragColor = ycbcra_to_bgra_sd(y, cb, cr, a) * gl_Color * gain;"\r
295                         "}                                                                                                                                              ");\r
296                 }\r
297                 return shaders_;\r
298         }\r
299 };\r
300 \r
301 image_kernel::image_kernel() : impl_(new implementation()){}\r
302 \r
303 void image_kernel::draw(size_t width, size_t height, const core::pixel_format_desc& pix_desc, const core::image_transform& transform, bool has_separate_key)\r
304 {\r
305         GL(glEnable(GL_TEXTURE_2D));\r
306         GL(glDisable(GL_DEPTH_TEST));   \r
307 \r
308         impl_->shaders()[pix_desc.pix_fmt].use();\r
309 \r
310         GL(glUniform1f(impl_->shaders()[pix_desc.pix_fmt].get_location("gain"), static_cast<GLfloat>(transform.get_gain())));\r
311         GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("HD"), pix_desc.planes.at(0).height > 700 ? 1 : 0));\r
312         GL(glUniform1i(impl_->shaders()[pix_desc.pix_fmt].get_location("has_separate_key"), has_separate_key ? 1 : 0));\r
313 \r
314         if(transform.get_mode() == core::video_mode::upper)\r
315                 glPolygonStipple(upper_pattern);\r
316         else if(transform.get_mode() == core::video_mode::lower)\r
317                 glPolygonStipple(lower_pattern);\r
318         else\r
319                 glPolygonStipple(progressive_pattern);\r
320                         \r
321         GL(glColor4d(1.0, 1.0, 1.0, transform.get_opacity()));\r
322         GL(glViewport(0, 0, width, height));\r
323                                                 \r
324         auto m_p = transform.get_key_translation();\r
325         auto m_s = transform.get_key_scale();\r
326         double w = static_cast<double>(width);\r
327         double h = static_cast<double>(height);\r
328 \r
329         GL(glEnable(GL_SCISSOR_TEST));\r
330         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
331                         \r
332         auto f_p = transform.get_fill_translation();\r
333         auto f_s = transform.get_fill_scale();\r
334                         \r
335         glBegin(GL_QUADS);\r
336                 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
337                 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
338                 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
339                 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
340         glEnd();\r
341         GL(glDisable(GL_SCISSOR_TEST)); \r
342 }\r
343 \r
344 }}