blob: e8e9f76d040fc10fc786e0fa8cb67f33f1dc1659 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qsgdistancefieldglyphnode_p_p.h"
#include "qsgrhidistancefieldglyphcache_p.h"
#include <QtGui/qopenglfunctions.h>
#include <QtGui/qsurface.h>
#include <QtGui/qwindow.h>
#include <qmath.h>
QT_BEGIN_NAMESPACE
static float qt_sg_envFloat(const char *name, float defaultValue)
{
if (Q_LIKELY(!qEnvironmentVariableIsSet(name)))
return defaultValue;
bool ok = false;
const float value = qgetenv(name).toFloat(&ok);
return ok ? value : defaultValue;
}
static float thresholdFunc(float glyphScale)
{
static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f);
static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f);
static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f);
static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f);
return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev);
}
static float spreadFunc(float glyphScale)
{
static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f);
return range / glyphScale;
}
class QSGDistanceFieldTextMaterialShader : public QSGMaterialShader
{
public:
QSGDistanceFieldTextMaterialShader();
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
char const *const *attributeNames() const override;
protected:
void initialize() override;
void updateAlphaRange();
void updateColor(const QVector4D &c);
void updateTextureScale(const QVector2D &ts);
float m_fontScale = 1.0;
float m_matrixScale = 1.0;
int m_matrix_id = -1;
int m_textureScale_id = -1;
int m_alphaMin_id = -1;
int m_alphaMax_id = -1;
int m_color_id = -1;
QVector2D m_lastTextureScale;
QVector4D m_lastColor;
float m_lastAlphaMin = -1;
float m_lastAlphaMax = -1;
};
char const *const *QSGDistanceFieldTextMaterialShader::attributeNames() const {
static char const *const attr[] = { "vCoord", "tCoord", nullptr };
return attr;
}
QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.frag"));
}
void QSGDistanceFieldTextMaterialShader::updateAlphaRange()
{
float combinedScale = m_fontScale * m_matrixScale;
float base = thresholdFunc(combinedScale);
float range = spreadFunc(combinedScale);
float alphaMin = qMax(0.0f, base - range);
float alphaMax = qMin(base + range, 1.0f);
if (alphaMin != m_lastAlphaMin) {
program()->setUniformValue(m_alphaMin_id, GLfloat(alphaMin));
m_lastAlphaMin = alphaMin;
}
if (alphaMax != m_lastAlphaMax) {
program()->setUniformValue(m_alphaMax_id, GLfloat(alphaMax));
m_lastAlphaMax = alphaMax;
}
}
void QSGDistanceFieldTextMaterialShader::updateColor(const QVector4D &c)
{
if (m_lastColor != c) {
program()->setUniformValue(m_color_id, c);
m_lastColor = c;
}
}
void QSGDistanceFieldTextMaterialShader::updateTextureScale(const QVector2D &ts)
{
if (m_lastTextureScale != ts) {
program()->setUniformValue(m_textureScale_id, ts);
m_lastTextureScale = ts;
}
}
void QSGDistanceFieldTextMaterialShader::initialize()
{
QSGMaterialShader::initialize();
m_matrix_id = program()->uniformLocation("matrix");
m_textureScale_id = program()->uniformLocation("textureScale");
m_color_id = program()->uniformLocation("color");
m_alphaMin_id = program()->uniformLocation("alphaMin");
m_alphaMax_id = program()->uniformLocation("alphaMax");
}
void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect);
QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect);
bool updated = material->updateTextureSize();
if (oldMaterial == nullptr
|| material->color() != oldMaterial->color()
|| state.isOpacityDirty()) {
QVector4D color = material->color();
color *= state.opacity();
updateColor(color);
}
bool updateRange = false;
if (oldMaterial == nullptr
|| material->fontScale() != oldMaterial->fontScale()) {
m_fontScale = material->fontScale();
updateRange = true;
}
if (state.isMatrixDirty()) {
program()->setUniformValue(m_matrix_id, state.combinedMatrix());
m_matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio();
updateRange = true;
}
if (updateRange) {
updateAlphaRange();
}
Q_ASSERT(material->glyphCache());
if (updated
|| oldMaterial == nullptr
|| oldMaterial->texture()->textureId != material->texture()->textureId) {
updateTextureScale(QVector2D(1.0 / material->textureSize().width(),
1.0 / material->textureSize().height()));
QOpenGLFunctions *funcs = state.context()->functions();
funcs->glBindTexture(GL_TEXTURE_2D, material->texture()->textureId);
if (updated) {
// Set the mag/min filters to be linear. We only need to do this when the texture
// has been recreated.
funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
}
}
class QSGDistanceFieldTextMaterialRhiShader : public QSGMaterialRhiShader
{
public:
QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture);
bool updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
protected:
float m_fontScale = 1.0;
float m_matrixScale = 1.0;
};
QSGDistanceFieldTextMaterialRhiShader::QSGDistanceFieldTextMaterialRhiShader(bool alphaTexture)
{
setShaderFileName(VertexStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.vert.qsb"));
if (alphaTexture)
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext_a.frag.qsb"));
else
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldtext.frag.qsb"));
}
bool QSGDistanceFieldTextMaterialRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
Q_ASSERT(oldMaterial == nullptr || newMaterial->type() == oldMaterial->type());
QSGDistanceFieldTextMaterial *mat = static_cast<QSGDistanceFieldTextMaterial *>(newMaterial);
QSGDistanceFieldTextMaterial *oldMat = static_cast<QSGDistanceFieldTextMaterial *>(oldMaterial);
// updateUniformData() is called before updateSampledImage() by the
// renderer. Hence updating the glyph cache stuff here.
const bool textureUpdated = mat->updateTextureSizeAndWrapper();
Q_ASSERT(mat->wrapperTexture());
Q_ASSERT(oldMat == nullptr || oldMat->texture());
bool changed = false;
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 104);
bool updateRange = false;
if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
m_fontScale = mat->fontScale();
updateRange = true;
}
if (state.isMatrixDirty()) {
const QMatrix4x4 m = state.combinedMatrix();
memcpy(buf->data(), m.constData(), 64);
changed = true;
m_matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio();
updateRange = true;
}
if (textureUpdated || !oldMat || oldMat->texture()->texture != mat->texture()->texture) {
const QVector2D ts(1.0f / mat->textureSize().width(), 1.0f / mat->textureSize().height());
Q_ASSERT(sizeof(ts) == 8);
memcpy(buf->data() + 64, &ts, 8);
changed = true;
}
if (!oldMat || mat->color() != oldMat->color() || state.isOpacityDirty()) {
const QVector4D color = mat->color() * state.opacity();
Q_ASSERT(sizeof(color) == 16);
memcpy(buf->data() + 80, &color, 16);
changed = true;
}
if (updateRange) { // deferred because depends on m_fontScale and m_matrixScale
const float combinedScale = m_fontScale * m_matrixScale;
const float base = thresholdFunc(combinedScale);
const float range = spreadFunc(combinedScale);
const QVector2D alphaMinMax(qMax(0.0f, base - range), qMin(base + range, 1.0f));
memcpy(buf->data() + 96, &alphaMinMax, 8);
changed = true;
}
// move texture uploads/copies onto the renderer's soon-to-be-committed list
static_cast<QSGRhiDistanceFieldGlyphCache *>(mat->glyphCache())->commitResourceUpdates(state.resourceUpdateBatch());
return changed;
}
void QSGDistanceFieldTextMaterialRhiShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
QSGMaterial *newMaterial, QSGMaterial *)
{
Q_UNUSED(state);
if (binding != 1)
return;
QSGDistanceFieldTextMaterial *mat = static_cast<QSGDistanceFieldTextMaterial *>(newMaterial);
QSGTexture *t = mat->wrapperTexture();
t->setFiltering(QSGTexture::Linear);
*texture = t;
}
QSGDistanceFieldTextMaterial::QSGDistanceFieldTextMaterial()
: m_glyph_cache(nullptr)
, m_texture(nullptr)
, m_fontScale(1.0)
, m_sgTexture(nullptr)
{
setFlag(Blending | RequiresDeterminant | SupportsRhiShader, true);
}
QSGDistanceFieldTextMaterial::~QSGDistanceFieldTextMaterial()
{
delete m_sgTexture;
}
QSGMaterialType *QSGDistanceFieldTextMaterial::type() const
{
static QSGMaterialType type;
return &type;
}
void QSGDistanceFieldTextMaterial::setColor(const QColor &color)
{
m_color = QVector4D(color.redF() * color.alphaF(),
color.greenF() * color.alphaF(),
color.blueF() * color.alphaF(),
color.alphaF());
}
QSGMaterialShader *QSGDistanceFieldTextMaterial::createShader() const
{
if (flags().testFlag(RhiShaderWanted))
return new QSGDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
else
return new QSGDistanceFieldTextMaterialShader;
}
bool QSGDistanceFieldTextMaterial::updateTextureSize()
{
if (!m_texture)
m_texture = m_glyph_cache->glyphTexture(0); // invalid texture
if (m_texture->size != m_size) {
m_size = m_texture->size;
return true;
}
return false;
}
// When using the RHI we need a QSGTexture wrapping the QRhiTexture, just
// exposing a QRhiTexture * (which would be the equivalent of GLuint textureId)
// is not sufficient to play nice with the material.
bool QSGDistanceFieldTextMaterial::updateTextureSizeAndWrapper()
{
bool updated = updateTextureSize();
if (updated) {
if (m_sgTexture)
delete m_sgTexture;
m_sgTexture = new QSGPlainTexture;
m_sgTexture->setTexture(m_texture->texture);
m_sgTexture->setTextureSize(m_size);
m_sgTexture->setOwnsTexture(false);
}
return updated;
}
int QSGDistanceFieldTextMaterial::compare(const QSGMaterial *o) const
{
Q_ASSERT(o && type() == o->type());
const QSGDistanceFieldTextMaterial *other = static_cast<const QSGDistanceFieldTextMaterial *>(o);
if (m_glyph_cache != other->m_glyph_cache)
return m_glyph_cache - other->m_glyph_cache;
if (m_fontScale != other->m_fontScale) {
return int(other->m_fontScale < m_fontScale) - int(m_fontScale < other->m_fontScale);
}
if (m_color != other->m_color)
return &m_color < &other->m_color ? -1 : 1;
int t0 = m_texture ? (m_texture->rhiBased ? qintptr(m_texture->texture) : m_texture->textureId) : 0;
int t1 = other->m_texture ? (other->m_texture->rhiBased ? qintptr(other->m_texture->texture) : other->m_texture->textureId) : 0;
return t0 - t1;
}
class DistanceFieldStyledTextMaterialShader : public QSGDistanceFieldTextMaterialShader
{
public:
DistanceFieldStyledTextMaterialShader();
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
protected:
void initialize() override;
int m_styleColor_id = -1;
};
DistanceFieldStyledTextMaterialShader::DistanceFieldStyledTextMaterialShader()
: QSGDistanceFieldTextMaterialShader()
{
}
void DistanceFieldStyledTextMaterialShader::initialize()
{
QSGDistanceFieldTextMaterialShader::initialize();
m_styleColor_id = program()->uniformLocation("styleColor");
}
void DistanceFieldStyledTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
QSGDistanceFieldTextMaterialShader::updateState(state, newEffect, oldEffect);
QSGDistanceFieldStyledTextMaterial *material = static_cast<QSGDistanceFieldStyledTextMaterial *>(newEffect);
QSGDistanceFieldStyledTextMaterial *oldMaterial = static_cast<QSGDistanceFieldStyledTextMaterial *>(oldEffect);
if (oldMaterial == nullptr
|| material->styleColor() != oldMaterial->styleColor()
|| (state.isOpacityDirty())) {
QVector4D color = material->styleColor();
color *= state.opacity();
program()->setUniformValue(m_styleColor_id, color);
}
}
class DistanceFieldStyledTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
{
public:
DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
DistanceFieldStyledTextMaterialRhiShader::DistanceFieldStyledTextMaterialRhiShader(bool alphaTexture)
: QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
{
}
bool DistanceFieldStyledTextMaterialRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
bool changed = QSGDistanceFieldTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
QSGDistanceFieldStyledTextMaterial *mat = static_cast<QSGDistanceFieldStyledTextMaterial *>(newMaterial);
QSGDistanceFieldStyledTextMaterial *oldMat = static_cast<QSGDistanceFieldStyledTextMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 128);
if (!oldMat || mat->styleColor() != oldMat->styleColor() || state.isOpacityDirty()) {
QVector4D styleColor = mat->styleColor();
styleColor *= state.opacity();
memcpy(buf->data() + 112, &styleColor, 16);
changed = true;
}
return changed;
}
QSGDistanceFieldStyledTextMaterial::QSGDistanceFieldStyledTextMaterial()
: QSGDistanceFieldTextMaterial()
{
}
QSGDistanceFieldStyledTextMaterial::~QSGDistanceFieldStyledTextMaterial()
{
}
void QSGDistanceFieldStyledTextMaterial::setStyleColor(const QColor &color)
{
m_styleColor = QVector4D(color.redF() * color.alphaF(),
color.greenF() * color.alphaF(),
color.blueF() * color.alphaF(),
color.alphaF());
}
int QSGDistanceFieldStyledTextMaterial::compare(const QSGMaterial *o) const
{
Q_ASSERT(o && type() == o->type());
const QSGDistanceFieldStyledTextMaterial *other = static_cast<const QSGDistanceFieldStyledTextMaterial *>(o);
if (m_styleColor != other->m_styleColor)
return &m_styleColor < &other->m_styleColor ? -1 : 1;
return QSGDistanceFieldTextMaterial::compare(o);
}
class DistanceFieldOutlineTextMaterialShader : public DistanceFieldStyledTextMaterialShader
{
public:
DistanceFieldOutlineTextMaterialShader();
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
protected:
void initialize() override;
void updateOutlineAlphaRange(int dfRadius);
int m_outlineAlphaMax0_id = -1;
int m_outlineAlphaMax1_id = -1;
};
DistanceFieldOutlineTextMaterialShader::DistanceFieldOutlineTextMaterialShader()
: DistanceFieldStyledTextMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldoutlinetext.frag"));
}
void DistanceFieldOutlineTextMaterialShader::initialize()
{
DistanceFieldStyledTextMaterialShader::initialize();
m_outlineAlphaMax0_id = program()->uniformLocation("outlineAlphaMax0");
m_outlineAlphaMax1_id = program()->uniformLocation("outlineAlphaMax1");
}
void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(int dfRadius)
{
float combinedScale = m_fontScale * m_matrixScale;
float base = thresholdFunc(combinedScale);
float range = spreadFunc(combinedScale);
float outlineLimit = qMax(0.2f, base - 0.5f / dfRadius / m_fontScale);
float alphaMin = qMax(0.0f, base - range);
float styleAlphaMin0 = qMax(0.0f, outlineLimit - range);
float styleAlphaMin1 = qMin(outlineLimit + range, alphaMin);
program()->setUniformValue(m_outlineAlphaMax0_id, GLfloat(styleAlphaMin0));
program()->setUniformValue(m_outlineAlphaMax1_id, GLfloat(styleAlphaMin1));
}
void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
DistanceFieldStyledTextMaterialShader::updateState(state, newEffect, oldEffect);
QSGDistanceFieldOutlineTextMaterial *material = static_cast<QSGDistanceFieldOutlineTextMaterial *>(newEffect);
QSGDistanceFieldOutlineTextMaterial *oldMaterial = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldEffect);
if (oldMaterial == nullptr
|| material->fontScale() != oldMaterial->fontScale()
|| state.isMatrixDirty())
updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius());
}
class DistanceFieldOutlineTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
{
public:
DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
DistanceFieldOutlineTextMaterialRhiShader::DistanceFieldOutlineTextMaterialRhiShader(bool alphaTexture)
: DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
{
setShaderFileName(VertexStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.vert.qsb"));
if (alphaTexture)
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext_a.frag.qsb"));
else
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldoutlinetext.frag.qsb"));
}
bool DistanceFieldOutlineTextMaterialRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
bool changed = DistanceFieldStyledTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
QSGDistanceFieldOutlineTextMaterial *mat = static_cast<QSGDistanceFieldOutlineTextMaterial *>(newMaterial);
QSGDistanceFieldOutlineTextMaterial *oldMat = static_cast<QSGDistanceFieldOutlineTextMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 136);
if (!oldMat || mat->fontScale() != oldMat->fontScale() || state.isMatrixDirty()) {
float dfRadius = mat->glyphCache()->distanceFieldRadius();
float combinedScale = m_fontScale * m_matrixScale;
float base = thresholdFunc(combinedScale);
float range = spreadFunc(combinedScale);
float outlineLimit = qMax(0.2f, base - 0.5f / dfRadius / m_fontScale);
float alphaMin = qMax(0.0f, base - range);
float styleAlphaMin0 = qMax(0.0f, outlineLimit - range);
float styleAlphaMin1 = qMin(outlineLimit + range, alphaMin);
memcpy(buf->data() + 128, &styleAlphaMin0, 4);
memcpy(buf->data() + 132, &styleAlphaMin1, 4);
changed = true;
}
return changed;
}
QSGDistanceFieldOutlineTextMaterial::QSGDistanceFieldOutlineTextMaterial()
: QSGDistanceFieldStyledTextMaterial()
{
}
QSGDistanceFieldOutlineTextMaterial::~QSGDistanceFieldOutlineTextMaterial()
{
}
QSGMaterialType *QSGDistanceFieldOutlineTextMaterial::type() const
{
static QSGMaterialType type;
return &type;
}
QSGMaterialShader *QSGDistanceFieldOutlineTextMaterial::createShader() const
{
if (flags().testFlag(RhiShaderWanted))
return new DistanceFieldOutlineTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
else
return new DistanceFieldOutlineTextMaterialShader;
}
class DistanceFieldShiftedStyleTextMaterialShader : public DistanceFieldStyledTextMaterialShader
{
public:
DistanceFieldShiftedStyleTextMaterialShader();
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
protected:
void initialize() override;
void updateShift(qreal fontScale, const QPointF& shift);
int m_shift_id = -1;
};
DistanceFieldShiftedStyleTextMaterialShader::DistanceFieldShiftedStyleTextMaterialShader()
: DistanceFieldStyledTextMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldshiftedtext.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldshiftedtext.frag"));
}
void DistanceFieldShiftedStyleTextMaterialShader::initialize()
{
DistanceFieldStyledTextMaterialShader::initialize();
m_shift_id = program()->uniformLocation("shift");
}
void DistanceFieldShiftedStyleTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
DistanceFieldStyledTextMaterialShader::updateState(state, newEffect, oldEffect);
QSGDistanceFieldShiftedStyleTextMaterial *material = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(newEffect);
QSGDistanceFieldShiftedStyleTextMaterial *oldMaterial = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldEffect);
if (oldMaterial == nullptr
|| oldMaterial->fontScale() != material->fontScale()
|| oldMaterial->shift() != material->shift()
|| oldMaterial->textureSize() != material->textureSize()) {
updateShift(material->fontScale(), material->shift());
}
}
void DistanceFieldShiftedStyleTextMaterialShader::updateShift(qreal fontScale, const QPointF &shift)
{
QPointF texel(1.0 / fontScale * shift.x(),
1.0 / fontScale * shift.y());
program()->setUniformValue(m_shift_id, texel);
}
class DistanceFieldShiftedStyleTextMaterialRhiShader : public DistanceFieldStyledTextMaterialRhiShader
{
public:
DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
DistanceFieldShiftedStyleTextMaterialRhiShader::DistanceFieldShiftedStyleTextMaterialRhiShader(bool alphaTexture)
: DistanceFieldStyledTextMaterialRhiShader(alphaTexture)
{
setShaderFileName(VertexStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.vert.qsb"));
if (alphaTexture)
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext_a.frag.qsb"));
else
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/distancefieldshiftedtext.frag.qsb"));
}
bool DistanceFieldShiftedStyleTextMaterialRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
bool changed = DistanceFieldStyledTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
QSGDistanceFieldShiftedStyleTextMaterial *mat = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(newMaterial);
QSGDistanceFieldShiftedStyleTextMaterial *oldMat = static_cast<QSGDistanceFieldShiftedStyleTextMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 136);
if (!oldMat || oldMat->fontScale() != mat->fontScale() || oldMat->shift() != mat->shift()
|| oldMat->textureSize() != mat->textureSize())
{
QPointF shift(1.0 / mat->fontScale() * mat->shift().x(),
1.0 / mat->fontScale() * mat->shift().y());
memcpy(buf->data() + 128, &shift, 8);
changed = true;
}
return changed;
}
QSGDistanceFieldShiftedStyleTextMaterial::QSGDistanceFieldShiftedStyleTextMaterial()
: QSGDistanceFieldStyledTextMaterial()
{
}
QSGDistanceFieldShiftedStyleTextMaterial::~QSGDistanceFieldShiftedStyleTextMaterial()
{
}
QSGMaterialType *QSGDistanceFieldShiftedStyleTextMaterial::type() const
{
static QSGMaterialType type;
return &type;
}
QSGMaterialShader *QSGDistanceFieldShiftedStyleTextMaterial::createShader() const
{
if (flags().testFlag(RhiShaderWanted))
return new DistanceFieldShiftedStyleTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
else
return new DistanceFieldShiftedStyleTextMaterialShader;
}
int QSGDistanceFieldShiftedStyleTextMaterial::compare(const QSGMaterial *o) const
{
const QSGDistanceFieldShiftedStyleTextMaterial *other = static_cast<const QSGDistanceFieldShiftedStyleTextMaterial *>(o);
if (m_shift != other->m_shift)
return &m_shift < &other->m_shift ? -1 : 1;
return QSGDistanceFieldStyledTextMaterial::compare(o);
}
class QSGHiQSubPixelDistanceFieldTextMaterialShader : public QSGDistanceFieldTextMaterialShader
{
public:
QSGHiQSubPixelDistanceFieldTextMaterialShader();
void initialize() override;
void activate() override;
void deactivate() override;
void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
private:
int m_fontScale_id = -1;
int m_vecDelta_id = -1;
};
QSGHiQSubPixelDistanceFieldTextMaterialShader::QSGHiQSubPixelDistanceFieldTextMaterialShader()
: QSGDistanceFieldTextMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/hiqsubpixeldistancefieldtext.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/hiqsubpixeldistancefieldtext.frag"));
}
void QSGHiQSubPixelDistanceFieldTextMaterialShader::initialize()
{
QSGDistanceFieldTextMaterialShader::initialize();
m_fontScale_id = program()->uniformLocation("fontScale");
m_vecDelta_id = program()->uniformLocation("vecDelta");
}
void QSGHiQSubPixelDistanceFieldTextMaterialShader::activate()
{
QSGDistanceFieldTextMaterialShader::activate();
QOpenGLContext::currentContext()->functions()->glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_COLOR);
}
void QSGHiQSubPixelDistanceFieldTextMaterialShader::deactivate()
{
QSGDistanceFieldTextMaterialShader::deactivate();
QOpenGLContext::currentContext()->functions()->glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
}
void QSGHiQSubPixelDistanceFieldTextMaterialShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
Q_ASSERT(oldEffect == nullptr || newEffect->type() == oldEffect->type());
QSGDistanceFieldTextMaterial *material = static_cast<QSGDistanceFieldTextMaterial *>(newEffect);
QSGDistanceFieldTextMaterial *oldMaterial = static_cast<QSGDistanceFieldTextMaterial *>(oldEffect);
if (oldMaterial == nullptr || material->color() != oldMaterial->color()) {
QVector4D c = material->color();
state.context()->functions()->glBlendColor(c.x(), c.y(), c.z(), 1.0f);
}
if (oldMaterial == nullptr || material->fontScale() != oldMaterial->fontScale())
program()->setUniformValue(m_fontScale_id, GLfloat(material->fontScale()));
if (oldMaterial == nullptr || state.isMatrixDirty()) {
int viewportWidth = state.viewportRect().width();
QMatrix4x4 mat = state.combinedMatrix().inverted();
program()->setUniformValue(m_vecDelta_id, mat.column(0) * (qreal(2) / viewportWidth));
}
QSGDistanceFieldTextMaterialShader::updateState(state, newEffect, oldEffect);
}
class QSGHiQSubPixelDistanceFieldTextMaterialRhiShader : public QSGDistanceFieldTextMaterialRhiShader
{
public:
QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
bool updateUniformData(RenderState &state, QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
bool updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
};
QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
: QSGDistanceFieldTextMaterialRhiShader(alphaTexture)
{
setFlag(UpdatesGraphicsPipelineState, true);
setShaderFileName(VertexStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.vert.qsb"));
if (alphaTexture)
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext_a.frag.qsb"));
else
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/hiqsubpixeldistancefieldtext.frag.qsb"));
}
bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateUniformData(RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
bool changed = QSGDistanceFieldTextMaterialRhiShader::updateUniformData(state, newMaterial, oldMaterial);
QSGHiQSubPixelDistanceFieldTextMaterial *mat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(newMaterial);
QSGHiQSubPixelDistanceFieldTextMaterial *oldMat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(oldMaterial);
QByteArray *buf = state.uniformData();
Q_ASSERT(buf->size() >= 128);
if (!oldMat || mat->fontScale() != oldMat->fontScale()) {
float fontScale = mat->fontScale();
memcpy(buf->data() + 104, &fontScale, 4);
changed = true;
}
if (!oldMat || state.isMatrixDirty()) {
int viewportWidth = state.viewportRect().width();
QMatrix4x4 mat = state.combinedMatrix().inverted();
QVector4D vecDelta = mat.column(0) * (qreal(2) / viewportWidth);
memcpy(buf->data() + 112, &vecDelta, 16);
}
return changed;
}
bool QSGHiQSubPixelDistanceFieldTextMaterialRhiShader::updateGraphicsPipelineState(RenderState &state, GraphicsPipelineState *ps,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
{
Q_UNUSED(state);
Q_UNUSED(oldMaterial);
QSGHiQSubPixelDistanceFieldTextMaterial *mat = static_cast<QSGHiQSubPixelDistanceFieldTextMaterial *>(newMaterial);
ps->blendEnable = true;
ps->srcColor = GraphicsPipelineState::ConstantColor;
ps->dstColor = GraphicsPipelineState::OneMinusSrcColor;
const QVector4D color = mat->color();
// this is dynamic state but it's - magic! - taken care of by the renderer
ps->blendConstant = QColor::fromRgbF(color.x(), color.y(), color.z(), 1.0f);
return true;
}
QSGMaterialType *QSGHiQSubPixelDistanceFieldTextMaterial::type() const
{
static QSGMaterialType type;
return &type;
}
QSGMaterialShader *QSGHiQSubPixelDistanceFieldTextMaterial::createShader() const
{
if (flags().testFlag(RhiShaderWanted))
return new QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
else
return new QSGHiQSubPixelDistanceFieldTextMaterialShader;
}
class QSGLoQSubPixelDistanceFieldTextMaterialShader : public QSGHiQSubPixelDistanceFieldTextMaterialShader
{
public:
QSGLoQSubPixelDistanceFieldTextMaterialShader();
};
QSGLoQSubPixelDistanceFieldTextMaterialShader::QSGLoQSubPixelDistanceFieldTextMaterialShader()
: QSGHiQSubPixelDistanceFieldTextMaterialShader()
{
setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/scenegraph/shaders/loqsubpixeldistancefieldtext.vert"));
setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/loqsubpixeldistancefieldtext.frag"));
}
class QSGLoQSubPixelDistanceFieldTextMaterialRhiShader : public QSGHiQSubPixelDistanceFieldTextMaterialRhiShader
{
public:
QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture);
};
QSGLoQSubPixelDistanceFieldTextMaterialRhiShader::QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(bool alphaTexture)
: QSGHiQSubPixelDistanceFieldTextMaterialRhiShader(alphaTexture)
{
setShaderFileName(VertexStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.vert.qsb"));
if (alphaTexture)
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext_a.frag.qsb"));
else
setShaderFileName(FragmentStage,
QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/loqsubpixeldistancefieldtext.frag.qsb"));
}
QSGMaterialType *QSGLoQSubPixelDistanceFieldTextMaterial::type() const
{
static QSGMaterialType type;
return &type;
}
QSGMaterialShader *QSGLoQSubPixelDistanceFieldTextMaterial::createShader() const
{
if (flags().testFlag(RhiShaderWanted))
return new QSGLoQSubPixelDistanceFieldTextMaterialRhiShader(m_glyph_cache->eightBitFormatIsAlphaSwizzled());
else
return new QSGLoQSubPixelDistanceFieldTextMaterialShader;
}
QT_END_NAMESPACE