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 QtQuick 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 "qquickpainteditem.h"
41#include <private/qquickpainteditem_p.h>
42
43#include <QtQuick/private/qsgdefaultpainternode_p.h>
44#include <QtQuick/private/qsgcontext_p.h>
45#include <private/qsgadaptationlayer_p.h>
46#include <qsgtextureprovider.h>
47
48#include <qmath.h>
49
50QT_BEGIN_NAMESPACE
51
52class QQuickPaintedItemTextureProvider : public QSGTextureProvider
53{
54public:
55 QSGPainterNode *node;
56 QSGTexture *texture() const override { return node ? node->texture() : nullptr; }
57 void fireTextureChanged() { emit textureChanged(); }
58};
59
60/*!
61 \class QQuickPaintedItem
62 \brief The QQuickPaintedItem class provides a way to use the QPainter API in the
63 QML Scene Graph.
64
65 \inmodule QtQuick
66
67 The QQuickPaintedItem makes it possible to use the QPainter API with the
68 QML Scene Graph. It sets up a textured rectangle in the Scene Graph and
69 uses a QPainter to paint onto the texture. The render target can be either
70 a QImage or, when OpenGL is in use, a QOpenGLFramebufferObject. When the
71 render target is a QImage, QPainter first renders into the image then the
72 content is uploaded to the texture. When a QOpenGLFramebufferObject is
73 used, QPainter paints directly onto the texture. Call update() to trigger a
74 repaint.
75
76 To enable QPainter to do anti-aliased rendering, use setAntialiasing().
77
78 To write your own painted item, you first create a subclass of
79 QQuickPaintedItem, and then start by implementing its only pure virtual
80 public function: paint(), which implements the actual painting. The
81 painting will be inside the rectangle spanning from 0,0 to
82 width(),height().
83
84 \note It important to understand the performance implications such items
85 can incur. See QQuickPaintedItem::RenderTarget and
86 QQuickPaintedItem::renderTarget.
87*/
88
89/*!
90 \enum QQuickPaintedItem::RenderTarget
91
92 This enum describes QQuickPaintedItem's render targets. The render target is the
93 surface QPainter paints onto before the item is rendered on screen.
94
95 \value Image The default; QPainter paints into a QImage using the raster paint engine.
96 The image's content needs to be uploaded to graphics memory afterward, this operation
97 can potentially be slow if the item is large. This render target allows high quality
98 anti-aliasing and fast item resizing.
99
100 \value FramebufferObject QPainter paints into a QOpenGLFramebufferObject using the GL
101 paint engine. Painting can be faster as no texture upload is required, but anti-aliasing
102 quality is not as good as if using an image. This render target allows faster rendering
103 in some cases, but you should avoid using it if the item is resized often.
104
105 \value InvertedYFramebufferObject Exactly as for FramebufferObject above, except once
106 the painting is done, prior to rendering the painted image is flipped about the
107 x-axis so that the top-most pixels are now at the bottom. Since this is done with the
108 OpenGL texture coordinates it is a much faster way to achieve this effect than using a
109 painter transform.
110
111 \sa setRenderTarget()
112*/
113
114/*!
115 \enum QQuickPaintedItem::PerformanceHint
116
117 This enum describes flags that you can enable to improve rendering
118 performance in QQuickPaintedItem. By default, none of these flags are set.
119
120 \value FastFBOResizing Resizing an FBO can be a costly operation on a few
121 OpenGL driver implementations. To work around this, one can set this flag
122 to let the QQuickPaintedItem allocate one large framebuffer object and
123 instead draw into a subregion of it. This saves the resize at the cost of
124 using more memory. Please note that this is not a common problem.
125
126*/
127
128/*!
129 \internal
130*/
131QQuickPaintedItemPrivate::QQuickPaintedItemPrivate()
132 : QQuickItemPrivate()
133 , contentsScale(1.0)
134 , fillColor(Qt::transparent)
135 , renderTarget(QQuickPaintedItem::Image)
136 , performanceHints(nullptr)
137 , opaquePainting(false)
138 , antialiasing(false)
139 , mipmap(false)
140 , textureProvider(nullptr)
141 , node(nullptr)
142{
143}
144
145/*!
146 Constructs a QQuickPaintedItem with the given \a parent item.
147 */
148QQuickPaintedItem::QQuickPaintedItem(QQuickItem *parent)
149 : QQuickItem(*(new QQuickPaintedItemPrivate), parent)
150{
151 setFlag(ItemHasContents);
152}
153
154/*!
155 \internal
156*/
157QQuickPaintedItem::QQuickPaintedItem(QQuickPaintedItemPrivate &dd, QQuickItem *parent)
158 : QQuickItem(dd, parent)
159{
160 setFlag(ItemHasContents);
161}
162
163/*!
164 Destroys the QQuickPaintedItem.
165*/
166QQuickPaintedItem::~QQuickPaintedItem()
167{
168 Q_D(QQuickPaintedItem);
169 if (d->textureProvider)
170 QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
171}
172
173/*!
174 Schedules a redraw of the area covered by \a rect in this item. You can call this function
175 whenever your item needs to be redrawn, such as if it changes appearance or size.
176
177 This function does not cause an immediate paint; instead it schedules a paint request that
178 is processed by the QML Scene Graph when the next frame is rendered. The item will only be
179 redrawn if it is visible.
180
181 \sa paint()
182*/
183void QQuickPaintedItem::update(const QRect &rect)
184{
185 Q_D(QQuickPaintedItem);
186
187 if (rect.isNull() && !d->dirtyRect.isNull())
188 d->dirtyRect = contentsBoundingRect().toAlignedRect();
189 else
190 d->dirtyRect |= (contentsBoundingRect() & rect).toAlignedRect();
191 QQuickItem::update();
192}
193
194/*!
195 Returns true if this item is opaque; otherwise, false is returned.
196
197 By default, painted items are not opaque.
198
199 \sa setOpaquePainting()
200*/
201bool QQuickPaintedItem::opaquePainting() const
202{
203 Q_D(const QQuickPaintedItem);
204 return d->opaquePainting;
205}
206
207/*!
208 If \a opaque is true, the item is opaque; otherwise, it is considered as translucent.
209
210 Opaque items are not blended with the rest of the scene, you should set this to true
211 if the content of the item is opaque to speed up rendering.
212
213 By default, painted items are not opaque.
214
215 \sa opaquePainting()
216*/
217void QQuickPaintedItem::setOpaquePainting(bool opaque)
218{
219 Q_D(QQuickPaintedItem);
220
221 if (d->opaquePainting == opaque)
222 return;
223
224 d->opaquePainting = opaque;
225 QQuickItem::update();
226}
227
228/*!
229 Returns true if antialiased painting is enabled; otherwise, false is returned.
230
231 By default, antialiasing is not enabled.
232
233 \sa setAntialiasing()
234*/
235bool QQuickPaintedItem::antialiasing() const
236{
237 Q_D(const QQuickPaintedItem);
238 return d->antialiasing;
239}
240
241/*!
242 If \a enable is true, antialiased painting is enabled.
243
244 By default, antialiasing is not enabled.
245
246 \sa antialiasing()
247*/
248void QQuickPaintedItem::setAntialiasing(bool enable)
249{
250 Q_D(QQuickPaintedItem);
251
252 if (d->antialiasing == enable)
253 return;
254
255 d->antialiasing = enable;
256 update();
257}
258
259/*!
260 Returns true if mipmaps are enabled; otherwise, false is returned.
261
262 By default, mipmapping is not enabled.
263
264 \sa setMipmap()
265*/
266bool QQuickPaintedItem::mipmap() const
267{
268 Q_D(const QQuickPaintedItem);
269 return d->mipmap;
270}
271
272/*!
273 If \a enable is true, mipmapping is enabled on the associated texture.
274
275 Mipmapping increases rendering speed and reduces aliasing artifacts when the item is
276 scaled down.
277
278 By default, mipmapping is not enabled.
279
280 \sa mipmap()
281*/
282void QQuickPaintedItem::setMipmap(bool enable)
283{
284 Q_D(QQuickPaintedItem);
285
286 if (d->mipmap == enable)
287 return;
288
289 d->mipmap = enable;
290 update();
291}
292
293/*!
294 Returns the performance hints.
295
296 By default, no performance hint is enabled.
297
298 \sa setPerformanceHint(), setPerformanceHints()
299*/
300QQuickPaintedItem::PerformanceHints QQuickPaintedItem::performanceHints() const
301{
302 Q_D(const QQuickPaintedItem);
303 return d->performanceHints;
304}
305
306/*!
307 Sets the given performance \a hint on the item if \a enabled is true;
308 otherwise clears the performance hint.
309
310 By default, no performance hint is enabled/
311
312 \sa setPerformanceHints(), performanceHints()
313*/
314void QQuickPaintedItem::setPerformanceHint(QQuickPaintedItem::PerformanceHint hint, bool enabled)
315{
316 Q_D(QQuickPaintedItem);
317 PerformanceHints oldHints = d->performanceHints;
318 if (enabled)
319 d->performanceHints |= hint;
320 else
321 d->performanceHints &= ~hint;
322 if (oldHints != d->performanceHints)
323 update();
324}
325
326/*!
327 Sets the performance hints to \a hints
328
329 By default, no performance hint is enabled/
330
331 \sa setPerformanceHint(), performanceHints()
332*/
333void QQuickPaintedItem::setPerformanceHints(QQuickPaintedItem::PerformanceHints hints)
334{
335 Q_D(QQuickPaintedItem);
336 if (d->performanceHints == hints)
337 return;
338 d->performanceHints = hints;
339 update();
340}
341
342QSize QQuickPaintedItem::textureSize() const
343{
344 Q_D(const QQuickPaintedItem);
345 return d->textureSize;
346}
347
348/*!
349 \property QQuickPaintedItem::textureSize
350
351 \brief Defines the size of the texture.
352
353 Changing the texture's size does not affect the coordinate system used in
354 paint(). A scale factor is instead applied so painting should still happen
355 inside 0,0 to width(),height().
356
357 By default, the texture size will have the same size as this item.
358
359 \note If the item is on a window with a device pixel ratio different from
360 1, this scale factor will be implicitly applied to the texture size.
361
362 */
363void QQuickPaintedItem::setTextureSize(const QSize &size)
364{
365 Q_D(QQuickPaintedItem);
366 if (d->textureSize == size)
367 return;
368 d->textureSize = size;
369 emit textureSizeChanged();
370}
371
372#if QT_VERSION >= 0x060000
373#warning "Remove: QQuickPaintedItem::contentsBoundingRect, contentsScale, contentsSize. Also remove them from qsgadaptationlayer_p.h and qsgdefaultpainternode.h/cpp."
374#endif
375
376/*!
377 \obsolete
378
379 This function is provided for compatibility, use size in combination
380 with textureSize to decide the size of what you are drawing.
381
382 \sa width(), height(), textureSize()
383*/
384QRectF QQuickPaintedItem::contentsBoundingRect() const
385{
386 Q_D(const QQuickPaintedItem);
387
388 qreal w = d->width;
389 QSizeF sz = d->contentsSize * d->contentsScale;
390 if (w < sz.width())
391 w = sz.width();
392 qreal h = d->height;
393 if (h < sz.height())
394 h = sz.height();
395
396 return QRectF(0, 0, w, h);
397}
398
399/*!
400 \property QQuickPaintedItem::contentsSize
401 \brief Obsolete method for setting the contents size.
402 \obsolete
403
404 This function is provided for compatibility, use size in combination
405 with textureSize to decide the size of what you are drawing.
406
407 \sa width(), height(), textureSize()
408*/
409QSize QQuickPaintedItem::contentsSize() const
410{
411 Q_D(const QQuickPaintedItem);
412 return d->contentsSize;
413}
414
415void QQuickPaintedItem::setContentsSize(const QSize &size)
416{
417 Q_D(QQuickPaintedItem);
418
419 if (d->contentsSize == size)
420 return;
421
422 d->contentsSize = size;
423 update();
424
425 emit contentsSizeChanged();
426}
427
428/*!
429 \obsolete
430 This convenience function is equivalent to calling setContentsSize(QSize()).
431*/
432void QQuickPaintedItem::resetContentsSize()
433{
434 setContentsSize(QSize());
435}
436
437/*!
438 \property QQuickPaintedItem::contentsScale
439 \brief Obsolete method for scaling the contents.
440 \obsolete
441
442 This function is provided for compatibility, use size() in combination
443 with textureSize() to decide the size of what you are drawing.
444
445 \sa width(), height(), textureSize()
446*/
447qreal QQuickPaintedItem::contentsScale() const
448{
449 Q_D(const QQuickPaintedItem);
450 return d->contentsScale;
451}
452
453void QQuickPaintedItem::setContentsScale(qreal scale)
454{
455 Q_D(QQuickPaintedItem);
456
457 if (d->contentsScale == scale)
458 return;
459
460 d->contentsScale = scale;
461 update();
462
463 emit contentsScaleChanged();
464}
465
466/*!
467 \property QQuickPaintedItem::fillColor
468 \brief The item's background fill color.
469
470 By default, the fill color is set to Qt::transparent.
471*/
472QColor QQuickPaintedItem::fillColor() const
473{
474 Q_D(const QQuickPaintedItem);
475 return d->fillColor;
476}
477
478void QQuickPaintedItem::setFillColor(const QColor &c)
479{
480 Q_D(QQuickPaintedItem);
481
482 if (d->fillColor == c)
483 return;
484
485 d->fillColor = c;
486 update();
487
488 emit fillColorChanged();
489}
490
491/*!
492 \property QQuickPaintedItem::renderTarget
493 \brief The item's render target.
494
495 This property defines which render target the QPainter renders into, it can be either
496 QQuickPaintedItem::Image, QQuickPaintedItem::FramebufferObject or QQuickPaintedItem::InvertedYFramebufferObject.
497
498 Each has certain benefits, typically performance versus quality. Using a framebuffer
499 object avoids a costly upload of the image contents to the texture in graphics memory,
500 while using an image enables high quality anti-aliasing.
501
502 \warning Resizing a framebuffer object is a costly operation, avoid using
503 the QQuickPaintedItem::FramebufferObject render target if the item gets resized often.
504
505 By default, the render target is QQuickPaintedItem::Image.
506
507 \note Some Qt Quick backends may not support all render target options. For
508 example, it is likely that non-OpenGL backends will lack support for
509 QQuickPaintedItem::FramebufferObject and
510 QQuickPaintedItem::InvertedYFramebufferObject. Requesting these will then
511 be ignored.
512*/
513QQuickPaintedItem::RenderTarget QQuickPaintedItem::renderTarget() const
514{
515 Q_D(const QQuickPaintedItem);
516 return d->renderTarget;
517}
518
519void QQuickPaintedItem::setRenderTarget(RenderTarget target)
520{
521 Q_D(QQuickPaintedItem);
522
523 if (d->renderTarget == target)
524 return;
525
526 d->renderTarget = target;
527 update();
528
529 emit renderTargetChanged();
530}
531
532/*!
533 \fn virtual void QQuickPaintedItem::paint(QPainter *painter) = 0
534
535 This function, which is usually called by the QML Scene Graph, paints the
536 contents of an item in local coordinates.
537
538 The underlying texture will have a size defined by textureSize when set,
539 or the item's size, multiplied by the window's device pixel ratio.
540
541 The function is called after the item has been filled with the fillColor.
542
543 Reimplement this function in a QQuickPaintedItem subclass to provide the
544 item's painting implementation, using \a painter.
545
546 \note The QML Scene Graph uses two separate threads, the main thread does things such as
547 processing events or updating animations while a second thread does the actual OpenGL rendering.
548 As a consequence, paint() is not called from the main GUI thread but from the GL enabled
549 renderer thread. At the moment paint() is called, the GUI thread is blocked and this is
550 therefore thread-safe.
551
552 \warning Extreme caution must be used when creating QObjects, emitting signals, starting
553 timers and similar inside this function as these will have affinity to the rendering thread.
554
555 \sa width(), height(), textureSize
556*/
557
558/*!
559 \reimp
560*/
561QSGNode *QQuickPaintedItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
562{
563 Q_UNUSED(data);
564 Q_D(QQuickPaintedItem);
565
566 if (width() <= 0 || height() <= 0) {
567 delete oldNode;
568 if (d->textureProvider) {
569 d->textureProvider->node = nullptr;
570 d->textureProvider->fireTextureChanged();
571 }
572 return nullptr;
573 }
574
575 QSGPainterNode *node = static_cast<QSGPainterNode *>(oldNode);
576 if (!node) {
577 node = d->sceneGraphContext()->createPainterNode(this);
578 d->node = node;
579 }
580
581 bool hasTextureSize = d->textureSize.width() > 0 && d->textureSize.height() > 0;
582
583 // Use the compat mode if any of the compat things are set and
584 // textureSize is 0x0.
585 if (!hasTextureSize
586 && (d->contentsScale != 1
587 || (d->contentsSize.width() > 0 && d->contentsSize.height() > 0))) {
588 QRectF br = contentsBoundingRect();
589 node->setContentsScale(d->contentsScale);
590 QSize size = QSize(qRound(br.width()), qRound(br.height()));
591 node->setSize(size);
592 node->setTextureSize(size);
593 } else {
594 // The default, use textureSize or item's size * device pixel ratio
595 node->setContentsScale(1);
596 QSize itemSize(qRound(width()), qRound(height()));
597 node->setSize(itemSize);
598 QSize textureSize = (hasTextureSize ? d->textureSize : itemSize)
599 * window()->effectiveDevicePixelRatio();
600 node->setTextureSize(textureSize);
601 }
602
603 node->setPreferredRenderTarget(d->renderTarget);
604 node->setFastFBOResizing(d->performanceHints & FastFBOResizing);
605 node->setSmoothPainting(d->antialiasing);
606 node->setLinearFiltering(d->smooth);
607 node->setMipmapping(d->mipmap);
608 node->setOpaquePainting(d->opaquePainting);
609 node->setFillColor(d->fillColor);
610 node->setDirty(d->dirtyRect);
611 node->update();
612
613 d->dirtyRect = QRect();
614
615 if (d->textureProvider) {
616 d->textureProvider->node = node;
617 d->textureProvider->fireTextureChanged();
618 }
619
620 return node;
621}
622
623/*!
624 \reimp
625*/
626void QQuickPaintedItem::releaseResources()
627{
628 Q_D(QQuickPaintedItem);
629 if (d->textureProvider) {
630 QQuickWindowQObjectCleanupJob::schedule(window(), d->textureProvider);
631 d->textureProvider = nullptr;
632 }
633 d->node = nullptr; // Managed by the scene graph, just clear the pointer.
634}
635
636void QQuickPaintedItem::invalidateSceneGraph()
637{
638 Q_D(QQuickPaintedItem);
639 delete d->textureProvider;
640 d->textureProvider = nullptr;
641 d->node = nullptr; // Managed by the scene graph, just clear the pointer
642}
643
644/*!
645 \reimp
646*/
647bool QQuickPaintedItem::isTextureProvider() const
648{
649 return true;
650}
651
652/*!
653 \reimp
654*/
655QSGTextureProvider *QQuickPaintedItem::textureProvider() const
656{
657 // When Item::layer::enabled == true, QQuickItem will be a texture
658 // provider. In this case we should prefer to return the layer rather
659 // than the image itself. The layer will include any children and any
660 // the image's wrap and fill mode.
661 if (QQuickItem::isTextureProvider())
662 return QQuickItem::textureProvider();
663
664 Q_D(const QQuickPaintedItem);
665#if QT_CONFIG(opengl)
666 QQuickWindow *w = window();
667 if (!w || !w->openglContext() || QThread::currentThread() != w->openglContext()->thread()) {
668 qWarning("QQuickPaintedItem::textureProvider: can only be queried on the rendering thread of an exposed window");
669 return nullptr;
670 }
671#endif
672 if (!d->textureProvider)
673 d->textureProvider = new QQuickPaintedItemTextureProvider();
674 d->textureProvider->node = d->node;
675 return d->textureProvider;
676}
677
678
679/*!
680 \reimp
681*/
682void QQuickPaintedItem::itemChange(ItemChange change, const ItemChangeData &value)
683{
684 if (change == ItemDevicePixelRatioHasChanged)
685 update();
686 QQuickItem::itemChange(change, value);
687}
688
689QT_END_NAMESPACE
690
691#include "moc_qquickpainteditem.cpp"
692