Revision control

++ // Copyright (C) 2016 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 "qsgdefaultglyphnode_p.h"
++ #include "qsgdefaultglyphnode_p_p.h"
++
++ #include <private/qrawfont_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ QSGDefaultGlyphNode::QSGDefaultGlyphNode(QSGRenderContext *context)
++ : QSGGlyphNode(QSGTextNode::NativeRendering)
++ , m_context(context)
++ , m_glyphNodeType(RootGlyphNode)
++ , m_dirtyGeometry(false)
++ , m_recycled(false)
++ , m_preferredAntialiasingMode(DefaultAntialiasing)
++ , m_style(QQuickText::Normal)
++ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
++ {
++ setFlag(UsePreprocess);
++ setFlag(OwnsMaterial);
++ m_geometry.setDrawingMode(QSGGeometry::DrawTriangles);
++ setGeometry(&m_geometry);
++ }
++
++ QSGDefaultGlyphNode::~QSGDefaultGlyphNode()
++ {
++ cleanup();
++ }
++
++ void QSGDefaultGlyphNode::cleanup()
++ {
++ if (m_glyphNodeType == SubGlyphNode)
++ return;
++
++ qDeleteAll(m_nodesToDelete);
++ m_nodesToDelete.clear();
++ }
++
++ void QSGDefaultGlyphNode::recycle()
++ {
++ QSGGlyphNode::recycle();
++
++ cleanup();
++
++ m_glyphs = QGlyphRun{};
++ m_position = QPointF{};
++ m_color = QColor{};
++ m_style = QQuickText::Normal;
++ m_baseLine = QPointF{};
++ m_geometry.allocate(0, 0);
++
++ m_dirtyGeometry = true;
++ m_preferredAntialiasingMode = DefaultAntialiasing;
++ m_recycled = true;
++ }
++
++ void QSGDefaultGlyphNode::setColor(const QColor &color)
++ {
++ m_color = color;
++ if (material() != nullptr && !m_recycled) {
++ static_cast<QSGTextMaskMaterial *>(material())->setColor(color);
++ markDirty(DirtyMaterial);
++ }
++ }
++
++ void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
++ {
++ m_position = position;
++ m_glyphs = glyphs;
++ m_dirtyGeometry = true;
++
++ #ifdef QSG_RUNTIME_DESCRIPTION
++ qsgnode_set_description(this, QLatin1String("glyphs"));
++ #endif
++ }
++
++ void QSGDefaultGlyphNode::setStyle(QQuickText::TextStyle style)
++ {
++ if (m_style == style)
++ return;
++ m_style = style;
++ }
++
++ void QSGDefaultGlyphNode::setStyleColor(const QColor &color)
++ {
++ if (m_styleColor == color)
++ return;
++ m_styleColor = color;
++ }
++
++ void QSGDefaultGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
++ {
++ m_preferredAntialiasingMode = mode;
++ }
++
++ void QSGDefaultGlyphNode::update()
++ {
++ m_recycled = false;
++
++ QRawFont font = m_glyphs.rawFont();
++ QMargins margins(0, 0, 0, 0);
++
++ const auto *fontEngine = QRawFontPrivate::get(font)->fontEngine;
++ const bool isColorFont = fontEngine->glyphFormat == QFontEngine::Format_ARGB;
++
++ QSGTextMaskMaterial *material = nullptr;
++ if (m_style == QQuickText::Normal || isColorFont) {
++ QFontEngine::GlyphFormat glyphFormat;
++
++ // Don't try to override glyph format of color fonts
++ if (isColorFont) {
++ glyphFormat = QFontEngine::Format_None;
++ } else {
++ switch (m_preferredAntialiasingMode) {
++ case GrayAntialiasing:
++ glyphFormat = QFontEngine::Format_A8;
++ break;
++ case HighQualitySubPixelAntialiasing:
++ case LowQualitySubPixelAntialiasing:
++ glyphFormat = QFontEngine::Format_A32;
++ break;
++ default:
++ glyphFormat = QFontEngine::Format_None;
++ break;
++ }
++ }
++
++ const auto rgbColor = m_color.toRgb();
++ material = new QSGTextMaskMaterial(m_context, QVector4D(rgbColor.redF(), rgbColor.greenF(), rgbColor.blueF(), rgbColor.alphaF()), font, glyphFormat);
++ } else if (m_style == QQuickText::Outline) {
++ QSGOutlinedTextMaterial *m = new QSGOutlinedTextMaterial(m_context, font);
++ m->setStyleColor(m_styleColor);
++ margins = QMargins(1, 1, 1, 1);
++ material = m;
++ } else {
++ QSGStyledTextMaterial *m = new QSGStyledTextMaterial(m_context, font);
++ if (m_style == QQuickText::Sunken) {
++ m->setStyleShift(QVector2D(0, -1));
++ margins.setTop(1);
++ } else if (m_style == QQuickText::Raised) {
++ m->setStyleShift(QVector2D(0, 1));
++ margins.setBottom(1);
++ }
++ m->setStyleColor(m_styleColor);
++ material = m;
++ }
++
++ material->setColor(m_color);
++
++ QRectF boundingRect;
++ material->populate(m_position, m_glyphs.glyphIndexes(), m_glyphs.positions(), geometry(),
++ &boundingRect, &m_baseLine, margins);
++ setBoundingRect(boundingRect);
++
++ setMaterial(material);
++ markDirty(DirtyGeometry);
++ }
++
++ void QSGDefaultGlyphNode::preprocess()
++ {
++ qDeleteAll(m_nodesToDelete);
++ m_nodesToDelete.clear();
++
++ if (m_dirtyGeometry)
++ updateGeometry();
++ }
++
++ void QSGDefaultGlyphNode::updateGeometry()
++ {
++ // Remove previously created sub glyph nodes
++ // We assume all the children are sub glyph nodes
++ QSGNode *subnode = firstChild();
++ while (subnode) {
++ // We can't delete the node now as it might be in the preprocess list
++ // It will be deleted in the next preprocess
++ m_nodesToDelete.append(subnode);
++ subnode = subnode->nextSibling();
++ }
++ removeAllChildNodes();
++
++ GlyphInfo glyphInfo;
++
++ const QList<quint32> indexes = m_glyphs.glyphIndexes();
++ const QList<QPointF> positions = m_glyphs.positions();
++
++ const int maxGlyphs = (USHRT_MAX + 1) / 4; // 16384
++ const int maxVertices = maxGlyphs * 4; // 65536
++ const int maxIndexes = maxGlyphs * 6; // 98304
++
++ for (int i = 0; i < indexes.size(); ++i) {
++ const int glyphIndex = indexes.at(i);
++ const QPointF position = positions.at(i);
++
++ // As we use UNSIGNED_SHORT indexing in the geometry, we overload the
++ // "glyphsInOtherNodes" concept as overflow for if there are more than
++ // 65536 (16384 * 4) vertices to render which would otherwise exceed
++ // the maximum index size. This will cause sub-nodes to be recursively
++ // created to handle any number of glyphs.
++ if (i >= maxGlyphs) {
++ glyphInfo.indexes.append(glyphIndex);
++ glyphInfo.positions.append(position);
++ continue;
++ }
++ }
++
++ if (!glyphInfo.indexes.isEmpty()) {
++ QGlyphRun subNodeGlyphRun(m_glyphs);
++ subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes);
++ subNodeGlyphRun.setPositions(glyphInfo.positions);
++
++ QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode(m_context);
++ subNode->setGlyphNodeType(SubGlyphNode);
++ subNode->setColor(m_color);
++ subNode->setStyle(m_style);
++ subNode->setStyleColor(m_styleColor);
++ subNode->setGlyphs(m_position, subNodeGlyphRun);
++ subNode->update();
++ subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered
++ appendChildNode(subNode);
++
++ QSGGeometry *g = geometry();
++
++ QSGGeometry::TexturedPoint2D *vertexData = g->vertexDataAsTexturedPoint2D();
++ quint16 *indexData = g->indexDataAsUShort();
++
++ QList<QSGGeometry::TexturedPoint2D> tempVertexData(maxVertices);
++ QList<quint16> tempIndexData(maxIndexes);
++
++ for (int i = 0; i < maxGlyphs; i++) {
++ tempVertexData[i * 4 + 0] = vertexData[i * 4 + 0];
++ tempVertexData[i * 4 + 1] = vertexData[i * 4 + 1];
++ tempVertexData[i * 4 + 2] = vertexData[i * 4 + 2];
++ tempVertexData[i * 4 + 3] = vertexData[i * 4 + 3];
++
++ tempIndexData[i * 6 + 0] = indexData[i * 6 + 0];
++ tempIndexData[i * 6 + 1] = indexData[i * 6 + 1];
++ tempIndexData[i * 6 + 2] = indexData[i * 6 + 2];
++ tempIndexData[i * 6 + 3] = indexData[i * 6 + 3];
++ tempIndexData[i * 6 + 4] = indexData[i * 6 + 4];
++ tempIndexData[i * 6 + 5] = indexData[i * 6 + 5];
++ }
++
++ g->allocate(maxVertices, maxIndexes);
++ vertexData = g->vertexDataAsTexturedPoint2D();
++ indexData = g->indexDataAsUShort();
++
++ for (int i = 0; i < maxGlyphs; i++) {
++ vertexData[i * 4 + 0] = tempVertexData[i * 4 + 0];
++ vertexData[i * 4 + 1] = tempVertexData[i * 4 + 1];
++ vertexData[i * 4 + 2] = tempVertexData[i * 4 + 2];
++ vertexData[i * 4 + 3] = tempVertexData[i * 4 + 3];
++
++ indexData[i * 6 + 0] = tempIndexData[i * 6 + 0];
++ indexData[i * 6 + 1] = tempIndexData[i * 6 + 1];
++ indexData[i * 6 + 2] = tempIndexData[i * 6 + 2];
++ indexData[i * 6 + 3] = tempIndexData[i * 6 + 3];
++ indexData[i * 6 + 4] = tempIndexData[i * 6 + 4];
++ indexData[i * 6 + 5] = tempIndexData[i * 6 + 5];
++ }
++ }
++
++ m_dirtyGeometry = false;
++ }
++
++ QT_END_NAMESPACE