Revision control
++ // 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 "qsgcurvefillnode_p_p.h"
++ #include "qsgcurvefillnode_p.h"
++ #include "util/qsggradientcache_p.h"
++
++ #include <private/qsgtexture_p.h>
++ #include <private/qsgplaintexture_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace {
++
++ class QSGCurveFillMaterialShader : public QSGMaterialShader
++ {
++ public:
++ QSGCurveFillMaterialShader(QGradient::Type gradientType,
++ bool useTextureFill,
++ bool useDerivatives,
++ int viewCount);
++
++ bool updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
++ void updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
++ QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
++ };
++
++ QSGCurveFillMaterialShader::QSGCurveFillMaterialShader(QGradient::Type gradientType,
++ bool useTextureFill,
++ bool useDerivatives,
++ int viewCount)
++ {
++ QString baseName = QStringLiteral(":/qt-project.org/scenegraph/shaders_ng/shapecurve");
++
++ if (gradientType == QGradient::LinearGradient) {
++ baseName += QStringLiteral("_lg");
++ } else if (gradientType == QGradient::RadialGradient) {
++ baseName += QStringLiteral("_rg");
++ } else if (gradientType == QGradient::ConicalGradient) {
++ baseName += QStringLiteral("_cg");
++ } else if (useTextureFill) {
++ baseName += QStringLiteral("_tf");
++ }
++
++ if (useDerivatives)
++ baseName += QStringLiteral("_derivatives");
++
++ setShaderFileName(VertexStage, baseName + QStringLiteral(".vert.qsb"), viewCount);
++ setShaderFileName(FragmentStage, baseName + QStringLiteral(".frag.qsb"), viewCount);
++ }
++
++ void QSGCurveFillMaterialShader::updateSampledImage(RenderState &state, int binding, QSGTexture **texture,
++ QSGMaterial *newMaterial, QSGMaterial *oldMaterial)
++ {
++ Q_UNUSED(oldMaterial);
++ QSGCurveFillMaterial *m = static_cast<QSGCurveFillMaterial *>(newMaterial);
++ const QSGCurveFillNode *node = m->node();
++ if (binding != 1
++ || (node->gradientType() == QGradient::NoGradient && node->fillTextureProvider() == nullptr)) {
++ return;
++ }
++
++ QSGTexture *t = nullptr;
++ if (node->gradientType() != QGradient::NoGradient) {
++ const QSGGradientCacheKey cacheKey(node->fillGradient()->stops,
++ node->fillGradient()->spread);
++ t = QSGGradientCache::cacheForRhi(state.rhi())->get(cacheKey);
++ } else if (node->fillTextureProvider() != nullptr) {
++ t = node->fillTextureProvider()->texture();
++ if (t != nullptr && t->isAtlasTexture()) {
++ // Create a non-atlas copy to make texture coordinate wrapping work. This
++ // texture copy is owned by the QSGTexture so memory is managed with the original
++ // texture provider.
++ QSGTexture *newTexture = t->removedFromAtlas(state.resourceUpdateBatch());
++ if (newTexture != nullptr)
++ t = newTexture;
++ }
++
++ }
++
++ if (t != nullptr) {
++ t->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
++ } else {
++ if (m->dummyTexture() == nullptr) {
++ QSGPlainTexture *dummyTexture = new QSGPlainTexture;
++ dummyTexture->setFiltering(QSGTexture::Nearest);
++ dummyTexture->setHorizontalWrapMode(QSGTexture::Repeat);
++ dummyTexture->setVerticalWrapMode(QSGTexture::Repeat);
++ QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
++ img.fill(0);
++ dummyTexture->setImage(img);
++ dummyTexture->commitTextureOperations(state.rhi(), state.resourceUpdateBatch());
++
++ m->setDummyTexture(dummyTexture);
++ }
++
++ t = m->dummyTexture();
++ }
++
++ *texture = t;
++ }
++
++ bool QSGCurveFillMaterialShader::updateUniformData(RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
++ {
++ bool changed = false;
++ QByteArray *buf = state.uniformData();
++ Q_ASSERT(buf->size() >= 80);
++ const int matrixCount = qMin(state.projectionMatrixCount(), newEffect->viewCount());
++
++ int offset = 0;
++ float matrixScale = 0.0f;
++ if (state.isMatrixDirty()) {
++ for (int viewIndex = 0; viewIndex < matrixCount; ++viewIndex) {
++ const QMatrix4x4 m = state.combinedMatrix(viewIndex);
++ memcpy(buf->data() + offset + viewIndex * 64, m.constData(), 64);
++ }
++
++ matrixScale = qSqrt(qAbs(state.determinant())) * state.devicePixelRatio();
++ memcpy(buf->data() + offset + newEffect->viewCount() * 64, &matrixScale, 4);
++
++ changed = true;
++ }
++ offset += newEffect->viewCount() * 64 + 4;
++
++ if (state.isOpacityDirty()) {
++ const float opacity = state.opacity();
++ memcpy(buf->data() + offset, &opacity, 4);
++ changed = true;
++ }
++ offset += 4;
++
++ QSGCurveFillMaterial *newMaterial = static_cast<QSGCurveFillMaterial *>(newEffect);
++ QSGCurveFillMaterial *oldMaterial = static_cast<QSGCurveFillMaterial *>(oldEffect);
++
++ QSGCurveFillNode *newNode = newMaterial != nullptr ? newMaterial->node() : nullptr;
++ QSGCurveFillNode *oldNode = oldMaterial != nullptr ? oldMaterial->node() : nullptr;
++
++ if (newNode == nullptr)
++ return changed;
++
++ if (oldNode == nullptr || oldNode->debug() != newNode->debug()) {
++ float debug = newNode->debug();
++ memcpy(buf->data() + offset, &debug, 4);
++ changed = true;
++ }
++ offset += 8;
++
++ if (newNode->gradientType() == QGradient::NoGradient
++ && newNode->fillTextureProvider() == nullptr) {
++ Q_ASSERT(buf->size() >= offset + 16);
++
++ QVector4D newColor = QVector4D(newNode->color().redF(),
++ newNode->color().greenF(),
++ newNode->color().blueF(),
++ newNode->color().alphaF());
++ QVector4D oldColor = oldNode != nullptr
++ ? QVector4D(oldNode->color().redF(),
++ oldNode->color().greenF(),
++ oldNode->color().blueF(),
++ oldNode->color().alphaF())
++ : QVector4D{};
++
++ if (oldNode == nullptr || oldColor != newColor) {
++ memcpy(buf->data() + offset, &newColor, 16);
++ changed = true;
++ }
++
++ offset += 16;
++ } else {
++ Q_ASSERT(buf->size() >= offset + 64);
++
++ if (!oldNode || *oldNode->fillTransform() != *newNode->fillTransform()) {
++ memcpy(buf->data() + offset, newNode->fillTransform()->invertedData(), 64);
++ changed = true;
++ }
++
++ offset += 64;
++ }
++
++ if (newNode->gradientType() == QGradient::NoGradient
++ && newNode->fillTextureProvider() != nullptr) {
++ Q_ASSERT(buf->size() >= offset + 8);
++ const QSizeF newTextureSize = newNode->fillTextureProvider()->texture() != nullptr
++ ? newNode->fillTextureProvider()->texture()->textureSize()
++ : QSizeF(0, 0);
++ const QVector2D newBoundsSize(newTextureSize.width() / state.devicePixelRatio(),
++ newTextureSize.height() / state.devicePixelRatio());
++ const QVector2D oldBoundsSize = oldNode != nullptr
++ ? oldNode->boundsSize()
++ : QVector2D{};
++
++ if (oldEffect == nullptr || newBoundsSize != oldBoundsSize) {
++ newNode->setBoundsSize(newBoundsSize);
++ memcpy(buf->data() + offset, &newBoundsSize, 8);
++ changed = true;
++ }
++ offset += 8;
++
++ } else if (newNode->gradientType() == QGradient::LinearGradient) {
++ Q_ASSERT(buf->size() >= offset + 8 + 8);
++
++ QVector2D newGradientStart = QVector2D(newNode->fillGradient()->a);
++ QVector2D oldGradientStart = oldNode != nullptr
++ ? QVector2D(oldNode->fillGradient()->a)
++ : QVector2D{};
++
++ if (newGradientStart != oldGradientStart || oldEffect == nullptr) {
++ memcpy(buf->data() + offset, &newGradientStart, 8);
++ changed = true;
++ }
++ offset += 8;
++
++ QVector2D newGradientEnd = QVector2D(newNode->fillGradient()->b);
++ QVector2D oldGradientEnd = oldNode!= nullptr
++ ? QVector2D(oldNode->fillGradient()->b)
++ : QVector2D{};
++
++ if (newGradientEnd != oldGradientEnd || oldEffect == nullptr) {
++ memcpy(buf->data() + offset, &newGradientEnd, 8);
++ changed = true;
++ }
++
++ offset += 8;
++ } else if (newNode->gradientType() == QGradient::RadialGradient) {
++ Q_ASSERT(buf->size() >= offset + 8 + 8 + 4 + 4);
++
++ QVector2D newFocalPoint = QVector2D(newNode->fillGradient()->b);
++ QVector2D oldFocalPoint = oldNode != nullptr
++ ? QVector2D(oldNode->fillGradient()->b)
++ : QVector2D{};
++ if (oldNode == nullptr || newFocalPoint != oldFocalPoint) {
++ memcpy(buf->data() + offset, &newFocalPoint, 8);
++ changed = true;
++ }
++ offset += 8;
++
++ QVector2D newCenterPoint = QVector2D(newNode->fillGradient()->a);
++ QVector2D oldCenterPoint = oldNode != nullptr
++ ? QVector2D(oldNode->fillGradient()->a)
++ : QVector2D{};
++
++ QVector2D newCenterToFocal = newCenterPoint - newFocalPoint;
++ QVector2D oldCenterToFocal = oldCenterPoint - oldFocalPoint;
++ if (oldNode == nullptr || newCenterToFocal != oldCenterToFocal) {
++ memcpy(buf->data() + offset, &newCenterToFocal, 8);
++ changed = true;
++ }
++ offset += 8;
++
++ float newCenterRadius = newNode->fillGradient()->v0;
++ float oldCenterRadius = oldNode != nullptr
++ ? oldNode->fillGradient()->v0
++ : 0.0f;
++ if (oldNode == nullptr || !qFuzzyCompare(newCenterRadius, oldCenterRadius)) {
++ memcpy(buf->data() + offset, &newCenterRadius, 4);
++ changed = true;
++ }
++ offset += 4;
++
++ float newFocalRadius = newNode->fillGradient()->v1;
++ float oldFocalRadius = oldNode != nullptr
++ ? oldNode->fillGradient()->v1
++ : 0.0f;
++ if (oldNode == nullptr || !qFuzzyCompare(newFocalRadius, oldFocalRadius)) {
++ memcpy(buf->data() + offset, &newFocalRadius, 4);
++ changed = true;
++ }
++ offset += 4;
++
++ } else if (newNode->gradientType() == QGradient::ConicalGradient) {
++ Q_ASSERT(buf->size() >= offset + 8 + 4);
++
++ QVector2D newFocalPoint = QVector2D(newNode->fillGradient()->a);
++ QVector2D oldFocalPoint = oldNode != nullptr
++ ? QVector2D(oldNode->fillGradient()->a)
++ : QVector2D{};
++ if (oldNode == nullptr || newFocalPoint != oldFocalPoint) {
++ memcpy(buf->data() + offset, &newFocalPoint, 8);
++ changed = true;
++ }
++ offset += 8;
++
++ float newAngle = newNode->fillGradient()->v0;
++ float oldAngle = oldNode != nullptr
++ ? oldNode->fillGradient()->v0
++ : 0.0f;
++ if (oldNode == nullptr || !qFuzzyCompare(newAngle, oldAngle)) {
++ newAngle = -qDegreesToRadians(newAngle);
++ memcpy(buf->data() + offset, &newAngle, 4);
++ changed = true;
++ }
++ offset += 4;
++ }
++
++ return changed;
++ }
++
++ }
++
++ QSGCurveFillMaterial::QSGCurveFillMaterial(QSGCurveFillNode *node)
++ : m_node(node)
++ {
++ setFlag(Blending, true);
++ setFlag(RequiresDeterminant, true);
++ }
++
++ QSGCurveFillMaterial::~QSGCurveFillMaterial()
++ {
++ delete m_dummyTexture;
++ }
++
++ int QSGCurveFillMaterial::compare(const QSGMaterial *other) const
++ {
++ if (other->type() != type())
++ return (type() - other->type());
++
++ const QSGCurveFillMaterial *otherMaterial =
++ static_cast<const QSGCurveFillMaterial *>(other);
++
++ QSGCurveFillNode *a = node();
++ QSGCurveFillNode *b = otherMaterial->node();
++ if (a == b)
++ return 0;
++
++ if (a->gradientType() == QGradient::NoGradient && a->fillTextureProvider() == nullptr) {
++ if (int d = a->color().red() - b->color().red())
++ return d;
++ if (int d = a->color().green() - b->color().green())
++ return d;
++ if (int d = a->color().blue() - b->color().blue())
++ return d;
++ if (int d = a->color().alpha() - b->color().alpha())
++ return d;
++ } else {
++ if (a->gradientType() != QGradient::NoGradient) {
++ const QSGGradientCache::GradientDesc &ga = *a->fillGradient();
++ const QSGGradientCache::GradientDesc &gb = *b->fillGradient();
++
++ if (int d = ga.compare(gb))
++ return d;
++ }
++
++ if (int d = a->fillTransform()->compareTo(*b->fillTransform()))
++ return d;
++ }
++
++ const qintptr diff = qintptr(a->fillTextureProvider()) - qintptr(b->fillTextureProvider());
++ return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
++ }
++
++ QSGMaterialType *QSGCurveFillMaterial::type() const
++ {
++ static QSGMaterialType type[5];
++ uint index = node()->gradientType();
++ Q_ASSERT((index & ~3) == 0); // Only two first bits for gradient type
++
++ if (node()->gradientType() == QGradient::NoGradient && node()->fillTextureProvider() != nullptr)
++ index = 4;
++
++ return &type[index];
++ }
++
++ QSGMaterialShader *QSGCurveFillMaterial::createShader(QSGRendererInterface::RenderMode renderMode) const
++ {
++ Q_UNUSED(renderMode);
++ return new QSGCurveFillMaterialShader(node()->gradientType(),
++ node()->gradientType() == QGradient::NoGradient
++ && node()->fillTextureProvider() != nullptr,
++ node()->useStandardDerivatives(),
++ viewCount());
++ }
++
++ QT_END_NAMESPACE