1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
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 Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/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 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
36**
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
63QT_BEGIN_NAMESPACE
64
65// qpixmapfilter.cpp
66Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
67Q_GUI_EXPORT QImage qt_halfScaled(const QImage &source);
68
69void 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
74void QGLPixmapFilterBase::drawImpl(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF& source) const
75{
76 processGL(painter, pos, src, source);
77}
78
79class QGLPixmapColorizeFilter: public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapColorizeFilter>
80{
81public:
82 QGLPixmapColorizeFilter();
83
84 void setUniforms(QGLShaderProgram *program);
85
86protected:
87 bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &pixmap, const QRectF &srcRect) const;
88};
89
90class QGLPixmapConvolutionFilter: public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapConvolutionFilter>
91{
92public:
93 QGLPixmapConvolutionFilter();
94 ~QGLPixmapConvolutionFilter();
95
96 void setUniforms(QGLShaderProgram *program);
97
98protected:
99 bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
100
101private:
102 QByteArray generateConvolutionShader() const;
103
104 mutable QSize m_srcSize;
105 mutable int m_prevKernelSize;
106};
107
108class QGLPixmapBlurFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapBlurFilter>
109{
110public:
111 QGLPixmapBlurFilter();
112
113protected:
114 bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
115};
116
117class QGLPixmapDropShadowFilter : public QGLCustomShaderStage, public QGLPixmapFilter<QPixmapDropShadowFilter>
118{
119public:
120 QGLPixmapDropShadowFilter();
121
122 void setUniforms(QGLShaderProgram *program);
123
124protected:
125 bool processGL(QPainter *painter, const QPointF &pos, const QPixmap &src, const QRectF &srcRect) const;
126};
127
128extern const QGLContext *qt_gl_share_context();
129
130QPixmapFilter *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
161static 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
172QGLPixmapColorizeFilter::QGLPixmapColorizeFilter()
173{
174 setSource(qt_gl_colorize_filter);
175}
176
177bool 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
188void QGLPixmapColorizeFilter::setUniforms(QGLShaderProgram *program)
189{
190 program->setUniformValue("colorizeColor", color());
191 program->setUniformValue("colorizeStrength", float(strength()));
192}
193
194void 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
222QByteArray 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
248QGLPixmapConvolutionFilter::QGLPixmapConvolutionFilter()
249 : m_prevKernelSize(-1)
250{
251}
252
253QGLPixmapConvolutionFilter::~QGLPixmapConvolutionFilter()
254{
255}
256
257bool 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
276QGLPixmapBlurFilter::QGLPixmapBlurFilter()
277{
278}
279
280class QGLBlurTextureInfo
281{
282public:
283 QGLBlurTextureInfo(const QImage &image, GLuint tex, qreal r)
284 : m_texture(tex)
285 , m_radius(r)
286 {
287 m_paddedImage << image;
288 }
289
290 ~QGLBlurTextureInfo()
291 {
292 glDeleteTextures(1, &m_texture);
293 }
294
295 QImage paddedImage(int scaleLevel = 0) const;
296 GLuint texture() const { return m_texture; }
297 qreal radius() const { return m_radius; }
298
299private:
300 mutable QList<QImage> m_paddedImage;
301 GLuint m_texture;
302 qreal m_radius;
303};
304
305QImage 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
313class QGLBlurTextureCache : public QObject
314{
315public:
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
328private:
329 static void pixmapDestroyed(QPixmapData *pixmap);
330
331 QCache<quint64, QGLBlurTextureInfo > cache;
332
333 static QList<QGLBlurTextureCache *> blurTextureCaches;
334
335 int timerId;
336};
337
338QList<QGLBlurTextureCache *> QGLBlurTextureCache::blurTextureCaches;
339Q_GLOBAL_STATIC(QGLContextGroupResource<QGLBlurTextureCache>, qt_blur_texture_caches)
340
341QGLBlurTextureCache::QGLBlurTextureCache(const QGLContext *)
342 : timerId(0)
343{
344 cache.setMaxCost(4 * 1024 * 1024);
345 blurTextureCaches.append(this);
346}
347
348QGLBlurTextureCache::~QGLBlurTextureCache()
349{
350 blurTextureCaches.removeAt(blurTextureCaches.indexOf(this));
351}
352
353void QGLBlurTextureCache::timerEvent(QTimerEvent *)
354{
355 killTimer(timerId);
356 timerId = 0;
357
358 cache.clear();
359}
360
361QGLBlurTextureCache *QGLBlurTextureCache::cacheForContext(const QGLContext *context)
362{
363 return qt_blur_texture_caches()->value(context);
364}
365
366QGLBlurTextureInfo *QGLBlurTextureCache::takeBlurTextureInfo(const QPixmap &pixmap)
367{
368 return cache.take(pixmap.cacheKey());
369}
370
371void QGLBlurTextureCache::clearBlurTextureInfo(quint64 cacheKey)
372{
373 cache.remove(cacheKey);
374}
375
376bool QGLBlurTextureCache::hasBlurTextureInfo(quint64 cacheKey) const
377{
378 return cache.contains(cacheKey);
379}
380
381void 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
399void QGLBlurTextureCache::pixmapDestroyed(QPixmapData *pmd)
400{
401 foreach (QGLBlurTextureCache *cache, blurTextureCaches) {
402 if (cache->hasBlurTextureInfo(pmd->cacheKey()))
403 cache->clearBlurTextureInfo(pmd->cacheKey());
404 }
405}
406
407static const int qAnimatedBlurLevelIncrement = 16;
408static const int qMaxBlurHalfScaleLevel = 1;
409
410static 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
420static 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
428Q_GUI_EXPORT void qt_memrotate90_gl(const quint32 *src, int srcWidth, int srcHeight, int srcStride,
429 quint32 *dest, int dstStride);
430
431bool 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
537static 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
545QGLPixmapDropShadowFilter::QGLPixmapDropShadowFilter()
546{
547 setSource(qt_gl_drop_shadow_filter);
548}
549
550bool 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
611void 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
621QT_END_NAMESPACE
622