1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qsgdistancefieldglyphnode_p.h"
43 #include "qsgdistancefieldglyphnode_p_p.h"
44 #include <QtQuick/private/qsgdistancefieldutil_p.h>
45 #include <QtQuick/private/qsgcontext_p.h>
49 QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheManager *cacheManager)
51 , m_glyph_cacheManager(cacheManager)
53 , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
54 , m_style(QQuickText::Normal)
55 , m_antialiasingMode(GrayAntialiasing)
57 , m_dirtyGeometry(false)
58 , m_dirtyMaterial(false)
60 setFlag(UsePreprocess);
61 m_geometry.setDrawingMode(GL_TRIANGLES);
62 setGeometry(&m_geometry);
63 setPreferredAntialiasingMode(cacheManager->defaultAntialiasingMode());
64 #ifdef QML_RUNTIME_TESTING
65 description = QLatin1String("glyphs");
69 QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode()
74 QVector<quint32> glyphIndexes;
75 for (int i = 0; i < m_allGlyphs.count(); ++i)
76 glyphIndexes.append(m_allGlyphs.at(i).glyphIndex);
77 m_glyph_cache->release(glyphIndexes);
78 m_glyph_cache->unregisterGlyphNode(this);
81 for (int i = 0; i < m_nodesToDelete.count(); ++i)
82 delete m_nodesToDelete.at(i);
83 m_nodesToDelete.clear();
86 void QSGDistanceFieldGlyphNode::setColor(const QColor &color)
89 if (m_material != 0) {
90 m_material->setColor(color);
91 markDirty(DirtyMaterial);
93 m_dirtyMaterial = true;
97 void QSGDistanceFieldGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
99 if (mode == m_antialiasingMode)
101 m_antialiasingMode = mode;
102 m_dirtyMaterial = true;
105 void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
107 QRawFont font = glyphs.rawFont();
108 m_originalPosition = position;
109 m_position = QPointF(position.x(), position.y() - font.ascent());
112 QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache;
113 m_glyph_cache = m_glyph_cacheManager->cache(m_glyphs.rawFont());
114 if (m_glyph_cache != oldCache) {
116 oldCache->unregisterGlyphNode(this);
117 m_glyph_cache->registerGlyphNode(this);
119 m_glyph_cache->populate(glyphs.glyphIndexes());
121 const QVector<quint32> &glyphIndexes = m_glyphs.glyphIndexes();
122 const QVector<QPointF> &glyphPositions = m_glyphs.positions();
123 for (int i = 0; i < glyphIndexes.count(); ++i) {
125 gi.glyphIndex = glyphIndexes.at(i);
126 gi.position = glyphPositions.at(i);
127 m_allGlyphs.append(gi);
128 m_allGlyphIndexesLookup.insert(gi.glyphIndex);
131 m_dirtyGeometry = true;
132 m_dirtyMaterial = true;
135 void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style)
137 if (m_style == style)
140 m_dirtyMaterial = true;
143 void QSGDistanceFieldGlyphNode::setStyleColor(const QColor &color)
145 if (m_styleColor == color)
147 m_styleColor = color;
148 m_dirtyMaterial = true;
151 void QSGDistanceFieldGlyphNode::update()
157 void QSGDistanceFieldGlyphNode::preprocess()
159 Q_ASSERT(m_glyph_cache);
161 m_glyph_cache->update();
163 for (int i = 0; i < m_nodesToDelete.count(); ++i)
164 delete m_nodesToDelete.at(i);
165 m_nodesToDelete.clear();
171 void QSGDistanceFieldGlyphNode::invalidateGlyphs(const QVector<quint32> &glyphs)
176 for (int i = 0; i < glyphs.count(); ++i) {
177 if (m_allGlyphIndexesLookup.contains(glyphs.at(i))) {
178 m_dirtyGeometry = true;
184 void QSGDistanceFieldGlyphNode::updateGeometry()
186 Q_ASSERT(m_glyph_cache);
188 // Remove previously created sub glyph nodes
189 QHash<const QSGDistanceFieldGlyphCache::Texture *, QSGDistanceFieldGlyphNode *>::iterator it = m_subNodes.begin();
190 while (it != m_subNodes.end()) {
191 removeChildNode(it.value());
192 // We can't delete the node now as it might be in the preprocess list
193 // It will be deleted in the next preprocess
194 m_nodesToDelete.append(it.value());
195 it = m_subNodes.erase(it);
198 QSGGeometry *g = geometry();
200 Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT);
202 QHash<const QSGDistanceFieldGlyphCache::Texture *, QList<GlyphInfo> > glyphsInOtherTextures;
204 QVector<QSGGeometry::TexturedPoint2D> vp;
205 vp.reserve(m_allGlyphs.size() * 4);
207 ip.reserve(m_allGlyphs.size() * 6);
209 QPointF margins(2, 2);
210 QPointF texMargins = margins / m_glyph_cache->fontScale();
212 for (int i = 0; i < m_allGlyphs.size(); ++i) {
213 GlyphInfo glyphInfo = m_allGlyphs.at(i);
214 QSGDistanceFieldGlyphCache::TexCoord c = m_glyph_cache->glyphTexCoord(glyphInfo.glyphIndex);
219 const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphInfo.glyphIndex);
220 if (texture->textureId && !m_texture)
223 if (m_texture != texture) {
224 if (texture->textureId)
225 glyphsInOtherTextures[texture].append(glyphInfo);
229 QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphInfo.glyphIndex);
231 if (!metrics.isNull() && !c.isNull()) {
232 metrics.width += margins.x() * 2;
233 metrics.height += margins.y() * 2;
234 metrics.baselineX -= margins.x();
235 metrics.baselineY += margins.y();
236 c.xMargin -= texMargins.x();
237 c.yMargin -= texMargins.y();
238 c.width += texMargins.x() * 2;
239 c.height += texMargins.y() * 2;
242 qreal x = glyphInfo.position.x() + metrics.baselineX + m_position.x();
243 qreal y = glyphInfo.position.y() - metrics.baselineY + m_position.y();
245 m_boundingRect |= QRectF(x, y, metrics.width, metrics.height);
248 float cx2 = x + metrics.width;
250 float cy2 = y + metrics.height;
252 float tx1 = c.x + c.xMargin;
253 float tx2 = tx1 + c.width;
254 float ty1 = c.y + c.yMargin;
255 float ty2 = ty1 + c.height;
257 if (m_baseLine.isNull())
258 m_baseLine = glyphInfo.position;
262 QSGGeometry::TexturedPoint2D v1;
263 v1.set(cx1, cy1, tx1, ty1);
264 QSGGeometry::TexturedPoint2D v2;
265 v2.set(cx2, cy1, tx2, ty1);
266 QSGGeometry::TexturedPoint2D v3;
267 v3.set(cx1, cy2, tx1, ty2);
268 QSGGeometry::TexturedPoint2D v4;
269 v4.set(cx2, cy2, tx2, ty2);
283 QHash<const QSGDistanceFieldGlyphCache::Texture *, QList<GlyphInfo> >::const_iterator ite = glyphsInOtherTextures.constBegin();
284 while (ite != glyphsInOtherTextures.constEnd()) {
285 QHash<const QSGDistanceFieldGlyphCache::Texture *, QSGDistanceFieldGlyphNode *>::iterator subIt = m_subNodes.find(ite.key());
286 if (subIt == m_subNodes.end()) {
287 QSGDistanceFieldGlyphNode *subNode = new QSGDistanceFieldGlyphNode(m_glyph_cacheManager);
288 subNode->setColor(m_color);
289 subNode->setStyle(m_style);
290 subNode->setStyleColor(m_styleColor);
292 appendChildNode(subNode);
293 subIt = m_subNodes.insert(ite.key(), subNode);
296 QVector<quint32> glyphIndexes;
297 QVector<QPointF> positions;
298 const QList<GlyphInfo> &subNodeGlyphs = ite.value();
299 for (int i = 0; i < subNodeGlyphs.count(); ++i) {
300 const GlyphInfo &info = subNodeGlyphs.at(i);
301 glyphIndexes.append(info.glyphIndex);
302 positions.append(info.position);
304 QGlyphRun subNodeGlyphRun(m_glyphs);
305 subNodeGlyphRun.setGlyphIndexes(glyphIndexes);
306 subNodeGlyphRun.setPositions(positions);
308 subIt.value()->setGlyphs(m_originalPosition, subNodeGlyphRun);
309 subIt.value()->update();
310 subIt.value()->updateGeometry(); // we have to explicity call this now as preprocess won't be called before it's rendered
315 g->allocate(vp.size(), ip.size());
316 memcpy(g->vertexDataAsTexturedPoint2D(), vp.constData(), vp.size() * sizeof(QSGGeometry::TexturedPoint2D));
317 memcpy(g->indexDataAsUShort(), ip.constData(), ip.size() * sizeof(quint16));
319 setBoundingRect(m_boundingRect);
320 markDirty(DirtyGeometry);
321 m_dirtyGeometry = false;
323 m_material->setTexture(m_texture);
326 void QSGDistanceFieldGlyphNode::updateMaterial()
330 if (m_style == QQuickText::Normal) {
331 switch (m_antialiasingMode) {
332 case HighQualitySubPixelAntialiasing:
333 m_material = new QSGHiQSubPixelDistanceFieldTextMaterial;
335 case LowQualitySubPixelAntialiasing:
336 m_material = new QSGLoQSubPixelDistanceFieldTextMaterial;
338 case GrayAntialiasing:
340 m_material = new QSGDistanceFieldTextMaterial;
344 QSGDistanceFieldStyledTextMaterial *material;
345 if (m_style == QQuickText::Outline) {
346 material = new QSGDistanceFieldOutlineTextMaterial;
348 QSGDistanceFieldShiftedStyleTextMaterial *sMaterial = new QSGDistanceFieldShiftedStyleTextMaterial;
349 if (m_style == QQuickText::Raised)
350 sMaterial->setShift(QPointF(0.0, 1.0));
352 sMaterial->setShift(QPointF(0.0, -1.0));
353 material = sMaterial;
355 material->setStyleColor(m_styleColor);
356 m_material = material;
359 m_material->setGlyphCache(m_glyph_cache);
360 m_material->setColor(m_color);
361 setMaterial(m_material);
362 m_dirtyMaterial = false;