mica material: support reading very large image files

This commit is contained in:
Yuhang Zhao 2023-07-01 18:16:57 +08:00
parent fa5389a0fc
commit 7c0d8c4b11
5 changed files with 102 additions and 32 deletions

2
cmake

@ -1 +1 @@
Subproject commit 69aa8ca8ed5ad4b06d0bc024293641b1a14884ff Subproject commit c1e8081d4de985e5c7edbe2d5209860cb1ceb62f

View File

@ -64,7 +64,7 @@ int main(int argc, char *argv[])
FramelessHelper::Core::setApplicationOSThemeAware(); FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); 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. // Enable QtRHI debug output if not explicitly requested by the user.
if (!qEnvironmentVariableIsSet("QSG_INFO")) { if (!qEnvironmentVariableIsSet("QSG_INFO")) {
@ -129,7 +129,7 @@ int main(int argc, char *argv[])
#endif #endif
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0)) #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){ [](const QUrl &url){
qCritical() << "The QML engine failed to create component:" << url; qCritical() << "The QML engine failed to create component:" << url;
QCoreApplication::exit(-1); QCoreApplication::exit(-1);

View File

@ -29,6 +29,12 @@
FRAMELESSHELPER_USE_NAMESPACE FRAMELESSHELPER_USE_NAMESPACE
#define CREATE_WINDOW(Name) \
const auto Name = std::make_unique<Widget>(); \
Name->setObjectName(FRAMELESSHELPER_STRING_LITERAL(#Name)); \
Name->waitReady(); \
Name->show();
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
Log::setup(FRAMELESSHELPER_STRING_LITERAL("widget")); Log::setup(FRAMELESSHELPER_STRING_LITERAL("widget"));
@ -51,17 +57,12 @@ int main(int argc, char *argv[])
FramelessHelper::Core::setApplicationOSThemeAware(); FramelessHelper::Core::setApplicationOSThemeAware();
FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow); FramelessConfig::instance()->set(Global::Option::EnableBlurBehindWindow);
FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial); //FramelessConfig::instance()->set(Global::Option::DisableLazyInitializationForMicaMaterial);
const auto window1 = std::make_unique<Widget>(); CREATE_WINDOW(window1)
window1->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window1")); CREATE_WINDOW(window2)
window1->waitReady(); CREATE_WINDOW(window3)
window1->show(); CREATE_WINDOW(window4)
const auto window2 = std::make_unique<Widget>();
window2->setObjectName(FRAMELESSHELPER_STRING_LITERAL("window2"));
window2->waitReady();
window2->show();
return QCoreApplication::exec(); return QCoreApplication::exec();
} }

View File

@ -64,6 +64,10 @@ private:
bool fallbackEnabled = true; bool fallbackEnabled = true;
QBrush micaBrush = {}; QBrush micaBrush = {};
bool initialized = false; bool initialized = false;
struct {
qreal x = 0;
qreal y = 0;
} transform = {};
}; };
FRAMELESSHELPER_END_NAMESPACE FRAMELESSHELPER_END_NAMESPACE

View File

@ -31,6 +31,7 @@
#include <QtCore/qloggingcategory.h> #include <QtCore/qloggingcategory.h>
#include <QtGui/qpixmap.h> #include <QtGui/qpixmap.h>
#include <QtGui/qimage.h> #include <QtGui/qimage.h>
#include <QtGui/qimagereader.h>
#include <QtGui/qpainter.h> #include <QtGui/qpainter.h>
#include <QtGui/qscreen.h> #include <QtGui/qscreen.h>
#include <QtGui/qguiapplication.h> #include <QtGui/qguiapplication.h>
@ -56,6 +57,9 @@ static Q_LOGGING_CATEGORY(lcMicaMaterial, "wangwenx190.framelesshelper.core.mica
using namespace Global; 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 kDefaultTintOpacity = 0.7;
[[maybe_unused]] static constexpr const qreal kDefaultNoiseOpacity = 0.04; [[maybe_unused]] static constexpr const qreal kDefaultNoiseOpacity = 0.04;
[[maybe_unused]] static constexpr const qreal kDefaultBlurRadius = 128.0; [[maybe_unused]] static constexpr const qreal kDefaultBlurRadius = 128.0;
@ -77,6 +81,26 @@ struct MicaMaterialData
Q_GLOBAL_STATIC(MicaMaterialData, g_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 #ifndef FRAMELESSHELPER_CORE_NO_PRIVATE
template<const int shift> template<const int shift>
[[nodiscard]] static inline constexpr int qt_static_shift(const int value) [[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<const int aprec, const int zprec, const bool alphaOnly> template<const int aprec, const int zprec, const bool alphaOnly>
static inline void expblur(QImage &img, qreal radius, const bool improvedQuality = false, const int transposed = 0) 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_RGB32)
|| (img.format() == QImage::Format_Indexed8) || (img.format() == QImage::Format_Indexed8)
|| (img.format() == QImage::Format_Grayscale8)); || (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_RGB32)
&& (img.format() != QImage::Format_Indexed8) && (img.format() != QImage::Format_Indexed8)
&& (img.format() != QImage::Format_Grayscale8)) { && (img.format() != QImage::Format_Grayscale8)) {
@ -345,9 +369,9 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality
return dest; return dest;
} }
if ((source.format() != QImage::Format_ARGB32_Premultiplied) if ((source.format() != kDefaultImageFormat)
&& (source.format() != QImage::Format_RGB32)) { && (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()); 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, [[maybe_unused]] static inline void qt_blurImage(QPainter *p, QImage &blurImage,
qreal radius, const bool quality, const bool alphaOnly, const int transposed = 0) 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.format() != QImage::Format_RGB32)) {
blurImage = blurImage.convertToFormat(QImage::Format_ARGB32_Premultiplied); blurImage = blurImage.convertToFormat(kDefaultImageFormat);
} }
qreal scale = 1.0; qreal scale = 1.0;
@ -508,21 +532,49 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force)
if (!g_micaMaterialData()->blurredWallpaper.isNull() && !force) { if (!g_micaMaterialData()->blurredWallpaper.isNull() && !force) {
return; return;
} }
const QSize size = QGuiApplication::primaryScreen()->virtualSize(); QSize monitorSize = QGuiApplication::primaryScreen()->virtualSize();
g_micaMaterialData()->blurredWallpaper = QPixmap(size); if (monitorSize.isEmpty()) {
g_micaMaterialData()->blurredWallpaper.fill(kDefaultTransparentColor); 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(); const QString wallpaperFilePath = Utils::getWallpaperFilePath();
if (wallpaperFilePath.isEmpty()) { if (wallpaperFilePath.isEmpty()) {
WARNING << "Failed to retrieve the wallpaper file path."; WARNING << "Failed to retrieve the wallpaper file path.";
return; 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()) { if (image.isNull()) {
WARNING << "QImage doesn't support this kind of file:" << wallpaperFilePath; WARNING << "The obtained image data is null.";
transform = {};
return; return;
} }
WallpaperAspectStyle aspectStyle = Utils::getWallpaperAspectStyle(); WallpaperAspectStyle aspectStyle = Utils::getWallpaperAspectStyle();
QImage buffer(size, QImage::Format_ARGB32_Premultiplied); QImage buffer(imageSize, kDefaultImageFormat);
#ifdef Q_OS_WINDOWS #ifdef Q_OS_WINDOWS
if (aspectStyle == WallpaperAspectStyle::Center) { if (aspectStyle == WallpaperAspectStyle::Center) {
buffer.fill(kDefaultBlackColor); buffer.fill(kDefaultBlackColor);
@ -538,11 +590,11 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force)
mode = Qt::KeepAspectRatio; mode = Qt::KeepAspectRatio;
} }
QSize newSize = image.size(); QSize newSize = image.size();
newSize.scale(size, mode); newSize.scale(imageSize, mode);
image = image.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); image = image.scaled(newSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
} }
static constexpr const QPoint desktopOriginPoint = {0, 0}; static constexpr const QPoint desktopOriginPoint = {0, 0};
const QRect desktopRect = {desktopOriginPoint, size}; const QRect desktopRect = {desktopOriginPoint, imageSize};
if (aspectStyle == WallpaperAspectStyle::Tile) { if (aspectStyle == WallpaperAspectStyle::Tile) {
QPainter bufferPainter(&buffer); QPainter bufferPainter(&buffer);
bufferPainter.setRenderHints(QPainter::Antialiasing | 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); const QRect rect = alignedRect(Qt::LeftToRight, Qt::AlignCenter, image.size(), desktopRect);
bufferPainter.drawImage(rect.topLeft(), image); bufferPainter.drawImage(rect.topLeft(), image);
} }
g_micaMaterialData()->blurredWallpaper = QPixmap(imageSize);
g_micaMaterialData()->blurredWallpaper.fill(kDefaultTransparentColor);
QPainter painter(&g_micaMaterialData()->blurredWallpaper); QPainter painter(&g_micaMaterialData()->blurredWallpaper);
painter.setRenderHints(QPainter::Antialiasing | painter.setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
@ -575,7 +629,7 @@ void MicaMaterialPrivate::updateMaterialBrush()
framelesshelpercore_initResource(); framelesshelpercore_initResource();
static const QImage noiseTexture = QImage(kNoiseImageFilePath); static const QImage noiseTexture = QImage(kNoiseImageFilePath);
#endif // FRAMELESSHELPER_CORE_NO_BUNDLE_RESOURCE #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); QColor fillColor = ((FramelessManager::instance()->systemTheme() == SystemTheme::Dark) ? kDefaultSystemDarkColor : kDefaultSystemLightColor2);
fillColor.setAlphaF(0.9f); fillColor.setAlphaF(0.9f);
micaTexture.fill(fillColor); micaTexture.fill(fillColor);
@ -599,20 +653,31 @@ void MicaMaterialPrivate::updateMaterialBrush()
void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoint &pos, const bool active) void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoint &pos, const bool active)
{ {
Q_ASSERT(painter); Q_ASSERT(painter);
if (!painter) { Q_ASSERT(!size.isEmpty());
if (!painter || size.isEmpty()) {
return; return;
} }
prepareGraphicsResources(); 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->save();
painter->setRenderHints(QPainter::Antialiasing | painter->setRenderHints(QPainter::Antialiasing |
QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform); QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
if (active) { 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->setCompositionMode(QPainter::CompositionMode_SourceOver);
painter->setOpacity(qreal(1)); painter->setOpacity(qreal(1));
painter->fillRect(QRect{originPoint, size}, [this, active]() -> QBrush { painter->fillRect(QRectF{originPoint, correctedSize}, [this, active]() -> QBrush {
if (!fallbackEnabled || active) { if (!fallbackEnabled || active) {
return micaBrush; return micaBrush;
} }