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 #include <memory>
58 
59 QT_BEGIN_NAMESPACE
60 
61 class QPixmapFilterPrivate : public QObjectPrivate
62 {
63     Q_DECLARE_PUBLIC(QPixmapFilter)
64 public:
65     QPixmapFilter::FilterType type;
66 };
67 
68 /*!
69     \class QPixmapFilter
70     \since 4.5
71     \ingroup painting
72 
73     \brief The QPixmapFilter class provides the basic functionality for
74     pixmap filter classes. Pixmap filter can be for example colorize or blur.
75 
76     QPixmapFilter is the base class for every pixmap filter. QPixmapFilter is
77     an abstract class and cannot itself be instantiated. It provides a standard
78     interface for filter processing.
79 
80     \internal
81 */
82 
83 /*!
84     \enum QPixmapFilter::FilterType
85 
86     \internal
87 
88     This enum describes the types of filter that can be applied to pixmaps.
89 
90     \value ConvolutionFilter  A filter that is used to calculate the convolution
91                               of the image with a kernel. See
92                               QPixmapConvolutionFilter for more information.
93     \value ColorizeFilter     A filter that is used to change the overall color
94                               of an image. See QPixmapColorizeFilter for more
95                               information.
96     \value DropShadowFilter   A filter that is used to add a drop shadow to an
97                               image. See QPixmapDropShadowFilter for more
98                               information.
99     \value BlurFilter         A filter that is used to blur an image using
100                               a simple blur radius. See QPixmapBlurFilter
101                               for more information.
102 
103     \value UserFilter   The first filter type that can be used for
104                         application-specific purposes.
105 */
106 
107 
108 /*!
109     Constructs a default QPixmapFilter with the given \a type.
110 
111     This constructor should be used when subclassing QPixmapFilter to
112     create custom user filters.
113 
114     \internal
115 */
QPixmapFilter(FilterType type,QObject * parent)116 QPixmapFilter::QPixmapFilter(FilterType type, QObject *parent)
117     : QObject(*new QPixmapFilterPrivate, parent)
118 {
119     d_func()->type = type;
120 }
121 
122 
123 
124 /*!
125    \internal
126 */
QPixmapFilter(QPixmapFilterPrivate & d,QPixmapFilter::FilterType type,QObject * parent)127 QPixmapFilter::QPixmapFilter(QPixmapFilterPrivate&d, QPixmapFilter::FilterType type, QObject *parent)
128     : QObject(d, parent)
129 {
130     d_func()->type = type;
131 }
132 
133 
134 /*!
135     Destroys the pixmap filter.
136 
137     \internal
138 */
~QPixmapFilter()139 QPixmapFilter::~QPixmapFilter()
140 {
141 }
142 
143 /*!
144     Returns the type of the filter. All standard pixmap filter classes
145     are associated with a unique value.
146 
147     \internal
148 */
type() const149 QPixmapFilter::FilterType QPixmapFilter::type() const
150 {
151     Q_D(const QPixmapFilter);
152     return d->type;
153 }
154 
155 /*!
156     Returns the bounding rectangle that is affected by the pixmap
157     filter if the filter is applied to the specified \a rect.
158 
159     \internal
160 */
boundingRectFor(const QRectF & rect) const161 QRectF QPixmapFilter::boundingRectFor(const QRectF &rect) const
162 {
163     return rect;
164 }
165 
166 /*!
167     \fn void QPixmapFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
168 
169     Uses \a painter to draw filtered result of \a src at the point
170     specified by \a p. If \a srcRect is specified the it will
171     be used as a source rectangle to only draw a part of the source.
172 
173     draw() will affect the area which boundingRectFor() returns.
174 
175     \internal
176 */
177 
178 /*!
179     \class QPixmapConvolutionFilter
180     \since 4.5
181     \ingroup painting
182 
183     \brief The QPixmapConvolutionFilter class provides convolution
184     filtering for pixmaps.
185 
186     QPixmapConvolutionFilter implements a convolution pixmap filter,
187     which is applied when \l{QPixmapFilter::}{draw()} is called. A
188     convolution filter lets you distort an image by setting the values
189     of a matrix of qreal values called its
190     \l{setConvolutionKernel()}{kernel}. The matrix's values are
191     usually between -1.0 and 1.0.
192 
193     \omit
194     In convolution filtering, the pixel value is calculated from the
195     neighboring pixels based on the weighting convolution kernel.
196     This needs explaining to be useful.
197     \endomit
198 
199     Example:
200     \snippet code/src_gui_image_qpixmapfilter.cpp 1
201 
202     \sa {Pixmap Filters Example}, QPixmapColorizeFilter, QPixmapDropShadowFilter
203 
204 
205     \internal
206 */
207 
208 class QPixmapConvolutionFilterPrivate : public QPixmapFilterPrivate
209 {
210 public:
QPixmapConvolutionFilterPrivate()211     QPixmapConvolutionFilterPrivate(): convolutionKernel(nullptr), kernelWidth(0), kernelHeight(0), convoluteAlpha(false) {}
~QPixmapConvolutionFilterPrivate()212     ~QPixmapConvolutionFilterPrivate() {
213         delete[] convolutionKernel;
214     }
215 
216     qreal *convolutionKernel;
217     int kernelWidth;
218     int kernelHeight;
219     bool convoluteAlpha;
220 };
221 
222 
223 /*!
224     Constructs a pixmap convolution filter.
225 
226     By default there is no convolution kernel.
227 
228     \internal
229 */
QPixmapConvolutionFilter(QObject * parent)230 QPixmapConvolutionFilter::QPixmapConvolutionFilter(QObject *parent)
231     : QPixmapFilter(*new QPixmapConvolutionFilterPrivate, ConvolutionFilter, parent)
232 {
233     Q_D(QPixmapConvolutionFilter);
234     d->convoluteAlpha = true;
235 }
236 
237 /*!
238     Destructor of pixmap convolution filter.
239 
240     \internal
241 */
~QPixmapConvolutionFilter()242 QPixmapConvolutionFilter::~QPixmapConvolutionFilter()
243 {
244 }
245 
246 /*!
247      Sets convolution kernel with the given number of \a rows and \a columns.
248      Values from \a kernel are copied to internal data structure.
249 
250      To preserve the intensity of the pixmap, the sum of all the
251      values in the convolution kernel should add up to 1.0. A sum
252      greater than 1.0 produces a lighter result and a sum less than 1.0
253      produces a darker and transparent result.
254 
255     \internal
256 */
setConvolutionKernel(const qreal * kernel,int rows,int columns)257 void QPixmapConvolutionFilter::setConvolutionKernel(const qreal *kernel, int rows, int columns)
258 {
259     Q_D(QPixmapConvolutionFilter);
260     delete [] d->convolutionKernel;
261     d->convolutionKernel = new qreal[rows * columns];
262     memcpy(d->convolutionKernel, kernel, sizeof(qreal) * rows * columns);
263     d->kernelWidth = columns;
264     d->kernelHeight = rows;
265 }
266 
267 /*!
268     Gets the convolution kernel data.
269 
270     \internal
271 */
convolutionKernel() const272 const qreal *QPixmapConvolutionFilter::convolutionKernel() const
273 {
274     Q_D(const QPixmapConvolutionFilter);
275     return d->convolutionKernel;
276 }
277 
278 /*!
279     Gets the number of rows in the convolution kernel.
280 
281     \internal
282 */
rows() const283 int QPixmapConvolutionFilter::rows() const
284 {
285     Q_D(const QPixmapConvolutionFilter);
286     return d->kernelHeight;
287 }
288 
289 /*!
290     Gets the number of columns in the convolution kernel.
291 
292     \internal
293 */
columns() const294 int QPixmapConvolutionFilter::columns() const
295 {
296     Q_D(const QPixmapConvolutionFilter);
297     return d->kernelWidth;
298 }
299 
300 
301 /*!
302     \internal
303 */
boundingRectFor(const QRectF & rect) const304 QRectF QPixmapConvolutionFilter::boundingRectFor(const QRectF &rect) const
305 {
306     Q_D(const QPixmapConvolutionFilter);
307     return rect.adjusted(-d->kernelWidth / 2, -d->kernelHeight / 2, (d->kernelWidth - 1) / 2, (d->kernelHeight - 1) / 2);
308 }
309 
310 // Convolutes the image
convolute(QImage * destImage,const QPointF & pos,const QImage & srcImage,const QRectF & srcRect,QPainter::CompositionMode mode,qreal * kernel,int kernelWidth,int kernelHeight)311 static void convolute(
312         QImage *destImage,
313         const QPointF &pos,
314         const QImage &srcImage,
315         const QRectF &srcRect,
316         QPainter::CompositionMode mode,
317         qreal *kernel,
318         int kernelWidth,
319         int kernelHeight )
320 {
321     const QImage processImage = (srcImage.format() != QImage::Format_ARGB32_Premultiplied ) ?               srcImage.convertToFormat(QImage::Format_ARGB32_Premultiplied) : srcImage;
322     // TODO: support also other formats directly without copying
323 
324     std::unique_ptr<int[]> fixedKernel(new int[kernelWidth * kernelHeight]);
325     for(int i = 0; i < kernelWidth*kernelHeight; i++)
326     {
327         fixedKernel[i] = (int)(65536 * kernel[i]);
328     }
329     QRectF trect = srcRect.isNull() ? processImage.rect() : srcRect;
330     trect.moveTo(pos);
331     QRectF bounded = trect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
332     QRect rect = bounded.toAlignedRect();
333     QRect targetRect = rect.intersected(destImage->rect());
334 
335     QRectF srect = srcRect.isNull() ? processImage.rect() : srcRect;
336     QRectF sbounded = srect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
337     QPoint srcStartPoint = sbounded.toAlignedRect().topLeft()+(targetRect.topLeft()-rect.topLeft());
338 
339     const uint *sourceStart = (const uint*)processImage.scanLine(0);
340     uint *outputStart = (uint*)destImage->scanLine(0);
341 
342     int yk = srcStartPoint.y();
343     for (int y = targetRect.top(); y <= targetRect.bottom(); y++) {
344         uint* output = outputStart + (destImage->bytesPerLine()/sizeof(uint))*y+targetRect.left();
345         int xk = srcStartPoint.x();
346         for(int x = targetRect.left(); x <= targetRect.right(); x++) {
347             int r = 0;
348             int g = 0;
349             int b = 0;
350             int a = 0;
351 
352             // some out of bounds pre-checking to avoid inner-loop ifs
353             int kernely = -kernelHeight/2;
354             int starty = 0;
355             int endy = kernelHeight;
356             if(yk+kernely+endy >= srcImage.height())
357                 endy = kernelHeight-((yk+kernely+endy)-srcImage.height())-1;
358             if(yk+kernely < 0)
359                 starty = -(yk+kernely);
360 
361             int kernelx = -kernelWidth/2;
362             int startx = 0;
363             int endx = kernelWidth;
364             if(xk+kernelx+endx >= srcImage.width())
365                 endx = kernelWidth-((xk+kernelx+endx)-srcImage.width())-1;
366             if(xk+kernelx < 0)
367                 startx = -(xk+kernelx);
368 
369             for (int ys = starty; ys < endy; ys ++) {
370                 const uint *pix = sourceStart + (processImage.bytesPerLine()/sizeof(uint))*(yk+kernely+ys) + ((xk+kernelx+startx));
371                 const uint *endPix = pix+endx-startx;
372                 int kernelPos = ys*kernelWidth+startx;
373                 while (pix < endPix) {
374                     int factor = fixedKernel[kernelPos++];
375                     a += (((*pix) & 0xff000000)>>24) * factor;
376                     r += (((*pix) & 0x00ff0000)>>16) * factor;
377                     g += (((*pix) & 0x0000ff00)>>8 ) * factor;
378                     b += (((*pix) & 0x000000ff)    ) * factor;
379                     pix++;
380                 }
381             }
382 
383             r = qBound((int)0, r >> 16, (int)255);
384             g = qBound((int)0, g >> 16, (int)255);
385             b = qBound((int)0, b >> 16, (int)255);
386             a = qBound((int)0, a >> 16, (int)255);
387             // composition mode checking could be moved outside of loop
388             if(mode == QPainter::CompositionMode_Source) {
389                 uint color = (a<<24)+(r<<16)+(g<<8)+b;
390                 *output++ = color;
391             } else {
392                 uint current = *output;
393                 uchar ca = (current&0xff000000)>>24;
394                 uchar cr = (current&0x00ff0000)>>16;
395                 uchar cg = (current&0x0000ff00)>>8;
396                 uchar cb = (current&0x000000ff);
397                 uint color =
398                         (((ca*(255-a) >> 8)+a) << 24)+
399                         (((cr*(255-a) >> 8)+r) << 16)+
400                         (((cg*(255-a) >> 8)+g) << 8)+
401                         (((cb*(255-a) >> 8)+b));
402                 *output++ = color;;
403             }
404             xk++;
405         }
406         yk++;
407     }
408 }
409 
410 /*!
411     \internal
412 */
draw(QPainter * painter,const QPointF & p,const QPixmap & src,const QRectF & srcRect) const413 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 = nullptr;
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 = nullptr;
436         } else {
437             QRasterPaintEngine *pe = static_cast<QRasterPaintEngine *>(painter->paintEngine());
438             if (pe->clipType() == QRasterPaintEngine::ComplexClip)
439                 // disabled because of complex clipping...
440                 target = nullptr;
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 = nullptr;
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:
QPixmapBlurFilterPrivate()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 */
QPixmapBlurFilter(QObject * parent)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 */
~QPixmapBlurFilter()524 QPixmapBlurFilter::~QPixmapBlurFilter()
525 {
526 }
527 
528 /*!
529     Sets the radius of the blur filter. Higher radius produces increased blurriness.
530 
531     \internal
532 */
setRadius(qreal radius)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 */
radius() const544 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 */
setBlurHints(QGraphicsBlurEffect::BlurHints hints)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 */
blurHints() const576 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 */
boundingRectFor(const QRectF & rect) const587 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>
qt_static_shift(int value)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>
qt_blurinner(uchar * bptr,int & zR,int & zG,int & zB,int & zA,int alpha)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>
qt_blurinner_alphaOnly(uchar * bptr,int & z,int alpha)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>
qt_blurrow(QImage & im,int line,int alpha)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>
expblur(QImage & img,qreal radius,bool improvedQuality=false,int transposed=0)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     temp.setDevicePixelRatio(img.devicePixelRatioF());
723     if (transposed >= 0) {
724         if (img.depth() == 8) {
725             qt_memrotate270(reinterpret_cast<const quint8*>(img.bits()),
726                             img.width(), img.height(), img.bytesPerLine(),
727                             reinterpret_cast<quint8*>(temp.bits()),
728                             temp.bytesPerLine());
729         } else {
730             qt_memrotate270(reinterpret_cast<const quint32*>(img.bits()),
731                             img.width(), img.height(), img.bytesPerLine(),
732                             reinterpret_cast<quint32*>(temp.bits()),
733                             temp.bytesPerLine());
734         }
735     } else {
736         if (img.depth() == 8) {
737             qt_memrotate90(reinterpret_cast<const quint8*>(img.bits()),
738                            img.width(), img.height(), img.bytesPerLine(),
739                            reinterpret_cast<quint8*>(temp.bits()),
740                            temp.bytesPerLine());
741         } else {
742             qt_memrotate90(reinterpret_cast<const quint32*>(img.bits()),
743                            img.width(), img.height(), img.bytesPerLine(),
744                            reinterpret_cast<quint32*>(temp.bits()),
745                            temp.bytesPerLine());
746         }
747     }
748 
749     img_height = temp.height();
750     for (int row = 0; row < img_height; ++row) {
751         for (int i = 0; i <= int(improvedQuality); ++i)
752             qt_blurrow<aprec, zprec, alphaOnly>(temp, row, alpha);
753     }
754 
755     if (transposed == 0) {
756         if (img.depth() == 8) {
757             qt_memrotate90(reinterpret_cast<const quint8*>(temp.bits()),
758                            temp.width(), temp.height(), temp.bytesPerLine(),
759                            reinterpret_cast<quint8*>(img.bits()),
760                            img.bytesPerLine());
761         } else {
762             qt_memrotate90(reinterpret_cast<const quint32*>(temp.bits()),
763                            temp.width(), temp.height(), temp.bytesPerLine(),
764                            reinterpret_cast<quint32*>(img.bits()),
765                            img.bytesPerLine());
766         }
767     } else {
768         img = temp;
769     }
770 }
771 #define AVG(a,b)  ( ((((a)^(b)) & 0xfefefefeUL) >> 1) + ((a)&(b)) )
772 #define AVG16(a,b)  ( ((((a)^(b)) & 0xf7deUL) >> 1) + ((a)&(b)) )
773 
qt_halfScaled(const QImage & source)774 Q_WIDGETS_EXPORT QImage qt_halfScaled(const QImage &source)
775 {
776     if (source.width() < 2 || source.height() < 2)
777         return QImage();
778 
779     QImage srcImage = source;
780 
781     if (source.format() == QImage::Format_Indexed8 || source.format() == QImage::Format_Grayscale8) {
782         // assumes grayscale
783         QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
784         dest.setDevicePixelRatio(source.devicePixelRatioF());
785 
786         const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
787         int sx = srcImage.bytesPerLine();
788         int sx2 = sx << 1;
789 
790         uchar *dst = reinterpret_cast<uchar*>(dest.bits());
791         int dx = dest.bytesPerLine();
792         int ww = dest.width();
793         int hh = dest.height();
794 
795         for (int y = hh; y; --y, dst += dx, src += sx2) {
796             const uchar *p1 = src;
797             const uchar *p2 = src + sx;
798             uchar *q = dst;
799             for (int x = ww; x; --x, ++q, p1 += 2, p2 += 2)
800                 *q = ((int(p1[0]) + int(p1[1]) + int(p2[0]) + int(p2[1])) + 2) >> 2;
801         }
802 
803         return dest;
804     } else if (source.format() == QImage::Format_ARGB8565_Premultiplied) {
805         QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
806         dest.setDevicePixelRatio(source.devicePixelRatioF());
807 
808         const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
809         int sx = srcImage.bytesPerLine();
810         int sx2 = sx << 1;
811 
812         uchar *dst = reinterpret_cast<uchar*>(dest.bits());
813         int dx = dest.bytesPerLine();
814         int ww = dest.width();
815         int hh = dest.height();
816 
817         for (int y = hh; y; --y, dst += dx, src += sx2) {
818             const uchar *p1 = src;
819             const uchar *p2 = src + sx;
820             uchar *q = dst;
821             for (int x = ww; x; --x, q += 3, p1 += 6, p2 += 6) {
822                 // alpha
823                 q[0] = AVG(AVG(p1[0], p1[3]), AVG(p2[0], p2[3]));
824                 // rgb
825                 const quint16 p16_1 = (p1[2] << 8) | p1[1];
826                 const quint16 p16_2 = (p1[5] << 8) | p1[4];
827                 const quint16 p16_3 = (p2[2] << 8) | p2[1];
828                 const quint16 p16_4 = (p2[5] << 8) | p2[4];
829                 const quint16 result = AVG16(AVG16(p16_1, p16_2), AVG16(p16_3, p16_4));
830                 q[1] = result & 0xff;
831                 q[2] = result >> 8;
832             }
833         }
834 
835         return dest;
836     } else if (source.format() != QImage::Format_ARGB32_Premultiplied
837                && source.format() != QImage::Format_RGB32)
838     {
839         srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
840     }
841 
842     QImage dest(source.width() / 2, source.height() / 2, srcImage.format());
843     dest.setDevicePixelRatio(source.devicePixelRatioF());
844 
845     const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
846     int sx = srcImage.bytesPerLine() >> 2;
847     int sx2 = sx << 1;
848 
849     quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
850     int dx = dest.bytesPerLine() >> 2;
851     int ww = dest.width();
852     int hh = dest.height();
853 
854     for (int y = hh; y; --y, dst += dx, src += sx2) {
855         const quint32 *p1 = src;
856         const quint32 *p2 = src + sx;
857         quint32 *q = dst;
858         for (int x = ww; x; --x, q++, p1 += 2, p2 += 2)
859             *q = AVG(AVG(p1[0], p1[1]), AVG(p2[0], p2[1]));
860     }
861 
862     return dest;
863 }
864 
qt_blurImage(QPainter * p,QImage & blurImage,qreal radius,bool quality,bool alphaOnly,int transposed=0)865 Q_WIDGETS_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0)
866 {
867     if (blurImage.format() != QImage::Format_ARGB32_Premultiplied
868         && blurImage.format() != QImage::Format_RGB32)
869     {
870         blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied);
871     }
872 
873     qreal scale = 1;
874     if (radius >= 4 && blurImage.width() >= 2 && blurImage.height() >= 2) {
875         blurImage = qt_halfScaled(blurImage);
876         scale = 2;
877         radius *= qreal(0.5);
878     }
879 
880     if (alphaOnly)
881         expblur<12, 10, true>(blurImage, radius, quality, transposed);
882     else
883         expblur<12, 10, false>(blurImage, radius, quality, transposed);
884 
885     if (p) {
886         p->scale(scale, scale);
887         p->setRenderHint(QPainter::SmoothPixmapTransform);
888         p->drawImage(QRect(QPoint(0, 0), blurImage.size() / blurImage.devicePixelRatioF()), blurImage);
889     }
890 }
891 
qt_blurImage(QImage & blurImage,qreal radius,bool quality,int transposed=0)892 Q_WIDGETS_EXPORT void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0)
893 {
894     if (blurImage.format() == QImage::Format_Indexed8 || blurImage.format() == QImage::Format_Grayscale8)
895         expblur<12, 10, true>(blurImage, radius, quality, transposed);
896     else
897         expblur<12, 10, false>(blurImage, radius, quality, transposed);
898 }
899 
900 Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
901 
902 /*!
903     \internal
904 */
draw(QPainter * painter,const QPointF & p,const QPixmap & src,const QRectF & rect) const905 void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &rect) const
906 {
907     Q_D(const QPixmapBlurFilter);
908     if (!painter->isActive())
909         return;
910 
911     if (src.isNull())
912         return;
913 
914     QRectF srcRect = rect;
915     if (srcRect.isNull())
916         srcRect = src.rect();
917 
918     if (d->radius <= 1) {
919         painter->drawPixmap(srcRect.translated(p), src, srcRect);
920         return;
921     }
922 
923     qreal scaledRadius = radiusScale * d->radius;
924     qreal scale;
925     if (qt_scaleForTransform(painter->transform(), &scale))
926         scaledRadius /= scale;
927 
928     QImage srcImage;
929 
930     if (srcRect == src.rect()) {
931         srcImage = src.toImage();
932     } else {
933         QRect rect = srcRect.toAlignedRect().intersected(src.rect());
934         srcImage = src.copy(rect).toImage();
935     }
936 
937     QTransform transform = painter->worldTransform();
938     painter->translate(p);
939     qt_blurImage(painter, srcImage, scaledRadius, (d->hints & QGraphicsBlurEffect::QualityHint), false);
940     painter->setWorldTransform(transform);
941 }
942 
943 // grayscales the image to dest (could be same). If rect isn't defined
944 // destination image size is used to determine the dimension of grayscaling
945 // process.
grayscale(const QImage & image,QImage & dest,const QRect & rect=QRect ())946 static void grayscale(const QImage &image, QImage &dest, const QRect& rect = QRect())
947 {
948     QRect destRect = rect;
949     QRect srcRect = rect;
950     if (rect.isNull()) {
951         srcRect = dest.rect();
952         destRect = dest.rect();
953     }
954     if (&image != &dest) {
955         destRect.moveTo(QPoint(0, 0));
956     }
957 
958     const unsigned int *data = (const unsigned int *)image.bits();
959     unsigned int *outData = (unsigned int *)dest.bits();
960 
961     if (dest.size() == image.size() && image.rect() == srcRect) {
962         // a bit faster loop for grayscaling everything
963         int pixels = dest.width() * dest.height();
964         for (int i = 0; i < pixels; ++i) {
965             int val = qGray(data[i]);
966             outData[i] = qRgba(val, val, val, qAlpha(data[i]));
967         }
968     } else {
969         int yd = destRect.top();
970         for (int y = srcRect.top(); y <= srcRect.bottom() && y < image.height(); y++) {
971             data = (const unsigned int*)image.scanLine(y);
972             outData = (unsigned int*)dest.scanLine(yd++);
973             int xd = destRect.left();
974             for (int x = srcRect.left(); x <= srcRect.right() && x < image.width(); x++) {
975                 int val = qGray(data[x]);
976                 outData[xd++] = qRgba(val, val, val, qAlpha(data[x]));
977             }
978         }
979     }
980 }
981 
982 /*!
983     \class QPixmapColorizeFilter
984     \since 4.5
985     \ingroup painting
986 
987     \brief The QPixmapColorizeFilter class provides colorizing
988     filtering for pixmaps.
989 
990     A colorize filter gives the pixmap a tint of its color(). The
991     filter first grayscales the pixmap and then converts those to
992     colorized values using QPainter::CompositionMode_Screen with the
993     chosen color. The alpha-channel is not changed.
994 
995     Example:
996     \snippet code/src_gui_image_qpixmapfilter.cpp 0
997 
998     \sa QPainter::CompositionMode
999 
1000     \internal
1001 */
1002 class QPixmapColorizeFilterPrivate : public QPixmapFilterPrivate
1003 {
1004     Q_DECLARE_PUBLIC(QPixmapColorizeFilter)
1005 public:
1006     QColor color;
1007     qreal strength;
1008     quint32 opaque : 1;
1009     quint32 alphaBlend : 1;
1010     quint32 padding : 30;
1011 };
1012 
1013 /*!
1014     Constructs an pixmap colorize filter.
1015 
1016     Default color value for colorizing is QColor(0, 0, 192).
1017 
1018     \internal
1019 */
QPixmapColorizeFilter(QObject * parent)1020 QPixmapColorizeFilter::QPixmapColorizeFilter(QObject *parent)
1021     : QPixmapFilter(*new QPixmapColorizeFilterPrivate, ColorizeFilter, parent)
1022 {
1023     Q_D(QPixmapColorizeFilter);
1024     d->color = QColor(0, 0, 192);
1025     d->strength = qreal(1);
1026     d->opaque = true;
1027     d->alphaBlend = false;
1028 }
1029 
1030 /*!
1031     \internal
1032 */
~QPixmapColorizeFilter()1033 QPixmapColorizeFilter::~QPixmapColorizeFilter()
1034 {
1035     // was inline until Qt 5.6, so essentially
1036     // must stay empty until ### Qt 6
1037 }
1038 
1039 /*!
1040     Gets the color of the colorize filter.
1041 
1042     \internal
1043 */
color() const1044 QColor QPixmapColorizeFilter::color() const
1045 {
1046     Q_D(const QPixmapColorizeFilter);
1047     return d->color;
1048 }
1049 
1050 /*!
1051     Sets the color of the colorize filter to the \a color specified.
1052 
1053     \internal
1054 */
setColor(const QColor & color)1055 void QPixmapColorizeFilter::setColor(const QColor &color)
1056 {
1057     Q_D(QPixmapColorizeFilter);
1058     d->color = color;
1059 }
1060 
1061 /*!
1062     Gets the strength of the colorize filter, 1.0 means full colorized while
1063     0.0 equals to no filtering at all.
1064 
1065     \internal
1066 */
strength() const1067 qreal QPixmapColorizeFilter::strength() const
1068 {
1069     Q_D(const QPixmapColorizeFilter);
1070     return d->strength;
1071 }
1072 
1073 /*!
1074     Sets the strength of the colorize filter to \a strength.
1075 
1076     \internal
1077 */
setStrength(qreal strength)1078 void QPixmapColorizeFilter::setStrength(qreal strength)
1079 {
1080     Q_D(QPixmapColorizeFilter);
1081     d->strength = qBound(qreal(0), strength, qreal(1));
1082     d->opaque = !qFuzzyIsNull(d->strength);
1083     d->alphaBlend = !qFuzzyIsNull(d->strength - 1);
1084 }
1085 
1086 /*!
1087     \internal
1088 */
draw(QPainter * painter,const QPointF & dest,const QPixmap & src,const QRectF & srcRect) const1089 void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
1090 {
1091     Q_D(const QPixmapColorizeFilter);
1092 
1093     if (src.isNull())
1094         return;
1095 
1096     // raster implementation
1097 
1098     if (!d->opaque) {
1099         painter->drawPixmap(dest, src, srcRect);
1100         return;
1101     }
1102 
1103     QImage srcImage;
1104     QImage destImage;
1105 
1106     if (srcRect.isNull()) {
1107         srcImage = src.toImage();
1108         const auto format = srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
1109         srcImage = std::move(srcImage).convertToFormat(format);
1110         destImage = QImage(srcImage.size(), srcImage.format());
1111     } else {
1112         QRect rect = srcRect.toAlignedRect().intersected(src.rect());
1113 
1114         srcImage = src.copy(rect).toImage();
1115         const auto format = srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
1116         srcImage = std::move(srcImage).convertToFormat(format);
1117         destImage = QImage(rect.size(), srcImage.format());
1118     }
1119     destImage.setDevicePixelRatio(src.devicePixelRatioF());
1120 
1121     // do colorizing
1122     QPainter destPainter(&destImage);
1123     grayscale(srcImage, destImage, srcImage.rect());
1124     destPainter.setCompositionMode(QPainter::CompositionMode_Screen);
1125     destPainter.fillRect(srcImage.rect(), d->color);
1126     destPainter.end();
1127 
1128     if (d->alphaBlend) {
1129         // alpha blending srcImage and destImage
1130         QImage buffer = srcImage;
1131         QPainter bufPainter(&buffer);
1132         bufPainter.setOpacity(d->strength);
1133         bufPainter.drawImage(0, 0, destImage);
1134         bufPainter.end();
1135         destImage = std::move(buffer);
1136     }
1137 
1138     if (srcImage.hasAlphaChannel()) {
1139         Q_ASSERT(destImage.format() == QImage::Format_ARGB32_Premultiplied);
1140         QPainter maskPainter(&destImage);
1141         maskPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
1142         maskPainter.drawImage(0, 0, srcImage);
1143     }
1144 
1145     painter->drawImage(dest, destImage);
1146 }
1147 
1148 class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate
1149 {
1150 public:
QPixmapDropShadowFilterPrivate()1151     QPixmapDropShadowFilterPrivate()
1152         : offset(8, 8), color(63, 63, 63, 180), radius(1) {}
1153 
1154     QPointF offset;
1155     QColor color;
1156     qreal radius;
1157 };
1158 
1159 /*!
1160     \class QPixmapDropShadowFilter
1161     \since 4.5
1162     \ingroup painting
1163 
1164     \brief The QPixmapDropShadowFilter class is a convenience class
1165     for drawing pixmaps with drop shadows.
1166 
1167     The drop shadow is produced by taking a copy of the source pixmap
1168     and applying a color to the copy using a
1169     QPainter::CompositionMode_DestinationIn operation. This produces a
1170     homogeneously-colored pixmap which is then drawn using a
1171     QPixmapConvolutionFilter at an offset. The original pixmap is
1172     drawn on top.
1173 
1174     The QPixmapDropShadowFilter class provides some customization
1175     options to specify how the drop shadow should appear. The color of
1176     the drop shadow can be modified using the setColor() function, the
1177     drop shadow offset can be modified using the setOffset() function,
1178     and the blur radius of the drop shadow can be changed through the
1179     setBlurRadius() function.
1180 
1181     By default, the drop shadow is a dark gray shadow, blurred with a
1182     radius of 1 at an offset of 8 pixels towards the lower right.
1183 
1184     Example:
1185     \snippet code/src_gui_image_qpixmapfilter.cpp 2
1186 
1187     \sa QPixmapColorizeFilter, QPixmapConvolutionFilter
1188 
1189     \internal
1190  */
1191 
1192 /*!
1193     Constructs drop shadow filter.
1194 
1195     \internal
1196 */
QPixmapDropShadowFilter(QObject * parent)1197 QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent)
1198     : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent)
1199 {
1200 }
1201 
1202 /*!
1203     Destroys drop shadow filter.
1204 
1205     \internal
1206 */
~QPixmapDropShadowFilter()1207 QPixmapDropShadowFilter::~QPixmapDropShadowFilter()
1208 {
1209 }
1210 
1211 /*!
1212     Returns the radius in pixels of the blur on the drop shadow.
1213 
1214     A smaller radius results in a sharper shadow.
1215 
1216     \sa color(), offset()
1217 
1218     \internal
1219 */
blurRadius() const1220 qreal QPixmapDropShadowFilter::blurRadius() const
1221 {
1222     Q_D(const QPixmapDropShadowFilter);
1223     return d->radius;
1224 }
1225 
1226 /*!
1227     Sets the radius in pixels of the blur on the drop shadow to the \a radius specified.
1228 
1229     Using a smaller radius results in a sharper shadow.
1230 
1231     \sa setColor(), setOffset()
1232 
1233     \internal
1234 */
setBlurRadius(qreal radius)1235 void QPixmapDropShadowFilter::setBlurRadius(qreal radius)
1236 {
1237     Q_D(QPixmapDropShadowFilter);
1238     d->radius = radius;
1239 }
1240 
1241 /*!
1242     Returns the color of the drop shadow.
1243 
1244     \sa blurRadius(), offset()
1245 
1246     \internal
1247 */
color() const1248 QColor QPixmapDropShadowFilter::color() const
1249 {
1250     Q_D(const QPixmapDropShadowFilter);
1251     return d->color;
1252 }
1253 
1254 /*!
1255     Sets the color of the drop shadow to the \a color specified.
1256 
1257     \sa setBlurRadius(), setOffset()
1258 
1259     \internal
1260 */
setColor(const QColor & color)1261 void QPixmapDropShadowFilter::setColor(const QColor &color)
1262 {
1263     Q_D(QPixmapDropShadowFilter);
1264     d->color = color;
1265 }
1266 
1267 /*!
1268     Returns the shadow offset in pixels.
1269 
1270     \sa blurRadius(), color()
1271 
1272     \internal
1273 */
offset() const1274 QPointF QPixmapDropShadowFilter::offset() const
1275 {
1276     Q_D(const QPixmapDropShadowFilter);
1277     return d->offset;
1278 }
1279 
1280 /*!
1281     Sets the shadow offset in pixels to the \a offset specified.
1282 
1283     \sa setBlurRadius(), setColor()
1284 
1285     \internal
1286 */
setOffset(const QPointF & offset)1287 void QPixmapDropShadowFilter::setOffset(const QPointF &offset)
1288 {
1289     Q_D(QPixmapDropShadowFilter);
1290     d->offset = offset;
1291 }
1292 
1293 /*!
1294     \fn void QPixmapDropShadowFilter::setOffset(qreal dx, qreal dy)
1295     \overload
1296 
1297     Sets the shadow offset in pixels to be the displacement specified by the
1298     horizontal \a dx and vertical \a dy coordinates.
1299 
1300     \sa setBlurRadius(), setColor()
1301 
1302     \internal
1303 */
1304 
1305 /*!
1306     \internal
1307  */
boundingRectFor(const QRectF & rect) const1308 QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const
1309 {
1310     Q_D(const QPixmapDropShadowFilter);
1311     return rect.united(rect.translated(d->offset).adjusted(-d->radius, -d->radius, d->radius, d->radius));
1312 }
1313 
1314 /*!
1315     \internal
1316  */
draw(QPainter * p,const QPointF & pos,const QPixmap & px,const QRectF & src) const1317 void QPixmapDropShadowFilter::draw(QPainter *p,
1318                                    const QPointF &pos,
1319                                    const QPixmap &px,
1320                                    const QRectF &src) const
1321 {
1322     Q_D(const QPixmapDropShadowFilter);
1323 
1324     if (px.isNull())
1325         return;
1326 
1327     QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
1328     tmp.setDevicePixelRatio(px.devicePixelRatioF());
1329     tmp.fill(0);
1330     QPainter tmpPainter(&tmp);
1331     tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
1332     tmpPainter.drawPixmap(d->offset, px);
1333     tmpPainter.end();
1334 
1335     // blur the alpha channel
1336     QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
1337     blurred.setDevicePixelRatio(px.devicePixelRatioF());
1338     blurred.fill(0);
1339     QPainter blurPainter(&blurred);
1340     qt_blurImage(&blurPainter, tmp, d->radius, false, true);
1341     blurPainter.end();
1342 
1343     tmp = std::move(blurred);
1344 
1345     // blacken the image...
1346     tmpPainter.begin(&tmp);
1347     tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
1348     tmpPainter.fillRect(tmp.rect(), d->color);
1349     tmpPainter.end();
1350 
1351     // draw the blurred drop shadow...
1352     p->drawImage(pos, tmp);
1353 
1354     // Draw the actual pixmap...
1355     p->drawPixmap(pos, px, src);
1356 }
1357 
1358 QT_END_NAMESPACE
1359 
1360 #include "moc_qpixmapfilter_p.cpp"
1361