Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpixmapfilter.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <qglobal.h>
6
7#include <QDebug>
8
9#include "qpainter.h"
10#include "qpixmap.h"
13
14#include "private/qguiapplication_p.h"
15#include "private/qpaintengineex_p.h"
16#include "private/qpaintengine_raster_p.h"
17#include "qmath.h"
18#include "private/qmath_p.h"
19#include "private/qdrawhelper_p.h"
20
21#include <memory>
22
24
26{
27 Q_DECLARE_PUBLIC(QPixmapFilter)
28public:
30};
31
32/*!
33 \class QPixmapFilter
34 \since 4.5
35 \ingroup painting
36
37 \brief The QPixmapFilter class provides the basic functionality for
38 pixmap filter classes. Pixmap filter can be for example colorize or blur.
39
40 QPixmapFilter is the base class for every pixmap filter. QPixmapFilter is
41 an abstract class and cannot itself be instantiated. It provides a standard
42 interface for filter processing.
43
44 \internal
45*/
46
47/*!
48 \enum QPixmapFilter::FilterType
49
50 \internal
51
52 This enum describes the types of filter that can be applied to pixmaps.
53
54 \value ConvolutionFilter A filter that is used to calculate the convolution
55 of the image with a kernel. See
56 QPixmapConvolutionFilter for more information.
57 \value ColorizeFilter A filter that is used to change the overall color
58 of an image. See QPixmapColorizeFilter for more
59 information.
60 \value DropShadowFilter A filter that is used to add a drop shadow to an
61 image. See QPixmapDropShadowFilter for more
62 information.
63 \value BlurFilter A filter that is used to blur an image using
64 a simple blur radius. See QPixmapBlurFilter
65 for more information.
66
67 \value UserFilter The first filter type that can be used for
68 application-specific purposes.
69*/
70
71
72/*!
73 Constructs a default QPixmapFilter with the given \a type.
74
75 This constructor should be used when subclassing QPixmapFilter to
76 create custom user filters.
77
78 \internal
79*/
80QPixmapFilter::QPixmapFilter(FilterType type, QObject *parent)
81 : QObject(*new QPixmapFilterPrivate, parent)
82{
83 d_func()->type = type;
84}
85
86
87
88/*!
89 \internal
90*/
91QPixmapFilter::QPixmapFilter(QPixmapFilterPrivate&d, QPixmapFilter::FilterType type, QObject *parent)
92 : QObject(d, parent)
93{
94 d_func()->type = type;
95}
96
97
98/*!
99 Destroys the pixmap filter.
100
101 \internal
102*/
103QPixmapFilter::~QPixmapFilter()
104{
105}
106
107/*!
108 Returns the type of the filter. All standard pixmap filter classes
109 are associated with a unique value.
110
111 \internal
112*/
113QPixmapFilter::FilterType QPixmapFilter::type() const
114{
115 Q_D(const QPixmapFilter);
116 return d->type;
117}
118
119/*!
120 Returns the bounding rectangle that is affected by the pixmap
121 filter if the filter is applied to the specified \a rect.
122
123 \internal
124*/
125QRectF QPixmapFilter::boundingRectFor(const QRectF &rect) const
126{
127 return rect;
128}
129
130/*!
131 \fn void QPixmapFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
132
133 Uses \a painter to draw filtered result of \a src at the point
134 specified by \a p. If \a srcRect is specified the it will
135 be used as a source rectangle to only draw a part of the source.
136
137 draw() will affect the area which boundingRectFor() returns.
138
139 \internal
140*/
141
142/*!
143 \class QPixmapConvolutionFilter
144 \since 4.5
145 \ingroup painting
146
147 \brief The QPixmapConvolutionFilter class provides convolution
148 filtering for pixmaps.
149
150 QPixmapConvolutionFilter implements a convolution pixmap filter,
151 which is applied when \l{QPixmapFilter::}{draw()} is called. A
152 convolution filter lets you distort an image by setting the values
153 of a matrix of qreal values called its
154 \l{setConvolutionKernel()}{kernel}. The matrix's values are
155 usually between -1.0 and 1.0.
156
157 \omit
158 In convolution filtering, the pixel value is calculated from the
159 neighboring pixels based on the weighting convolution kernel.
160 This needs explaining to be useful.
161 \endomit
162
163 Example:
164 \snippet code/src_gui_image_qpixmapfilter.cpp 1
165
166 \sa {Pixmap Filters Example}, QPixmapColorizeFilter, QPixmapDropShadowFilter
167
168
169 \internal
170*/
171
185
186
187/*!
188 Constructs a pixmap convolution filter.
189
190 By default there is no convolution kernel.
191
192 \internal
193*/
194QPixmapConvolutionFilter::QPixmapConvolutionFilter(QObject *parent)
195 : QPixmapFilter(*new QPixmapConvolutionFilterPrivate, ConvolutionFilter, parent)
196{
197 Q_D(QPixmapConvolutionFilter);
198 d->convoluteAlpha = true;
199}
200
201/*!
202 Destructor of pixmap convolution filter.
203
204 \internal
205*/
206QPixmapConvolutionFilter::~QPixmapConvolutionFilter()
207{
208}
209
210/*!
211 Sets convolution kernel with the given number of \a rows and \a columns.
212 Values from \a kernel are copied to internal data structure.
213
214 To preserve the intensity of the pixmap, the sum of all the
215 values in the convolution kernel should add up to 1.0. A sum
216 greater than 1.0 produces a lighter result and a sum less than 1.0
217 produces a darker and transparent result.
218
219 \internal
220*/
221void QPixmapConvolutionFilter::setConvolutionKernel(const qreal *kernel, int rows, int columns)
222{
223 Q_D(QPixmapConvolutionFilter);
224 delete [] d->convolutionKernel;
225 d->convolutionKernel = new qreal[rows * columns];
226 memcpy(d->convolutionKernel, kernel, sizeof(qreal) * rows * columns);
227 d->kernelWidth = columns;
228 d->kernelHeight = rows;
229}
230
231/*!
232 Gets the convolution kernel data.
233
234 \internal
235*/
236const qreal *QPixmapConvolutionFilter::convolutionKernel() const
237{
238 Q_D(const QPixmapConvolutionFilter);
239 return d->convolutionKernel;
240}
241
242/*!
243 Gets the number of rows in the convolution kernel.
244
245 \internal
246*/
247int QPixmapConvolutionFilter::rows() const
248{
249 Q_D(const QPixmapConvolutionFilter);
250 return d->kernelHeight;
251}
252
253/*!
254 Gets the number of columns in the convolution kernel.
255
256 \internal
257*/
258int QPixmapConvolutionFilter::columns() const
259{
260 Q_D(const QPixmapConvolutionFilter);
261 return d->kernelWidth;
262}
263
264
265/*!
266 \internal
267*/
268QRectF QPixmapConvolutionFilter::boundingRectFor(const QRectF &rect) const
269{
270 Q_D(const QPixmapConvolutionFilter);
271 return rect.adjusted(-d->kernelWidth / 2, -d->kernelHeight / 2, (d->kernelWidth - 1) / 2, (d->kernelHeight - 1) / 2);
272}
273
274// Convolutes the image
275static void convolute(
276 QImage *destImage,
277 const QPointF &pos,
278 const QImage &srcImage,
279 const QRectF &srcRect,
280 QPainter::CompositionMode mode,
281 qreal *kernel,
282 int kernelWidth,
283 int kernelHeight )
284{
285 const QImage processImage = (srcImage.format() != QImage::Format_ARGB32_Premultiplied ) ? srcImage.convertToFormat(QImage::Format_ARGB32_Premultiplied) : srcImage;
286 // TODO: support also other formats directly without copying
287
288 std::unique_ptr<int[]> fixedKernel(new int[kernelWidth * kernelHeight]);
289 for(int i = 0; i < kernelWidth*kernelHeight; i++)
290 {
291 fixedKernel[i] = (int)(65536 * kernel[i]);
292 }
293 QRectF trect = srcRect.isNull() ? processImage.rect() : srcRect;
294 trect.moveTo(pos);
295 QRectF bounded = trect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
296 QRect rect = bounded.toAlignedRect();
297 QRect targetRect = rect.intersected(destImage->rect());
298
299 QRectF srect = srcRect.isNull() ? processImage.rect() : srcRect;
300 QRectF sbounded = srect.adjusted(-kernelWidth / 2, -kernelHeight / 2, (kernelWidth - 1) / 2, (kernelHeight - 1) / 2);
301 QPoint srcStartPoint = sbounded.toAlignedRect().topLeft()+(targetRect.topLeft()-rect.topLeft());
302
303 const uint *sourceStart = (const uint*)processImage.scanLine(0);
304 uint *outputStart = (uint*)destImage->scanLine(0);
305
306 int yk = srcStartPoint.y();
307 for (int y = targetRect.top(); y <= targetRect.bottom(); y++) {
308 uint* output = outputStart + (destImage->bytesPerLine()/sizeof(uint))*y+targetRect.left();
309 int xk = srcStartPoint.x();
310 for(int x = targetRect.left(); x <= targetRect.right(); x++) {
311 int r = 0;
312 int g = 0;
313 int b = 0;
314 int a = 0;
315
316 // some out of bounds pre-checking to avoid inner-loop ifs
317 int kernely = -kernelHeight/2;
318 int starty = 0;
319 int endy = kernelHeight;
320 if (yk+kernely+endy >= srcImage.height())
321 endy = kernelHeight-((yk+kernely+endy)-srcImage.height())-1;
322 if (yk+kernely < 0)
323 starty = -(yk+kernely);
324
325 int kernelx = -kernelWidth/2;
326 int startx = 0;
327 int endx = kernelWidth;
328 if (xk+kernelx+endx >= srcImage.width())
329 endx = kernelWidth-((xk+kernelx+endx)-srcImage.width())-1;
330 if (xk+kernelx < 0)
331 startx = -(xk+kernelx);
332
333 for (int ys = starty; ys < endy; ys ++) {
334 const uint *pix = sourceStart + (processImage.bytesPerLine()/sizeof(uint))*(yk+kernely+ys) + ((xk+kernelx+startx));
335 const uint *endPix = pix+endx-startx;
336 int kernelPos = ys*kernelWidth+startx;
337 while (pix < endPix) {
338 int factor = fixedKernel[kernelPos++];
339 a += (((*pix) & 0xff000000)>>24) * factor;
340 r += (((*pix) & 0x00ff0000)>>16) * factor;
341 g += (((*pix) & 0x0000ff00)>>8 ) * factor;
342 b += (((*pix) & 0x000000ff) ) * factor;
343 pix++;
344 }
345 }
346
347 r = qBound((int)0, r >> 16, (int)255);
348 g = qBound((int)0, g >> 16, (int)255);
349 b = qBound((int)0, b >> 16, (int)255);
350 a = qBound((int)0, a >> 16, (int)255);
351 // composition mode checking could be moved outside of loop
352 if (mode == QPainter::CompositionMode_Source) {
353 uint color = (a<<24)+(r<<16)+(g<<8)+b;
354 *output++ = color;
355 } else {
356 uint current = *output;
357 uchar ca = (current&0xff000000)>>24;
358 uchar cr = (current&0x00ff0000)>>16;
359 uchar cg = (current&0x0000ff00)>>8;
360 uchar cb = (current&0x000000ff);
361 uint color =
362 (((ca*(255-a) >> 8)+a) << 24)+
363 (((cr*(255-a) >> 8)+r) << 16)+
364 (((cg*(255-a) >> 8)+g) << 8)+
365 (((cb*(255-a) >> 8)+b));
366 *output++ = color;
367 }
368 xk++;
369 }
370 yk++;
371 }
372}
373
374/*!
375 \internal
376*/
377void QPixmapConvolutionFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF& srcRect) const
378{
379 Q_D(const QPixmapConvolutionFilter);
380 if (!painter->isActive())
381 return;
382
383 if (d->kernelWidth<=0 || d->kernelHeight <= 0)
384 return;
385
386 if (src.isNull())
387 return;
388
389 // raster implementation
390
391 QImage *target = nullptr;
392 if (painter->paintEngine()->paintDevice()->devType() == QInternal::Image) {
393 target = static_cast<QImage *>(painter->paintEngine()->paintDevice());
394
395 QTransform mat = painter->combinedTransform();
396
397 if (mat.type() > QTransform::TxTranslate) {
398 // Disabled because of transformation...
399 target = nullptr;
400 } else {
401 QRasterPaintEngine *pe = static_cast<QRasterPaintEngine *>(painter->paintEngine());
402 if (pe->clipType() == QRasterPaintEngine::ComplexClip)
403 // disabled because of complex clipping...
404 target = nullptr;
405 else {
406 QRectF clip = pe->clipBoundingRect();
407 QRectF rect = boundingRectFor(srcRect.isEmpty() ? src.rect() : srcRect);
408 QTransform x = painter->deviceTransform();
409 if (!clip.contains(rect.translated(x.dx() + p.x(), x.dy() + p.y()))) {
410 target = nullptr;
411 }
412
413 }
414 }
415 }
416
417 if (target) {
418 QTransform x = painter->deviceTransform();
419 QPointF offset(x.dx(), x.dy());
420
421 convolute(target, p+offset, src.toImage(), srcRect, QPainter::CompositionMode_SourceOver, d->convolutionKernel, d->kernelWidth, d->kernelHeight);
422 } else {
423 QRect srect = srcRect.isNull() ? src.rect() : srcRect.toRect();
424 QRect rect = boundingRectFor(srect).toRect();
425 QImage result = QImage(rect.size(), QImage::Format_ARGB32_Premultiplied);
426 QPoint offset = srect.topLeft() - rect.topLeft();
427 convolute(&result,
428 offset,
429 src.toImage(),
430 srect,
431 QPainter::CompositionMode_Source,
432 d->convolutionKernel,
433 d->kernelWidth,
434 d->kernelHeight);
435 painter->drawImage(p - offset, result);
436 }
437}
438
439/*!
440 \class QPixmapBlurFilter
441 \since 4.6
442 \ingroup multimedia
443
444 \brief The QPixmapBlurFilter class provides blur filtering
445 for pixmaps.
446
447 QPixmapBlurFilter implements a blur pixmap filter,
448 which is applied when \l{QPixmapFilter::}{draw()} is called.
449
450 The filter lets you specialize the radius of the blur as well
451 as hints as to whether to prefer performance or quality.
452
453 By default, the blur effect is produced by applying an exponential
454 filter generated from the specified blurRadius(). Paint engines
455 may override this with a custom blur that is faster on the
456 underlying hardware.
457
458 \sa {Pixmap Filters Example}, QPixmapConvolutionFilter, QPixmapDropShadowFilter
459
460 \internal
461*/
462
471
472
473/*!
474 Constructs a pixmap blur filter.
475
476 \internal
477*/
478QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent)
479 : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent)
480{
481}
482
483/*!
484 Destructor of pixmap blur filter.
485
486 \internal
487*/
488QPixmapBlurFilter::~QPixmapBlurFilter()
489{
490}
491
492/*!
493 Sets the radius of the blur filter. Higher radius produces increased blurriness.
494
495 \internal
496*/
497void QPixmapBlurFilter::setRadius(qreal radius)
498{
499 Q_D(QPixmapBlurFilter);
500 d->radius = radius;
501}
502
503/*!
504 Gets the radius of the blur filter.
505
506 \internal
507*/
508qreal QPixmapBlurFilter::radius() const
509{
510 Q_D(const QPixmapBlurFilter);
511 return d->radius;
512}
513
514/*!
515 Setting the blur hints to PerformanceHint causes the implementation
516 to trade off visual quality to blur the image faster. Setting the
517 blur hints to QualityHint causes the implementation to improve
518 visual quality at the expense of speed.
519
520 AnimationHint causes the implementation to optimize for animating
521 the blur radius, possibly by caching blurred versions of the source
522 pixmap.
523
524 The implementation is free to ignore this value if it only has a single
525 blur algorithm.
526
527 \internal
528*/
529void QPixmapBlurFilter::setBlurHints(QGraphicsBlurEffect::BlurHints hints)
530{
531 Q_D(QPixmapBlurFilter);
532 d->hints = hints;
533}
534
535/*!
536 Gets the blur hints of the blur filter.
537
538 \internal
539*/
540QGraphicsBlurEffect::BlurHints QPixmapBlurFilter::blurHints() const
541{
542 Q_D(const QPixmapBlurFilter);
543 return d->hints;
544}
545
546const qreal radiusScale = qreal(2.5);
547
548/*!
549 \internal
550*/
551QRectF QPixmapBlurFilter::boundingRectFor(const QRectF &rect) const
552{
553 Q_D(const QPixmapBlurFilter);
554 const qreal delta = radiusScale * d->radius + 1;
555 return rect.adjusted(-delta, -delta, delta, delta);
556}
557
558Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
559
560Q_GUI_EXPORT extern void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
561
562/*!
563 \internal
564*/
565void QPixmapBlurFilter::draw(QPainter *painter, const QPointF &p, const QPixmap &src, const QRectF &rect) const
566{
567 Q_D(const QPixmapBlurFilter);
568 if (!painter->isActive())
569 return;
570
571 if (src.isNull())
572 return;
573
574 QRectF srcRect = rect;
575 if (srcRect.isNull())
576 srcRect = src.rect();
577
578 if (d->radius <= 1) {
579 painter->drawPixmap(srcRect.translated(p), src, srcRect);
580 return;
581 }
582
583 qreal scaledRadius = radiusScale * d->radius;
584 qreal scale;
585 if (qt_scaleForTransform(painter->transform(), &scale))
586 scaledRadius /= scale;
587
588 QImage srcImage;
589
590 if (srcRect == src.rect()) {
591 srcImage = src.toImage();
592 } else {
593 QRect rect = srcRect.toAlignedRect().intersected(src.rect());
594 srcImage = src.copy(rect).toImage();
595 }
596
597 QTransform transform = painter->worldTransform();
598 painter->translate(p);
599 qt_blurImage(painter, srcImage, scaledRadius, (d->hints & QGraphicsBlurEffect::QualityHint), false);
600 painter->setWorldTransform(transform);
601}
602
603// grayscales the image to dest (could be same). If rect isn't defined
604// destination image size is used to determine the dimension of grayscaling
605// process.
606static void grayscale(const QImage &image, QImage &dest, const QRect& rect = QRect())
607{
608 QRect destRect = rect;
609 QRect srcRect = rect;
610 if (rect.isNull()) {
611 srcRect = dest.rect();
612 destRect = dest.rect();
613 }
614 if (&image != &dest) {
615 destRect.moveTo(QPoint(0, 0));
616 }
617
618 const unsigned int *data = (const unsigned int *)image.bits();
619 unsigned int *outData = (unsigned int *)dest.bits();
620
621 if (dest.size() == image.size() && image.rect() == srcRect) {
622 // a bit faster loop for grayscaling everything
623 int pixels = dest.width() * dest.height();
624 for (int i = 0; i < pixels; ++i) {
625 int val = qGray(data[i]);
626 outData[i] = qRgba(val, val, val, qAlpha(data[i]));
627 }
628 } else {
629 int yd = destRect.top();
630 for (int y = srcRect.top(); y <= srcRect.bottom() && y < image.height(); y++) {
631 data = (const unsigned int*)image.scanLine(y);
632 outData = (unsigned int*)dest.scanLine(yd++);
633 int xd = destRect.left();
634 for (int x = srcRect.left(); x <= srcRect.right() && x < image.width(); x++) {
635 int val = qGray(data[x]);
636 outData[xd++] = qRgba(val, val, val, qAlpha(data[x]));
637 }
638 }
639 }
640}
641
642/*!
643 \class QPixmapColorizeFilter
644 \since 4.5
645 \ingroup painting
646
647 \brief The QPixmapColorizeFilter class provides colorizing
648 filtering for pixmaps.
649
650 A colorize filter gives the pixmap a tint of its color(). The
651 filter first grayscales the pixmap and then converts those to
652 colorized values using QPainter::CompositionMode_Screen with the
653 chosen color. The alpha-channel is not changed.
654
655 Example:
656 \snippet code/src_gui_image_qpixmapfilter.cpp 0
657
658 \sa QPainter::CompositionMode
659
660 \internal
661*/
663{
664 Q_DECLARE_PUBLIC(QPixmapColorizeFilter)
665public:
671};
672
673/*!
674 Constructs an pixmap colorize filter.
675
676 Default color value for colorizing is QColor(0, 0, 192).
677
678 \internal
679*/
680QPixmapColorizeFilter::QPixmapColorizeFilter(QObject *parent)
681 : QPixmapFilter(*new QPixmapColorizeFilterPrivate, ColorizeFilter, parent)
682{
683 Q_D(QPixmapColorizeFilter);
684 d->color = QColor(0, 0, 192);
685 d->strength = qreal(1);
686 d->opaque = true;
687 d->alphaBlend = false;
688}
689
690/*!
691 \internal
692*/
693QPixmapColorizeFilter::~QPixmapColorizeFilter()
694{
695}
696
697/*!
698 Gets the color of the colorize filter.
699
700 \internal
701*/
702QColor QPixmapColorizeFilter::color() const
703{
704 Q_D(const QPixmapColorizeFilter);
705 return d->color;
706}
707
708/*!
709 Sets the color of the colorize filter to the \a color specified.
710
711 \internal
712*/
713void QPixmapColorizeFilter::setColor(const QColor &color)
714{
715 Q_D(QPixmapColorizeFilter);
716 d->color = color;
717}
718
719/*!
720 Gets the strength of the colorize filter, 1.0 means full colorized while
721 0.0 equals to no filtering at all.
722
723 \internal
724*/
725qreal QPixmapColorizeFilter::strength() const
726{
727 Q_D(const QPixmapColorizeFilter);
728 return d->strength;
729}
730
731/*!
732 Sets the strength of the colorize filter to \a strength.
733
734 \internal
735*/
736void QPixmapColorizeFilter::setStrength(qreal strength)
737{
738 Q_D(QPixmapColorizeFilter);
739 d->strength = qBound(qreal(0), strength, qreal(1));
740 d->opaque = !qFuzzyIsNull(d->strength);
741 d->alphaBlend = !qFuzzyIsNull(d->strength - 1);
742}
743
744/*!
745 \internal
746*/
747void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
748{
749 Q_D(const QPixmapColorizeFilter);
750
751 if (src.isNull())
752 return;
753
754 // raster implementation
755
756 if (!d->opaque) {
757 painter->drawPixmap(dest, src, srcRect);
758 return;
759 }
760
761 QImage srcImage;
762 QImage destImage;
763
764 if (srcRect.isNull()) {
765 srcImage = src.toImage();
766 const auto format = srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
767 srcImage = std::move(srcImage).convertToFormat(format);
768 destImage = QImage(srcImage.size(), srcImage.format());
769 } else {
770 QRect rect = srcRect.toAlignedRect().intersected(src.rect());
771
772 srcImage = src.copy(rect).toImage();
773 const auto format = srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
774 srcImage = std::move(srcImage).convertToFormat(format);
775 destImage = QImage(rect.size(), srcImage.format());
776 }
777 destImage.setDevicePixelRatio(src.devicePixelRatio());
778
779 // do colorizing
780 QPainter destPainter(&destImage);
781 grayscale(srcImage, destImage, srcImage.rect());
782 destPainter.setCompositionMode(QPainter::CompositionMode_Screen);
783 destPainter.fillRect(srcImage.rect(), d->color);
784 destPainter.end();
785
786 if (d->alphaBlend) {
787 // alpha blending srcImage and destImage
788 QImage buffer = srcImage;
789 QPainter bufPainter(&buffer);
790 bufPainter.setOpacity(d->strength);
791 bufPainter.drawImage(0, 0, destImage);
792 bufPainter.end();
793 destImage = std::move(buffer);
794 }
795
796 if (srcImage.hasAlphaChannel()) {
797 Q_ASSERT(destImage.format() == QImage::Format_ARGB32_Premultiplied);
798 QPainter maskPainter(&destImage);
799 maskPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
800 maskPainter.drawImage(0, 0, srcImage);
801 }
802
803 painter->drawImage(dest, destImage);
804}
805
807{
808public:
810 : offset(8, 8), color(63, 63, 63, 180), radius(1) {}
811
813 QColor color;
815};
816
817/*!
818 \class QPixmapDropShadowFilter
819 \since 4.5
820 \ingroup painting
821
822 \brief The QPixmapDropShadowFilter class is a convenience class
823 for drawing pixmaps with drop shadows.
824
825 The drop shadow is produced by taking a copy of the source pixmap
826 and applying a color to the copy using a
827 QPainter::CompositionMode_DestinationIn operation. This produces a
828 homogeneously-colored pixmap which is then drawn using a
829 QPixmapConvolutionFilter at an offset. The original pixmap is
830 drawn on top.
831
832 The QPixmapDropShadowFilter class provides some customization
833 options to specify how the drop shadow should appear. The color of
834 the drop shadow can be modified using the setColor() function, the
835 drop shadow offset can be modified using the setOffset() function,
836 and the blur radius of the drop shadow can be changed through the
837 setBlurRadius() function.
838
839 By default, the drop shadow is a dark gray shadow, blurred with a
840 radius of 1 at an offset of 8 pixels towards the lower right.
841
842 Example:
843 \snippet code/src_gui_image_qpixmapfilter.cpp 2
844
845 \sa QPixmapColorizeFilter, QPixmapConvolutionFilter
846
847 \internal
848 */
849
850/*!
851 Constructs drop shadow filter.
852
853 \internal
854*/
855QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent)
856 : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent)
857{
858}
859
860/*!
861 Destroys drop shadow filter.
862
863 \internal
864*/
865QPixmapDropShadowFilter::~QPixmapDropShadowFilter()
866{
867}
868
869/*!
870 Returns the radius in pixels of the blur on the drop shadow.
871
872 A smaller radius results in a sharper shadow.
873
874 \sa color(), offset()
875
876 \internal
877*/
878qreal QPixmapDropShadowFilter::blurRadius() const
879{
880 Q_D(const QPixmapDropShadowFilter);
881 return d->radius;
882}
883
884/*!
885 Sets the radius in pixels of the blur on the drop shadow to the \a radius specified.
886
887 Using a smaller radius results in a sharper shadow.
888
889 \sa setColor(), setOffset()
890
891 \internal
892*/
893void QPixmapDropShadowFilter::setBlurRadius(qreal radius)
894{
895 Q_D(QPixmapDropShadowFilter);
896 d->radius = radius;
897}
898
899/*!
900 Returns the color of the drop shadow.
901
902 \sa blurRadius(), offset()
903
904 \internal
905*/
906QColor QPixmapDropShadowFilter::color() const
907{
908 Q_D(const QPixmapDropShadowFilter);
909 return d->color;
910}
911
912/*!
913 Sets the color of the drop shadow to the \a color specified.
914
915 \sa setBlurRadius(), setOffset()
916
917 \internal
918*/
919void QPixmapDropShadowFilter::setColor(const QColor &color)
920{
921 Q_D(QPixmapDropShadowFilter);
922 d->color = color;
923}
924
925/*!
926 Returns the shadow offset in pixels.
927
928 \sa blurRadius(), color()
929
930 \internal
931*/
932QPointF QPixmapDropShadowFilter::offset() const
933{
934 Q_D(const QPixmapDropShadowFilter);
935 return d->offset;
936}
937
938/*!
939 Sets the shadow offset in pixels to the \a offset specified.
940
941 \sa setBlurRadius(), setColor()
942
943 \internal
944*/
945void QPixmapDropShadowFilter::setOffset(const QPointF &offset)
946{
947 Q_D(QPixmapDropShadowFilter);
948 d->offset = offset;
949}
950
951/*!
952 \fn void QPixmapDropShadowFilter::setOffset(qreal dx, qreal dy)
953 \overload
954
955 Sets the shadow offset in pixels to be the displacement specified by the
956 horizontal \a dx and vertical \a dy coordinates.
957
958 \sa setBlurRadius(), setColor()
959
960 \internal
961*/
962
963/*!
964 \internal
965 */
966QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const
967{
968 Q_D(const QPixmapDropShadowFilter);
969 return rect.united(rect.translated(d->offset).adjusted(-d->radius, -d->radius, d->radius, d->radius));
970}
971
972/*!
973 \internal
974 */
975void QPixmapDropShadowFilter::draw(QPainter *p,
976 const QPointF &pos,
977 const QPixmap &px,
978 const QRectF &src) const
979{
980 Q_D(const QPixmapDropShadowFilter);
981
982 if (px.isNull())
983 return;
984
985 QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
986 tmp.setDevicePixelRatio(px.devicePixelRatio());
987 tmp.fill(0);
988 QPainter tmpPainter(&tmp);
989 tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
990 tmpPainter.drawPixmap(d->offset, px);
991 tmpPainter.end();
992
993 // blur the alpha channel
994 QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
995 blurred.setDevicePixelRatio(px.devicePixelRatio());
996 blurred.fill(0);
997 QPainter blurPainter(&blurred);
998 qt_blurImage(&blurPainter, tmp, d->radius, false, true);
999 blurPainter.end();
1000
1001 tmp = std::move(blurred);
1002
1003 // blacken the image...
1004 tmpPainter.begin(&tmp);
1005 tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
1006 tmpPainter.fillRect(tmp.rect(), d->color);
1007 tmpPainter.end();
1008
1009 // draw the blurred drop shadow...
1010 p->drawImage(pos, tmp);
1011
1012 // Draw the actual pixmap...
1013 p->drawPixmap(pos, px, src);
1014}
1015
1016QT_END_NAMESPACE
1017
1018#include "moc_qpixmapfilter_p.cpp"
friend class QPainter
QGraphicsBlurEffect::BlurHints hints
Q_GUI_EXPORT bool qt_scaleForTransform(const QTransform &transform, qreal *scale)
Q_GUI_EXPORT void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed=0)
const qreal radiusScale
static void grayscale(const QImage &image, QImage &dest, const QRect &rect=QRect())
static void convolute(QImage *destImage, const QPointF &pos, const QImage &srcImage, const QRectF &srcRect, QPainter::CompositionMode mode, qreal *kernel, int kernelWidth, int kernelHeight)