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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/widgets/effects/qpixmapfilter.cpp