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