summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2019-02-15 14:25:10 +0100
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2019-02-21 09:04:19 +0000
commit450840717b15001be5b527bdd88eeb9b9bf6caee (patch)
tree03679fc21bcb90f594b0faa0775a214893a03b0b /src
parent48f0ca7f2cab006eda17f3b6d43bf4b7337f050e (diff)
Support pregenerated distance field cache
Unfortunately, this code was never written to be shared, so we currently have to duplicate it. Longer term, the correct approach is to refactor certain OpenGL-parts out from the implementation in Qt Quick, which shouldn't be a huge effort. But for now, we have to duplicate it, since the we are going to be based on 5.12.2 where we can't retroactively do this anyway. Fixes: QT3DS-2833 Change-Id: I5b88b70a69ee4dcb8b13ef0f7408ab9c09cbf254 Reviewed-by: Tomi Korpipää <tomi.korpipaa@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/runtime/q3dsdistancefieldglyphcache.cpp240
-rw-r--r--src/runtime/q3dsdistancefieldglyphcache_p.h2
2 files changed, 241 insertions, 1 deletions
diff --git a/src/runtime/q3dsdistancefieldglyphcache.cpp b/src/runtime/q3dsdistancefieldglyphcache.cpp
index 07ceedf..a4ca621 100644
--- a/src/runtime/q3dsdistancefieldglyphcache.cpp
+++ b/src/runtime/q3dsdistancefieldglyphcache.cpp
@@ -35,6 +35,7 @@
#include <QtQuick/private/qsgareaallocator_p.h>
#include <QtCore/qmath.h>
+#include <QtCore/qendian.h>
#if QT_VERSION >= QT_VERSION_CHECK(5,12,2)
@@ -142,6 +143,10 @@ Q3DSDistanceFieldGlyphCache::Q3DSDistanceFieldGlyphCache(const QRawFont &font, Q
{
Q3DSGraphicsLimits gfxLimits = Q3DS::graphicsLimits();
m_isOpenGLES = gfxLimits.format.renderableType() == QSurfaceFormat::OpenGLES;
+
+ m_maxTextureSize = Q3DSDISTANCEFIELDGLYPHCACHE_MAXIMUM_TEXURE_SIZE;
+
+ loadPregeneratedCache(font);
}
Q3DSDistanceFieldGlyphCache::~Q3DSDistanceFieldGlyphCache()
@@ -150,7 +155,7 @@ Q3DSDistanceFieldGlyphCache::~Q3DSDistanceFieldGlyphCache()
int Q3DSDistanceFieldGlyphCache::maxTextureSize() const
{
- return Q3DSDISTANCEFIELDGLYPHCACHE_MAXIMUM_TEXURE_SIZE;
+ return m_maxTextureSize;
}
Q3DSDistanceFieldGlyphCache::TextureInfo *Q3DSDistanceFieldGlyphCache::textureInfo(int index) const
@@ -322,6 +327,239 @@ Q3DSDistanceFieldGlyphCache::TextureInfo *Q3DSDistanceFieldGlyphCache::textureIn
return textureInfo(id - 1);
}
+// This is all copy-pasted from Qt Quick, as sharing it would require some refactoring, and we
+// need to work with Qt 5.12.2 at the moment.
+namespace {
+ struct Qtdf {
+ // We need these structs to be tightly packed, but some compilers we use do not
+ // support #pragma pack(1), so we need to hardcode the offsets/sizes in the
+ // file format
+ enum TableSize {
+ HeaderSize = 14,
+ GlyphRecordSize = 46,
+ TextureRecordSize = 17
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ pixelSize = 2,
+ textureSize = 4,
+ flags = 8,
+ headerPadding = 9,
+ numGlyphs = 10,
+
+ // Glyph record
+ glyphIndex = 0,
+ textureOffsetX = 4,
+ textureOffsetY = 8,
+ textureWidth = 12,
+ textureHeight = 16,
+ xMargin = 20,
+ yMargin = 24,
+ boundingRectX = 28,
+ boundingRectY = 32,
+ boundingRectWidth = 36,
+ boundingRectHeight = 40,
+ textureIndex = 44,
+
+ // Texture record
+ allocatedX = 0,
+ allocatedY = 4,
+ allocatedWidth = 8,
+ allocatedHeight = 12,
+ texturePadding = 16
+
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+ };
+}
+
+bool Q3DSDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+{
+ // The pregenerated data must be loaded first, otherwise the area allocator
+ // will be wrong
+ if (m_areaAllocator != nullptr) {
+ qWarning("Font cache must be loaded before cache is used");
+ return false;
+ }
+
+ QByteArray qtdfTable = font.fontTable("qtdf");
+ if (qtdfTable.isEmpty())
+ return false;
+
+ typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
+
+ GlyphTextureHash glyphTextures;
+
+ if (uint(qtdfTable.size()) < Qtdf::HeaderSize) {
+ qWarning("Invalid qtdf table in font '%s'",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ const char *qtdfTableStart = qtdfTable.constData();
+ const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size();
+
+ int padding = 0;
+ int textureCount = 0;
+ {
+ quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion);
+ quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Invalid version of qtdf table %d.%d in font '%s'",
+ majorVersion,
+ minorVersion,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize));
+ m_maxTextureSize = int(Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize));
+ m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1;
+ padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding);
+
+ if (pixelSize <= 0.0) {
+ qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (m_maxTextureSize <= 0) {
+ qWarning("Invalid texture size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (padding != Q3DSDISTANCEFIELDGLYPHCACHE_PADDING) {
+ qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
+ qPrintable(font.familyName()),
+ padding,
+ Q3DSDISTANCEFIELDGLYPHCACHE_PADDING);
+ }
+
+ m_referenceFont.setPixelSize(pixelSize);
+
+ quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs);
+ m_unusedGlyphs.reserve(int(glyphCount));
+
+ const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize;
+ {
+ m_areaAllocator = new QSGAreaAllocator(QSize(0, 0));
+ allocatorData = m_areaAllocator->deserialize(allocatorData, qtdfTableEnd - allocatorData);
+ if (allocatorData == nullptr)
+ return false;
+ }
+
+ if (m_areaAllocator->size().height() % m_maxTextureSize != 0) {
+ qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ textureCount = m_areaAllocator->size().height() / m_maxTextureSize;
+ m_maxTextureCount = qMax(m_maxTextureCount, textureCount);
+
+ const char *textureRecord = allocatorData;
+ for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
+ if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ TextureInfo *tex = textureInfo(i);
+ tex->allocatedArea.setX(int(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX)));
+ tex->allocatedArea.setY(int(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY)));
+ tex->allocatedArea.setWidth(int(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedWidth)));
+ tex->allocatedArea.setHeight(int(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedHeight)));
+ tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding);
+ }
+
+ const char *glyphRecord = textureRecord;
+ for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
+ if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex);
+ m_unusedGlyphs.insert(glyph);
+
+ GlyphData &glyphData = emptyData(glyph);
+
+#define FROM_FIXED_POINT(value) \
+(qreal(value)/qreal(65536))
+
+ glyphData.texCoord.x = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX));
+ glyphData.texCoord.y = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY));
+ glyphData.texCoord.width = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth));
+ glyphData.texCoord.height = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight));
+ glyphData.texCoord.xMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin));
+ glyphData.texCoord.yMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin));
+ glyphData.boundingRect.setX(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX)));
+ glyphData.boundingRect.setY(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY)));
+ glyphData.boundingRect.setWidth(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectWidth)));
+ glyphData.boundingRect.setHeight(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectHeight)));
+
+#undef FROM_FIXED_POINT
+
+ int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex);
+ if (textureIndex < 0 || textureIndex >= textureCount) {
+ qWarning("Invalid texture index %d (texture count == %d) in '%s'",
+ textureIndex,
+ textureCount,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+
+ TextureInfo *texInfo = textureInfo(textureIndex);
+ m_glyphsTexture.insert(glyph, texInfo);
+
+ glyphTextures[texInfo].append(glyph);
+ }
+
+ const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord);
+ for (int i = 0; i < textureCount; ++i) {
+
+ TextureInfo *texInfo = textureInfo(i);
+
+ int width = texInfo->allocatedArea.width();
+ int height = texInfo->allocatedArea.height();
+ qint64 size = width * height;
+ if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ resizeTexture(texInfo, width, height);
+
+ memcpy(texInfo->copy.bits(), textureData, size);
+ textureData += size;
+
+ for (Qt3DRender::QAbstractTextureImage *textureImage : texInfo->texture->textureImages())
+ static_cast<Q3DSDistanceFieldTextureImage *>(textureImage)->updateGenerator();
+
+
+ QVector<glyph_t> glyphs = glyphTextures.value(texInfo);
+
+ Texture t;
+ t.textureId = uint(i + 1);
+ t.size = texInfo->copy.size();
+
+ setGlyphsTexture(glyphs, t);
+ }
+ }
+
+ return true;
+}
+
QT_END_NAMESPACE
#endif // QT_VERSION >= QT_VERSION_CHECK(5,12,2)
diff --git a/src/runtime/q3dsdistancefieldglyphcache_p.h b/src/runtime/q3dsdistancefieldglyphcache_p.h
index a93dc6d..e29f614 100644
--- a/src/runtime/q3dsdistancefieldglyphcache_p.h
+++ b/src/runtime/q3dsdistancefieldglyphcache_p.h
@@ -75,12 +75,14 @@ public:
TextureInfo *textureInfoById(uint textureId) const;
private:
+ bool loadPregeneratedCache(const QRawFont &font);
TextureInfo *textureInfo(int index) const;
int maxTextureSize() const;
void resizeTexture(TextureInfo *info, int width, int height);
QSGAreaAllocator *m_areaAllocator = nullptr;
+ int m_maxTextureSize = 0;
int m_maxTextureCount = 3;
bool m_isOpenGLES = false;