summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Rødal <sroedal@trolltech.com>2009-10-28 13:44:10 (GMT)
committerSamuel Rødal <sroedal@trolltech.com>2009-10-29 09:47:14 (GMT)
commit7d5b560f71e0f11c20b7ebef11f3095e760ca32c (patch)
treeac8397e1ff54344a5331da055c5707a90851ce68
parentf5c553078b7381c3dff7d0bd6b9990a7acf86abb (diff)
downloadQt-7d5b560f71e0f11c20b7ebef11f3095e760ca32c.zip
Qt-7d5b560f71e0f11c20b7ebef11f3095e760ca32c.tar.gz
Qt-7d5b560f71e0f11c20b7ebef11f3095e760ca32c.tar.bz2
Added some optimizations to the blur and drop shadow GL filters.
* Use ExpandToTransparentBorderPadMode since we can use GL_CLAMP_TO_EDGE to clamp to the texture. * Shrink the bounding rects reported by the blur and drop shadow filters (expanding by 2 * radius isn't needed). * Use a single-pass blur for radii <= 3 to avoid the overhead of rendering to an FBO. * Made the fast blur setting generate filters for only a predefined set of radii, and then use the actual blur radius to spread the sample points outwards. * Optimized the generated program to rely less on temporary variables, as those seemed to not be handled very well by certain GLSL compilers. Reviewed-by: Gunnar Sletta
-rw-r--r--src/gui/effects/qgraphicseffect.cpp16
-rw-r--r--src/gui/effects/qgraphicseffect_p.h8
-rw-r--r--src/gui/graphicsview/qgraphicsitem.cpp3
-rw-r--r--src/gui/image/qpixmapfilter.cpp13
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp2
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h16
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp8
-rw-r--r--src/opengl/qglpixmapfilter.cpp436
8 files changed, 307 insertions, 195 deletions
diff --git a/src/gui/effects/qgraphicseffect.cpp b/src/gui/effects/qgraphicseffect.cpp
index 9ed003c..3a6bab5 100644
--- a/src/gui/effects/qgraphicseffect.cpp
+++ b/src/gui/effects/qgraphicseffect.cpp
@@ -101,6 +101,7 @@
#include <QtGui/qimage.h>
#include <QtGui/qpainter.h>
+#include <QtGui/qpaintengine.h>
#include <QtCore/qrect.h>
#include <QtCore/qdebug.h>
#include <private/qdrawhelper_p.h>
@@ -260,12 +261,13 @@ QPixmap QGraphicsEffectSource::pixmap(Qt::CoordinateSystem system, QPoint *offse
}
QPixmap pm;
- if (d->m_cachedSystem == system)
+ if (d->m_cachedSystem == system && d->m_cachedMode == mode)
QPixmapCache::find(d->m_cacheKey, &pm);
if (pm.isNull()) {
pm = d->pixmap(system, &d->m_cachedOffset, mode);
d->m_cachedSystem = system;
+ d->m_cachedMode = mode;
d->invalidateCache();
d->m_cacheKey = QPixmapCache::insert(pm);
@@ -708,9 +710,13 @@ void QGraphicsBlurEffect::draw(QPainter *painter, QGraphicsEffectSource *source)
return;
}
+ QGraphicsEffectSource::PixmapPadMode mode = QGraphicsEffectSource::ExpandToEffectRectPadMode;
+ if (painter->paintEngine()->type() == QPaintEngine::OpenGL2)
+ mode = QGraphicsEffectSource::ExpandToTransparentBorderPadMode;
+
// Draw pixmap in device coordinates to avoid pixmap scaling.
QPoint offset;
- const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+ const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset, mode);
QTransform restoreTransform = painter->worldTransform();
painter->setWorldTransform(QTransform());
d->filter->draw(painter, offset, pixmap);
@@ -893,9 +899,13 @@ void QGraphicsDropShadowEffect::draw(QPainter *painter, QGraphicsEffectSource *s
return;
}
+ QGraphicsEffectSource::PixmapPadMode mode = QGraphicsEffectSource::ExpandToEffectRectPadMode;
+ if (painter->paintEngine()->type() == QPaintEngine::OpenGL2)
+ mode = QGraphicsEffectSource::ExpandToTransparentBorderPadMode;
+
// Draw pixmap in device coordinates to avoid pixmap scaling.
QPoint offset;
- const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset);
+ const QPixmap pixmap = source->pixmap(Qt::DeviceCoordinates, &offset, mode);
QTransform restoreTransform = painter->worldTransform();
painter->setWorldTransform(QTransform());
d->filter->draw(painter, offset, pixmap);
diff --git a/src/gui/effects/qgraphicseffect_p.h b/src/gui/effects/qgraphicseffect_p.h
index dff84a1..1ed7103 100644
--- a/src/gui/effects/qgraphicseffect_p.h
+++ b/src/gui/effects/qgraphicseffect_p.h
@@ -66,7 +66,12 @@ class QGraphicsEffectSourcePrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QGraphicsEffectSource)
public:
- QGraphicsEffectSourcePrivate() : QObjectPrivate() {}
+ QGraphicsEffectSourcePrivate()
+ : QObjectPrivate()
+ , m_cachedSystem(Qt::DeviceCoordinates)
+ , m_cachedMode(QGraphicsEffectSource::ExpandToTransparentBorderPadMode)
+ {}
+
virtual ~QGraphicsEffectSourcePrivate() { invalidateCache(); }
virtual void detach() = 0;
virtual QRectF boundingRect(Qt::CoordinateSystem system) const = 0;
@@ -88,6 +93,7 @@ public:
private:
mutable Qt::CoordinateSystem m_cachedSystem;
+ mutable QGraphicsEffectSource::PixmapPadMode m_cachedMode;
mutable QPoint m_cachedOffset;
mutable QPixmapCache::Key m_cacheKey;
};
diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp
index 7d9390c..738c6e3 100644
--- a/src/gui/graphicsview/qgraphicsitem.cpp
+++ b/src/gui/graphicsview/qgraphicsitem.cpp
@@ -10703,7 +10703,8 @@ QPixmap QGraphicsItemEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QP
if (mode == QGraphicsEffectSource::ExpandToEffectRectPadMode) {
effectRect = item->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect();
} else if (mode == QGraphicsEffectSource::ExpandToTransparentBorderPadMode) {
- effectRect = sourceRect.adjusted(-1, -1, 1, 1).toAlignedRect();
+ // adjust by 1.5 to account for cosmetic pens
+ effectRect = sourceRect.adjusted(-1.5, -1.5, 1.5, 1.5).toAlignedRect();
} else {
effectRect = sourceRect.toAlignedRect();
}
diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp
index f9ac79f..d0de03e 100644
--- a/src/gui/image/qpixmapfilter.cpp
+++ b/src/gui/image/qpixmapfilter.cpp
@@ -584,7 +584,7 @@ Qt::RenderHint QPixmapBlurFilter::blurHint() const
QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const
{
Q_D(const QPixmapBlurFilter);
- const qreal delta = d->radius * 2;
+ const qreal delta = d->radius + 1;
return rect.adjusted(-delta, -delta, delta, delta);
}
@@ -1057,14 +1057,9 @@ void QPixmapDropShadowFilter::setOffset(const QPointF &offset)
QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const
{
Q_D(const QPixmapDropShadowFilter);
-
- const qreal delta = qreal(d->radius * 2);
- qreal x1 = qMin(rect.left(), rect.left() + d->offset.x() - delta);
- qreal y1 = qMin(rect.top(), rect.top() + d->offset.y() - delta);
- qreal x2 = qMax(rect.right(), rect.right() + d->offset.x() + delta);
- qreal y2 = qMax(rect.bottom(), rect.bottom() + d->offset.y() + delta);
-
- return QRectF(x1, y1, x2 - x1, y2 - y1);
+ qreal delta = d->radius + 1;
+ return rect.adjusted(-2, -2, 2, 2).united(
+ rect.translated(d->offset).adjusted(-delta, -delta, delta, delta));
}
/*!
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index e22303d..af9306f 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -476,7 +476,7 @@ bool QGLEngineShaderManager::useCorrectShaderProg()
return false;
bool useCustomSrc = customSrcStage != 0;
- if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc) {
+ if (useCustomSrc && srcPixelType != QGLEngineShaderManager::ImageSrc && srcPixelType != Qt::TexturePattern) {
useCustomSrc = false;
qWarning("QGLEngineShaderManager - Ignoring custom shader stage for non image src");
}
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
index 3eef808..2407979 100644
--- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -258,7 +258,7 @@ static const char* const qglslPositionWithTextureBrushVertexShader = "\
uniform mediump vec2 halfViewportSize; \
uniform highp vec2 invertedTextureSize; \
uniform highp mat3 brushTransform; \
- varying highp vec2 brushTextureCoords; \
+ varying highp vec2 textureCoords; \
void setPosition(void) { \
gl_Position = pmvMatrix * vertexCoordsArray;\
gl_Position.xy = gl_Position.xy / gl_Position.w; \
@@ -267,7 +267,7 @@ static const char* const qglslPositionWithTextureBrushVertexShader = "\
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
gl_Position.w = invertedHTexCoordsZ; \
- brushTextureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \
+ textureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \
}";
static const char* const qglslAffinePositionWithTextureBrushVertexShader
@@ -278,26 +278,26 @@ static const char* const qglslAffinePositionWithTextureBrushVertexShader
// we emulate GL_REPEAT by only taking the fractional part of the texture coords.
// TODO: Special case POT textures which don't need this emulation
static const char* const qglslTextureBrushSrcFragmentShader = "\
- varying highp vec2 brushTextureCoords; \
+ varying highp vec2 textureCoords; \
uniform lowp sampler2D brushTexture; \
lowp vec4 srcPixel() { \
- return texture2D(brushTexture, fract(brushTextureCoords)); \
+ return texture2D(brushTexture, fract(textureCoords)); \
}";
#else
static const char* const qglslTextureBrushSrcFragmentShader = "\
- varying highp vec2 brushTextureCoords; \
+ varying highp vec2 textureCoords; \
uniform lowp sampler2D brushTexture; \
lowp vec4 srcPixel() { \
- return texture2D(brushTexture, brushTextureCoords); \
+ return texture2D(brushTexture, textureCoords); \
}";
#endif
static const char* const qglslTextureBrushSrcWithPatternFragmentShader = "\
- varying highp vec2 brushTextureCoords; \
+ varying highp vec2 textureCoords; \
uniform lowp vec4 patternColor; \
uniform lowp sampler2D brushTexture; \
lowp vec4 srcPixel() { \
- return patternColor * (1.0 - texture2D(brushTexture, brushTextureCoords).r); \
+ return patternColor * (1.0 - texture2D(brushTexture, textureCoords).r); \
}";
// Solid Fill Brush
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index b70810d..a9744b3 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -535,7 +535,7 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::PatternColor), col);
}
- QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 * textureInvertedY / texPixmap.height());
+ QSizeF invertedTextureSize(1.0 / texPixmap.width(), 1.0 / texPixmap.height());
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::InvertedTextureSize), invertedTextureSize);
QVector2D halfViewportSize(width*0.5, height*0.5);
@@ -550,7 +550,11 @@ void QGL2PaintEngineExPrivate::updateBrushUniforms()
QTransform translate(1, 0, 0, 1, -translationPoint.x(), -translationPoint.y());
QTransform gl_to_qt(1, 0, 0, -1, 0, height);
- QTransform inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
+ QTransform inv_matrix;
+ if (style == Qt::TexturePattern && textureInvertedY == -1)
+ inv_matrix = gl_to_qt * (QTransform(1, 0, 0, -1, 0, currentBrush->texture().height()) * brushQTransform * matrix).inverted() * translate;
+ else
+ inv_matrix = gl_to_qt * (brushQTransform * matrix).inverted() * translate;
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTransform), inv_matrix);
shaderManager->currentProgram()->setUniformValue(location(QGLEngineShaderManager::BrushTexture), QT_BRUSH_TEXTURE_UNIT);
diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp
index 656957d..e381a5d 100644
--- a/src/opengl/qglpixmapfilter.cpp
+++ b/src/opengl/qglpixmapfilter.cpp
@@ -104,7 +104,7 @@ public:
void setUniforms(QGLShaderProgram *program);
- static QByteArray generateGaussianShader(int radius, bool dropShadow = false);
+ static QByteArray generateGaussianShader(int radius, bool singlePass = false, bool dropShadow = false);
protected:
bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
@@ -113,6 +113,7 @@ private:
mutable QSize m_textureSize;
mutable bool m_horizontalBlur;
+ mutable bool m_singlePass;
mutable bool m_haveCached;
mutable int m_cachedRadius;
@@ -132,6 +133,7 @@ protected:
private:
mutable QSize m_textureSize;
mutable bool m_horizontalBlur;
+ mutable bool m_singlePass;
mutable bool m_haveCached;
mutable int m_cachedRadius;
@@ -298,123 +300,155 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *painter, const QPointF &pos
return true;
}
-static const char *qt_gl_blur_filter_fast =
- "const int samples = 9;"
- "uniform mediump vec2 delta;"
- "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {"
- " mediump vec4 color = vec4(0.0, 0.0, 0.0, 0.0);"
- " mediump float offset = (float(samples) - 1.0) / 2.0;"
- " for (int i = 0; i < samples; i++) {"
- " mediump vec2 coord = srcCoords + delta * (offset - float(i)) / offset;"
- " color += texture2D(src, coord);"
- " }"
- " return color * (1.0 / float(samples));"
- "}";
-
-static const char *qt_gl_drop_shadow_filter_fast =
- "const int samples = 9;"
- "uniform mediump vec2 delta;"
- "uniform mediump vec4 shadowColor;"
- "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {"
- " mediump vec4 color = vec4(0.0, 0.0, 0.0, 0.0);"
- " mediump float offset = (float(samples) - 1.0) / 2.0;"
- " for (int i = 0; i < samples; i++) {"
- " mediump vec2 coord = srcCoords + delta * (offset - float(i)) / offset;"
- " color += texture2D(src, coord).a * shadowColor;"
- " }"
- " return color * (1.0 / float(samples));"
- "}";
+static const char *qt_gl_texture_sampling_helper =
+ "lowp float texture2DAlpha(lowp sampler2D src, highp vec2 srcCoords) {\n"
+ " return texture2D(src, srcCoords).a;\n"
+ "}\n";
+
+static const char *qt_gl_clamped_texture_sampling_helper =
+ "highp vec4 texture_dimensions;\n" // x = width, y = height, z = 0.5/width, w = 0.5/height
+ "lowp float clampedTexture2DAlpha(lowp sampler2D src, highp vec2 srcCoords) {\n"
+ " highp vec2 clampedCoords = clamp(srcCoords, texture_dimensions.zw, vec2(1.0) - texture_dimensions.zw);\n"
+ " highp vec2 t = clamp(min(srcCoords, vec2(1.0) - srcCoords) * srcDim + 0.5, 0.0, 1.0);\n"
+ " return texture2D(src, clampedCoords).a * t.x * t.y;\n"
+ "}\n"
+ "lowp vec4 clampedTexture2D(lowp sampler2D src, highp vec2 srcCoords) {\n"
+ " highp vec2 clampedCoords = clamp(srcCoords, texture_dimensions.zw, vec2(1.0) - texture_dimensions.zw);\n"
+ " highp vec2 t = clamp(min(srcCoords, vec2(1.0) - srcCoords) * srcDim + 0.5, 0.0, 1.0);\n"
+ " return texture2D(src, clampedCoords) * t.x * t.y;\n"
+ "}\n";
+
+static QByteArray qt_gl_convertToClamped(const QByteArray &source)
+{
+ QByteArray result;
+ result.append(qt_gl_clamped_texture_sampling_helper);
+ result.append(QByteArray(source).replace("texture2DAlpha", "clampedTexture2DAlpha")
+ .replace("texture2D", "clampedTexture2D"));
+ return result;
+}
QGLPixmapBlurFilter::QGLPixmapBlurFilter(Qt::RenderHint hint)
: m_haveCached(false)
- , m_cachedRadius(5)
+ , m_cachedRadius(0)
, m_hint(hint)
{
- if (hint == Qt::PerformanceHint) {
- QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this);
- filter->setSource(qt_gl_blur_filter_fast);
- m_haveCached = true;
- }
}
bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const
{
QGLPixmapBlurFilter *filter = const_cast<QGLPixmapBlurFilter *>(this);
- int radius = qRound(this->radius());
- if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) {
- // Only regenerate the shader from source if parameters have changed.
- m_haveCached = true;
- m_cachedRadius = radius;
- filter->setSource(generateGaussianShader(radius));
+ int actualRadius = qRound(radius());
+ int filterRadius = actualRadius;
+ int fastRadii[] = { 1, 2, 3, 5, 8, 15, 25 };
+ if (m_hint == Qt::PerformanceHint) {
+ uint i = 0;
+ for (; i < (sizeof(fastRadii)/sizeof(*fastRadii))-1; ++i) {
+ if (fastRadii[i+1] > filterRadius)
+ break;
+ }
+ filterRadius = fastRadii[i];
}
- QGLFramebufferObjectFormat format;
- format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
- QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format);
+ m_singlePass = filterRadius <= 3;
- if (!fbo)
- return false;
+ if (!m_haveCached || filterRadius != m_cachedRadius) {
+ // Only regenerate the shader from source if parameters have changed.
+ m_haveCached = true;
+ m_cachedRadius = filterRadius;
+ QByteArray source = generateGaussianShader(filterRadius, m_singlePass);
+ filter->setSource(source);
+ }
- glBindTexture(GL_TEXTURE_2D, fbo->texture());
+ QRect targetRect = QRectF(src.rect()).translated(pos).adjusted(-actualRadius, -actualRadius, actualRadius, actualRadius).toAlignedRect();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glBindTexture(GL_TEXTURE_2D, 0);
+ if (m_singlePass) {
+ // prepare for updateUniforms
+ m_textureSize = src.size();
- // prepare for updateUniforms
- m_textureSize = src.size();
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ QBrush pixmapBrush = src;
+ pixmapBrush.setTransform(QTransform::fromTranslate(pos.x(), pos.y()));
+ painter->fillRect(targetRect, pixmapBrush);
+ filter->removeFromPainter(painter);
+ } else {
+ QGLFramebufferObjectFormat format;
+ format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
+ QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(targetRect.size(), format);
- // horizontal pass, to pixmap
- m_horizontalBlur = true;
+ if (!fbo)
+ return false;
- QPainter fboPainter(fbo);
+ glBindTexture(GL_TEXTURE_2D, fbo->texture());
- if (src.hasAlphaChannel()) {
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
- }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_2D, 0);
- // ensure GL_LINEAR filtering is used
- fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
- filter->setOnPainter(&fboPainter);
- fboPainter.drawPixmap(0, 0, src);
- filter->removeFromPainter(&fboPainter);
- fboPainter.end();
+ // prepare for updateUniforms
+ m_textureSize = src.size();
- QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+ // horizontal pass, to pixmap
+ m_horizontalBlur = true;
- // vertical pass, to painter
- m_horizontalBlur = false;
+ QPainter fboPainter(fbo);
- painter->save();
- // ensure GL_LINEAR filtering is used
- painter->setRenderHint(QPainter::SmoothPixmapTransform);
- filter->setOnPainter(painter);
- engine->drawTexture(src.rect().translated(pos.x(), pos.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height()));
- filter->removeFromPainter(painter);
- painter->restore();
+ if (src.hasAlphaChannel()) {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
- qgl_fbo_pool()->release(fbo);
+ // ensure GL_LINEAR filtering is used
+ fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(&fboPainter);
+ QBrush pixmapBrush = src;
+ pixmapBrush.setTransform(QTransform::fromTranslate(actualRadius, actualRadius));
+ fboPainter.fillRect(QRect(0, 0, targetRect.width(), targetRect.height()), pixmapBrush);
+ filter->removeFromPainter(&fboPainter);
+ fboPainter.end();
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+
+ // vertical pass, to painter
+ m_horizontalBlur = false;
+ m_textureSize = fbo->size();
+
+ painter->save();
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ engine->drawTexture(targetRect, fbo->texture(), fbo->size(), QRect(QPoint(), targetRect.size()).translated(0, fbo->height() - targetRect.height()));
+ filter->removeFromPainter(painter);
+ painter->restore();
+
+ qgl_fbo_pool()->release(fbo);
+ }
return true;
}
void QGLPixmapBlurFilter::setUniforms(QGLShaderProgram *program)
{
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
if (m_hint == Qt::QualityHint) {
- if (m_horizontalBlur)
+ if (m_singlePass)
+ program->setUniformValue("delta", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height());
+ else if (m_horizontalBlur)
program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0);
else
program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height());
} else {
- // 1.4 is chosen to most closely match the blurriness of the gaussian blur
- // at low radii
- qreal blur = radius() / 1.4f;
+ qreal blur = radius() / qreal(m_cachedRadius);
- if (m_horizontalBlur)
+ if (m_singlePass)
+ program->setUniformValue("delta", blur / m_textureSize.width(), blur / m_textureSize.height());
+ else if (m_horizontalBlur)
program->setUniformValue("delta", blur / m_textureSize.width(), 0.0);
else
program->setUniformValue("delta", 0.0, blur / m_textureSize.height());
@@ -426,12 +460,21 @@ static inline qreal gaussian(qreal dx, qreal sigma)
return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma);
}
-QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShadow)
+QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool singlePass, bool dropShadow)
{
Q_ASSERT(radius >= 1);
+ radius = qMin(127, radius);
+
+ static QCache<uint, QByteArray> shaderSourceCache;
+ uint key = radius | (int(singlePass) << 7) | (int(dropShadow) << 8);
+ QByteArray *cached = shaderSourceCache.object(key);
+ if (cached)
+ return *cached;
+
QByteArray source;
source.reserve(1000);
+ source.append(qt_gl_texture_sampling_helper);
source.append("uniform highp vec2 delta;\n");
if (dropShadow)
@@ -446,7 +489,7 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShad
qreal sigma = radius / 1.65;
qreal sum = 0;
- for (int i = -radius; i <= radius; ++i) {
+ for (int i = -radius; i < radius; ++i) {
float value = gaussian(i, sigma);
gaussianComponents << value;
sum += value;
@@ -464,43 +507,67 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShad
weights << weight;
}
- // odd size ?
- if (gaussianComponents.size() & 1) {
- sampleOffsets << radius;
- weights << gaussianComponents.last();
- }
-
- int currentVariable = 1;
- source.append(" mediump vec4 sample = vec4(0.0);\n");
- source.append(" mediump vec2 coord;\n");
-
- qreal weightSum = 0;
- source.append(" mediump float c;\n");
- for (int i = 0; i < sampleOffsets.size(); ++i) {
- qreal delta = sampleOffsets.at(i);
-
- ++currentVariable;
+ int limit = sampleOffsets.size();
+ if (singlePass)
+ limit *= limit;
+
+ QByteArray baseCoordinate = "srcCoords";
+
+ for (int i = 0; i < limit; ++i) {
+ QByteArray coordinate = baseCoordinate;
+
+ qreal weight;
+ if (singlePass) {
+ const int xIndex = i % sampleOffsets.size();
+ const int yIndex = i / sampleOffsets.size();
+
+ const qreal deltaX = sampleOffsets.at(xIndex);
+ const qreal deltaY = sampleOffsets.at(yIndex);
+ weight = weights.at(xIndex) * weights.at(yIndex);
+
+ if (!qFuzzyCompare(deltaX, deltaY)) {
+ coordinate.append(" + vec2(delta.x * float(");
+ coordinate.append(QByteArray::number(deltaX));
+ coordinate.append("), delta.y * float(");
+ coordinate.append(QByteArray::number(deltaY));
+ coordinate.append("))");
+ } else if (!qFuzzyIsNull(deltaX)) {
+ coordinate.append(" + delta * float(");
+ coordinate.append(QByteArray::number(deltaX));
+ coordinate.append(")");
+ }
+ } else {
+ const qreal delta = sampleOffsets.at(i);
+ weight = weights.at(i);
+ if (!qFuzzyIsNull(delta)) {
+ coordinate.append(" + delta * float(");
+ coordinate.append(QByteArray::number(delta));
+ coordinate.append(")");
+ }
+ }
- QByteArray coordinate = "srcCoords";
- if (delta != qreal(0)) {
- coordinate.append(" + delta * float(");
- coordinate.append(QByteArray::number(delta));
- coordinate.append(")");
+ if (i == 0) {
+ if (dropShadow)
+ source.append(" mediump float sample = ");
+ else
+ source.append(" mediump vec4 sample = ");
+ } else {
+ if (dropShadow)
+ source.append(" sample += ");
+ else
+ source.append(" sample += ");
}
- source.append(" coord = ");
+ source.append("texture2D(src, ");
source.append(coordinate);
- source.append(";\n");
+ source.append(")");
if (dropShadow)
- source.append(" sample += texture2D(src, coord).a * shadowColor");
- else
- source.append(" sample += texture2D(src, coord)");
+ source.append(".a");
- weightSum += weights.at(i);
- if (weights.at(i) != qreal(1)) {
+ if (!qFuzzyCompare(weight, qreal(1))) {
source.append(" * float(");
- source.append(QByteArray::number(weights.at(i)));
+ source.append(QByteArray::number(weight));
source.append(");\n");
} else {
source.append(";\n");
@@ -508,86 +575,109 @@ QByteArray QGLPixmapBlurFilter::generateGaussianShader(int radius, bool dropShad
}
source.append(" return ");
+ if (dropShadow)
+ source.append("shadowColor * ");
source.append("sample;\n");
source.append("}\n");
+ cached = new QByteArray(source);
+ shaderSourceCache.insert(key, cached);
+
return source;
}
QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter(Qt::RenderHint hint)
: m_haveCached(false)
- , m_cachedRadius(5)
+ , m_cachedRadius(0)
, m_hint(hint)
{
- if (hint == Qt::PerformanceHint) {
- QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this);
- filter->setSource(qt_gl_drop_shadow_filter_fast);
- m_haveCached = true;
- }
}
bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
{
QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this);
- int radius = qRound(this->blurRadius());
- if (!m_haveCached || (m_hint == Qt::QualityHint && radius != m_cachedRadius)) {
+ int actualRadius = qRound(blurRadius());
+ int filterRadius = actualRadius;
+ m_singlePass = filterRadius <= 3;
+
+ if (!m_haveCached || filterRadius != m_cachedRadius) {
// Only regenerate the shader from source if parameters have changed.
m_haveCached = true;
- m_cachedRadius = radius;
- filter->setSource(QGLPixmapBlurFilter::generateGaussianShader(radius, true));
+ m_cachedRadius = filterRadius;
+ QByteArray source = QGLPixmapBlurFilter::generateGaussianShader(filterRadius, m_singlePass, true);
+ filter->setSource(source);
}
- QGLFramebufferObjectFormat format;
- format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
- QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format);
-
- if (!fbo)
- return false;
-
- glBindTexture(GL_TEXTURE_2D, fbo->texture());
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glBindTexture(GL_TEXTURE_2D, 0);
+ QRect targetRect = QRectF(src.rect()).translated(pos + offset()).adjusted(-actualRadius, -actualRadius, actualRadius, actualRadius).toAlignedRect();
+
+ if (m_singlePass) {
+ // prepare for updateUniforms
+ m_textureSize = src.size();
+
+ painter->save();
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ QBrush pixmapBrush = src;
+ pixmapBrush.setTransform(QTransform::fromTranslate(pos.x() + offset().x(), pos.y() + offset().y()));
+ painter->fillRect(targetRect, pixmapBrush);
+ filter->removeFromPainter(painter);
+ painter->restore();
+ } else {
+ QGLFramebufferObjectFormat format;
+ format.setInternalTextureFormat(GLenum(src.hasAlphaChannel() ? GL_RGBA : GL_RGB));
+ QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(targetRect.size(), format);
- // prepare for updateUniforms
- m_textureSize = src.size();
+ if (!fbo)
+ return false;
- // horizontal pass, to pixmap
- m_horizontalBlur = true;
+ glBindTexture(GL_TEXTURE_2D, fbo->texture());
- QPainter fboPainter(fbo);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glBindTexture(GL_TEXTURE_2D, 0);
- if (src.hasAlphaChannel()) {
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT);
- }
+ // prepare for updateUniforms
+ m_textureSize = src.size();
- // ensure GL_LINEAR filtering is used
- fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
- filter->setOnPainter(&fboPainter);
- fboPainter.drawPixmap(0, 0, src);
- filter->removeFromPainter(&fboPainter);
- fboPainter.end();
+ // horizontal pass, to pixmap
+ m_horizontalBlur = true;
- QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+ QPainter fboPainter(fbo);
- // vertical pass, to painter
- m_horizontalBlur = false;
-
- painter->save();
- // ensure GL_LINEAR filtering is used
- painter->setRenderHint(QPainter::SmoothPixmapTransform);
- filter->setOnPainter(painter);
- QPointF ofs = offset();
- engine->drawTexture(src.rect().translated(pos.x() + ofs.x(), pos.y() + ofs.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height()));
- filter->removeFromPainter(painter);
- painter->restore();
+ if (src.hasAlphaChannel()) {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
- qgl_fbo_pool()->release(fbo);
+ // ensure GL_LINEAR filtering is used
+ fboPainter.setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(&fboPainter);
+ QBrush pixmapBrush = src;
+ pixmapBrush.setTransform(QTransform::fromTranslate(actualRadius, actualRadius));
+ fboPainter.fillRect(QRect(0, 0, targetRect.width(), targetRect.height()), pixmapBrush);
+ filter->removeFromPainter(&fboPainter);
+ fboPainter.end();
+
+ QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
+
+ // vertical pass, to painter
+ m_horizontalBlur = false;
+ m_textureSize = fbo->size();
+
+ painter->save();
+ // ensure GL_LINEAR filtering is used
+ painter->setRenderHint(QPainter::SmoothPixmapTransform);
+ filter->setOnPainter(painter);
+ engine->drawTexture(targetRect, fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height()));
+ filter->removeFromPainter(painter);
+ painter->restore();
+
+ qgl_fbo_pool()->release(fbo);
+ }
// Now draw the actual pixmap over the top.
painter->drawPixmap(pos, src, srcRect);
@@ -597,8 +687,11 @@ bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos,
void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program)
{
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
QColor col = color();
- if (m_horizontalBlur) {
+ if (m_horizontalBlur && !m_singlePass) {
program->setUniformValue("shadowColor", 1.0f, 1.0f, 1.0f, 1.0f);
} else {
qreal alpha = col.alphaF();
@@ -607,17 +700,20 @@ void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program)
col.blueF() * alpha,
alpha);
}
+
if (m_hint == Qt::QualityHint) {
- if (m_horizontalBlur)
+ if (m_singlePass)
+ program->setUniformValue("delta", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height());
+ else if (m_horizontalBlur)
program->setUniformValue("delta", 1.0 / m_textureSize.width(), 0.0);
else
program->setUniformValue("delta", 0.0, 1.0 / m_textureSize.height());
} else {
- // 1.4 is chosen to most closely match the blurriness of the gaussian blur
- // at low radii
- qreal blur = blurRadius() / 1.4f;
+ qreal blur = blurRadius() / qreal(m_cachedRadius);
- if (m_horizontalBlur)
+ if (m_singlePass)
+ program->setUniformValue("delta", blur / m_textureSize.width(), blur / m_textureSize.height());
+ else if (m_horizontalBlur)
program->setUniformValue("delta", blur / m_textureSize.width(), 0.0);
else
program->setUniformValue("delta", 0.0, blur / m_textureSize.height());