~gabriel1984sibiu/minitube/qt5.6

« back to all changes in this revision

Viewing changes to src/widgets/effects/qpixmapfilter.cpp

  • Committer: Grevutiu Gabriel
  • Date: 2017-06-13 08:43:17 UTC
  • Revision ID: gabriel1984sibiu@gmail.com-20170613084317-ek0zqe0u9g3ocvi8
OriginalĀ upstreamĀ code

Show diffs side-by-side

added added

removed removed

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