Revision control
++ // Copyright (C) 2018 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qsgadaptationlayer_p.h"
++
++ #include <qmath.h>
++ #include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
++ #include <QtQuick/private/qsgcontext_p.h>
++ #include <private/qrawfont_p.h>
++ #include <QtGui/qguiapplication.h>
++ #include <qdir.h>
++ #include <qsgrendernode.h>
++
++ #include <private/qquickprofiler_p.h>
++ #include <QElapsedTimer>
++
++ #include <qtquick_tracepoints_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_update_entry, int count)
++ Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_update_exit)
++ Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphRender_entry)
++ Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphRender_exit)
++ Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphStore_entry)
++ Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphStore_exit)
++
++ static QElapsedTimer qsg_render_timer;
++
++ QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
++
++ QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality)
++ : m_renderTypeQuality(renderTypeQuality)
++ , m_pendingGlyphs(64)
++ {
++ Q_ASSERT(font.isValid());
++
++ QRawFontPrivate *fontD = QRawFontPrivate::get(font);
++ m_glyphCount = fontD->fontEngine->glyphCount();
++
++ m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
++
++ m_referenceFont = font;
++ // we set the same pixel size as used by the distance field internally.
++ // this allows us to call pathForGlyph once and reuse the result.
++ m_referenceFont.setPixelSize(baseFontSize() * QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
++ Q_ASSERT(m_referenceFont.isValid());
++ }
++
++ QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
++ {
++ }
++
++ int QSGDistanceFieldGlyphCache::baseFontSize() const
++ {
++ return m_renderTypeQuality > 0 ? m_renderTypeQuality : QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution);
++ }
++
++ QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::emptyData(glyph_t glyph)
++ {
++ GlyphData gd;
++ gd.texture = &s_emptyTexture;
++ QHash<glyph_t, GlyphData>::iterator it = m_glyphsData.insert(glyph, gd);
++ return it.value();
++ }
++
++ QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph)
++ {
++ QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph);
++ if (data == m_glyphsData.end()) {
++ GlyphData &gd = emptyData(glyph);
++ gd.path = m_referenceFont.pathForGlyph(glyph);
++ // need bounding rect in base font size scale
++ qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
++ QTransform scaleDown;
++ scaleDown.scale(scaleFactor, scaleFactor);
++ gd.boundingRect = scaleDown.mapRect(gd.path.boundingRect());
++
++ // Guard against corrupt fonts with excessively large glyph bounds, which would cause
++ // us to allocate huge distance fields and atlas textures. Treat them as empty glyphs.
++ const qreal pixelSize = baseFontSize();
++ if (QFontEngine::glyphBoundingBoxExceedsLimit(gd.boundingRect.width(),
++ gd.boundingRect.height(),
++ pixelSize * pixelSize)) {
++ gd.boundingRect = QRectF();
++ gd.path = QPainterPath();
++ }
++
++ return gd;
++ }
++ return data.value();
++ }
++
++ QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph, qreal pixelSize)
++ {
++ GlyphData &gd = glyphData(glyph);
++ qreal scale = fontScale(pixelSize);
++
++ Metrics m;
++ m.width = gd.boundingRect.width() * scale;
++ m.height = gd.boundingRect.height() * scale;
++ m.baselineX = gd.boundingRect.x() * scale;
++ m.baselineY = -gd.boundingRect.y() * scale;
++
++ return m;
++ }
++
++ void QSGDistanceFieldGlyphCache::populate(const QList<glyph_t> &glyphs)
++ {
++ QSet<glyph_t> referencedGlyphs;
++ QSet<glyph_t> newGlyphs;
++ int count = glyphs.size();
++ for (int i = 0; i < count; ++i) {
++ glyph_t glyphIndex = glyphs.at(i);
++ const bool isValid = int(glyphIndex) < glyphCount() || glyphCount() <= 0;
++
++ if (!isValid) {
++ qWarning("Warning: distance-field glyph is not available with index %d (glyph count: %d, font: %s)",
++ glyphIndex,
++ glyphCount(),
++ qPrintable(m_referenceFont.familyName()));
++ }
++
++ GlyphData &gd = isValid ? glyphData(glyphIndex) : emptyData(glyphIndex);
++ ++gd.ref;
++ referencedGlyphs.insert(glyphIndex);
++
++ if (gd.texCoord.isValid() || m_populatingGlyphs.contains(glyphIndex))
++ continue;
++
++ m_populatingGlyphs.insert(glyphIndex);
++
++ if (gd.boundingRect.isEmpty()) {
++ gd.texCoord.width = 0;
++ gd.texCoord.height = 0;
++ } else {
++ newGlyphs.insert(glyphIndex);
++ }
++ }
++
++ referenceGlyphs(referencedGlyphs);
++ if (!newGlyphs.isEmpty())
++ requestGlyphs(newGlyphs);
++ }
++
++ void QSGDistanceFieldGlyphCache::release(const QList<glyph_t> &glyphs)
++ {
++ QSet<glyph_t> unusedGlyphs;
++ for (glyph_t glyphIndex : glyphs) {
++ if (auto it = m_glyphsData.find(glyphIndex); it != m_glyphsData.end()) {
++ GlyphData &gd = it.value();
++ if (--gd.ref == 0)
++ unusedGlyphs.insert(glyphIndex);
++ }
++ }
++ releaseGlyphs(unusedGlyphs);
++ }
++
++ bool QSGDistanceFieldGlyphCache::isActive() const
++ {
++ return true;
++ }
++
++ void QSGDistanceFieldGlyphCache::update()
++ {
++ m_populatingGlyphs.clear();
++
++ if (m_pendingGlyphs.isEmpty())
++ return;
++
++ Q_TRACE_SCOPE(QSGDistanceFieldGlyphCache_update, m_pendingGlyphs.size());
++
++ bool profileFrames = QSG_LOG_TIME_GLYPH().isDebugEnabled();
++ if (profileFrames)
++ qsg_render_timer.start();
++ Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphAdaptationLayerFrame);
++ Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_entry);
++
++ QList<QDistanceField> distanceFields;
++ const int pendingGlyphsSize = m_pendingGlyphs.size();
++ distanceFields.reserve(pendingGlyphsSize);
++ for (int i = 0; i < pendingGlyphsSize; ++i) {
++ GlyphData &gd = glyphData(m_pendingGlyphs.at(i));
++
++ QSize size = QSize(qCeil(gd.texCoord.width + gd.texCoord.xMargin * 2),
++ qCeil(gd.texCoord.height + gd.texCoord.yMargin * 2));
++
++ distanceFields.append(QDistanceField(size,
++ gd.path,
++ m_pendingGlyphs.at(i),
++ m_doubleGlyphResolution));
++ gd.path = QPainterPath(); // no longer needed, so release memory used by the painter path
++ }
++
++ qint64 renderTime = 0;
++ int count = m_pendingGlyphs.size();
++ if (profileFrames)
++ renderTime = qsg_render_timer.nsecsElapsed();
++
++ Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_exit);
++ Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
++ QQuickProfiler::SceneGraphAdaptationLayerGlyphRender);
++ Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_entry);
++
++ m_pendingGlyphs.reset();
++
++ storeGlyphs(distanceFields);
++
++ #if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
++ for (Texture texture : std::as_const(m_textures))
++ saveTexture(texture.texture, m_referenceFont.familyName());
++ #endif
++
++ if (QSG_LOG_TIME_GLYPH().isDebugEnabled()) {
++ quint64 now = qsg_render_timer.elapsed();
++ qCDebug(QSG_LOG_TIME_GLYPH,
++ "distancefield: %d glyphs prepared in %dms, rendering=%d, upload=%d",
++ count,
++ (int) now,
++ int(renderTime / 1000000),
++ int((now - (renderTime / 1000000))));
++ }
++ Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_exit);
++ Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
++ QQuickProfiler::SceneGraphAdaptationLayerGlyphStore,
++ (qint64)count);
++ }
++
++ void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
++ {
++ QList<quint32> invalidatedGlyphs;
++
++ int count = glyphs.size();
++ for (int i = 0; i < count; ++i) {
++ GlyphPosition glyph = glyphs.at(i);
++ GlyphData &gd = glyphData(glyph.glyph);
++
++ if (!gd.texCoord.isNull())
++ invalidatedGlyphs.append(glyph.glyph);
++
++ gd.texCoord.xMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
++ gd.texCoord.yMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
++ gd.texCoord.x = glyph.position.x();
++ gd.texCoord.y = glyph.position.y();
++ gd.texCoord.width = gd.boundingRect.width();
++ gd.texCoord.height = gd.boundingRect.height();
++ }
++
++ if (!invalidatedGlyphs.isEmpty()) {
++ for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
++ iter->invalidateGlyphs(invalidatedGlyphs);
++ }
++ }
++ }
++
++ void QSGDistanceFieldGlyphCache::processPendingGlyphs()
++ {
++ /* Intentionally empty */
++ }
++
++ void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QList<glyph_t> &glyphs, const Texture &tex)
++ {
++ int i = m_textures.indexOf(tex);
++ if (i == -1) {
++ m_textures.append(tex);
++ i = m_textures.size() - 1;
++ } else {
++ m_textures[i].size = tex.size;
++ }
++ Texture *texture = &(m_textures[i]);
++
++ QList<quint32> invalidatedGlyphs;
++
++ int count = glyphs.size();
++ for (int j = 0; j < count; ++j) {
++ glyph_t glyphIndex = glyphs.at(j);
++ GlyphData &gd = glyphData(glyphIndex);
++ if (gd.texture != &s_emptyTexture)
++ invalidatedGlyphs.append(glyphIndex);
++ gd.texture = texture;
++ }
++
++ if (!invalidatedGlyphs.isEmpty()) {
++ for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
++ iter->invalidateGlyphs(invalidatedGlyphs);
++ }
++ }
++ }
++
++ void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QList<glyph_t> &glyphs)
++ {
++ int count = glyphs.size();
++ for (int i = 0; i < count; ++i)
++ m_pendingGlyphs.add(glyphs.at(i));
++ }
++
++ void QSGDistanceFieldGlyphCache::updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize)
++ {
++ int count = m_textures.size();
++ for (int i = 0; i < count; ++i) {
++ Texture &tex = m_textures[i];
++ if (tex.texture == oldTex) {
++ tex.texture = newTex;
++ tex.size = newTexSize;
++ return;
++ }
++ }
++ }
++
++ QSGNodeVisitorEx::~QSGNodeVisitorEx()
++ = default;
++
++ void QSGNodeVisitorEx::visitChildren(QSGNode *node)
++ {
++ for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
++ switch (child->type()) {
++ case QSGNode::ClipNodeType: {
++ QSGClipNode *c = static_cast<QSGClipNode*>(child);
++ if (visit(c))
++ visitChildren(c);
++ endVisit(c);
++ break;
++ }
++ case QSGNode::TransformNodeType: {
++ QSGTransformNode *c = static_cast<QSGTransformNode*>(child);
++ if (visit(c))
++ visitChildren(c);
++ endVisit(c);
++ break;
++ }
++ case QSGNode::OpacityNodeType: {
++ QSGOpacityNode *c = static_cast<QSGOpacityNode*>(child);
++ if (visit(c))
++ visitChildren(c);
++ endVisit(c);
++ break;
++ }
++ case QSGNode::GeometryNodeType: {
++ if (child->flags() & QSGNode::IsVisitableNode) {
++ QSGVisitableNode *v = static_cast<QSGVisitableNode*>(child);
++ v->accept(this);
++ } else {
++ QSGGeometryNode *c = static_cast<QSGGeometryNode*>(child);
++ if (visit(c))
++ visitChildren(c);
++ endVisit(c);
++ }
++ break;
++ }
++ case QSGNode::RootNodeType: {
++ QSGRootNode *root = static_cast<QSGRootNode*>(child);
++ if (visit(root))
++ visitChildren(root);
++ endVisit(root);
++ break;
++ }
++ case QSGNode::BasicNodeType: {
++ visitChildren(child);
++ break;
++ }
++ case QSGNode::RenderNodeType: {
++ QSGRenderNode *r = static_cast<QSGRenderNode*>(child);
++ if (visit(r))
++ visitChildren(r);
++ endVisit(r);
++ break;
++ }
++ default:
++ Q_UNREACHABLE();
++ break;
++ }
++ }
++ }
++
++ QSGVisitableNode::~QSGVisitableNode()
++ = default;
++
++ QSGInternalRectangleNode::~QSGInternalRectangleNode()
++ = default;
++
++ QSGInternalImageNode::~QSGInternalImageNode()
++ = default;
++
++ QSGPainterNode::~QSGPainterNode()
++ = default;
++
++ #ifndef QT_NO_DEBUG_STREAM
++ QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v)
++ {
++ QDebugStateSaver saver(debug);
++ debug.space();
++ debug << v.name;
++ switch (v.type) {
++ case QSGGuiThreadShaderEffectManager::ShaderInfo::Constant:
++ debug << "cvar" << "offset" << v.offset << "size" << v.size;
++ break;
++ case QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler:
++ debug << "sampler" << "bindpoint" << v.bindPoint;
++ break;
++ case QSGGuiThreadShaderEffectManager::ShaderInfo::Texture:
++ debug << "texture" << "bindpoint" << v.bindPoint;
++ break;
++ default:
++ break;
++ }
++ return debug;
++ }
++
++ QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd)
++ {
++ QDebugStateSaver saver(debug);
++ debug.space();
++ debug << vd.specialType;
++ return debug;
++ }
++ #endif
++
++ /*!
++ \internal
++ */
++ QSGLayer::QSGLayer(QSGTexturePrivate &dd)
++ : QSGDynamicTexture(dd)
++ {
++ }
++
++ QSGLayer::~QSGLayer()
++ = default;
++
++ #if QT_CONFIG(quick_sprite)
++
++ QSGSpriteNode::~QSGSpriteNode()
++ = default;
++
++ #endif
++
++ QSGGuiThreadShaderEffectManager::~QSGGuiThreadShaderEffectManager()
++ = default;
++
++ QSGShaderEffectNode::~QSGShaderEffectNode()
++ = default;
++
++ QSGGlyphNode::~QSGGlyphNode()
++ = default;
++
++ void QSGGlyphNode::recycle()
++ {
++ m_bounding_rect = QRectF{};
++
++ setRenderOrder(0);
++ setInheritedOpacity(1);
++ setRendererMatrix(nullptr);
++ setRendererClipList(nullptr);
++ }
++
++ QSGDistanceFieldGlyphConsumer::~QSGDistanceFieldGlyphConsumer()
++ = default;
++
++ QT_END_NAMESPACE
++
++ #include "moc_qsgadaptationlayer_p.cpp"