From 351e890aa13faac69a0ceaa8cb02660b2275cf82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Tue, 30 Jun 2009 09:45:14 +0200 Subject: Added custom shader hook to the GL 2 paint engine. This will make it easier to implement pixmap filters, YUV->RGB conversions, etc in other parts of Qt. --- .../gl2paintengineex/qglengineshadermanager.cpp | 65 +++++++++++++++++++--- .../gl2paintengineex/qglengineshadermanager_p.h | 41 +++++++++++--- .../gl2paintengineex/qglengineshadersource_p.h | 8 +++ .../gl2paintengineex/qpaintengineex_opengl2.cpp | 12 ++-- .../gl2paintengineex/qpaintengineex_opengl2_p.h | 5 +- 5 files changed, 107 insertions(+), 24 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 4b73ca9..068f804 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -49,6 +49,30 @@ QT_BEGIN_NAMESPACE +QGLCustomShader::QGLCustomShader(QGLShader *shader) + : m_shader(shader) +{ +} + +QGLCustomShader::~QGLCustomShader() +{ + delete m_shader; +} + +void QGLCustomShader::updateUniforms(QGLShaderProgram *) +{ +} + +void QGLCustomShader::setShader(QGLShader *shader) +{ + m_shader = shader; +} + +QGLShader *QGLCustomShader::shader() const +{ + return m_shader; +} + const char* QGLEngineShaderManager::qglEngineShaderSourceCode[] = { 0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0, @@ -66,7 +90,8 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) compositionMode(QPainter::CompositionMode_SourceOver), blitShaderProg(0), simpleShaderProg(0), - currentShaderProg(0) + currentShaderProg(0), + customShader(0) { memset(compiledShaders, 0, sizeof(compiledShaders)); @@ -107,6 +132,7 @@ QGLEngineShaderManager::QGLEngineShaderManager(QGLContext* context) code[MainFragmentShader] = qglslMainFragmentShader; code[ImageSrcFragmentShader] = qglslImageSrcFragmentShader; + code[CustomSrcFragmentShader] = qglslCustomSrcFragmentShader; code[ImageSrcWithPatternFragmentShader] = qglslImageSrcWithPatternFragmentShader; code[NonPremultipliedImageSrcFragmentShader] = qglslNonPremultipliedImageSrcFragmentShader; code[SolidBrushSrcFragmentShader] = qglslSolidBrushSrcFragmentShader; @@ -228,7 +254,7 @@ void QGLEngineShaderManager::setDirty() void QGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style) { - if (srcPixelType == PixelSrcType(style)) + if (customShader || srcPixelType == PixelSrcType(style)) return; srcPixelType = style; @@ -237,13 +263,20 @@ void QGLEngineShaderManager::setSrcPixelType(Qt::BrushStyle style) void QGLEngineShaderManager::setSrcPixelType(PixelSrcType type) { - if (srcPixelType == type) + if (customShader || srcPixelType == type) return; srcPixelType = type; shaderProgNeedsChanging = true; //### } +void QGLEngineShaderManager::setCustomShader(QGLCustomShader *shader) +{ + srcPixelType = CustomSrc; + shaderProgNeedsChanging = true; + customShader = shader; +} + void QGLEngineShaderManager::setTextureCoordsEnabled(bool enabled) { if (useTextureCoords == enabled) @@ -338,6 +371,10 @@ bool QGLEngineShaderManager::useCorrectShaderProg() srcPixelFragShaderName = ImageSrcFragmentShader; positionVertexShaderName = PositionOnlyVertexShader; break; + case QGLEngineShaderManager::CustomSrc: + srcPixelFragShaderName = CustomSrcFragmentShader; + positionVertexShaderName = PositionOnlyVertexShader; + break; case QGLEngineShaderManager::PatternSrc: srcPixelFragShaderName = ImageSrcWithPatternFragmentShader; positionVertexShaderName = PositionOnlyVertexShader; @@ -381,7 +418,6 @@ bool QGLEngineShaderManager::useCorrectShaderProg() requiredProgram.positionVertexShader = compiledShaders[positionVertexShaderName]; requiredProgram.srcPixelFragShader = compiledShaders[srcPixelFragShaderName]; - const bool hasCompose = compositionMode > QPainter::CompositionMode_Plus; const bool hasMask = maskType != QGLEngineShaderManager::NoMask; @@ -483,6 +519,7 @@ bool QGLEngineShaderManager::useCorrectShaderProg() currentShaderProg = &prog; currentShaderProg->program->enable(); shaderProgNeedsChanging = false; + return true; } } @@ -496,6 +533,9 @@ bool QGLEngineShaderManager::useCorrectShaderProg() requiredProgram.program->addShader(requiredProgram.maskFragShader); requiredProgram.program->addShader(requiredProgram.compositionFragShader); + if (customShader) + requiredProgram.program->addShader(customShader->shader()); + // We have to bind the vertex attribute names before the program is linked: requiredProgram.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); if (useTextureCoords) @@ -520,11 +560,20 @@ bool QGLEngineShaderManager::useCorrectShaderProg() qWarning() << error; } else { - cachedPrograms.append(requiredProgram); - // taking the address here is safe since - // cachePrograms isn't resized anywhere else - currentShaderProg = &cachedPrograms.last(); + if (customShader) { + // don't cache custom shaders + customShaderProg = requiredProgram; + currentShaderProg = &customShaderProg; + } else { + cachedPrograms.append(requiredProgram); + // taking the address here is safe since + // cachePrograms isn't resized anywhere else + currentShaderProg = &cachedPrograms.last(); + } currentShaderProg->program->enable(); + + if (customShader) + customShader->updateUniforms(currentShaderProg->program); } shaderProgNeedsChanging = false; return true; diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index 34f0768..891776f 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -199,18 +199,19 @@ O = Global Opacity - CUSTOM SHADER CODE (idea, depricated) + CUSTOM SHADER CODE ================== The use of custom shader code is supported by the engine for drawImage and drawPixmap calls. This is implemented via hooks in the fragment pipeline. - The custom shader is passed to the engine as a partial fragment shader - (QGLCustomizedShader). The shader will implement a pre-defined method name - which Qt's fragment pipeline will call. There are two different hooks which - can be implemented as custom shader code: + The custom shader is passed to the shader manager as a partial fragment shader + by calling setCustomShader(). Qt's fragment pipeline will call the following + method name in the custom shader: mediump vec4 customShader(sampler2d src, vec2 srcCoords) - mediump vec4 customShaderWithDest(sampler2d dest, sampler2d src, vec2 srcCoords) + + Transformations, clipping, opacity, and composition modes set using QPainter + will be respected when using the custom shader hook. */ @@ -227,6 +228,22 @@ QT_BEGIN_NAMESPACE QT_MODULE(OpenGL) +class Q_OPENGL_EXPORT QGLCustomShader +{ +public: + QGLCustomShader(QGLShader *shader = 0); + virtual ~QGLCustomShader(); + + virtual void updateUniforms(QGLShaderProgram *program); + + void setShader(QGLShader *shader); + QGLShader *shader() const; + +private: + Q_DISABLE_COPY(QGLCustomShader) + + QGLShader *m_shader; +}; struct QGLEngineShaderProg { @@ -259,7 +276,7 @@ struct QGLEngineCachedShaderProg static const GLuint QT_VERTEX_COORDS_ATTR = 0; static const GLuint QT_TEXTURE_COORDS_ATTR = 1; -class QGLEngineShaderManager : public QObject +class Q_OPENGL_EXPORT QGLEngineShaderManager : public QObject { Q_OBJECT public: @@ -271,7 +288,8 @@ public: ImageSrc = Qt::TexturePattern+1, NonPremultipliedImageSrc = Qt::TexturePattern+2, PatternSrc = Qt::TexturePattern+3, - TextureSrcWithPattern = Qt::TexturePattern+4 + TextureSrcWithPattern = Qt::TexturePattern+4, + CustomSrc = Qt::TexturePattern + 5 }; // There are optimisations we can do, depending on the brush transform: @@ -285,6 +303,8 @@ public: void setMaskType(MaskType); void setCompositionMode(QPainter::CompositionMode); + void setCustomShader(QGLCustomShader *shader); + uint getUniformIdentifier(const char *uniformName); uint getUniformLocation(uint id); @@ -322,6 +342,7 @@ public: MainFragmentShader, ImageSrcFragmentShader, + CustomSrcFragmentShader, ImageSrcWithPatternFragmentShader, NonPremultipliedImageSrcFragmentShader, SolidBrushSrcFragmentShader, @@ -366,7 +387,6 @@ public: Q_ENUMS(ShaderName) #endif - private: QGLContext* ctx; bool shaderProgNeedsChanging; @@ -383,6 +403,9 @@ private: QGLShaderProgram* simpleShaderProg; QGLEngineShaderProg* currentShaderProg; + QGLEngineShaderProg customShaderProg; + QGLCustomShader* customShader; + // TODO: Possibly convert to a LUT QList cachedPrograms; diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h index 4e32f91..e379aa3 100644 --- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h @@ -290,6 +290,14 @@ static const char* const qglslImageSrcFragmentShader = "\ return texture2D(imageTexture, textureCoords); \ }"; +static const char* const qglslCustomSrcFragmentShader = "\ + varying highp vec2 textureCoords; \ + uniform sampler2D imageTexture; \ + lowp vec4 customShader(sampler2D texture, vec2 coords); \ + lowp vec4 srcPixel() { \ + return customShader(imageTexture, textureCoords); \ + }"; + static const char* const qglslImageSrcWithPatternFragmentShader = "\ varying highp vec2 textureCoords; \ uniform lowp vec4 patternColor; \ diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 4bf5d4c..a679a62 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -130,10 +130,6 @@ QGLTextureGlyphCache::QGLTextureGlyphCache(QGLContext *context, QFontEngineGlyph QGLTextureGlyphCache::~QGLTextureGlyphCache() { - glDeleteFramebuffers(1, &m_fbo); - - if (m_width || m_height) - glDeleteTextures(1, &m_texture); } void QGLTextureGlyphCache::createTextureData(int width, int height) @@ -251,7 +247,7 @@ QGL2PaintEngineExPrivate::~QGL2PaintEngineExPrivate() void QGL2PaintEngineExPrivate::updateTextureFilter(GLenum target, GLenum wrapMode, bool smoothPixmapTransform, GLuint id) { // glActiveTexture(GL_TEXTURE0 + QT_BRUSH_TEXTURE_UNIT); //### Is it always this texture unit? - if (id != -1 && id == lastTexture) + if (id != GLuint(-1) && id == lastTexture) return; lastTexture = id; @@ -1605,4 +1601,10 @@ QOpenGL2PaintEngineState::~QOpenGL2PaintEngineState() { } +QGLEngineShaderManager *QGL2PaintEngineEx::shaderManager() const +{ + Q_D(const QGL2PaintEngineEx); + return d->shaderManager; +} + QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 0d28a49..f8d3b53 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -88,8 +88,7 @@ public: bool canRestoreClip; }; - -class QGL2PaintEngineEx : public QPaintEngineEx +class Q_OPENGL_EXPORT QGL2PaintEngineEx : public QPaintEngineEx { Q_DECLARE_PRIVATE(QGL2PaintEngineEx) public: @@ -133,6 +132,8 @@ public: } virtual void sync(); + QGLEngineShaderManager *shaderManager() const; + private: Q_DISABLE_COPY(QGL2PaintEngineEx) }; -- cgit v0.12 From 23f589030f41166ccb0b650cba83c5569787b9c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Tue, 30 Jun 2009 14:21:03 +0200 Subject: Added drawTexture function to GL 2 paint engine. Works just like drawImage / drawPixmap but uses the given texture id. --- src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 15 +++++++++++++++ src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h | 3 ++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index a679a62..03ef8c8 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1045,6 +1045,21 @@ void QGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, const d->drawTexture(dest, src, image.size(), !image.hasAlphaChannel()); } +void QGL2PaintEngineEx::drawTexture(const QRectF &dest, GLuint textureId, const QSize &size, const QRectF &src) +{ + Q_D(QGL2PaintEngineEx); + ensureActive(); + d->transferMode(ImageDrawingMode); + + QGLContext *ctx = d->ctx; + glActiveTexture(GL_TEXTURE0 + QT_IMAGE_TEXTURE_UNIT); + glBindTexture(GL_TEXTURE_2D, textureId); + + d->updateTextureFilter(GL_TEXTURE_2D, GL_REPEAT, + state()->renderHints & QPainter::SmoothPixmapTransform, textureId); + d->drawTexture(dest, src, size, false); +} + void QGL2PaintEngineEx::drawTextItem(const QPointF &p, const QTextItem &textItem) { Q_D(QGL2PaintEngineEx); diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index f8d3b53..5e15f40 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -115,9 +115,10 @@ public: virtual void drawPixmap(const QRectF &r, const QPixmap &pm, const QRectF &sr); - virtual void drawImage(const QRectF &r, const QImage &pm, const QRectF &sr, Qt::ImageConversionFlags flags = Qt::AutoColor); + virtual void drawTexture(const QRectF &r, GLuint textureId, const QSize &size, const QRectF &sr); + virtual void drawTextItem(const QPointF &p, const QTextItem &textItem); Type type() const { return OpenGL; } -- cgit v0.12 From a50aa375477c88e688bb919cd1776be9afe4f6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Tue, 30 Jun 2009 14:22:42 +0200 Subject: Added missing createPixmapFilter() implementation in GL 2 engine. --- src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp | 8 ++++++++ src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp index 03ef8c8..6b0dc04 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp @@ -1622,4 +1622,12 @@ QGLEngineShaderManager *QGL2PaintEngineEx::shaderManager() const return d->shaderManager; } +QPixmapFilter *QGL2PaintEngineEx::createPixmapFilter(int type) const +{ + const QGLContext *ctx = QGLContext::currentContext(); + if (ctx) + return ctx->d_func()->createPixmapFilter(type); + return 0; +} + QT_END_NAMESPACE diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h index 5e15f40..6b34418 100644 --- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h +++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h @@ -135,6 +135,8 @@ public: QGLEngineShaderManager *shaderManager() const; + QPixmapFilter *createPixmapFilter(int type) const; + private: Q_DISABLE_COPY(QGL2PaintEngineEx) }; -- cgit v0.12 From c92b73a19b34ec97262c0959653ac1faf6621de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Tue, 30 Jun 2009 14:23:20 +0200 Subject: Added QPixmapBlurFilter with GL implementation. No raster engine based fallback so far... Also, performance is lacking since the QGLShaderProgram isn't cached. --- src/gui/image/qpixmapfilter.cpp | 183 ++++++++++++++++++++++++++++ src/gui/image/qpixmapfilter_p.h | 25 ++++ src/opengl/qglpixmapfilter.cpp | 256 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 463 insertions(+), 1 deletion(-) diff --git a/src/gui/image/qpixmapfilter.cpp b/src/gui/image/qpixmapfilter.cpp index c5f3663..ee4f7cf 100644 --- a/src/gui/image/qpixmapfilter.cpp +++ b/src/gui/image/qpixmapfilter.cpp @@ -87,6 +87,8 @@ public: \value ConvolutionFilter A filter that is used to calculate the convolution of the image with a kernel. See QPixmapConvolutionFilter for more information. + \value BlurFilter A filter that is used to blur an image. See + QPixmapConvolutionFilter for more information. \value ColorizeFilter A filter that is used to change the overall color of an image. See QPixmapColorizeFilter for more information. @@ -479,6 +481,187 @@ void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const Q } } +/*! + \class QPixmapBlurFilter + \since 4.6 + \ingroup multimedia + + \brief The QPixmapBlurFilter class provides blur filtering + for pixmaps. + + QPixmapBlurFilter implements a blur pixmap filter, + which is applied when \l{QPixmapFilter::}{draw()} is called. + + The filter lets you specialize the radius of the blur as well + as the quality. + + \sa {Pixmap Filters Example}, QPixmapConvolutionFilter, QPixmapDropShadowFilter + + \internal +*/ + +class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate +{ +public: + QPixmapBlurFilterPrivate() : radius(5), quality(Qt::FastTransformation) {} + + int radius; + Qt::TransformationMode quality; +}; + + +/*! + Constructs a pixmap blur filter. + + \internal +*/ +QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent) + : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent) +{ +} + +/*! + Destructor of pixmap blur filter. + + \internal +*/ +QPixmapBlurFilter::~QPixmapBlurFilter() +{ +} + +/*! + Sets the radius of the blur filter. Higher radius produces increased blurriness. + + \internal +*/ +void QPixmapBlurFilter::setRadius(int radius) +{ + Q_D(QPixmapBlurFilter); + d->radius = radius; +} + +/*! + Gets the radius of the blur filter. + + \internal +*/ +int QPixmapBlurFilter::radius() const +{ + Q_D(const QPixmapBlurFilter); + return d->radius; +} + +/*! + Sets the quality of the blur filter. Lower quality yields better performance. + + \internal +*/ +void QPixmapBlurFilter::setQuality(Qt::TransformationMode quality) +{ + Q_D(QPixmapBlurFilter); + d->quality = quality; +} + +/*! + Gets the quality of the blur filter. + + \internal +*/ +Qt::TransformationMode QPixmapBlurFilter::quality() const +{ + Q_D(const QPixmapBlurFilter); + return d->quality; +} + +/*! + \reimp + + \internal +*/ +QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const +{ + return rect; +} + +/*! + \reimp + + \internal +*/ +void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &srcRect) const +{ + Q_D(const QPixmapBlurFilter); + if (!painter->isActive()) + return; + + if (d->radius == 0) { + painter->drawPixmap(srcRect.translated(p), src, srcRect); + return; + } + + QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ? + static_cast(painter->paintEngine())->createPixmapFilter(type()) : 0; + QPixmapBlurFilter *blurFilter = static_cast(filter); + if (blurFilter) { + blurFilter->setRadius(d->radius); + blurFilter->setQuality(d->quality); + blurFilter->draw(painter, p, src, srcRect); + delete blurFilter; + return; + } + +#if 0 + // falling back to raster implementation + + QImage *target = 0; + if (painter->paintEngine()->paintDevice()->devType() == QInternal::Image) { + target = static_cast(painter->paintEngine()->paintDevice()); + + QTransform mat = painter->combinedTransform(); + + if (mat.type() > QTransform::TxTranslate) { + // Disabled because of transformation... + target = 0; + } else { + QRasterPaintEngine *pe = static_cast(painter->paintEngine()); + if (pe->clipType() == QRasterPaintEngine::ComplexClip) + // disabled because of complex clipping... + target = 0; + else { + QRectF clip = pe->clipBoundingRect(); + QRectF rect = boundingRectFor(srcRect.isEmpty() ? src.rect() : srcRect); + QTransform x = painter->deviceTransform(); + if (!clip.contains(rect.translated(x.dx() + p.x(), x.dy() + p.y()))) { + target = 0; + } + + } + } + } + + if (target) { + QTransform x = painter->deviceTransform(); + QPointF offset(x.dx(), x.dy()); + + convolute(target, p+offset, src.toImage(), srcRect, QPainter::CompositionMode_SourceOver, d->convolutionKernel, d->kernelWidth, d->kernelHeight); + } else { + QRect srect = srcRect.isNull() ? src.rect() : srcRect.toRect(); + QRect rect = boundingRectFor(srect).toRect(); + QImage result = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied); + QPoint offset = srect.topLeft() - rect.topLeft(); + convolute(&result, + offset, + src.toImage(), + srect, + QPainter::CompositionMode_Source, + d->convolutionKernel, + d->kernelWidth, + d->kernelHeight); + painter->drawImage(p - offset, result); + } +#endif +} + // grayscales the image to dest (could be same). If rect isn't defined // destination image size is used to determine the dimension of grayscaling // process. diff --git a/src/gui/image/qpixmapfilter_p.h b/src/gui/image/qpixmapfilter_p.h index 51292b3..6978f03 100644 --- a/src/gui/image/qpixmapfilter_p.h +++ b/src/gui/image/qpixmapfilter_p.h @@ -78,6 +78,7 @@ public: ConvolutionFilter, ColorizeFilter, DropShadowFilter, + BlurFilter, UserFilter = 1024 }; @@ -117,6 +118,30 @@ private: int columns() const; }; +class QPixmapBlurFilterPrivate; + +class Q_GUI_EXPORT QPixmapBlurFilter : public QPixmapFilter +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QPixmapBlurFilter) + +public: + QPixmapBlurFilter(QObject *parent = 0); + ~QPixmapBlurFilter(); + + void setRadius(int radius); + void setQuality(Qt::TransformationMode mode); + + QRectF boundingRectFor(const QRectF &rect) const; + void draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect = QRectF()) const; + +private: + friend class QGLPixmapBlurFilter; + + int radius() const; + Qt::TransformationMode quality() const; +}; + class QPixmapColorizeFilterPrivate; class Q_GUI_EXPORT QPixmapColorizeFilter : public QPixmapFilter diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 7514743..6812c43 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -40,15 +40,19 @@ ****************************************************************************/ #include "private/qpixmapfilter_p.h" +#include "private/qpaintengineex_opengl2_p.h" +#include "private/qglengineshadermanager_p.h" #include "qglpixmapfilter_p.h" #include "qgraphicssystem_gl_p.h" #include "qpaintengine_opengl_p.h" +#include "qcache.h" -#include "qglpixelbuffer.h" +#include "qglframebufferobject.h" #include "qglshaderprogram.h" #include "qgl_p.h" #include "private/qapplication_p.h" +#include "private/qmath_p.h" QT_BEGIN_NAMESPACE @@ -97,6 +101,28 @@ private: mutable int m_kernelHeight; }; +class QGLPixmapBlurFilter : public QGLCustomShader, public QGLPixmapFilter +{ +public: + QGLPixmapBlurFilter(); + ~QGLPixmapBlurFilter(); + + void updateUniforms(QGLShaderProgram *program); + +protected: + bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; + +private: + static QByteArray generateBlurShader(int radius, bool gaussianBlur); + + mutable QGLShader *m_shader; + mutable QGLFramebufferObject *m_fbo; + + mutable QSize m_textureSize; + + QGLShaderProgram *m_program; +}; + extern QGLWidget *qt_gl_share_widget(); QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const @@ -105,6 +131,8 @@ QPixmapFilter *QGLContextPrivate::createPixmapFilter(int type) const case QPixmapFilter::ColorizeFilter: return new QGLPixmapColorizeFilter; + case QPixmapFilter::BlurFilter: + return new QGLPixmapBlurFilter; case QPixmapFilter::ConvolutionFilter: return new QGLPixmapConvolutionFilter; @@ -281,4 +309,230 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *, const QPointF &pos, const return true; } +QGLPixmapBlurFilter::QGLPixmapBlurFilter() + : m_fbo(0) +{ +} + +QGLPixmapBlurFilter::~QGLPixmapBlurFilter() +{ + delete m_fbo; +} + +bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const +{ + QGLCustomShader *customShader = const_cast(this); + + if (!shader()) { + QGLShader *blurShader = new QGLShader(QGLShader::FragmentShader); + blurShader->compile(generateBlurShader(radius(), quality() == Qt::SmoothTransformation)); + + customShader->setShader(blurShader); + + m_fbo = new QGLFramebufferObject(src.size()); + + glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + } + + QGL2PaintEngineEx *engine = static_cast(painter->paintEngine()); + QGLEngineShaderManager *manager = engine->shaderManager(); + + engine->syncState(); + painter->save(); + + // ensure GL_LINEAR filtering is used + painter->setRenderHint(QPainter::SmoothPixmapTransform); + + // prepare for updateUniforms + m_textureSize = src.size(); + + // first pass, to fbo + m_fbo->bind(); + manager->setCustomShader(customShader); + engine->drawPixmap(src.rect(), src, src.rect()); + m_fbo->release(); + + // second pass, to widget + m_program->setUniformValue("delta", 0.0, 1.0); + engine->drawTexture(src.rect().translated(pos.x(), pos.y()), m_fbo->texture(), src.size(), src.rect()); + manager->setCustomShader(0); + + painter->restore(); + + return true; +} + +void QGLPixmapBlurFilter::updateUniforms(QGLShaderProgram *program) +{ + program->setUniformValue("invTextureSize", 1.0 / m_textureSize.width(), 1.0 / m_textureSize.height()); + program->setUniformValue("delta", 1.0, 0.0); + + m_program = program; +} + +static inline qreal gaussian(qreal dx, qreal sigma) +{ + return exp(-dx * dx / (2 * sigma * sigma)) / (Q_2PI * sigma * sigma); +} + +QByteArray QGLPixmapBlurFilter::generateBlurShader(int radius, bool gaussianBlur) +{ + Q_ASSERT(radius >= 1); + + QByteArray source; + + source.append("uniform highp vec2 invTextureSize;\n"); + + bool separateXY = true; + bool clip = false; + + if (separateXY) { + source.append("uniform highp vec2 delta;\n"); + + if (clip) + source.append("uniform highp vec2 clip;\n"); + } else if (clip) { + source.append("uniform highp vec4 clip;\n"); + } + + source.append("mediump vec4 customShader(sampler2D src, vec2 srcCoords) {\n"); + + QVector sampleOffsets; + QVector weights; + + if (gaussianBlur) { + QVector gaussianComponents; + + qreal sigma = radius / 1.65; + + qreal sum = 0; + for (int i = -radius; i <= radius; ++i) { + float value = gaussian(i, sigma); + gaussianComponents << value; + sum += value; + } + + // normalize + for (int i = 0; i < gaussianComponents.size(); ++i) + gaussianComponents[i] /= sum; + + for (int i = 0; i < gaussianComponents.size() - 1; i += 2) { + qreal weight = gaussianComponents.at(i) + gaussianComponents.at(i + 1); + qreal offset = i - radius + gaussianComponents.at(i + 1) / weight; + + sampleOffsets << offset; + weights << weight; + } + + // odd size ? + if (gaussianComponents.size() & 1) { + sampleOffsets << radius; + weights << gaussianComponents.last(); + } + } else { + for (int i = 0; i < radius; ++i) { + sampleOffsets << 2 * i - radius + 0.5; + weights << qreal(1); + } + sampleOffsets << radius; + weights << qreal(0.5); + } + + int currentVariable = 1; + source.append(" mediump vec4 sample = vec4(0.0);\n"); + source.append(" mediump vec2 coord;\n"); + + qreal weightSum = 0; + if (separateXY) { + source.append(" mediump float c;\n"); + for (int i = 0; i < sampleOffsets.size(); ++i) { + qreal delta = sampleOffsets.at(i); + + ++currentVariable; + + QByteArray coordinate = "srcCoords"; + if (delta != qreal(0)) { + coordinate.append(" + invTextureSize * delta * float("); + coordinate.append(QByteArray::number(delta)); + coordinate.append(")"); + } + + source.append(" coord = "); + source.append(coordinate); + source.append(";\n"); + + if (clip) { + source.append(" c = dot(coord, delta);\n"); + source.append(" if (c > clip.x && c < clip.y)\n "); + } + + source.append(" sample += texture2D(src, coord)"); + + weightSum += weights.at(i); + if (weights.at(i) != qreal(1)) { + source.append(" * float("); + source.append(QByteArray::number(weights.at(i))); + source.append(");\n"); + } else { + source.append(";\n"); + } + } + } else { + for (int y = 0; y < sampleOffsets.size(); ++y) { + for (int x = 0; x < sampleOffsets.size(); ++x) { + QByteArray coordinate = "srcCoords"; + + qreal dx = sampleOffsets.at(x); + qreal dy = sampleOffsets.at(y); + + if (dx != qreal(0) || dy != qreal(0)) { + coordinate.append(" + invTextureSize * vec2(float("); + coordinate.append(QByteArray::number(dx)); + coordinate.append("), float("); + coordinate.append(QByteArray::number(dy)); + coordinate.append("))"); + } + + source.append(" coord = "); + source.append(coordinate); + source.append(";\n"); + + if (clip) + source.append(" if (coord.x > clip.x && coord.x < clip.y && coord.y > clip.z && coord.y < clip.w)\n "); + + source.append(" sample += texture2D(src, coord)"); + + ++currentVariable; + + weightSum += weights.at(x) * weights.at(y); + if ((weights.at(x) != qreal(1) || weights.at(y) != qreal(1))) { + source.append(" * float("); + source.append(QByteArray::number(weights.at(x) * weights.at(y))); + source.append(");\n"); + } else { + source.append(";\n"); + } + } + } + } + + source.append(" return "); + if (!gaussianBlur) { + source.append("float("); + if (separateXY) + source.append(QByteArray::number(1 / weightSum)); + else + source.append(QByteArray::number(1 / weightSum)); + source.append(") * "); + } + source.append("sample;\n"); + source.append("}\n"); + + return source; +} + QT_END_NAMESPACE -- cgit v0.12 From f86a6467c0dbfd54603b588e6c001265b0d527e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Wed, 1 Jul 2009 15:17:15 +0200 Subject: Added caching of custom shader programs as well in GL 2 shader manager. Ensures that the programs are evicted from the cache when the shaders are destroyed. --- .../gl2paintengineex/qglengineshadermanager.cpp | 42 ++++++++++++++-------- .../gl2paintengineex/qglengineshadermanager_p.h | 5 ++- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 068f804..10654ae 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -506,6 +506,7 @@ bool QGLEngineShaderManager::useCorrectShaderProg() else requiredProgram.compositionFragShader = 0; + requiredProgram.customShader = customShader ? customShader->shader() : 0; // At this point, requiredProgram is fully populated so try to find the program in the cache for (int i = 0; i < cachedPrograms.size(); ++i) { @@ -514,12 +515,16 @@ bool QGLEngineShaderManager::useCorrectShaderProg() && (prog.positionVertexShader == requiredProgram.positionVertexShader) && (prog.mainFragShader == requiredProgram.mainFragShader) && (prog.srcPixelFragShader == requiredProgram.srcPixelFragShader) - && (prog.compositionFragShader == requiredProgram.compositionFragShader) ) + && (prog.compositionFragShader == requiredProgram.compositionFragShader) + && (prog.customShader == requiredProgram.customShader) ) { currentShaderProg = &prog; currentShaderProg->program->enable(); shaderProgNeedsChanging = false; + if (customShader) + customShader->updateUniforms(currentShaderProg->program); + return true; } } @@ -533,8 +538,8 @@ bool QGLEngineShaderManager::useCorrectShaderProg() requiredProgram.program->addShader(requiredProgram.maskFragShader); requiredProgram.program->addShader(requiredProgram.compositionFragShader); - if (customShader) - requiredProgram.program->addShader(customShader->shader()); + if (requiredProgram.customShader) + requiredProgram.program->addShader(requiredProgram.customShader); // We have to bind the vertex attribute names before the program is linked: requiredProgram.program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR); @@ -560,25 +565,34 @@ bool QGLEngineShaderManager::useCorrectShaderProg() qWarning() << error; } else { - if (customShader) { - // don't cache custom shaders - customShaderProg = requiredProgram; - currentShaderProg = &customShaderProg; - } else { - cachedPrograms.append(requiredProgram); - // taking the address here is safe since - // cachePrograms isn't resized anywhere else - currentShaderProg = &cachedPrograms.last(); - } + cachedPrograms.append(requiredProgram); + // taking the address here is safe since + // cachePrograms isn't resized anywhere else + currentShaderProg = &cachedPrograms.last(); currentShaderProg->program->enable(); - if (customShader) + if (customShader) { customShader->updateUniforms(currentShaderProg->program); + connect(customShader->shader(), SIGNAL(destroyed(QObject *)), + this, SLOT(shaderDestroyed(QObject *))); + } } shaderProgNeedsChanging = false; return true; } +void QGLEngineShaderManager::shaderDestroyed(QObject *shader) +{ + for (int i = 0; i < cachedPrograms.size(); ++i) { + if (cachedPrograms.at(i).customShader == shader) { + delete cachedPrograms.at(i).program; + cachedPrograms.removeAt(i--); + } + } + + shaderProgNeedsChanging = true; +} + void QGLEngineShaderManager::compileNamedShader(QGLEngineShaderManager::ShaderName name, QGLShader::ShaderType type) { if (compiledShaders[name]) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h index 891776f..c749f52 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager_p.h +++ b/src/opengl/gl2paintengineex/qglengineshadermanager_p.h @@ -253,6 +253,7 @@ struct QGLEngineShaderProg QGLShader* srcPixelFragShader; QGLShader* maskFragShader; // Can be null for no mask QGLShader* compositionFragShader; // Can be null for GL-handled mode + QGLShader* customShader; QGLShaderProgram* program; QVector uniformLocations; @@ -387,6 +388,9 @@ public: Q_ENUMS(ShaderName) #endif +private slots: + void shaderDestroyed(QObject *shader); + private: QGLContext* ctx; bool shaderProgNeedsChanging; @@ -403,7 +407,6 @@ private: QGLShaderProgram* simpleShaderProg; QGLEngineShaderProg* currentShaderProg; - QGLEngineShaderProg customShaderProg; QGLCustomShader* customShader; // TODO: Possibly convert to a LUT -- cgit v0.12 From ef9fad3391f7b5c59d3fe62b491a4760c2917810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 2 Jul 2009 09:41:40 +0200 Subject: Refactored the GL QPixmap backend to use an FBO pool class. The FBO pool will be useful in other places as well, plus it makes it easier to deal with graphics memory management issues. --- src/opengl/qpixmapdata_gl.cpp | 134 +++++++++++++++++++++++++++++------------- src/opengl/qpixmapdata_gl_p.h | 13 ++++ 2 files changed, 106 insertions(+), 41 deletions(-) diff --git a/src/opengl/qpixmapdata_gl.cpp b/src/opengl/qpixmapdata_gl.cpp index f0c7e20..f6a9bf4 100644 --- a/src/opengl/qpixmapdata_gl.cpp +++ b/src/opengl/qpixmapdata_gl.cpp @@ -55,6 +55,85 @@ QT_BEGIN_NAMESPACE extern QGLWidget* qt_gl_share_widget(); +/*! + \class QGLFramebufferObjectPool + \since 4.6 + + \brief The QGLFramebufferObject class provides a pool of framebuffer + objects for offscreen rendering purposes. + + When requesting an FBO of a given size and format, an FBO of the same + format and a size at least as big as the requested size will be returned. + + \internal +*/ + +static inline int areaDiff(const QSize &size, const QGLFramebufferObject *fbo) +{ + return qAbs(size.width() * size.height() - fbo->width() * fbo->height()); +} + +QGLFramebufferObject *QGLFramebufferObjectPool::acquire(const QSize &requestSize, const QGLFramebufferObjectFormat &requestFormat) +{ + QGLFramebufferObject *chosen = 0; + QGLFramebufferObject *candidate = 0; + for (int i = 0; !chosen && i < m_fbos.size(); ++i) { + QGLFramebufferObject *fbo = m_fbos.at(i); + + QGLFramebufferObjectFormat format = fbo->format(); + if (format.samples() == requestFormat.samples() + && format.attachment() == requestFormat.attachment() + && format.textureTarget() == requestFormat.textureTarget() + && format.internalFormat() == requestFormat.internalFormat()) + { + // choose the fbo with a matching format and the closest size + if (!candidate || areaDiff(requestSize, candidate) > areaDiff(requestSize, fbo)) + candidate = fbo; + } + + if (candidate) { + m_fbos.removeOne(candidate); + + const QSize fboSize = candidate->size(); + QSize sz = fboSize; + + if (sz.width() < requestSize.width()) + sz.setWidth(qMax(requestSize.width(), qRound(sz.width() * 1.5))); + if (sz.height() < requestSize.height()) + sz.setHeight(qMax(requestSize.height(), qRound(sz.height() * 1.5))); + + // wasting too much space? + if (sz.width() * sz.height() > requestSize.width() * requestSize.height() * 2.5) + sz = requestSize; + + if (sz != fboSize) { + delete candidate; + qDebug() << "Resizing fbo in pool:" << sz; + candidate = new QGLFramebufferObject(sz, requestFormat); + } + + chosen = candidate; + } + } + + if (!chosen) { + qDebug() << "Creating new fbo in pool:" << requestSize; + chosen = new QGLFramebufferObject(requestSize, requestFormat); + } + + if (!chosen->isValid()) { + delete chosen; + chosen = 0; + } + + return chosen; +} + +void QGLFramebufferObjectPool::release(QGLFramebufferObject *fbo) +{ + m_fbos << fbo; +} + class QGLShareContextScope { public: @@ -330,8 +409,11 @@ struct TextureBuffer QGL2PaintEngineEx *engine; }; -static QVector textureBufferStack; -static int currentTextureBuffer = 0; +Q_GLOBAL_STATIC(QGLFramebufferObjectPool, _qgl_fbo_pool) +QGLFramebufferObjectPool* qgl_fbo_pool() +{ + return _qgl_fbo_pool(); +} void QGLPixmapData::copyBackFromRenderFbo(bool keepCurrentFboBound) const { @@ -378,7 +460,8 @@ void QGLPixmapData::swapBuffers() copyBackFromRenderFbo(false); m_renderFbo->release(); - --currentTextureBuffer; + qgl_fbo_pool()->release(m_renderFbo); + delete m_engine; m_renderFbo = 0; m_engine = 0; @@ -396,19 +479,6 @@ void QGLPixmapData::doneCurrent() m_renderFbo->release(); } -static TextureBuffer createTextureBuffer(const QSize &size, QGL2PaintEngineEx *engine = 0) -{ - TextureBuffer buffer; - QGLFramebufferObjectFormat fmt; - fmt.setAttachment(QGLFramebufferObject::CombinedDepthStencil); - fmt.setSamples(4); - - buffer.fbo = new QGLFramebufferObject(size, fmt); - buffer.engine = engine ? engine : new QGL2PaintEngineEx; - - return buffer; -} - bool QGLPixmapData::useFramebufferObjects() { return QGLFramebufferObject::hasOpenGLFramebufferObjects() @@ -431,33 +501,15 @@ QPaintEngine* QGLPixmapData::paintEngine() const qt_gl_share_widget()->makeCurrent(); QGLShareContextScope ctx(qt_gl_share_widget()->context()); - if (textureBufferStack.size() <= currentTextureBuffer) { - textureBufferStack << createTextureBuffer(size()); - } else { - QSize sz = textureBufferStack.at(currentTextureBuffer).fbo->size(); - if (sz.width() < w || sz.height() < h) { - if (sz.width() < w) - sz.setWidth(qMax(w, qRound(sz.width() * 1.5))); - if (sz.height() < h) - sz.setHeight(qMax(h, qRound(sz.height() * 1.5))); - - // wasting too much space? - if (sz.width() * sz.height() > w * h * 2.5) - sz = QSize(w, h); - - delete textureBufferStack.at(currentTextureBuffer).fbo; - textureBufferStack[currentTextureBuffer] = - createTextureBuffer(sz, textureBufferStack.at(currentTextureBuffer).engine); - qDebug() << "Creating new pixmap texture buffer:" << sz; - } - } - - if (textureBufferStack.at(currentTextureBuffer).fbo->isValid()) { - m_renderFbo = textureBufferStack.at(currentTextureBuffer).fbo; - m_engine = textureBufferStack.at(currentTextureBuffer).engine; + QGLFramebufferObjectFormat format; + format.setAttachment(QGLFramebufferObject::CombinedDepthStencil); + format.setSamples(4); + format.setInternalFormat(m_hasAlpha ? GL_RGBA : GL_RGB); - ++currentTextureBuffer; + m_renderFbo = qgl_fbo_pool()->acquire(size(), format); + if (m_renderFbo) { + m_engine = new QGL2PaintEngineEx; return m_engine; } diff --git a/src/opengl/qpixmapdata_gl_p.h b/src/opengl/qpixmapdata_gl_p.h index a6aa22d..aaeb8ac 100644 --- a/src/opengl/qpixmapdata_gl_p.h +++ b/src/opengl/qpixmapdata_gl_p.h @@ -61,6 +61,19 @@ QT_BEGIN_NAMESPACE class QPaintEngine; class QGLFramebufferObject; +class QGLFramebufferObjectFormat; + +class QGLFramebufferObjectPool +{ +public: + QGLFramebufferObject *acquire(const QSize &size, const QGLFramebufferObjectFormat &format); + void release(QGLFramebufferObject *fbo); + +private: + QList m_fbos; +}; + +QGLFramebufferObjectPool* qgl_fbo_pool(); class QGLPixmapData : public QPixmapData { -- cgit v0.12 From b1832b5a32e1ff5536e5fe9efeca3013ba947eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 2 Jul 2009 10:01:56 +0200 Subject: Made GL blur filter use the new FBO pool for improved performance. --- src/opengl/qglpixmapfilter.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 6812c43..4e859d4 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -116,7 +116,6 @@ private: static QByteArray generateBlurShader(int radius, bool gaussianBlur); mutable QGLShader *m_shader; - mutable QGLFramebufferObject *m_fbo; mutable QSize m_textureSize; @@ -310,13 +309,11 @@ bool QGLPixmapConvolutionFilter::processGL(QPainter *, const QPointF &pos, const } QGLPixmapBlurFilter::QGLPixmapBlurFilter() - : m_fbo(0) { } QGLPixmapBlurFilter::~QGLPixmapBlurFilter() { - delete m_fbo; } bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const @@ -328,15 +325,20 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const blurShader->compile(generateBlurShader(radius(), quality() == Qt::SmoothTransformation)); customShader->setShader(blurShader); + } - m_fbo = new QGLFramebufferObject(src.size()); + QGLFramebufferObjectFormat format; + format.setInternalFormat(src.hasAlphaChannel() ? GL_RGBA : GL_RGB); + QGLFramebufferObject *fbo = qgl_fbo_pool()->acquire(src.size(), format); - glBindTexture(GL_TEXTURE_2D, m_fbo->texture()); + if (!fbo) + return false; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glBindTexture(GL_TEXTURE_2D, 0); - } + 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); + glBindTexture(GL_TEXTURE_2D, 0); QGL2PaintEngineEx *engine = static_cast(painter->paintEngine()); QGLEngineShaderManager *manager = engine->shaderManager(); @@ -351,18 +353,20 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const m_textureSize = src.size(); // first pass, to fbo - m_fbo->bind(); + fbo->bind(); manager->setCustomShader(customShader); engine->drawPixmap(src.rect(), src, src.rect()); - m_fbo->release(); + fbo->release(); // second pass, to widget m_program->setUniformValue("delta", 0.0, 1.0); - engine->drawTexture(src.rect().translated(pos.x(), pos.y()), m_fbo->texture(), src.size(), src.rect()); + engine->drawTexture(src.rect().translated(pos.x(), pos.y()), fbo->texture(), src.size(), src.rect()); manager->setCustomShader(0); painter->restore(); + qgl_fbo_pool()->release(fbo); + return true; } -- cgit v0.12 From 6dc1536c1d3964a16484b2a55aa161b49e639901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 2 Jul 2009 12:28:53 +0200 Subject: Fixed GL blur filter to handle painter translates and larger FBO sizes. --- src/opengl/qglpixmapfilter.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 4e859d4..662e818 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -338,6 +338,8 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const 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); QGL2PaintEngineEx *engine = static_cast(painter->paintEngine()); @@ -355,12 +357,26 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const // first pass, to fbo fbo->bind(); manager->setCustomShader(customShader); + + QTransform transform = engine->state()->matrix; + + if (!transform.isIdentity()) { + engine->state()->matrix = QTransform(); + engine->transformChanged(); + } + engine->drawPixmap(src.rect(), src, src.rect()); + + if (!transform.isIdentity()) { + engine->state()->matrix = transform; + engine->transformChanged(); + } + fbo->release(); // second pass, to widget m_program->setUniformValue("delta", 0.0, 1.0); - engine->drawTexture(src.rect().translated(pos.x(), pos.y()), fbo->texture(), src.size(), src.rect()); + engine->drawTexture(src.rect().translated(pos.x(), pos.y()), fbo->texture(), fbo->size(), src.rect().translated(0, fbo->height() - src.height())); manager->setCustomShader(0); painter->restore(); -- cgit v0.12 From 0431548ddffc56f74cc60e7d341ade3920adefb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 2 Jul 2009 13:16:25 +0200 Subject: Further optimized the GL blur filter by caching the QGLShader. --- src/opengl/gl2paintengineex/qglengineshadermanager.cpp | 1 - src/opengl/qglpixmapfilter.cpp | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp index 10654ae..9f62b59 100644 --- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp +++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp @@ -56,7 +56,6 @@ QGLCustomShader::QGLCustomShader(QGLShader *shader) QGLCustomShader::~QGLCustomShader() { - delete m_shader; } void QGLCustomShader::updateUniforms(QGLShaderProgram *) diff --git a/src/opengl/qglpixmapfilter.cpp b/src/opengl/qglpixmapfilter.cpp index 662e818..06542a1 100644 --- a/src/opengl/qglpixmapfilter.cpp +++ b/src/opengl/qglpixmapfilter.cpp @@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE +static QCache shaderCache; void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const { @@ -321,9 +322,13 @@ bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QGLCustomShader *customShader = const_cast(this); if (!shader()) { - QGLShader *blurShader = new QGLShader(QGLShader::FragmentShader); - blurShader->compile(generateBlurShader(radius(), quality() == Qt::SmoothTransformation)); - + const QByteArray blurSource = generateBlurShader(radius(), quality() == Qt::SmoothTransformation); + QGLShader *blurShader = shaderCache.object(blurSource); + if (!blurShader) { + blurShader = new QGLShader(QGLShader::FragmentShader); + blurShader->compile(blurSource); + shaderCache.insert(blurSource, blurShader); + } customShader->setShader(blurShader); } @@ -404,6 +409,7 @@ QByteArray QGLPixmapBlurFilter::generateBlurShader(int radius, bool gaussianBlur Q_ASSERT(radius >= 1); QByteArray source; + source.reserve(1000); source.append("uniform highp vec2 invTextureSize;\n"); -- cgit v0.12