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