Revision control
Copy as Markdown
// Copyright (C) 2023 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 "qsgcurveglyphnode_p.h"
#include "qsgcurveglyphatlas_p.h"
#include "qsgcurvefillnode_p.h"
#include "qsgcurvestrokenode_p.h"
#include <private/qsgcurveabstractnode_p.h>
#include <private/qsgcontext_p.h>
#include <private/qsgtexturematerial_p.h>
#include <private/qrawfont_p.h>
#include <QtGui/qcolor.h>
QT_BEGIN_NAMESPACE
QSGCurveGlyphNode::QSGCurveGlyphNode(QSGRenderContext *context)
: QSGGlyphNode(QSGTextNode::CurveRendering)
, m_context(context)
, m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 0)
, m_dirtyGeometry(false)
{
setFlag(UsePreprocess);
setFlag(OwnsMaterial);
// #### To avoid asserts: we should probably merge this with QSGCurveFillNode
setGeometry(&m_geometry);
setMaterial(new QSGTextureMaterial);
}
QSGCurveGlyphNode::~QSGCurveGlyphNode()
{
}
void QSGCurveGlyphNode::cleanup()
{
delete m_glyphNode;
m_glyphNode = nullptr;
delete m_styleNode;
m_styleNode = nullptr;
}
void QSGCurveGlyphNode::recycle()
{
Q_ASSERT(m_geometry.vertexCount() == 0);
Q_ASSERT(m_geometry.indexCount() == 0);
QSGGlyphNode::recycle();
m_color = Qt::black;
m_dirtyGeometry = true;
m_fontSize = 0.0f;
m_glyphs = QGlyphRun{};
m_style = QQuickText::Normal;
m_styleColor = QColor{};
m_baseLine = QPointF{};
m_position = QPointF{};
cleanup();
}
void QSGCurveGlyphNode::setPreferredAntialiasingMode(AntialiasingMode mode)
{
Q_UNUSED(mode);
}
void QSGCurveGlyphNode::setColor(const QColor &color)
{
m_color = color;
if (m_glyphNode != nullptr)
m_glyphNode->setColor(color);
}
void QSGCurveGlyphNode::setStyleColor(const QColor &styleColor)
{
m_styleColor = styleColor;
if (m_styleNode != nullptr)
m_styleNode->setColor(styleColor);
}
void QSGCurveGlyphNode::setStyle(QQuickText::TextStyle style)
{
if (m_style != style) {
m_style = style;
m_dirtyGeometry = true;
update();
}
}
void QSGCurveGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs)
{
m_glyphs = glyphs;
QRawFont font = glyphs.rawFont();
m_fontSize = font.pixelSize();
m_position = QPointF(position.x(), position.y() - font.ascent());
m_dirtyGeometry = true;
#ifdef QSG_RUNTIME_DESCRIPTION
qsgnode_set_description(this, QString::number(glyphs.glyphIndexes().count())
+ QStringLiteral(" curve glyphs: ")
+ m_glyphs.rawFont().familyName()
+ QStringLiteral(" ")
+ QString::number(m_glyphs.rawFont().pixelSize()));
#endif
}
void QSGCurveGlyphNode::update()
{
markDirty(DirtyGeometry);
}
void QSGCurveGlyphNode::preprocess()
{
if (m_dirtyGeometry)
updateGeometry();
}
void QSGCurveGlyphNode::updateGeometry()
{
cleanup();
QSGCurveGlyphAtlas *curveGlyphAtlas = m_context->curveGlyphAtlas(m_glyphs.rawFont());
curveGlyphAtlas->populate(m_glyphs.glyphIndexes());
m_glyphNode = new QSGCurveFillNode;
m_glyphNode->setColor(m_color);
QPointF offset;
float fontScale = float(m_fontSize / curveGlyphAtlas->fontSize());
QSGCurveFillNode *raisedSunkenStyleNode = nullptr;
QSGCurveStrokeNode *outlineNode = nullptr;
if (m_style == QQuickText::Raised || m_style == QQuickText::Sunken) {
raisedSunkenStyleNode = new QSGCurveFillNode;
raisedSunkenStyleNode ->setColor(m_styleColor);
offset = m_style == QQuickText::Raised ? QPointF(0.0f, 1.0f) : QPointF(0.0f, -1.0f);
m_styleNode = raisedSunkenStyleNode;
} else if (m_style == QQuickText::Outline) {
outlineNode = new QSGCurveStrokeNode;
outlineNode->setColor(m_styleColor);
outlineNode->setStrokeWidth(2 / fontScale);
outlineNode->setLocalScale(fontScale);
m_styleNode = outlineNode;
}
const QList<quint32> indexes = m_glyphs.glyphIndexes();
const QList<QPointF> positions = m_glyphs.positions();
for (qsizetype i = 0; i < indexes.size(); ++i) {
if (i == 0)
m_baseLine = positions.at(i);
curveGlyphAtlas->addGlyph(m_glyphNode,
indexes.at(i),
m_position + positions.at(i),
m_fontSize);
if (raisedSunkenStyleNode != nullptr) {
curveGlyphAtlas->addGlyph(raisedSunkenStyleNode,
indexes.at(i),
m_position + positions.at(i) + offset,
m_fontSize);
}
if (outlineNode != nullptr) {
// Since the stroke node will scale everything by fontScale internally (the
// shader does not support pre-transforming the vertices), we have to also first
// do the inverse scale on the glyph position to get the correct position.
curveGlyphAtlas->addStroke(outlineNode,
indexes.at(i),
(m_position + positions.at(i)) / fontScale);
}
}
if (m_styleNode != nullptr) {
m_styleNode->cookGeometry();
appendChildNode(m_styleNode);
}
m_glyphNode->cookGeometry();
appendChildNode(m_glyphNode);
m_dirtyGeometry = false;
}
QT_END_NAMESPACE