QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskPaintedNode.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskPaintedNode.h"
7#include "QskSGNode.h"
8#include "QskTextureRenderer.h"
9#include "QskInternalMacros.h"
10
11#include <qsgimagenode.h>
12#include <qquickwindow.h>
13#include <qimage.h>
14#include <qpainter.h>
15
16QSK_QT_PRIVATE_BEGIN
17#include <private/qsgplaintexture_p.h>
18QSK_QT_PRIVATE_END
19
20static inline QSGImageNode::TextureCoordinatesTransformMode
21 qskEffectiveTransformMode( const Qt::Orientations mirrored )
22{
23 QSGImageNode::TextureCoordinatesTransformMode mode;
24
25 if ( mirrored & Qt::Vertical )
26 mode |= QSGImageNode::MirrorVertically;
27
28 if ( mirrored & Qt::Horizontal )
29 mode |= QSGImageNode::MirrorHorizontally;
30
31 return mode;
32}
33
34namespace
35{
36 const quint8 imageRole = 250; // reserved for internal use
37
38 inline QSGImageNode* findImageNode( const QSGNode* parentNode )
39 {
40 auto node = QskSGNode::findChildNode(
41 const_cast< QSGNode* >( parentNode ), imageRole );
42
43 return static_cast< QSGImageNode* >( node );
44 }
45}
46
47QskPaintedNode::QskPaintedNode()
48{
49}
50
51QskPaintedNode::~QskPaintedNode()
52{
53}
54
55void QskPaintedNode::setRenderHint( RenderHint renderHint )
56{
57 m_renderHint = renderHint;
58}
59
60QskPaintedNode::RenderHint QskPaintedNode::renderHint() const
61{
62 return m_renderHint;
63}
64
65void QskPaintedNode::setMirrored( Qt::Orientations orientations )
66{
67 if ( orientations != m_mirrored )
68 {
69 m_mirrored = orientations;
70
71 if ( auto imageNode = findImageNode( this ) )
72 {
73 imageNode->setTextureCoordinatesTransform(
74 qskEffectiveTransformMode( orientations ) );
75 }
76 }
77}
78
79Qt::Orientations QskPaintedNode::mirrored() const
80{
81 return m_mirrored;
82}
83
84QSize QskPaintedNode::textureSize() const
85{
86 if ( const auto imageNode = findImageNode( this ) )
87 {
88 if ( auto texture = imageNode->texture() )
89 return texture->textureSize();
90 }
91
92 return QSize();
93}
94
95QRectF QskPaintedNode::rect() const
96{
97 const auto imageNode = findImageNode( this );
98 return imageNode ? imageNode->rect() : QRectF();
99}
100
101void QskPaintedNode::update( QQuickWindow* window,
102 const QRectF& rect, const QSizeF& size, const void* nodeData )
103{
104 auto imageNode = findImageNode( this );
105
106 if ( rect.isEmpty() )
107 {
108 if ( imageNode )
109 {
110 removeChildNode( imageNode );
111 delete imageNode;
112 }
113
114 return;
115 }
116
117 if ( imageNode == nullptr )
118 {
119 imageNode = window->createImageNode();
120
121 imageNode->setOwnsTexture( true );
122 QskSGNode::setNodeRole( imageNode, imageRole );
123
124 appendChildNode( imageNode );
125 }
126
127 QSize imageSize;
128
129 {
130 auto scaledSize = size.isEmpty() ? rect.size() : size;
131 scaledSize *= window->effectiveDevicePixelRatio();
132
133 imageSize = scaledSize.toSize();
134 }
135
136 bool isTextureDirty = false;
137
138 const auto newHash = hash( nodeData );
139 if ( ( newHash == 0 ) || ( newHash != m_hash ) )
140 {
141 m_hash = newHash;
142 isTextureDirty = true;
143 }
144 else
145 {
146 isTextureDirty = ( imageSize != textureSize() );
147 }
148
149
150 if ( isTextureDirty )
151 updateTexture( window, imageSize, nodeData );
152
153 imageNode->setRect( rect );
154 imageNode->setTextureCoordinatesTransform(
155 qskEffectiveTransformMode( m_mirrored ) );
156}
157
158void QskPaintedNode::updateTexture( QQuickWindow* window,
159 const QSize& size, const void* nodeData )
160{
161 auto imageNode = findImageNode( this );
162
163 if ( ( m_renderHint == OpenGL ) && QskTextureRenderer::isOpenGLWindow( window ) )
164 {
165 const auto textureId = createTextureGL( window, size, nodeData );
166
167 auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() );
168 if ( texture == nullptr )
169 {
170 texture = new QSGPlainTexture;
171 texture->setHasAlphaChannel( true );
172 texture->setOwnsTexture( true );
173
174 imageNode->setTexture( texture );
175 }
176
177 QskTextureRenderer::setTextureId( window, textureId, size, texture );
178 }
179 else
180 {
181 const auto image = createImage( window, size, nodeData );
182
183 if ( auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ) )
184 texture->setImage( image );
185 else
186 imageNode->setTexture( window->createTextureFromImage( image ) );
187 }
188}
189
190QImage QskPaintedNode::createImage( QQuickWindow* window,
191 const QSize& size, const void* nodeData )
192{
193 QImage image( size, QImage::Format_RGBA8888_Premultiplied );
194 image.fill( Qt::transparent );
195
196 QPainter painter( &image );
197
198 /*
199 setting a devicePixelRatio for the image only works for
200 value >= 1.0. So we have to scale manually.
201 */
202 const auto ratio = window->effectiveDevicePixelRatio();
203 painter.scale( ratio, ratio );
204
205 paint( &painter, size / ratio, nodeData );
206
207 painter.end();
208
209 return image;
210}
211
212quint32 QskPaintedNode::createTextureGL(
213 QQuickWindow* window, const QSize& size, const void* nodeData )
214{
215 class PaintHelper : public QskTextureRenderer::PaintHelper
216 {
217 public:
218 PaintHelper( QskPaintedNode* node, const void* nodeData )
219 : m_node( node )
220 , m_nodeData( nodeData )
221 {
222 }
223
224 void paint( QPainter* painter, const QSize& size ) override
225 {
226 m_node->paint( painter, size, m_nodeData );
227 }
228
229 private:
230 QskPaintedNode* m_node;
231 const void* m_nodeData;
232 };
233
234 PaintHelper helper( this, nodeData );
235 return createPaintedTextureGL( window, size, &helper );
236}