From 7c0d8c4b1171f052ee3867f9bf7f884ac6897e4d Mon Sep 17 00:00:00 2001 From: Yuhang Zhao Date: Sat, 1 Jul 2023 18:16:57 +0800 Subject: [PATCH] mica material: support reading very large image files --- cmake | 2 +- examples/quick/main.cpp | 4 +- examples/widget/main.cpp | 21 ++-- .../Core/private/micamaterial_p.h | 4 + src/core/micamaterial.cpp | 103 ++++++++++++++---- 5 files changed, 102 insertions(+), 32 deletions(-) diff --git a/cmake b/cmake index 69aa8ca..c1e8081 160000 --- a/cmake +++ b/cmake @@ -1 +1 @@ -Subproject commit 69aa8ca8ed5ad4b06d0bc024293641b1a14884ff +Subproject commit c1e8081d4de985e5c7edbe2d5209860cb1ceb62f diff --git a/examples/quick/main.cpp b/examples/quick/main.cpp index 64705a9..387089f 100644 --- a/examples/quick/main.cpp +++ b/examples/quick/main.cpp @@ -64,7 +64,7 @@ int main(int argc, char *argv[]) FramelessHelper::Core::setApplicationOSThemeAware(); FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); - FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); + //FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); // Enable QtRHI debug output if not explicitly requested by the user. if (!qEnvironmentVariableIsSet("QSG_INFO")) { @@ -129,7 +129,7 @@ int main(int argc, char *argv[]) #endif #if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) - QObject::connect(engine.get(), &QQmlApplicationEngine::objectCreationFailed, qApp, + QObject::connect(engine.get(), &QQmlApplicationEngine::objectCreationFailed, application.get(), [](const QUrl &url){ qCritical() << "The QML engine failed to create component:" << url; QCoreApplication::exit(-1); diff --git a/examples/widget/main.cpp b/examples/widget/main.cpp index bf0f070..01ae825 100644 --- a/examples/widget/main.cpp +++ b/examples/widget/main.cpp @@ -29,6 +29,12 @@ FRAMELESSHELPER_USE_NAMESPACE +#define CREATE_WINDOW(Name) \ + const auto Name = std::make_unique(); \ + Name->setObjectName(FRAMELESSHELPER_STRING_LITERAL(#Name)); \ + Name->waitReady(); \ + Name->show(); + int main(int argc, char *argv[]) { Log::setup(FRAMELESSHELPER_STRING_LITERAL("widget")); @@ -51,17 +57,12 @@ int main(int argc, char *argv[]) FramelessHelper::Core::setApplicationOSThemeAware(); FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); - FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); + //FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); - const auto window1 = std::make_unique(); - window1->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window1")); - window1->waitReady(); - window1->show(); - - const auto window2 = std::make_unique(); - window2->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window2")); - window2->waitReady(); - window2->show(); + CREATE_WINDOW(window1) + CREATE_WINDOW(window2) + CREATE_WINDOW(window3) + CREATE_WINDOW(window4) return QCoreApplication::exec(); } diff --git a/include/FramelessHelper/Core/private/micamaterial_p.h b/include/FramelessHelper/Core/private/micamaterial_p.h index 8ec65a0..f8853e1 100644 --- a/include/FramelessHelper/Core/private/micamaterial_p.h +++ b/include/FramelessHelper/Core/private/micamaterial_p.h @@ -64,6 +64,10 @@ private: bool fallbackEnabled = true; QBrush micaBrush = {}; bool initialized = false; + struct { + qreal x = 0; + qreal y = 0; + } transform = {}; }; FRAMELESSHELPER_END_NAMESPACE diff --git a/src/core/micamaterial.cpp b/src/core/micamaterial.cpp index 7c047a7..3627d76 100644 --- a/src/core/micamaterial.cpp +++ b/src/core/micamaterial.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,9 @@ static Q_LOGGING_CATEGORY(lcMicaMaterial, "wangwenx190.framelesshelper.core.mica using namespace Global; +[[maybe_unused]] static constexpr const QSize kMaximumPictureSize = { 1920, 1080 }; +[[maybe_unused]] static constexpr const QImage::Format kDefaultImageFormat = QImage::Format_ARGB32_Premultiplied; + [[maybe_unused]] static constexpr const qreal kDefaultTintOpacity = 0.7; [[maybe_unused]] static constexpr const qreal kDefaultNoiseOpacity = 0.04; [[maybe_unused]] static constexpr const qreal kDefaultBlurRadius = 128.0; @@ -77,6 +81,26 @@ struct MicaMaterialData Q_GLOBAL_STATIC(MicaMaterialData, g_micaMaterialData) +[[maybe_unused]] [[nodiscard]] static inline constexpr bool operator>(const QSize &lhs, const QSize &rhs) noexcept +{ + return ((lhs.width() * lhs.height()) > (rhs.width() * rhs.height())); +} + +[[maybe_unused]] [[nodiscard]] static inline constexpr bool operator>=(const QSize &lhs, const QSize &rhs) noexcept +{ + return (operator>(lhs, rhs) || operator==(lhs, rhs)); +} + +[[maybe_unused]] [[nodiscard]] static inline constexpr bool operator<(const QSize &lhs, const QSize &rhs) noexcept +{ + return (operator!=(lhs, rhs) && !operator>(lhs, rhs)); +} + +[[maybe_unused]] [[nodiscard]] static inline constexpr bool operator<=(const QSize &lhs, const QSize &rhs) noexcept +{ + return (operator<(lhs, rhs) || operator==(lhs, rhs)); +} + #ifndef FRAMELESSHELPER_CORE_NO_PRIVATE template [[nodiscard]] static inline constexpr int qt_static_shift(const int value) @@ -192,11 +216,11 @@ static inline void qt_blurrow(QImage &im, const int line, const int alpha) template static inline void expblur(QImage &img, qreal radius, const bool improvedQuality = false, const int transposed = 0) { - Q_ASSERT((img.format() == QImage::Format_ARGB32_Premultiplied) + Q_ASSERT((img.format() == kDefaultImageFormat) || (img.format() == QImage::Format_RGB32) || (img.format() == QImage::Format_Indexed8) || (img.format() == QImage::Format_Grayscale8)); - if ((img.format() != QImage::Format_ARGB32_Premultiplied) + if ((img.format() != kDefaultImageFormat) && (img.format() != QImage::Format_RGB32) && (img.format() != QImage::Format_Indexed8) && (img.format() != QImage::Format_Grayscale8)) { @@ -345,9 +369,9 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality return dest; } - if ((source.format() != QImage::Format_ARGB32_Premultiplied) + if ((source.format() != kDefaultImageFormat) && (source.format() != QImage::Format_RGB32)) { - srcImage = source.convertToFormat(QImage::Format_ARGB32_Premultiplied); + srcImage = source.convertToFormat(kDefaultImageFormat); } QImage dest(source.width() / 2, source.height() / 2, srcImage.format()); @@ -377,9 +401,9 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality [[maybe_unused]] static inline void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, const bool quality, const bool alphaOnly, const int transposed = 0) { - if ((blurImage.format() != QImage::Format_ARGB32_Premultiplied) + if ((blurImage.format() != kDefaultImageFormat) && (blurImage.format() != QImage::Format_RGB32)) { - blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); + blurImage = blurImage.convertToFormat(kDefaultImageFormat); } qreal scale = 1.0; @@ -508,21 +532,49 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force) if (!g_micaMaterialData()->blurredWallpaper.isNull() && !force) { return; } - const QSize size = QGuiApplication::primaryScreen()->virtualSize(); - g_micaMaterialData()->blurredWallpaper = QPixmap(size); - g_micaMaterialData()->blurredWallpaper.fill(kDefaultTransparentColor); + QSize monitorSize = QGuiApplication::primaryScreen()->virtualSize(); + if (monitorSize.isEmpty()) { + WARNING << "Failed to retrieve the monitor size. Using default size (1920x1080) instead ..."; + monitorSize = kMaximumPictureSize; + } + const QSize imageSize = (monitorSize > kMaximumPictureSize ? kMaximumPictureSize : monitorSize); const QString wallpaperFilePath = Utils::getWallpaperFilePath(); if (wallpaperFilePath.isEmpty()) { WARNING << "Failed to retrieve the wallpaper file path."; return; } - QImage image(wallpaperFilePath); + QImageReader reader(wallpaperFilePath); + if (!reader.canRead()) { + WARNING << "Qt can't read the wallpaper file:" << reader.errorString(); + return; + } + const QSize actualSize = reader.size(); + if (actualSize.isEmpty()) { + WARNING << "The wallpaper picture size is invalid."; + return; + } + const QSize correctedSize = (actualSize > kMaximumPictureSize ? kMaximumPictureSize : actualSize); + if (actualSize == correctedSize) { + transform = {}; + } else { + DEBUG << "The wallpaper picture size is greater than 1920x1080, it will be shrinked to reduce memory consumption."; + reader.setScaledSize(correctedSize); + transform.x = qreal(correctedSize.width()) / qreal(actualSize.width()); + transform.y = qreal(correctedSize.height()) / qreal(actualSize.height()); + } + QImage image(correctedSize, kDefaultImageFormat); + if (!reader.read(&image)) { + WARNING << "Failed to read the wallpaper image:" << reader.errorString(); + transform = {}; + return; + } if (image.isNull()) { - WARNING << "QImage doesn't support this kind of file:" << wallpaperFilePath; + WARNING << "The obtained image data is null."; + transform = {}; return; } WallpaperAspectStyle aspectStyle = Utils::getWallpaperAspectStyle(); - QImage buffer(size, QImage::Format_ARGB32_Premultiplied); + QImage buffer(imageSize, kDefaultImageFormat); #ifdef Q_OS_WINDOWS if (aspectStyle == WallpaperAspectStyle::Center) { buffer.fill(kDefaultBlackColor); @@ -538,11 +590,11 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force) mode = Qt::KeepAspectRatio; } QSize newSize = image.size(); - newSize.scale(size, mode); + newSize.scale(imageSize, mode); image = image.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } static constexpr const QPoint desktopOriginPoint = {0, 0}; - const QRect desktopRect = {desktopOriginPoint, size}; + const QRect desktopRect = {desktopOriginPoint, imageSize}; if (aspectStyle == WallpaperAspectStyle::Tile) { QPainter bufferPainter(&buffer); bufferPainter.setRenderHints(QPainter::Antialiasing | @@ -555,6 +607,8 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force) const QRect rect = alignedRect(Qt::LeftToRight, Qt::AlignCenter, image.size(), desktopRect); bufferPainter.drawImage(rect.topLeft(), image); } + g_micaMaterialData()->blurredWallpaper = QPixmap(imageSize); + g_micaMaterialData()->blurredWallpaper.fill(kDefaultTransparentColor); QPainter painter(&g_micaMaterialData()->blurredWallpaper); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); @@ -575,7 +629,7 @@ void MicaMaterialPrivate::updateMaterialBrush() framelesshelpercore_initResource(); static const QImage noiseTexture = QImage(kNoiseImageFilePath); #endif // FRAMELESSHELPER_CORE_NO_BUNDLE_RESOURCE - QImage micaTexture = QImage(QSize(64, 64), QImage::Format_ARGB32_Premultiplied); + QImage micaTexture = QImage(QSize(64, 64), kDefaultImageFormat); QColor fillColor = ((FramelessManager::instance()->systemTheme() == SystemTheme::Dark) ? kDefaultSystemDarkColor : kDefaultSystemLightColor2); fillColor.setAlphaF(0.9f); micaTexture.fill(fillColor); @@ -599,20 +653,31 @@ void MicaMaterialPrivate::updateMaterialBrush() void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoint &pos, const bool active) { Q_ASSERT(painter); - if (!painter) { + Q_ASSERT(!size.isEmpty()); + if (!painter || size.isEmpty()) { return; } prepareGraphicsResources(); - static constexpr const QPoint originPoint = {0, 0}; + static constexpr const QPointF originPoint = {0, 0}; + QPointF correctedPos = pos; + QSizeF correctedSize = size; + if (!qFuzzyIsNull(transform.x) && (transform.x > qreal(0)) && !qFuzzyCompare(transform.x, qreal(1))) { + correctedPos.setX(correctedPos.x() * transform.x); + correctedSize.setWidth(correctedSize.width() * transform.x); + } + if (!qFuzzyIsNull(transform.y) && (transform.y > qreal(0)) && !qFuzzyCompare(transform.y, qreal(1))) { + correctedPos.setY(correctedPos.y() * transform.y); + correctedSize.setHeight(correctedSize.height() * transform.y); + } painter->save(); painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); if (active) { - painter->drawPixmap(originPoint, g_micaMaterialData()->blurredWallpaper, QRect(pos, size)); + painter->drawPixmap(originPoint, g_micaMaterialData()->blurredWallpaper, QRectF{correctedPos, correctedSize}); } painter->setCompositionMode(QPainter::CompositionMode_SourceOver); painter->setOpacity(qreal(1)); - painter->fillRect(QRect{originPoint, size}, [this, active]() -> QBrush { + painter->fillRect(QRectF{originPoint, correctedSize}, [this, active]() -> QBrush { if (!fallbackEnabled || active) { return micaBrush; }