1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qbrush.h"
6#include "qpixmap.h"
7#include "qbitmap.h"
8#include "qpixmapcache.h"
9#include <qpa/qplatformpixmap.h>
10#include "qdatastream.h"
11#include "qvariant.h"
12#include "qline.h"
13#include "qdebug.h"
14#include <QtCore/qjsondocument.h>
15#include <QtCore/qjsonarray.h>
16#include <QtCore/qcoreapplication.h>
17#include "private/qhexstring_p.h"
18#include <QtCore/qnumeric.h>
19#include <QtCore/qfile.h>
20#include <QtCore/qmutex.h>
21#include <QtCore/private/qoffsetstringarray_p.h>
22
23QT_BEGIN_NAMESPACE
24
25using namespace Qt::StringLiterals;
26
27#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
28// Avoid an ABI break due to the QScopedPointer->std::unique_ptr change
29static_assert(sizeof(QBrush::DataPtr) == sizeof(QScopedPointer<QBrushData, QBrushDataPointerDeleter>));
30#endif
31
32const uchar *qt_patternForBrush(int brushStyle, bool invert)
33{
34 Q_ASSERT(brushStyle > Qt::SolidPattern && brushStyle < Qt::LinearGradientPattern);
35 static const uchar pat_tbl[][2][8] = {
36 {
37 /* dense1 */ { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 },
38 /*~dense1 */ { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff },
39 }, {
40 /* dense2 */ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
41 /*~dense2 */ { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff },
42 }, {
43 /* dense3 */ { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 },
44 /*~dense3 */ { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee },
45 }, {
46 /* dense4 */ { 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa },
47 /*~dense4 */ { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 },
48 }, {
49 /* dense5 */ { 0x55, 0xbb, 0x55, 0xee, 0x55, 0xbb, 0x55, 0xee },
50 /*~dense5 */ { 0xaa, 0x44, 0xaa, 0x11, 0xaa, 0x44, 0xaa, 0x11 },
51 }, {
52 /* dense6 */ { 0x77, 0xff, 0xdd, 0xff, 0x77, 0xff, 0xdd, 0xff },
53 /*~dense6 */ { 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00 },
54 }, {
55 /* dense7 */ { 0xff, 0xbb, 0xff, 0xff, 0xff, 0xbb, 0xff, 0xff },
56 /*~dense7 */ { 0x00, 0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00 },
57 }, {
58 /* hor */ { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff },
59 /*~hor */ { 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00 },
60 }, {
61 /* ver */ { 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef },
62 /*~ver */ { 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10 },
63 }, {
64 /* cross */ { 0xef, 0xef, 0xef, 0x00, 0xef, 0xef, 0xef, 0xef },
65 /*~cross */ { 0x10, 0x10, 0x10, 0xff, 0x10, 0x10, 0x10, 0x10 },
66 }, {
67 /* bdiag */ { 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0xfe },
68 /*~bdiag */ { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 },
69 }, {
70 /* fdiag */ { 0xfe, 0xfd, 0xfb, 0xf7, 0xef, 0xdf, 0xbf, 0x7f },
71 /*~fdiag */ { 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 },
72 }, {
73 /* dcross */ { 0x7e, 0xbd, 0xdb, 0xe7, 0xe7, 0xdb, 0xbd, 0x7e },
74 /*~dcross */ { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 },
75 },
76 };
77 return pat_tbl[brushStyle - Qt::Dense1Pattern][invert];
78}
79
80Q_GUI_EXPORT QPixmap qt_pixmapForBrush(int brushStyle, bool invert)
81{
82
83 QPixmap pm;
84 QString key = "$qt-brush$"_L1
85 % HexString<uint>(brushStyle)
86 % QLatin1Char(invert ? '1' : '0');
87 if (!QPixmapCache::find(key, pixmap: &pm)) {
88 pm = QBitmap::fromData(size: QSize(8, 8), bits: qt_patternForBrush(brushStyle, invert),
89 monoFormat: QImage::Format_MonoLSB);
90 QPixmapCache::insert(key, pixmap: pm);
91 }
92
93 return pm;
94}
95
96static void qt_cleanup_brush_pattern_image_cache();
97class QBrushPatternImageCache
98{
99public:
100 QBrushPatternImageCache()
101 : m_initialized(false)
102 {
103 init();
104 }
105
106 void init()
107 {
108 qAddPostRoutine(qt_cleanup_brush_pattern_image_cache);
109 for (int style = Qt::Dense1Pattern; style <= Qt::DiagCrossPattern; ++style) {
110 int i = style - Qt::Dense1Pattern;
111 m_images[i][0] = QImage(qt_patternForBrush(brushStyle: style, invert: 0), 8, 8, 1, QImage::Format_MonoLSB);
112 m_images[i][1] = QImage(qt_patternForBrush(brushStyle: style, invert: 1), 8, 8, 1, QImage::Format_MonoLSB);
113 }
114 m_initialized = true;
115 }
116
117 QImage getImage(int brushStyle, bool invert) const
118 {
119 Q_ASSERT(brushStyle >= Qt::Dense1Pattern && brushStyle <= Qt::DiagCrossPattern);
120 if (!m_initialized)
121 const_cast<QBrushPatternImageCache*>(this)->init();
122 return m_images[brushStyle - Qt::Dense1Pattern][invert];
123 }
124
125 void cleanup() {
126 for (int style = Qt::Dense1Pattern; style <= Qt::DiagCrossPattern; ++style) {
127 int i = style - Qt::Dense1Pattern;
128 m_images[i][0] = QImage();
129 m_images[i][1] = QImage();
130 }
131 m_initialized = false;
132 }
133
134private:
135 QImage m_images[Qt::DiagCrossPattern - Qt::Dense1Pattern + 1][2];
136 bool m_initialized;
137};
138
139Q_GLOBAL_STATIC(QBrushPatternImageCache, qt_brushPatternImageCache)
140
141static void qt_cleanup_brush_pattern_image_cache()
142{
143 qt_brushPatternImageCache()->cleanup();
144}
145
146Q_GUI_EXPORT QImage qt_imageForBrush(int brushStyle, bool invert)
147{
148 return qt_brushPatternImageCache()->getImage(brushStyle, invert);
149}
150
151struct QTexturedBrushData : public QBrushData
152{
153 QTexturedBrushData() {
154 m_has_pixmap_texture = false;
155 m_pixmap = nullptr;
156 }
157 ~QTexturedBrushData() {
158 delete m_pixmap;
159 }
160
161 void setPixmap(const QPixmap &pm) {
162 delete m_pixmap;
163
164 if (pm.isNull()) {
165 m_pixmap = nullptr;
166 m_has_pixmap_texture = false;
167 } else {
168 m_pixmap = new QPixmap(pm);
169 m_has_pixmap_texture = true;
170 }
171
172 m_image = QImage();
173 }
174
175 void setImage(const QImage &image) {
176 m_image = image;
177 delete m_pixmap;
178 m_pixmap = nullptr;
179 m_has_pixmap_texture = false;
180 }
181
182 QPixmap &pixmap() {
183 if (!m_pixmap) {
184 m_pixmap = new QPixmap(QPixmap::fromImage(image: m_image));
185 }
186 return *m_pixmap;
187 }
188
189 QImage &image() {
190 if (m_image.isNull() && m_pixmap)
191 m_image = m_pixmap->toImage();
192 return m_image;
193 }
194
195 QPixmap *m_pixmap;
196 QImage m_image;
197 bool m_has_pixmap_texture;
198};
199
200// returns true if the brush has a pixmap (or bitmap) set as the
201// brush texture, false otherwise
202bool Q_GUI_EXPORT qHasPixmapTexture(const QBrush& brush)
203{
204 if (brush.style() != Qt::TexturePattern)
205 return false;
206 QTexturedBrushData *tx_data = static_cast<QTexturedBrushData *>(brush.d.get());
207 return tx_data->m_has_pixmap_texture;
208}
209
210struct QGradientBrushData : public QBrushData
211{
212 QGradient gradient;
213};
214
215static void deleteData(QBrushData *d)
216{
217 switch (d->style) {
218 case Qt::TexturePattern:
219 delete static_cast<QTexturedBrushData*>(d);
220 break;
221 case Qt::LinearGradientPattern:
222 case Qt::RadialGradientPattern:
223 case Qt::ConicalGradientPattern:
224 delete static_cast<QGradientBrushData*>(d);
225 break;
226 default:
227 delete d;
228 }
229}
230
231void QBrushDataPointerDeleter::operator()(QBrushData *d) const noexcept
232{
233 if (d && !d->ref.deref())
234 deleteData(d);
235}
236
237/*!
238 \class QBrush
239 \ingroup painting
240 \ingroup shared
241 \inmodule QtGui
242
243 \brief The QBrush class defines the fill pattern of shapes drawn
244 by QPainter.
245
246 A brush has a style, a color, a gradient and a texture.
247
248 The brush style() defines the fill pattern using the
249 Qt::BrushStyle enum. The default brush style is Qt::NoBrush
250 (depending on how you construct a brush). This style tells the
251 painter to not fill shapes. The standard style for filling is
252 Qt::SolidPattern. The style can be set when the brush is created
253 using the appropriate constructor, and in addition the setStyle()
254 function provides means for altering the style once the brush is
255 constructed.
256
257 \image brush-styles.png Brush Styles
258
259 The brush color() defines the color of the fill pattern. The color
260 can either be one of Qt's predefined colors, Qt::GlobalColor, or
261 any other custom QColor. The currently set color can be retrieved
262 and altered using the color() and setColor() functions,
263 respectively.
264
265 The gradient() defines the gradient fill used when the current
266 style is either Qt::LinearGradientPattern,
267 Qt::RadialGradientPattern or Qt::ConicalGradientPattern. Gradient
268 brushes are created by giving a QGradient as a constructor
269 argument when creating the QBrush. Qt provides three different
270 gradients: QLinearGradient, QConicalGradient, and QRadialGradient
271 - all of which inherit QGradient.
272
273 \snippet brush/gradientcreationsnippet.cpp 0
274
275 The texture() defines the pixmap used when the current style is
276 Qt::TexturePattern. You can create a brush with a texture by
277 providing the pixmap when the brush is created or by using
278 setTexture().
279
280 Note that applying setTexture() makes style() ==
281 Qt::TexturePattern, regardless of previous style
282 settings. Also, calling setColor() will not make a difference if
283 the style is a gradient. The same is the case if the style is
284 Qt::TexturePattern style unless the current texture is a QBitmap.
285
286 The isOpaque() function returns \c true if the brush is fully opaque
287 otherwise false. A brush is considered opaque if:
288
289 \list
290 \li The alpha component of the color() is 255.
291 \li Its texture() does not have an alpha channel and is not a QBitmap.
292 \li The colors in the gradient() all have an alpha component that is 255.
293 \endlist
294
295 \table 100%
296 \row
297 \li \inlineimage brush-outline.png Outlines
298 \li
299
300 To specify the style and color of lines and outlines, use the
301 QPainter's \l {QPen}{pen} combined with Qt::PenStyle and
302 Qt::GlobalColor:
303
304 \snippet code/src_gui_painting_qbrush.cpp 0
305
306 Note that, by default, QPainter renders the outline (using the
307 currently set pen) when drawing shapes. Use \l {Qt::NoPen}{\c
308 painter.setPen(Qt::NoPen)} to disable this behavior.
309
310 \endtable
311
312 For more information about painting in general, see the \l{Paint
313 System}.
314
315 \sa Qt::BrushStyle, QPainter, QColor
316*/
317
318class QNullBrushData
319{
320public:
321 QBrushData *brush;
322 QNullBrushData() : brush(new QBrushData)
323 {
324 brush->ref.storeRelaxed(newValue: 1);
325 brush->style = Qt::BrushStyle(0);
326 brush->color = Qt::black;
327 }
328 ~QNullBrushData()
329 {
330 if (!brush->ref.deref())
331 delete brush;
332 brush = nullptr;
333 }
334};
335
336Q_GLOBAL_STATIC(QNullBrushData, nullBrushInstance_holder)
337static QBrushData *nullBrushInstance()
338{
339 return nullBrushInstance_holder()->brush;
340}
341
342static bool qbrush_check_type(Qt::BrushStyle style) {
343 switch (style) {
344 case Qt::TexturePattern:
345 qWarning(msg: "QBrush: Incorrect use of TexturePattern");
346 break;
347 case Qt::LinearGradientPattern:
348 case Qt::RadialGradientPattern:
349 case Qt::ConicalGradientPattern:
350 qWarning(msg: "QBrush: Wrong use of a gradient pattern");
351 break;
352 default:
353 return true;
354 }
355 return false;
356}
357
358/*!
359 \internal
360 Initializes the brush.
361*/
362
363void QBrush::init(const QColor &color, Qt::BrushStyle style)
364{
365 switch(style) {
366 case Qt::NoBrush:
367 d.reset(p: nullBrushInstance());
368 d->ref.ref();
369 if (d->color != color) setColor(color);
370 return;
371 case Qt::TexturePattern:
372 d.reset(p: new QTexturedBrushData);
373 break;
374 case Qt::LinearGradientPattern:
375 case Qt::RadialGradientPattern:
376 case Qt::ConicalGradientPattern:
377 d.reset(p: new QGradientBrushData);
378 break;
379 default:
380 d.reset(p: new QBrushData);
381 break;
382 }
383 d->ref.storeRelaxed(newValue: 1);
384 d->style = style;
385 d->color = color;
386}
387
388/*!
389 Constructs a default black brush with the style Qt::NoBrush
390 (i.e. this brush will not fill shapes).
391*/
392
393QBrush::QBrush()
394 : d(nullBrushInstance())
395{
396 Q_ASSERT(d);
397 d->ref.ref();
398}
399
400/*!
401 Constructs a brush with a black color and a texture set to the
402 given \a pixmap. The style is set to Qt::TexturePattern.
403
404 \sa setTexture()
405*/
406
407QBrush::QBrush(const QPixmap &pixmap)
408{
409 init(color: Qt::black, style: Qt::TexturePattern);
410 setTexture(pixmap);
411}
412
413
414/*!
415 Constructs a brush with a black color and a texture set to the
416 given \a image. The style is set to Qt::TexturePattern.
417
418 \sa setTextureImage()
419*/
420
421QBrush::QBrush(const QImage &image)
422{
423 init(color: Qt::black, style: Qt::TexturePattern);
424 setTextureImage(image);
425}
426
427/*!
428 Constructs a black brush with the given \a style.
429
430 \sa setStyle()
431*/
432
433QBrush::QBrush(Qt::BrushStyle style)
434 : QBrush(QColor(Qt::black), style)
435{
436}
437
438/*!
439 Constructs a brush with the given \a color and \a style.
440
441 \sa setColor(), setStyle()
442*/
443
444QBrush::QBrush(const QColor &color, Qt::BrushStyle style)
445{
446 if (qbrush_check_type(style))
447 init(color, style);
448 else {
449 d.reset(p: nullBrushInstance());
450 d->ref.ref();
451 }
452}
453
454/*!
455 \fn QBrush::QBrush(Qt::GlobalColor color, Qt::BrushStyle style)
456
457 Constructs a brush with the given \a color and \a style.
458
459 \sa setColor(), setStyle()
460*/
461QBrush::QBrush(Qt::GlobalColor color, Qt::BrushStyle style)
462 : QBrush(QColor(color), style)
463{
464}
465
466/*!
467 Constructs a brush with the given \a color and the custom pattern
468 stored in \a pixmap.
469
470 The style is set to Qt::TexturePattern. The color will only have
471 an effect for QBitmaps.
472
473 \sa setColor(), setTexture()
474*/
475
476QBrush::QBrush(const QColor &color, const QPixmap &pixmap)
477{
478 init(color, style: Qt::TexturePattern);
479 setTexture(pixmap);
480}
481
482/*!
483
484 Constructs a brush with the given \a color and the custom pattern
485 stored in \a pixmap.
486
487 The style is set to Qt::TexturePattern. The color will only have
488 an effect for QBitmaps.
489
490 \sa setColor(), setTexture()
491*/
492QBrush::QBrush(Qt::GlobalColor color, const QPixmap &pixmap)
493{
494 init(color, style: Qt::TexturePattern);
495 setTexture(pixmap);
496}
497
498/*!
499 Constructs a copy of \a other.
500*/
501
502QBrush::QBrush(const QBrush &other)
503 : d(other.d.get())
504{
505 d->ref.ref();
506}
507
508/*!
509 Constructs a brush based on the given \a gradient.
510
511 The brush style is set to the corresponding gradient style (either
512 Qt::LinearGradientPattern, Qt::RadialGradientPattern or
513 Qt::ConicalGradientPattern).
514*/
515QBrush::QBrush(const QGradient &gradient)
516{
517 if (Q_UNLIKELY(gradient.type() == QGradient::NoGradient)) {
518 d.reset(p: nullBrushInstance());
519 d->ref.ref();
520 return;
521 }
522
523 const Qt::BrushStyle enum_table[] = {
524 Qt::LinearGradientPattern,
525 Qt::RadialGradientPattern,
526 Qt::ConicalGradientPattern
527 };
528
529 init(color: QColor(), style: enum_table[gradient.type()]);
530 QGradientBrushData *grad = static_cast<QGradientBrushData *>(d.get());
531 grad->gradient = gradient;
532}
533
534/*!
535 Destroys the brush.
536*/
537
538QBrush::~QBrush()
539{
540}
541
542static constexpr inline bool use_same_brushdata(Qt::BrushStyle lhs, Qt::BrushStyle rhs)
543{
544 return lhs == rhs // includes Qt::TexturePattern
545 || (lhs >= Qt::NoBrush && lhs <= Qt::DiagCrossPattern && rhs >= Qt::NoBrush && rhs <= Qt::DiagCrossPattern)
546 || (lhs >= Qt::LinearGradientPattern && lhs <= Qt::ConicalGradientPattern && rhs >= Qt::LinearGradientPattern && rhs <= Qt::ConicalGradientPattern)
547 ;
548}
549
550void QBrush::detach(Qt::BrushStyle newStyle)
551{
552 if (use_same_brushdata(lhs: newStyle, rhs: d->style) && d->ref.loadRelaxed() == 1) {
553 d->style = newStyle;
554 return;
555 }
556
557 DataPtr x;
558 switch(newStyle) {
559 case Qt::TexturePattern: {
560 QTexturedBrushData *tbd = new QTexturedBrushData;
561 if (d->style == Qt::TexturePattern) {
562 QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d.get());
563 if (data->m_has_pixmap_texture)
564 tbd->setPixmap(data->pixmap());
565 else
566 tbd->setImage(data->image());
567 }
568 x.reset(p: tbd);
569 break;
570 }
571 case Qt::LinearGradientPattern:
572 case Qt::RadialGradientPattern:
573 case Qt::ConicalGradientPattern: {
574 QGradientBrushData *gbd = new QGradientBrushData;
575 switch (d->style) {
576 case Qt::LinearGradientPattern:
577 case Qt::RadialGradientPattern:
578 case Qt::ConicalGradientPattern:
579 gbd->gradient =
580 static_cast<QGradientBrushData *>(d.get())->gradient;
581 break;
582 default:
583 break;
584 }
585 x.reset(p: gbd);
586 break;
587 }
588 default:
589 x.reset(p: new QBrushData);
590 break;
591 }
592 x->ref.storeRelaxed(newValue: 1); // must be first lest the QBrushDataPointerDeleter turns into a no-op
593 x->style = newStyle;
594 x->color = d->color;
595 x->transform = d->transform;
596 d.swap(u&: x);
597}
598
599
600/*!
601 \fn QBrush &QBrush::operator=(const QBrush &brush)
602
603 Assigns the given \a brush to \e this brush and returns a
604 reference to \e this brush.
605*/
606
607QBrush &QBrush::operator=(const QBrush &b)
608{
609 if (d == b.d)
610 return *this;
611
612 b.d->ref.ref();
613 d.reset(p: b.d.get());
614 return *this;
615}
616
617/*!
618 \fn QBrush &QBrush::operator=(QBrush &&other)
619
620 Move-assigns \a other to this QBrush instance.
621
622 \since 5.2
623*/
624
625/*!
626 \fn void QBrush::swap(QBrush &other)
627 \since 4.8
628 \memberswap{brush}
629*/
630
631/*!
632 Returns the brush as a QVariant
633*/
634QBrush::operator QVariant() const
635{
636 return QVariant::fromValue(value: *this);
637}
638
639/*!
640 \fn Qt::BrushStyle QBrush::style() const
641
642 Returns the brush style.
643
644 \sa setStyle()
645*/
646
647/*!
648 Sets the brush style to \a style.
649
650 \sa style()
651*/
652
653void QBrush::setStyle(Qt::BrushStyle style)
654{
655 if (d->style == style)
656 return;
657
658 if (qbrush_check_type(style)) {
659 detach(newStyle: style);
660 d->style = style;
661 }
662}
663
664
665/*!
666 \fn const QColor &QBrush::color() const
667
668 Returns the brush color.
669
670 \sa setColor()
671*/
672
673/*!
674 \fn void QBrush::setColor(const QColor &color)
675
676 Sets the brush color to the given \a color.
677
678 Note that calling setColor() will not make a difference if the
679 style is a gradient. The same is the case if the style is
680 Qt::TexturePattern style unless the current texture is a QBitmap.
681
682 \sa color()
683*/
684
685void QBrush::setColor(const QColor &c)
686{
687 if (d->color == c)
688 return;
689
690 detach(newStyle: d->style);
691 d->color = c;
692}
693
694/*!
695 \fn void QBrush::setColor(Qt::GlobalColor color)
696 \overload
697
698 Sets the brush color to the given \a color.
699*/
700
701/*!
702 \fn QPixmap QBrush::texture() const
703
704 Returns the custom brush pattern, or a null pixmap if no custom brush pattern
705 has been set.
706
707 \sa setTexture()
708*/
709QPixmap QBrush::texture() const
710{
711 return d->style == Qt::TexturePattern
712 ? (static_cast<QTexturedBrushData *>(d.get()))->pixmap()
713 : QPixmap();
714}
715
716/*!
717 Sets the brush pixmap to \a pixmap. The style is set to
718 Qt::TexturePattern.
719
720 The current brush color will only have an effect for monochrome
721 pixmaps, i.e. for QPixmap::depth() == 1 (\l {QBitmap}{QBitmaps}).
722
723 \sa texture()
724*/
725
726void QBrush::setTexture(const QPixmap &pixmap)
727{
728 if (!pixmap.isNull()) {
729 detach(newStyle: Qt::TexturePattern);
730 QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d.get());
731 data->setPixmap(pixmap);
732 } else {
733 detach(newStyle: Qt::NoBrush);
734 }
735}
736
737
738/*!
739 \since 4.2
740
741 Returns the custom brush pattern, or a null image if no custom
742 brush pattern has been set.
743
744 If the texture was set as a QPixmap it will be converted to a
745 QImage.
746
747 \sa setTextureImage()
748*/
749
750QImage QBrush::textureImage() const
751{
752 return d->style == Qt::TexturePattern
753 ? (static_cast<QTexturedBrushData *>(d.get()))->image()
754 : QImage();
755}
756
757
758/*!
759 \since 4.2
760
761 Sets the brush image to \a image. The style is set to
762 Qt::TexturePattern.
763
764 Note the current brush color will \e not have any affect on
765 monochrome images, as opposed to calling setTexture() with a
766 QBitmap. If you want to change the color of monochrome image
767 brushes, either convert the image to QBitmap with \c
768 QBitmap::fromImage() and set the resulting QBitmap as a texture,
769 or change the entries in the color table for the image.
770
771 \sa textureImage(), setTexture()
772*/
773
774void QBrush::setTextureImage(const QImage &image)
775{
776 if (!image.isNull()) {
777 detach(newStyle: Qt::TexturePattern);
778 QTexturedBrushData *data = static_cast<QTexturedBrushData *>(d.get());
779 data->setImage(image);
780 } else {
781 detach(newStyle: Qt::NoBrush);
782 }
783}
784
785
786/*!
787 Returns the gradient describing this brush.
788*/
789const QGradient *QBrush::gradient() const
790{
791 if (d->style == Qt::LinearGradientPattern
792 || d->style == Qt::RadialGradientPattern
793 || d->style == Qt::ConicalGradientPattern) {
794 return &static_cast<const QGradientBrushData *>(d.get())->gradient;
795 }
796 return nullptr;
797}
798
799Q_GUI_EXPORT bool qt_isExtendedRadialGradient(const QBrush &brush)
800{
801 if (brush.style() == Qt::RadialGradientPattern) {
802 const QGradient *g = brush.gradient();
803 const QRadialGradient *rg = static_cast<const QRadialGradient *>(g);
804
805 if (!qFuzzyIsNull(d: rg->focalRadius()))
806 return true;
807
808 QPointF delta = rg->focalPoint() - rg->center();
809 if (delta.x() * delta.x() + delta.y() * delta.y() > rg->radius() * rg->radius())
810 return true;
811 }
812
813 return false;
814}
815
816/*!
817 Returns \c true if the brush is fully opaque otherwise false. A brush
818 is considered opaque if:
819
820 \list
821 \li The alpha component of the color() is 255.
822 \li Its texture() does not have an alpha channel and is not a QBitmap.
823 \li The colors in the gradient() all have an alpha component that is 255.
824 \li It is an extended radial gradient.
825 \endlist
826*/
827
828bool QBrush::isOpaque() const
829{
830 bool opaqueColor = d->color.alphaF() >= 1.0f;
831
832 // Test awfully simple case first
833 if (d->style == Qt::SolidPattern)
834 return opaqueColor;
835
836 if (qt_isExtendedRadialGradient(brush: *this))
837 return false;
838
839 if (d->style == Qt::LinearGradientPattern
840 || d->style == Qt::RadialGradientPattern
841 || d->style == Qt::ConicalGradientPattern) {
842 QGradientStops stops = gradient()->stops();
843 for (int i=0; i<stops.size(); ++i)
844 if (stops.at(i).second.alphaF() < 1.0f)
845 return false;
846 return true;
847 } else if (d->style == Qt::TexturePattern) {
848 return qHasPixmapTexture(brush: *this)
849 ? !texture().hasAlphaChannel() && !texture().isQBitmap()
850 : !textureImage().hasAlphaChannel();
851 }
852
853 return false;
854}
855
856/*!
857 \since 4.3
858
859 Sets \a matrix as an explicit transformation matrix on the
860 current brush. The brush transformation matrix is merged with
861 QPainter transformation matrix to produce the final result.
862
863 \sa transform()
864*/
865void QBrush::setTransform(const QTransform &matrix)
866{
867 detach(newStyle: d->style);
868 d->transform = matrix;
869}
870
871
872/*!
873 \fn bool QBrush::operator!=(const QBrush &brush) const
874
875 Returns \c true if the brush is different from the given \a brush;
876 otherwise returns \c false.
877
878 Two brushes are different if they have different styles, colors or
879 transforms or different pixmaps or gradients depending on the style.
880
881 \sa operator==()
882*/
883
884/*!
885 \fn bool QBrush::operator==(const QBrush &brush) const
886
887 Returns \c true if the brush is equal to the given \a brush;
888 otherwise returns \c false.
889
890 Two brushes are equal if they have equal styles, colors and
891 transforms and equal pixmaps or gradients depending on the style.
892
893 \sa operator!=()
894*/
895
896bool QBrush::operator==(const QBrush &b) const
897{
898 if (b.d == d)
899 return true;
900 if (b.d->style != d->style || b.d->color != d->color || b.d->transform != d->transform)
901 return false;
902 switch (d->style) {
903 case Qt::TexturePattern:
904 {
905 // Note this produces false negatives if the textures have identical data,
906 // but does not share the same data in memory. Since equality is likely to
907 // be used to avoid iterating over the data for a texture update, this should
908 // still be better than doing an accurate comparison.
909 const QPixmap *us = nullptr, *them = nullptr;
910 qint64 cacheKey1, cacheKey2;
911 if (qHasPixmapTexture(brush: *this)) {
912 us = (static_cast<QTexturedBrushData *>(d.get()))->m_pixmap;
913 cacheKey1 = us->cacheKey();
914 } else
915 cacheKey1 = (static_cast<QTexturedBrushData *>(d.get()))->image().cacheKey();
916
917 if (qHasPixmapTexture(brush: b)) {
918 them = (static_cast<QTexturedBrushData *>(b.d.get()))->m_pixmap;
919 cacheKey2 = them->cacheKey();
920 } else
921 cacheKey2 = (static_cast<QTexturedBrushData *>(b.d.get()))->image().cacheKey();
922
923 if (cacheKey1 != cacheKey2)
924 return false;
925 if (!us == !them) // both images or both pixmaps
926 return true;
927 // Only raster QPixmaps use the same cachekeys as QImages.
928 if (us && us->handle()->classId() == QPlatformPixmap::RasterClass)
929 return true;
930 if (them && them->handle()->classId() == QPlatformPixmap::RasterClass)
931 return true;
932 return false;
933 }
934 case Qt::LinearGradientPattern:
935 case Qt::RadialGradientPattern:
936 case Qt::ConicalGradientPattern:
937 {
938 const QGradientBrushData *d1 = static_cast<QGradientBrushData *>(d.get());
939 const QGradientBrushData *d2 = static_cast<QGradientBrushData *>(b.d.get());
940 return d1->gradient == d2->gradient;
941 }
942 default:
943 return true;
944 }
945}
946
947#ifndef QT_NO_DEBUG_STREAM
948/*!
949 \internal
950*/
951QDebug operator<<(QDebug dbg, const QBrush &b)
952{
953 static constexpr auto BRUSH_STYLES = qOffsetStringArray(
954 strings: "NoBrush",
955 strings: "SolidPattern",
956 strings: "Dense1Pattern",
957 strings: "Dense2Pattern",
958 strings: "Dense3Pattern",
959 strings: "Dense4Pattern",
960 strings: "Dense5Pattern",
961 strings: "Dense6Pattern",
962 strings: "Dense7Pattern",
963 strings: "HorPattern",
964 strings: "VerPattern",
965 strings: "CrossPattern",
966 strings: "BDiagPattern",
967 strings: "FDiagPattern",
968 strings: "DiagCrossPattern",
969 strings: "LinearGradientPattern",
970 strings: "RadialGradientPattern",
971 strings: "ConicalGradientPattern",
972 strings: "", strings: "", strings: "", strings: "", strings: "", strings: "",
973 strings: "TexturePattern" // 24
974 );
975
976 QDebugStateSaver saver(dbg);
977 dbg.nospace() << "QBrush(" << b.color() << ',' << BRUSH_STYLES[b.style()] << ')';
978 return dbg;
979}
980#endif
981
982/*****************************************************************************
983 QBrush stream functions
984 *****************************************************************************/
985#ifndef QT_NO_DATASTREAM
986/*!
987 \fn QDataStream &operator<<(QDataStream &stream, const QBrush &brush)
988 \relates QBrush
989
990 Writes the given \a brush to the given \a stream and returns a
991 reference to the \a stream.
992
993 \sa {Serializing Qt Data Types}
994*/
995
996QDataStream &operator<<(QDataStream &s, const QBrush &b)
997{
998 quint8 style = (quint8) b.style();
999 bool gradient_style = false;
1000
1001 if (style == Qt::LinearGradientPattern || style == Qt::RadialGradientPattern
1002 || style == Qt::ConicalGradientPattern)
1003 gradient_style = true;
1004
1005 if (s.version() < QDataStream::Qt_4_0 && gradient_style)
1006 style = Qt::NoBrush;
1007
1008 s << style << b.color();
1009 if (b.style() == Qt::TexturePattern) {
1010 if (s.version() >= QDataStream::Qt_5_5)
1011 s << b.textureImage();
1012 else
1013 s << b.texture();
1014 } else if (s.version() >= QDataStream::Qt_4_0 && gradient_style) {
1015 const QGradient *gradient = b.gradient();
1016 int type_as_int = int(gradient->type());
1017 s << type_as_int;
1018 if (s.version() >= QDataStream::Qt_4_3) {
1019 s << int(gradient->spread());
1020 QGradient::CoordinateMode co_mode = gradient->coordinateMode();
1021 if (s.version() < QDataStream::Qt_5_12 && co_mode == QGradient::ObjectMode)
1022 co_mode = QGradient::ObjectBoundingMode;
1023 s << int(co_mode);
1024 }
1025
1026 if (s.version() >= QDataStream::Qt_4_5)
1027 s << int(gradient->interpolationMode());
1028
1029 if (sizeof(qreal) == sizeof(double)) {
1030 s << gradient->stops();
1031 } else {
1032 // ensure that we write doubles here instead of streaming the stops
1033 // directly; otherwise, platforms that redefine qreal might generate
1034 // data that cannot be read on other platforms.
1035 QList<QGradientStop> stops = gradient->stops();
1036 s << quint32(stops.size());
1037 for (int i = 0; i < stops.size(); ++i) {
1038 const QGradientStop &stop = stops.at(i);
1039 s << QPair<double, QColor>(double(stop.first), stop.second);
1040 }
1041 }
1042
1043 if (gradient->type() == QGradient::LinearGradient) {
1044 s << static_cast<const QLinearGradient *>(gradient)->start();
1045 s << static_cast<const QLinearGradient *>(gradient)->finalStop();
1046 } else if (gradient->type() == QGradient::RadialGradient) {
1047 s << static_cast<const QRadialGradient *>(gradient)->center();
1048 s << static_cast<const QRadialGradient *>(gradient)->focalPoint();
1049 s << (double) static_cast<const QRadialGradient *>(gradient)->radius();
1050 if (s.version() >= QDataStream::Qt_6_0)
1051 s << (double) static_cast<const QRadialGradient *>(gradient)->focalRadius();
1052 } else { // type == Conical
1053 s << static_cast<const QConicalGradient *>(gradient)->center();
1054 s << (double) static_cast<const QConicalGradient *>(gradient)->angle();
1055 }
1056 }
1057 if (s.version() >= QDataStream::Qt_4_3)
1058 s << b.transform();
1059 return s;
1060}
1061
1062/*!
1063 \fn QDataStream &operator>>(QDataStream &stream, QBrush &brush)
1064 \relates QBrush
1065
1066 Reads the given \a brush from the given \a stream and returns a
1067 reference to the \a stream.
1068
1069 \sa {Serializing Qt Data Types}
1070*/
1071
1072QDataStream &operator>>(QDataStream &s, QBrush &b)
1073{
1074 quint8 style;
1075 QColor color;
1076 s >> style;
1077 s >> color;
1078 b = QBrush(color);
1079 if (style == Qt::TexturePattern) {
1080 if (s.version() >= QDataStream::Qt_5_5) {
1081 QImage img;
1082 s >> img;
1083 b.setTextureImage(std::move(img));
1084 } else {
1085 QPixmap pm;
1086 s >> pm;
1087 b.setTexture(std::move(pm));
1088 }
1089 } else if (style == Qt::LinearGradientPattern
1090 || style == Qt::RadialGradientPattern
1091 || style == Qt::ConicalGradientPattern) {
1092
1093 int type_as_int;
1094 QGradient::Type type;
1095 QGradientStops stops;
1096 QGradient::CoordinateMode cmode = QGradient::LogicalMode;
1097 QGradient::Spread spread = QGradient::PadSpread;
1098 QGradient::InterpolationMode imode = QGradient::ColorInterpolation;
1099
1100 s >> type_as_int;
1101 type = QGradient::Type(type_as_int);
1102 if (s.version() >= QDataStream::Qt_4_3) {
1103 s >> type_as_int;
1104 spread = QGradient::Spread(type_as_int);
1105 s >> type_as_int;
1106 cmode = QGradient::CoordinateMode(type_as_int);
1107 }
1108
1109 if (s.version() >= QDataStream::Qt_4_5) {
1110 s >> type_as_int;
1111 imode = QGradient::InterpolationMode(type_as_int);
1112 }
1113
1114 if (sizeof(qreal) == sizeof(double)) {
1115 s >> stops;
1116 } else {
1117 quint32 numStops;
1118 double n;
1119 QColor c;
1120
1121 s >> numStops;
1122 stops.reserve(asize: numStops);
1123 for (quint32 i = 0; i < numStops; ++i) {
1124 s >> n >> c;
1125 stops << QPair<qreal, QColor>(n, c);
1126 }
1127 }
1128
1129 if (type == QGradient::LinearGradient) {
1130 QPointF p1, p2;
1131 s >> p1;
1132 s >> p2;
1133 QLinearGradient lg(p1, p2);
1134 lg.setStops(stops);
1135 lg.setSpread(spread);
1136 lg.setCoordinateMode(cmode);
1137 lg.setInterpolationMode(imode);
1138 b = QBrush(lg);
1139 } else if (type == QGradient::RadialGradient) {
1140 QPointF center, focal;
1141 double radius;
1142 double focalRadius = 0;
1143 s >> center;
1144 s >> focal;
1145 s >> radius;
1146 QRadialGradient rg(center, radius, focal);
1147 rg.setStops(stops);
1148 rg.setSpread(spread);
1149 rg.setCoordinateMode(cmode);
1150 rg.setInterpolationMode(imode);
1151 if (s.version() >= QDataStream::Qt_6_0)
1152 s >> focalRadius;
1153 rg.setFocalRadius(focalRadius);
1154 b = QBrush(rg);
1155 } else { // type == QGradient::ConicalGradient
1156 QPointF center;
1157 double angle;
1158 s >> center;
1159 s >> angle;
1160 QConicalGradient cg(center, angle);
1161 cg.setStops(stops);
1162 cg.setSpread(spread);
1163 cg.setCoordinateMode(cmode);
1164 cg.setInterpolationMode(imode);
1165 b = QBrush(cg);
1166 }
1167 } else {
1168 b = QBrush(color, (Qt::BrushStyle)style);
1169 }
1170 if (s.version() >= QDataStream::Qt_4_3) {
1171 QTransform transform;
1172 s >> transform;
1173 b.setTransform(transform);
1174 }
1175 return s;
1176}
1177#endif // QT_NO_DATASTREAM
1178
1179/*******************************************************************************
1180 * QGradient implementations
1181 */
1182
1183
1184/*!
1185 \class QGradient
1186 \ingroup painting
1187 \ingroup shared
1188 \inmodule QtGui
1189
1190 \brief The QGradient class is used in combination with QBrush to
1191 specify gradient fills.
1192
1193 Qt currently supports three types of gradient fills:
1194
1195 \list
1196 \li \e Linear gradients interpolate colors between start and end points.
1197 \li \e Simple radial gradients interpolate colors between a focal point
1198 and end points on a circle surrounding it.
1199 \li \e Extended radial gradients interpolate colors between a center and
1200 a focal circle.
1201 \li \e Conical gradients interpolate colors around a center point.
1202 \endlist
1203
1204 A gradient's type can be retrieved using the type() function.
1205 Each of the types is represented by a subclass of QGradient:
1206
1207 \table
1208 \header
1209 \li QLinearGradient
1210 \li QRadialGradient
1211 \li QConicalGradient
1212 \row
1213 \li \inlineimage qgradient-linear.png
1214 \li \inlineimage qgradient-radial.png
1215 \li \inlineimage qgradient-conical.png
1216 \endtable
1217
1218 The colors in a gradient are defined using stop points of the
1219 QGradientStop type; i.e., a position and a color. Use the setColorAt()
1220 function to define a single stop point. Alternatively, use the
1221 setStops() function to define several stop points in one go. Note that
1222 the latter function \e replaces the current set of stop points.
1223
1224 It is the gradient's complete set of stop points (accessible
1225 through the stops() function) that describes how the gradient area
1226 should be filled. If no stop points have been specified, a gradient
1227 of black at 0 to white at 1 is used.
1228
1229 A diagonal linear gradient from black at (100, 100) to white at
1230 (200, 200) could be specified like this:
1231
1232 \snippet brush/brush.cpp 0
1233
1234 A gradient can have an arbitrary number of stop points. The
1235 following would create a radial gradient starting with
1236 red in the center, blue and then green on the edges:
1237
1238 \snippet brush/brush.cpp 1
1239
1240 It is possible to repeat or reflect the gradient outside its area
1241 by specifying the \l {QGradient::Spread}{spread method} using the
1242 setSpread() function. The default is to pad the outside area with
1243 the color at the closest stop point. The currently set \l
1244 {QGradient::Spread}{spread method} can be retrieved using the
1245 spread() function. The QGradient::Spread enum defines three
1246 different methods:
1247
1248 \table
1249 \row
1250 \li \inlineimage qradialgradient-pad.png
1251 \li \inlineimage qradialgradient-repeat.png
1252 \li \inlineimage qradialgradient-reflect.png
1253 \row
1254 \li \l {QGradient::PadSpread}{PadSpread}
1255 \li \l {QGradient::RepeatSpread}{RepeatSpread}
1256 \li \l {QGradient::ReflectSpread}{ReflectSpread}
1257 \endtable
1258
1259 Note that the setSpread() function only has effect for linear and
1260 radial gradients. The reason is that the conical gradient is
1261 closed by definition, i.e. the \e conical gradient fills the
1262 entire circle from 0 - 360 degrees, while the boundary of a radial
1263 or a linear gradient can be specified through its radius or final
1264 stop points, respectively.
1265
1266 The gradient coordinates can be specified in logical coordinates,
1267 relative to device coordinates, or relative to object bounding box coordinates.
1268 The \l {QGradient::CoordinateMode}{coordinate mode} can be set using the
1269 setCoordinateMode() function. The default is LogicalMode, where the
1270 gradient coordinates are specified in the same way as the object
1271 coordinates. To retrieve the currently set \l {QGradient::CoordinateMode}
1272 {coordinate mode} use coordinateMode().
1273
1274
1275 \sa {painting/gradients}{The Gradients Example}, QBrush
1276*/
1277
1278/*!
1279 \internal
1280*/
1281QGradient::QGradient()
1282 : m_type(NoGradient)
1283{
1284}
1285
1286/*!
1287 \enum QGradient::Preset
1288 \since 5.12
1289
1290 This enum specifies a set of predefined presets for QGradient,
1291 based on the gradients from \l {https://webgradients.com/}.
1292
1293 \value WarmFlame
1294 \value NightFade
1295 \value SpringWarmth
1296 \value JuicyPeach
1297 \value YoungPassion
1298 \value LadyLips
1299 \value SunnyMorning
1300 \value RainyAshville
1301 \value FrozenDreams
1302 \value WinterNeva
1303 \value DustyGrass
1304 \value TemptingAzure
1305 \value HeavyRain
1306 \value AmyCrisp
1307 \value MeanFruit
1308 \value DeepBlue
1309 \value RipeMalinka
1310 \value CloudyKnoxville
1311 \value MalibuBeach
1312 \value NewLife
1313 \value TrueSunset
1314 \value MorpheusDen
1315 \value RareWind
1316 \value NearMoon
1317 \value WildApple
1318 \value SaintPetersburg
1319 \value PlumPlate
1320 \value EverlastingSky
1321 \value HappyFisher
1322 \value Blessing
1323 \value SharpeyeEagle
1324 \value LadogaBottom
1325 \value LemonGate
1326 \value ItmeoBranding
1327 \value ZeusMiracle
1328 \value OldHat
1329 \value StarWine
1330 \value HappyAcid
1331 \value AwesomePine
1332 \value NewYork
1333 \value ShyRainbow
1334 \value MixedHopes
1335 \value FlyHigh
1336 \value StrongBliss
1337 \value FreshMilk
1338 \value SnowAgain
1339 \value FebruaryInk
1340 \value KindSteel
1341 \value SoftGrass
1342 \value GrownEarly
1343 \value SharpBlues
1344 \value ShadyWater
1345 \value DirtyBeauty
1346 \value GreatWhale
1347 \value TeenNotebook
1348 \value PoliteRumors
1349 \value SweetPeriod
1350 \value WideMatrix
1351 \value SoftCherish
1352 \value RedSalvation
1353 \value BurningSpring
1354 \value NightParty
1355 \value SkyGlider
1356 \value HeavenPeach
1357 \value PurpleDivision
1358 \value AquaSplash
1359 \value SpikyNaga
1360 \value LoveKiss
1361 \value CleanMirror
1362 \value PremiumDark
1363 \value ColdEvening
1364 \value CochitiLake
1365 \value SummerGames
1366 \value PassionateBed
1367 \value MountainRock
1368 \value DesertHump
1369 \value JungleDay
1370 \value PhoenixStart
1371 \value OctoberSilence
1372 \value FarawayRiver
1373 \value AlchemistLab
1374 \value OverSun
1375 \value PremiumWhite
1376 \value MarsParty
1377 \value EternalConstance
1378 \value JapanBlush
1379 \value SmilingRain
1380 \value CloudyApple
1381 \value BigMango
1382 \value HealthyWater
1383 \value AmourAmour
1384 \value RiskyConcrete
1385 \value StrongStick
1386 \value ViciousStance
1387 \value PaloAlto
1388 \value HappyMemories
1389 \value MidnightBloom
1390 \value Crystalline
1391 \value PartyBliss
1392 \value ConfidentCloud
1393 \value LeCocktail
1394 \value RiverCity
1395 \value FrozenBerry
1396 \value ChildCare
1397 \value FlyingLemon
1398 \value NewRetrowave
1399 \value HiddenJaguar
1400 \value AboveTheSky
1401 \value Nega
1402 \value DenseWater
1403 \value Seashore
1404 \value MarbleWall
1405 \value CheerfulCaramel
1406 \value NightSky
1407 \value MagicLake
1408 \value YoungGrass
1409 \value ColorfulPeach
1410 \value GentleCare
1411 \value PlumBath
1412 \value HappyUnicorn
1413 \value AfricanField
1414 \value SolidStone
1415 \value OrangeJuice
1416 \value GlassWater
1417 \value NorthMiracle
1418 \value FruitBlend
1419 \value MillenniumPine
1420 \value HighFlight
1421 \value MoleHall
1422 \value SpaceShift
1423 \value ForestInei
1424 \value RoyalGarden
1425 \value RichMetal
1426 \value JuicyCake
1427 \value SmartIndigo
1428 \value SandStrike
1429 \value NorseBeauty
1430 \value AquaGuidance
1431 \value SunVeggie
1432 \value SeaLord
1433 \value BlackSea
1434 \value GrassShampoo
1435 \value LandingAircraft
1436 \value WitchDance
1437 \value SleeplessNight
1438 \value AngelCare
1439 \value CrystalRiver
1440 \value SoftLipstick
1441 \value SaltMountain
1442 \value PerfectWhite
1443 \value FreshOasis
1444 \value StrictNovember
1445 \value MorningSalad
1446 \value DeepRelief
1447 \value SeaStrike
1448 \value NightCall
1449 \value SupremeSky
1450 \value LightBlue
1451 \value MindCrawl
1452 \value LilyMeadow
1453 \value SugarLollipop
1454 \value SweetDessert
1455 \value MagicRay
1456 \value TeenParty
1457 \value FrozenHeat
1458 \value GagarinView
1459 \value FabledSunset
1460 \value PerfectBlue
1461*/
1462
1463#include "webgradients.cpp"
1464
1465/*!
1466 \fn QGradient::QGradient(QGradient::Preset preset)
1467 \since 5.12
1468
1469 Constructs a gradient based on a predefined \a preset.
1470
1471 The coordinate mode of the resulting gradient is
1472 QGradient::ObjectMode, allowing the preset
1473 to be applied to arbitrary object sizes.
1474*/
1475QGradient::QGradient(Preset preset)
1476 : m_type(LinearGradient)
1477 , m_stops(qt_preset_gradient_stops(preset))
1478 , m_data(qt_preset_gradient_data[preset - 1])
1479 , m_coordinateMode(ObjectMode)
1480{
1481}
1482
1483/*!
1484 \internal
1485*/
1486QGradient::~QGradient()
1487{
1488}
1489
1490/*!
1491 \enum QGradient::Type
1492
1493 Specifies the type of gradient.
1494
1495 \value LinearGradient Interpolates colors between start and end points
1496 (QLinearGradient).
1497
1498 \value RadialGradient Interpolate colors between a focal point and end
1499 points on a circle surrounding it (QRadialGradient).
1500
1501 \value ConicalGradient Interpolate colors around a center point (QConicalGradient).
1502 \value NoGradient No gradient is used.
1503
1504 \sa type()
1505*/
1506
1507/*!
1508 \enum QGradient::Spread
1509
1510 Specifies how the area outside the gradient area should be
1511 filled.
1512
1513 \value PadSpread The area is filled with the closest stop
1514 color. This is the default.
1515
1516 \value RepeatSpread The gradient is repeated outside the gradient
1517 area.
1518
1519 \value ReflectSpread The gradient is reflected outside the
1520 gradient area.
1521
1522 \sa spread(), setSpread()
1523*/
1524
1525/*!
1526 \fn void QGradient::setSpread(Spread method)
1527
1528 Specifies the spread \a method that should be used for this
1529 gradient.
1530
1531 Note that this function only has effect for linear and radial
1532 gradients.
1533
1534 \sa spread()
1535*/
1536
1537/*!
1538 \fn QGradient::Spread QGradient::spread() const
1539
1540 Returns the spread method use by this gradient. The default is
1541 PadSpread.
1542
1543 \sa setSpread()
1544*/
1545
1546/*!
1547 \fn QGradient::Type QGradient::type() const
1548
1549 Returns the type of gradient.
1550*/
1551
1552/*!
1553 \fn void QGradient::setColorAt(qreal position, const QColor &color)
1554
1555 Creates a stop point at the given \a position with the given \a
1556 color. The given \a position must be in the range 0 to 1.
1557
1558 \sa setStops(), stops()
1559*/
1560
1561void QGradient::setColorAt(qreal pos, const QColor &color)
1562{
1563 if ((pos > 1 || pos < 0) && !qIsNaN(d: pos)) {
1564 qWarning(msg: "QGradient::setColorAt: Color position must be specified in the range 0 to 1");
1565 return;
1566 }
1567
1568 int index = 0;
1569 if (!qIsNaN(d: pos))
1570 while (index < m_stops.size() && m_stops.at(i: index).first < pos) ++index;
1571
1572 if (index < m_stops.size() && m_stops.at(i: index).first == pos)
1573 m_stops[index].second = color;
1574 else
1575 m_stops.insert(i: index, t: QGradientStop(pos, color));
1576}
1577
1578static inline bool ok(QGradientStop stop)
1579{
1580 return stop.first >= 0 && stop.first <= 1; // rejects NaNs
1581}
1582
1583static inline bool ok(const QGradientStops &stops)
1584{
1585 qreal lastPos = -1;
1586 for (const QGradientStop &stop : stops) {
1587 if (Q_UNLIKELY(!ok(stop)))
1588 return false;
1589 const bool sorted = stop.first > lastPos; // rejects duplicates
1590 if (Q_UNLIKELY(!sorted))
1591 return false;
1592 lastPos = stop.first;
1593 }
1594 return true;
1595}
1596
1597/*!
1598 \fn void QGradient::setStops(const QGradientStops &stopPoints)
1599
1600 Replaces the current set of stop points with the given \a
1601 stopPoints. The positions of the points must be in the range 0 to
1602 1, and must be sorted with the lowest point first.
1603
1604 \sa setColorAt(), stops()
1605*/
1606void QGradient::setStops(const QGradientStops &stops)
1607{
1608 if (Q_LIKELY(ok(stops))) {
1609 // fast path for the common case: if everything is ok with the stops, just copy them
1610 m_stops = stops;
1611 return;
1612 }
1613 // otherwise, to keep the pre-5.9 behavior, add them one after another,
1614 // so each stop is checked, invalid ones are skipped, they are added in-order (which may be O(N^2)).
1615 m_stops.clear();
1616 for (int i=0; i<stops.size(); ++i)
1617 setColorAt(pos: stops.at(i).first, color: stops.at(i).second);
1618}
1619
1620
1621/*!
1622 Returns the stop points for this gradient.
1623
1624 If no stop points have been specified, a gradient of black at 0 to white
1625 at 1 is used.
1626
1627 \sa setStops(), setColorAt()
1628*/
1629QGradientStops QGradient::stops() const
1630{
1631 if (m_stops.isEmpty()) {
1632 static constexpr QGradientStop blackAndWhite[] = {
1633 {0, QColorConstants::Black}, {1, QColorConstants::White},
1634 };
1635 return QGradientStops::fromReadOnlyData(t: blackAndWhite);
1636 }
1637 return m_stops;
1638}
1639
1640/*!
1641 \enum QGradient::CoordinateMode
1642 \since 4.4
1643
1644 This enum specifies how gradient coordinates map to the paint
1645 device on which the gradient is used.
1646
1647 \value LogicalMode This is the default mode. The gradient coordinates
1648 are specified logical space just like the object coordinates.
1649 \value ObjectMode In this mode the gradient coordinates are
1650 relative to the bounding rectangle of the object being drawn, with
1651 (0,0) in the top left corner, and (1,1) in the bottom right corner
1652 of the object's bounding rectangle. This value was added in Qt
1653 5.12.
1654 \value StretchToDeviceMode In this mode the gradient coordinates
1655 are relative to the bounding rectangle of the paint device,
1656 with (0,0) in the top left corner, and (1,1) in the bottom right
1657 corner of the paint device.
1658 \value ObjectBoundingMode This mode is the same as ObjectMode, except that
1659 the {QBrush::transform()} {brush transform}, if any, is applied relative to
1660 the logical space instead of the object space. This enum value is
1661 deprecated and should not be used in new code.
1662*/
1663
1664/*!
1665 \since 4.4
1666
1667 Returns the coordinate mode of this gradient. The default mode is
1668 LogicalMode.
1669*/
1670QGradient::CoordinateMode QGradient::coordinateMode() const
1671{
1672 return m_coordinateMode;
1673}
1674
1675/*!
1676 \since 4.4
1677
1678 Sets the coordinate mode of this gradient to \a mode. The default
1679 mode is LogicalMode.
1680*/
1681void QGradient::setCoordinateMode(CoordinateMode mode)
1682{
1683 m_coordinateMode = mode;
1684}
1685
1686/*!
1687 \enum QGradient::InterpolationMode
1688 \since 4.5
1689 \internal
1690
1691 \value ComponentInterpolation The color components and the alpha component are
1692 independently linearly interpolated.
1693 \value ColorInterpolation The colors are linearly interpolated in
1694 premultiplied color space.
1695*/
1696
1697/*!
1698 \since 4.5
1699 \internal
1700
1701 Returns the interpolation mode of this gradient. The default mode is
1702 ColorInterpolation.
1703*/
1704QGradient::InterpolationMode QGradient::interpolationMode() const
1705{
1706 return m_interpolationMode;
1707}
1708
1709/*!
1710 \since 4.5
1711 \internal
1712
1713 Sets the interpolation mode of this gradient to \a mode. The default
1714 mode is ColorInterpolation.
1715*/
1716void QGradient::setInterpolationMode(InterpolationMode mode)
1717{
1718 m_interpolationMode = mode;
1719}
1720
1721/*!
1722 \fn bool QGradient::operator!=(const QGradient &gradient) const
1723 \since 4.2
1724
1725 Returns \c true if the gradient is the same as the other \a gradient
1726 specified; otherwise returns \c false.
1727
1728 \sa operator==()
1729*/
1730
1731/*!
1732 Returns \c true if the gradient is the same as the other \a gradient
1733 specified; otherwise returns \c false.
1734
1735 \sa operator!=()
1736*/
1737bool QGradient::operator==(const QGradient &gradient) const
1738{
1739 if (gradient.m_type != m_type
1740 || gradient.m_spread != m_spread
1741 || gradient.m_coordinateMode != m_coordinateMode
1742 || gradient.m_interpolationMode != m_interpolationMode) return false;
1743
1744 if (m_type == LinearGradient) {
1745 if (m_data.linear.x1 != gradient.m_data.linear.x1
1746 || m_data.linear.y1 != gradient.m_data.linear.y1
1747 || m_data.linear.x2 != gradient.m_data.linear.x2
1748 || m_data.linear.y2 != gradient.m_data.linear.y2)
1749 return false;
1750 } else if (m_type == RadialGradient) {
1751 if (m_data.radial.cx != gradient.m_data.radial.cx
1752 || m_data.radial.cy != gradient.m_data.radial.cy
1753 || m_data.radial.fx != gradient.m_data.radial.fx
1754 || m_data.radial.fy != gradient.m_data.radial.fy
1755 || m_data.radial.cradius != gradient.m_data.radial.cradius
1756 || m_data.radial.fradius != gradient.m_data.radial.fradius)
1757 return false;
1758 } else { // m_type == ConicalGradient
1759 if (m_data.conical.cx != gradient.m_data.conical.cx
1760 || m_data.conical.cy != gradient.m_data.conical.cy
1761 || m_data.conical.angle != gradient.m_data.conical.angle)
1762 return false;
1763 }
1764
1765 return stops() == gradient.stops();
1766}
1767
1768/*!
1769 \class QLinearGradient
1770 \ingroup painting
1771 \inmodule QtGui
1772
1773 \brief The QLinearGradient class is used in combination with QBrush to
1774 specify a linear gradient brush.
1775
1776 Linear gradients interpolate colors between start and end
1777 points. Outside these points the gradient is either padded,
1778 reflected or repeated depending on the currently set \l
1779 {QGradient::Spread}{spread} method:
1780
1781 \table
1782 \row
1783 \li \inlineimage qlineargradient-pad.png
1784 \li \inlineimage qlineargradient-reflect.png
1785 \li \inlineimage qlineargradient-repeat.png
1786 \row
1787 \li \l {QGradient::PadSpread}{PadSpread} (default)
1788 \li \l {QGradient::ReflectSpread}{ReflectSpread}
1789 \li \l {QGradient::RepeatSpread}{RepeatSpread}
1790 \endtable
1791
1792 The colors in a gradient is defined using stop points of the
1793 QGradientStop type, i.e. a position and a color. Use the
1794 QGradient::setColorAt() or the QGradient::setStops() function to
1795 define the stop points. It is the gradient's complete set of stop
1796 points that describes how the gradient area should be filled. If
1797 no stop points have been specified, a gradient of black at 0 to
1798 white at 1 is used.
1799
1800 In addition to the functions inherited from QGradient, the
1801 QLinearGradient class provides the finalStop() function which
1802 returns the final stop point of the gradient, and the start()
1803 function returning the start point of the gradient.
1804
1805 \sa QRadialGradient, QConicalGradient, {painting/gradients}{The
1806 Gradients Example}
1807*/
1808
1809
1810/*!
1811 Constructs a default linear gradient with interpolation area
1812 between (0, 0) and (1, 1).
1813
1814 \sa QGradient::setColorAt(), setStart(), setFinalStop()
1815*/
1816
1817QLinearGradient::QLinearGradient()
1818{
1819 m_type = LinearGradient;
1820 m_spread = PadSpread;
1821 m_data.linear.x1 = 0;
1822 m_data.linear.y1 = 0;
1823 m_data.linear.x2 = 1;
1824 m_data.linear.y2 = 1;
1825}
1826
1827
1828/*!
1829 Constructs a linear gradient with interpolation area between the
1830 given \a start point and \a finalStop.
1831
1832 \note The expected parameter values are in pixels.
1833
1834 \sa QGradient::setColorAt(), QGradient::setStops()
1835*/
1836QLinearGradient::QLinearGradient(const QPointF &start, const QPointF &finalStop)
1837{
1838 m_type = LinearGradient;
1839 m_spread = PadSpread;
1840 m_data.linear.x1 = start.x();
1841 m_data.linear.y1 = start.y();
1842 m_data.linear.x2 = finalStop.x();
1843 m_data.linear.y2 = finalStop.y();
1844}
1845
1846/*!
1847 \fn QLinearGradient::QLinearGradient(qreal x1, qreal y1, qreal x2, qreal y2)
1848
1849 Constructs a linear gradient with interpolation area between (\a
1850 x1, \a y1) and (\a x2, \a y2).
1851
1852 \note The expected parameter values are in pixels.
1853
1854 \sa QGradient::setColorAt(), QGradient::setStops()
1855*/
1856QLinearGradient::QLinearGradient(qreal xStart, qreal yStart, qreal xFinalStop, qreal yFinalStop)
1857 : QLinearGradient(QPointF(xStart, yStart), QPointF(xFinalStop, yFinalStop))
1858{
1859}
1860
1861/*!
1862 \internal
1863*/
1864QLinearGradient::~QLinearGradient()
1865{
1866}
1867
1868/*!
1869 Returns the start point of this linear gradient in logical coordinates.
1870
1871 \sa QGradient::stops()
1872*/
1873
1874QPointF QLinearGradient::start() const
1875{
1876 Q_ASSERT(m_type == LinearGradient);
1877 return QPointF(m_data.linear.x1, m_data.linear.y1);
1878}
1879
1880/*!
1881 \fn void QLinearGradient::setStart(qreal x, qreal y)
1882 \overload
1883 \since 4.2
1884
1885 Sets the start point of this linear gradient in logical
1886 coordinates to \a x, \a y.
1887
1888 \sa start()
1889*/
1890
1891/*!
1892 \since 4.2
1893
1894 Sets the start point of this linear gradient in logical
1895 coordinates to \a start.
1896
1897 \sa start()
1898*/
1899
1900void QLinearGradient::setStart(const QPointF &start)
1901{
1902 Q_ASSERT(m_type == LinearGradient);
1903 m_data.linear.x1 = start.x();
1904 m_data.linear.y1 = start.y();
1905}
1906
1907
1908/*!
1909 \fn void QLinearGradient::setFinalStop(qreal x, qreal y)
1910 \overload
1911 \since 4.2
1912
1913 Sets the final stop point of this linear gradient in logical
1914 coordinates to \a x, \a y.
1915
1916 \sa start()
1917*/
1918
1919/*!
1920 Returns the final stop point of this linear gradient in logical coordinates.
1921
1922 \sa QGradient::stops()
1923*/
1924
1925QPointF QLinearGradient::finalStop() const
1926{
1927 Q_ASSERT(m_type == LinearGradient);
1928 return QPointF(m_data.linear.x2, m_data.linear.y2);
1929}
1930
1931
1932/*!
1933 \since 4.2
1934
1935 Sets the final stop point of this linear gradient in logical
1936 coordinates to \a stop.
1937
1938 \sa finalStop()
1939*/
1940
1941void QLinearGradient::setFinalStop(const QPointF &stop)
1942{
1943 Q_ASSERT(m_type == LinearGradient);
1944 m_data.linear.x2 = stop.x();
1945 m_data.linear.y2 = stop.y();
1946}
1947
1948
1949/*!
1950 \class QRadialGradient
1951 \ingroup painting
1952 \inmodule QtGui
1953
1954 \brief The QRadialGradient class is used in combination with QBrush to
1955 specify a radial gradient brush.
1956
1957 Qt supports both simple and extended radial gradients.
1958
1959 Simple radial gradients interpolate colors between a focal point and end
1960 points on a circle surrounding it. Extended radial gradients interpolate
1961 colors between a focal circle and a center circle. Points outside the cone
1962 defined by the two circles will be transparent. For simple radial gradients
1963 the focal point is adjusted to lie inside the center circle, whereas the
1964 focal point can have any position in an extended radial gradient.
1965
1966 Outside the end points the gradient is either padded, reflected or repeated
1967 depending on the currently set \l {QGradient::Spread}{spread} method:
1968
1969 \table
1970 \row
1971 \li \inlineimage qradialgradient-pad.png
1972 \li \inlineimage qradialgradient-reflect.png
1973 \li \inlineimage qradialgradient-repeat.png
1974 \row
1975 \li \l {QGradient::PadSpread}{PadSpread} (default)
1976 \li \l {QGradient::ReflectSpread}{ReflectSpread}
1977 \li \l {QGradient::RepeatSpread}{RepeatSpread}
1978 \endtable
1979
1980 The colors in a gradient is defined using stop points of the
1981 QGradientStop type, i.e. a position and a color. Use the
1982 QGradient::setColorAt() or the QGradient::setStops() function to
1983 define the stop points. It is the gradient's complete set of stop
1984 points that describes how the gradient area should be filled. If
1985 no stop points have been specified, a gradient of black at 0 to
1986 white at 1 is used.
1987
1988 In addition to the functions inherited from QGradient, the
1989 QRadialGradient class provides the center(), focalPoint() and
1990 radius() functions returning the gradient's center, focal point
1991 and radius respectively.
1992
1993 \sa QLinearGradient, QConicalGradient, {painting/gradients}{The
1994 Gradients Example}
1995*/
1996
1997static QPointF qt_radial_gradient_adapt_focal_point(const QPointF &center,
1998 qreal radius,
1999 const QPointF &focalPoint)
2000{
2001 // We have a one pixel buffer zone to avoid numerical instability on the
2002 // circle border
2003 //### this is hacky because technically we should adjust based on current matrix
2004 const qreal compensated_radius = radius - radius * qreal(0.001);
2005 QLineF line(center, focalPoint);
2006 if (line.length() > (compensated_radius))
2007 line.setLength(compensated_radius);
2008 return line.p2();
2009}
2010
2011/*!
2012 Constructs a simple radial gradient with the given \a center, \a
2013 radius and \a focalPoint.
2014
2015 \note If the given focal point is outside the circle defined by the
2016 \a center point and \a radius, it will be re-adjusted to lie at a point on
2017 the circle where it intersects with the line from \a center to
2018 \a focalPoint.
2019
2020 \sa QGradient::setColorAt(), QGradient::setStops()
2021*/
2022
2023QRadialGradient::QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint)
2024{
2025 m_type = RadialGradient;
2026 m_spread = PadSpread;
2027 m_data.radial.cx = center.x();
2028 m_data.radial.cy = center.y();
2029 m_data.radial.cradius = radius;
2030 m_data.radial.fradius = 0;
2031
2032 QPointF adapted_focal = qt_radial_gradient_adapt_focal_point(center, radius, focalPoint);
2033 m_data.radial.fx = adapted_focal.x();
2034 m_data.radial.fy = adapted_focal.y();
2035}
2036
2037/*!
2038 Constructs a simple radial gradient with the given \a center, \a
2039 radius and the focal point in the circle center.
2040
2041 \sa QGradient::setColorAt(), QGradient::setStops()
2042*/
2043QRadialGradient::QRadialGradient(const QPointF &center, qreal radius)
2044{
2045 m_type = RadialGradient;
2046 m_spread = PadSpread;
2047 m_data.radial.cx = center.x();
2048 m_data.radial.cy = center.y();
2049 m_data.radial.cradius = radius;
2050 m_data.radial.fradius = 0;
2051 m_data.radial.fx = center.x();
2052 m_data.radial.fy = center.y();
2053}
2054
2055
2056/*!
2057 Constructs a simple radial gradient with the given center (\a cx, \a cy),
2058 \a radius and focal point (\a fx, \a fy).
2059
2060 \note If the given focal point is outside the circle defined by the
2061 center (\a cx, \a cy) and the \a radius it will be re-adjusted to
2062 the intersection between the line from the center to the focal point
2063 and the circle.
2064
2065 \sa QGradient::setColorAt(), QGradient::setStops()
2066*/
2067
2068QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy)
2069 : QRadialGradient(QPointF(cx, cy), radius, QPointF(fx, fy))
2070{
2071}
2072
2073/*!
2074 Constructs a simple radial gradient with the center at (\a cx, \a cy) and the
2075 specified \a radius. The focal point lies at the center of the circle.
2076
2077 \sa QGradient::setColorAt(), QGradient::setStops()
2078 */
2079QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal radius)
2080 : QRadialGradient(QPointF(cx, cy), radius)
2081{
2082}
2083
2084
2085/*!
2086 Constructs a simple radial gradient with the center and focal point at
2087 (0, 0) with a radius of 1.
2088*/
2089QRadialGradient::QRadialGradient()
2090{
2091 m_type = RadialGradient;
2092 m_spread = PadSpread;
2093 m_data.radial.cx = 0;
2094 m_data.radial.cy = 0;
2095 m_data.radial.cradius = 1;
2096 m_data.radial.fradius = 0;
2097 m_data.radial.fx = 0;
2098 m_data.radial.fy = 0;
2099}
2100
2101/*!
2102 \since 4.8
2103
2104 Constructs an extended radial gradient with the given \a center, \a
2105 centerRadius, \a focalPoint, and \a focalRadius.
2106*/
2107QRadialGradient::QRadialGradient(const QPointF &center, qreal centerRadius, const QPointF &focalPoint, qreal focalRadius)
2108{
2109 m_type = RadialGradient;
2110 m_spread = PadSpread;
2111 m_data.radial.cx = center.x();
2112 m_data.radial.cy = center.y();
2113 m_data.radial.cradius = centerRadius;
2114 m_data.radial.fradius = 0;
2115
2116 m_data.radial.fx = focalPoint.x();
2117 m_data.radial.fy = focalPoint.y();
2118 setFocalRadius(focalRadius);
2119}
2120
2121/*!
2122 \since 4.8
2123
2124 Constructs an extended radial gradient with the given center
2125 (\a cx, \a cy), center radius, \a centerRadius, focal point, (\a fx, \a fy),
2126 and focal radius \a focalRadius.
2127*/
2128QRadialGradient::QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal fx, qreal fy, qreal focalRadius)
2129{
2130 m_type = RadialGradient;
2131 m_spread = PadSpread;
2132 m_data.radial.cx = cx;
2133 m_data.radial.cy = cy;
2134 m_data.radial.cradius = centerRadius;
2135 m_data.radial.fradius = 0;
2136
2137 m_data.radial.fx = fx;
2138 m_data.radial.fy = fy;
2139 setFocalRadius(focalRadius);
2140}
2141
2142/*!
2143 \internal
2144*/
2145QRadialGradient::~QRadialGradient()
2146{
2147}
2148
2149/*!
2150 Returns the center of this radial gradient in logical coordinates.
2151
2152 \sa QGradient::stops()
2153*/
2154
2155QPointF QRadialGradient::center() const
2156{
2157 Q_ASSERT(m_type == RadialGradient);
2158 return QPointF(m_data.radial.cx, m_data.radial.cy);
2159}
2160
2161/*!
2162 \fn void QRadialGradient::setCenter(qreal x, qreal y)
2163 \overload
2164 \since 4.2
2165
2166 Sets the center of this radial gradient in logical coordinates
2167 to (\a x, \a y).
2168
2169 \sa center()
2170*/
2171
2172/*!
2173 \since 4.2
2174
2175 Sets the center of this radial gradient in logical coordinates
2176 to \a center.
2177
2178 \sa center()
2179*/
2180
2181void QRadialGradient::setCenter(const QPointF &center)
2182{
2183 Q_ASSERT(m_type == RadialGradient);
2184 m_data.radial.cx = center.x();
2185 m_data.radial.cy = center.y();
2186}
2187
2188
2189/*!
2190 Returns the radius of this radial gradient in logical coordinates.
2191
2192 Equivalent to centerRadius()
2193
2194 \sa QGradient::stops()
2195*/
2196
2197qreal QRadialGradient::radius() const
2198{
2199 Q_ASSERT(m_type == RadialGradient);
2200 return m_data.radial.cradius;
2201}
2202
2203
2204/*!
2205 \since 4.2
2206
2207 Sets the radius of this radial gradient in logical coordinates
2208 to \a radius
2209
2210 Equivalent to setCenterRadius()
2211*/
2212void QRadialGradient::setRadius(qreal radius)
2213{
2214 Q_ASSERT(m_type == RadialGradient);
2215 m_data.radial.cradius = radius;
2216}
2217
2218/*!
2219 \since 4.8
2220
2221 Returns the center radius of this radial gradient in logical
2222 coordinates.
2223
2224 \sa QGradient::stops()
2225*/
2226qreal QRadialGradient::centerRadius() const
2227{
2228 Q_ASSERT(m_type == RadialGradient);
2229 return m_data.radial.cradius;
2230}
2231
2232/*!
2233 \since 4.8
2234
2235 Sets the center radius of this radial gradient in logical coordinates
2236 to \a radius
2237*/
2238void QRadialGradient::setCenterRadius(qreal radius)
2239{
2240 Q_ASSERT(m_type == RadialGradient);
2241 m_data.radial.cradius = radius;
2242}
2243
2244/*!
2245 \since 4.8
2246
2247 Returns the focal radius of this radial gradient in logical
2248 coordinates.
2249
2250 \sa QGradient::stops()
2251*/
2252qreal QRadialGradient::focalRadius() const
2253{
2254 Q_ASSERT(m_type == RadialGradient);
2255 return m_data.radial.fradius;
2256}
2257
2258/*!
2259 \since 4.8
2260
2261 Sets the focal radius of this radial gradient in logical coordinates
2262 to \a radius
2263*/
2264void QRadialGradient::setFocalRadius(qreal radius)
2265{
2266 Q_ASSERT(m_type == RadialGradient);
2267 m_data.radial.fradius = radius;
2268}
2269
2270/*!
2271 Returns the focal point of this radial gradient in logical
2272 coordinates.
2273
2274 \sa QGradient::stops()
2275*/
2276
2277QPointF QRadialGradient::focalPoint() const
2278{
2279 Q_ASSERT(m_type == RadialGradient);
2280 return QPointF(m_data.radial.fx, m_data.radial.fy);
2281}
2282
2283/*!
2284 \fn void QRadialGradient::setFocalPoint(qreal x, qreal y)
2285 \overload
2286 \since 4.2
2287
2288 Sets the focal point of this radial gradient in logical
2289 coordinates to (\a x, \a y).
2290
2291 \sa focalPoint()
2292*/
2293
2294/*!
2295 \since 4.2
2296
2297 Sets the focal point of this radial gradient in logical
2298 coordinates to \a focalPoint.
2299
2300 \sa focalPoint()
2301*/
2302
2303void QRadialGradient::setFocalPoint(const QPointF &focalPoint)
2304{
2305 Q_ASSERT(m_type == RadialGradient);
2306 m_data.radial.fx = focalPoint.x();
2307 m_data.radial.fy = focalPoint.y();
2308}
2309
2310
2311
2312/*!
2313 \class QConicalGradient
2314 \ingroup painting
2315 \inmodule QtGui
2316
2317 \brief The QConicalGradient class is used in combination with QBrush to
2318 specify a conical gradient brush.
2319
2320 Conical gradients interpolate interpolate colors counter-clockwise
2321 around a center point.
2322
2323 \image qconicalgradient.png
2324
2325 The colors in a gradient is defined using stop points of the
2326 QGradientStop type, i.e. a position and a color. Use the
2327 QGradient::setColorAt() or the QGradient::setStops() function to
2328 define the stop points. It is the gradient's complete set of stop
2329 points that describes how the gradient area should be filled. If
2330 no stop points have been specified, a gradient of black at 0 to
2331 white at 1 is used.
2332
2333 In addition to the functions inherited from QGradient, the
2334 QConicalGradient class provides the angle() and center() functions
2335 returning the start angle and center of the gradient.
2336
2337 Note that the setSpread() function has no effect for conical
2338 gradients. The reason is that the conical gradient is closed by
2339 definition, i.e. the conical gradient fills the entire circle from
2340 0 - 360 degrees, while the boundary of a radial or a linear
2341 gradient can be specified through its radius or final stop points,
2342 respectively.
2343
2344 \sa QLinearGradient, QRadialGradient, {painting/gradients}{The
2345 Gradients Example}
2346*/
2347
2348
2349/*!
2350 Constructs a conical gradient with the given \a center, starting
2351 the interpolation at the given \a angle. The \a angle must be
2352 specified in degrees between 0 and 360.
2353
2354 \sa QGradient::setColorAt(), QGradient::setStops()
2355*/
2356
2357QConicalGradient::QConicalGradient(const QPointF &center, qreal angle)
2358{
2359 m_type = ConicalGradient;
2360 m_spread = PadSpread;
2361 m_data.conical.cx = center.x();
2362 m_data.conical.cy = center.y();
2363 m_data.conical.angle = angle;
2364}
2365
2366
2367/*!
2368 Constructs a conical gradient with the given center (\a cx, \a
2369 cy), starting the interpolation at the given \a angle. The angle
2370 must be specified in degrees between 0 and 360.
2371
2372 \sa QGradient::setColorAt(), QGradient::setStops()
2373*/
2374
2375QConicalGradient::QConicalGradient(qreal cx, qreal cy, qreal angle)
2376 : QConicalGradient(QPointF(cx, cy), angle)
2377{
2378}
2379
2380/*!
2381 \internal
2382*/
2383QConicalGradient::~QConicalGradient()
2384{
2385}
2386
2387
2388/*!
2389 Constructs a conical with center at (0, 0) starting the
2390 interpolation at angle 0.
2391
2392 \sa QGradient::setColorAt(), setCenter(), setAngle()
2393*/
2394
2395QConicalGradient::QConicalGradient()
2396{
2397 m_type = ConicalGradient;
2398 m_spread = PadSpread;
2399 m_data.conical.cx = 0;
2400 m_data.conical.cy = 0;
2401 m_data.conical.angle = 0;
2402}
2403
2404
2405/*!
2406 Returns the center of the conical gradient in logical
2407 coordinates.
2408
2409 \sa stops()
2410*/
2411
2412QPointF QConicalGradient::center() const
2413{
2414 Q_ASSERT(m_type == ConicalGradient);
2415 return QPointF(m_data.conical.cx, m_data.conical.cy);
2416}
2417
2418
2419/*!
2420 \fn void QConicalGradient::setCenter(qreal x, qreal y)
2421
2422 \overload
2423
2424 Sets the center of this conical gradient in logical coordinates to
2425 (\a x, \a y).
2426
2427 \sa center()
2428*/
2429
2430/*!
2431 Sets the center of this conical gradient in logical coordinates to
2432 \a center.
2433
2434 \sa center()
2435*/
2436
2437void QConicalGradient::setCenter(const QPointF &center)
2438{
2439 Q_ASSERT(m_type == ConicalGradient);
2440 m_data.conical.cx = center.x();
2441 m_data.conical.cy = center.y();
2442}
2443
2444/*!
2445 Returns the start angle of the conical gradient in logical
2446 coordinates.
2447
2448 \sa stops()
2449*/
2450
2451qreal QConicalGradient::angle() const
2452{
2453 Q_ASSERT(m_type == ConicalGradient);
2454 return m_data.conical.angle;
2455}
2456
2457
2458/*!
2459 \since 4.2
2460
2461 Sets \a angle to be the start angle for this conical gradient in
2462 logical coordinates.
2463
2464 \sa angle()
2465*/
2466
2467void QConicalGradient::setAngle(qreal angle)
2468{
2469 Q_ASSERT(m_type == ConicalGradient);
2470 m_data.conical.angle = angle;
2471}
2472
2473/*!
2474 \typedef QGradientStop
2475 \relates QGradient
2476
2477 Typedef for QPair<\l qreal, QColor>.
2478*/
2479
2480/*!
2481 \typedef QGradientStops
2482 \relates QGradient
2483
2484 Typedef for QList<QGradientStop>.
2485*/
2486
2487/*!
2488 \typedef QBrush::DataPtr
2489 \internal
2490*/
2491
2492/*!
2493 \fn DataPtr &QBrush::data_ptr()
2494 \internal
2495*/
2496
2497
2498/*!
2499 \fn bool QBrush::isDetached() const
2500 \internal
2501*/
2502
2503/*!
2504 \fn QTransform QBrush::transform() const
2505 \since 4.3
2506
2507 Returns the current transformation matrix for the brush.
2508
2509 \sa setTransform()
2510*/
2511
2512QT_END_NAMESPACE
2513
2514#include "moc_qbrush.cpp"
2515

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtbase/src/gui/painting/qbrush.cpp