| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtOpenGL module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "private/qpixmapfilter_p.h" |
| #include "private/qpixmapdata_gl_p.h" |
| #include "private/qpaintengineex_opengl2_p.h" |
| #include "private/qglengineshadermanager_p.h" |
| #include "private/qpixmapdata_p.h" |
| #include "private/qimagepixmapcleanuphooks_p.h" |
| #include "qglpixmapfilter_p.h" |
| #include "qgraphicssystem_gl_p.h" |
| #include "qpaintengine_opengl_p.h" |
| #include "qcache.h" |
| |
| #include "qglframebufferobject.h" |
| #include "qglshaderprogram.h" |
| #include "qgl_p.h" |
| |
| #include "private/qapplication_p.h" |
| #include "private/qdrawhelper_p.h" |
| #include "private/qmemrotate_p.h" |
| #include "private/qmath_p.h" |
| #include "qmath.h" |
| |
| QT_BEGIN_NAMESPACE |
| |
| // qpixmapfilter.cpp |
| Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0); |
| Q_GUI_EXPORT QImage qt_halfScaled(const QImage &source); |
| |
| void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const |
| { |
| const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, QGLContext::BindOptions(QGLContext::DefaultBindOption | QGLContext::MemoryManagedBindOption)); |
| } |
| |
| void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF& source) const |
| { |
| processGL(painter, pos, src, source); |
| } |
| |
| class QGLPixmapColorizeFilter: public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapColorizeFilter> |
| { |
| public: |
| QGLPixmapColorizeFilter(); |
| |
| void setUniforms(QGLShaderProgram *program); |
| |
| protected: |
| bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &pixmap, const QRectF &srcRect) const; |
| }; |
| |
| class QGLPixmapConvolutionFilter: public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapConvolutionFilter> |
| { |
| public: |
| QGLPixmapConvolutionFilter(); |
| ~QGLPixmapConvolutionFilter(); |
| |
| void setUniforms(QGLShaderProgram *program); |
| |
| protected: |
| bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; |
| |
| private: |
| QByteArray generateConvolutionShader() const; |
| |
| mutable QSize m_srcSize; |
| mutable int m_prevKernelSize; |
| }; |
| |
| class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter> |
| { |
| public: |
| QGLPixmapBlurFilter(); |
| |
| protected: |
| bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; |
| }; |
| |
| class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapDropShadowFilter> |
| { |
| public: |
| QGLPixmapDropShadowFilter(); |
| |
| void setUniforms(QGLShaderProgram *program); |
| |
| protected: |
| bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const; |
| }; |
| |
| extern QGLWidget *qt_gl_share_widget(); |
| |
| QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *prototype) |
| { |
| Q_D(QGL2PaintEngineEx); |
| switch (type) { |
| case QPixmapFilter::ColorizeFilter: |
| if (!d->colorizeFilter) |
| d->colorizeFilter.reset(new QGLPixmapColorizeFilter); |
| return d->colorizeFilter.data(); |
| |
| case QPixmapFilter::BlurFilter: { |
| if (!d->blurFilter) |
| d->blurFilter.reset(new QGLPixmapBlurFilter()); |
| return d->blurFilter.data(); |
| } |
| |
| case QPixmapFilter::DropShadowFilter: { |
| if (!d->dropShadowFilter) |
| d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter()); |
| return d->dropShadowFilter.data(); |
| } |
| |
| case QPixmapFilter::ConvolutionFilter: |
| if (!d->convolutionFilter) |
| d->convolutionFilter.reset(new QGLPixmapConvolutionFilter); |
| return d->convolutionFilter.data(); |
| |
| default: break; |
| } |
| return QPaintEngineEx::pixmapFilter(type, prototype); |
| } |
| |
| static const char *qt_gl_colorize_filter = |
| "uniform lowp vec4 colorizeColor;" |
| "uniform lowp float colorizeStrength;" |
| "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)" |
| "{" |
| " lowp vec4 srcPixel = texture2D(src, srcCoords);" |
| " lowp float gray = dot(srcPixel.rgb, vec3(0.212671, 0.715160, 0.072169));" |
| " lowp vec3 colorized = 1.0-((1.0-gray)*(1.0-colorizeColor.rgb));" |
| " return vec4(mix(srcPixel.rgb, colorized * srcPixel.a, colorizeStrength), srcPixel.a);" |
| "}"; |
| |
| QGLPixmapColorizeFilter::QGLPixmapColorizeFilter() |
| { |
| setSource(qt_gl_colorize_filter); |
| } |
| |
| bool QGLPixmapColorizeFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const |
| { |
| QGLPixmapColorizeFilter *filter = const_cast<QGLPixmapColorizeFilter *>(this); |
| |
| filter->setOnPainter(painter); |
| painter->drawPixmap(pos, src); |
| filter->removeFromPainter(painter); |
| |
| return true; |
| } |
| |
| void QGLPixmapColorizeFilter::setUniforms(QGLShaderProgram *program) |
| { |
| program->setUniformValue("colorizeColor", color()); |
| program->setUniformValue("colorizeStrength", float(strength())); |
| } |
| |
| void QGLPixmapConvolutionFilter::setUniforms(QGLShaderProgram *program) |
| { |
| const qreal *kernel = convolutionKernel(); |
| int kernelWidth = columns(); |
| int kernelHeight = rows(); |
| int kernelSize = kernelWidth * kernelHeight; |
| |
| QVarLengthArray<GLfloat> matrix(kernelSize); |
| QVarLengthArray<GLfloat> offset(kernelSize * 2); |
| |
| for(int i = 0; i < kernelSize; ++i) |
| matrix[i] = kernel[i]; |
| |
| for(int y = 0; y < kernelHeight; ++y) { |
| for(int x = 0; x < kernelWidth; ++x) { |
| offset[(y * kernelWidth + x) * 2] = x - (kernelWidth / 2); |
| offset[(y * kernelWidth + x) * 2 + 1] = (kernelHeight / 2) - y; |
| } |
| } |
| |
| const qreal iw = 1.0 / m_srcSize.width(); |
| const qreal ih = 1.0 / m_srcSize.height(); |
| program->setUniformValue("inv_texture_size", iw, ih); |
| program->setUniformValueArray("matrix", matrix.constData(), kernelSize, 1); |
| program->setUniformValueArray("offset", offset.constData(), kernelSize, 2); |
| } |
| |
| // generates convolution filter code for arbitrary sized kernel |
| QByteArray QGLPixmapConvolutionFilter::generateConvolutionShader() const { |
| QByteArray code; |
| int kernelWidth = columns(); |
| int kernelHeight = rows(); |
| int kernelSize = kernelWidth * kernelHeight; |
| code.append("uniform highp vec2 inv_texture_size;\n" |
| "uniform mediump float matrix["); |
| code.append(QByteArray::number(kernelSize)); |
| code.append("];\n" |
| "uniform highp vec2 offset["); |
| code.append(QByteArray::number(kernelSize)); |
| code.append("];\n"); |
| code.append("lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {\n"); |
| |
| code.append(" int i = 0;\n" |
| " lowp vec4 sum = vec4(0.0);\n" |
| " for (i = 0; i < "); |
| code.append(QByteArray::number(kernelSize)); |
| code.append("; i++) {\n" |
| " sum += matrix[i] * texture2D(src,srcCoords+inv_texture_size*offset[i]);\n" |
| " }\n" |
| " return sum;\n" |
| "}"); |
| return code; |
| } |
| |
| QGLPixmapConvolutionFilter::QGLPixmapConvolutionFilter() |
| : m_prevKernelSize(-1) |
| { |
| } |
| |
| QGLPixmapConvolutionFilter::~QGLPixmapConvolutionFilter() |
| { |
| } |
| |
| bool QGLPixmapConvolutionFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const |
| { |
| QGLPixmapConvolutionFilter *filter = const_cast<QGLPixmapConvolutionFilter *>(this); |
| |
| m_srcSize = src.size(); |
| |
| int kernelSize = rows() * columns(); |
| if (m_prevKernelSize == -1 || m_prevKernelSize != kernelSize) { |
| filter->setSource(generateConvolutionShader()); |
| m_prevKernelSize = kernelSize; |
| } |
| |
| filter->setOnPainter(painter); |
| painter->drawPixmap(pos, src, srcRect); |
| filter->removeFromPainter(painter); |
| |
| return true; |
| } |
| |
| QGLPixmapBlurFilter::QGLPixmapBlurFilter() |
| { |
| } |
| |
| class QGLBlurTextureInfo |
| { |
| public: |
| QGLBlurTextureInfo(const QImage &image, GLuint tex, qreal r) |
| : m_texture(tex) |
| , m_radius(r) |
| { |
| m_paddedImage << image; |
| } |
| |
| ~QGLBlurTextureInfo() |
| { |
| glDeleteTextures(1, &m_texture); |
| } |
| |
| QImage paddedImage(int scaleLevel = 0) const; |
| GLuint texture() const { return m_texture; } |
| qreal radius() const { return m_radius; } |
| |
| private: |
| mutable QList<QImage> m_paddedImage; |
| GLuint m_texture; |
| qreal m_radius; |
| }; |
| |
| QImage QGLBlurTextureInfo::paddedImage(int scaleLevel) const |
| { |
| for (int i = m_paddedImage.size() - 1; i <= scaleLevel; ++i) |
| m_paddedImage << qt_halfScaled(m_paddedImage.at(i)); |
| |
| return m_paddedImage.at(scaleLevel); |
| } |
| |
| class QGLBlurTextureCache : public QObject |
| { |
| public: |
| static QGLBlurTextureCache *cacheForContext(const QGLContext *context); |
| |
| QGLBlurTextureCache(); |
| ~QGLBlurTextureCache(); |
| |
| QGLBlurTextureInfo *takeBlurTextureInfo(const QPixmap &pixmap); |
| bool hasBlurTextureInfo(quint64 cacheKey) const; |
| void insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info); |
| void clearBlurTextureInfo(quint64 cacheKey); |
| |
| void timerEvent(QTimerEvent *event); |
| |
| private: |
| static void pixmapDestroyed(QPixmapData *pixmap); |
| |
| QCache<quint64, QGLBlurTextureInfo > cache; |
| |
| static QList<QGLBlurTextureCache *> blurTextureCaches; |
| |
| int timerId; |
| }; |
| |
| QList<QGLBlurTextureCache *> QGLBlurTextureCache::blurTextureCaches; |
| |
| static void QGLBlurTextureCache_free(void *ptr) |
| { |
| delete reinterpret_cast<QGLBlurTextureCache *>(ptr); |
| } |
| |
| Q_GLOBAL_STATIC_WITH_ARGS(QGLContextResource, qt_blur_texture_caches, (QGLBlurTextureCache_free)) |
| |
| QGLBlurTextureCache::QGLBlurTextureCache() |
| : timerId(0) |
| { |
| cache.setMaxCost(4 * 1024 * 1024); |
| blurTextureCaches.append(this); |
| } |
| |
| QGLBlurTextureCache::~QGLBlurTextureCache() |
| { |
| blurTextureCaches.removeAt(blurTextureCaches.indexOf(this)); |
| } |
| |
| void QGLBlurTextureCache::timerEvent(QTimerEvent *) |
| { |
| killTimer(timerId); |
| timerId = 0; |
| |
| cache.clear(); |
| } |
| |
| QGLBlurTextureCache *QGLBlurTextureCache::cacheForContext(const QGLContext *context) |
| { |
| QGLBlurTextureCache *p = reinterpret_cast<QGLBlurTextureCache *>(qt_blur_texture_caches()->value(context)); |
| if (!p) { |
| p = new QGLBlurTextureCache; |
| qt_blur_texture_caches()->insert(context, p); |
| } |
| return p; |
| } |
| |
| QGLBlurTextureInfo *QGLBlurTextureCache::takeBlurTextureInfo(const QPixmap &pixmap) |
| { |
| return cache.take(pixmap.cacheKey()); |
| } |
| |
| void QGLBlurTextureCache::clearBlurTextureInfo(quint64 cacheKey) |
| { |
| cache.remove(cacheKey); |
| } |
| |
| bool QGLBlurTextureCache::hasBlurTextureInfo(quint64 cacheKey) const |
| { |
| return cache.contains(cacheKey); |
| } |
| |
| void QGLBlurTextureCache::insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info) |
| { |
| static bool hookAdded = false; |
| if (!hookAdded) { |
| QImagePixmapCleanupHooks::instance()->addPixmapDataDestructionHook(pixmapDestroyed); |
| QImagePixmapCleanupHooks::instance()->addPixmapDataModificationHook(pixmapDestroyed); |
| hookAdded = true; |
| } |
| |
| QImagePixmapCleanupHooks::enableCleanupHooks(pixmap); |
| cache.insert(pixmap.cacheKey(), info, pixmap.width() * pixmap.height()); |
| |
| if (timerId) |
| killTimer(timerId); |
| |
| timerId = startTimer(8000); |
| } |
| |
| void QGLBlurTextureCache::pixmapDestroyed(QPixmapData *pmd) |
| { |
| foreach (QGLBlurTextureCache *cache, blurTextureCaches) { |
| if (cache->hasBlurTextureInfo(pmd->cacheKey())) |
| cache->clearBlurTextureInfo(pmd->cacheKey()); |
| } |
| } |
| |
| static const int qAnimatedBlurLevelIncrement = 16; |
| static const int qMaxBlurHalfScaleLevel = 1; |
| |
| static GLuint generateBlurTexture(const QSize &size, GLenum format = GL_RGBA) |
| { |
| GLuint texture; |
| glGenTextures(1, &texture); |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0, format, |
| GL_UNSIGNED_BYTE, 0); |
| return texture; |
| } |
| |
| static inline uint nextMultiple(uint x, uint multiplier) |
| { |
| uint mod = x % multiplier; |
| if (mod == 0) |
| return x; |
| return x + multiplier - mod; |
| } |
| |
| Q_GUI_EXPORT void qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride, |
| quint32 *dest, int dstStride); |
| |
| bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const |
| { |
| if (radius() < 1) { |
| painter->drawPixmap(pos, src); |
| return true; |
| } |
| |
| qreal actualRadius = radius(); |
| |
| QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); |
| |
| QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx); |
| QGLBlurTextureInfo *info = 0; |
| int padding = nextMultiple(qCeil(actualRadius), qAnimatedBlurLevelIncrement); |
| QRect targetRect = src.rect().adjusted(-padding, -padding, padding, padding); |
| |
| // pad so that we'll be able to half-scale qMaxBlurHalfScaleLevel times |
| targetRect.setWidth((targetRect.width() + (qMaxBlurHalfScaleLevel-1)) & ~(qMaxBlurHalfScaleLevel-1)); |
| targetRect.setHeight((targetRect.height() + (qMaxBlurHalfScaleLevel-1)) & ~(qMaxBlurHalfScaleLevel-1)); |
| |
| QSize textureSize; |
| |
| info = blurTextureCache->takeBlurTextureInfo(src); |
| if (!info || info->radius() < actualRadius) { |
| QSize paddedSize = targetRect.size() / 2; |
| |
| QImage padded(paddedSize.height(), paddedSize.width(), QImage::Format_ARGB32_Premultiplied); |
| padded.fill(0); |
| |
| if (info) { |
| int oldPadding = qRound(info->radius()); |
| |
| QPainter p(&padded); |
| p.setCompositionMode(QPainter::CompositionMode_Source); |
| p.drawImage((padding - oldPadding) / 2, (padding - oldPadding) / 2, info->paddedImage()); |
| p.end(); |
| } else { |
| // TODO: combine byteswapping and memrotating into one by declaring |
| // custom GL_RGBA pixel type and qt_colorConvert template for it |
| QImage prepadded = qt_halfScaled(src.toImage()).convertToFormat(QImage::Format_ARGB32_Premultiplied); |
| |
| // byte-swap and memrotates in one go |
| qt_memrotate90_gl(reinterpret_cast<const quint32*>(prepadded.bits()), |
| prepadded.width(), prepadded.height(), prepadded.bytesPerLine(), |
| reinterpret_cast<quint32*>(padded.scanLine(padding / 2)) + padding / 2, |
| padded.bytesPerLine()); |
| } |
| |
| delete info; |
| info = new QGLBlurTextureInfo(padded, generateBlurTexture(paddedSize), padding); |
| |
| textureSize = paddedSize; |
| } else { |
| textureSize = QSize(info->paddedImage().height(), info->paddedImage().width()); |
| } |
| |
| actualRadius *= qreal(0.5); |
| int level = 1; |
| for (; level < qMaxBlurHalfScaleLevel; ++level) { |
| if (actualRadius <= 16) |
| break; |
| actualRadius *= qreal(0.5); |
| } |
| |
| const int s = (1 << level); |
| |
| int prepadding = qRound(info->radius()); |
| padding = qMin(prepadding, qCeil(actualRadius) << level); |
| targetRect = src.rect().adjusted(-padding, -padding, padding, padding); |
| |
| targetRect.setWidth(targetRect.width() & ~(s-1)); |
| targetRect.setHeight(targetRect.height() & ~(s-1)); |
| |
| int paddingDelta = (prepadding - padding) >> level; |
| |
| QRect subRect(paddingDelta, paddingDelta, targetRect.width() >> level, targetRect.height() >> level); |
| QImage sourceImage = info->paddedImage(level-1); |
| |
| QImage subImage(subRect.height(), subRect.width(), QImage::Format_ARGB32_Premultiplied); |
| qt_rectcopy((QRgb *)subImage.bits(), ((QRgb *)sourceImage.scanLine(paddingDelta)) + paddingDelta, |
| 0, 0, subRect.height(), subRect.width(), subImage.bytesPerLine(), sourceImage.bytesPerLine()); |
| |
| GLuint texture = info->texture(); |
| |
| qt_blurImage(subImage, actualRadius, blurHints() & QGraphicsBlurEffect::QualityHint, 1); |
| |
| // subtract one pixel off the end to prevent the bilinear sampling from sampling uninitialized data |
| QRect textureSubRect = subImage.rect().adjusted(0, 0, -1, -1); |
| QRectF targetRectF = QRectF(targetRect).adjusted(0, 0, -targetRect.width() / qreal(textureSize.width()), -targetRect.height() / qreal(textureSize.height())); |
| |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subImage.width(), subImage.height(), GL_RGBA, |
| GL_UNSIGNED_BYTE, const_cast<const QImage &>(subImage).bits()); |
| |
| QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); |
| painter->setRenderHint(QPainter::SmoothPixmapTransform); |
| |
| // texture is flipped on the y-axis |
| targetRectF = QRectF(targetRectF.x(), targetRectF.bottom(), targetRectF.width(), -targetRectF.height()); |
| engine->drawTexture(targetRectF.translated(pos), texture, textureSize, textureSubRect); |
| |
| blurTextureCache->insertBlurTextureInfo(src, info); |
| |
| return true; |
| } |
| |
| static const char *qt_gl_drop_shadow_filter = |
| "uniform lowp vec4 shadowColor;" |
| "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)" |
| "{" |
| " return shadowColor * texture2D(src, srcCoords.yx).a;" |
| "}"; |
| |
| |
| QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter() |
| { |
| setSource(qt_gl_drop_shadow_filter); |
| } |
| |
| bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const |
| { |
| QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this); |
| |
| qreal r = blurRadius(); |
| QRectF targetRectUnaligned = QRectF(src.rect()).translated(pos + offset()).adjusted(-r, -r, r, r); |
| QRect targetRect = targetRectUnaligned.toAlignedRect(); |
| |
| // ensure even dimensions (going to divide by two) |
| targetRect.setWidth((targetRect.width() + 1) & ~1); |
| targetRect.setHeight((targetRect.height() + 1) & ~1); |
| |
| QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext()); |
| QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx); |
| |
| QGLBlurTextureInfo *info = blurTextureCache->takeBlurTextureInfo(src); |
| if (!info || info->radius() != r) { |
| QImage half = qt_halfScaled(src.toImage().alphaChannel()); |
| |
| qreal rx = r + targetRect.left() - targetRectUnaligned.left(); |
| qreal ry = r + targetRect.top() - targetRectUnaligned.top(); |
| |
| QImage image = QImage(targetRect.size() / 2, QImage::Format_Indexed8); |
| image.setColorTable(half.colorTable()); |
| image.fill(0); |
| int dx = qRound(rx * qreal(0.5)); |
| int dy = qRound(ry * qreal(0.5)); |
| qt_rectcopy(image.bits(), half.bits(), dx, dy, |
| half.width(), half.height(), |
| image.bytesPerLine(), half.bytesPerLine()); |
| |
| qt_blurImage(image, r * qreal(0.5), false, 1); |
| |
| GLuint texture; |
| glGenTextures(1, &texture); |
| glBindTexture(GL_TEXTURE_2D, texture); |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, image.width(), image.height(), |
| 0, GL_ALPHA, GL_UNSIGNED_BYTE, image.bits()); |
| |
| info = new QGLBlurTextureInfo(image, texture, r); |
| } |
| |
| GLuint texture = info->texture(); |
| |
| filter->setOnPainter(painter); |
| |
| QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine()); |
| painter->setRenderHint(QPainter::SmoothPixmapTransform); |
| |
| engine->drawTexture(targetRect, texture, info->paddedImage().size(), info->paddedImage().rect()); |
| |
| filter->removeFromPainter(painter); |
| |
| // Now draw the actual pixmap over the top. |
| painter->drawPixmap(pos, src, srcRect); |
| |
| blurTextureCache->insertBlurTextureInfo(src, info); |
| |
| return true; |
| } |
| |
| void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program) |
| { |
| QColor col = color(); |
| qreal alpha = col.alphaF(); |
| program->setUniformValue("shadowColor", col.redF() * alpha, |
| col.greenF() * alpha, |
| col.blueF() * alpha, |
| alpha); |
| } |
| |
| QT_END_NAMESPACE |