Say hello to QtQuick module
[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_dirtyGeometry(false)
57     , m_dirtyMaterial(false)
58 {
59     m_geometry.setDrawingMode(GL_TRIANGLES);
60     setGeometry(&m_geometry);
61     setPreferredAntialiasingMode(cacheManager->defaultAntialiasingMode());
62 #ifdef QML_RUNTIME_TESTING
63     description = QLatin1String("glyphs");
64 #endif
65 }
66
67 QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode()
68 {
69     delete m_material;
70     if (m_glyph_cache) {
71         m_glyph_cache->release(m_glyphs.glyphIndexes());
72         m_glyph_cache->unregisterGlyphNode(this);
73     }
74 }
75
76 void QSGDistanceFieldGlyphNode::setColor(const QColor &color)
77 {
78     m_color = color;
79     if (m_material != 0) {
80         m_material->setColor(color);
81         markDirty(DirtyMaterial);
82     }
83 }
84
85 void QSGDistanceFieldGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
86 {
87     if (mode == m_antialiasingMode)
88         return;
89     m_antialiasingMode = mode;
90     m_dirtyMaterial = true;
91 }
92
93 void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
94 {
95     QRawFont font = glyphs.rawFont();
96     m_position = QPointF(position.x(), position.y() - font.ascent());
97     m_glyphs = glyphs;
98
99     QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache;
100     m_glyph_cache = m_glyph_cacheManager->cache(m_glyphs.rawFont());
101     if (m_glyph_cache != oldCache) {
102         if (oldCache)
103             oldCache->unregisterGlyphNode(this);
104         m_glyph_cache->registerGlyphNode(this);
105     }
106     m_glyph_cache->populate(glyphs.glyphIndexes());
107
108     const QVector<quint32> &glyphIndexes = m_glyphs.glyphIndexes();
109     const QVector<QPointF> &glyphPositions = m_glyphs.positions();
110     for (int i = 0; i < glyphIndexes.size(); ++i) {
111         GlyphInfo g;
112         g.glyphIndex = glyphIndexes.at(i);
113         g.position = glyphPositions.at(i);
114         m_glyphsToAdd.append(g);
115     }
116
117     m_dirtyGeometry = true;
118     m_dirtyMaterial = true;
119 }
120
121 void QSGDistanceFieldGlyphNode::setStyle(QQuickText::TextStyle style)
122 {
123     if (m_style == style)
124         return;
125     m_style = style;
126     m_dirtyMaterial = true;
127 }
128
129 void QSGDistanceFieldGlyphNode::setStyleColor(const QColor &color)
130 {
131     if (m_styleColor == color)
132         return;
133     m_styleColor = color;
134     m_dirtyMaterial = true;
135 }
136
137 void QSGDistanceFieldGlyphNode::update()
138 {
139     if (m_dirtyMaterial)
140         updateMaterial();
141     if (m_dirtyGeometry)
142         updateGeometry();
143 }
144
145 void QSGDistanceFieldGlyphNode::updateGeometry()
146 {
147     Q_ASSERT(m_glyph_cache);
148
149     if (m_glyphsToAdd.isEmpty())
150         return;
151
152     QSGGeometry *g = geometry();
153
154     Q_ASSERT(g->indexType() == GL_UNSIGNED_SHORT);
155
156     int oldVertexCount = g->vertexCount();
157     int oldIndexCount = g->indexCount();
158
159     QVector<QSGGeometry::TexturedPoint2D> vp;
160     vp.reserve(m_glyphsToAdd.size() * 4);
161     QVector<ushort> ip;
162     ip.reserve(m_glyphsToAdd.size() * 6);
163
164     QPointF margins(2, 2);
165     QPointF texMargins = margins / m_glyph_cache->fontScale();
166
167     const QSGDistanceFieldGlyphCache::Texture *textureToUse = 0;
168
169     QLinkedList<GlyphInfo>::iterator it = m_glyphsToAdd.begin();
170     while (it != m_glyphsToAdd.end()) {
171         quint32 glyphIndex = it->glyphIndex;
172         QSGDistanceFieldGlyphCache::TexCoord c = m_glyph_cache->glyphTexCoord(glyphIndex);
173
174         if (c.isNull()) {
175             if (!c.isValid())
176                 ++it;
177             else
178                 it = m_glyphsToAdd.erase(it);
179             continue;
180         }
181
182         const QSGDistanceFieldGlyphCache::Texture *texture = m_glyph_cache->glyphTexture(glyphIndex);
183         if (!texture->textureId) {
184             ++it;
185             continue;
186         }
187
188         QSGDistanceFieldGlyphCache::Metrics metrics = m_glyph_cache->glyphMetrics(glyphIndex);
189
190         if (!textureToUse)
191             textureToUse = texture;
192
193         metrics.width += margins.x() * 2;
194         metrics.height += margins.y() * 2;
195         metrics.baselineX -= margins.x();
196         metrics.baselineY += margins.y();
197         c.xMargin -= texMargins.x();
198         c.yMargin -= texMargins.y();
199         c.width += texMargins.x() * 2;
200         c.height += texMargins.y() * 2;
201
202         const QPointF &glyphPosition = it->position;
203         qreal x = glyphPosition.x() + metrics.baselineX + m_position.x();
204         qreal y = glyphPosition.y() - metrics.baselineY + m_position.y();
205
206         m_boundingRect |= QRectF(x, y, metrics.width, metrics.height);
207
208         float cx1 = x;
209         float cx2 = x + metrics.width;
210         float cy1 = y;
211         float cy2 = y + metrics.height;
212
213         float tx1 = c.x + c.xMargin;
214         float tx2 = tx1 + c.width;
215         float ty1 = c.y + c.yMargin;
216         float ty2 = ty1 + c.height;
217
218         if (m_baseLine.isNull())
219             m_baseLine = glyphPosition;
220
221         int i = vp.size();
222
223         QSGGeometry::TexturedPoint2D v1;
224         v1.set(cx1, cy1, tx1, ty1);
225         QSGGeometry::TexturedPoint2D v2;
226         v2.set(cx2, cy1, tx2, ty1);
227         QSGGeometry::TexturedPoint2D v3;
228         v3.set(cx1, cy2, tx1, ty2);
229         QSGGeometry::TexturedPoint2D v4;
230         v4.set(cx2, cy2, tx2, ty2);
231         vp.append(v1);
232         vp.append(v2);
233         vp.append(v3);
234         vp.append(v4);
235
236         int o = i + oldVertexCount;
237         ip.append(o + 0);
238         ip.append(o + 2);
239         ip.append(o + 3);
240         ip.append(o + 3);
241         ip.append(o + 1);
242         ip.append(o + 0);
243
244         it = m_glyphsToAdd.erase(it);
245     }
246
247     if (vp.isEmpty())
248         return;
249
250     void *data = 0;
251     if (oldVertexCount && oldIndexCount) {
252         int byteSize = oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D)
253                      + oldIndexCount * sizeof(quint16);
254         data = qMalloc(byteSize);
255         memcpy(data, g->vertexData(), byteSize);
256     }
257
258     g->allocate(oldVertexCount + vp.size(), oldIndexCount + ip.size());
259
260     if (data) {
261         memcpy(g->vertexData(), data, oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D));
262         memcpy(g->indexData(), ((char *) data) + oldVertexCount * sizeof(QSGGeometry::TexturedPoint2D),
263                oldIndexCount * sizeof(quint16));
264         qFree(data);
265     }
266
267     memcpy(g->vertexDataAsTexturedPoint2D() + oldVertexCount, vp.constData(), vp.size() * sizeof(QSGGeometry::TexturedPoint2D));
268     memcpy(g->indexDataAsUShort() + oldIndexCount, ip.constData(), ip.size() * sizeof(quint16));
269
270     setBoundingRect(m_boundingRect);
271     markDirty(DirtyGeometry);
272     m_dirtyGeometry = false;
273
274     m_material->setTexture(textureToUse);
275 }
276
277 void QSGDistanceFieldGlyphNode::updateMaterial()
278 {
279     delete m_material;
280
281     if (m_style == QQuickText::Normal) {
282         switch (m_antialiasingMode) {
283         case HighQualitySubPixelAntialiasing:
284             m_material = new QSGHiQSubPixelDistanceFieldTextMaterial;
285             break;
286         case LowQualitySubPixelAntialiasing:
287             m_material = new QSGLoQSubPixelDistanceFieldTextMaterial;
288             break;
289         case GrayAntialiasing:
290         default:
291             m_material = new QSGDistanceFieldTextMaterial;
292             break;
293         }
294     } else {
295         QSGDistanceFieldStyledTextMaterial *material;
296         if (m_style == QQuickText::Outline) {
297             material = new QSGDistanceFieldOutlineTextMaterial;
298         } else {
299             QSGDistanceFieldShiftedStyleTextMaterial *sMaterial = new QSGDistanceFieldShiftedStyleTextMaterial;
300             if (m_style == QQuickText::Raised)
301                 sMaterial->setShift(QPointF(0.0, 1.0));
302             else
303                 sMaterial->setShift(QPointF(0.0, -1.0));
304             material = sMaterial;
305         }
306         material->setStyleColor(m_styleColor);
307         m_material = material;
308     }
309
310     m_material->setGlyphCache(m_glyph_cache);
311     m_material->setColor(m_color);
312     setMaterial(m_material);
313     m_dirtyMaterial = false;
314 }
315
316 QT_END_NAMESPACE