DtkDeclarative
DTK Declarative module
载入中...
搜索中...
未找到
dquickitemviewport_p.h
1// SPDX-FileCopyrightText: 2020 - 2022 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: LGPL-3.0-or-later
4
5#ifndef DQUICKITEMVIEWPORT_P_H
6#define DQUICKITEMVIEWPORT_P_H
7
8#include "dquickitemviewport.h"
9
10#include <DObjectPrivate>
11
12#include <QSGImageNode>
13#include <private/qsgadaptationlayer_p.h>
14#include <private/qquickitem_p.h>
15#include <private/qquickitemchangelistener_p.h>
16
17class Q_DECL_HIDDEN MaskTextureCache {
18public:
19 class Texture : public QSharedData {
20 public:
21 explicit Texture(QSGTexture *t, const qint8 key)
22 : cacheKey(key)
23 , texture(t)
24 {
25 MaskTextureCache::instance()->m_cache[cacheKey] = this;
26 }
27
28 ~Texture() {
29 MaskTextureCache::instance()->m_cache.remove(cacheKey);
30 delete texture;
31 }
32
33 qint8 cacheKey = 0;
34 QSGTexture *texture = nullptr;
35 };
36
37 typedef QExplicitlySharedDataPointer<Texture> TextureData;
38
39 static MaskTextureCache *instance()
40 {
41 static MaskTextureCache *object = new MaskTextureCache();
42 return object;
43 }
44
45 // 根据圆角大小获取一个蒙版材质,此材质将用于片段着色器中实现圆角效果
46 TextureData getTexture(QSGRenderContext *context, int radius, bool antialiasing)
47 {
48 // 用于获取材质缓存key的key
49 qint8 to_cache_key_key = ((antialiasing << 7) | radius);
50 Texture *texture = nullptr;
51
52 if (m_radiusToCacheKey.contains(to_cache_key_key)) {
53 texture = m_cache.value(m_radiusToCacheKey.value(to_cache_key_key));
54 }
55
56 if (!texture) {
57 // 在边缘额外加一个像素,用于 ClampToEdge 时不会取到边缘的透明像素点
58 QImage mask(QSize(radius + 1, radius + 1), QImage::Format_ARGB32);
59 mask.fill(Qt::transparent);
60 // 必须填充为白色,在着色器中运算时会使用rgb三个通道相乘
61 const QColor maskColor = Qt::white;
62 QPainter pa(&mask);
63 pa.setPen(maskColor);
64 const QRect r = mask.rect();
65 pa.drawLine(r.bottomLeft(), r.bottomRight());
66 pa.drawLine(r.topRight(), r.bottomRight());
67 pa.setRenderHint(QPainter::Antialiasing, antialiasing);
68 QPainterPath path;
69 path.moveTo(radius, radius);
70 path.arcTo(0, 0, radius * 2, radius * 2, 90, 90);
71 path.lineTo(radius, radius);
72 path.closeSubpath();
73 pa.fillPath(path, maskColor);
74 pa.end();
75
76 texture = new Texture(context->createTexture(mask), to_cache_key_key);
77 texture->texture->setFiltering(QSGTexture::Nearest);
78 texture->texture->setVerticalWrapMode(QSGTexture::ClampToEdge);
79 texture->texture->setHorizontalWrapMode(QSGTexture::ClampToEdge);
80
81 m_radiusToCacheKey[to_cache_key_key] = texture->cacheKey;
82 }
83
84 // 为窗口保存mask材质
85 TextureData data(texture);
86
87 return data;
88 }
89
90private:
92
93 }
94
95 QHash<qint8, Texture*> m_cache;
96 QMap<int, qint8> m_radiusToCacheKey;
97
98 friend class Texture;
99};
100
101DQUICK_BEGIN_NAMESPACE
102class DQuickViewportTextureProvider : public QSGTextureProvider
103{
104 Q_OBJECT
105public:
107 : sourceTexture(nullptr)
108 {
109 }
110
111 QSGTexture *texture() const override
112 {
113 return sourceTexture;
114 }
115
116 QSGLayer *sourceTexture;
117};
118
119class Q_DECL_HIDDEN DQuickViewportCleanup : public QRunnable
120{
121public:
122 DQuickViewportCleanup(QSGLayer *texture, MaskTextureCache::TextureData maskTexture,
124 : texture(texture)
125 , maskTexture(maskTexture)
126 , provider(provider)
127 {}
128 void run() override {
129 delete texture;
130 delete provider;
131 maskTexture.reset();
132 }
133 QSGLayer *texture;
134 MaskTextureCache::TextureData maskTexture;
136};
137
138class PreprocessNode;
139class Q_DECL_HIDDEN DQuickItemViewportPrivate : public DCORE_NAMESPACE::DObjectPrivate, public QQuickItemChangeListener
140{
141public:
142 enum DirtyStateBit {
143 DirtyNothing = 0x0,
144 DirtySourceSizeRatio = 0x1,
145 DirtyMaskTexture = 0x2,
146 DirtyMaskSizeRatio = 0x4,
147 DirtyMaskOffset = 0x8,
148 DirtyContentNode = 0x10
149 };
150 Q_DECLARE_FLAGS(DirtyState, DirtyStateBit)
151
153 : DObjectPrivate(qq)
154 {
155
156 }
157
159
160 inline void markDirtys(DirtyState states) {
161 dirtyState |= states;
162 }
163 inline void markDirty(DirtyStateBit state, bool dirty = true) {
164 if (dirty) {
165 dirtyState |= state;
166 } else {
167 dirtyState &= ~state;
168 }
169 }
170
171 // 根据radius获取对应的蒙版材质
172 void initSourceItem(QQuickItem *old, QQuickItem *item);
173
174 void itemGeometryChanged(QQuickItem *item, QQuickGeometryChange data, const QRectF &) override;
175
176 inline const QVector2D &getSoureSizeRatio() {
177 if (Q_LIKELY(!dirtyState.testFlag(DirtySourceSizeRatio))) {
178 return soureSizeRatio;
179 }
180
181 Q_ASSERT(sourceItem);
182 markDirty(DirtySourceSizeRatio, false);
183 const auto &sr = getSourceRect();
184 soureSizeRatio.setX(static_cast<float>(sourceItem->width() / sr.width()));
185 soureSizeRatio.setY(static_cast<float>(sourceItem->height() / sr.height()));
186 return soureSizeRatio;
187 }
188 inline const QVector2D &getMaskSizeRatio() {
189 if (Q_LIKELY(!dirtyState.testFlag(DirtyMaskSizeRatio))) {
190 return maskSizeRatio;
191 }
192
193 markDirty(DirtyMaskSizeRatio, false);
194 const auto &sr = getSourceRect();
195 maskSizeRatio.setX(radius <= 0 ? sr.width() : static_cast<float>(sr.width() / static_cast<qreal>(radius)));
196 maskSizeRatio.setY(radius <= 0 ? sr.height() : static_cast<float>(sr.height() / static_cast<qreal>(radius)));
197 return maskSizeRatio;
198 }
199 inline const QVector2D &getMaskOffset() {
200 if (Q_LIKELY(!dirtyState.testFlag(DirtyMaskOffset))) {
201 return maskOffset;
202 }
203
204 Q_ASSERT(sourceItem && sourceItem->width() > 0 && sourceItem->height() > 0);
205 markDirty(DirtyMaskOffset, false);
206 auto offset = getSourceRect().topLeft();
207 maskOffset.setX(static_cast<float>(offset.x() / sourceItem->width()));
208 maskOffset.setY(static_cast<float>(offset.y() / sourceItem->height()));
209 return maskOffset;
210 }
211
212 inline QSGTexture *textureForRadiusMask()
213 {
214 if (Q_UNLIKELY(dirtyState.testFlag(DirtyMaskTexture) || !maskTexture)) {
215 QQuickItemPrivate *d = QQuickItemPrivate::get(q_func());
216 maskTexture = MaskTextureCache::instance()->getTexture(d->sceneGraphRenderContext(),
217 qRound(radius * d->window->effectiveDevicePixelRatio()),
218 d->antialiasing);
219
220 markDirty(DirtyMaskTexture, false);
221 }
222
223 return maskTexture->texture;
224 }
225
226 // MaskNode is required when need composition in transparent window.
227 inline bool needMaskNode() const {
228 return radius > 0
229#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
230 || compositionMode != DefaultCompositionMode
231#endif
232 ;
233 }
234
235 inline bool updateOffset(const QPointF &offset) {
236 if (this->offset == offset)
237 return false;
238 this->offset = offset;
239 markDirty(DirtyMaskOffset);
240 return true;
241 }
242
243 inline QRectF getSourceRect() const {
244 QRectF sr = sourceRect;
245 if (!sourceRect.isValid()) {
246 sr = QRectF(QPointF(0, 0), q_func()->size());
247 }
248
249 return fixed ? sr : sr.translated(offset);
250 }
251
252 template<typename T>
253 inline void updateSourceRect(T *node) const {
254 const QSizeF &textureSize = node->texture()->textureSize();
255 qreal xScale = textureSize.width() / sourceItem->width();
256 qreal yScale = textureSize.height() / sourceItem->height();
257 // 计算sourceItem应该被绘制的区域,如果此区域大小为0, 则没有必要再继续绘制
258 const QRectF &sourceRect = getSourceRect();
259 // 更新 DQuickItemViewport 所对应的sourceItem的材质区域
260 node->setSourceRect(QRectF(sourceRect.x() * xScale, sourceRect.y() * yScale,
261 sourceRect.width() * xScale, sourceRect.height() * yScale));
262 }
263
264 void setPreprocessNode(PreprocessNode *newNode);
265 void clearPreprocessNode(PreprocessNode *oldNode);
266 void updateUsePreprocess() const;
267 void ensureTexture();
268
269 D_DECLARE_PUBLIC(DQuickItemViewport)
270
271 QPointer<QQuickItem> sourceItem;
272 QAtomicPointer<PreprocessNode> preprocessNode;
273 // 记录sourceItem的大小是自身的多少倍
274 QVector2D soureSizeRatio;
275 // 显示圆角的mask材质
276 MaskTextureCache::TextureData maskTexture;
277 // item自身相对于圆角大小的比例
278 QVector2D maskSizeRatio;
279 // mask材质相对于sourceItem材质的偏移量
280 QVector2D maskOffset;
281 QMetaObject::Connection textureChangedConnection;
282 // 自身位置相对于sourceItem的偏移量
283 QPointF offset = QPointF(0, 0);
284 QRectF sourceRect;
285 // 记录待更新的数据类型
286 DirtyState dirtyState = DirtyNothing;
287 // 圆角半径大小
288 float radius = 0;
289 bool fixed = false;
290 bool hideSource = false;
291 QSGLayer *texture = nullptr;
292 DQuickViewportTextureProvider *provider = nullptr;
293
294#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
295 constexpr static QPainter::CompositionMode DefaultCompositionMode = QPainter::CompositionMode_SourceOver;
296 QPainter::CompositionMode compositionMode = DefaultCompositionMode;
297#endif
298};
299Q_DECLARE_OPERATORS_FOR_FLAGS(DQuickItemViewportPrivate::DirtyState)
300DQUICK_END_NAMESPACE
301#endif // DQUICKITEMVIEWPORT_P_H
Definition dquickitemviewport_p.h:140
DQuickItemViewport 类是根据 sourceItem 属性设定的 QQuickItem 作为绘制时的材质来源,这个行为依赖于 QQuickItem::textureProvider 提供...
Definition dquickitemviewport.h:20
Definition dquickitemviewport_p.h:120
Definition dquickitemviewport_p.h:103
Definition dquickitemviewport.cpp:25
Definition dquickitemviewport_p.h:19
Definition dquickitemviewport_p.h:17