1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qsgadaptationlayer_p.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>
54 QHash<QString, QOpenGLMultiGroupSharedResource> QSGDistanceFieldGlyphCache::m_caches_data;
56 QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
60 Q_ASSERT(font.isValid());
63 m_cacheData = cacheData();
65 QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
66 m_glyphCount = fontD->fontEngine->glyphCount();
68 m_cacheData->doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT;
70 m_referenceFont = m_font;
71 m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution));
72 Q_ASSERT(m_referenceFont.isValid());
75 QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
79 QSGDistanceFieldGlyphCache::GlyphCacheData *QSGDistanceFieldGlyphCache::cacheData()
81 QString key = QString::fromLatin1("%1_%2_%3_%4")
82 .arg(m_font.familyName())
83 .arg(m_font.styleName())
86 return m_caches_data[key].value<QSGDistanceFieldGlyphCache::GlyphCacheData>(ctx);
89 qreal QSGDistanceFieldGlyphCache::fontScale() const
91 return qreal(m_font.pixelSize()) / QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution);
94 int QSGDistanceFieldGlyphCache::distanceFieldRadius() const
96 return QT_DISTANCEFIELD_DEFAULT_RADIUS / QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution);
99 QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph)
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();
107 m.width = br.width();
108 m.height = br.height();
109 m.baselineX = br.x();
110 m.baselineY = -br.y();
112 metric = m_metrics.insert(glyph, m);
115 return metric.value();
118 QSGDistanceFieldGlyphCache::TexCoord QSGDistanceFieldGlyphCache::glyphTexCoord(glyph_t glyph) const
120 return m_cacheData->texCoords.value(glyph);
123 static QSGDistanceFieldGlyphCache::Texture g_emptyTexture;
125 const QSGDistanceFieldGlyphCache::Texture *QSGDistanceFieldGlyphCache::glyphTexture(glyph_t glyph) const
127 QHash<glyph_t, Texture*>::const_iterator it = m_cacheData->glyphTextures.find(glyph);
128 if (it == m_cacheData->glyphTextures.constEnd())
129 return &g_emptyTexture;
133 void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs)
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);
145 ++m_cacheData->glyphRefCount[glyphIndex];
146 referencedGlyphs.insert(glyphIndex);
148 if (m_cacheData->texCoords.contains(glyphIndex) || newGlyphs.contains(glyphIndex))
151 QPainterPath path = m_referenceFont.pathForGlyph(glyphIndex);
152 m_cacheData->glyphPaths.insert(glyphIndex, path);
153 if (path.isEmpty()) {
157 m_cacheData->texCoords.insert(glyphIndex, c);
161 newGlyphs.insert(glyphIndex);
164 if (newGlyphs.isEmpty())
167 referenceGlyphs(referencedGlyphs);
168 requestGlyphs(newGlyphs);
171 void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs)
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);
180 releaseGlyphs(unusedGlyphs);
183 void QSGDistanceFieldGlyphCache::update()
185 if (m_cacheData->pendingGlyphs.isEmpty())
188 QHash<glyph_t, QImage> distanceFields;
190 // ### Remove before final release
191 static bool cacheDistanceFields = QGuiApplication::arguments().contains(QLatin1String("--cache-distance-fields"));
193 QString tmpPath = QString::fromLatin1("%1/.qt/").arg(QDir::tempPath());
194 QString keyBase = QString::fromLatin1("%1%2%3_%4_%5_%6.fontblob")
196 .arg(m_font.familyName())
197 .arg(m_font.styleName())
198 .arg(m_font.weight())
199 .arg(m_font.style());
201 if (cacheDistanceFields && !QFile::exists(tmpPath))
202 QDir(tmpPath).mkpath(tmpPath);
204 for (int i = 0; i < m_cacheData->pendingGlyphs.size(); ++i) {
205 glyph_t glyphIndex = m_cacheData->pendingGlyphs.at(i);
207 if (cacheDistanceFields) {
208 QString key = keyBase.arg(glyphIndex);
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);
221 QImage distanceField = qt_renderDistanceFieldGlyph(m_font, glyphIndex, m_cacheData->doubleGlyphResolution);
222 distanceFields.insert(glyphIndex, distanceField);
224 if (cacheDistanceFields) {
225 QString key = keyBase.arg(glyphIndex);
227 file.open(QFile::WriteOnly);
228 file.write((const char *) distanceField.constBits(), distanceField.width() * distanceField.height());
232 m_cacheData->pendingGlyphs.reset();
234 storeGlyphs(distanceFields);
237 void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
239 QVector<quint32> invalidatedGlyphs;
241 int count = glyphs.count();
242 for (int i = 0; i < count; ++i) {
243 GlyphPosition glyph = glyphs.at(i);
245 QPainterPath path = m_cacheData->glyphPaths.value(glyph.glyph);
246 QRectF br = path.boundingRect();
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();
255 if (m_cacheData->texCoords.contains(glyph.glyph))
256 invalidatedGlyphs.append(glyph.glyph);
258 m_cacheData->texCoords.insert(glyph.glyph, c);
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);
270 void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex)
272 int i = m_cacheData->textures.indexOf(tex);
274 m_cacheData->textures.append(tex);
275 i = m_cacheData->textures.size() - 1;
277 m_cacheData->textures[i].size = tex.size;
279 Texture *texture = &(m_cacheData->textures[i]);
281 QVector<quint32> invalidatedGlyphs;
283 int count = glyphs.count();
284 for (int j = 0; j < count; ++j) {
285 glyph_t glyphIndex = glyphs.at(j);
286 if (m_cacheData->glyphTextures.contains(glyphIndex))
287 invalidatedGlyphs.append(glyphIndex);
288 m_cacheData->glyphTextures.insert(glyphIndex, texture);
291 if (!invalidatedGlyphs.isEmpty()) {
292 QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin();
293 while (it != m_cacheData->m_registeredNodes.end()) {
294 (*it)->invalidateGlyphs(invalidatedGlyphs);
300 void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyphs)
302 int count = glyphs.count();
303 for (int i = 0; i < count; ++i)
304 m_cacheData->pendingGlyphs.add(glyphs.at(i));
307 void QSGDistanceFieldGlyphCache::removeGlyph(glyph_t glyph)
309 m_cacheData->texCoords.remove(glyph);
310 m_cacheData->glyphTextures.remove(glyph);
313 void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize)
315 int count = m_cacheData->textures.count();
316 for (int i = 0; i < count; ++i) {
317 Texture &tex = m_cacheData->textures[i];
318 if (tex.textureId == oldTex) {
319 tex.textureId = newTex;
320 tex.size = newTexSize;
326 bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) const
328 return m_cacheData->texCoords.contains(glyph);
331 void QSGDistanceFieldGlyphCache::registerGlyphNode(QSGDistanceFieldGlyphNode *node)
333 m_cacheData->m_registeredNodes.append(node);
336 void QSGDistanceFieldGlyphCache::unregisterGlyphNode(QSGDistanceFieldGlyphNode *node)
338 m_cacheData->m_registeredNodes.removeOne(node);