Reference count glyphs in QSGDistanceFieldGlyphCache.
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgdistancefieldglyphnode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
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>
46
47 QT_BEGIN_NAMESPACE
48
49 QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGDistanceFieldGlyphCacheManager *cacheManager)
50     : m_material(0)
51     , m_glyph_cacheManager(cacheManager)
52     , m_glyph_cache(0)
53     , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
54     , m_style(QQuickText::Normal)
55     , m_antialiasingMode(GrayAntialiasing)
56     , m_texture(0)
57     , m_dirtyGeometry(false)
58     , m_dirtyMaterial(false)
59 {
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");
66 #endif
67 }
68
69 QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode()
70 {
71     delete m_material;
72
73     if (m_glyph_cache) {
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);
79     }
80
81     for (int i = 0; i < m_nodesToDelete.count(); ++i)
82         delete m_nodesToDelete.at(i);
83     m_nodesToDelete.clear();
84 }
85
86 void QSGDistanceFieldGlyphNode::setColor(const QColor &color)
87 {
88     m_color = color;
89     if (m_material != 0) {
90         m_material->setColor(color);
91         markDirty(DirtyMaterial);
92     } else {
93         m_dirtyMaterial = true;
94     }
95 }
96
97 void QSGDistanceFieldGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
98 {
99     if (mode == m_antialiasingMode)
100         return;
101     m_antialiasingMode = mode;
102     m_dirtyMaterial = true;
103 }
104
105 void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
106 {
107     QRawFont font = glyphs.rawFont();
108     m_originalPosition = position;
109     m_position = QPointF(position.x(), position.y() - font.ascent());
110     m_glyphs = glyphs;
111
112     QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache;
113     m_glyph_cache = m_glyph_cacheManager->cache(m_glyphs.rawFont());
114     if (m_glyph_cache != oldCache) {
115         if (oldCache)
116             oldCache->unregisterGlyphNode(this);
117         m_glyph_cache->registerGlyphNode(this);
118     }
119     m_glyph_cache->populate(glyphs.glyphIndexes());
120
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) {
124         GlyphInfo gi;
125         gi.glyphIndex = glyphIndexes.at(i);
126         gi.position = glyphPositions.at(i);
127         m_allGlyphs.append(gi);
128         m_allGlyphIndexesLookup.insert(gi.glyphIndex);
129     }
130
131     m_dirtyGeometry = true;
132     m_dirtyMaterial = true;
133 }
134
135 void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style)
136 {
137     if (m_style == style)
138         return;
139     m_style = style;
140     m_dirtyMaterial = true;
141 }
142
143 void QSGDistanceFieldGlyphNode::setStyleColor(const QColor &color)
144 {
145     if (m_styleColor == color)
146         return;
147     m_styleColor = color;
148     m_dirtyMaterial = true;
149 }
150
151 void QSGDistanceFieldGlyphNode::update()
152 {
153     if (m_dirtyMaterial)
154         updateMaterial();
155 }
156
157 void QSGDistanceFieldGlyphNode::preprocess()
158 {
159     Q_ASSERT(m_glyph_cache);
160
161     m_glyph_cache->update();
162
163     for (int i = 0; i < m_nodesToDelete.count(); ++i)
164         delete m_nodesToDelete.at(i);
165     m_nodesToDelete.clear();
166
167     if (m_dirtyGeometry)
168         updateGeometry();
169 }
170
171 void QSGDistanceFieldGlyphNode::invalidateGlyphs(const QVector<quint32> &glyphs)
172 {
173     if (m_dirtyGeometry)
174         return;
175
176     for (int i = 0; i < glyphs.count(); ++i) {
177         if (m_allGlyphIndexesLookup.contains(glyphs.at(i))) {
178             m_dirtyGeometry = true;
179             return;
180         }
181     }
182 }
183
184 void QSGDistanceFieldGlyphNode::updateGeometry()
185 {
186     Q_ASSERT(m_glyph_cache);
187
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);
196     }
197
198     QSGGeometry *g = geometry();
199
200     Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT);
201
202     QHash<const QSGDistanceFieldGlyphCache::Texture *, QList<GlyphInfo> > glyphsInOtherTextures;
203
204     QVector<QSGGeometry::TexturedPoint2D> vp;
205     vp.reserve(m_allGlyphs.size() * 4);
206     QVector<ushort> ip;
207     ip.reserve(m_allGlyphs.size() * 6);
208
209     QPointF margins(2, 2);
210     QPointF texMargins = margins / m_glyph_cache->fontScale();
211
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);
215
216         if (c.isNull())
217             continue;
218
219         const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphInfo.glyphIndex);
220         if (texture->textureId && !m_texture)
221             m_texture = texture;
222
223         if (m_texture != texture) {
224             if (texture->textureId)
225                 glyphsInOtherTextures[texture].append(glyphInfo);
226             continue;
227         }
228
229         QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphInfo.glyphIndex);
230
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;
240         }
241
242         qreal x = glyphInfo.position.x() + metrics.baselineX + m_position.x();
243         qreal y = glyphInfo.position.y() - metrics.baselineY + m_position.y();
244
245         m_boundingRect |= QRectF(x, y, metrics.width, metrics.height);
246
247         float cx1 = x;
248         float cx2 = x + metrics.width;
249         float cy1 = y;
250         float cy2 = y + metrics.height;
251
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;
256
257         if (m_baseLine.isNull())
258             m_baseLine = glyphInfo.position;
259
260         int o = vp.size();
261
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);
270         vp.append(v1);
271         vp.append(v2);
272         vp.append(v3);
273         vp.append(v4);
274
275         ip.append(o + 0);
276         ip.append(o + 2);
277         ip.append(o + 3);
278         ip.append(o + 3);
279         ip.append(o + 1);
280         ip.append(o + 0);
281     }
282
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);
291             subNode->update();
292             appendChildNode(subNode);
293             subIt = m_subNodes.insert(ite.key(), subNode);
294         }
295
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);
303         }
304         QGlyphRun subNodeGlyphRun(m_glyphs);
305         subNodeGlyphRun.setGlyphIndexes(glyphIndexes);
306         subNodeGlyphRun.setPositions(positions);
307
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
311
312         ++ite;
313     }
314
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));
318
319     setBoundingRect(m_boundingRect);
320     markDirty(DirtyGeometry);
321     m_dirtyGeometry = false;
322
323     m_material->setTexture(m_texture);
324 }
325
326 void QSGDistanceFieldGlyphNode::updateMaterial()
327 {
328     delete m_material;
329
330     if (m_style == QQuickText::Normal) {
331         switch (m_antialiasingMode) {
332         case HighQualitySubPixelAntialiasing:
333             m_material = new QSGHiQSubPixelDistanceFieldTextMaterial;
334             break;
335         case LowQualitySubPixelAntialiasing:
336             m_material = new QSGLoQSubPixelDistanceFieldTextMaterial;
337             break;
338         case GrayAntialiasing:
339         default:
340             m_material = new QSGDistanceFieldTextMaterial;
341             break;
342         }
343     } else {
344         QSGDistanceFieldStyledTextMaterial *material;
345         if (m_style == QQuickText::Outline) {
346             material = new QSGDistanceFieldOutlineTextMaterial;
347         } else {
348             QSGDistanceFieldShiftedStyleTextMaterial *sMaterial = new QSGDistanceFieldShiftedStyleTextMaterial;
349             if (m_style == QQuickText::Raised)
350                 sMaterial->setShift(QPointF(0.0, 1.0));
351             else
352                 sMaterial->setShift(QPointF(0.0, -1.0));
353             material = sMaterial;
354         }
355         material->setStyleColor(m_styleColor);
356         m_material = material;
357     }
358
359     m_material->setGlyphCache(m_glyph_cache);
360     m_material->setColor(m_color);
361     setMaterial(m_material);
362     m_dirtyMaterial = false;
363 }
364
365 QT_END_NAMESPACE