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
59QT_BEGIN_NAMESPACE
60
61class QPixmapFilterPrivate : public QObjectPrivate
62{
63 Q_DECLARE_PUBLIC(QPixmapFilter)
64public:
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*/
116QPixmapFilter::QPixmapFilter(FilterType type, QObject *parent)
117 : QObject(*new QPixmapFilterPrivate, parent)
118{
119 d_func()->type = type;
120}
121
122
123
124/*!
125 \internal
126*/
127QPixmapFilter::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*/
139QPixmapFilter::~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*/
149QPixmapFilter::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*/
161QRectF 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
208class QPixmapConvolutionFilterPrivate : public QPixmapFilterPrivate
209{
210public:
211 QPixmapConvolutionFilterPrivate(): convolutionKernel(nullptr), kernelWidth(0), kernelHeight(0), convoluteAlpha(false) {}
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*/
230QPixmapConvolutionFilter::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*/
242QPixmapConvolutionFilter::~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*/
257void 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*/
272const 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*/
283int 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*/
294int QPixmapConvolutionFilter::columns() const
295{
296 Q_D(const QPixmapConvolutionFilter);
297 return d->kernelWidth;
298}
299
300
301/*!
302 \internal
303*/
304QRectF 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
311static 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*/
413void 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
499class QPixmapBlurFilterPrivate : public QPixmapFilterPrivate
500{
501public:
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*/
514QPixmapBlurFilter::QPixmapBlurFilter(QObject *parent)
515 : QPixmapFilter(*new QPixmapBlurFilterPrivate, BlurFilter, parent)
516{
517}
518
519/*!
520 Destructor of pixmap blur filter.
521
522 \internal
523*/
524QPixmapBlurFilter::~QPixmapBlurFilter()
525{
526}
527
528/*!
529 Sets the radius of the blur filter. Higher radius produces increased blurriness.
530
531 \internal
532*/
533void 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*/
544qreal 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*/
565void 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*/
576QGraphicsBlurEffect::BlurHints QPixmapBlurFilter::blurHints() const
577{
578 Q_D(const QPixmapBlurFilter);
579 return d->hints;
580}
581
582const qreal radiusScale = qreal(2.5);
583
584/*!
585 \internal
586*/
587QRectF 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
594template <int shift>
595inline 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
605template<int aprec, int zprec>
606inline 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
636const int alphaIndex = (QSysInfo::ByteOrder == QSysInfo::BigEndian ? 0 : 3);
637
638template<int aprec, int zprec>
639inline 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
647template<int aprec, int zprec, bool alphaOnly>
648inline 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*/
695template <int aprec, int zprec, bool alphaOnly>
696void 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.devicePixelRatio());
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
774Q_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.devicePixelRatio());
785
786 const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
787 qsizetype sx = srcImage.bytesPerLine();
788 qsizetype sx2 = sx << 1;
789
790 uchar *dst = reinterpret_cast<uchar*>(dest.bits());
791 qsizetype 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.devicePixelRatio());
807
808 const uchar *src = reinterpret_cast<const uchar*>(const_cast<const QImage &>(srcImage).bits());
809 qsizetype sx = srcImage.bytesPerLine();
810 qsizetype sx2 = sx << 1;
811
812 uchar *dst = reinterpret_cast<uchar*>(dest.bits());
813 qsizetype 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.devicePixelRatio());
844
845 const quint32 *src = reinterpret_cast<const quint32*>(const_cast<const QImage &>(srcImage).bits());
846 qsizetype sx = srcImage.bytesPerLine() >> 2;
847 qsizetype sx2 = sx << 1;
848
849 quint32 *dst = reinterpret_cast<quint32*>(dest.bits());
850 qsizetype 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
865Q_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.devicePixelRatio()), blurImage);
889 }
890}
891
892Q_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
900Q_GUI_EXPORT extern bool qt_scaleForTransform(const QTransform &transform, qreal *scale);
901
902/*!
903 \internal
904*/
905void 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.
946static 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*/
1002class QPixmapColorizeFilterPrivate : public QPixmapFilterPrivate
1003{
1004 Q_DECLARE_PUBLIC(QPixmapColorizeFilter)
1005public:
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*/
1020QPixmapColorizeFilter::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*/
1033QPixmapColorizeFilter::~QPixmapColorizeFilter()
1034{
1035}
1036
1037/*!
1038 Gets the color of the colorize filter.
1039
1040 \internal
1041*/
1042QColor QPixmapColorizeFilter::color() const
1043{
1044 Q_D(const QPixmapColorizeFilter);
1045 return d->color;
1046}
1047
1048/*!
1049 Sets the color of the colorize filter to the \a color specified.
1050
1051 \internal
1052*/
1053void QPixmapColorizeFilter::setColor(const QColor &color)
1054{
1055 Q_D(QPixmapColorizeFilter);
1056 d->color = color;
1057}
1058
1059/*!
1060 Gets the strength of the colorize filter, 1.0 means full colorized while
1061 0.0 equals to no filtering at all.
1062
1063 \internal
1064*/
1065qreal QPixmapColorizeFilter::strength() const
1066{
1067 Q_D(const QPixmapColorizeFilter);
1068 return d->strength;
1069}
1070
1071/*!
1072 Sets the strength of the colorize filter to \a strength.
1073
1074 \internal
1075*/
1076void QPixmapColorizeFilter::setStrength(qreal strength)
1077{
1078 Q_D(QPixmapColorizeFilter);
1079 d->strength = qBound(qreal(0), strength, qreal(1));
1080 d->opaque = !qFuzzyIsNull(d->strength);
1081 d->alphaBlend = !qFuzzyIsNull(d->strength - 1);
1082}
1083
1084/*!
1085 \internal
1086*/
1087void QPixmapColorizeFilter::draw(QPainter *painter, const QPointF &dest, const QPixmap &src, const QRectF &srcRect) const
1088{
1089 Q_D(const QPixmapColorizeFilter);
1090
1091 if (src.isNull())
1092 return;
1093
1094 // raster implementation
1095
1096 if (!d->opaque) {
1097 painter->drawPixmap(dest, src, srcRect);
1098 return;
1099 }
1100
1101 QImage srcImage;
1102 QImage destImage;
1103
1104 if (srcRect.isNull()) {
1105 srcImage = src.toImage();
1106 const auto format = srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
1107 srcImage = std::move(srcImage).convertToFormat(format);
1108 destImage = QImage(srcImage.size(), srcImage.format());
1109 } else {
1110 QRect rect = srcRect.toAlignedRect().intersected(src.rect());
1111
1112 srcImage = src.copy(rect).toImage();
1113 const auto format = srcImage.hasAlphaChannel() ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32;
1114 srcImage = std::move(srcImage).convertToFormat(format);
1115 destImage = QImage(rect.size(), srcImage.format());
1116 }
1117 destImage.setDevicePixelRatio(src.devicePixelRatio());
1118
1119 // do colorizing
1120 QPainter destPainter(&destImage);
1121 grayscale(srcImage, destImage, srcImage.rect());
1122 destPainter.setCompositionMode(QPainter::CompositionMode_Screen);
1123 destPainter.fillRect(srcImage.rect(), d->color);
1124 destPainter.end();
1125
1126 if (d->alphaBlend) {
1127 // alpha blending srcImage and destImage
1128 QImage buffer = srcImage;
1129 QPainter bufPainter(&buffer);
1130 bufPainter.setOpacity(d->strength);
1131 bufPainter.drawImage(0, 0, destImage);
1132 bufPainter.end();
1133 destImage = std::move(buffer);
1134 }
1135
1136 if (srcImage.hasAlphaChannel()) {
1137 Q_ASSERT(destImage.format() == QImage::Format_ARGB32_Premultiplied);
1138 QPainter maskPainter(&destImage);
1139 maskPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
1140 maskPainter.drawImage(0, 0, srcImage);
1141 }
1142
1143 painter->drawImage(dest, destImage);
1144}
1145
1146class QPixmapDropShadowFilterPrivate : public QPixmapFilterPrivate
1147{
1148public:
1149 QPixmapDropShadowFilterPrivate()
1150 : offset(8, 8), color(63, 63, 63, 180), radius(1) {}
1151
1152 QPointF offset;
1153 QColor color;
1154 qreal radius;
1155};
1156
1157/*!
1158 \class QPixmapDropShadowFilter
1159 \since 4.5
1160 \ingroup painting
1161
1162 \brief The QPixmapDropShadowFilter class is a convenience class
1163 for drawing pixmaps with drop shadows.
1164
1165 The drop shadow is produced by taking a copy of the source pixmap
1166 and applying a color to the copy using a
1167 QPainter::CompositionMode_DestinationIn operation. This produces a
1168 homogeneously-colored pixmap which is then drawn using a
1169 QPixmapConvolutionFilter at an offset. The original pixmap is
1170 drawn on top.
1171
1172 The QPixmapDropShadowFilter class provides some customization
1173 options to specify how the drop shadow should appear. The color of
1174 the drop shadow can be modified using the setColor() function, the
1175 drop shadow offset can be modified using the setOffset() function,
1176 and the blur radius of the drop shadow can be changed through the
1177 setBlurRadius() function.
1178
1179 By default, the drop shadow is a dark gray shadow, blurred with a
1180 radius of 1 at an offset of 8 pixels towards the lower right.
1181
1182 Example:
1183 \snippet code/src_gui_image_qpixmapfilter.cpp 2
1184
1185 \sa QPixmapColorizeFilter, QPixmapConvolutionFilter
1186
1187 \internal
1188 */
1189
1190/*!
1191 Constructs drop shadow filter.
1192
1193 \internal
1194*/
1195QPixmapDropShadowFilter::QPixmapDropShadowFilter(QObject *parent)
1196 : QPixmapFilter(*new QPixmapDropShadowFilterPrivate, DropShadowFilter, parent)
1197{
1198}
1199
1200/*!
1201 Destroys drop shadow filter.
1202
1203 \internal
1204*/
1205QPixmapDropShadowFilter::~QPixmapDropShadowFilter()
1206{
1207}
1208
1209/*!
1210 Returns the radius in pixels of the blur on the drop shadow.
1211
1212 A smaller radius results in a sharper shadow.
1213
1214 \sa color(), offset()
1215
1216 \internal
1217*/
1218qreal QPixmapDropShadowFilter::blurRadius() const
1219{
1220 Q_D(const QPixmapDropShadowFilter);
1221 return d->radius;
1222}
1223
1224/*!
1225 Sets the radius in pixels of the blur on the drop shadow to the \a radius specified.
1226
1227 Using a smaller radius results in a sharper shadow.
1228
1229 \sa setColor(), setOffset()
1230
1231 \internal
1232*/
1233void QPixmapDropShadowFilter::setBlurRadius(qreal radius)
1234{
1235 Q_D(QPixmapDropShadowFilter);
1236 d->radius = radius;
1237}
1238
1239/*!
1240 Returns the color of the drop shadow.
1241
1242 \sa blurRadius(), offset()
1243
1244 \internal
1245*/
1246QColor QPixmapDropShadowFilter::color() const
1247{
1248 Q_D(const QPixmapDropShadowFilter);
1249 return d->color;
1250}
1251
1252/*!
1253 Sets the color of the drop shadow to the \a color specified.
1254
1255 \sa setBlurRadius(), setOffset()
1256
1257 \internal
1258*/
1259void QPixmapDropShadowFilter::setColor(const QColor &color)
1260{
1261 Q_D(QPixmapDropShadowFilter);
1262 d->color = color;
1263}
1264
1265/*!
1266 Returns the shadow offset in pixels.
1267
1268 \sa blurRadius(), color()
1269
1270 \internal
1271*/
1272QPointF QPixmapDropShadowFilter::offset() const
1273{
1274 Q_D(const QPixmapDropShadowFilter);
1275 return d->offset;
1276}
1277
1278/*!
1279 Sets the shadow offset in pixels to the \a offset specified.
1280
1281 \sa setBlurRadius(), setColor()
1282
1283 \internal
1284*/
1285void QPixmapDropShadowFilter::setOffset(const QPointF &offset)
1286{
1287 Q_D(QPixmapDropShadowFilter);
1288 d->offset = offset;
1289}
1290
1291/*!
1292 \fn void QPixmapDropShadowFilter::setOffset(qreal dx, qreal dy)
1293 \overload
1294
1295 Sets the shadow offset in pixels to be the displacement specified by the
1296 horizontal \a dx and vertical \a dy coordinates.
1297
1298 \sa setBlurRadius(), setColor()
1299
1300 \internal
1301*/
1302
1303/*!
1304 \internal
1305 */
1306QRectF QPixmapDropShadowFilter::boundingRectFor(const QRectF &rect) const
1307{
1308 Q_D(const QPixmapDropShadowFilter);
1309 return rect.united(rect.translated(d->offset).adjusted(-d->radius, -d->radius, d->radius, d->radius));
1310}
1311
1312/*!
1313 \internal
1314 */
1315void QPixmapDropShadowFilter::draw(QPainter *p,
1316 const QPointF &pos,
1317 const QPixmap &px,
1318 const QRectF &src) const
1319{
1320 Q_D(const QPixmapDropShadowFilter);
1321
1322 if (px.isNull())
1323 return;
1324
1325 QImage tmp(px.size(), QImage::Format_ARGB32_Premultiplied);
1326 tmp.setDevicePixelRatio(px.devicePixelRatio());
1327 tmp.fill(0);
1328 QPainter tmpPainter(&tmp);
1329 tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
1330 tmpPainter.drawPixmap(d->offset, px);
1331 tmpPainter.end();
1332
1333 // blur the alpha channel
1334 QImage blurred(tmp.size(), QImage::Format_ARGB32_Premultiplied);
1335 blurred.setDevicePixelRatio(px.devicePixelRatio());
1336 blurred.fill(0);
1337 QPainter blurPainter(&blurred);
1338 qt_blurImage(&blurPainter, tmp, d->radius, false, true);
1339 blurPainter.end();
1340
1341 tmp = std::move(blurred);
1342
1343 // blacken the image...
1344 tmpPainter.begin(&tmp);
1345 tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
1346 tmpPainter.fillRect(tmp.rect(), d->color);
1347 tmpPainter.end();
1348
1349 // draw the blurred drop shadow...
1350 p->drawImage(pos, tmp);
1351
1352 // Draw the actual pixmap...
1353 p->drawPixmap(pos, px, src);
1354}
1355
1356QT_END_NAMESPACE
1357
1358#include "moc_qpixmapfilter_p.cpp"
1359