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

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