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