1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** 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 m_geometry.setDrawingMode(GL_TRIANGLES);
61 setGeometry(&m_geometry);
62 setPreferredAntialiasingMode(cacheManager->defaultAntialiasingMode());
63 setFlag(UsePreprocess);
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) {
115 Q_ASSERT(ownerElement() != 0);
117 oldCache->unregisterGlyphNode(this);
118 oldCache->unregisterOwnerElement(ownerElement());
120 m_glyph_cache->registerGlyphNode(this);
121 m_glyph_cache->registerOwnerElement(ownerElement());
123 m_glyph_cache->populate(glyphs.glyphIndexes());
125 const QVector<quint32> &glyphIndexes = m_glyphs.glyphIndexes();
126 const QVector<QPointF> &glyphPositions = m_glyphs.positions();
127 for (int i = 0; i < glyphIndexes.count(); ++i) {
129 gi.glyphIndex = glyphIndexes.at(i);
130 gi.position = glyphPositions.at(i);
131 m_allGlyphs.append(gi);
132 m_allGlyphIndexesLookup.insert(gi.glyphIndex);
135 m_dirtyGeometry = true;
136 m_dirtyMaterial = true;
139 void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style)
141 if (m_style == style)
144 m_dirtyMaterial = true;
147 void QSGDistanceFieldGlyphNode::setStyleColor(const QColor &color)
149 if (m_styleColor == color)
151 m_styleColor = color;
152 m_dirtyMaterial = true;
155 void QSGDistanceFieldGlyphNode::update()
161 void QSGDistanceFieldGlyphNode::preprocess()
163 Q_ASSERT(m_glyph_cache);
165 for (int i = 0; i < m_nodesToDelete.count(); ++i)
166 delete m_nodesToDelete.at(i);
167 m_nodesToDelete.clear();
169 m_glyph_cache->processPendingGlyphs();
170 m_glyph_cache->update();
176 void QSGDistanceFieldGlyphNode::invalidateGlyphs(const QVector<quint32> &glyphs)
181 for (int i = 0; i < glyphs.count(); ++i) {
182 if (m_allGlyphIndexesLookup.contains(glyphs.at(i))) {
183 m_dirtyGeometry = true;
189 void QSGDistanceFieldGlyphNode::updateGeometry()
191 Q_ASSERT(m_glyph_cache);
193 // Remove previously created sub glyph nodes
194 QHash<const QSGDistanceFieldGlyphCache::Texture *, QSGDistanceFieldGlyphNode *>::iterator it = m_subNodes.begin();
195 while (it != m_subNodes.end()) {
196 removeChildNode(it.value());
197 // We can't delete the node now as it might be in the preprocess list
198 // It will be deleted in the next preprocess
199 m_nodesToDelete.append(it.value());
200 it = m_subNodes.erase(it);
203 QSGGeometry *g = geometry();
205 Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT);
207 QHash<const QSGDistanceFieldGlyphCache::Texture *, QList<GlyphInfo> > glyphsInOtherTextures;
209 QVector<QSGGeometry::TexturedPoint2D> vp;
210 vp.reserve(m_allGlyphs.size() * 4);
212 ip.reserve(m_allGlyphs.size() * 6);
214 QPointF margins(2, 2);
215 QPointF texMargins = margins / m_glyph_cache->fontScale();
217 for (int i = 0; i < m_allGlyphs.size(); ++i) {
218 GlyphInfo glyphInfo = m_allGlyphs.at(i);
219 QSGDistanceFieldGlyphCache::TexCoord c = m_glyph_cache->glyphTexCoord(glyphInfo.glyphIndex);
224 const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphInfo.glyphIndex);
225 if (texture->textureId && !m_texture)
228 if (m_texture != texture) {
229 if (texture->textureId)
230 glyphsInOtherTextures[texture].append(glyphInfo);
234 QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphInfo.glyphIndex);
236 if (!metrics.isNull() && !c.isNull()) {
237 metrics.width += margins.x() * 2;
238 metrics.height += margins.y() * 2;
239 metrics.baselineX -= margins.x();
240 metrics.baselineY += margins.y();
241 c.xMargin -= texMargins.x();
242 c.yMargin -= texMargins.y();
243 c.width += texMargins.x() * 2;
244 c.height += texMargins.y() * 2;
247 qreal x = glyphInfo.position.x() + metrics.baselineX + m_position.x();
248 qreal y = glyphInfo.position.y() - metrics.baselineY + m_position.y();
250 m_boundingRect |= QRectF(x, y, metrics.width, metrics.height);
253 float cx2 = x + metrics.width;
255 float cy2 = y + metrics.height;
257 float tx1 = c.x + c.xMargin;
258 float tx2 = tx1 + c.width;
259 float ty1 = c.y + c.yMargin;
260 float ty2 = ty1 + c.height;
262 if (m_baseLine.isNull())
263 m_baseLine = glyphInfo.position;
267 QSGGeometry::TexturedPoint2D v1;
268 v1.set(cx1, cy1, tx1, ty1);
269 QSGGeometry::TexturedPoint2D v2;
270 v2.set(cx2, cy1, tx2, ty1);
271 QSGGeometry::TexturedPoint2D v3;
272 v3.set(cx1, cy2, tx1, ty2);
273 QSGGeometry::TexturedPoint2D v4;
274 v4.set(cx2, cy2, tx2, ty2);
288 QHash<const QSGDistanceFieldGlyphCache::Texture *, QList<GlyphInfo> >::const_iterator ite = glyphsInOtherTextures.constBegin();
289 while (ite != glyphsInOtherTextures.constEnd()) {
290 QHash<const QSGDistanceFieldGlyphCache::Texture *, QSGDistanceFieldGlyphNode *>::iterator subIt = m_subNodes.find(ite.key());
291 if (subIt == m_subNodes.end()) {
292 QSGDistanceFieldGlyphNode *subNode = new QSGDistanceFieldGlyphNode(m_glyph_cacheManager);
293 subNode->setOwnerElement(m_ownerElement);
294 subNode->setColor(m_color);
295 subNode->setStyle(m_style);
296 subNode->setStyleColor(m_styleColor);
298 appendChildNode(subNode);
299 subIt = m_subNodes.insert(ite.key(), subNode);
302 QVector<quint32> glyphIndexes;
303 QVector<QPointF> positions;
304 const QList<GlyphInfo> &subNodeGlyphs = ite.value();
305 for (int i = 0; i < subNodeGlyphs.count(); ++i) {
306 const GlyphInfo &info = subNodeGlyphs.at(i);
307 glyphIndexes.append(info.glyphIndex);
308 positions.append(info.position);
310 QGlyphRun subNodeGlyphRun(m_glyphs);
311 subNodeGlyphRun.setGlyphIndexes(glyphIndexes);
312 subNodeGlyphRun.setPositions(positions);
314 subIt.value()->setGlyphs(m_originalPosition, subNodeGlyphRun);
315 subIt.value()->update();
316 subIt.value()->updateGeometry(); // we have to explicity call this now as preprocess won't be called before it's rendered
321 g->allocate(vp.size(), ip.size());
322 memcpy(g->vertexDataAsTexturedPoint2D(), vp.constData(), vp.size() * sizeof(QSGGeometry::TexturedPoint2D));
323 memcpy(g->indexDataAsUShort(), ip.constData(), ip.size() * sizeof(quint16));
325 setBoundingRect(m_boundingRect);
326 markDirty(DirtyGeometry);
327 m_dirtyGeometry = false;
329 m_material->setTexture(m_texture);
332 void QSGDistanceFieldGlyphNode::updateMaterial()
336 if (m_style == QQuickText::Normal) {
337 switch (m_antialiasingMode) {
338 case HighQualitySubPixelAntialiasing:
339 m_material = new QSGHiQSubPixelDistanceFieldTextMaterial;
341 case LowQualitySubPixelAntialiasing:
342 m_material = new QSGLoQSubPixelDistanceFieldTextMaterial;
344 case GrayAntialiasing:
346 m_material = new QSGDistanceFieldTextMaterial;
350 QSGDistanceFieldStyledTextMaterial *material;
351 if (m_style == QQuickText::Outline) {
352 material = new QSGDistanceFieldOutlineTextMaterial;
354 QSGDistanceFieldShiftedStyleTextMaterial *sMaterial = new QSGDistanceFieldShiftedStyleTextMaterial;
355 if (m_style == QQuickText::Raised)
356 sMaterial->setShift(QPointF(0.0, 1.0));
358 sMaterial->setShift(QPointF(0.0, -1.0));
359 material = sMaterial;
361 material->setStyleColor(m_styleColor);
362 m_material = material;
365 m_material->setGlyphCache(m_glyph_cache);
366 m_material->setColor(m_color);
367 setMaterial(m_material);
368 m_dirtyMaterial = false;