1
/****************************************************************************
2
**
3
** Copyright (C) 2011 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 QtGui 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 <qglobal.h>
43
44
#include <QDebug>
45
46
#include "qpainter.h"
47
#include "qpixmap.h"
48
#include "qpixmapfilter_p.h"
49
#include "qvarlengtharray.h"
50
51
#include "private/qapplication_p.h"
52
#include "private/qgraphicssystem_p.h"
53
#include "private/qpaintengineex_p.h"
54
#include "private/qpaintengine_raster_p.h"
55
#include "qmath.h"
56
#include "private/qmath_p.h"
57
#include "private/qmemrotate_p.h"
58
#include "private/qdrawhelper_p.h"
59
60
#ifndef QT_NO_GRAPHICSEFFECT
61
QT_BEGIN_NAMESPACE
62
63
class QPixmapFilterPrivate : public QObjectPrivate
64
{
65
    Q_DECLARE_PUBLIC(QPixmapFilter)
66
public:
67
    QPixmapFilter::FilterType type;
68
};
69
70
/*!
71
    \class QPixmapFilter
72
    \since 4.5
73
    \ingroup painting
74
75
    \brief The QPixmapFilter class provides the basic functionality for
76
    pixmap filter classes. Pixmap filter can be for example colorize or blur.
77
78
    QPixmapFilter is the base class for every pixmap filter. QPixmapFilter is
79
    an abstract class and cannot itself be instantiated. It provides a standard
80
    interface for filter processing.
81
82
    \internal
83
*/
84
85
/*!
86
    \enum QPixmapFilter::FilterType
87
88
    \internal
89
90
    This enum describes the types of filter that can be applied to pixmaps.
91
92
    \value ConvolutionFilter  A filter that is used to calculate the convolution
93
                              of the image with a kernel. See
94
                              QPixmapConvolutionFilter for more information.
95
    \value ColorizeFilter     A filter that is used to change the overall color
96
                              of an image. See QPixmapColorizeFilter for more
97
                              information.
98
    \value DropShadowFilter   A filter that is used to add a drop shadow to an
99
                              image. See QPixmapDropShadowFilter for more
100
                              information.
101
    \value BlurFilter         A filter that is used to blur an image using
102
                              a simple blur radius. See QPixmapBlurFilter
103
                              for more information.
104
105
    \value UserFilter   The first filter type that can be used for
106
                        application-specific purposes.
107
*/
108
109
110
/*!
111
    Constructs a default QPixmapFilter with the given \a type.
112
113
    This constructor should be used when subclassing QPixmapFilter to
114
    create custom user filters.
115
116
    \internal
117
*/
118
QPixmapFilter::QPixmapFilter(FilterType type, QObject *parent)
119
    : QObject(*new QPixmapFilterPrivate, parent)
120
{
121
    d_func()->type = type;
122
}
123
124
125
126
/*!
127
   \internal
128
*/
129
QPixmapFilter::QPixmapFilter(QPixmapFilterPrivate&d, QPixmapFilter::FilterType type, QObject *parent)
130
    : QObject(d, parent)
131
{
132
    d_func()->type = type;
133
}
134
135
136
/*!
137
    Destroys the pixmap filter.
138
139
    \internal
140
*/
141
QPixmapFilter::~QPixmapFilter()
142
{
143
}
144
145
/*!
146
    Returns the type of the filter. All standard pixmap filter classes
147
    are associated with a unique value.
148
149
    \internal
150
*/
151
QPixmapFilter::FilterType QPixmapFilter::type() const
152
{
153
    Q_D(const QPixmapFilter);
154
    return d->type;
155
}
156
157
/*!
158
    Returns the bounding rectangle that is affected by the pixmap
159
    filter if the filter is applied to the specified \a rect.
160
161
    \internal
162
*/
163
QRectF QPixmapFilter::boundingRectFor(const QRectF &rect) const
164
{
165
    return rect;
166
}
167
168
/*!
169
    \fn void QPixmapFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
170
171
    Uses \a painter to draw filtered result of \a src at the point
172
    specified by \a p. If \a srcRect is specified the it will
173
    be used as a source rectangle to only draw a part of the source.
174
175
    draw() will affect the area which boundingRectFor() returns.
176
177
    \internal
178
*/
179
180
/*!
181
    \class QPixmapConvolutionFilter
182
    \since 4.5
183
    \ingroup painting
184
185
    \brief The QPixmapConvolutionFilter class provides convolution
186
    filtering for pixmaps.
187
188
    QPixmapConvolutionFilter implements a convolution pixmap filter,
189
    which is applied when \l{QPixmapFilter::}{draw()} is called. A
190
    convolution filter lets you distort an image by setting the values
191
    of a matrix of qreal values called its
192
    \l{setConvolutionKernel()}{kernel}. The matrix's values are
193
    usually between -1.0 and 1.0.
194
195
    \omit
196
    In convolution filtering, the pixel value is calculated from the
197
    neighboring pixels based on the weighting convolution kernel.
198
    This needs explaining to be useful.
199
    \endomit
200
201
    Example:
202
    \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 1
203
204
    \sa {Pixmap Filters Example}, QPixmapColorizeFilter, QPixmapDropShadowFilter
205
206
207
    \internal
208
*/
209
210
class QPixmapConvolutionFilterPrivate : public QPixmapFilterPrivate
211
{
212
public:
213
    QPixmapConvolutionFilterPrivate(): convolutionKernel(0), kernelWidth(0), kernelHeight(0), convoluteAlpha(false) {}
214
    ~QPixmapConvolutionFilterPrivate() {
215
        delete[] convolutionKernel;
216
    }
217
218
    qreal *convolutionKernel;
219
    int kernelWidth;
220
    int kernelHeight;
221
    bool convoluteAlpha;
222
};
223
224
225
/*!
226
    Constructs a pixmap convolution filter.
227
228
    By default there is no convolution kernel.
229
230
    \internal
231
*/
232
QPixmapConvolutionFilter::QPixmapConvolutionFilter(QObject *parent)
233
    : QPixmapFilter(*new QPixmapConvolutionFilterPrivate, ConvolutionFilter, parent)
234
{
235
    Q_D(QPixmapConvolutionFilter);
236
    d->convoluteAlpha = true;
237
}
238
239
/*!
240
    Destructor of pixmap convolution filter.
241
242
    \internal
243
*/
244
QPixmapConvolutionFilter::~QPixmapConvolutionFilter()
245
{
246
}
247
248
/*!
249
     Sets convolution kernel with the given number of \a rows and \a columns.
250
     Values from \a kernel are copied to internal data structure.
251
252
     To preserve the intensity of the pixmap, the sum of all the
253
     values in the convolution kernel should add up to 1.0. A sum
254
     greater than 1.0 produces a lighter result and a sum less than 1.0
255
     produces a darker and transparent result.
256
257
    \internal
258
*/
259
void QPixmapConvolutionFilter::setConvolutionKernel(const qreal *kernel, int rows, int columns)
260
{
261
    Q_D(QPixmapConvolutionFilter);
262
    delete [] d->convolutionKernel;
263
    d->convolutionKernel = new qreal[rows * columns];
264
    memcpy(d->convolutionKernel, kernel, sizeof(qreal) * rows * columns);
265
    d->kernelWidth = columns;
266
    d->kernelHeight = rows;
267
}
268
269
/*!
270
    Gets the convolution kernel data.
271
272
    \internal
273
*/
274
const qreal *QPixmapConvolutionFilter::convolutionKernel() const
275
{
276
    Q_D(const QPixmapConvolutionFilter);
277
    return d->convolutionKernel;
278
}
279
280
/*!
281
    Gets the number of rows in the convolution kernel.
282
283
    \internal
284
*/
285
int QPixmapConvolutionFilter::rows() const
286
{
287
    Q_D(const QPixmapConvolutionFilter);
288
    return d->kernelHeight;
289
}
290
291
/*!
292
    Gets the number of columns in the convolution kernel.
293
294
    \internal
295
*/
296
int QPixmapConvolutionFilter::columns() const
297
{
298
    Q_D(const QPixmapConvolutionFilter);
299
    return d->kernelWidth;
300
}
301
302
303
/*!
304
    \internal
305
*/
306
QRectF QPixmapConvolutionFilter::boundingRectFor(const QRectF &rect) const
307
{
308
    Q_D(const QPixmapConvolutionFilter);
309
    return rect.adjusted(-d->kernelWidth / 2, -d->kernelHeight / 2, (d->kernelWidth - 1) / 2, (d->kernelHeight - 1) / 2);
310
}
311
312
// Convolutes the image
313
static void convolute(
314
        QImage *destImage,
315
        const QPointF &pos,
316
        const QImage &srcImage,
317
        const QRectF &srcRect,
318
        QPainter::CompositionMode mode,
319
        qreal *kernel,
320
        int kernelWidth,
321
        int kernelHeight )
322
{
323
    const QImage processImage = (srcImage.format() != QImage::Format_ARGB32_Premultiplied ) ?               srcImage.convertToFormat(QImage::Format_ARGB32_Premultiplied) : srcImage;
324
    // TODO: support also other formats directly without copying
325
326
    int *fixedKernel = new int[kernelWidth*kernelHeight];
327
    for(int i = 0; i < kernelWidth*kernelHeight; i++)
328
    {
329
        fixedKernel[i] = (int)(65536 * kernel[i]);
330
    }
331
    QRectF trect = srcRect.isNull() ? processImage.rect() : srcRect;
332
    trect.moveTo(pos);
333
    QRectF bounded = trect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
334
    QRect rect = bounded.toAlignedRect();
335
    QRect targetRect = rect.intersected(destImage->rect());
336
337
    QRectF srect = srcRect.isNull() ? processImage.rect() : srcRect;
338
    QRectF sbounded = srect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
339
    QPoint srcStartPoint = sbounded.toAlignedRect().topLeft()+(targetRect.topLeft()-rect.topLeft());
340
341
    const uint *sourceStart = (uint*)processImage.scanLine(0);
342
    uint *outputStart = (uint*)destImage->scanLine(0);
343
344
    int yk = srcStartPoint.y();
345
    for (int y = targetRect.top(); y <= targetRect.bottom(); y++) {
346
        uint* output = outputStart + (destImage->bytesPerLine()/sizeof(uint))*y+targetRect.left();
347
        int xk = srcStartPoint.x();
348
        for(int x = targetRect.left(); x <= targetRect.right(); x++) {
349
            int r = 0;
350
            int g = 0;
351
            int b = 0;
352
            int a = 0;
353
354
            // some out of bounds pre-checking to avoid inner-loop ifs
355
            int kernely = -kernelHeight/2;
356
            int starty = 0;
357
            int endy = kernelHeight;
358
            if(yk+kernely+endy >= srcImage.height())
359
                endy = kernelHeight-((yk+kernely+endy)-srcImage.height())-1;
360
            if(yk+kernely < 0)
361
                starty = -(yk+kernely);
362
363
            int kernelx = -kernelWidth/2;
364
            int startx = 0;
365
            int endx = kernelWidth;
366
            if(xk+kernelx+endx >= srcImage.width())
367
                endx = kernelWidth-((xk+kernelx+endx)-srcImage.width())-1;
368
            if(xk+kernelx < 0)
369
                startx = -(xk+kernelx);
370
371
            for (int ys = starty; ys < endy; ys ++) {
372
                const uint *pix = sourceStart + (processImage.bytesPerLine()/sizeof(uint))*(yk+kernely+ys) + ((xk+kernelx+startx));
373
                const uint *endPix = pix+endx-startx;
374
                int kernelPos = ys*kernelWidth+startx;
375
                while (pix < endPix) {
376
                    int factor = fixedKernel[kernelPos++];
377
                    a += (((*pix) & 0xff000000)>>24) * factor;
378
                    r += (((*pix) & 0x00ff0000)>>16) * factor;
379
                    g += (((*pix) & 0x0000ff00)>>8 ) * factor;
380
                    b += (((*pix) & 0x000000ff)    ) * factor;
381
                    pix++;
382
                }
383
            }
384
385
            r = qBound((int)0, r >> 16, (int)255);
386
            g = qBound((int)0, g >> 16, (int)255);
387
            b = qBound((int)0, b >> 16, (int)255);
388
            a = qBound((int)0, a >> 16, (int)255);
389
            // composition mode checking could be moved outside of loop
390
            if(mode == QPainter::CompositionMode_Source) {
391
                uint color = (a<<24)+(r<<16)+(g<<8)+b;
392
                *output++ = color;
393
            } else {
394
                uint current = *output;
395
                uchar ca = (current&0xff000000)>>24;
396
                uchar cr = (current&0x00ff0000)>>16;
397
                uchar cg = (current&0x0000ff00)>>8;
398
                uchar cb = (current&0x000000ff);
399
                uint color =
400
                        (((ca*(255-a) >> 8)+a) << 24)+
401
                        (((cr*(255-a) >> 8)+r) << 16)+
402
                        (((cg*(255-a) >> 8)+g) << 8)+
403
                        (((cb*(255-a) >> 8)+b));
404
                *output++ = color;;
405
            }
406
            xk++;
407
        }
408
        yk++;
409
    }
410
    delete[] fixedKernel;
411
}
412
413
/*!
414
    \internal
415
*/
416
void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
417
{
418
    Q_D(const QPixmapConvolutionFilter);
419
    if (!painter->isActive())
420
        return;
421
422
    if(d->kernelWidth<=0 || d->kernelHeight <= 0)
423
        return;
424
425
    if (src.isNull())
426
        return;
427
428
    QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
429
        static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0;
430
    QPixmapConvolutionFilter *convolutionFilter = static_cast<QPixmapConvolutionFilter*>(filter);
431
    if (convolutionFilter) {
432
        convolutionFilter->setConvolutionKernel(d->convolutionKernel, d->kernelWidth, d->kernelHeight);
433
        convolutionFilter->d_func()->convoluteAlpha = d->convoluteAlpha;
434
        convolutionFilter->draw(painter, p, src, srcRect);
435
        return;
436
    }
437
438
    // falling back to raster implementation
439
440
    QImage *target = 0;
441
    if (painter->paintEngine()->paintDevice()->devType() == QInternal::Image) {
442
        target = static_cast<QImage *>(painter->paintEngine()->paintDevice());
443
444
        QTransform mat = painter->combinedTransform();
445
446
        if (mat.type() > QTransform::TxTranslate) {
447
            // Disabled because of transformation...
448
            target = 0;
449
        } else {
450
            QRasterPaintEngine *pe = static_cast<QRasterPaintEngine *>(painter->paintEngine());
451
            if (pe->clipType() == QRasterPaintEngine::ComplexClip)
452
                // disabled because of complex clipping...
453
                target = 0;
454
            else {
455
                QRectF clip = pe->clipBoundingRect();
456
                QRectF rect = boundingRectFor(srcRect.isEmpty() ? src.rect() : srcRect);
457
                QTransform x = painter->deviceTransform();
458
                if (!clip.contains(rect.translated(x.dx() + p.x(), x.dy() + p.y()))) {
459
                    target = 0;
460
                }
461
462
            }
463
        }
464
    }
465
466
    if (target) {
467
        QTransform x = painter->deviceTransform();
468
        QPointF offset(x.dx(), x.dy());
469
470
        convolute(target, p+offset, src.toImage(), srcRect, QPainter::CompositionMode_SourceOver, d->convolutionKernel, d->kernelWidth, d->kernelHeight);
471
    } else {
472
        QRect srect = srcRect.isNull() ? src.rect() : srcRect.toRect();
473
        QRect rect = boundingRectFor(srect).toRect();
474
        QImage result = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
475
        QPoint offset = srect.topLeft() - rect.topLeft();
476
        convolute(&result,
477
                  offset,
478
                  src.toImage(),
479
                  srect,
480
                  QPainter::CompositionMode_Source,
481
                  d->convolutionKernel,
482
                  d->kernelWidth,
483
                  d->kernelHeight);
484
        painter->drawImage(p - offset, result);
485
    }
486
}
487
488
/*!
489
    \class QPixmapBlurFilter
490
    \since 4.6
491
    \ingroup multimedia
492
493
    \brief The QPixmapBlurFilter class provides blur filtering
494
    for pixmaps.
495
496
    QPixmapBlurFilter implements a blur pixmap filter,
497
    which is applied when \l{QPixmapFilter::}{draw()} is called.
498
499
    The filter lets you specialize the radius of the blur as well
500
    as hints as to whether to prefer performance or quality.
501
502
    By default, the blur effect is produced by applying an exponential
503
    filter generated from the specified blurRadius().  Paint engines
504
    may override this with a custom blur that is faster on the
505
    underlying hardware.
506
507
    \sa {Pixmap Filters Example}, QPixmapConvolutionFilter, QPixmapDropShadowFilter
508
509
    \internal
510
*/
511
512
class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate
513
{
514
public:
515
    QPixmapBlurFilterPrivate() : radius(5), hints(QGraphicsBlurEffect::PerformanceHint) {}
516
517
    qreal radius;
518
    QGraphicsBlurEffect::BlurHints hints;
519
};
520
521
522
/*!
523
    Constructs a pixmap blur filter.
524
525
    \internal
526
*/
527
QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent)
528
    : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent)
529
{
530
}
531
532
/*!
533
    Destructor of pixmap blur filter.
534
535
    \internal
536
*/
537
QPixmapBlurFilter::~QPixmapBlurFilter()
538
{
539
}
540
541
/*!
542
    Sets the radius of the blur filter. Higher radius produces increased blurriness.
543
544
    \internal
545
*/
546
void QPixmapBlurFilter::setRadius(qreal radius)
547
{
548
    Q_D(QPixmapBlurFilter);
549
    d->radius = radius;
550
}
551
552
/*!
553
    Gets the radius of the blur filter.
554
555
    \internal
556
*/
557
qreal QPixmapBlurFilter::radius() const
558
{
559
    Q_D(const QPixmapBlurFilter);
560
    return d->radius;
561
}
562
563
/*!
564
    Setting the blur hints to PerformanceHint causes the implementation
565
    to trade off visual quality to blur the image faster.  Setting the
566
    blur hints to QualityHint causes the implementation to improve
567
    visual quality at the expense of speed.
568
569
    AnimationHint causes the implementation to optimize for animating
570
    the blur radius, possibly by caching blurred versions of the source
571
    pixmap.
572
573
    The implementation is free to ignore this value if it only has a single
574
    blur algorithm.
575
576
    \internal
577
*/
578
void QPixmapBlurFilter::setBlurHints(QGraphicsBlurEffect::BlurHints hints)
579
{
580
    Q_D(QPixmapBlurFilter);
581
    d->hints = hints;
582
}
583
584
/*!
585
    Gets the blur hints of the blur filter.
586
587
    \internal
588
*/
589
QGraphicsBlurEffect::BlurHints QPixmapBlurFilter::blurHints() const
590
{
591
    Q_D(const QPixmapBlurFilter);
592
    return d->hints;
593
}
594
595
const qreal radiusScale = qreal(2.5);
596
597
/*!
598
    \internal
599
*/
600
QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const
601
{
602
    Q_D(const QPixmapBlurFilter);
603
    const qreal delta = radiusScale * d->radius + 1;
604
    return rect.adjusted(-delta, -delta, delta, delta);
605
}
606
607
template <int shift>
608
inline int qt_static_shift(int value)
609
{
610
    if (shift == 0)
611
        return value;
612
    else if (shift > 0)
613
        return value << (uint(shift) & 0x1f);
614
    else
615
        return value >> (uint(-shift) & 0x1f);
616
}
617
618
template<int aprec, int zprec>
619
inline void qt_blurinner(uchar *bptr, int &zR, int &zG, int &zB, int &zA, int alpha)
620
{
621
    QRgb *pixel = (QRgb *)bptr;
622
623
#define Z_MASK (0xff << zprec)
624
    const int A_zprec = qt_static_shift<zprec - 24>(*pixel) & Z_MASK;
625
    const int R_zprec = qt_static_shift<zprec - 16>(*pixel) & Z_MASK;
626
    const int G_zprec = qt_static_shift<zprec - 8>(*pixel)  & Z_MASK;
627
    const int B_zprec = qt_static_shift<zprec>(*pixel)      & Z_MASK;
628
#undef Z_MASK
629
630
    const int zR_zprec = zR >> aprec;
631
    const int zG_zprec = zG >> aprec;
632
    const int zB_zprec = zB >> aprec;
633
    const int zA_zprec = zA >> aprec;
634
635
    zR += alpha * (R_zprec - zR_zprec);
636
    zG += alpha * (G_zprec - zG_zprec);
637
    zB += alpha * (B_zprec - zB_zprec);
638
    zA += alpha * (A_zprec - zA_zprec);
639
640
#define ZA_MASK (0xff << (zprec + aprec))
641
    *pixel =
642
        qt_static_shift<24 - zprec - aprec>(zA & ZA_MASK)
643
        | qt_static_shift<16 - zprec - aprec>(zR & ZA_MASK)
644
        | qt_static_shift<8 - zprec - aprec>(zG & ZA_MASK)
645
        | qt_static_shift<-zprec - aprec>(zB & ZA_MASK);
646
#undef ZA_MASK
647
}
648
649
const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
650
651
template<int aprec, int zprec>
652
inline void qt_blurinner_alphaOnly(uchar *bptr, int &z, int alpha)
653
{
654
    const int A_zprec = int(*(bptr)) << zprec;
655
    const int z_zprec = z >> aprec;
656
    z += alpha * (A_zprec - z_zprec);
657
    *(bptr) = z >> (zprec + aprec);
658
}
659
660
template<int aprec, int zprec, bool alphaOnly>
661
inline void qt_blurrow(QImage & im, int line, int alpha)
662
{
663
    uchar *bptr = im.scanLine(line);
664
665
    int zR = 0, zG = 0, zB = 0, zA = 0;
666
667
    if (alphaOnly && im.format() != QImage::Format_Indexed8)
668
        bptr += alphaIndex;
669
670
    const int stride = im.depth() >> 3;
671
    const int im_width = im.width();
672
    for (int index = 0; index < im_width; ++index) {
673
        if (alphaOnly)
674
            qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
675
        else
676
            qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
677
        bptr += stride;
678
    }
679
680
    bptr -= stride;
681
682
    for (int index = im_width - 2; index >= 0; --index) {
683
        bptr -= stride;
684
        if (alphaOnly)
685
            qt_blurinner_alphaOnly<aprec, zprec>(bptr, zA, alpha);
686
        else
687
            qt_blurinner<aprec, zprec>(bptr, zR, zG, zB, zA, alpha);
688
    }
689
}
690
691
/*
692
*  expblur(QImage &img, int radius)
693
*
694
*  Based on exponential blur algorithm by Jani Huhtanen
695
*
696
*  In-place blur of image 'img' with kernel
697
*  of approximate radius 'radius'.
698
*
699
*  Blurs with two sided exponential impulse
700
*  response.
701
*
702
*  aprec = precision of alpha parameter
703
*  in fixed-point format 0.aprec
704
*
705
*  zprec = precision of state parameters
706
*  zR,zG,zB and zA in fp format 8.zprec
707
*/
708
template <int aprec, int zprec, bool alphaOnly>
709
void expblur(QImage &img, qreal radius, bool improvedQuality = false, int transposed = 0)
710
{
711
    // halve the radius if we're using two passes
712
    if (improvedQuality)
713
        radius *= qreal(0.5);
714
715
    Q_ASSERT(img.format() == QImage::Format_ARGB32_Premultiplied
716
             || img.format() == QImage::Format_RGB32
717
             || img.format() == QImage::Format_Indexed8);
718
719
    // choose the alpha such that pixels at radius distance from a fully
720
    // saturated pixel will have an alpha component of no greater than
721
    // the cutOffIntensity
722
    const qreal cutOffIntensity = 2;
723
    int alpha = radius <= qreal(1e-5)
724
        ? ((1 << aprec)-1)
725
        : qRound((1<<aprec)*(1 - qPow(cutOffIntensity * (1 / qreal(255)), 1 / radius)));
726
727
    int img_height = img.height();
728
    for (int row = 0; row < img_height; ++row) {
729
        for (int i = 0; i <= improvedQuality; ++i)
730
            qt_blurrow<aprec, zprec, alphaOnly>(img, row, alpha);
731
    }
732
733
    QImage temp(img.height(), img.width(), img.format());
734
    if (transposed >= 0) {
735
        if (img.depth() == 8) {
736
            qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
737
                            img.width(), img.height(), img.bytesPerLine(),
738
                            reinterpret_cast<quint8*>(temp.bits()),
739
                            temp.bytesPerLine());
740
        } else {
741
            qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
742
                            img.width(), img.height(), img.bytesPerLine(),
743
                            reinterpret_cast<quint32*>(temp.bits()),
744
                            temp.bytesPerLine());
745
        }
746
    } else {
747
        if (img.depth() == 8) {
748
            qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
749
                           img.width(), img.height(), img.bytesPerLine(),
750
                           reinterpret_cast<quint8*>(temp.bits()),
751
                           temp.bytesPerLine());
752
        } else {
753
            qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
754
                           img.width(), img.height(), img.bytesPerLine(),
755
                           reinterpret_cast<quint32*>(temp.bits()),
756
                           temp.bytesPerLine());
757
        }
758
    }
759
760
    img_height = temp.height();
761
    for (int row = 0; row < img_height; ++row) {
762
        for (int i = 0; i <= improvedQuality; ++i)
763
            qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
764
    }
765
766
    if (transposed == 0) {
767
        qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
768
                       temp.width(), temp.height(), temp.bytesPerLine(),
769
                       reinterpret_cast<quint32*>(img.bits()),
770
                       img.bytesPerLine());
771
    } else {
772
        img = temp;
773
    }
774
}
775
#define AVG(a,b)  ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
776
#define AVG16(a,b)  ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
777
778
Q_GUI_EXPORT QImage qt_halfScaled(const QImage &source)
779
{
780
    if (source.width() < 2 || source.height() < 2)
781
        return QImage();
782
783
    QImage srcImage = source;
784
785
    if (source.format() == QImage::Format_Indexed8) {
786
        // assumes grayscale
787
        QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
788
789
        const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
790
        int sx = srcImage.bytesPerLine();
791
        int sx2 = sx << 1;
792
793
        uchar *dst = reinterpret_cast<uchar*>(dest.bits());
794
        int dx = dest.bytesPerLine();
795
        int ww = dest.width();
796
        int hh = dest.height();
797
798
        for (int y = hh; y; --y, dst += dx, src += sx2) {
799
            const uchar *p1 = src;
800
            const uchar *p2 = src + sx;
801
            uchar *q = dst;
802
            for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2)
803
                *q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
804
        }
805
806
        return dest;
807
    } else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
808
        QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
809
810
        const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
811
        int sx = srcImage.bytesPerLine();
812
        int sx2 = sx << 1;
813
814
        uchar *dst = reinterpret_cast<uchar*>(dest.bits());
815
        int dx = dest.bytesPerLine();
816
        int ww = dest.width();
817
        int hh = dest.height();
818
819
        for (int y = hh; y; --y, dst += dx, src += sx2) {
820
            const uchar *p1 = src;
821
            const uchar *p2 = src + sx;
822
            uchar *q = dst;
823
            for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
824
                // alpha
825
                q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
826
                // rgb
827
                const quint16 p16_1 = (p1[2] << 8) | p1[1];
828
                const quint16 p16_2 = (p1[5] << 8) | p1[4];
829
                const quint16 p16_3 = (p2[2] << 8) | p2[1];
830
                const quint16 p16_4 = (p2[5] << 8) | p2[4];
831
                const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
832
                q[1] = result & 0xff;
833
                q[2] = result >> 8;
834
            }
835
        }
836
837
        return dest;
838
    } else if (source.format() != QImage::Format_ARGB32_Premultiplied
839
               && source.format() != QImage::Format_RGB32)
840
    {
841
        srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
842
    }
843
844
    QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
845
846
    const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
847
    int sx = srcImage.bytesPerLine() >> 2;
848
    int sx2 = sx << 1;
849
850
    quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
851
    int dx = dest.bytesPerLine() >> 2;
852
    int ww = dest.width();
853
    int hh = dest.height();
854
855
    for (int y = hh; y; --y, dst += dx, src += sx2) {
856
        const quint32 *p1 = src;
857
        const quint32 *p2 = src + sx;
858
        quint32 *q = dst;
859
        for (int x = ww; x; --x, q++, p1 += 2, p2 += 2)
860
            *q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
861
    }
862
863
    return dest;
864
}
865
866
Q_GUI_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0)
867
{
868
    if (blurImage.format() != QImage::Format_ARGB32_Premultiplied
869
        && blurImage.format() != QImage::Format_RGB32)
870
    {
871
        blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
872
    }
873
874
    qreal scale = 1;
875
    if (radius >= 4 && blurImage.width() >= 2 && blurImage.height() >= 2) {
876
        blurImage = qt_halfScaled(blurImage);
877
        scale = 2;
878
        radius *= qreal(0.5);
879
    }
880
881
    if (alphaOnly)
882
        expblur<12, 10, true>(blurImage, radius, quality, transposed);
883
    else
884
        expblur<12, 10, false>(blurImage, radius, quality, transposed);
885
886
    if (p) {
887
        p->scale(scale, scale);
888
        p->setRenderHint(QPainter::SmoothPixmapTransform);
889
        p->drawImage(QRect(0, 0, blurImage.width(), blurImage.height()), blurImage);
890
    }
891
}
892
893
Q_GUI_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0)
894
{
895
    if (blurImage.format() == QImage::Format_Indexed8)
896
        expblur<12, 10, true>(blurImage, radius, quality, transposed);
897
    else
898
        expblur<12, 10, false>(blurImage, radius, quality, transposed);
899
}
900
901
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
902
903
/*!
904
    \internal
905
*/
906
void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &rect) const
907
{
908
    Q_D(const QPixmapBlurFilter);
909
    if (!painter->isActive())
910
        return;
911
912
    if (src.isNull())
913
        return;
914
915
    QRectF srcRect = rect;
916
    if (srcRect.isNull())
917
        srcRect = src.rect();
918
919
    if (d->radius <= 1) {
920
        painter->drawPixmap(srcRect.translated(p), src, srcRect);
921
        return;
922
    }
923
924
    qreal scaledRadius = radiusScale * d->radius;
925
    qreal scale;
926
    if (qt_scaleForTransform(painter->transform(), &scale))
927
        scaledRadius /= scale;
928
929
    QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
930
        static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0;
931
    QPixmapBlurFilter *blurFilter = static_cast<QPixmapBlurFilter*>(filter);
932
    if (blurFilter) {
933
        blurFilter->setRadius(scaledRadius);
934
        blurFilter->setBlurHints(d->hints);
935
        blurFilter->draw(painter, p, src, srcRect);
936
        return;
937
    }
938
939
    QImage srcImage;
940
    QImage destImage;
941
942
    if (srcRect == src.rect()) {
943
        srcImage = src.toImage();
944
    } else {
945
        QRect rect = srcRect.toAlignedRect().intersected(src.rect());
946
        srcImage = src.copy(rect).toImage();
947
    }
948
949
    QTransform transform = painter->worldTransform();
950
    painter->translate(p);
951
    qt_blurImage(painter, srcImage, scaledRadius, (d->hints & QGraphicsBlurEffect::QualityHint), false);
952
    painter->setWorldTransform(transform);
953
}
954
955
// grayscales the image to dest (could be same). If rect isn't defined
956
// destination image size is used to determine the dimension of grayscaling
957
// process.
958
static void grayscale(const QImage &image, QImage &dest, const QRect& rect = QRect())
959
{
960
    QRect destRect = rect;
961
    QRect srcRect = rect;
962
    if (rect.isNull()) {
963
        srcRect = dest.rect();
964
        destRect = dest.rect();
965
    }
966
    if (&image != &dest) {
967
        destRect.moveTo(QPoint(0, 0));
968
    }
969
970
    unsigned int *data = (unsigned int *)image.bits();
971
    unsigned int *outData = (unsigned int *)dest.bits();
972
973
    if (dest.size() == image.size() && image.rect() == srcRect) {
974
        // a bit faster loop for grayscaling everything
975
        int pixels = dest.width() * dest.height();
976
        for (int i = 0; i < pixels; ++i) {
977
            int val = qGray(data[i]);
978
            outData[i] = qRgba(val, val, val, qAlpha(data[i]));
979
        }
980
    } else {
981
        int yd = destRect.top();
982
        for (int y = srcRect.top(); y <= srcRect.bottom() && y < image.height(); y++) {
983
            data = (unsigned int*)image.scanLine(y);
984
            outData = (unsigned int*)dest.scanLine(yd++);
985
            int xd = destRect.left();
986
            for (int x = srcRect.left(); x <= srcRect.right() && x < image.width(); x++) {
987
                int val = qGray(data[x]);
988
                outData[xd++] = qRgba(val, val, val, qAlpha(data[x]));
989
            }
990
        }
991
    }
992
}
993
994
/*!
995
    \class QPixmapColorizeFilter
996
    \since 4.5
997
    \ingroup painting
998
999
    \brief The QPixmapColorizeFilter class provides colorizing
1000
    filtering for pixmaps.
1001
1002
    A colorize filter gives the pixmap a tint of its color(). The
1003
    filter first grayscales the pixmap and then converts those to
1004
    colorized values using QPainter::CompositionMode_Screen with the
1005
    chosen color. The alpha-channel is not changed.
1006
1007
    Example:
1008
    \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 0
1009
1010
    \sa QPainter::CompositionMode
1011
1012
    \internal
1013
*/
1014
class QPixmapColorizeFilterPrivate : public QPixmapFilterPrivate
1015
{
1016
    Q_DECLARE_PUBLIC(QPixmapColorizeFilter)
1017
public:
1018
    QColor color;
1019
    qreal strength;
1020
    quint32 opaque : 1;
1021
    quint32 alphaBlend : 1;
1022
    quint32 padding : 30;
1023
};
1024
1025
/*!
1026
    Constructs an pixmap colorize filter.
1027
1028
    Default color value for colorizing is QColor(0, 0, 192).
1029
1030
    \internal
1031
*/
1032
QPixmapColorizeFilter::QPixmapColorizeFilter(QObject *parent)
1033
    : QPixmapFilter(*new QPixmapColorizeFilterPrivate, ColorizeFilter, parent)
1034
{
1035
    Q_D(QPixmapColorizeFilter);
1036
    d->color = QColor(0, 0, 192);
1037
    d->strength = qreal(1);
1038
    d->opaque = true;
1039
    d->alphaBlend = false;
1040
}
1041
1042
/*!
1043
    Gets the color of the colorize filter.
1044
1045
    \internal
1046
*/
1047
QColor QPixmapColorizeFilter::color() const
1048
{
1049
    Q_D(const QPixmapColorizeFilter);
1050
    return d->color;
1051
}
1052
1053
/*!
1054
    Sets the color of the colorize filter to the \a color specified.
1055
1056
    \internal
1057
*/
1058
void QPixmapColorizeFilter::setColor(const QColor &color)
1059
{
1060
    Q_D(QPixmapColorizeFilter);
1061
    d->color = color;
1062
}
1063
1064
/*!
1065
    Gets the strength of the colorize filter, 1.0 means full colorized while
1066
    0.0 equals to no filtering at all.
1067
1068
    \internal
1069
*/
1070
qreal QPixmapColorizeFilter::strength() const
1071
{
1072
    Q_D(const QPixmapColorizeFilter);
1073
    return d->strength;
1074
}
1075
1076
/*!
1077
    Sets the strength of the colorize filter to \a strength.
1078
1079
    \internal
1080
*/
1081
void QPixmapColorizeFilter::setStrength(qreal strength)
1082
{
1083
    Q_D(QPixmapColorizeFilter);
1084
    d->strength = qBound(qreal(0), strength, qreal(1));
1085
    d->opaque = !qFuzzyIsNull(d->strength);
1086
    d->alphaBlend = !qFuzzyIsNull(d->strength - 1);
1087
}
1088
1089
/*!
1090
    \internal
1091
*/
1092
void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
1093
{
1094
    Q_D(const QPixmapColorizeFilter);
1095
1096
    if (src.isNull())
1097
        return;
1098
1099
    QPixmapFilter *filter = painter->paintEngine() && painter->paintEngine()->isExtended() ?
1100
        static_cast<QPaintEngineEx *>(painter->paintEngine())->pixmapFilter(type(), this) : 0;
1101
    QPixmapColorizeFilter *colorizeFilter = static_cast<QPixmapColorizeFilter*>(filter);
1102
    if (colorizeFilter) {
1103
        colorizeFilter->setColor(d->color);
1104
        colorizeFilter->setStrength(d->strength);
1105
        colorizeFilter->draw(painter, dest, src, srcRect);
1106
        return;
1107
    }
1108
1109
    // falling back to raster implementation
1110
1111
    if (!d->opaque) {
1112
        painter->drawPixmap(dest, src, srcRect);
1113
        return;
1114
    }
1115
1116
    QImage srcImage;
1117
    QImage destImage;
1118
1119
    if (srcRect.isNull()) {
1120
        srcImage = src.toImage();
1121
        srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
1122
        destImage = QImage(srcImage.size(), srcImage.format());
1123
    } else {
1124
        QRect rect = srcRect.toAlignedRect().intersected(src.rect());
1125
1126
        srcImage = src.copy(rect).toImage();
1127
        srcImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32);
1128
        destImage = QImage(rect.size(), srcImage.format());
1129
    }
1130
1131
    // do colorizing
1132
    QPainter destPainter(&destImage);
1133
    grayscale(srcImage, destImage, srcImage.rect());
1134
    destPainter.setCompositionMode(QPainter::CompositionMode_Screen);
1135
    destPainter.fillRect(srcImage.rect(), d->color);
1136
    destPainter.end();
1137
1138
    if (d->alphaBlend) {
1139
        // alpha blending srcImage and destImage
1140
        QImage buffer = srcImage;
1141
        QPainter bufPainter(&buffer);
1142
        bufPainter.setOpacity(d->strength);
1143
        bufPainter.drawImage(0, 0, destImage);
1144
        bufPainter.end();
1145
        destImage = buffer;
1146
    }
1147
1148
    if (srcImage.hasAlphaChannel())
1149
        destImage.setAlphaChannel(srcImage.alphaChannel());
1150
1151
    painter->drawImage(dest, destImage);
1152
}
1153
1154
class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate
1155
{
1156
public:
1157
    QPixmapDropShadowFilterPrivate()
1158
        : offset(8, 8), color(63, 63, 63, 180), radius(1) {}
1159
1160
    QPointF offset;
1161
    QColor color;
1162
    qreal radius;
1163
};
1164
1165
/*!
1166
    \class QPixmapDropShadowFilter
1167
    \since 4.5
1168
    \ingroup painting
1169
1170
    \brief The QPixmapDropShadowFilter class is a convenience class
1171
    for drawing pixmaps with drop shadows.
1172
1173
    The drop shadow is produced by taking a copy of the source pixmap
1174
    and applying a color to the copy using a
1175
    QPainter::CompositionMode_DestinationIn operation. This produces a
1176
    homogeneously-colored pixmap which is then drawn using a
1177
    QPixmapConvolutionFilter at an offset. The original pixmap is
1178
    drawn on top.
1179
1180
    The QPixmapDropShadowFilter class provides some customization
1181
    options to specify how the drop shadow should appear. The color of
1182
    the drop shadow can be modified using the setColor() function, the
1183
    drop shadow offset can be modified using the setOffset() function,
1184
    and the blur radius of the drop shadow can be changed through the
1185
    setBlurRadius() function.
1186
1187
    By default, the drop shadow is a dark gray shadow, blurred with a
1188
    radius of 1 at an offset of 8 pixels towards the lower right.
1189
1190
    Example:
1191
    \snippet doc/src/snippets/code/src_gui_image_qpixmapfilter.cpp 2
1192
1193
    \sa QPixmapColorizeFilter, QPixmapConvolutionFilter
1194
1195
    \internal
1196
 */
1197
1198
/*!
1199
    Constructs drop shadow filter.
1200
1201
    \internal
1202
*/
1203
QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent)
1204
    : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent)
1205
{
1206
}
1207
1208
/*!
1209
    Destroys drop shadow filter.
1210
1211
    \internal
1212
*/
1213
QPixmapDropShadowFilter::~QPixmapDropShadowFilter()
1214
{
1215
}
1216
1217
/*!
1218
    Returns the radius in pixels of the blur on the drop shadow.
1219
1220
    A smaller radius results in a sharper shadow.
1221
1222
    \sa color(), offset()
1223
1224
    \internal
1225
*/
1226
qreal QPixmapDropShadowFilter::blurRadius() const
1227
{
1228
    Q_D(const QPixmapDropShadowFilter);
1229
    return d->radius;
1230
}
1231
1232
/*!
1233
    Sets the radius in pixels of the blur on the drop shadow to the \a radius specified.
1234
1235
    Using a smaller radius results in a sharper shadow.
1236
1237
    \sa setColor(), setOffset()
1238
1239
    \internal
1240
*/
1241
void QPixmapDropShadowFilter::setBlurRadius(qreal radius)
1242
{
1243
    Q_D(QPixmapDropShadowFilter);
1244
    d->radius = radius;
1245
}
1246
1247
/*!
1248
    Returns the color of the drop shadow.
1249
1250
    \sa blurRadius(), offset()
1251
1252
    \internal
1253
*/
1254
QColor QPixmapDropShadowFilter::color() const
1255
{
1256
    Q_D(const QPixmapDropShadowFilter);
1257
    return d->color;
1258
}
1259
1260
/*!
1261
    Sets the color of the drop shadow to the \a color specified.
1262
1263
    \sa setBlurRadius(), setOffset()
1264
1265
    \internal
1266
*/
1267
void QPixmapDropShadowFilter::setColor(const QColor &color)
1268
{
1269
    Q_D(QPixmapDropShadowFilter);
1270
    d->color = color;
1271
}
1272
1273
/*!
1274
    Returns the shadow offset in pixels.
1275
1276
    \sa blurRadius(), color()
1277
1278
    \internal
1279
*/
1280
QPointF QPixmapDropShadowFilter::offset() const
1281
{
1282
    Q_D(const QPixmapDropShadowFilter);
1283
    return d->offset;
1284
}
1285
1286
/*!
1287
    Sets the shadow offset in pixels to the \a offset specified.
1288
1289
    \sa setBlurRadius(), setColor()
1290
1291
    \internal
1292
*/
1293
void QPixmapDropShadowFilter::setOffset(const QPointF &offset)
1294
{
1295
    Q_D(QPixmapDropShadowFilter);
1296
    d->offset = offset;
1297
}
1298
1299
/*!
1300
    \fn void QPixmapDropShadowFilter::setOffset(qreal dx, qreal dy)
1301
    \overload
1302
1303
    Sets the shadow offset in pixels to be the displacement specified by the
1304
    horizontal \a dx and vertical \a dy coordinates.
1305
1306
    \sa setBlurRadius(), setColor()
1307
1308
    \internal
1309
*/
1310
1311
/*!
1312
    \internal
1313
 */
1314
QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const
1315
{
1316
    Q_D(const QPixmapDropShadowFilter);
1317
    return rect.united(rect.translated(d->offset).adjusted(-d->radius, -d->radius, d->radius, d->radius));
1318
}
1319
1320
/*!
1321
    \internal
1322
 */
1323
void QPixmapDropShadowFilter::draw(QPainter *p,
1324
                                   const QPointF &pos,
1325
                                   const QPixmap &px,
1326
                                   const QRectF &src) const
1327
{
1328
    Q_D(const QPixmapDropShadowFilter);
1329
1330
    if (px.isNull())
1331
        return;
1332
1333
    QPixmapFilter *filter = p->paintEngine() && p->paintEngine()->isExtended() ?
1334
        static_cast<QPaintEngineEx *>(p->paintEngine())->pixmapFilter(type(), this) : 0;
1335
    QPixmapDropShadowFilter *dropShadowFilter = static_cast<QPixmapDropShadowFilter*>(filter);
1336
    if (dropShadowFilter) {
1337
        dropShadowFilter->setColor(d->color);
1338
        dropShadowFilter->setBlurRadius(d->radius);
1339
        dropShadowFilter->setOffset(d->offset);
1340
        dropShadowFilter->draw(p, pos, px, src);
1341
        return;
1342
    }
1343
1344
    QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
1345
    tmp.fill(0);
1346
    QPainter tmpPainter(&tmp);
1347
    tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
1348
    tmpPainter.drawPixmap(d->offset, px);
1349
    tmpPainter.end();
1350
1351
    // blur the alpha channel
1352
    QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
1353
    blurred.fill(0);
1354
    QPainter blurPainter(&blurred);
1355
    qt_blurImage(&blurPainter, tmp, d->radius, false, true);
1356
    blurPainter.end();
1357
1358
    tmp = blurred;
1359
1360
    // blacken the image...
1361
    tmpPainter.begin(&tmp);
1362
    tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
1363
    tmpPainter.fillRect(tmp.rect(), d->color);
1364
    tmpPainter.end();
1365
1366
    // draw the blurred drop shadow...
1367
    p->drawImage(pos, tmp);
1368
1369
    // Draw the actual pixmap...
1370
    p->drawPixmap(pos, px, src);
1371
}
1372
1373
QT_END_NAMESPACE
1374
1375
#endif //QT_NO_GRAPHICSEFFECT