Add some utilities for OpenGL shaders.

This commit is contained in:
John Preston 2021-05-25 14:48:38 +04:00
parent 2a26d4a91a
commit 808f8c7dea
7 changed files with 382 additions and 0 deletions

View file

@ -61,6 +61,10 @@ PRIVATE
ui/gl/gl_detection.h ui/gl/gl_detection.h
ui/gl/gl_image.cpp ui/gl/gl_image.cpp
ui/gl/gl_image.h ui/gl/gl_image.h
ui/gl/gl_math.cpp
ui/gl/gl_math.h
ui/gl/gl_shader.cpp
ui/gl/gl_shader.h
ui/gl/gl_surface.cpp ui/gl/gl_surface.cpp
ui/gl/gl_surface.h ui/gl/gl_surface.h
ui/image/image_prepare.cpp ui/image/image_prepare.cpp

View file

@ -82,4 +82,31 @@ void Image::destroy(QOpenGLFunctions &f) {
_cacheKey = 0; _cacheKey = 0;
} }
TexturedRect Image::texturedRect(
const QRect &geometry,
const QRect &texture,
const QRect &clip) {
Expects(!_image.isNull());
const auto visible = clip.isNull()
? geometry
: clip.intersected(geometry);
const auto xFactor = texture.width() / geometry.width();
const auto yFactor = texture.height() / geometry.height();
const auto usedTexture = QRect(
texture.x() + (visible.x() - geometry.x()) * xFactor,
texture.y() + (visible.y() - geometry.y()) * yFactor,
visible.width() * xFactor,
visible.height() * yFactor);
const auto dimensions = QSizeF(_image.size());
return {
.geometry = Rect(visible),
.texture = Rect(QRectF(
usedTexture.x() / dimensions.width(),
usedTexture.y() / dimensions.height(),
usedTexture.width() / dimensions.width(),
usedTexture.height() / dimensions.height())),
};
}
} // namespace Ui::GL } // namespace Ui::GL

View file

@ -6,6 +6,8 @@
// //
#pragma once #pragma once
#include "ui/gl/gl_math.h"
#include <QtGui/QOpenGLFunctions> #include <QtGui/QOpenGLFunctions>
namespace Ui::GL { namespace Ui::GL {
@ -47,6 +49,11 @@ private:
}; };
struct TexturedRect {
Rect geometry;
Rect texture;
};
class Image final { class Image final {
public: public:
void setImage(QImage image); void setImage(QImage image);
@ -57,6 +64,11 @@ public:
void bind(QOpenGLFunctions &f); void bind(QOpenGLFunctions &f);
void destroy(QOpenGLFunctions &f); void destroy(QOpenGLFunctions &f);
[[nodiscard]] TexturedRect texturedRect(
const QRect &geometry,
const QRect &texture,
const QRect &clip = QRect());
explicit operator bool() const { explicit operator bool() const {
return !_image.isNull(); return !_image.isNull();
} }

35
ui/gl/gl_math.cpp Normal file
View file

@ -0,0 +1,35 @@
// 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_math.h"
namespace Ui::GL {
QVector4D Uniform(const QRect &rect, float factor) {
return QVector4D(
rect.x() * factor,
rect.y() * factor,
rect.width() * factor,
rect.height() * factor);
}
QVector4D Uniform(const Rect &rect) {
return QVector4D(rect.x(), rect.y(), rect.width(), rect.height());
}
QVector4D Uniform(const QColor &color) {
return QVector4D(
color.redF(),
color.greenF(),
color.blueF(),
color.alphaF());
}
QSizeF Uniform(QSize size) {
return size;
}
} // namespace Ui::GL

75
ui/gl/gl_math.h Normal file
View file

@ -0,0 +1,75 @@
// 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
//
#pragma once
#include <QtGui/QVector4D>
#include <QtCore/QSizeF>
namespace Ui::GL {
class Rect final {
public:
Rect(QRect rect)
: _x(rect.x())
, _y(rect.y())
, _width(rect.width())
, _height(rect.height()) {
}
Rect(QRectF rect)
: _x(rect.x())
, _y(rect.y())
, _width(rect.width())
, _height(rect.height()) {
}
Rect(float x, float y, float width, float height)
: _x(x)
, _y(y)
, _width(width)
, _height(height) {
}
[[nodiscard]] float x() const {
return _x;
}
[[nodiscard]] float y() const {
return _y;
}
[[nodiscard]] float width() const {
return _width;
}
[[nodiscard]] float height() const {
return _height;
}
[[nodiscard]] float left() const {
return _x;
}
[[nodiscard]] float top() const {
return _y;
}
[[nodiscard]] float right() const {
return _x + _width;
}
[[nodiscard]] float bottom() const {
return _y + _height;
}
private:
float _x = 0;
float _y = 0;
float _width = 0;
float _height = 0;
};
[[nodiscard]] QVector4D Uniform(const QRect &rect, float factor);
[[nodiscard]] QVector4D Uniform(const Rect &rect);
[[nodiscard]] QVector4D Uniform(const QColor &color);
[[nodiscard]] QSizeF Uniform(QSize size);
} // namespace Ui::GL

183
ui/gl/gl_shader.cpp Normal file
View file

@ -0,0 +1,183 @@
// 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 "base/debug_log.h"
namespace Ui::GL {
QString VertexShader(const std::vector<ShaderPart> &parts) {
const auto accumulate = [&](auto proj) {
return ranges::accumulate(parts, QString(), std::plus<>(), proj);
};
return R"(
#version 120
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 accumulate = [&](auto proj) {
return ranges::accumulate(parts, QString(), std::plus<>(), proj);
};
return R"(
#version 120
)" + accumulate(&ShaderPart::header) + R"(
void main() {
vec4 result = vec4(0., 0., 0., 0.);
)" + accumulate(&ShaderPart::body) + R"(
gl_FragColor = result;
}
)";
}
ShaderPart VertexPassTextureCoord() {
return {
.header = R"(
attribute vec2 texcoord;
varying vec2 v_texcoord;
)",
.body = R"(
v_texcoord = texcoord;
)",
};
}
ShaderPart FragmentSampleARGB32Texture() {
return {
.header = R"(
varying vec2 v_texcoord;
uniform sampler2D s_texture;
)",
.body = R"(
result = texture2D(s_texture, v_texcoord);
result = vec4(result.b, result.g, result.r, result.a);
)",
};
}
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).r;
float u = texture2D(u_texture, v_texcoord).r - 0.5;
float v = texture2D(v_texture, v_texcoord).r - 0.5;
result = vec4(y + 1.403 * v, y - 0.344 * u - 0.714 * v, y + 1.77 * u, 1);
)",
};
}
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 vec4 roundBg;
uniform float roundRadius;
float roundedCorner() {
vec2 rectHalf = roundRect.zw / 2;
vec2 rectCenter = roundRect.xy + rectHalf;
vec2 fromRectCenter = abs(gl_FragCoord.xy - rectCenter);
vec2 vectorRadius = vec2(roundRadius + 0.5, roundRadius + 0.5);
vec2 fromCenterWithRadius = fromRectCenter + vectorRadius;
vec2 fromRoundingCenter = max(fromCenterWithRadius, rectHalf)
- rectHalf;
float d = length(fromRoundingCenter) - roundRadius;
return 1. - smoothstep(0., 1., d);
}
)",
.body = R"(
float rounded = roundedCorner();
result = result * rounded + roundBg * (1. - rounded);
)",
};
}
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

46
ui/gl/gl_shader.h Normal file
View file

@ -0,0 +1,46 @@
// 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
//
#pragma once
#include <QtCore/QString>
#include <QtGui/QOpenGLShader>
class OpenGLShaderProgram;
namespace Ui::GL {
struct ShaderPart {
QString header;
QString body;
};
[[nodiscard]] QString VertexShader(const std::vector<ShaderPart> &parts);
[[nodiscard]] QString FragmentShader(const std::vector<ShaderPart> &parts);
[[nodiscard]] ShaderPart VertexPassTextureCoord();
[[nodiscard]] ShaderPart FragmentSampleARGB32Texture();
[[nodiscard]] ShaderPart FragmentSampleYUV420Texture();
[[nodiscard]] ShaderPart VertexViewportTransform();
[[nodiscard]] ShaderPart FragmentRoundCorners();
[[nodiscard]] ShaderPart FragmentStaticColor();
not_null<QOpenGLShader*> MakeShader(
not_null<QOpenGLShaderProgram*> program,
QOpenGLShader::ShaderType type,
const QString &source);
struct Program {
not_null<QOpenGLShader*> vertex;
not_null<QOpenGLShader*> fragment;
};
Program LinkProgram(
not_null<QOpenGLShaderProgram*> program,
std::variant<QString, not_null<QOpenGLShader*>> vertex,
std::variant<QString, not_null<QOpenGLShader*>> fragment);
} // namespace Ui::GL