1/****************************************************************************
2**
3** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QtGui/qrawfont.h>
41#include <QtGui/qglyphrun.h>
42#include <QtGui/private/qrawfont_p.h>
43
44#include "qdistancefieldglyphcache_p.h"
45#include "qtextureatlas_p.h"
46
47#include <QtGui/qfont.h>
48#include <QtGui/private/qdistancefield_p.h>
49#include <Qt3DCore/private/qnode_p.h>
50#include <Qt3DExtras/private/qtextureatlas_p.h>
51
52QT_BEGIN_NAMESPACE
53
54#define DEFAULT_IMAGE_PADDING 1
55
56using namespace Qt3DCore;
57
58namespace Qt3DExtras {
59
60// ref-count glyphs and keep track of where they are stored
61class StoredGlyph {
62public:
63 StoredGlyph() = default;
64 StoredGlyph(const StoredGlyph &) = default;
65 StoredGlyph(const QRawFont &font, quint32 glyph, bool doubleResolution);
66
67 int refCount() const { return m_ref; }
68 void ref() { ++m_ref; }
69 int deref() { return m_ref = std::max(m_ref - 1, (quint32) 0); }
70
71 bool addToTextureAtlas(QTextureAtlas *atlas);
72 void removeFromTextureAtlas();
73
74 QTextureAtlas *atlas() const { return m_atlas; }
75 QRectF glyphPathBoundingRect() const { return m_glyphPathBoundingRect; }
76 QRectF texCoords() const;
77
78private:
79 quint32 m_ref = 0;
80 QTextureAtlas *m_atlas = nullptr;
81 QTextureAtlas::TextureId m_atlasEntry = QTextureAtlas::InvalidTexture;
82 QRectF m_glyphPathBoundingRect;
83 QImage m_distanceFieldImage; // only used until added to texture atlas
84};
85
86// A DistanceFieldFont stores all glyphs for a given QRawFont.
87// it will use multiple QTextureAtlasess to store the distance
88// fields and uses ref-counting for each glyph to ensure that
89// unused glyphs are removed from the texture atlasses.
90class DistanceFieldFont
91{
92public:
93 DistanceFieldFont(const QRawFont &font, bool doubleRes, Qt3DCore::QNode *parent);
94 ~DistanceFieldFont();
95
96 StoredGlyph findGlyph(quint32 glyph) const;
97 StoredGlyph refGlyph(quint32 glyph);
98 void derefGlyph(quint32 glyph);
99
100 bool doubleGlyphResolution() const { return m_doubleGlyphResolution; }
101
102private:
103 QRawFont m_font;
104 bool m_doubleGlyphResolution;
105 Qt3DCore::QNode *m_parentNode; // parent node for the QTextureAtlasses
106
107 QHash<quint32, StoredGlyph> m_glyphs;
108
109 QVector<QTextureAtlas*> m_atlasses;
110};
111
112StoredGlyph::StoredGlyph(const QRawFont &font, quint32 glyph, bool doubleResolution)
113 : m_ref(1)
114 , m_atlas(nullptr)
115 , m_atlasEntry(QTextureAtlas::InvalidTexture)
116{
117 // create new single-channel distance field image for given glyph
118 const QPainterPath path = font.pathForGlyph(glyph);
119 const QDistanceField dfield(font, glyph, doubleResolution);
120 m_distanceFieldImage = dfield.toImage(QImage::Format_Alpha8);
121
122 // scale bounding rect down (as in QSGDistanceFieldGlyphCache::glyphData())
123 const QRectF pathBound = path.boundingRect();
124 float f = 1.0f / QT_DISTANCEFIELD_SCALE(doubleResolution);
125 m_glyphPathBoundingRect = QRectF(pathBound.left() * f, -pathBound.top() * f, pathBound.width() * f, pathBound.height() * f);
126}
127
128bool StoredGlyph::addToTextureAtlas(QTextureAtlas *atlas)
129{
130 if (m_atlas || m_distanceFieldImage.isNull())
131 return false;
132
133 const auto texId = atlas->addImage(m_distanceFieldImage, DEFAULT_IMAGE_PADDING);
134 if (texId != QTextureAtlas::InvalidTexture) {
135 m_atlas = atlas;
136 m_atlasEntry = texId;
137 m_distanceFieldImage = QImage(); // free glyph image data
138 return true;
139 }
140
141 return false;
142}
143
144void StoredGlyph::removeFromTextureAtlas()
145{
146 if (m_atlas) {
147 m_atlas->removeImage(m_atlasEntry);
148 m_atlas = nullptr;
149 m_atlasEntry = QTextureAtlas::InvalidTexture;
150 }
151}
152
153QRectF StoredGlyph::texCoords() const
154{
155 return m_atlas ? m_atlas->imageTexCoords(m_atlasEntry) : QRectF();
156}
157
158DistanceFieldFont::DistanceFieldFont(const QRawFont &font, bool doubleRes, Qt3DCore::QNode *parent)
159 : m_font(font)
160 , m_doubleGlyphResolution(doubleRes)
161 , m_parentNode(parent)
162{
163}
164
165DistanceFieldFont::~DistanceFieldFont()
166{
167 qDeleteAll(m_atlasses);
168}
169
170StoredGlyph DistanceFieldFont::findGlyph(quint32 glyph) const
171{
172 const auto it = m_glyphs.find(glyph);
173 return (it != m_glyphs.cend()) ? it.value() : StoredGlyph();
174}
175
176StoredGlyph DistanceFieldFont::refGlyph(quint32 glyph)
177{
178 // if glyph already exists, just increase ref-count
179 auto it = m_glyphs.find(glyph);
180 if (it != m_glyphs.end()) {
181 it.value().ref();
182 return it.value();
183 }
184
185 // need to create new glyph
186 StoredGlyph storedGlyph(m_font, glyph, m_doubleGlyphResolution);
187
188 // see if one of the existing atlasses can hold the distance field image
189 for (int i = 0; i < m_atlasses.size(); i++)
190 if (storedGlyph.addToTextureAtlas(m_atlasses[i]))
191 break;
192
193 // if no texture atlas is big enough (or no exists yet), allocate a new one
194 if (!storedGlyph.atlas()) {
195 // this should be enough to store 40-60 glyphs, which should be sufficient for most
196 // scenarios
197 const int size = m_doubleGlyphResolution ? 512 : 256;
198
199 QTextureAtlas *atlas = new QTextureAtlas(m_parentNode);
200 atlas->setWidth(size);
201 atlas->setHeight(size);
202 atlas->setFormat(Qt3DRender::QAbstractTexture::R8_UNorm);
203 atlas->setPixelFormat(QOpenGLTexture::Red);
204 atlas->setMinificationFilter(Qt3DRender::QAbstractTexture::Linear);
205 atlas->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
206 m_atlasses << atlas;
207
208 if (!storedGlyph.addToTextureAtlas(atlas))
209 qWarning() << Q_FUNC_INFO << "Couldn't add glyph to newly allocated atlas. Glyph could be huge?";
210 }
211
212 m_glyphs.insert(glyph, storedGlyph);
213 return storedGlyph;
214}
215
216void DistanceFieldFont::derefGlyph(quint32 glyph)
217{
218 auto it = m_glyphs.find(glyph);
219 if (it == m_glyphs.end())
220 return;
221
222 // TODO
223 // possible optimization: keep unreferenced glyphs as the texture atlas
224 // still has space. only if a new glyph needs to be allocated, and there
225 // is no more space within the atlas, then we can actually remove the glyphs
226 // from the atlasses.
227
228 // remove glyph if no refs anymore
229 if (it.value().deref() <= 0) {
230 QTextureAtlas *atlas = it.value().atlas();
231 it.value().removeFromTextureAtlas();
232
233 // remove atlas, if it contains no glyphs anymore
234 if (atlas && atlas->imageCount() == 0) {
235 Q_ASSERT(m_atlasses.contains(atlas));
236
237 m_atlasses.removeAll(atlas);
238 delete atlas;
239 }
240
241 m_glyphs.erase(it);
242 }
243}
244
245// copied from QSGDistanceFieldGlyphCacheManager::fontKey
246// we use this function to compare QRawFonts, as QRawFont doesn't
247// implement a stable comparison function
248QString QDistanceFieldGlyphCache::fontKey(const QRawFont &font)
249{
250 QFontEngine *fe = QRawFontPrivate::get(font)->fontEngine;
251 if (!fe->faceId().filename.isEmpty()) {
252 QByteArray keyName = fe->faceId().filename;
253 if (font.style() != QFont::StyleNormal)
254 keyName += QByteArray(" I");
255 if (font.weight() != QFont::Normal)
256 keyName += ' ' + QByteArray::number(font.weight());
257 keyName += QByteArray(" DF");
258 return QString::fromUtf8(keyName);
259 } else {
260 return QString::fromLatin1("%1_%2_%3_%4")
261 .arg(font.familyName())
262 .arg(font.styleName())
263 .arg(font.weight())
264 .arg(font.style());
265 }
266}
267
268DistanceFieldFont* QDistanceFieldGlyphCache::getOrCreateDistanceFieldFont(const QRawFont &font)
269{
270 // return, if font already exists (make sure to only create one DistanceFieldFont for
271 // each unique QRawFont, by building a hash on the QRawFont that ignores the font size)
272 const QString key = fontKey(font);
273 const auto it = m_fonts.constFind(key);
274 if (it != m_fonts.cend())
275 return it.value();
276
277 // logic taken from QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache
278 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
279 const int glyphCount = fontD->fontEngine->glyphCount();
280 const bool useDoubleRes = qt_fontHasNarrowOutlines(font) && glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
281
282 // only keep one FontCache with a fixed pixel size for each distinct font type
283 QRawFont actualFont = font;
284 actualFont.setPixelSize(QT_DISTANCEFIELD_BASEFONTSIZE(useDoubleRes) * QT_DISTANCEFIELD_SCALE(useDoubleRes));
285
286 // create new font cache
287 DistanceFieldFont *dff = new DistanceFieldFont(actualFont, useDoubleRes, m_rootNode);
288 m_fonts.insert(key, dff);
289 return dff;
290}
291
292QDistanceFieldGlyphCache::QDistanceFieldGlyphCache()
293 : m_rootNode(nullptr)
294{
295}
296
297QDistanceFieldGlyphCache::~QDistanceFieldGlyphCache()
298{
299}
300
301void QDistanceFieldGlyphCache::setRootNode(QNode *rootNode)
302{
303 m_rootNode = rootNode;
304}
305
306QNode *QDistanceFieldGlyphCache::rootNode() const
307{
308 return m_rootNode;
309}
310
311bool QDistanceFieldGlyphCache::doubleGlyphResolution(const QRawFont &font)
312{
313 return getOrCreateDistanceFieldFont(font)->doubleGlyphResolution();
314}
315
316namespace {
317QDistanceFieldGlyphCache::Glyph refAndGetGlyph(DistanceFieldFont *dff, quint32 glyph)
318{
319 QDistanceFieldGlyphCache::Glyph ret;
320
321 if (dff) {
322 const auto entry = dff->refGlyph(glyph);
323
324 if (entry.atlas()) {
325 ret.glyphPathBoundingRect = entry.glyphPathBoundingRect();
326 ret.texCoords = entry.texCoords();
327 ret.texture = entry.atlas();
328 }
329 }
330
331 return ret;
332}
333} // anonymous
334
335QVector<QDistanceFieldGlyphCache::Glyph> QDistanceFieldGlyphCache::refGlyphs(const QGlyphRun &run)
336{
337 DistanceFieldFont *dff = getOrCreateDistanceFieldFont(run.rawFont());
338 QVector<QDistanceFieldGlyphCache::Glyph> ret;
339
340 const QVector<quint32> glyphs = run.glyphIndexes();
341 for (quint32 glyph : glyphs)
342 ret << refAndGetGlyph(dff, glyph);
343
344 return ret;
345}
346
347QDistanceFieldGlyphCache::Glyph QDistanceFieldGlyphCache::refGlyph(const QRawFont &font, quint32 glyph)
348{
349 return refAndGetGlyph(getOrCreateDistanceFieldFont(font), glyph);
350}
351
352void QDistanceFieldGlyphCache::derefGlyphs(const QGlyphRun &run)
353{
354 DistanceFieldFont *dff = getOrCreateDistanceFieldFont(run.rawFont());
355
356 const QVector<quint32> glyphs = run.glyphIndexes();
357 for (quint32 glyph : glyphs)
358 dff->derefGlyph(glyph);
359}
360
361void QDistanceFieldGlyphCache::derefGlyph(const QRawFont &font, quint32 glyph)
362{
363 getOrCreateDistanceFieldFont(font)->derefGlyph(glyph);
364}
365
366} // namespace Qt3DExtras
367
368QT_END_NAMESPACE
369