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