1 /* smplayer, GUI front-end for mplayer.
2 Copyright (C) 2006-2021 Ricardo Villalba <ricardo@smplayer.info>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 #include "openglrenderer.h"
20
21 #include <QGLShaderProgram>
22 #include <QDebug>
23
OpenGLRenderer(QObject * parent)24 OpenGLRenderer::OpenGLRenderer(QObject * parent)
25 : QObject(parent)
26 , vertex_buffer(QOpenGLBuffer::VertexBuffer)
27 , vertex_shader(0)
28 , fragment_shader(0)
29 , current_format(0)
30 , color_conversion(BT709)
31 {
32 for (int n = 0; n < 3; n++) {
33 textures[n] = 0;
34 }
35 }
36
~OpenGLRenderer()37 OpenGLRenderer::~OpenGLRenderer() {
38 }
39
setFormat(ConnectionBase::Format format)40 void OpenGLRenderer::setFormat(ConnectionBase::Format format) {
41 qDebug("OpenGLRenderer::setFormat: %s", ConnectionBase::formatToString(format).toLatin1().constData());
42
43 if (format != current_format) {
44 if (program.isLinked()) {
45 program.release();
46 program.removeShader(fragment_shader);
47 delete fragment_shader;
48 fragment_shader = 0;
49 }
50 }
51 current_format = format;
52 }
53
createFragmentShader()54 void OpenGLRenderer::createFragmentShader() {
55 int attribute_vertex;
56 int attribute_texture;
57
58 fragment_shader = new QOpenGLShader(QOpenGLShader::Fragment, this);
59
60 QString code;
61
62 switch(current_format) {
63 case ConnectionBase::RGB24:
64 case ConnectionBase::RGB16: code = rgbShader(); break;
65 case ConnectionBase::YUY2: code = packedShader(YUYV); break;
66 case ConnectionBase::UYVY: code = packedShader(UYVY); break;
67 case ConnectionBase::I420: code = yuv420Shader(); break;
68 }
69
70 //qDebug() << "OpenGLRenderer::createFragmentShader: code:" << code;
71
72 if (!fragment_shader->compileSourceCode(code)) {
73 qWarning() << "OpenGLRenderer::createFragmentShader:" << fragment_shader->log();
74 }
75
76 program.addShader(fragment_shader);
77 program.link();
78 program.bind();
79
80 attribute_vertex = program.attributeLocation("vertexIn");
81 attribute_texture = program.attributeLocation("textureIn");
82
83 program.enableAttributeArray(attribute_vertex);
84 program.setAttributeBuffer(attribute_vertex, GL_FLOAT, 0, 2, 2 * sizeof(GLfloat));
85
86 program.enableAttributeArray(attribute_texture);
87 program.setAttributeBuffer(attribute_texture, GL_FLOAT, 8 * sizeof(GLfloat), 2, 2 * sizeof(GLfloat));
88
89 textureUniformY = program.uniformLocation("tex_y");
90 textureUniformU = program.uniformLocation("tex_u");
91 textureUniformV = program.uniformLocation("tex_v");
92 textureUniformStepX = program.uniformLocation("tex_stepx");
93
94 for (int n = 0; n < 3; n++) {
95 if (textures[n] == 0) {
96 textures[n] = new QOpenGLTexture(QOpenGLTexture::Target2D);
97 textures[n]->create();
98 }
99 }
100 }
101
initializeGL(int window_width,int window_height)102 void OpenGLRenderer::initializeGL(int window_width, int window_height) {
103 Q_UNUSED(window_width);
104 Q_UNUSED(window_height);
105
106 qDebug("OpenGLRenderer::initializeGL");
107 initializeOpenGLFunctions();
108
109 glEnable(GL_TEXTURE_2D);
110 glDisable(GL_DEPTH_TEST);
111
112 static const GLfloat coordinates[2][4][2]{
113 {
114 /* Vertex coordinates */
115 { -1.0f, -1.0f },
116 { -1.0f, +1.0f },
117 { +1.0f, +1.0f },
118 { +1.0f, -1.0f },
119 },
120 {
121 /* Texture coordinates */
122 { 0.0f, 1.0f },
123 { 0.0f, 0.0f },
124 { 1.0f, 0.0f },
125 { 1.0f, 1.0f },
126 },
127 };
128
129 vertex_buffer.create();
130 vertex_buffer.bind();
131 vertex_buffer.allocate(coordinates, sizeof(coordinates));
132
133 /* Create Vertex Shader */
134 vertex_shader = new QOpenGLShader(QOpenGLShader::Vertex, this);
135 QString code = QString(
136 "attribute vec4 vertexIn;"
137 "attribute vec2 textureIn;"
138 "varying vec2 textureOut;"
139 "void main(void)"
140 "{"
141 "gl_Position = vertexIn;"
142 "textureOut = textureIn;"
143 "}");
144 if (!vertex_shader->compileSourceCode(code)) {
145 qWarning() << "OpenGLRenderer::initializeGL:" << vertex_shader->log();
146 }
147 program.addShader(vertex_shader);
148 }
149
configureTexture(QOpenGLTexture & texture)150 void OpenGLRenderer::configureTexture(QOpenGLTexture &texture) {
151 glBindTexture(GL_TEXTURE_2D, texture.textureId());
152 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
153 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
154 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
155 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
156 }
157
paintGL(int window_width,int window_height,int image_width,int image_height,uint32_t image_format,unsigned char * image_buffer)158 void OpenGLRenderer::paintGL(int window_width, int window_height, int image_width, int image_height, uint32_t image_format, unsigned char * image_buffer) {
159 Q_UNUSED(window_width);
160 Q_UNUSED(window_height);
161 Q_UNUSED(image_format);
162
163 //qDebug("OpenGLRenderer::paintGL: current_format: %d image_format: %d", current_format, image_format);
164
165 if (fragment_shader == 0) {
166 createFragmentShader();
167 }
168
169 //glClearColor(0.0, 0.0, 0.0, 1.0);
170 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
171
172 // Render
173 if (current_format == ConnectionBase::YUY2 || current_format == ConnectionBase::UYVY) {
174 glActiveTexture(GL_TEXTURE0);
175 configureTexture(*textures[0]);
176
177 glTexImage2D(GL_TEXTURE_2D,
178 0,
179 GL_RGBA,
180 image_width / 2,
181 image_height,
182 0,
183 GL_RGBA,
184 GL_UNSIGNED_BYTE,
185 image_buffer);
186 program.setUniformValue(textureUniformY, 0);
187 program.setUniformValue(textureUniformStepX, 1.0f / (image_width / 2 - 1));
188 }
189 else
190 if (current_format == ConnectionBase::RGB24) {
191 glActiveTexture(GL_TEXTURE0);
192 configureTexture(*textures[0]);
193 glTexImage2D(GL_TEXTURE_2D,
194 0,
195 GL_RGB,
196 image_width,
197 image_height,
198 0,
199 GL_RGB,
200 GL_UNSIGNED_BYTE,
201 image_buffer);
202 program.setUniformValue(textureUniformY, 0);
203 }
204 else
205 if (current_format == ConnectionBase::RGB16) {
206 glActiveTexture(GL_TEXTURE0);
207 configureTexture(*textures[0]);
208 glTexImage2D(GL_TEXTURE_2D,
209 0,
210 GL_RGB565,
211 image_width,
212 image_height,
213 0,
214 GL_RGB,
215 GL_UNSIGNED_SHORT_5_6_5_REV,
216 image_buffer);
217 program.setUniformValue(textureUniformY, 0);
218 }
219 else
220 if (current_format == ConnectionBase::I420) {
221 uint32_t plane_size[2];
222 plane_size[0] = image_width * image_height;
223 plane_size[1] = (image_width * image_height) / 2;
224
225 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
226
227 /* Activate texture Y */
228 glActiveTexture(GL_TEXTURE0);
229 configureTexture(*textures[0]);
230 glTexImage2D(GL_TEXTURE_2D,
231 0,
232 GL_RED,
233 image_width,
234 image_height,
235 0,
236 GL_RED,
237 GL_UNSIGNED_BYTE,
238 image_buffer);
239
240 /* Activate texture U */
241 glActiveTexture(GL_TEXTURE1);
242 configureTexture(*textures[1]);
243 glTexImage2D(GL_TEXTURE_2D,
244 0,
245 GL_RED,
246 image_width / 2,
247 image_height / 2,
248 0,
249 GL_RED,
250 GL_UNSIGNED_BYTE,
251 image_buffer + plane_size[0]);
252
253 /* Activate texture V */
254 glActiveTexture(GL_TEXTURE2);
255 configureTexture(*textures[2]);
256 glTexImage2D(GL_TEXTURE_2D,
257 0,
258 GL_RED,
259 image_width / 2,
260 image_height / 2,
261 0,
262 GL_RED,
263 GL_UNSIGNED_BYTE,
264 image_buffer + plane_size[0] + plane_size[1]);
265
266 program.setUniformValue(textureUniformY, 0);
267 program.setUniformValue(textureUniformU, 1);
268 program.setUniformValue(textureUniformV, 2);
269 }
270
271 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
272 }
273
resizeGL(int w,int h)274 void OpenGLRenderer::resizeGL(int w, int h) {
275 Q_UNUSED(w);
276 Q_UNUSED(h);
277
278 qDebug("OpenGLRenderer::resizeGL");
279 }
280
rgbShader()281 QString OpenGLRenderer::rgbShader() {
282 return QString(
283 "varying vec2 textureOut;"
284 "uniform sampler2D tex_y;"
285 "void main(void)"
286 "{"
287 "vec3 rgb;"
288 "rgb = texture2D(tex_y, textureOut).rgb;"
289 "gl_FragColor = vec4(rgb, 1.0);"
290 "}"
291 );
292 }
293
yuv420Shader()294 QString OpenGLRenderer::yuv420Shader() {
295 QString code = QString(
296 "varying vec2 textureOut;"
297 "uniform sampler2D tex_y;"
298 "uniform sampler2D tex_u;"
299 "uniform sampler2D tex_v;"
300 "void main(void)"
301 "{"
302 "vec3 yuv;"
303 "vec3 rgb;"
304 );
305
306 code += colorConversionMat();
307
308 code += QString(
309 "yuv.x = texture2D(tex_y, textureOut).r - 0.063;"
310 "yuv.y = texture2D(tex_u, textureOut).r - 0.500;"
311 "yuv.z = texture2D(tex_v, textureOut).r - 0.500;"
312 "rgb = yuv2rgb_mat * yuv;"
313 "gl_FragColor = vec4(rgb, 1.0);"
314 "}"
315 );
316
317 return code;
318 }
319
packedShader(PackedPattern p)320 QString OpenGLRenderer::packedShader(PackedPattern p) {
321 QString code = QString(
322 "varying vec2 textureOut;"
323 "uniform sampler2D tex_y;"
324 "uniform float tex_stepx;"
325 "void main(void)"
326 "{"
327 );
328
329 code += colorConversionMat();
330
331 code += QString(
332 "vec3 yuv2rgb_offset = vec3(0.063, 0.500, 0.500);"
333 "vec2 pos = textureOut;"
334 "float f_x = fract(pos.x / tex_stepx);"
335 "vec4 left = texture2D(tex_y, vec2(pos.x - f_x * tex_stepx, pos.y));"
336 "vec4 right = texture2D(tex_y, vec2(pos.x + (1.0 - f_x) * tex_stepx , pos.y));"
337 );
338
339 if (p == YUYV) {
340 code += QString(
341 "float y_left = mix(left.r, left.b, f_x * 2.0);"
342 "float y_right = mix(left.b, right.r, f_x * 2.0 - 1.0);"
343 "vec2 uv = mix(left.ga, right.ga, f_x);"
344 );
345 }
346 else
347 if (p == UYVY) {
348 code += QString(
349 "float y_left = mix(left.g, left.a, f_x * 2.0);"
350 "float y_right = mix(left.a, right.g, f_x * 2.0 - 1.0);"
351 "vec2 uv = mix(left.rb, right.rb, f_x);"
352 );
353 }
354
355 code += QString(
356 "float y = mix(y_left, y_right, step(0.5, f_x));"
357 "vec3 rgb = yuv2rgb_mat * (vec3(y, uv) - yuv2rgb_offset);"
358 "gl_FragColor = vec4(rgb, 1.0);"
359 "}"
360 );
361
362 return code;
363 }
364
colorConversionMat()365 QString OpenGLRenderer::colorConversionMat() {
366 qDebug("OpenGLRenderer::colorConversionMat: %d", color_conversion);
367
368 QString bt601 = QString(
369 "mat3 yuv2rgb_mat = mat3("
370 "vec3(1.164, 1.164, 1.164),"
371 "vec3(0.000, -0.392, 2.017),"
372 "vec3(1.596, -0.813, 0.000)"
373 ");");
374
375 QString bt709 = QString(
376 "mat3 yuv2rgb_mat = mat3("
377 "vec3(1.164, 1.164, 1.164),"
378 "vec3(0.000, -0.213, 2.112),"
379 "vec3(1.793, -0.533, 0.000)"
380 ");");
381
382 QString jpeg = QString(
383 "mat3 yuv2rgb_mat = mat3("
384 "vec3(1.000, 1.000, 1.000),"
385 "vec3(0.000, -0.343, 1.765),"
386 "vec3(1.400, -0.711, 0.000)"
387 ");");
388
389 QString bt2020ncl = QString(
390 "mat3 yuv2rgb_mat = mat3("
391 "vec3(1.164, 1.164, 1.164),"
392 "vec3(0.000, -0.187, 2.141),"
393 "vec3(1.679, -0.650, 0.000)"
394 ");");
395
396 QString bt2020cl = QString(
397 "mat3 yuv2rgb_mat = mat3("
398 "vec3(0.000, 1.164, 0.000),"
399 "vec3(0.000, 0.000, 1.138),"
400 "vec3(1.138, 0.000, 0.000)"
401 ");");
402
403 if (color_conversion == BT601)
404 return bt601;
405 else
406 return bt709;
407 }
408
409 #include "moc_openglrenderer.cpp"
410