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
4#include "qsgdefaultglyphnode_p.h"
5#include "qsgdefaultglyphnode_p_p.h"
6
7#include <private/qrawfont_p.h>
8
9QT_BEGIN_NAMESPACE
10
11QSGDefaultGlyphNode::QSGDefaultGlyphNode(QSGRenderContext *context)
12 : m_context(context)
13 , m_glyphNodeType(RootGlyphNode)
14 , m_dirtyGeometry(false)
15 , m_preferredAntialiasingMode(DefaultAntialiasing)
16{
17 setFlag(UsePreprocess);
18}
19
20QSGDefaultGlyphNode::~QSGDefaultGlyphNode()
21{
22 if (m_glyphNodeType == SubGlyphNode)
23 return;
24
25 qDeleteAll(c: m_nodesToDelete);
26 m_nodesToDelete.clear();
27}
28
29void QSGDefaultGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
30{
31 m_preferredAntialiasingMode = mode;
32}
33
34void QSGDefaultGlyphNode::setMaterialColor(const QColor &color)
35{
36 static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color);
37}
38
39void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
40{
41 QSGBasicGlyphNode::setGlyphs(position, glyphs);
42 m_dirtyGeometry = true;
43}
44
45void QSGDefaultGlyphNode::update()
46{
47 QRawFont font = m_glyphs.rawFont();
48 QMargins margins(0, 0, 0, 0);
49
50 if (m_style == QQuickText::Normal) {
51 QFontEngine::GlyphFormat glyphFormat;
52
53 // Don't try to override glyph format of color fonts
54 if (QRawFontPrivate::get(font)->fontEngine->glyphFormat == QFontEngine::Format_ARGB) {
55 glyphFormat = QFontEngine::Format_None;
56 } else {
57 switch (m_preferredAntialiasingMode) {
58 case GrayAntialiasing:
59 glyphFormat = QFontEngine::Format_A8;
60 break;
61 case HighQualitySubPixelAntialiasing:
62 case LowQualitySubPixelAntialiasing:
63 glyphFormat = QFontEngine::Format_A32;
64 break;
65 default:
66 glyphFormat = QFontEngine::Format_None;
67 break;
68 }
69 }
70
71 const auto rgbColor = m_color.toRgb();
72 m_material = new QSGTextMaskMaterial(m_context, QVector4D(rgbColor.redF(), rgbColor.greenF(), rgbColor.blueF(), rgbColor.alphaF()), font, glyphFormat);
73 } else if (m_style == QQuickText::Outline) {
74 QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(m_context, font);
75 material->setStyleColor(m_styleColor);
76 m_material = material;
77 margins = QMargins(1, 1, 1, 1);
78 } else {
79 QSGStyledTextMaterial *material = new QSGStyledTextMaterial(m_context, font);
80 if (m_style == QQuickText::Sunken) {
81 material->setStyleShift(QVector2D(0, -1));
82 margins.setTop(1);
83 } else if (m_style == QQuickText::Raised) {
84 material->setStyleShift(QVector2D(0, 1));
85 margins.setBottom(1);
86 }
87 material->setStyleColor(m_styleColor);
88 m_material = material;
89 }
90
91 QSGTextMaskMaterial *textMaskMaterial = static_cast<QSGTextMaskMaterial *>(m_material);
92 textMaskMaterial->setColor(m_color);
93
94 QRectF boundingRect;
95 textMaskMaterial->populate(position: m_position, glyphIndexes: m_glyphs.glyphIndexes(), glyphPositions: m_glyphs.positions(), geometry: geometry(),
96 boundingRect: &boundingRect, baseLine: &m_baseLine, margins);
97 setBoundingRect(boundingRect);
98
99 setMaterial(m_material);
100 markDirty(bits: DirtyGeometry);
101}
102
103void QSGDefaultGlyphNode::preprocess()
104{
105 qDeleteAll(c: m_nodesToDelete);
106 m_nodesToDelete.clear();
107
108 if (m_dirtyGeometry)
109 updateGeometry();
110}
111
112void QSGDefaultGlyphNode::updateGeometry()
113{
114 // Remove previously created sub glyph nodes
115 // We assume all the children are sub glyph nodes
116 QSGNode *subnode = firstChild();
117 while (subnode) {
118 // We can't delete the node now as it might be in the preprocess list
119 // It will be deleted in the next preprocess
120 m_nodesToDelete.append(t: subnode);
121 subnode = subnode->nextSibling();
122 }
123 removeAllChildNodes();
124
125 GlyphInfo glyphInfo;
126
127 const QVector<quint32> indexes = m_glyphs.glyphIndexes();
128 const QVector<QPointF> positions = m_glyphs.positions();
129
130 const int maxGlyphs = (USHRT_MAX + 1) / 4; // 16384
131 const int maxVertices = maxGlyphs * 4; // 65536
132 const int maxIndexes = maxGlyphs * 6; // 98304
133
134 for (int i = 0; i < indexes.size(); ++i) {
135 const int glyphIndex = indexes.at(i);
136 const QPointF position = positions.at(i);
137
138 // As we use UNSIGNED_SHORT indexing in the geometry, we overload the
139 // "glyphsInOtherNodes" concept as overflow for if there are more than
140 // 65536 (16384 * 4) vertices to render which would otherwise exceed
141 // the maximum index size. This will cause sub-nodes to be recursively
142 // created to handle any number of glyphs.
143 if (i >= maxGlyphs) {
144 glyphInfo.indexes.append(t: glyphIndex);
145 glyphInfo.positions.append(t: position);
146 continue;
147 }
148 }
149
150 if (!glyphInfo.indexes.isEmpty()) {
151 QGlyphRun subNodeGlyphRun(m_glyphs);
152 subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes);
153 subNodeGlyphRun.setPositions(glyphInfo.positions);
154
155 QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode(m_context);
156 subNode->setGlyphNodeType(SubGlyphNode);
157 subNode->setColor(m_color);
158 subNode->setStyle(m_style);
159 subNode->setStyleColor(m_styleColor);
160 subNode->setGlyphs(position: m_position, glyphs: subNodeGlyphRun);
161 subNode->update();
162 subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered
163 appendChildNode(node: subNode);
164
165 QSGGeometry *g = geometry();
166
167 QSGGeometry::TexturedPoint2D *vertexData = g->vertexDataAsTexturedPoint2D();
168 quint16 *indexData = g->indexDataAsUShort();
169
170 QVector<QSGGeometry::TexturedPoint2D> tempVertexData(maxVertices);
171 QVector<quint16> tempIndexData(maxIndexes);
172
173 for (int i = 0; i < maxGlyphs; i++) {
174 tempVertexData[i * 4 + 0] = vertexData[i * 4 + 0];
175 tempVertexData[i * 4 + 1] = vertexData[i * 4 + 1];
176 tempVertexData[i * 4 + 2] = vertexData[i * 4 + 2];
177 tempVertexData[i * 4 + 3] = vertexData[i * 4 + 3];
178
179 tempIndexData[i * 6 + 0] = indexData[i * 6 + 0];
180 tempIndexData[i * 6 + 1] = indexData[i * 6 + 1];
181 tempIndexData[i * 6 + 2] = indexData[i * 6 + 2];
182 tempIndexData[i * 6 + 3] = indexData[i * 6 + 3];
183 tempIndexData[i * 6 + 4] = indexData[i * 6 + 4];
184 tempIndexData[i * 6 + 5] = indexData[i * 6 + 5];
185 }
186
187 g->allocate(vertexCount: maxVertices, indexCount: maxIndexes);
188 vertexData = g->vertexDataAsTexturedPoint2D();
189 indexData = g->indexDataAsUShort();
190
191 for (int i = 0; i < maxGlyphs; i++) {
192 vertexData[i * 4 + 0] = tempVertexData[i * 4 + 0];
193 vertexData[i * 4 + 1] = tempVertexData[i * 4 + 1];
194 vertexData[i * 4 + 2] = tempVertexData[i * 4 + 2];
195 vertexData[i * 4 + 3] = tempVertexData[i * 4 + 3];
196
197 indexData[i * 6 + 0] = tempIndexData[i * 6 + 0];
198 indexData[i * 6 + 1] = tempIndexData[i * 6 + 1];
199 indexData[i * 6 + 2] = tempIndexData[i * 6 + 2];
200 indexData[i * 6 + 3] = tempIndexData[i * 6 + 3];
201 indexData[i * 6 + 4] = tempIndexData[i * 6 + 4];
202 indexData[i * 6 + 5] = tempIndexData[i * 6 + 5];
203 }
204 }
205
206 m_dirtyGeometry = false;
207}
208
209QT_END_NAMESPACE
210

source code of qtdeclarative/src/quick/scenegraph/qsgdefaultglyphnode.cpp