1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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.
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.
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.
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.
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 <private/qdistancefield_p.h>
49 #include <QtGui/qguiapplication.h>
55 QHash<QString, QOpenGLMultiGroupSharedResource> QSGDistanceFieldGlyphCache::m_caches_data;
57 QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font)
61 Q_ASSERT(font.isValid());
64 m_cacheData = cacheData();
66 QRawFontPrivate *fontD = QRawFontPrivate::get(m_font);
67 m_glyphCount = fontD->fontEngine->glyphCount();
69 m_cacheData->doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT;
71 m_referenceFont = m_font;
72 m_referenceFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution));
73 Q_ASSERT(m_referenceFont.isValid());
76 QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
80 QSGDistanceFieldGlyphCache::GlyphCacheData *QSGDistanceFieldGlyphCache::cacheData()
82 QString key = QString::fromLatin1("%1_%2_%3_%4")
83 .arg(m_font.familyName())
84 .arg(m_font.styleName())
87 return m_caches_data[key].value<QSGDistanceFieldGlyphCache::GlyphCacheData>(ctx);
90 qreal QSGDistanceFieldGlyphCache::fontScale() const
92 return qreal(m_font.pixelSize()) / QT_DISTANCEFIELD_BASEFONTSIZE(m_cacheData->doubleGlyphResolution);
95 int QSGDistanceFieldGlyphCache::distanceFieldRadius() const
97 return QT_DISTANCEFIELD_DEFAULT_RADIUS / QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution);
100 QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph)
102 QHash<glyph_t, Metrics>::iterator metric = m_metrics.find(glyph);
103 if (metric == m_metrics.end()) {
104 QPainterPath path = m_font.pathForGlyph(glyph);
105 QRectF br = path.boundingRect();
108 m.width = br.width();
109 m.height = br.height();
110 m.baselineX = br.x();
111 m.baselineY = -br.y();
113 metric = m_metrics.insert(glyph, m);
116 return metric.value();
119 QSGDistanceFieldGlyphCache::TexCoord QSGDistanceFieldGlyphCache::glyphTexCoord(glyph_t glyph) const
121 return m_cacheData->texCoords.value(glyph);
124 static QSGDistanceFieldGlyphCache::Texture g_emptyTexture;
126 const QSGDistanceFieldGlyphCache::Texture *QSGDistanceFieldGlyphCache::glyphTexture(glyph_t glyph) const
128 QHash<glyph_t, Texture*>::const_iterator it = m_cacheData->glyphTextures.find(glyph);
129 if (it == m_cacheData->glyphTextures.constEnd())
130 return &g_emptyTexture;
134 void QSGDistanceFieldGlyphCache::populate(const QVector<glyph_t> &glyphs)
136 QSet<glyph_t> referencedGlyphs;
137 QSet<glyph_t> newGlyphs;
138 int count = glyphs.count();
139 for (int i = 0; i < count; ++i) {
140 glyph_t glyphIndex = glyphs.at(i);
141 if ((int) glyphIndex >= glyphCount()) {
142 qWarning("Warning: distance-field glyph is not available with index %d", glyphIndex);
146 ++m_cacheData->glyphRefCount[glyphIndex];
147 referencedGlyphs.insert(glyphIndex);
149 if (m_cacheData->texCoords.contains(glyphIndex) || newGlyphs.contains(glyphIndex))
152 QPainterPath path = m_referenceFont.pathForGlyph(glyphIndex);
153 m_cacheData->glyphPaths.insert(glyphIndex, path);
154 if (path.isEmpty()) {
158 m_cacheData->texCoords.insert(glyphIndex, c);
162 newGlyphs.insert(glyphIndex);
165 if (newGlyphs.isEmpty())
168 referenceGlyphs(referencedGlyphs);
169 requestGlyphs(newGlyphs);
172 void QSGDistanceFieldGlyphCache::release(const QVector<glyph_t> &glyphs)
174 QSet<glyph_t> unusedGlyphs;
175 int count = glyphs.count();
176 for (int i = 0; i < count; ++i) {
177 glyph_t glyphIndex = glyphs.at(i);
178 if (--m_cacheData->glyphRefCount[glyphIndex] == 0 && !glyphTexCoord(glyphIndex).isNull())
179 unusedGlyphs.insert(glyphIndex);
181 releaseGlyphs(unusedGlyphs);
184 void QSGDistanceFieldGlyphCache::update()
186 if (m_cacheData->pendingGlyphs.isEmpty())
189 QHash<glyph_t, QImage> distanceFields;
191 // ### Remove before final release
192 static bool cacheDistanceFields = QGuiApplication::arguments().contains(QLatin1String("--cache-distance-fields"));
194 QString tmpPath = QString::fromLatin1("%1/.qt/").arg(QDir::tempPath());
195 QString keyBase = QString::fromLatin1("%1%2%3_%4_%5_%6.fontblob")
197 .arg(m_font.familyName())
198 .arg(m_font.styleName())
199 .arg(m_font.weight())
200 .arg(m_font.style());
202 if (cacheDistanceFields && !QFile::exists(tmpPath))
203 QDir(tmpPath).mkpath(tmpPath);
205 for (int i = 0; i < m_cacheData->pendingGlyphs.size(); ++i) {
206 glyph_t glyphIndex = m_cacheData->pendingGlyphs.at(i);
208 if (cacheDistanceFields) {
209 QString key = keyBase.arg(glyphIndex);
211 if (file.open(QFile::ReadOnly)) {
212 int fileSize = file.size();
213 int dim = sqrt(float(fileSize));
214 QByteArray blob = file.readAll();
215 QImage df(dim, dim, QImage::Format_Indexed8);
216 memcpy(df.bits(), blob.constData(), fileSize);
217 distanceFields.insert(glyphIndex, df);
222 QImage distanceField = qt_renderDistanceFieldGlyph(m_font, glyphIndex, m_cacheData->doubleGlyphResolution);
223 distanceFields.insert(glyphIndex, distanceField);
225 if (cacheDistanceFields) {
226 QString key = keyBase.arg(glyphIndex);
228 file.open(QFile::WriteOnly);
229 file.write((const char *) distanceField.constBits(), distanceField.width() * distanceField.height());
233 m_cacheData->pendingGlyphs.reset();
235 storeGlyphs(distanceFields);
238 void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
240 QVector<quint32> invalidatedGlyphs;
242 int count = glyphs.count();
243 for (int i = 0; i < count; ++i) {
244 GlyphPosition glyph = glyphs.at(i);
246 QPainterPath path = m_cacheData->glyphPaths.value(glyph.glyph);
247 QRectF br = path.boundingRect();
249 c.xMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution));
250 c.yMargin = QT_DISTANCEFIELD_RADIUS(m_cacheData->doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_cacheData->doubleGlyphResolution));
251 c.x = glyph.position.x();
252 c.y = glyph.position.y();
253 c.width = br.width();
254 c.height = br.height();
256 if (m_cacheData->texCoords.contains(glyph.glyph))
257 invalidatedGlyphs.append(glyph.glyph);
259 m_cacheData->texCoords.insert(glyph.glyph, c);
262 if (!invalidatedGlyphs.isEmpty()) {
263 QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin();
264 while (it != m_cacheData->m_registeredNodes.end()) {
265 (*it)->invalidateGlyphs(invalidatedGlyphs);
271 void QSGDistanceFieldGlyphCache::registerOwnerElement(QQuickItem *ownerElement)
273 Q_UNUSED(ownerElement);
276 void QSGDistanceFieldGlyphCache::unregisterOwnerElement(QQuickItem *ownerElement)
278 Q_UNUSED(ownerElement);
281 void QSGDistanceFieldGlyphCache::processPendingGlyphs()
283 /* Intentionally empty */
286 void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QVector<glyph_t> &glyphs, const Texture &tex)
288 int i = m_cacheData->textures.indexOf(tex);
290 m_cacheData->textures.append(tex);
291 i = m_cacheData->textures.size() - 1;
293 m_cacheData->textures[i].size = tex.size;
295 Texture *texture = &(m_cacheData->textures[i]);
297 QVector<quint32> invalidatedGlyphs;
299 int count = glyphs.count();
300 for (int j = 0; j < count; ++j) {
301 glyph_t glyphIndex = glyphs.at(j);
302 if (m_cacheData->glyphTextures.contains(glyphIndex))
303 invalidatedGlyphs.append(glyphIndex);
304 m_cacheData->glyphTextures.insert(glyphIndex, texture);
307 if (!invalidatedGlyphs.isEmpty()) {
308 QLinkedList<QSGDistanceFieldGlyphNode *>::iterator it = m_cacheData->m_registeredNodes.begin();
309 while (it != m_cacheData->m_registeredNodes.end()) {
310 (*it)->invalidateGlyphs(invalidatedGlyphs);
316 void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QVector<glyph_t> &glyphs)
318 int count = glyphs.count();
319 for (int i = 0; i < count; ++i)
320 m_cacheData->pendingGlyphs.add(glyphs.at(i));
323 void QSGDistanceFieldGlyphCache::removeGlyph(glyph_t glyph)
325 m_cacheData->texCoords.remove(glyph);
326 m_cacheData->glyphTextures.remove(glyph);
329 void QSGDistanceFieldGlyphCache::updateTexture(GLuint oldTex, GLuint newTex, const QSize &newTexSize)
331 int count = m_cacheData->textures.count();
332 for (int i = 0; i < count; ++i) {
333 Texture &tex = m_cacheData->textures[i];
334 if (tex.textureId == oldTex) {
335 tex.textureId = newTex;
336 tex.size = newTexSize;
342 bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) const
344 return m_cacheData->texCoords.contains(glyph);
347 void QSGDistanceFieldGlyphCache::registerGlyphNode(QSGDistanceFieldGlyphNode *node)
349 m_cacheData->m_registeredNodes.append(node);
352 void QSGDistanceFieldGlyphCache::unregisterGlyphNode(QSGDistanceFieldGlyphNode *node)
354 m_cacheData->m_registeredNodes.removeOne(node);