diff options
Diffstat (limited to 'src/effects/private/qgfxshaderbuilder.cpp')
-rw-r--r-- | src/effects/private/qgfxshaderbuilder.cpp | 573 |
1 files changed, 0 insertions, 573 deletions
diff --git a/src/effects/private/qgfxshaderbuilder.cpp b/src/effects/private/qgfxshaderbuilder.cpp deleted file mode 100644 index 6439ff3..0000000 --- a/src/effects/private/qgfxshaderbuilder.cpp +++ /dev/null @@ -1,573 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Jolla Ltd, author: <gunnar.sletta@jollamobile.com> -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Graphical Effects module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qgfxshaderbuilder_p.h" - -#include <QtCore/QDebug> -#include <QtGui/QOffscreenSurface> -#include <QtGui/QOpenGLContext> -#include <QtGui/QOpenGLFunctions> - -#include <qmath.h> -#include <qnumeric.h> - -#ifndef GL_MAX_VARYING_COMPONENTS -#define GL_MAX_VARYING_COMPONENTS 0x8B4B -#endif - -#ifndef GL_MAX_VARYING_FLOATS -#define GL_MAX_VARYING_FLOATS 0x8B4B -#endif - -#ifndef GL_MAX_VARYING_VECTORS -#define GL_MAX_VARYING_VECTORS 0x8DFC -#endif - -QGfxShaderBuilder::QGfxShaderBuilder() - : m_coreProfile(false) -{ - // The following code makes the assumption that an OpenGL context the GUI - // thread will get the same capabilities as the render thread's OpenGL - // context. Not 100% accurate, but it works... - QOpenGLContext context; - if (!context.create()) { - qDebug() << "failed to acquire GL context to resolve capabilities, using defaults.."; - m_maxBlurSamples = 8; // minimum number of varyings in the ES 2.0 spec. - return; - } - - QOffscreenSurface surface; - // In very odd cases, we can get incompatible configs here unless we pass the - // GL context's format on to the offscreen format. - surface.setFormat(context.format()); - surface.create(); - - QOpenGLContext *oldContext = QOpenGLContext::currentContext(); - QSurface *oldSurface = oldContext ? oldContext->surface() : 0; - if (context.makeCurrent(&surface)) { - QOpenGLFunctions *gl = context.functions(); - if (context.isOpenGLES()) { - gl->glGetIntegerv(GL_MAX_VARYING_VECTORS, &m_maxBlurSamples); - } else if (context.format().majorVersion() >= 3) { - int components; - gl->glGetIntegerv(GL_MAX_VARYING_COMPONENTS, &components); - m_maxBlurSamples = components / 2.0; - m_coreProfile = context.format().profile() == QSurfaceFormat::CoreProfile; - } else { - int floats; - gl->glGetIntegerv(GL_MAX_VARYING_FLOATS, &floats); - m_maxBlurSamples = floats / 2.0; - } - if (oldContext && oldSurface) - oldContext->makeCurrent(oldSurface); - else - context.doneCurrent(); - } else { - qDebug() << "failed to acquire GL context to resolve capabilities, using defaults.."; - m_maxBlurSamples = 8; // minimum number of varyings in the ES 2.0 spec. - } -} - -/* - - The algorithm works like this.. - - For every two pixels we want to sample we take one sample between those - two pixels and rely on linear interpoliation to get both values at the - cost of one texture sample. The sample point is calculated based on the - gaussian weights at the two texels. - - I've included the table here for future reference: - - Requested Effective Actual Actual - Samples Radius/Kernel Samples Radius(*) - ------------------------------------------------- - 0 0 / 1x1 1 0 - 1 0 / 1x1 1 0 - 2 1 / 3x3 2 0 - 3 1 / 3x3 2 0 - 4 2 / 5x5 3 1 - 5 2 / 5x5 3 1 - 6 3 / 7x7 4 1 - 7 3 / 7x7 4 1 - 8 4 / 9x9 5 2 - 9 4 / 9x9 5 2 - 10 5 / 11x11 6 2 - 11 5 / 11x11 6 2 - 12 6 / 13x13 7 3 - 13 6 / 13x13 7 3 - ... ... ... ... - - When ActualSamples is an 'odd' nunber, sample center pixel separately: - EffectiveRadius: 4 - EffectiveKernel: 9x9 - ActualSamples: 5 - -4 -3 -2 -1 0 +1 +2 +3 +4 - | | | | | | | | | | - \ / \ / | \ / \ / - tL2 tL1 tC tR1 tR2 - - When ActualSamples is an 'even' number, sample 3 center pixels with two - samples: - EffectiveRadius: 3 - EffectiveKernel: 7x7 - ActualSamples: 4 - -3 -2 -1 0 +1 +2 +3 - | | | | | | | | - \ / \ / | \ / - tL1 tL0 tR0 tR2 - - From this table we have the following formulas: - EffectiveRadius = RequestedSamples / 2; - EffectiveKernel = EffectiveRadius * 2 + 1 - ActualSamples = 1 + RequstedSamples / 2; - ActualRadius = RequestedSamples / 4; - - (*) ActualRadius excludes the pixel pair sampled in the center - for even 'actual sample' counts -*/ - -static qreal qgfx_gaussian(qreal x, qreal d) -{ - return qExp(- x * x / (2 * d * d)); -} - -struct QGfxGaussSample -{ - QByteArray name; - qreal pos; - qreal weight; - inline void set(const QByteArray &n, qreal p, qreal w) { - name = n; - pos = p; - weight = w; - } -}; - -static void qgfx_declareBlurVaryings(QByteArray &shader, QGfxGaussSample *s, int samples) -{ - for (int i=0; i<samples; ++i) { - shader += "varying highp vec2 "; - shader += s[i].name; - shader += ";\n"; - } -} - -static void qgfx_declareCoreBlur(QByteArray &shader, const QByteArray& direction, QGfxGaussSample *s, int samples) -{ - for (int i=0; i<samples; ++i) { - shader += direction + " vec2 "; - shader += s[i].name; - shader += ";\n"; - } -} - -static void qgfx_buildGaussSamplePoints(QGfxGaussSample *p, int samples, int radius, qreal deviation) -{ - - if ((samples % 2) == 1) { - p[radius].set("tC", 0, 1); - for (int i=0; i<radius; ++i) { - qreal p0 = (i + 1) * 2 - 1; - qreal p1 = (i + 1) * 2; - qreal w0 = qgfx_gaussian(p0, deviation); - qreal w1 = qgfx_gaussian(p1, deviation); - qreal w = w0 + w1; - qreal samplePos = (p0 * w0 + p1 * w1) / w; - if (qIsNaN(samplePos)) { - samplePos = 0; - w = 0; - } - p[radius - i - 1].set("tL" + QByteArray::number(i), samplePos, w); - p[radius + i + 1].set("tR" + QByteArray::number(i), -samplePos, w); - } - } else { - { // tL0 - qreal wl = qgfx_gaussian(-1.0, deviation); - qreal wc = qgfx_gaussian(0.0, deviation); - qreal w = wl + wc; - p[radius].set("tL0", -1.0 * wl / w, w); - p[radius+1].set("tR0", 1.0, wl); // reuse wl as gauss(-1)==gauss(1); - } - for (int i=0; i<radius; ++i) { - qreal p0 = (i + 1) * 2; - qreal p1 = (i + 1) * 2 + 1; - qreal w0 = qgfx_gaussian(p0, deviation); - qreal w1 = qgfx_gaussian(p1, deviation); - qreal w = w0 + w1; - qreal samplePos = (p0 * w0 + p1 * w1) / w; - if (qIsNaN(samplePos)) { - samplePos = 0; - w = 0; - } - p[radius - i - 1].set("tL" + QByteArray::number(i+1), samplePos, w); - p[radius + i + 2].set("tR" + QByteArray::number(i+1), -samplePos, w); - - } - } -} - -QByteArray qgfx_gaussianVertexShader(QGfxGaussSample *p, int samples) -{ - QByteArray shader; - shader.reserve(1024); - shader += "attribute highp vec4 qt_Vertex;\n" - "attribute highp vec2 qt_MultiTexCoord0;\n\n" - "uniform highp mat4 qt_Matrix;\n" - "uniform highp float spread;\n" - "uniform highp vec2 dirstep;\n\n"; - - qgfx_declareBlurVaryings(shader, p, samples); - - shader += "\nvoid main() {\n" - " gl_Position = qt_Matrix * qt_Vertex;\n\n"; - - for (int i=0; i<samples; ++i) { - shader += " "; - shader += p[i].name; - shader += " = qt_MultiTexCoord0"; - if (p[i].pos != 0.0) { - shader += " + spread * dirstep * float("; - shader += QByteArray::number(p[i].pos); - shader += ')'; - } - shader += ";\n"; - } - - shader += "}\n"; - - return shader; -} - -QByteArray qgfx_gaussianVertexCoreShader(QGfxGaussSample *p, int samples) -{ - QByteArray shader; - shader.reserve(1024); - shader += "#version 150 core\n" - "in vec4 qt_Vertex;\n" - "in vec2 qt_MultiTexCoord0;\n\n" - "uniform mat4 qt_Matrix;\n" - "uniform float spread;\n" - "uniform vec2 dirstep;\n\n"; - - qgfx_declareCoreBlur(shader, "out", p, samples); - - shader += "\nvoid main() {\n" - " gl_Position = qt_Matrix * qt_Vertex;\n\n"; - - for (int i=0; i<samples; ++i) { - shader += " "; - shader += p[i].name; - shader += " = qt_MultiTexCoord0"; - if (p[i].pos != 0.0) { - shader += " + spread * dirstep * float("; - shader += QByteArray::number(p[i].pos); - shader += ')'; - } - shader += ";\n"; - } - - shader += "}\n"; - - return shader; -} - -QByteArray qgfx_gaussianFragmentShader(QGfxGaussSample *p, int samples, bool alphaOnly) -{ - QByteArray shader; - shader.reserve(1024); - shader += "uniform lowp sampler2D source;\n" - "uniform lowp float qt_Opacity;\n"; - - if (alphaOnly) { - shader += "uniform lowp vec4 color;\n" - "uniform lowp float thickness;\n"; - } - - shader += "\n"; - - - - qgfx_declareBlurVaryings(shader, p, samples); - - shader += "\nvoid main() {\n" - " gl_FragColor = "; - if (alphaOnly) - shader += "mix(vec4(0), color, clamp(("; - else - shader += "("; - - qreal sum = 0; - for (int i=0; i<samples; ++i) - sum += p[i].weight; - - for (int i=0; i<samples; ++i) { - shader += "\n + float("; - shader += QByteArray::number(p[i].weight / sum); - shader += ") * texture2D(source, "; - shader += p[i].name; - shader += ")"; - if (alphaOnly) - shader += ".a"; - } - - shader += "\n )"; - if (alphaOnly) - shader += "/thickness, 0.0, 1.0))"; - shader += "* qt_Opacity;\n}"; - - return shader; -} - -QByteArray qgfx_gaussianFragmentCoreShader(QGfxGaussSample *p, int samples, bool alphaOnly) -{ - QByteArray shader; - shader.reserve(1024); - shader += "#version 150 core\n" - "uniform sampler2D source;\n" - "uniform float qt_Opacity;\n"; - - if (alphaOnly) { - shader += "uniform vec4 color;\n" - "uniform float thickness;\n"; - } - - shader += "out vec4 fragColor;\n"; - - qgfx_declareCoreBlur(shader, "in", p, samples); - - shader += "\nvoid main() {\n" - " fragColor = "; - if (alphaOnly) - shader += "mix(vec4(0), color, clamp(("; - else - shader += "("; - - qreal sum = 0; - for (int i=0; i<samples; ++i) - sum += p[i].weight; - - for (int i=0; i<samples; ++i) { - shader += "\n + float("; - shader += QByteArray::number(p[i].weight / sum); - shader += ") * texture(source, "; - shader += p[i].name; - shader += ")"; - if (alphaOnly) - shader += ".a"; - } - - shader += "\n )"; - if (alphaOnly) - shader += "/thickness, 0.0, 1.0))"; - shader += "* qt_Opacity;\n}"; - - return shader; -} - -static QByteArray qgfx_fallbackVertexShader() -{ - return "attribute highp vec4 qt_Vertex;\n" - "attribute highp vec2 qt_MultiTexCoord0;\n" - "uniform highp mat4 qt_Matrix;\n" - "varying highp vec2 qt_TexCoord0;\n" - "void main() {\n" - " gl_Position = qt_Matrix * qt_Vertex;\n" - " qt_TexCoord0 = qt_MultiTexCoord0;\n" - "}\n"; -} - -static QByteArray qgfx_fallbackCoreVertexShader() -{ - return "#version 150 core\n" - "in vec4 qt_Vertex;\n" - "in vec2 qt_MultiTexCoord0;\n" - "uniform mat4 qt_Matrix;\n" - "out vec2 qt_TexCoord0;\n" - "void main() {\n" - " gl_Position = qt_Matrix * qt_Vertex;\n" - " qt_TexCoord0 = qt_MultiTexCoord0;\n" - "}\n"; -} - -static QByteArray qgfx_fallbackFragmentShader(int requestedRadius, qreal deviation, bool masked, bool alphaOnly) -{ - QByteArray fragShader; - if (masked) - fragShader += "uniform mediump sampler2D mask;\n"; - fragShader += - "uniform highp sampler2D source;\n" - "uniform lowp float qt_Opacity;\n" - "uniform mediump float spread;\n" - "uniform highp vec2 dirstep;\n"; - if (alphaOnly) { - fragShader += "uniform lowp vec4 color;\n" - "uniform lowp float thickness;\n"; - } - fragShader += - "\n" - "varying highp vec2 qt_TexCoord0;\n" - "\n" - "void main() {\n"; - if (alphaOnly) - fragShader += " mediump float result = 0.0;\n"; - else - fragShader += " mediump vec4 result = vec4(0);\n"; - fragShader += " highp vec2 pixelStep = dirstep * spread;\n"; - if (masked) - fragShader += " pixelStep *= texture2D(mask, qt_TexCoord0).a;\n"; - - float wSum = 0; - for (int r=-requestedRadius; r<=requestedRadius; ++r) { - float w = qgfx_gaussian(r, deviation); - wSum += w; - fragShader += " result += float("; - fragShader += QByteArray::number(w); - fragShader += ") * texture2D(source, qt_TexCoord0 + pixelStep * float("; - fragShader += QByteArray::number(r); - fragShader += "))"; - if (alphaOnly) - fragShader += ".a"; - fragShader += ";\n"; - } - fragShader += " const mediump float wSum = float("; - fragShader += QByteArray::number(wSum); - fragShader += ");\n" - " gl_FragColor = "; - if (alphaOnly) - fragShader += "mix(vec4(0), color, clamp((result / wSum) / thickness, 0.0, 1.0)) * qt_Opacity;\n"; - else - fragShader += "(qt_Opacity / wSum) * result;\n"; - fragShader += "}\n"; - - return fragShader; -} - -static QByteArray qgfx_fallbackCoreFragmentShader(int requestedRadius, qreal deviation, bool masked, bool alphaOnly) -{ - QByteArray fragShader = "#version 150 core\n"; - if (masked) - fragShader += "uniform sampler2D mask;\n"; - fragShader += - "uniform sampler2D source;\n" - "uniform float qt_Opacity;\n" - "uniform float spread;\n" - "uniform vec2 dirstep;\n"; - if (alphaOnly) { - fragShader += "uniform vec4 color;\n" - "uniform float thickness;\n"; - } - fragShader += - "out vec4 fragColor;\n" - "in vec2 qt_TexCoord0;\n" - "\n" - "void main() {\n"; - if (alphaOnly) - fragShader += " float result = 0.0;\n"; - else - fragShader += " vec4 result = vec4(0);\n"; - fragShader += " vec2 pixelStep = dirstep * spread;\n"; - if (masked) - fragShader += " pixelStep *= texture(mask, qt_TexCoord0).a;\n"; - - float wSum = 0; - for (int r=-requestedRadius; r<=requestedRadius; ++r) { - float w = qgfx_gaussian(r, deviation); - wSum += w; - fragShader += " result += float("; - fragShader += QByteArray::number(w); - fragShader += ") * texture(source, qt_TexCoord0 + pixelStep * float("; - fragShader += QByteArray::number(r); - fragShader += "))"; - if (alphaOnly) - fragShader += ".a"; - fragShader += ";\n"; - } - fragShader += " const float wSum = float("; - fragShader += QByteArray::number(wSum); - fragShader += ");\n" - " fragColor = "; - if (alphaOnly) - fragShader += "mix(vec4(0), color, clamp((result / wSum) / thickness, 0.0, 1.0)) * qt_Opacity;\n"; - else - fragShader += "(qt_Opacity / wSum) * result;\n"; - fragShader += "}\n"; - - return fragShader; -} - -QVariantMap QGfxShaderBuilder::gaussianBlur(const QJSValue ¶meters) -{ - int requestedRadius = qMax(0.0, parameters.property(QStringLiteral("radius")).toNumber()); - qreal deviation = parameters.property(QStringLiteral("deviation")).toNumber(); - bool masked = parameters.property(QStringLiteral("masked")).toBool(); - bool alphaOnly = parameters.property(QStringLiteral("alphaOnly")).toBool(); - - int requestedSamples = requestedRadius * 2 + 1; - int samples = 1 + requestedSamples / 2; - int radius = requestedSamples / 4; - bool fallback = parameters.property(QStringLiteral("fallback")).toBool(); - - QVariantMap result; - - if (samples > m_maxBlurSamples || masked || fallback) { - - if (m_coreProfile) { - result[QStringLiteral("fragmentShader")] = qgfx_fallbackCoreFragmentShader(requestedRadius, deviation, masked, alphaOnly); - result[QStringLiteral("vertexShader")] = qgfx_fallbackCoreVertexShader(); - } else { - result[QStringLiteral("fragmentShader")] = qgfx_fallbackFragmentShader(requestedRadius, deviation, masked, alphaOnly); - result[QStringLiteral("vertexShader")] = qgfx_fallbackVertexShader(); - } - return result; - } - - QVarLengthArray<QGfxGaussSample, 64> p(samples); - qgfx_buildGaussSamplePoints(p.data(), samples, radius, deviation); - - if (m_coreProfile) { - result[QStringLiteral("fragmentShader")] = qgfx_gaussianFragmentCoreShader(p.data(), samples, alphaOnly); - result[QStringLiteral("vertexShader")] = qgfx_gaussianVertexCoreShader(p.data(), samples); - } else { - result[QStringLiteral("fragmentShader")] = qgfx_gaussianFragmentShader(p.data(), samples, alphaOnly); - result[QStringLiteral("vertexShader")] = qgfx_gaussianVertexShader(p.data(), samples); - } - return result; -} - |