Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsgdistancefieldglyphnode.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6#include <QtQuick/private/qsgcontext_p.h>
7
9
10Q_LOGGING_CATEGORY(lcSgText, "qt.scenegraph.text")
11
12// all SG glyph vertices and indices; only for qCDebug metrics
13static std::atomic<qint64> s_totalAllocation = 0;
14
16 : m_glyphNodeType(RootGlyphNode)
17 , m_context(context)
18 , m_material(nullptr)
19 , m_glyph_cache(nullptr)
23 , m_texture(nullptr)
24 , m_renderTypeQuality(-1)
25 , m_dirtyGeometry(false)
26 , m_dirtyMaterial(false)
27{
28 m_geometry.setDrawingMode(QSGGeometry::DrawTriangles);
29 setGeometry(&m_geometry);
30#ifdef QSG_RUNTIME_DESCRIPTION
31 qsgnode_set_description(this, QLatin1String("glyphs"));
32#endif
33}
34
36{
37 delete m_material;
38
39 if (m_glyphNodeType == SubGlyphNode)
40 return;
41
42 if (m_glyph_cache) {
43 m_glyph_cache->release(m_glyphs.glyphIndexes());
44 m_glyph_cache->unregisterGlyphNode(this);
45 }
46}
47
48void QSGDistanceFieldGlyphNode::setColor(const QColor &color)
49{
50 m_color = color;
51 if (m_material != nullptr) {
52 m_material->setColor(color);
53 markDirty(DirtyMaterial);
54 } else {
55 m_dirtyMaterial = true;
56 }
57}
58
60{
61 if (renderTypeQuality == m_renderTypeQuality)
62 return;
63
64 m_renderTypeQuality = renderTypeQuality;
65}
66
68{
69 if (mode == m_antialiasingMode)
70 return;
71 m_antialiasingMode = mode;
72 m_dirtyMaterial = true;
73}
74
75void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
76{
77 QRawFont font = glyphs.rawFont();
78 m_originalPosition = position;
79 m_position = QPointF(position.x(), position.y() - font.ascent());
80 m_glyphs = glyphs;
81
82 m_dirtyGeometry = true;
83 m_dirtyMaterial = true;
84 setFlag(UsePreprocess);
85
86 QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache;
87 m_glyph_cache = m_context->distanceFieldGlyphCache(m_glyphs.rawFont(), m_renderTypeQuality);
88
89 if (m_glyphNodeType == SubGlyphNode)
90 return;
91
92 if (m_glyph_cache != oldCache) {
93 if (oldCache) {
94 oldCache->unregisterGlyphNode(this);
95 }
96 m_glyph_cache->registerGlyphNode(this);
97 }
98 if (m_glyph_cache)
99 m_glyph_cache->populate(glyphs.glyphIndexes());
100
101 const QVector<quint32> glyphIndexes = m_glyphs.glyphIndexes();
102 for (int i = 0; i < glyphIndexes.size(); ++i)
103 m_allGlyphIndexesLookup.insert(glyphIndexes.at(i));
104 qCDebug(lcSgText, "inserting %" PRIdQSIZETYPE " glyphs, %" PRIdQSIZETYPE " unique",
105 glyphIndexes.size(),
106 m_allGlyphIndexesLookup.size());
107#ifdef QSG_RUNTIME_DESCRIPTION
108 qsgnode_set_description(this, QString::number(glyphs.glyphIndexes().count()) + QStringLiteral(" DF glyphs: ") +
109 m_glyphs.rawFont().familyName() + QStringLiteral(" ") + QString::number(m_glyphs.rawFont().pixelSize()));
110#endif
111}
112
113void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style)
114{
115 if (m_style == style)
116 return;
117 m_style = style;
118 m_dirtyMaterial = true;
119}
120
121void QSGDistanceFieldGlyphNode::setStyleColor(const QColor &color)
122{
123 if (m_styleColor == color)
124 return;
125 m_styleColor = color;
126 m_dirtyMaterial = true;
127}
128
130{
131 if (m_dirtyMaterial)
132 updateMaterial();
133}
134
136{
137 if (m_dirtyGeometry)
139
140 setFlag(UsePreprocess, false);
141}
142
143void QSGDistanceFieldGlyphNode::invalidateGlyphs(const QVector<quint32> &glyphs)
144{
145 if (m_dirtyGeometry)
146 return;
147
148 for (int i = 0; i < glyphs.size(); ++i) {
149 if (m_allGlyphIndexesLookup.contains(glyphs.at(i))) {
150 m_dirtyGeometry = true;
151 setFlag(UsePreprocess);
152 return;
153 }
154 }
155}
156
158{
159 if (!m_glyph_cache)
160 return;
161
162 // Remove previously created sub glyph nodes
163 // We assume all the children are sub glyph nodes
164 QSGNode *subnode = firstChild();
165 QSGNode *nextNode = nullptr;
166 while (subnode) {
167 nextNode = subnode->nextSibling();
168 delete subnode;
169 subnode = nextNode;
170 }
171
172 QSGGeometry *g = geometry();
173
174 Q_ASSERT(g->indexType() == QSGGeometry::UnsignedShortType);
175 m_glyphsInOtherTextures.clear();
176
177 const QVector<quint32> indexes = m_glyphs.glyphIndexes();
178 const QVector<QPointF> positions = m_glyphs.positions();
179 qreal fontPixelSize = m_glyphs.rawFont().pixelSize();
180
181 // The template parameters here are assuming that most strings are short, 64
182 // characters or less.
183 QVarLengthArray<QSGGeometry::TexturedPoint2D, 256> vp;
184 QVarLengthArray<ushort, 384> ip;
185 const qsizetype maxIndexCount = (std::numeric_limits<quint16>::max() - 1) / 4; // 16383 (see below: 0xFFFF is not allowed)
186 const qsizetype maxVertexCount = maxIndexCount * 4; // 65532
187 const auto likelyGlyphCount = qMin(indexes.size(), maxIndexCount);
188 vp.reserve(likelyGlyphCount * 4);
189 ip.reserve(likelyGlyphCount * 6);
190
191 qreal maxTexMargin = m_glyph_cache->distanceFieldRadius();
192 qreal fontScale = m_glyph_cache->fontScale(fontPixelSize);
193 qreal margin = 2;
194 qreal texMargin = margin / fontScale;
195 if (texMargin > maxTexMargin) {
196 texMargin = maxTexMargin;
197 margin = maxTexMargin * fontScale;
198 }
199
200 for (int i = 0; i < indexes.size(); ++i) {
201 const int glyphIndex = indexes.at(i);
202 QSGDistanceFieldGlyphCache::TexCoord c = m_glyph_cache->glyphTexCoord(glyphIndex);
203
204 if (c.isNull())
205 continue;
206
207 const QPointF position = positions.at(i);
208
209 const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphIndex);
210 if (texture->texture && !m_texture)
211 m_texture = texture;
212
213 // As we use UNSIGNED_SHORT indexing in the geometry, we overload the
214 // "glyphsInOtherTextures" concept as overflow for if there are more
215 // than 65532 vertices to render, which would otherwise exceed the
216 // maximum index size. (leave 0xFFFF unused in order not to clash with
217 // primitive restart) This will cause sub-nodes to be
218 // created to handle any number of glyphs. But only the RootGlyphNode
219 // needs to do this classification; from the perspective of a SubGlyphNode,
220 // it's already done, and m_glyphs contains only pointers to ranges of
221 // indices and positions that the RootGlyphNode is storing.
222 if (m_texture != texture || vp.size() >= maxVertexCount) {
223 if (m_glyphNodeType == RootGlyphNode && texture->texture) {
224 GlyphInfo &glyphInfo = m_glyphsInOtherTextures[texture];
225 glyphInfo.indexes.append(glyphIndex);
226 glyphInfo.positions.append(position);
227 } else if (vp.size() >= maxVertexCount && m_glyphNodeType == SubGlyphNode) {
228 break; // out of this loop over indices, because we won't add any more vertices
229 }
230 continue;
231 }
232
233 QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphIndex, fontPixelSize);
234
235 if (!metrics.isNull() && !c.isNull()) {
236 metrics.width += margin * 2;
237 metrics.height += margin * 2;
238 metrics.baselineX -= margin;
239 metrics.baselineY += margin;
240 c.xMargin -= texMargin;
241 c.yMargin -= texMargin;
242 c.width += texMargin * 2;
243 c.height += texMargin * 2;
244 }
245
246 qreal x = position.x() + metrics.baselineX + m_position.x();
247 qreal y = position.y() - metrics.baselineY + m_position.y();
248
249 m_boundingRect |= QRectF(x, y, metrics.width, metrics.height);
250
251 float cx1 = x;
252 float cx2 = x + metrics.width;
253 float cy1 = y;
254 float cy2 = y + metrics.height;
255
256 float tx1 = c.x + c.xMargin;
257 float tx2 = tx1 + c.width;
258 float ty1 = c.y + c.yMargin;
259 float ty2 = ty1 + c.height;
260
261 if (m_baseLine.isNull())
262 m_baseLine = position;
263
264 int o = vp.size();
265
266 QSGGeometry::TexturedPoint2D v1;
267 v1.set(cx1, cy1, tx1, ty1);
268 QSGGeometry::TexturedPoint2D v2;
269 v2.set(cx2, cy1, tx2, ty1);
270 QSGGeometry::TexturedPoint2D v3;
271 v3.set(cx1, cy2, tx1, ty2);
272 QSGGeometry::TexturedPoint2D v4;
273 v4.set(cx2, cy2, tx2, ty2);
274 vp.append(v1);
275 vp.append(v2);
276 vp.append(v3);
277 vp.append(v4);
278
279 ip.append(o + 0);
280 ip.append(o + 2);
281 ip.append(o + 3);
282 ip.append(o + 3);
283 ip.append(o + 1);
284 ip.append(o + 0);
285 }
286
287 if (m_glyphNodeType == SubGlyphNode) {
288 Q_ASSERT(m_glyphsInOtherTextures.isEmpty());
289 } else {
290 if (!m_glyphsInOtherTextures.isEmpty())
291 qCDebug(lcSgText, "%" PRIdQSIZETYPE " 'other' textures", m_glyphsInOtherTextures.size());
292 QHash<const QSGDistanceFieldGlyphCache::Texture *, GlyphInfo>::const_iterator ite = m_glyphsInOtherTextures.constBegin();
293 while (ite != m_glyphsInOtherTextures.constEnd()) {
294 QGlyphRun subNodeGlyphRun(m_glyphs);
295 for (int i = 0; i < ite->indexes.size(); i += maxIndexCount) {
296 int len = qMin(maxIndexCount, ite->indexes.size() - i);
297 subNodeGlyphRun.setRawData(ite->indexes.constData() + i, ite->positions.constData() + i, len);
298 qCDebug(lcSgText) << "subNodeGlyphRun has" << len << "positions:"
299 << *(ite->positions.constData() + i) << "->" << *(ite->positions.constData() + i + len - 1);
300
302 subNode->setGlyphNodeType(SubGlyphNode);
303 subNode->setColor(m_color);
304 subNode->setStyle(m_style);
305 subNode->setStyleColor(m_styleColor);
306 subNode->setPreferredAntialiasingMode(m_antialiasingMode);
307 subNode->setGlyphs(m_originalPosition, subNodeGlyphRun);
308 subNode->update();
309 subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered
310 appendChildNode(subNode);
311 }
312 ++ite;
313 }
314 }
315
316 s_totalAllocation += vp.size() * sizeof(QSGGeometry::TexturedPoint2D) + ip.size() * sizeof(quint16);
317 qCDebug(lcSgText) << "allocating for" << vp.size() << "vtx (reserved" << likelyGlyphCount * 4 << "):" << vp.size() * sizeof(QSGGeometry::TexturedPoint2D)
318 << "bytes;" << ip.size() << "idx:" << ip.size() * sizeof(quint16) << "bytes; total bytes so far" << s_totalAllocation;
319 g->allocate(vp.size(), ip.size());
320 memcpy(g->vertexDataAsTexturedPoint2D(), vp.constData(), vp.size() * sizeof(QSGGeometry::TexturedPoint2D));
321 memcpy(g->indexDataAsUShort(), ip.constData(), ip.size() * sizeof(quint16));
322
323 setBoundingRect(m_boundingRect);
324 markDirty(DirtyGeometry);
325 m_dirtyGeometry = false;
326
327 m_material->setTexture(m_texture);
328}
329
330void QSGDistanceFieldGlyphNode::updateMaterial()
331{
332 delete m_material;
333
334 if (m_style == QQuickText::Normal) {
335 switch (m_antialiasingMode) {
336 case HighQualitySubPixelAntialiasing:
337 m_material = new QSGHiQSubPixelDistanceFieldTextMaterial;
338 break;
339 case LowQualitySubPixelAntialiasing:
340 m_material = new QSGLoQSubPixelDistanceFieldTextMaterial;
341 break;
342 case GrayAntialiasing:
343 default:
344 m_material = new QSGDistanceFieldTextMaterial;
345 break;
346 }
347 } else {
348 QSGDistanceFieldStyledTextMaterial *material;
349 if (m_style == QQuickText::Outline) {
350 material = new QSGDistanceFieldOutlineTextMaterial;
351 } else {
352 QSGDistanceFieldShiftedStyleTextMaterial *sMaterial = new QSGDistanceFieldShiftedStyleTextMaterial;
353 if (m_style == QQuickText::Raised)
354 sMaterial->setShift(QPointF(0.0, 1.0));
355 else
356 sMaterial->setShift(QPointF(0.0, -1.0));
357 material = sMaterial;
358 }
359 material->setStyleColor(m_styleColor);
360 m_material = material;
361 }
362
363 m_material->setGlyphCache(m_glyph_cache);
364 if (m_glyph_cache)
365 m_material->setFontScale(m_glyph_cache->fontScale(m_glyphs.rawFont().pixelSize()));
366 m_material->setColor(m_color);
367 setMaterial(m_material);
368 m_dirtyMaterial = false;
369}
370
371QT_END_NAMESPACE
QSGDistanceFieldGlyphNode(QSGRenderContext *context)
void setStyleColor(const QColor &color) override
void setStyle(QQuickText::TextStyle style) override
void setRenderTypeQuality(int renderTypeQuality) override
void setColor(const QColor &color) override
void setPreferredAntialiasingMode(AntialiasingMode mode) override
void preprocess() override
Override this function to do processing on the node before it is rendered.
void invalidateGlyphs(const QVector< quint32 > &glyphs) override
void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override