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