File indexing completed on 2025-01-12 03:40:53
0001 /* 0002 SPDX-FileCopyrightText: 2010 Marco Martin <mart@kde.org> 0003 SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "svgitem.h" 0009 0010 #include <QDebug> 0011 #include <QQuickWindow> 0012 #include <QRectF> 0013 #include <QSGTexture> 0014 0015 #include "ksvg/svg.h" 0016 0017 #include "managedtexturenode.h" 0018 0019 #include <cmath> //floor() 0020 0021 #include <Kirigami/Platform/PlatformTheme> 0022 #include <debug_p.h> 0023 0024 namespace KSvg 0025 { 0026 SvgItem::SvgItem(QQuickItem *parent) 0027 : QQuickItem(parent) 0028 , m_textureChanged(false) 0029 { 0030 m_svg = new KSvg::Svg(this); 0031 setFlag(QQuickItem::ItemHasContents, true); 0032 0033 connect(m_svg, &Svg::repaintNeeded, this, &SvgItem::updateNeeded); 0034 connect(m_svg, &Svg::repaintNeeded, this, &SvgItem::naturalSizeChanged); 0035 connect(m_svg, &Svg::sizeChanged, this, &SvgItem::naturalSizeChanged); 0036 connect(m_svg, &Svg::repaintNeeded, this, &SvgItem::elementRectChanged); 0037 connect(m_svg, &Svg::sizeChanged, this, &SvgItem::elementRectChanged); 0038 } 0039 0040 SvgItem::~SvgItem() 0041 { 0042 } 0043 0044 void SvgItem::componentComplete() 0045 { 0046 m_kirigamiTheme = qobject_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true)); 0047 if (!m_kirigamiTheme) { 0048 qCWarning(LOG_KSVGQML) << "No theme!" << qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true) << this; 0049 return; 0050 } 0051 0052 auto checkApplyTheme = [this]() { 0053 if (!m_svg->imageSet()->filePath(QStringLiteral("colors")).isEmpty()) { 0054 m_svg->clearColorOverrides(); 0055 } 0056 }; 0057 auto applyTheme = [this]() { 0058 if (!m_svg) { 0059 return; 0060 } 0061 if (!m_svg->imageSet()->filePath(QStringLiteral("colors")).isEmpty()) { 0062 m_svg->clearColorOverrides(); 0063 return; 0064 } 0065 m_svg->setColor(Svg::Text, m_kirigamiTheme->textColor()); 0066 m_svg->setColor(Svg::Background, m_kirigamiTheme->backgroundColor()); 0067 m_svg->setColor(Svg::Highlight, m_kirigamiTheme->highlightColor()); 0068 m_svg->setColor(Svg::HighlightedText, m_kirigamiTheme->highlightedTextColor()); 0069 m_svg->setColor(Svg::PositiveText, m_kirigamiTheme->positiveTextColor()); 0070 m_svg->setColor(Svg::NeutralText, m_kirigamiTheme->neutralTextColor()); 0071 m_svg->setColor(Svg::NegativeText, m_kirigamiTheme->negativeTextColor()); 0072 }; 0073 applyTheme(); 0074 connect(m_kirigamiTheme, &Kirigami::Platform::PlatformTheme::colorsChanged, this, applyTheme); 0075 connect(m_svg->imageSet(), &ImageSet::imageSetChanged, this, checkApplyTheme); 0076 connect(m_svg, &Svg::imageSetChanged, this, checkApplyTheme); 0077 0078 QQuickItem::componentComplete(); 0079 } 0080 0081 void SvgItem::setImagePath(const QString &path) 0082 { 0083 if (!m_svg || m_svg->imagePath() == path) { 0084 return; 0085 } 0086 0087 updateDevicePixelRatio(); 0088 m_svg->setImagePath(path); 0089 0090 Q_EMIT imagePathChanged(); 0091 0092 if (isComponentComplete()) { 0093 update(); 0094 } 0095 } 0096 0097 QString SvgItem::imagePath() const 0098 { 0099 return m_svg->imagePath(); 0100 } 0101 0102 void SvgItem::setElementId(const QString &elementID) 0103 { 0104 if (elementID == m_elementID) { 0105 return; 0106 } 0107 0108 if (implicitWidth() <= 0) { 0109 setImplicitWidth(naturalSize().width()); 0110 } 0111 if (implicitHeight() <= 0) { 0112 setImplicitHeight(naturalSize().height()); 0113 } 0114 0115 m_elementID = elementID; 0116 Q_EMIT elementIdChanged(); 0117 Q_EMIT naturalSizeChanged(); 0118 Q_EMIT elementRectChanged(); 0119 0120 scheduleImageUpdate(); 0121 } 0122 0123 QString SvgItem::elementId() const 0124 { 0125 return m_elementID; 0126 } 0127 0128 void SvgItem::setSvg(KSvg::Svg *svg) 0129 { 0130 if (m_svg) { 0131 disconnect(m_svg.data(), nullptr, this, nullptr); 0132 } 0133 m_svg = svg; 0134 0135 if (svg) { 0136 connect(svg, &Svg::repaintNeeded, this, &SvgItem::updateNeeded); 0137 connect(svg, &Svg::repaintNeeded, this, &SvgItem::naturalSizeChanged); 0138 connect(svg, &Svg::repaintNeeded, this, &SvgItem::elementRectChanged); 0139 connect(svg, &Svg::sizeChanged, this, &SvgItem::naturalSizeChanged); 0140 connect(svg, &Svg::sizeChanged, this, &SvgItem::elementRectChanged); 0141 } 0142 0143 if (implicitWidth() <= 0) { 0144 setImplicitWidth(naturalSize().width()); 0145 } 0146 if (implicitHeight() <= 0) { 0147 setImplicitHeight(naturalSize().height()); 0148 } 0149 0150 scheduleImageUpdate(); 0151 0152 Q_EMIT svgChanged(); 0153 Q_EMIT naturalSizeChanged(); 0154 Q_EMIT elementRectChanged(); 0155 Q_EMIT imagePathChanged(); 0156 } 0157 0158 KSvg::Svg *SvgItem::svg() const 0159 { 0160 return m_svg.data(); 0161 } 0162 0163 QSizeF SvgItem::naturalSize() const 0164 { 0165 if (!m_svg) { 0166 return QSizeF(); 0167 } else if (!m_elementID.isEmpty()) { 0168 return m_svg->elementSize(m_elementID); 0169 } 0170 0171 return m_svg->size(); 0172 } 0173 0174 QRectF SvgItem::elementRect() const 0175 { 0176 if (!m_svg) { 0177 return QRectF(); 0178 } else if (!m_elementID.isEmpty()) { 0179 return m_svg->elementRect(m_elementID); 0180 } 0181 0182 return QRectF(QPointF(0, 0), m_svg->size()); 0183 } 0184 0185 QSGNode *SvgItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) 0186 { 0187 Q_UNUSED(updatePaintNodeData); 0188 if (!window() || !m_svg) { 0189 delete oldNode; 0190 return nullptr; 0191 } 0192 0193 // this is more than just an optimization, uploading a null image to QSGAtlasTexture causes a crash 0194 if (width() == 0.0 || height() == 0.0) { 0195 delete oldNode; 0196 return nullptr; 0197 } 0198 0199 ManagedTextureNode *textureNode = static_cast<ManagedTextureNode *>(oldNode); 0200 if (!textureNode) { 0201 textureNode = new ManagedTextureNode; 0202 m_textureChanged = true; 0203 } 0204 0205 // TODO use a heuristic to work out when to redraw 0206 // if !m_smooth and size is approximate simply change the textureNode.rect without 0207 // updating the material 0208 0209 if (m_textureChanged || textureNode->texture()->textureSize() != QSize(width(), height())) { 0210 // despite having a valid size sometimes we still get a null QImage from KSvg::Svg 0211 // loading a null texture to an atlas fatals 0212 // Dave E fixed this in Qt in 5.3.something onwards but we need this for now 0213 if (m_image.isNull()) { 0214 delete textureNode; 0215 return nullptr; 0216 } 0217 0218 QSharedPointer<QSGTexture> texture(window()->createTextureFromImage(m_image, QQuickWindow::TextureCanUseAtlas)); 0219 textureNode->setTexture(texture); 0220 m_textureChanged = false; 0221 0222 textureNode->setRect(0, 0, width(), height()); 0223 } 0224 0225 textureNode->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); 0226 0227 return textureNode; 0228 } 0229 0230 void SvgItem::updateNeeded() 0231 { 0232 if (implicitWidth() <= 0) { 0233 setImplicitWidth(naturalSize().width()); 0234 } 0235 if (implicitHeight() <= 0) { 0236 setImplicitHeight(naturalSize().height()); 0237 } 0238 scheduleImageUpdate(); 0239 } 0240 0241 void SvgItem::scheduleImageUpdate() 0242 { 0243 polish(); 0244 update(); 0245 } 0246 0247 void SvgItem::updatePolish() 0248 { 0249 QQuickItem::updatePolish(); 0250 0251 if (m_svg) { 0252 // setContainsMultipleImages has to be done there since m_svg can be shared with somebody else 0253 m_textureChanged = true; 0254 m_svg->setContainsMultipleImages(!m_elementID.isEmpty()); 0255 m_image = m_svg->image(QSize(width(), height()), m_elementID); 0256 } 0257 } 0258 0259 void SvgItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) 0260 { 0261 if (newGeometry.size() != oldGeometry.size() && newGeometry.isValid()) { 0262 scheduleImageUpdate(); 0263 } 0264 0265 QQuickItem::geometryChange(newGeometry, oldGeometry); 0266 } 0267 0268 void SvgItem::updateDevicePixelRatio() 0269 { 0270 // devicepixelratio is always set integer in the svg, so needs at least 192dpi to double up. 0271 //(it needs to be integer to have lines contained inside a svg piece to keep being pixel aligned) 0272 const auto newDevicePixelRatio = std::max<qreal>(1.0, floor(window() ? window()->devicePixelRatio() : qApp->devicePixelRatio())); 0273 0274 if (newDevicePixelRatio != m_svg->devicePixelRatio()) { 0275 m_svg->setDevicePixelRatio(newDevicePixelRatio); 0276 m_textureChanged = true; 0277 } 0278 } 0279 0280 void SvgItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) 0281 { 0282 if (change == ItemSceneChange && value.window) { 0283 updateDevicePixelRatio(); 0284 } else if (change == QQuickItem::ItemDevicePixelRatioHasChanged) { 0285 updateDevicePixelRatio(); 0286 } 0287 0288 QQuickItem::itemChange(change, value); 0289 } 0290 0291 } // KSvg namespace 0292 0293 #include "moc_svgitem.cpp"