1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtOpenGL module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "private/qpixmapfilter_p.h"
43 #include "private/qpixmapdata_gl_p.h"
44 #include "private/qpaintengineex_opengl2_p.h"
45 #include "private/qglengineshadermanager_p.h"
46 #include "private/qpixmapdata_p.h"
47 #include "private/qimagepixmapcleanuphooks_p.h"
48 #include "qglpixmapfilter_p.h"
49 #include "qgraphicssystem_gl_p.h"
50 #include "qpaintengine_opengl_p.h"
51 #include "qcache.h"
52 
53 #include "qglframebufferobject.h"
54 #include "qglshaderprogram.h"
55 #include "qgl_p.h"
56 
57 #include "private/qapplication_p.h"
58 #include "private/qdrawhelper_p.h"
59 #include "private/qmemrotate_p.h"
60 #include "private/qmath_p.h"
61 #include "qmath.h"
62 
63 QT_BEGIN_NAMESPACE
64 
65 // qpixmapfilter.cpp
66 Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
67 Q_GUI_EXPORT QImage qt_halfScaled(const QImage &source);
68 
bindTexture(const QPixmap & src) const69 void QGLPixmapFilterBase::bindTexture(const QPixmap &src) const
70 {
71     const_cast<QGLContext *>(QGLContext::currentContext())->d_func()->bindTexture(src, GL_TEXTURE_2D, GL_RGBA, QGLContext::BindOptions(QGLContext::DefaultBindOption | QGLContext::MemoryManagedBindOption));
72 }
73 
drawImpl(QPainter * painter,const QPointF & pos,const QPixmap & src,const QRectF & source) const74 void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF& source) const
75 {
76     processGL(painter, pos, src, source);
77 }
78 
79 class QGLPixmapColorizeFilter: public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapColorizeFilter>
80 {
81 public:
82     QGLPixmapColorizeFilter();
83 
84     void setUniforms(QGLShaderProgram *program);
85 
86 protected:
87     bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &pixmap, const QRectF &srcRect) const;
88 };
89 
90 class QGLPixmapConvolutionFilter: public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapConvolutionFilter>
91 {
92 public:
93     QGLPixmapConvolutionFilter();
94     ~QGLPixmapConvolutionFilter();
95 
96     void setUniforms(QGLShaderProgram *program);
97 
98 protected:
99     bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
100 
101 private:
102     QByteArray generateConvolutionShader() const;
103 
104     mutable QSize m_srcSize;
105     mutable int m_prevKernelSize;
106 };
107 
108 class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter>
109 {
110 public:
111     QGLPixmapBlurFilter();
112 
113 protected:
114     bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
115 };
116 
117 class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapDropShadowFilter>
118 {
119 public:
120     QGLPixmapDropShadowFilter();
121 
122     void setUniforms(QGLShaderProgram *program);
123 
124 protected:
125     bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
126 };
127 
128 extern const QGLContext *qt_gl_share_context();
129 
pixmapFilter(int type,const QPixmapFilter * prototype)130 QPixmapFilter *QGL2PaintEngineEx::pixmapFilter(int type, const QPixmapFilter *prototype)
131 {
132     Q_D(QGL2PaintEngineEx);
133     switch (type) {
134     case QPixmapFilter::ColorizeFilter:
135         if (!d->colorizeFilter)
136             d->colorizeFilter.reset(new QGLPixmapColorizeFilter);
137         return d->colorizeFilter.data();
138 
139     case QPixmapFilter::BlurFilter: {
140         if (!d->blurFilter)
141             d->blurFilter.reset(new QGLPixmapBlurFilter());
142         return d->blurFilter.data();
143         }
144 
145     case QPixmapFilter::DropShadowFilter: {
146         if (!d->dropShadowFilter)
147             d->dropShadowFilter.reset(new QGLPixmapDropShadowFilter());
148         return d->dropShadowFilter.data();
149         }
150 
151     case QPixmapFilter::ConvolutionFilter:
152         if (!d->convolutionFilter)
153             d->convolutionFilter.reset(new QGLPixmapConvolutionFilter);
154         return d->convolutionFilter.data();
155 
156     default: break;
157     }
158     return QPaintEngineEx::pixmapFilter(type, prototype);
159 }
160 
161 static const char *qt_gl_colorize_filter =
162         "uniform lowp vec4 colorizeColor;"
163         "uniform lowp float colorizeStrength;"
164         "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)"
165         "{"
166         "        lowp vec4 srcPixel = texture2D(src, srcCoords);"
167         "        lowp float gray = dot(srcPixel.rgb, vec3(0.212671, 0.715160, 0.072169));"
168         "        lowp vec3 colorized = 1.0-((1.0-gray)*(1.0-colorizeColor.rgb));"
169         "        return vec4(mix(srcPixel.rgb, colorized * srcPixel.a, colorizeStrength), srcPixel.a);"
170         "}";
171 
QGLPixmapColorizeFilter()172 QGLPixmapColorizeFilter::QGLPixmapColorizeFilter()
173 {
174     setSource(qt_gl_colorize_filter);
175 }
176 
processGL(QPainter * painter,const QPointF & pos,const QPixmap & src,const QRectF &) const177 bool QGLPixmapColorizeFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const
178 {
179     QGLPixmapColorizeFilter *filter = const_cast<QGLPixmapColorizeFilter *>(this);
180 
181     filter->setOnPainter(painter);
182     painter->drawPixmap(pos, src);
183     filter->removeFromPainter(painter);
184 
185     return true;
186 }
187 
setUniforms(QGLShaderProgram * program)188 void QGLPixmapColorizeFilter::setUniforms(QGLShaderProgram *program)
189 {
190     program->setUniformValue("colorizeColor", color());
191     program->setUniformValue("colorizeStrength", float(strength()));
192 }
193 
setUniforms(QGLShaderProgram * program)194 void QGLPixmapConvolutionFilter::setUniforms(QGLShaderProgram *program)
195 {
196     const qreal *kernel = convolutionKernel();
197     int kernelWidth = columns();
198     int kernelHeight = rows();
199     int kernelSize = kernelWidth * kernelHeight;
200 
201     QVarLengthArray<GLfloat> matrix(kernelSize);
202     QVarLengthArray<GLfloat> offset(kernelSize * 2);
203 
204     for(int i = 0; i < kernelSize; ++i)
205         matrix[i] = kernel[i];
206 
207     for(int y = 0; y < kernelHeight; ++y) {
208         for(int x = 0; x < kernelWidth; ++x) {
209             offset[(y * kernelWidth + x) * 2] = x - (kernelWidth / 2);
210             offset[(y * kernelWidth + x) * 2 + 1] = (kernelHeight / 2) - y;
211         }
212     }
213 
214     const qreal iw = 1.0 / m_srcSize.width();
215     const qreal ih = 1.0 / m_srcSize.height();
216     program->setUniformValue("inv_texture_size", iw, ih);
217     program->setUniformValueArray("matrix", matrix.constData(), kernelSize, 1);
218     program->setUniformValueArray("offset", offset.constData(), kernelSize, 2);
219 }
220 
221 // generates convolution filter code for arbitrary sized kernel
generateConvolutionShader() const222 QByteArray QGLPixmapConvolutionFilter::generateConvolutionShader() const {
223     QByteArray code;
224     int kernelWidth = columns();
225     int kernelHeight = rows();
226     int kernelSize = kernelWidth * kernelHeight;
227     code.append("uniform highp vec2 inv_texture_size;\n"
228                 "uniform mediump float matrix[");
229     code.append(QByteArray::number(kernelSize));
230     code.append("];\n"
231                 "uniform highp vec2 offset[");
232     code.append(QByteArray::number(kernelSize));
233     code.append("];\n");
234     code.append("lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords) {\n");
235 
236     code.append("  int i = 0;\n"
237                 "  lowp vec4 sum = vec4(0.0);\n"
238                 "  for (i = 0; i < ");
239     code.append(QByteArray::number(kernelSize));
240     code.append("; i++) {\n"
241                 "    sum += matrix[i] * texture2D(src,srcCoords+inv_texture_size*offset[i]);\n"
242                 "  }\n"
243                 "  return sum;\n"
244                 "}");
245     return code;
246 }
247 
QGLPixmapConvolutionFilter()248 QGLPixmapConvolutionFilter::QGLPixmapConvolutionFilter()
249     : m_prevKernelSize(-1)
250 {
251 }
252 
~QGLPixmapConvolutionFilter()253 QGLPixmapConvolutionFilter::~QGLPixmapConvolutionFilter()
254 {
255 }
256 
processGL(QPainter * painter,const QPointF & pos,const QPixmap & src,const QRectF & srcRect) const257 bool QGLPixmapConvolutionFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
258 {
259     QGLPixmapConvolutionFilter *filter = const_cast<QGLPixmapConvolutionFilter *>(this);
260 
261     m_srcSize = src.size();
262 
263     int kernelSize = rows() * columns();
264     if (m_prevKernelSize == -1 || m_prevKernelSize != kernelSize) {
265         filter->setSource(generateConvolutionShader());
266         m_prevKernelSize = kernelSize;
267     }
268 
269     filter->setOnPainter(painter);
270     painter->drawPixmap(pos, src, srcRect);
271     filter->removeFromPainter(painter);
272 
273     return true;
274 }
275 
QGLPixmapBlurFilter()276 QGLPixmapBlurFilter::QGLPixmapBlurFilter()
277 {
278 }
279 
280 class QGLBlurTextureInfo
281 {
282 public:
QGLBlurTextureInfo(const QImage & image,GLuint tex,qreal r)283     QGLBlurTextureInfo(const QImage &image, GLuint tex, qreal r)
284         : m_texture(tex)
285         , m_radius(r)
286     {
287         m_paddedImage << image;
288     }
289 
~QGLBlurTextureInfo()290     ~QGLBlurTextureInfo()
291     {
292         glDeleteTextures(1, &m_texture);
293     }
294 
295     QImage paddedImage(int scaleLevel = 0) const;
texture() const296     GLuint texture() const { return m_texture; }
radius() const297     qreal radius() const { return m_radius; }
298 
299 private:
300     mutable QList<QImage> m_paddedImage;
301     GLuint m_texture;
302     qreal m_radius;
303 };
304 
paddedImage(int scaleLevel) const305 QImage QGLBlurTextureInfo::paddedImage(int scaleLevel) const
306 {
307     for (int i = m_paddedImage.size() - 1; i <= scaleLevel; ++i)
308         m_paddedImage << qt_halfScaled(m_paddedImage.at(i));
309 
310     return m_paddedImage.at(scaleLevel);
311 }
312 
313 class QGLBlurTextureCache : public QObject
314 {
315 public:
316     static QGLBlurTextureCache *cacheForContext(const QGLContext *context);
317 
318     QGLBlurTextureCache(const QGLContext *);
319     ~QGLBlurTextureCache();
320 
321     QGLBlurTextureInfo *takeBlurTextureInfo(const QPixmap &pixmap);
322     bool hasBlurTextureInfo(quint64 cacheKey) const;
323     void insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info);
324     void clearBlurTextureInfo(quint64 cacheKey);
325 
326     void timerEvent(QTimerEvent *event);
327 
328 private:
329     static void pixmapDestroyed(QPixmapData *pixmap);
330 
331     QCache<quint64, QGLBlurTextureInfo > cache;
332 
333     static QList<QGLBlurTextureCache *> blurTextureCaches;
334 
335     int timerId;
336 };
337 
338 QList<QGLBlurTextureCache *> QGLBlurTextureCache::blurTextureCaches;
Q_GLOBAL_STATIC(QGLContextGroupResource<QGLBlurTextureCache>,qt_blur_texture_caches)339 Q_GLOBAL_STATIC(QGLContextGroupResource<QGLBlurTextureCache>, qt_blur_texture_caches)
340 
341 QGLBlurTextureCache::QGLBlurTextureCache(const QGLContext *)
342     : timerId(0)
343 {
344     cache.setMaxCost(4 * 1024 * 1024);
345     blurTextureCaches.append(this);
346 }
347 
~QGLBlurTextureCache()348 QGLBlurTextureCache::~QGLBlurTextureCache()
349 {
350     blurTextureCaches.removeAt(blurTextureCaches.indexOf(this));
351 }
352 
timerEvent(QTimerEvent *)353 void QGLBlurTextureCache::timerEvent(QTimerEvent *)
354 {
355     killTimer(timerId);
356     timerId = 0;
357 
358     cache.clear();
359 }
360 
cacheForContext(const QGLContext * context)361 QGLBlurTextureCache *QGLBlurTextureCache::cacheForContext(const QGLContext *context)
362 {
363     return qt_blur_texture_caches()->value(context);
364 }
365 
takeBlurTextureInfo(const QPixmap & pixmap)366 QGLBlurTextureInfo *QGLBlurTextureCache::takeBlurTextureInfo(const QPixmap &pixmap)
367 {
368     return cache.take(pixmap.cacheKey());
369 }
370 
clearBlurTextureInfo(quint64 cacheKey)371 void QGLBlurTextureCache::clearBlurTextureInfo(quint64 cacheKey)
372 {
373     cache.remove(cacheKey);
374 }
375 
hasBlurTextureInfo(quint64 cacheKey) const376 bool QGLBlurTextureCache::hasBlurTextureInfo(quint64 cacheKey) const
377 {
378     return cache.contains(cacheKey);
379 }
380 
insertBlurTextureInfo(const QPixmap & pixmap,QGLBlurTextureInfo * info)381 void QGLBlurTextureCache::insertBlurTextureInfo(const QPixmap &pixmap, QGLBlurTextureInfo *info)
382 {
383     static bool hookAdded = false;
384     if (!hookAdded) {
385         QImagePixmapCleanupHooks::instance()->addPixmapDataDestructionHook(pixmapDestroyed);
386         QImagePixmapCleanupHooks::instance()->addPixmapDataModificationHook(pixmapDestroyed);
387         hookAdded = true;
388     }
389 
390     QImagePixmapCleanupHooks::enableCleanupHooks(pixmap);
391     cache.insert(pixmap.cacheKey(), info, pixmap.width() * pixmap.height());
392 
393     if (timerId)
394         killTimer(timerId);
395 
396     timerId = startTimer(8000);
397 }
398 
pixmapDestroyed(QPixmapData * pmd)399 void QGLBlurTextureCache::pixmapDestroyed(QPixmapData *pmd)
400 {
401     foreach (QGLBlurTextureCache *cache, blurTextureCaches) {
402         if (cache->hasBlurTextureInfo(pmd->cacheKey()))
403             cache->clearBlurTextureInfo(pmd->cacheKey());
404     }
405 }
406 
407 static const int qAnimatedBlurLevelIncrement = 16;
408 static const int qMaxBlurHalfScaleLevel = 1;
409 
generateBlurTexture(const QSize & size,GLenum format=GL_RGBA)410 static GLuint generateBlurTexture(const QSize &size, GLenum format = GL_RGBA)
411 {
412     GLuint texture;
413     glGenTextures(1, &texture);
414     glBindTexture(GL_TEXTURE_2D, texture);
415     glTexImage2D(GL_TEXTURE_2D, 0, format, size.width(), size.height(), 0, format,
416                  GL_UNSIGNED_BYTE, 0);
417     return texture;
418 }
419 
nextMultiple(uint x,uint multiplier)420 static inline uint nextMultiple(uint x, uint multiplier)
421 {
422     uint mod = x % multiplier;
423     if (mod == 0)
424         return x;
425     return x + multiplier - mod;
426 }
427 
428 Q_GUI_EXPORT void qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride,
429                        quint32 *dest, int dstStride);
430 
processGL(QPainter * painter,const QPointF & pos,const QPixmap & src,const QRectF &) const431 bool QGLPixmapBlurFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &) const
432 {
433     if (radius() < 1) {
434         painter->drawPixmap(pos, src);
435         return true;
436     }
437 
438     qreal actualRadius = radius();
439 
440     QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
441 
442     QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx);
443     QGLBlurTextureInfo *info = 0;
444     int padding = nextMultiple(qCeil(actualRadius), qAnimatedBlurLevelIncrement);
445     QRect targetRect = src.rect().adjusted(-padding, -padding, padding, padding);
446 
447     // pad so that we'll be able to half-scale qMaxBlurHalfScaleLevel times
448     targetRect.setWidth((targetRect.width() + (qMaxBlurHalfScaleLevel-1)) & ~(qMaxBlurHalfScaleLevel-1));
449     targetRect.setHeight((targetRect.height() + (qMaxBlurHalfScaleLevel-1)) & ~(qMaxBlurHalfScaleLevel-1));
450 
451     QSize textureSize;
452 
453     info = blurTextureCache->takeBlurTextureInfo(src);
454     if (!info || info->radius() < actualRadius) {
455         QSize paddedSize = targetRect.size() / 2;
456 
457         QImage padded(paddedSize.height(), paddedSize.width(), QImage::Format_ARGB32_Premultiplied);
458         padded.fill(0);
459 
460         if (info) {
461             int oldPadding = qRound(info->radius());
462 
463             QPainter p(&padded);
464             p.setCompositionMode(QPainter::CompositionMode_Source);
465             p.drawImage((padding - oldPadding) / 2, (padding - oldPadding) / 2, info->paddedImage());
466             p.end();
467         } else {
468             // TODO: combine byteswapping and memrotating into one by declaring
469             // custom GL_RGBA pixel type and qt_colorConvert template for it
470             QImage prepadded = qt_halfScaled(src.toImage()).convertToFormat(QImage::Format_ARGB32_Premultiplied);
471 
472             // byte-swap and memrotates in one go
473             qt_memrotate90_gl(reinterpret_cast<const quint32*>(prepadded.bits()),
474                               prepadded.width(), prepadded.height(), prepadded.bytesPerLine(),
475                               reinterpret_cast<quint32*>(padded.scanLine(padding / 2)) + padding / 2,
476                               padded.bytesPerLine());
477         }
478 
479         delete info;
480         info = new QGLBlurTextureInfo(padded, generateBlurTexture(paddedSize), padding);
481 
482         textureSize = paddedSize;
483     } else {
484         textureSize = QSize(info->paddedImage().height(), info->paddedImage().width());
485     }
486 
487     actualRadius *= qreal(0.5);
488     int level = 1;
489     for (; level < qMaxBlurHalfScaleLevel; ++level) {
490         if (actualRadius <= 16)
491             break;
492         actualRadius *= qreal(0.5);
493     }
494 
495     const int s = (1 << level);
496 
497     int prepadding = qRound(info->radius());
498     padding = qMin(prepadding, qCeil(actualRadius) << level);
499     targetRect = src.rect().adjusted(-padding, -padding, padding, padding);
500 
501     targetRect.setWidth(targetRect.width() & ~(s-1));
502     targetRect.setHeight(targetRect.height() & ~(s-1));
503 
504     int paddingDelta = (prepadding - padding) >> level;
505 
506     QRect subRect(paddingDelta, paddingDelta, targetRect.width() >> level, targetRect.height() >> level);
507     QImage sourceImage = info->paddedImage(level-1);
508 
509     QImage subImage(subRect.height(), subRect.width(), QImage::Format_ARGB32_Premultiplied);
510     qt_rectcopy((QRgb *)subImage.bits(), ((QRgb *)sourceImage.scanLine(paddingDelta)) + paddingDelta,
511                 0, 0, subRect.height(), subRect.width(), subImage.bytesPerLine(), sourceImage.bytesPerLine());
512 
513     GLuint texture = info->texture();
514 
515     qt_blurImage(subImage, actualRadius, blurHints() & QGraphicsBlurEffect::QualityHint, 1);
516 
517     // subtract one pixel off the end to prevent the bilinear sampling from sampling uninitialized data
518     QRect textureSubRect = subImage.rect().adjusted(0, 0, -1, -1);
519     QRectF targetRectF = QRectF(targetRect).adjusted(0, 0, -targetRect.width() / qreal(textureSize.width()), -targetRect.height() / qreal(textureSize.height()));
520 
521     glBindTexture(GL_TEXTURE_2D, texture);
522     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subImage.width(), subImage.height(), GL_RGBA,
523             GL_UNSIGNED_BYTE, const_cast<const QImage &>(subImage).bits());
524 
525     QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
526     painter->setRenderHint(QPainter::SmoothPixmapTransform);
527 
528     // texture is flipped on the y-axis
529     targetRectF = QRectF(targetRectF.x(), targetRectF.bottom(), targetRectF.width(), -targetRectF.height());
530     engine->drawTexture(targetRectF.translated(pos), texture, textureSize, textureSubRect);
531 
532     blurTextureCache->insertBlurTextureInfo(src, info);
533 
534     return true;
535 }
536 
537 static const char *qt_gl_drop_shadow_filter =
538         "uniform lowp vec4 shadowColor;"
539         "lowp vec4 customShader(lowp sampler2D src, highp vec2 srcCoords)"
540         "{"
541         "    return shadowColor * texture2D(src, srcCoords.yx).a;"
542         "}";
543 
544 
QGLPixmapDropShadowFilter()545 QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter()
546 {
547     setSource(qt_gl_drop_shadow_filter);
548 }
549 
processGL(QPainter * painter,const QPointF & pos,const QPixmap & src,const QRectF & srcRect) const550 bool QGLPixmapDropShadowFilter::processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const
551 {
552     QGLPixmapDropShadowFilter *filter = const_cast<QGLPixmapDropShadowFilter *>(this);
553 
554     qreal r = blurRadius();
555     QRectF targetRectUnaligned = QRectF(src.rect()).translated(pos + offset()).adjusted(-r, -r, r, r);
556     QRect targetRect = targetRectUnaligned.toAlignedRect();
557 
558     // ensure even dimensions (going to divide by two)
559     targetRect.setWidth((targetRect.width() + 1) & ~1);
560     targetRect.setHeight((targetRect.height() + 1) & ~1);
561 
562     QGLContext *ctx = const_cast<QGLContext *>(QGLContext::currentContext());
563     QGLBlurTextureCache *blurTextureCache = QGLBlurTextureCache::cacheForContext(ctx);
564 
565     QGLBlurTextureInfo *info = blurTextureCache->takeBlurTextureInfo(src);
566     if (!info || info->radius() != r) {
567         QImage half = qt_halfScaled(src.toImage().alphaChannel());
568 
569         qreal rx = r + targetRect.left() - targetRectUnaligned.left();
570         qreal ry = r + targetRect.top() - targetRectUnaligned.top();
571 
572         QImage image = QImage(targetRect.size() / 2, QImage::Format_Indexed8);
573         image.setColorTable(half.colorTable());
574         image.fill(0);
575         int dx = qRound(rx * qreal(0.5));
576         int dy = qRound(ry * qreal(0.5));
577         qt_rectcopy(image.bits(), half.bits(), dx, dy,
578                     half.width(), half.height(),
579                     image.bytesPerLine(), half.bytesPerLine());
580 
581         qt_blurImage(image, r * qreal(0.5), false, 1);
582 
583         GLuint texture;
584         glGenTextures(1, &texture);
585         glBindTexture(GL_TEXTURE_2D, texture);
586         glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, image.width(), image.height(),
587                      0, GL_ALPHA, GL_UNSIGNED_BYTE, image.bits());
588 
589         info = new QGLBlurTextureInfo(image, texture, r);
590     }
591 
592     GLuint texture = info->texture();
593 
594     filter->setOnPainter(painter);
595 
596     QGL2PaintEngineEx *engine = static_cast<QGL2PaintEngineEx *>(painter->paintEngine());
597     painter->setRenderHint(QPainter::SmoothPixmapTransform);
598 
599     engine->drawTexture(targetRect, texture, info->paddedImage().size(), info->paddedImage().rect());
600 
601     filter->removeFromPainter(painter);
602 
603     // Now draw the actual pixmap over the top.
604     painter->drawPixmap(pos, src, srcRect);
605 
606     blurTextureCache->insertBlurTextureInfo(src, info);
607 
608     return true;
609 }
610 
setUniforms(QGLShaderProgram * program)611 void QGLPixmapDropShadowFilter::setUniforms(QGLShaderProgram *program)
612 {
613     QColor col = color();
614     qreal alpha = col.alphaF();
615     program->setUniformValue("shadowColor", col.redF() * alpha,
616                                             col.greenF() * alpha,
617                                             col.blueF() * alpha,
618                                             alpha);
619 }
620 
621 QT_END_NAMESPACE
622