Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgdistancefieldglyphnode.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
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     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");
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         Q_ASSERT(ownerElement() != 0);
116         if (oldCache) {
117             oldCache->unregisterGlyphNode(this);
118             oldCache->unregisterOwnerElement(ownerElement());
119         }
120         m_glyph_cache->registerGlyphNode(this);
121         m_glyph_cache->registerOwnerElement(ownerElement());
122     }
123     m_glyph_cache->populate(glyphs.glyphIndexes());
124
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) {
128         GlyphInfo gi;
129         gi.glyphIndex = glyphIndexes.at(i);
130         gi.position = glyphPositions.at(i);
131         m_allGlyphs.append(gi);
132         m_allGlyphIndexesLookup.insert(gi.glyphIndex);
133     }
134
135     m_dirtyGeometry = true;
136     m_dirtyMaterial = true;
137 }
138
139 void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style)
140 {
141     if (m_style == style)
142         return;
143     m_style = style;
144     m_dirtyMaterial = true;
145 }
146
147 void QSGDistanceFieldGlyphNode::setStyleColor(const QColor &color)
148 {
149     if (m_styleColor == color)
150         return;
151     m_styleColor = color;
152     m_dirtyMaterial = true;
153 }
154
155 void QSGDistanceFieldGlyphNode::update()
156 {
157     if (m_dirtyMaterial)
158         updateMaterial();
159 }
160
161 void QSGDistanceFieldGlyphNode::preprocess()
162 {
163     Q_ASSERT(m_glyph_cache);
164
165     for (int i = 0; i < m_nodesToDelete.count(); ++i)
166         delete m_nodesToDelete.at(i);
167     m_nodesToDelete.clear();
168
169     m_glyph_cache->processPendingGlyphs();
170     m_glyph_cache->update();
171
172     if (m_dirtyGeometry)
173         updateGeometry();
174 }
175
176 void QSGDistanceFieldGlyphNode::invalidateGlyphs(const QVector<quint32> &glyphs)
177 {
178     if (m_dirtyGeometry)
179         return;
180
181     for (int i = 0; i < glyphs.count(); ++i) {
182         if (m_allGlyphIndexesLookup.contains(glyphs.at(i))) {
183             m_dirtyGeometry = true;
184             return;
185         }
186     }
187 }
188
189 void QSGDistanceFieldGlyphNode::updateGeometry()
190 {
191     Q_ASSERT(m_glyph_cache);
192
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);
201     }
202
203     QSGGeometry *g = geometry();
204
205     Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT);
206
207     QHash<const QSGDistanceFieldGlyphCache::Texture *, QList<GlyphInfo> > glyphsInOtherTextures;
208
209     QVector<QSGGeometry::TexturedPoint2D> vp;
210     vp.reserve(m_allGlyphs.size() * 4);
211     QVector<ushort> ip;
212     ip.reserve(m_allGlyphs.size() * 6);
213
214     QPointF margins(2, 2);
215     QPointF texMargins = margins / m_glyph_cache->fontScale();
216
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);
220
221         if (c.isNull())
222             continue;
223
224         const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphInfo.glyphIndex);
225         if (texture->textureId && !m_texture)
226             m_texture = texture;
227
228         if (m_texture != texture) {
229             if (texture->textureId)
230                 glyphsInOtherTextures[texture].append(glyphInfo);
231             continue;
232         }
233
234         QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphInfo.glyphIndex);
235
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;
245         }
246
247         qreal x = glyphInfo.position.x() + metrics.baselineX + m_position.x();
248         qreal y = glyphInfo.position.y() - metrics.baselineY + m_position.y();
249
250         m_boundingRect |= QRectF(x, y, metrics.width, metrics.height);
251
252         float cx1 = x;
253         float cx2 = x + metrics.width;
254         float cy1 = y;
255         float cy2 = y + metrics.height;
256
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;
261
262         if (m_baseLine.isNull())
263             m_baseLine = glyphInfo.position;
264
265         int o = vp.size();
266
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);
275         vp.append(v1);
276         vp.append(v2);
277         vp.append(v3);
278         vp.append(v4);
279
280         ip.append(o + 0);
281         ip.append(o + 2);
282         ip.append(o + 3);
283         ip.append(o + 3);
284         ip.append(o + 1);
285         ip.append(o + 0);
286     }
287
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);
297             subNode->update();
298             appendChildNode(subNode);
299             subIt = m_subNodes.insert(ite.key(), subNode);
300         }
301
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);
309         }
310         QGlyphRun subNodeGlyphRun(m_glyphs);
311         subNodeGlyphRun.setGlyphIndexes(glyphIndexes);
312         subNodeGlyphRun.setPositions(positions);
313
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
317
318         ++ite;
319     }
320
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));
324
325     setBoundingRect(m_boundingRect);
326     markDirty(DirtyGeometry);
327     m_dirtyGeometry = false;
328
329     m_material->setTexture(m_texture);
330 }
331
332 void QSGDistanceFieldGlyphNode::updateMaterial()
333 {
334     delete m_material;
335
336     if (m_style == QQuickText::Normal) {
337         switch (m_antialiasingMode) {
338         case HighQualitySubPixelAntialiasing:
339             m_material = new QSGHiQSubPixelDistanceFieldTextMaterial;
340             break;
341         case LowQualitySubPixelAntialiasing:
342             m_material = new QSGLoQSubPixelDistanceFieldTextMaterial;
343             break;
344         case GrayAntialiasing:
345         default:
346             m_material = new QSGDistanceFieldTextMaterial;
347             break;
348         }
349     } else {
350         QSGDistanceFieldStyledTextMaterial *material;
351         if (m_style == QQuickText::Outline) {
352             material = new QSGDistanceFieldOutlineTextMaterial;
353         } else {
354             QSGDistanceFieldShiftedStyleTextMaterial *sMaterial = new QSGDistanceFieldShiftedStyleTextMaterial;
355             if (m_style == QQuickText::Raised)
356                 sMaterial->setShift(QPointF(0.0, 1.0));
357             else
358                 sMaterial->setShift(QPointF(0.0, -1.0));
359             material = sMaterial;
360         }
361         material->setStyleColor(m_styleColor);
362         m_material = material;
363     }
364
365     m_material->setGlyphCache(m_glyph_cache);
366     m_material->setColor(m_color);
367     setMaterial(m_material);
368     m_dirtyMaterial = false;
369 }
370
371 QT_END_NAMESPACE