574c1218bb0fb3678d12180dbb35c412fef9a091
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / qsgadaptationlayer.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
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 "qsgadaptationlayer_p.h"
43
44 #include <qmath.h>
45 #include <QtQuick/private/qsgdistancefieldutil_p.h>
46 #include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
47 #include <private/qrawfont_p.h>
48 #include <QtGui/qguiapplication.h>
49 #include <qdir.h>
50
51 QT_BEGIN_NAMESPACE
52
53
54 QHash<QString, QOpenGLMultiGroupSharedResource> QSGDistanceFieldGlyphCache::m_caches_data;
55
56 QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
57     : ctx(c)
58     , m_manager(man)
59 {
60     Q_ASSERT(font.isValid());
61     m_font = font;
62
63     m_cacheData = cacheData();
64
65     QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
66     m_glyphCount = fontD->fontEngine->glyphCount();
67
68     m_cacheData->doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT;
69
70     m_referenceFont = m_font;
71     m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution));
72     Q_ASSERT(m_referenceFont.isValid());
73 }
74
75 QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
76 {
77 }
78
79 QSGDistanceFieldGlyphCache::GlyphCacheData *QSGDistanceFieldGlyphCache::cacheData()
80 {
81     QString key = QString::fromLatin1("%1_%2_%3_%4")
82             .arg(m_font.familyName())
83             .arg(m_font.styleName())
84             .arg(m_font.weight())
85             .arg(m_font.style());
86     return m_caches_data[key].value<QSGDistanceFieldGlyphCache::GlyphCacheData>(ctx);
87 }
88
89 qreal QSGDistanceFieldGlyphCache::fontScale() const
90 {
91     return qreal(m_font.pixelSize()) / QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution);
92 }
93
94 int QSGDistanceFieldGlyphCache::distanceFieldRadius() const
95 {
96     return QT_DISTANCEFIELD_DEFAULT_RADIUS / QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution);
97 }
98
99 QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph)
100 {
101     QHash<glyph_t, Metrics>::iterator metric = m_metrics.find(glyph);
102     if (metric == m_metrics.end()) {
103         QPainterPath path = m_font.pathForGlyph(glyph);
104         QRectF br = path.boundingRect();
105
106         Metrics m;
107         m.width = br.width();
108         m.height = br.height();
109         m.baselineX = br.x();
110         m.baselineY = -br.y();
111
112         metric = m_metrics.insert(glyph, m);
113     }
114
115     return metric.value();
116 }
117
118 QSGDistanceFieldGlyphCache::TexCoord QSGDistanceFieldGlyphCache::glyphTexCoord(glyph_t glyph) const
119 {
120     return m_cacheData->texCoords.value(glyph);
121 }
122
123 static QSGDistanceFieldGlyphCache::Texture g_emptyTexture;
124
125 const QSGDistanceFieldGlyphCache::Texture *QSGDistanceFieldGlyphCache::glyphTexture(glyph_t glyph) const
126 {
127     QHash<glyph_t, Texture*>::const_iterator it = m_cacheData->glyphTextures.find(glyph);
128     if (it == m_cacheData->glyphTextures.constEnd())
129         return &g_emptyTexture;
130     return it.value();
131 }
132
133 void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs)
134 {
135     QSet<glyph_t> referencedGlyphs;
136     QSet<glyph_t> newGlyphs;
137     int count = glyphs.count();
138     for (int i = 0; i < count; ++i) {
139         glyph_t glyphIndex = glyphs.at(i);
140         if ((int) glyphIndex >= glyphCount()) {
141             qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex);
142             continue;
143         }
144
145         ++m_cacheData->glyphRefCount[glyphIndex];
146         referencedGlyphs.insert(glyphIndex);
147
148         if (m_cacheData->texCoords.contains(glyphIndex) || newGlyphs.contains(glyphIndex))
149             continue;
150
151         QPainterPath path = m_referenceFont.pathForGlyph(glyphIndex);
152         m_cacheData->glyphPaths.insert(glyphIndex, path);
153         if (path.isEmpty()) {
154             TexCoord c;
155             c.width = 0;
156             c.height = 0;
157             m_cacheData->texCoords.insert(glyphIndex, c);
158             continue;
159         }
160
161         newGlyphs.insert(glyphIndex);
162     }
163
164     if (newGlyphs.isEmpty())
165         return;
166
167     referenceGlyphs(referencedGlyphs);
168     requestGlyphs(newGlyphs);
169 }
170
171 void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs)
172 {
173     QSet<glyph_t> unusedGlyphs;
174     int count = glyphs.count();
175     for (int i = 0; i < count; ++i) {
176         glyph_t glyphIndex = glyphs.at(i);
177         if (--m_cacheData->glyphRefCount[glyphIndex] == 0 && !glyphTexCoord(glyphIndex).isNull())
178             unusedGlyphs.insert(glyphIndex);
179     }
180     releaseGlyphs(unusedGlyphs);
181 }
182
183 void QSGDistanceFieldGlyphCache::update()
184 {
185     if (m_cacheData->pendingGlyphs.isEmpty())
186         return;
187
188     QHash<glyph_t, QImage> distanceFields;
189
190     // ### Remove before final release
191     static bool cacheDistanceFields = QGuiApplication::arguments().contains(QLatin1String("--cache-distance-fields"));
192
193     QString tmpPath = QString::fromLatin1("%1/.qt/").arg(QDir::tempPath());
194     QString keyBase = QString::fromLatin1("%1%2%3_%4_%5_%6.fontblob")
195             .arg(tmpPath)
196             .arg(m_font.familyName())
197             .arg(m_font.styleName())
198             .arg(m_font.weight())
199             .arg(m_font.style());
200
201     if (cacheDistanceFields && !QFile::exists(tmpPath))
202         QDir(tmpPath).mkpath(tmpPath);
203
204     for (int i = 0; i < m_cacheData->pendingGlyphs.size(); ++i) {
205         glyph_t glyphIndex = m_cacheData->pendingGlyphs.at(i);
206
207         if (cacheDistanceFields) {
208             QString key = keyBase.arg(glyphIndex);
209             QFile file(key);
210             if (file.open(QFile::ReadOnly)) {
211                 int fileSize = file.size();
212                 int dim = sqrt(float(fileSize));
213                 QByteArray blob = file.readAll();
214                 QImage df(dim, dim, QImage::Format_Indexed8);
215                 memcpy(df.bits(), blob.constData(), fileSize);
216                 distanceFields.insert(glyphIndex, df);
217                 continue;
218             }
219         }
220
221         QImage distanceField = qt_renderDistanceFieldGlyph(m_font, glyphIndex, m_cacheData->doubleGlyphResolution);
222         distanceFields.insert(glyphIndex, distanceField);
223
224         if (cacheDistanceFields) {
225             QString key = keyBase.arg(glyphIndex);
226             QFile file(key);
227             file.open(QFile::WriteOnly);
228             file.write((const char *) distanceField.constBits(), distanceField.width() * distanceField.height());
229         }
230     }
231
232     m_cacheData->pendingGlyphs.reset();
233
234     storeGlyphs(distanceFields);
235 }
236
237 void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
238 {
239     QVector<quint32> invalidatedGlyphs;
240
241     int count = glyphs.count();
242     for (int i = 0; i < count; ++i) {
243         GlyphPosition glyph = glyphs.at(i);
244
245         QPainterPath path = m_cacheData->glyphPaths.value(glyph.glyph);
246         QRectF br = path.boundingRect();
247         TexCoord c;
248         c.xMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution));
249         c.yMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution));
250         c.x = glyph.position.x();
251         c.y = glyph.position.y();
252         c.width = br.width();
253         c.height = br.height();
254
255         if (m_cacheData->texCoords.contains(glyph.glyph))
256             invalidatedGlyphs.append(glyph.glyph);
257
258         m_cacheData->texCoords.insert(glyph.glyph, c);
259     }
260
261     if (!invalidatedGlyphs.isEmpty()) {
262         QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin();
263         while (it != m_cacheData->m_registeredNodes.end()) {
264             (*it)->invalidateGlyphs(invalidatedGlyphs);
265             ++it;
266         }
267     }
268 }
269
270 void QSGDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
271 {
272     Q_UNUSED(ownerElement);
273 }
274
275 void QSGDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
276 {
277     Q_UNUSED(ownerElement);
278 }
279
280 void QSGDistanceFieldGlyphCache::processPendingGlyphs()
281 {
282     /* Intentionally empty */
283 }
284
285 void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex)
286 {
287     int i = m_cacheData->textures.indexOf(tex);
288     if (i == -1) {
289         m_cacheData->textures.append(tex);
290         i = m_cacheData->textures.size() - 1;
291     } else {
292         m_cacheData->textures[i].size = tex.size;
293     }
294     Texture *texture = &(m_cacheData->textures[i]);
295
296     QVector<quint32> invalidatedGlyphs;
297
298     int count = glyphs.count();
299     for (int j = 0; j < count; ++j) {
300         glyph_t glyphIndex = glyphs.at(j);
301         if (m_cacheData->glyphTextures.contains(glyphIndex))
302             invalidatedGlyphs.append(glyphIndex);
303         m_cacheData->glyphTextures.insert(glyphIndex, texture);
304     }
305
306     if (!invalidatedGlyphs.isEmpty()) {
307         QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin();
308         while (it != m_cacheData->m_registeredNodes.end()) {
309             (*it)->invalidateGlyphs(invalidatedGlyphs);
310             ++it;
311         }
312     }
313 }
314
315 void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyphs)
316 {
317     int count = glyphs.count();
318     for (int i = 0; i < count; ++i)
319         m_cacheData->pendingGlyphs.add(glyphs.at(i));
320 }
321
322 void QSGDistanceFieldGlyphCache::removeGlyph(glyph_t glyph)
323 {
324     m_cacheData->texCoords.remove(glyph);
325     m_cacheData->glyphTextures.remove(glyph);
326 }
327
328 void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize)
329 {
330     int count = m_cacheData->textures.count();
331     for (int i = 0; i < count; ++i) {
332         Texture &tex = m_cacheData->textures[i];
333         if (tex.textureId == oldTex) {
334             tex.textureId = newTex;
335             tex.size = newTexSize;
336             return;
337         }
338     }
339 }
340
341 bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) const
342 {
343     return m_cacheData->texCoords.contains(glyph);
344 }
345
346 void QSGDistanceFieldGlyphCache::registerGlyphNode(QSGDistanceFieldGlyphNode *node)
347 {
348     m_cacheData->m_registeredNodes.append(node);
349 }
350
351 void QSGDistanceFieldGlyphCache::unregisterGlyphNode(QSGDistanceFieldGlyphNode *node)
352 {
353     m_cacheData->m_registeredNodes.removeOne(node);
354 }
355
356
357 QT_END_NAMESPACE