diff --git a/include/FramelessHelper/Quick/private/quickmicamaterial_p.h b/include/FramelessHelper/Quick/private/quickmicamaterial_p.h index 78e419c8..ce6acb4b 100644 --- a/include/FramelessHelper/Quick/private/quickmicamaterial_p.h +++ b/include/FramelessHelper/Quick/private/quickmicamaterial_p.h @@ -26,15 +26,10 @@ #include -QT_BEGIN_NAMESPACE -class QQuickRectangle; -QT_END_NAMESPACE - FRAMELESSHELPER_BEGIN_NAMESPACE class MicaMaterial; class QuickMicaMaterial; -class WallpaperImageNode; class FRAMELESSHELPER_QUICK_API QuickMicaMaterialPrivate : public QObject { @@ -51,23 +46,16 @@ class FRAMELESSHELPER_QUICK_API QuickMicaMaterialPrivate : public QObject public Q_SLOTS: void rebindWindow(); - void forceRegenerateWallpaperImageCache(); - void appendNode(WallpaperImageNode *node); - void removeNode(WallpaperImageNode *node); - void updateFallbackColor(); + void repaint(QPainter *painter); private: void initialize(); private: - friend class WallpaperImageNode; - QuickMicaMaterial *q_ptr = nullptr; QMetaObject::Connection m_rootWindowXChangedConnection = {}; QMetaObject::Connection m_rootWindowYChangedConnection = {}; QMetaObject::Connection m_rootWindowActiveChangedConnection = {}; - QList> m_nodes = {}; - QQuickRectangle *m_fallbackColorItem = nullptr; MicaMaterial *m_micaMaterial = nullptr; }; diff --git a/include/FramelessHelper/Quick/quickmicamaterial.h b/include/FramelessHelper/Quick/quickmicamaterial.h index b0a98990..8a0c7e8c 100644 --- a/include/FramelessHelper/Quick/quickmicamaterial.h +++ b/include/FramelessHelper/Quick/quickmicamaterial.h @@ -25,13 +25,13 @@ #pragma once #include -#include +#include FRAMELESSHELPER_BEGIN_NAMESPACE class QuickMicaMaterialPrivate; -class FRAMELESSHELPER_QUICK_API QuickMicaMaterial : public QQuickItem +class FRAMELESSHELPER_QUICK_API QuickMicaMaterial : public QQuickPaintedItem { Q_OBJECT #ifdef QML_NAMED_ELEMENT @@ -50,6 +50,8 @@ class FRAMELESSHELPER_QUICK_API QuickMicaMaterial : public QQuickItem explicit QuickMicaMaterial(QQuickItem *parent = nullptr); ~QuickMicaMaterial() override; + void paint(QPainter *painter) override; + Q_NODISCARD QColor tintColor() const; void setTintColor(const QColor &value); @@ -74,7 +76,6 @@ class FRAMELESSHELPER_QUICK_API QuickMicaMaterial : public QQuickItem protected: void itemChange(const ItemChange change, const ItemChangeData &value) override; - [[nodiscard]] QSGNode *updatePaintNode(QSGNode *old, UpdatePaintNodeData *data) override; void classBegin() override; void componentComplete() override; diff --git a/src/core/micamaterial.cpp b/src/core/micamaterial.cpp index 48dd94f4..5aff9f7b 100644 --- a/src/core/micamaterial.cpp +++ b/src/core/micamaterial.cpp @@ -704,6 +704,7 @@ void MicaMaterialPrivate::paint(QPainter *painter, const QRect &rect, const bool } prepareGraphicsResources(); static constexpr const QPoint originPoint = {0, 0}; + const QRect wallpaperRect = { originPoint, wallpaperSize() }; const QRect mappedRect = mapToWallpaper(rect); painter->save(); // Same as above. Speed is more important here. @@ -711,8 +712,39 @@ void MicaMaterialPrivate::paint(QPainter *painter, const QRect &rect, const bool painter->setRenderHint(QPainter::TextAntialiasing, false); painter->setRenderHint(QPainter::SmoothPixmapTransform, false); if (active) { - const QMutexLocker locker(&g_imageData()->mutex); - painter->drawPixmap(originPoint, g_imageData()->blurredWallpaper, mappedRect); + const QRect intersectedRect = wallpaperRect.intersected(mappedRect); + g_imageData()->mutex.lock(); + painter->drawPixmap(originPoint, g_imageData()->blurredWallpaper, intersectedRect); + g_imageData()->mutex.unlock(); + if (intersectedRect != mappedRect) { + static constexpr const auto xOffset = QPoint{ 1, 0 }; + if (mappedRect.y() + mappedRect.height() <= wallpaperRect.height()) { + const QRect outerRect = { intersectedRect.topRight() + xOffset, QSize{ mappedRect.width() - intersectedRect.width(), intersectedRect.height() } }; + const QPoint outerRectOriginPoint = originPoint + QPoint{ intersectedRect.width(), 0 } + xOffset; + const QRect mappedOuterRect = mapToWallpaper(outerRect); + const QMutexLocker locker(&g_imageData()->mutex); + painter->drawPixmap(outerRectOriginPoint, g_imageData()->blurredWallpaper, mappedOuterRect); + } else { + static constexpr const auto yOffset = QPoint{ 0, 1 }; + const QRect outerRectBottom = { intersectedRect.bottomLeft() + yOffset, QSize{ intersectedRect.width(), mappedRect.height() - intersectedRect.height() } }; + const QPoint outerRectBottomOriginPoint = originPoint + QPoint{ 0, intersectedRect.height() } + yOffset; + const QRect mappedOuterRectBottom = mapToWallpaper(outerRectBottom); + g_imageData()->mutex.lock(); + painter->drawPixmap(outerRectBottomOriginPoint, g_imageData()->blurredWallpaper, mappedOuterRectBottom); + g_imageData()->mutex.unlock(); + if (mappedRect.x() + mappedRect.width() > wallpaperRect.width()) { + const QRect outerRectRight = { intersectedRect.topRight() + xOffset, QSize{ mappedRect.width() - intersectedRect.width(), intersectedRect.height() } }; + const QPoint outerRectRightOriginPoint = originPoint + QPoint{ intersectedRect.width(), 0 } + xOffset; + const QRect mappedOuterRectRight = mapToWallpaper(outerRectRight); + const QRect outerRectCorner = { intersectedRect.bottomRight() + xOffset + yOffset, QSize{ outerRectRight.width(), outerRectBottom.height() } }; + const QPoint outerRectCornerOriginPoint = originPoint + QPoint{ intersectedRect.width(), intersectedRect.height() } + xOffset + yOffset; + const QRect mappedOuterRectCorner = mapToWallpaper(outerRectCorner); + const QMutexLocker locker(&g_imageData()->mutex); + painter->drawPixmap(outerRectRightOriginPoint, g_imageData()->blurredWallpaper, mappedOuterRectRight); + painter->drawPixmap(outerRectCornerOriginPoint, g_imageData()->blurredWallpaper, mappedOuterRectCorner); + } + } + } } painter->setCompositionMode(QPainter::CompositionMode_SourceOver); painter->setOpacity(qreal(1)); @@ -854,13 +886,13 @@ QPoint MicaMaterialPrivate::mapToWallpaper(const QPoint &pos) const while (result.x() < qreal(0)) { result.setX(result.x() + imageSize.width()); } - while (result.x() > imageSize.width()) { + while ((result.x() > imageSize.width()) || qFuzzyCompare(result.x(), imageSize.width())) { result.setX(result.x() - imageSize.width()); } while (result.y() < qreal(0)) { result.setY(result.y() + imageSize.height()); } - while (result.y() > imageSize.height()) { + while ((result.y() > imageSize.height()) || qFuzzyCompare(result.y(), imageSize.height())) { result.setY(result.y() - imageSize.height()); } return result.toPoint(); @@ -899,14 +931,7 @@ QRect MicaMaterialPrivate::mapToWallpaper(const QRect &rect) const WARNING << "The calculated mapped rectangle is not valid."; return wallpaperRect.toRect(); } - // Make sure we don't get something outside of the wallpaper area. - const QRectF intersectedRect = wallpaperRect.intersected(mappedRect); - // OK, the two rectangles are not intersected, just draw the whole wallpaper. - if (!Utils::isValidGeometry(intersectedRect)) { - WARNING << "The mapped rectangle and the wallpaper rectangle are not intersected."; - return wallpaperRect.toRect(); - } - return intersectedRect.toRect(); + return mappedRect.toRect(); } MicaMaterial::MicaMaterial(QObject *parent) diff --git a/src/quick/quickmicamaterial.cpp b/src/quick/quickmicamaterial.cpp index a081dd7b..16142e03 100644 --- a/src/quick/quickmicamaterial.cpp +++ b/src/quick/quickmicamaterial.cpp @@ -25,16 +25,10 @@ #include "quickmicamaterial.h" #include "quickmicamaterial_p.h" #include -#include -#include -#include #include -#include #include -#include #ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE # include -# include # include #endif // FRAMELESSHELPER_QUICK_NO_PRIVATE @@ -56,94 +50,6 @@ FRAMELESSHELPER_BEGIN_NAMESPACE using namespace Global; -class WallpaperImageNode : public QObject, public QSGTransformNode -{ - Q_OBJECT - Q_DISABLE_COPY_MOVE(WallpaperImageNode) - -public: - explicit WallpaperImageNode(QuickMicaMaterial *item); - ~WallpaperImageNode() override; - -public Q_SLOTS: - void maybeUpdateWallpaperImageClipRect(); - void maybeGenerateWallpaperImageCache(const bool force = false); - -private: - void initialize(); - -private: - QPointer m_item = nullptr; - std::unique_ptr m_node = nullptr; - std::unique_ptr m_texture = nullptr; - QPointer m_mica{ nullptr }; - QPointer m_micaPriv{ nullptr }; - QPointer m_window{ nullptr }; -}; - -WallpaperImageNode::WallpaperImageNode(QuickMicaMaterial *item) -{ - Q_ASSERT(item); - if (!item) { - return; - } - m_item = item; - initialize(); -} - -WallpaperImageNode::~WallpaperImageNode() -{ - QuickMicaMaterialPrivate::get(m_item)->removeNode(this); -} - -void WallpaperImageNode::initialize() -{ - m_window = m_item->window(); - m_mica = QuickMicaMaterialPrivate::get(m_item)->m_micaMaterial; - m_micaPriv = MicaMaterialPrivate::get(m_mica); - - m_node = std::make_unique(); - m_node->setFiltering(QSGTexture::Linear); - - maybeGenerateWallpaperImageCache(); - maybeUpdateWallpaperImageClipRect(); - - appendChildNode(m_node.get()); - - connect(m_window, &QQuickWindow::beforeRendering, this, - &WallpaperImageNode::maybeUpdateWallpaperImageClipRect, Qt::DirectConnection); - - QuickMicaMaterialPrivate::get(m_item)->appendNode(this); -} - -void WallpaperImageNode::maybeGenerateWallpaperImageCache(const bool force) -{ - if (m_texture && !force) { - return; - } - static constexpr const auto originPoint = QPoint{ 0, 0 }; - const QSize imageSize = MicaMaterialPrivate::wallpaperSize(); - auto pixmap = QPixmap(imageSize); - pixmap.fill(kDefaultTransparentColor); - QPainter painter(&pixmap); - // We need the real wallpaper image here, so always use "active" state. - m_mica->paint(&painter, QRect{ originPoint, imageSize }, true); - m_texture.reset(m_window->createTextureFromImage(pixmap.toImage())); - m_node->setTexture(m_texture.get()); -} - -void WallpaperImageNode::maybeUpdateWallpaperImageClipRect() -{ -#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) - const QSizeF itemSize = m_item->size(); -#else - const QSizeF itemSize = {m_item->width(), m_item->height()}; -#endif - m_node->setRect(QRectF(QPointF(0.0, 0.0), itemSize)); - const auto rect = QRectF(m_item->mapToGlobal(QPointF(0.0, 0.0)), itemSize); - m_node->setSourceRect(m_micaPriv->mapToWallpaper(rect.toRect())); -} - QuickMicaMaterialPrivate::QuickMicaMaterialPrivate(QuickMicaMaterial *q) : QObject(q) { Q_ASSERT(q); @@ -178,15 +84,23 @@ void QuickMicaMaterialPrivate::initialize() { Q_Q(QuickMicaMaterial); - // Without this flag, our QQuickItem won't paint anything. - // We MUST enable this flag manually if we want to create a visible item. - q->setFlag(QuickMicaMaterial::ItemHasContents); - // No smooth needed. + // No smooth needed. The blurry image is already low quality, enabling + // smooth won't help much and we also don't want it to slow down the + // general performance. q->setSmooth(false); - // We don't need anti-aliasing. + // We don't need anti-aliasing. Same reason as above. q->setAntialiasing(false); // Enable clipping, to improve performance in some certain cases. q->setClip(true); + // Disable mipmap, we don't need high quality scaling here. + q->setMipmap(false); + // Mica material should not be translucent anyway, enabling this option + // will disable the alpha blending of this item, which can also improve + // the rendering performance. + q->setOpaquePainting(true); + // Set an invalid fill color to prevent QQuickPaintedItem from drawing the background, + // we don't need it anyway and it can improve the general performance as well. + q->setFillColor(QColor{}); m_micaMaterial = new MicaMaterial(this); connect(m_micaMaterial, &MicaMaterial::tintColorChanged, q, &QuickMicaMaterial::tintColorChanged); @@ -194,18 +108,7 @@ void QuickMicaMaterialPrivate::initialize() connect(m_micaMaterial, &MicaMaterial::fallbackColorChanged, q, &QuickMicaMaterial::fallbackColorChanged); connect(m_micaMaterial, &MicaMaterial::noiseOpacityChanged, q, &QuickMicaMaterial::noiseOpacityChanged); connect(m_micaMaterial, &MicaMaterial::fallbackEnabledChanged, q, &QuickMicaMaterial::fallbackEnabledChanged); - connect(m_micaMaterial, &MicaMaterial::shouldRedraw, this, &QuickMicaMaterialPrivate::forceRegenerateWallpaperImageCache); - -#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE - m_fallbackColorItem = new QQuickRectangle(q); - QQuickItemPrivate::get(m_fallbackColorItem)->anchors()->setFill(q); - QQuickPen * const border = m_fallbackColorItem->border(); - border->setColor(kDefaultTransparentColor); - border->setWidth(0); - updateFallbackColor(); - m_fallbackColorItem->setVisible(false); - connect(FramelessManager::instance(), &FramelessManager::systemThemeChanged, this, &QuickMicaMaterialPrivate::updateFallbackColor); -#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE + connect(m_micaMaterial, &MicaMaterial::shouldRedraw, q, [q](){ q->update(); }); } void QuickMicaMaterialPrivate::rebindWindow() @@ -230,75 +133,50 @@ void QuickMicaMaterialPrivate::rebindWindow() disconnect(m_rootWindowYChangedConnection); m_rootWindowYChangedConnection = {}; } - m_rootWindowXChangedConnection = connect(window, &QQuickWindow::xChanged, q, [q](){ q->update(); }); - m_rootWindowYChangedConnection = connect(window, &QQuickWindow::yChanged, q, [q](){ q->update(); }); -#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE if (m_rootWindowActiveChangedConnection) { disconnect(m_rootWindowActiveChangedConnection); m_rootWindowActiveChangedConnection = {}; } - m_rootWindowActiveChangedConnection = connect(window, &QQuickWindow::activeChanged, q, [this, window](){ - if (m_micaMaterial->isFallbackEnabled()) { - m_fallbackColorItem->setVisible(!window->isActive()); - } else { - m_fallbackColorItem->setVisible(false); - } - }); -#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE + m_rootWindowXChangedConnection = connect(window, &QQuickWindow::xChanged, q, [q](){ q->update(); }); + m_rootWindowYChangedConnection = connect(window, &QQuickWindow::yChanged, q, [q](){ q->update(); }); + m_rootWindowActiveChangedConnection = connect(window, &QQuickWindow::activeChanged, q, [q](){ q->update(); }); } -void QuickMicaMaterialPrivate::forceRegenerateWallpaperImageCache() +void QuickMicaMaterialPrivate::repaint(QPainter *painter) { - if (m_nodes.isEmpty()) { + Q_ASSERT(painter); + Q_ASSERT(m_micaMaterial); + if (!painter || !m_micaMaterial) { return; } - for (auto &&node : std::as_const(m_nodes)) { - if (node) { - node->maybeGenerateWallpaperImageCache(true); - } - } + Q_Q(QuickMicaMaterial); + const bool isActive = q->window() ? q->window()->isActive() : false; + const QPoint originPoint = q->mapToGlobal(QPointF{ 0, 0 }).toPoint(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + const QSize size = q->size().toSize(); +#else // (QT_VERSION < QT_VERSION_CHECK(5, 10, 0)) + const QSize size = QSizeF{ q->width(), q->height() }.toSize(); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) + m_micaMaterial->paint(painter, QRect{ originPoint, size }, isActive); } -void QuickMicaMaterialPrivate::appendNode(WallpaperImageNode *node) +QuickMicaMaterial::QuickMicaMaterial(QQuickItem *parent) + : QQuickPaintedItem(parent), d_ptr(new QuickMicaMaterialPrivate(this)) { - Q_ASSERT(node); - if (!node) { - return; - } - m_nodes.append(node); } -void QuickMicaMaterialPrivate::removeNode(WallpaperImageNode *node) -{ - Q_ASSERT(node); - if (!node) { - return; - } - m_nodes.removeAll(node); -} +QuickMicaMaterial::~QuickMicaMaterial() = default; -void QuickMicaMaterialPrivate::updateFallbackColor() +void QuickMicaMaterial::paint(QPainter *painter) { -#ifndef FRAMELESSHELPER_QUICK_NO_PRIVATE - if (!m_fallbackColorItem || !m_micaMaterial) { - return; - } - const QColor color = m_micaMaterial->fallbackColor(); - if (color.isValid()) { - m_fallbackColorItem->setColor(color); + Q_ASSERT(painter); + if (!painter) { return; } - m_fallbackColorItem->setColor(MicaMaterialPrivate::systemFallbackColor()); -#endif // FRAMELESSHELPER_QUICK_NO_PRIVATE -} - -QuickMicaMaterial::QuickMicaMaterial(QQuickItem *parent) - : QQuickItem(parent), d_ptr(new QuickMicaMaterialPrivate(this)) -{ + Q_D(QuickMicaMaterial); + d->repaint(painter); } -QuickMicaMaterial::~QuickMicaMaterial() = default; - QColor QuickMicaMaterial::tintColor() const { Q_D(const QuickMicaMaterial); @@ -361,11 +239,10 @@ void QuickMicaMaterial::setFallbackEnabled(const bool value) void QuickMicaMaterial::itemChange(const ItemChange change, const ItemChangeData &value) { - QQuickItem::itemChange(change, value); + QQuickPaintedItem::itemChange(change, value); Q_D(QuickMicaMaterial); switch (change) { case ItemDevicePixelRatioHasChanged: { - d->forceRegenerateWallpaperImageCache(); update(); // Force re-paint immediately. } break; case ItemSceneChange: { @@ -378,26 +255,14 @@ void QuickMicaMaterial::itemChange(const ItemChange change, const ItemChangeData } } -QSGNode *QuickMicaMaterial::updatePaintNode(QSGNode *old, UpdatePaintNodeData *data) -{ - Q_UNUSED(data); - auto node = static_cast(old); - if (!node) { - node = new WallpaperImageNode(this); - } - return node; -} - void QuickMicaMaterial::classBegin() { - QQuickItem::classBegin(); + QQuickPaintedItem::classBegin(); } void QuickMicaMaterial::componentComplete() { - QQuickItem::componentComplete(); + QQuickPaintedItem::componentComplete(); } FRAMELESSHELPER_END_NAMESPACE - -#include "quickmicamaterial.moc"