File indexing completed on 2024-04-28 03:56:00

0001 /*
0002  *  SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "shadowedrectanglenode.h"
0008 #include "shadowedborderrectanglematerial.h"
0009 
0010 QColor premultiply(const QColor &color)
0011 {
0012     return QColor::fromRgbF(color.redF() * color.alphaF(), //
0013                             color.greenF() * color.alphaF(),
0014                             color.blueF() * color.alphaF(),
0015                             color.alphaF());
0016 }
0017 
0018 ShadowedRectangleNode::ShadowedRectangleNode()
0019 {
0020     m_geometry = new QSGGeometry{QSGGeometry::defaultAttributes_TexturedPoint2D(), 4};
0021     setGeometry(m_geometry);
0022 
0023     setFlags(QSGNode::OwnsGeometry | QSGNode::OwnsMaterial);
0024 }
0025 
0026 void ShadowedRectangleNode::setBorderEnabled(bool enabled)
0027 {
0028     // We can achieve more performant shaders by splitting the two into separate
0029     // shaders. This requires separating the materials as well. So when
0030     // borderWidth is increased to something where the border should be visible,
0031     // switch to the with-border material. Otherwise use the no-border version.
0032 
0033     if (enabled) {
0034         if (!m_material || m_material->type() == borderlessMaterialType()) {
0035             auto newMaterial = createBorderMaterial();
0036             newMaterial->shaderType = m_shaderType;
0037             setMaterial(newMaterial);
0038             m_material = newMaterial;
0039             m_rect = QRectF{};
0040             markDirty(QSGNode::DirtyMaterial);
0041         }
0042     } else {
0043         if (!m_material || m_material->type() == borderMaterialType()) {
0044             auto newMaterial = createBorderlessMaterial();
0045             newMaterial->shaderType = m_shaderType;
0046             setMaterial(newMaterial);
0047             m_material = newMaterial;
0048             m_rect = QRectF{};
0049             markDirty(QSGNode::DirtyMaterial);
0050         }
0051     }
0052 }
0053 
0054 void ShadowedRectangleNode::setRect(const QRectF &rect)
0055 {
0056     if (rect == m_rect) {
0057         return;
0058     }
0059 
0060     m_rect = rect;
0061 
0062     QVector2D newAspect{1.0, 1.0};
0063     if (m_rect.width() >= m_rect.height()) {
0064         newAspect.setX(m_rect.width() / m_rect.height());
0065     } else {
0066         newAspect.setY(m_rect.height() / m_rect.width());
0067     }
0068 
0069     if (m_material->aspect != newAspect) {
0070         m_material->aspect = newAspect;
0071         markDirty(QSGNode::DirtyMaterial);
0072         m_aspect = newAspect;
0073     }
0074 }
0075 
0076 void ShadowedRectangleNode::setSize(qreal size)
0077 {
0078     auto minDimension = std::min(m_rect.width(), m_rect.height());
0079     float uniformSize = (size / minDimension) * 2.0;
0080 
0081     if (!qFuzzyCompare(m_material->size, uniformSize)) {
0082         m_material->size = uniformSize;
0083         markDirty(QSGNode::DirtyMaterial);
0084         m_size = size;
0085     }
0086 }
0087 
0088 void ShadowedRectangleNode::setRadius(const QVector4D &radius)
0089 {
0090     float minDimension = std::min(m_rect.width(), m_rect.height());
0091     auto uniformRadius = QVector4D{std::min(radius.x() * 2.0f / minDimension, 1.0f),
0092                                    std::min(radius.y() * 2.0f / minDimension, 1.0f),
0093                                    std::min(radius.z() * 2.0f / minDimension, 1.0f),
0094                                    std::min(radius.w() * 2.0f / minDimension, 1.0f)};
0095 
0096     if (m_material->radius != uniformRadius) {
0097         m_material->radius = uniformRadius;
0098         markDirty(QSGNode::DirtyMaterial);
0099         m_radius = radius;
0100     }
0101 }
0102 
0103 void ShadowedRectangleNode::setColor(const QColor &color)
0104 {
0105     auto premultiplied = premultiply(color);
0106     if (m_material->color != premultiplied) {
0107         m_material->color = premultiplied;
0108         markDirty(QSGNode::DirtyMaterial);
0109     }
0110 }
0111 
0112 void ShadowedRectangleNode::setShadowColor(const QColor &color)
0113 {
0114     auto premultiplied = premultiply(color);
0115     if (m_material->shadowColor != premultiplied) {
0116         m_material->shadowColor = premultiplied;
0117         markDirty(QSGNode::DirtyMaterial);
0118     }
0119 }
0120 
0121 void ShadowedRectangleNode::setOffset(const QVector2D &offset)
0122 {
0123     auto minDimension = std::min(m_rect.width(), m_rect.height());
0124     auto uniformOffset = offset / minDimension;
0125 
0126     if (m_material->offset != uniformOffset) {
0127         m_material->offset = uniformOffset;
0128         markDirty(QSGNode::DirtyMaterial);
0129         m_offset = offset;
0130     }
0131 }
0132 
0133 void ShadowedRectangleNode::setBorderWidth(qreal width)
0134 {
0135     if (m_material->type() != borderMaterialType()) {
0136         return;
0137     }
0138 
0139     auto minDimension = std::min(m_rect.width(), m_rect.height());
0140     float uniformBorderWidth = width / minDimension;
0141 
0142     auto borderMaterial = static_cast<ShadowedBorderRectangleMaterial *>(m_material);
0143     if (!qFuzzyCompare(borderMaterial->borderWidth, uniformBorderWidth)) {
0144         borderMaterial->borderWidth = uniformBorderWidth;
0145         markDirty(QSGNode::DirtyMaterial);
0146         m_borderWidth = width;
0147     }
0148 }
0149 
0150 void ShadowedRectangleNode::setBorderColor(const QColor &color)
0151 {
0152     if (m_material->type() != borderMaterialType()) {
0153         return;
0154     }
0155 
0156     auto borderMaterial = static_cast<ShadowedBorderRectangleMaterial *>(m_material);
0157     auto premultiplied = premultiply(color);
0158     if (borderMaterial->borderColor != premultiplied) {
0159         borderMaterial->borderColor = premultiplied;
0160         markDirty(QSGNode::DirtyMaterial);
0161     }
0162 }
0163 
0164 void ShadowedRectangleNode::setShaderType(ShadowedRectangleMaterial::ShaderType type)
0165 {
0166     m_shaderType = type;
0167 }
0168 
0169 void ShadowedRectangleNode::updateGeometry()
0170 {
0171     auto rect = m_rect;
0172     if (m_shaderType == ShadowedRectangleMaterial::ShaderType::Standard) {
0173         rect = rect.adjusted(-m_size * m_aspect.x(), //
0174                              -m_size * m_aspect.y(),
0175                              m_size * m_aspect.x(),
0176                              m_size * m_aspect.y());
0177 
0178         auto offsetLength = m_offset.length();
0179         rect = rect.adjusted(-offsetLength * m_aspect.x(), //
0180                              -offsetLength * m_aspect.y(),
0181                              offsetLength * m_aspect.x(),
0182                              offsetLength * m_aspect.y());
0183     }
0184 
0185     QSGGeometry::updateTexturedRectGeometry(m_geometry, rect, QRectF{0.0, 0.0, 1.0, 1.0});
0186     markDirty(QSGNode::DirtyGeometry);
0187 }
0188 
0189 ShadowedRectangleMaterial *ShadowedRectangleNode::createBorderlessMaterial()
0190 {
0191     return new ShadowedRectangleMaterial{};
0192 }
0193 
0194 ShadowedBorderRectangleMaterial *ShadowedRectangleNode::createBorderMaterial()
0195 {
0196     return new ShadowedBorderRectangleMaterial{};
0197 }
0198 
0199 QSGMaterialType *ShadowedRectangleNode::borderlessMaterialType()
0200 {
0201     return &ShadowedRectangleMaterial::staticType;
0202 }
0203 
0204 QSGMaterialType *ShadowedRectangleNode::borderMaterialType()
0205 {
0206     return &ShadowedBorderRectangleMaterial::staticType;
0207 }