1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsgdefaultglyphnode_p.h"
41#include "qsgdefaultglyphnode_p_p.h"
42
43#include <private/qrawfont_p.h>
44
45QT_BEGIN_NAMESPACE
46
47QSGDefaultGlyphNode::QSGDefaultGlyphNode(QSGRenderContext *context)
48 : m_context(context)
49 , m_glyphNodeType(RootGlyphNode)
50 , m_dirtyGeometry(false)
51 , m_preferredAntialiasingMode(DefaultAntialiasing)
52{
53 setFlag(UsePreprocess);
54}
55
56QSGDefaultGlyphNode::~QSGDefaultGlyphNode()
57{
58 if (m_glyphNodeType == SubGlyphNode)
59 return;
60
61 qDeleteAll(c: m_nodesToDelete);
62 m_nodesToDelete.clear();
63}
64
65void QSGDefaultGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
66{
67 m_preferredAntialiasingMode = mode;
68}
69
70void QSGDefaultGlyphNode::setMaterialColor(const QColor &color)
71{
72 static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color);
73}
74
75void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
76{
77 QSGBasicGlyphNode::setGlyphs(position, glyphs);
78 m_dirtyGeometry = true;
79}
80
81void QSGDefaultGlyphNode::update()
82{
83 QRawFont font = m_glyphs.rawFont();
84 QMargins margins(0, 0, 0, 0);
85
86 if (m_style == QQuickText::Normal) {
87 QFontEngine::GlyphFormat glyphFormat;
88
89 // Don't try to override glyph format of color fonts
90 if (QRawFontPrivate::get(font)->fontEngine->glyphFormat == QFontEngine::Format_ARGB) {
91 glyphFormat = QFontEngine::Format_None;
92 } else {
93 switch (m_preferredAntialiasingMode) {
94 case GrayAntialiasing:
95 glyphFormat = QFontEngine::Format_A8;
96 break;
97 case HighQualitySubPixelAntialiasing:
98 case LowQualitySubPixelAntialiasing:
99 glyphFormat = QFontEngine::Format_A32;
100 break;
101 default:
102 glyphFormat = QFontEngine::Format_None;
103 break;
104 }
105 }
106
107 m_material = new QSGTextMaskMaterial(m_context, QVector4D(m_color.redF(), m_color.greenF(), m_color.blueF(), m_color.alphaF()), font, glyphFormat);
108 } else if (m_style == QQuickText::Outline) {
109 QSGOutlinedTextMaterial *material = new QSGOutlinedTextMaterial(m_context, font);
110 material->setStyleColor(m_styleColor);
111 m_material = material;
112 margins = QMargins(1, 1, 1, 1);
113 } else {
114 QSGStyledTextMaterial *material = new QSGStyledTextMaterial(m_context, font);
115 if (m_style == QQuickText::Sunken) {
116 material->setStyleShift(QVector2D(0, -1));
117 margins.setTop(1);
118 } else if (m_style == QQuickText::Raised) {
119 material->setStyleShift(QVector2D(0, 1));
120 margins.setBottom(1);
121 }
122 material->setStyleColor(m_styleColor);
123 m_material = material;
124 }
125
126 QSGTextMaskMaterial *textMaskMaterial = static_cast<QSGTextMaskMaterial *>(m_material);
127 textMaskMaterial->setColor(m_color);
128
129 QRectF boundingRect;
130 textMaskMaterial->populate(position: m_position, glyphIndexes: m_glyphs.glyphIndexes(), glyphPositions: m_glyphs.positions(), geometry: geometry(),
131 boundingRect: &boundingRect, baseLine: &m_baseLine, margins);
132 setBoundingRect(boundingRect);
133
134 setMaterial(m_material);
135 markDirty(bits: DirtyGeometry);
136}
137
138void QSGDefaultGlyphNode::preprocess()
139{
140 qDeleteAll(c: m_nodesToDelete);
141 m_nodesToDelete.clear();
142
143 if (m_dirtyGeometry)
144 updateGeometry();
145}
146
147void QSGDefaultGlyphNode::updateGeometry()
148{
149 // Remove previously created sub glyph nodes
150 // We assume all the children are sub glyph nodes
151 QSGNode *subnode = firstChild();
152 while (subnode) {
153 // We can't delete the node now as it might be in the preprocess list
154 // It will be deleted in the next preprocess
155 m_nodesToDelete.append(t: subnode);
156 subnode = subnode->nextSibling();
157 }
158 removeAllChildNodes();
159
160 GlyphInfo glyphInfo;
161
162 const QVector<quint32> indexes = m_glyphs.glyphIndexes();
163 const QVector<QPointF> positions = m_glyphs.positions();
164
165 const int maxGlyphs = (USHRT_MAX + 1) / 4; // 16384
166 const int maxVertices = maxGlyphs * 4; // 65536
167 const int maxIndexes = maxGlyphs * 6; // 98304
168
169 for (int i = 0; i < indexes.size(); ++i) {
170 const int glyphIndex = indexes.at(i);
171 const QPointF position = positions.at(i);
172
173 // As we use UNSIGNED_SHORT indexing in the geometry, we overload the
174 // "glyphsInOtherNodes" concept as overflow for if there are more than
175 // 65536 (16384 * 4) vertices to render which would otherwise exceed
176 // the maximum index size. This will cause sub-nodes to be recursively
177 // created to handle any number of glyphs.
178 if (i >= maxGlyphs) {
179 glyphInfo.indexes.append(t: glyphIndex);
180 glyphInfo.positions.append(t: position);
181 continue;
182 }
183 }
184
185 if (!glyphInfo.indexes.isEmpty()) {
186 QGlyphRun subNodeGlyphRun(m_glyphs);
187 subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes);
188 subNodeGlyphRun.setPositions(glyphInfo.positions);
189
190 QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode(m_context);
191 subNode->setGlyphNodeType(SubGlyphNode);
192 subNode->setColor(m_color);
193 subNode->setStyle(m_style);
194 subNode->setStyleColor(m_styleColor);
195 subNode->setGlyphs(position: m_position, glyphs: subNodeGlyphRun);
196 subNode->update();
197 subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered
198 appendChildNode(node: subNode);
199
200 QSGGeometry *g = geometry();
201
202 QSGGeometry::TexturedPoint2D *vertexData = g->vertexDataAsTexturedPoint2D();
203 quint16 *indexData = g->indexDataAsUShort();
204
205 QVector<QSGGeometry::TexturedPoint2D> tempVertexData(maxVertices);
206 QVector<quint16> tempIndexData(maxIndexes);
207
208 for (int i = 0; i < maxGlyphs; i++) {
209 tempVertexData[i * 4 + 0] = vertexData[i * 4 + 0];
210 tempVertexData[i * 4 + 1] = vertexData[i * 4 + 1];
211 tempVertexData[i * 4 + 2] = vertexData[i * 4 + 2];
212 tempVertexData[i * 4 + 3] = vertexData[i * 4 + 3];
213
214 tempIndexData[i * 6 + 0] = indexData[i * 6 + 0];
215 tempIndexData[i * 6 + 1] = indexData[i * 6 + 1];
216 tempIndexData[i * 6 + 2] = indexData[i * 6 + 2];
217 tempIndexData[i * 6 + 3] = indexData[i * 6 + 3];
218 tempIndexData[i * 6 + 4] = indexData[i * 6 + 4];
219 tempIndexData[i * 6 + 5] = indexData[i * 6 + 5];
220 }
221
222 g->allocate(vertexCount: maxVertices, indexCount: maxIndexes);
223 vertexData = g->vertexDataAsTexturedPoint2D();
224 indexData = g->indexDataAsUShort();
225
226 for (int i = 0; i < maxGlyphs; i++) {
227 vertexData[i * 4 + 0] = tempVertexData[i * 4 + 0];
228 vertexData[i * 4 + 1] = tempVertexData[i * 4 + 1];
229 vertexData[i * 4 + 2] = tempVertexData[i * 4 + 2];
230 vertexData[i * 4 + 3] = tempVertexData[i * 4 + 3];
231
232 indexData[i * 6 + 0] = tempIndexData[i * 6 + 0];
233 indexData[i * 6 + 1] = tempIndexData[i * 6 + 1];
234 indexData[i * 6 + 2] = tempIndexData[i * 6 + 2];
235 indexData[i * 6 + 3] = tempIndexData[i * 6 + 3];
236 indexData[i * 6 + 4] = tempIndexData[i * 6 + 4];
237 indexData[i * 6 + 5] = tempIndexData[i * 6 + 5];
238 }
239 }
240
241 m_dirtyGeometry = false;
242}
243
244QT_END_NAMESPACE
245

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