240 lines
		
	
	
	
		
			5.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			240 lines
		
	
	
	
		
			5.6 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()),
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
QString FragmentYUV2RGB() {
 | 
						|
	return R"(
 | 
						|
	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 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;
 | 
						|
)" + FragmentYUV2RGB(),
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
ShaderPart FragmentSampleNV12Texture() {
 | 
						|
	return {
 | 
						|
		.header = R"(
 | 
						|
varying vec2 v_texcoord;
 | 
						|
uniform sampler2D y_texture;
 | 
						|
uniform sampler2D uv_texture;
 | 
						|
)",
 | 
						|
		.body = R"(
 | 
						|
	float y = texture2D(y_texture, v_texcoord).a - 0.0625;
 | 
						|
	vec2 uv = texture2D(uv_texture, v_texcoord).rg - vec2(0.5, 0.5);
 | 
						|
	float u = uv.x;
 | 
						|
	float v = uv.y;
 | 
						|
)" + FragmentYUV2RGB(),
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
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
 |