summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@jollamobile.com>2015-01-19 20:05:22 +0100
committerGunnar Sletta <gunnar@sletta.org>2015-03-13 09:07:20 +0000
commit81a27fa85f5656d3784d7d4fdcd99d3c66ebb446 (patch)
treef9128276b78a4684723b73b4ecb68f20700667aa
parent64fdf70c2249c20895101a7428a9828825a00542 (diff)
downloadqtgraphicaleffects-81a27fa85f5656d3784d7d4fdcd99d3c66ebb446.tar.gz
Improve Gaussian Blur.
The shader source generation has been moved to C++ and the following improvements have been made to the algorithm: - Rely on linear sampling to roughly halve the number of samples required to perform blurring, while still mathmatically accurate. - Avoid dependent texture reads by calculating the sample positions in the vertex shader. This only works if the vec2 is used in texture2D() directly from the varying without any arithmetic and no swizzle mask applied. The fragment shader can then in many cases prefetch the texture value. - Implement a fallback shader which is used when samples exceed the maximum number of varyings. The old implementation supported 32 samples. The new one switches to the fallback when the required samples / 2 exceeds the number of available varying registers on the GPU. The fallback shader is equivalent to the old code performance wise, but supports an arbitrary high number of samples. [ChangeLog] Gaussian Blur has a new implementation. Faster for smaller kernels, similar for larger kernels but allows arbitrarily large kernels. The fast version will support at least 15x15 kernels on OpenGL ES and 59x59 kernels on Desktop GL. GaussianBlur.deviation is now a very costly parameter to change. Change-Id: I1ac44633ec6b3b667ebfab2211fa53e706804787 Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
-rw-r--r--src/effects/GaussianBlur.qml147
-rw-r--r--src/effects/private/plugin.cpp7
-rw-r--r--src/effects/private/private.pro4
-rw-r--r--src/effects/private/qgfxshaderbuilder.cpp378
-rw-r--r--src/effects/private/qgfxshaderbuilder_p.h59
-rw-r--r--src/effects/private/qgfxsourceproxy.cpp11
6 files changed, 547 insertions, 59 deletions
diff --git a/src/effects/GaussianBlur.qml b/src/effects/GaussianBlur.qml
index 8c3f0e6..72a55f3 100644
--- a/src/effects/GaussianBlur.qml
+++ b/src/effects/GaussianBlur.qml
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
+** Copyright (C) 2015 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Graphical Effects module.
@@ -39,6 +40,7 @@
****************************************************************************/
import QtQuick 2.0
+import QtQuick.Window 2.1
import QtGraphicalEffects.private 1.0
/*!
@@ -68,9 +70,13 @@ import QtGraphicalEffects.private 1.0
The following example shows how to apply the effect.
\snippet GaussianBlur-example.qml example
+ Performing blur live is a costly operation. Fullscreen gaussian blur
+ with even a moderate number of samples will only run at 60 fps on highend
+ graphics hardware.
+
*/
Item {
- id: rootItem
+ id: root
/*!
This property defines the source item that is going to be blurred.
@@ -92,6 +98,8 @@ Item {
The value ranges from 0.0 (no blur) to inf. By default, the property is
set to \c 0.0 (no blur).
+ By default, this property is set to \c 4.
+
\table
\header
\li Output examples with different radius values
@@ -116,7 +124,7 @@ Item {
\endtable
*/
- property real radius: 0.0
+ property real radius: Math.floor(samples / 2);
/*!
This property defines how many samples are taken per pixel when blur
@@ -124,16 +132,16 @@ Item {
to render.
Ideally, this value should be twice as large as the highest required
- radius value, for example, if the radius is animated between 0.0 and
- 4.0, samples should be set to 8.
+ radius value plus 1, for example, if the radius is animated between 0.0
+ and 4.0, samples should be set to 9.
- The value ranges from 0 to 32. By default, the property is set to \c 0.
+ By default, the property is set to \c 9.
- This property is not intended to be animated. Changing this property may
+ \warn This property is not intended to be animated. Changing this property may
cause the underlying OpenGL shaders to be recompiled.
*/
- property int samples: 0
+ property int samples: 9
/*!
This property is a parameter to the gaussian function that is used when
@@ -220,6 +228,10 @@ Item {
\li \l deviation: 2.7
\endtable
+ \warn This property is not intended to be animated. Changing this property may
+ cause the underlying OpenGL shaders to be recompiled.
+
+
*/
property bool transparentBorder: false
@@ -238,58 +250,95 @@ Item {
*/
property bool cached: false
+
+ // private members...
+ property int _paddedTexWidth: transparentBorder ? width + 2 * _kernelRadius: width;
+ property int _paddedTexHeight: transparentBorder ? height + 2 * _kernelRadius: height;
+ property int _kernelRadius: Math.max(0, samples / 2);
+ property int _kernelSize: _kernelRadius * 2 + 1;
+ property int _dpr: Screen.devicePixelRatio;
+ property bool _alphaOnly: false;
+
+ property alias _output: sourceProxy.output;
+ property alias _outputRect: sourceProxy.sourceRect;
+ property alias _color: verticalBlur.color;
+ property real _thickness: 0;
+
+ onSamplesChanged: rebuildShaders();
+ on_KernelSizeChanged: rebuildShaders();
+ onDeviationChanged: rebuildShaders();
+ on_DprChanged: rebuildShaders();
+ Component.onCompleted: rebuildShaders();
+
+ function rebuildShaders() {
+ if (samples < 1)
+ return;
+
+ var params = {
+ radius: _kernelRadius,
+ deviation: deviation,
+ alphaOnly: root._alphaOnly,
+ masked: false
+ }
+ var shaders = ShaderBuilder.gaussianBlur(params);
+ horizontalBlur.fragmentShader = shaders.fragmentShader;
+ horizontalBlur.vertexShader = shaders.vertexShader;
+ }
+
SourceProxy {
id: sourceProxy
- input: rootItem.source
- sourceRect: rootItem.transparentBorder ? Qt.rect(-1, -1, parent.width + 2.0, parent.height + 2.0) : Qt.rect(0, 0, 0, 0)
+ interpolation: SourceProxy.LinearInterpolation
+ input: root.source
+ sourceRect: root.transparentBorder
+ ? Qt.rect(-root._kernelRadius, 0, root._paddedTexWidth, parent.height)
+ : Qt.rect(0, 0, 0, 0)
+ }
+
+ ShaderEffect {
+ id: horizontalBlur
+ width: root.transparentBorder ? root._paddedTexWidth : root.width
+ height: root.height;
+ property Item source: sourceProxy.output;
+ property real deviation: root.deviation
+ property real radius: root._kernelRadius
+ property real spread: root.radius / root._kernelRadius;
+ property var step: Qt.vector2d(1 / (root._paddedTexWidth * root._dpr), 0);
+ property color color: "white"
+ property real thickness: Math.max(0, Math.min(0.98, 1 - root._thickness * 0.98));
+ layer.enabled: true
+ layer.smooth: true
+ layer.sourceRect: root.transparentBorder
+ ? Qt.rect(0, -root._kernelRadius, width, root._paddedTexHeight)
+ : Qt.rect(0, 0, 0, 0)
+ visible: false
+ blending: false
+ }
+
+ ShaderEffect {
+ id: verticalBlur
+ x: transparentBorder ? -root._kernelRadius : 0
+ y: x;
+ width: root.transparentBorder ? root._paddedTexWidth: root.width
+ height: root.transparentBorder ? root._paddedTexHeight : root.height;
+ fragmentShader: horizontalBlur.fragmentShader
+ vertexShader: horizontalBlur.vertexShader
+ property Item source: horizontalBlur
+ property real deviation: horizontalBlur.deviation
+ property real radius: horizontalBlur.radius
+ property real spread: horizontalBlur.spread
+ property var step: Qt.vector2d(0, 1 / (root._paddedTexHeight * root._dpr));
+ property color color: "black"
+ property real thickness: horizontalBlur.thickness;
+ visible: true;
}
ShaderEffectSource {
id: cacheItem
anchors.fill: verticalBlur
- visible: rootItem.cached
+ visible: root.cached
smooth: true
sourceItem: verticalBlur
- live: true
hideSource: visible
}
- GaussianDirectionalBlur {
- id: verticalBlur
- x: transparentBorder ? -maximumRadius - 1 : 0
- y: transparentBorder ? -maximumRadius - 1 : 0
- width: horizontalBlur.width
- height: horizontalBlur.height
-
- horizontalStep: 0.0
- verticalStep: 1.0 / parent.height
-
- source: ShaderEffectSource {
- id: horizontalBlurSource
- sourceItem: horizontalBlur
- hideSource: true
- visible: false
- smooth: true
- }
-
- deviation: rootItem.deviation
- radius: rootItem.radius
- maximumRadius: rootItem.samples * 0.5
- transparentBorder: rootItem.transparentBorder
- }
-
- GaussianDirectionalBlur {
- id: horizontalBlur
- width: transparentBorder ? parent.width + 2 * maximumRadius + 2 : parent.width
- height: transparentBorder ? parent.height + 2 * maximumRadius + 2 : parent.height
-
- horizontalStep: 1.0 / parent.width
- verticalStep: 0.0
-
- source: sourceProxy.output
- deviation: rootItem.deviation
- radius: rootItem.radius
- maximumRadius: rootItem.samples / 2.0
- transparentBorder: rootItem.transparentBorder
- }
}
diff --git a/src/effects/private/plugin.cpp b/src/effects/private/plugin.cpp
index 6cd9047..9fd9ca2 100644
--- a/src/effects/private/plugin.cpp
+++ b/src/effects/private/plugin.cpp
@@ -36,9 +36,15 @@
#include <QtQml/qqmlengine.h>
#include "qgfxsourceproxy_p.h"
+#include "qgfxshaderbuilder_p.h"
QT_BEGIN_NAMESPACE
+static QObject *qgfxshaderbuilder_provider(QQmlEngine *, QJSEngine *)
+{
+ return new QGfxShaderBuilder();
+}
+
class QtGraphicalEffectsPlugin : public QQmlExtensionPlugin
{
Q_OBJECT
@@ -49,6 +55,7 @@ public:
{
Q_ASSERT(QByteArray(uri) == QByteArrayLiteral("QtGraphicalEffects.private"));
qmlRegisterType<QGfxSourceProxy>(uri, 1, 0, "SourceProxy");
+ qmlRegisterSingletonType<QGfxShaderBuilder>(uri, 1, 0, "ShaderBuilder", qgfxshaderbuilder_provider);
}
};
diff --git a/src/effects/private/private.pro b/src/effects/private/private.pro
index 9346b41..9ae7e80 100644
--- a/src/effects/private/private.pro
+++ b/src/effects/private/private.pro
@@ -7,10 +7,12 @@ QT += quick qml
QT += quick-private gui-private core-private qml-private
SOURCES += plugin.cpp \
+ qgfxshaderbuilder.cpp \
qgfxsourceproxy.cpp
HEADERS += \
- qgfxsourceproxy_p.h
+ qgfxsourceproxy_p.h \
+ qgfxshaderbuilder_p.h
QML_FILES = \
FastGlow.qml \
diff --git a/src/effects/private/qgfxshaderbuilder.cpp b/src/effects/private/qgfxshaderbuilder.cpp
new file mode 100644
index 0000000..9a109b0
--- /dev/null
+++ b/src/effects/private/qgfxshaderbuilder.cpp
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $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 0x8DFC
+#endif
+
+QGfxShaderBuilder::QGfxShaderBuilder()
+{
+ // 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;
+ context.create();
+ 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();
+ 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;
+ } else {
+ int floats;
+ gl->glGetIntegerv(GL_MAX_VARYING_FLOATS, &floats);
+ m_maxBlurSamples = floats / 2.0;
+ }
+ 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_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 step;\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 * step * 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;
+}
+
+
+QVariantMap QGfxShaderBuilder::gaussianBlur(const QJSValue &parameters)
+{
+ 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;
+
+ QVariantMap result;
+
+ if (samples > m_maxBlurSamples || masked) {
+ 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 step;\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 = step * 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 = ";
+ 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";
+ result[QStringLiteral("fragmentShader")] = fragShader;
+
+ result[QStringLiteral("vertexShader")] =
+ "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";
+ return result;
+ }
+
+ QVarLengthArray<QGfxGaussSample, 64> p(samples);
+ qgfx_buildGaussSamplePoints(p.data(), samples, radius, deviation);
+
+ result[QStringLiteral("fragmentShader")] = qgfx_gaussianFragmentShader(p.data(), samples, alphaOnly);
+ result[QStringLiteral("vertexShader")] = qgfx_gaussianVertexShader(p.data(), samples);
+
+ return result;
+}
+
diff --git a/src/effects/private/qgfxshaderbuilder_p.h b/src/effects/private/qgfxshaderbuilder_p.h
new file mode 100644
index 0000000..3ec993d
--- /dev/null
+++ b/src/effects/private/qgfxshaderbuilder_p.h
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 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:LGPL21$
+** 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 http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://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 2.1 or version 3 as published by the Free
+** Software Foundation and appearing in the file LICENSE.LGPLv21 and
+** LICENSE.LGPLv3 included in the packaging of this file. Please review the
+** following information to ensure the GNU Lesser General Public License
+** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QGFXSHADERBUILDER_P_H
+#define QGFXSHADERBUILDER_P_H
+
+#include <QtCore/QObject>
+#include <QtCore/QVariantMap>
+
+#include <QtQml/QJSValue>
+
+QT_BEGIN_NAMESPACE
+
+class QGfxShaderBuilder : public QObject
+{
+ Q_OBJECT
+
+public:
+ QGfxShaderBuilder();
+
+ Q_INVOKABLE QVariantMap gaussianBlur(const QJSValue &parameters);
+
+private:
+ int m_maxBlurSamples;
+};
+
+QT_END_NAMESPACE
+
+#endif // QGFXSHADERBUILDER_P_H
diff --git a/src/effects/private/qgfxsourceproxy.cpp b/src/effects/private/qgfxsourceproxy.cpp
index cdc6fc1..8f841f2 100644
--- a/src/effects/private/qgfxsourceproxy.cpp
+++ b/src/effects/private/qgfxsourceproxy.cpp
@@ -102,13 +102,12 @@ void QGfxSourceProxy::useProxy()
void QGfxSourceProxy::updatePolish()
{
- QQuickItemPrivate *d = QQuickItemPrivate::get(m_input);
-
if (m_input == 0) {
setOutput(0);
return;
}
+ QQuickItemPrivate *d = QQuickItemPrivate::get(m_input);
QQuickImage *image = qobject_cast<QQuickImage *>(m_input);
QQuickShaderEffectSource *shaderSource = qobject_cast<QQuickShaderEffectSource *>(m_input);
bool layered = d->extra.isAllocated() && d->extra->transparentForPositioner;
@@ -117,14 +116,8 @@ void QGfxSourceProxy::updatePolish()
if (layered) {
shaderSource->setSourceRect(m_sourceRect);
shaderSource->setSmooth(m_interpolation != NearestInterpolation);
- setOutput(m_input);
- } else if ((shaderSource->sourceRect() != m_sourceRect)
- || (m_interpolation == LinearInterpolation && !shaderSource->smooth())
- || (m_interpolation == NearestInterpolation && shaderSource->smooth())) {
- useProxy();
- } else {
- setOutput(m_input);
}
+ setOutput(m_input);
} else if (image && image->fillMode() == QQuickImage::Stretch && m_input->childItems().size() == 0) {
// item is an image with default tiling, use directly