219 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
	
		
			5.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // This file is part of Desktop App Toolkit,
 | |
| // a set of libraries for developing nice desktop applications.
 | |
| //
 | |
| // For license and copyright information please follow this link:
 | |
| // https://github.com/desktop-app/legal/blob/master/LEGAL
 | |
| //
 | |
| #include "ui/gl/gl_shader.h"
 | |
| 
 | |
| #include "ui/gl/gl_image.h"
 | |
| #include "base/debug_log.h"
 | |
| 
 | |
| #include <QtGui/QOpenGLContext>
 | |
| 
 | |
| namespace Ui::GL {
 | |
| 
 | |
| [[nodiscard]] bool IsOpenGLES() {
 | |
| 	const auto current = QOpenGLContext::currentContext();
 | |
| 	Assert(current != nullptr);
 | |
| 
 | |
| 	return (current->format().renderableType() == QSurfaceFormat::OpenGLES);
 | |
| }
 | |
| 
 | |
| QString VertexShader(const std::vector<ShaderPart> &parts) {
 | |
| 	const auto version = IsOpenGLES()
 | |
| 		? QString("#version 100\nprecision highp float;\n")
 | |
| 		: QString("#version 120\n");
 | |
| 	const auto accumulate = [&](auto proj) {
 | |
| 		return ranges::accumulate(parts, QString(), std::plus<>(), proj);
 | |
| 	};
 | |
| 	return version + R"(
 | |
| attribute vec2 position;
 | |
| )" + accumulate(&ShaderPart::header) + R"(
 | |
| void main() {
 | |
| 	vec4 result = vec4(position, 0., 1.);
 | |
| )" + accumulate(&ShaderPart::body) + R"(
 | |
| 	gl_Position = result;
 | |
| }
 | |
| )";
 | |
| }
 | |
| 
 | |
| QString FragmentShader(const std::vector<ShaderPart> &parts) {
 | |
| 	const auto version = IsOpenGLES()
 | |
| 		? QString("#version 100\nprecision highp float;\n")
 | |
| 		: QString("#version 120\n");
 | |
| 	const auto accumulate = [&](auto proj) {
 | |
| 		return ranges::accumulate(parts, QString(), std::plus<>(), proj);
 | |
| 	};
 | |
| 	return version + accumulate(&ShaderPart::header) + R"(
 | |
| void main() {
 | |
| 	vec4 result = vec4(0., 0., 0., 0.);
 | |
| )" + accumulate(&ShaderPart::body) + R"(
 | |
| 	gl_FragColor = result;
 | |
| }
 | |
| )";
 | |
| }
 | |
| 
 | |
| ShaderPart VertexPassTextureCoord(char prefix) {
 | |
| 	const auto name = prefix + QString("_texcoord");
 | |
| 	return {
 | |
| 		.header = R"(
 | |
| attribute vec2 )" + name + R"(In;
 | |
| varying vec2 )" + name + ";\n",
 | |
| 		.body = R"(
 | |
| 	)" + name + " = " + name + "In;\n",
 | |
| 	};
 | |
| }
 | |
| 
 | |
| ShaderPart FragmentSampleARGB32Texture() {
 | |
| 	return {
 | |
| 		.header = R"(
 | |
| varying vec2 v_texcoord;
 | |
| uniform sampler2D s_texture;
 | |
| )",
 | |
| 		.body = R"(
 | |
| 	result = texture2D(s_texture, v_texcoord);
 | |
| )" + (kSwizzleRedBlue
 | |
| 	? R"(
 | |
| 	result = vec4(result.b, result.g, result.r, result.a);
 | |
| )" : QString()),
 | |
| 	};
 | |
| }
 | |
| 
 | |
| ShaderPart FragmentSampleYUV420Texture() {
 | |
| 	return {
 | |
| 		.header = R"(
 | |
| varying vec2 v_texcoord;
 | |
| uniform sampler2D y_texture;
 | |
| uniform sampler2D u_texture;
 | |
| uniform sampler2D v_texture;
 | |
| )",
 | |
| 		.body = R"(
 | |
| 	float y = texture2D(y_texture, v_texcoord).a - 0.0625;
 | |
| 	float u = texture2D(u_texture, v_texcoord).a - 0.5;
 | |
| 	float v = texture2D(v_texture, v_texcoord).a - 0.5;
 | |
| 	result = vec4(
 | |
| 		1.164 * y + 1.596 * v,
 | |
| 		1.164 * y - 0.392 * u - 0.813 * v,
 | |
| 		1.164 * y + 2.17 * u,
 | |
| 		1.);
 | |
| )",
 | |
| 	};
 | |
| }
 | |
| 
 | |
| ShaderPart FragmentGlobalOpacity() {
 | |
| 	return {
 | |
| 		.header = R"(
 | |
| uniform float g_opacity;
 | |
| )",
 | |
| 		.body = R"(
 | |
| 	result *= g_opacity;
 | |
| )",
 | |
| 	};
 | |
| }
 | |
| 
 | |
| ShaderPart VertexViewportTransform() {
 | |
| 	return {
 | |
| 		.header = R"(
 | |
| uniform vec2 viewport;
 | |
| vec4 transform(vec4 position) {
 | |
| 	return vec4(
 | |
| 		vec2(-1, -1) + 2. * position.xy / viewport,
 | |
| 		position.z,
 | |
| 		position.w);
 | |
| }
 | |
| )",
 | |
| 		.body = R"(
 | |
| 	result = transform(result);
 | |
| )",
 | |
| 	};
 | |
| }
 | |
| 
 | |
| ShaderPart FragmentRoundCorners() {
 | |
| 	return {
 | |
| 		.header = R"(
 | |
| uniform vec4 roundRect;
 | |
| uniform vec2 radiusOutline;
 | |
| uniform vec4 roundBg;
 | |
| uniform vec4 outlineFg;
 | |
| vec2 roundedCorner() {
 | |
| 	vec2 rectHalf = roundRect.zw / 2.;
 | |
| 	vec2 rectCenter = roundRect.xy + rectHalf;
 | |
| 	vec2 fromRectCenter = abs(gl_FragCoord.xy - rectCenter);
 | |
| 	vec2 vectorRadius = radiusOutline.xx + vec2(0.5, 0.5);
 | |
| 	vec2 fromCenterWithRadius = fromRectCenter + vectorRadius;
 | |
| 	vec2 fromRoundingCenter = max(fromCenterWithRadius, rectHalf)
 | |
| 		- rectHalf;
 | |
| 	float rounded = length(fromRoundingCenter) - radiusOutline.x;
 | |
| 	float outline = rounded + radiusOutline.y;
 | |
| 
 | |
| 	return vec2(
 | |
| 		1. - smoothstep(0., 1., rounded),
 | |
| 		1. - (smoothstep(0., 1., outline) * outlineFg.a));
 | |
| }
 | |
| )",
 | |
| 		.body = R"(
 | |
| 	vec2 roundOutline = roundedCorner();
 | |
| 	result = result * roundOutline.y
 | |
| 		+ vec4(outlineFg.rgb, 1) * (1. - roundOutline.y);
 | |
| 	result = result * roundOutline.x + roundBg * (1. - roundOutline.x);
 | |
| )",
 | |
| 	};
 | |
| }
 | |
| 
 | |
| ShaderPart FragmentStaticColor() {
 | |
| 	return {
 | |
| 		.header = R"(
 | |
| uniform vec4 s_color;
 | |
| )",
 | |
| 		.body = R"(
 | |
| 	result = s_color;
 | |
| )",
 | |
| 	};
 | |
| }
 | |
| 
 | |
| not_null<QOpenGLShader*> MakeShader(
 | |
| 		not_null<QOpenGLShaderProgram*> program,
 | |
| 		QOpenGLShader::ShaderType type,
 | |
| 		const QString &source) {
 | |
| 	const auto result = new QOpenGLShader(type, program);
 | |
| 	if (!result->compileSourceCode(source)) {
 | |
| 		LOG(("Shader Compilation Failed: %1, error %2."
 | |
| 			).arg(source
 | |
| 			).arg(result->log()));
 | |
| 	}
 | |
| 	program->addShader(result);
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| Program LinkProgram(
 | |
| 		not_null<QOpenGLShaderProgram*> program,
 | |
| 		std::variant<QString, not_null<QOpenGLShader*>> vertex,
 | |
| 		std::variant<QString, not_null<QOpenGLShader*>> fragment) {
 | |
| 	const auto vertexAsSource = v::is<QString>(vertex);
 | |
| 	const auto v = vertexAsSource
 | |
| 		? MakeShader(
 | |
| 			program,
 | |
| 			QOpenGLShader::Vertex,
 | |
| 			v::get<QString>(vertex))
 | |
| 		: v::get<not_null<QOpenGLShader*>>(vertex);
 | |
| 	if (!vertexAsSource) {
 | |
| 		program->addShader(v);
 | |
| 	}
 | |
| 	const auto fragmentAsSource = v::is<QString>(fragment);
 | |
| 	const auto f = fragmentAsSource
 | |
| 		? MakeShader(
 | |
| 			program,
 | |
| 			QOpenGLShader::Fragment,
 | |
| 			v::get<QString>(fragment))
 | |
| 		: v::get<not_null<QOpenGLShader*>>(fragment);
 | |
| 	if (!fragmentAsSource) {
 | |
| 		program->addShader(f);
 | |
| 	}
 | |
| 	if (!program->link()) {
 | |
| 		LOG(("Shader Link Failed: %1.").arg(program->log()));
 | |
| 	}
 | |
| 	return { v, f };
 | |
| }
 | |
| 
 | |
| } // namespace Ui::GL
 | 
