forked from github_mirror/framelesshelper
mica material: use separate thread to do time-consuming task
This commit is contained in:
parent
6b09aa9e91
commit
d1b4aadb76
|
@ -29,6 +29,7 @@ You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate
|
||||||
- Linux: There have been many improvements to the Linux/X11 implementation! Most of them won't be directly visible to the user, but the code quality has been greatly improved.
|
- Linux: There have been many improvements to the Linux/X11 implementation! Most of them won't be directly visible to the user, but the code quality has been greatly improved.
|
||||||
- macOS: The frameless windows will now use native window frame and buttons, only the title bar itself is hidden, which also means the window will have round corners as all other native windows on macOS.
|
- macOS: The frameless windows will now use native window frame and buttons, only the title bar itself is hidden, which also means the window will have round corners as all other native windows on macOS.
|
||||||
- Mica Material: It is now possible to load wallpaper images with very large file size or resolution, for example, 4K pictures. However, if the images have larger resolution than 1920x1080, they will be shrinked to reduce memory usage, and this process will also lower the image quality and break the aspect ratio of them.
|
- Mica Material: It is now possible to load wallpaper images with very large file size or resolution, for example, 4K pictures. However, if the images have larger resolution than 1920x1080, they will be shrinked to reduce memory usage, and this process will also lower the image quality and break the aspect ratio of them.
|
||||||
|
- Mica Material: FramelessHelper will now use a seperate thread to load and apply special effects to the wallpaper image, to speed up application startup performance and avoid such process block the main thread.
|
||||||
- Window management: It is now possible to close the window (the dtor is executed) and show it again without breaking the frameless functionalities.
|
- Window management: It is now possible to close the window (the dtor is executed) and show it again without breaking the frameless functionalities.
|
||||||
- Theme: It is now possible to force a desired theme instead of always respecting the system theme.
|
- Theme: It is now possible to force a desired theme instead of always respecting the system theme.
|
||||||
- Build system: The [**Ninja Multi-Config**](https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html) generator is fully supported now, finally!
|
- Build system: The [**Ninja Multi-Config**](https://cmake.org/cmake/help/latest/generator/Ninja%20Multi-Config.html) generator is fully supported now, finally!
|
||||||
|
|
|
@ -31,6 +31,12 @@ FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||||
|
|
||||||
class MicaMaterial;
|
class MicaMaterial;
|
||||||
|
|
||||||
|
using Transform = struct Transform
|
||||||
|
{
|
||||||
|
qreal Horizontal = 0;
|
||||||
|
qreal Vertical = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class FRAMELESSHELPER_CORE_API MicaMaterialPrivate : public QObject
|
class FRAMELESSHELPER_CORE_API MicaMaterialPrivate : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -64,10 +70,7 @@ private:
|
||||||
bool fallbackEnabled = true;
|
bool fallbackEnabled = true;
|
||||||
QBrush micaBrush = {};
|
QBrush micaBrush = {};
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
struct {
|
Transform transform = {};
|
||||||
qreal horizontal = 0;
|
|
||||||
qreal vertical = 0;
|
|
||||||
} transform = {};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <QtCore/qsysinfo.h>
|
#include <QtCore/qsysinfo.h>
|
||||||
#include <QtCore/qloggingcategory.h>
|
#include <QtCore/qloggingcategory.h>
|
||||||
#include <QtCore/qmutex.h>
|
#include <QtCore/qmutex.h>
|
||||||
|
#include <QtCore/qthread.h>
|
||||||
#include <QtGui/qpixmap.h>
|
#include <QtGui/qpixmap.h>
|
||||||
#include <QtGui/qimage.h>
|
#include <QtGui/qimage.h>
|
||||||
#include <QtGui/qimagereader.h>
|
#include <QtGui/qimagereader.h>
|
||||||
|
@ -78,7 +79,7 @@ struct MicaMaterialData
|
||||||
{
|
{
|
||||||
QPixmap blurredWallpaper = {};
|
QPixmap blurredWallpaper = {};
|
||||||
bool graphicsResourcesReady = false;
|
bool graphicsResourcesReady = false;
|
||||||
QMutex mutex;
|
QMutex mutex{};
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(MicaMaterialData, g_micaMaterialData)
|
Q_GLOBAL_STATIC(MicaMaterialData, g_micaMaterialData)
|
||||||
|
@ -499,44 +500,22 @@ static inline void expblur(QImage &img, qreal radius, const bool improvedQuality
|
||||||
return {x, y, w, h};
|
return {x, y, w, h};
|
||||||
}
|
}
|
||||||
|
|
||||||
MicaMaterialPrivate::MicaMaterialPrivate(MicaMaterial *q) : QObject(q)
|
class WallpaperThread : public QThread
|
||||||
{
|
{
|
||||||
Q_ASSERT(q);
|
Q_OBJECT
|
||||||
if (!q) {
|
Q_DISABLE_COPY_MOVE(WallpaperThread)
|
||||||
return;
|
|
||||||
}
|
|
||||||
q_ptr = q;
|
|
||||||
initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
MicaMaterialPrivate::~MicaMaterialPrivate() = default;
|
public:
|
||||||
|
explicit WallpaperThread(QObject *parent = nullptr) : QThread(parent) {}
|
||||||
|
~WallpaperThread() override = default;
|
||||||
|
|
||||||
MicaMaterialPrivate *MicaMaterialPrivate::get(MicaMaterial *q)
|
Q_SIGNALS:
|
||||||
{
|
void imageUpdated(const Transform &);
|
||||||
Q_ASSERT(q);
|
|
||||||
if (!q) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return q->d_func();
|
|
||||||
}
|
|
||||||
|
|
||||||
const MicaMaterialPrivate *MicaMaterialPrivate::get(const MicaMaterial *q)
|
protected:
|
||||||
{
|
void run() override
|
||||||
Q_ASSERT(q);
|
{
|
||||||
if (!q) {
|
Transform transform = {};
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return q->d_func();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force)
|
|
||||||
{
|
|
||||||
g_micaMaterialData()->mutex.lock();
|
|
||||||
if (!g_micaMaterialData()->blurredWallpaper.isNull() && !force) {
|
|
||||||
g_micaMaterialData()->mutex.unlock();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
g_micaMaterialData()->mutex.unlock();
|
|
||||||
// ### FIXME: Ideally, we should not use virtual desktop size here.
|
// ### FIXME: Ideally, we should not use virtual desktop size here.
|
||||||
QSize monitorSize = QGuiApplication::primaryScreen()->virtualSize();
|
QSize monitorSize = QGuiApplication::primaryScreen()->virtualSize();
|
||||||
if (monitorSize.isEmpty()) {
|
if (monitorSize.isEmpty()) {
|
||||||
|
@ -544,11 +523,9 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force)
|
||||||
monitorSize = kMaximumPictureSize;
|
monitorSize = kMaximumPictureSize;
|
||||||
}
|
}
|
||||||
const QSize imageSize = (monitorSize > kMaximumPictureSize ? kMaximumPictureSize : monitorSize);
|
const QSize imageSize = (monitorSize > kMaximumPictureSize ? kMaximumPictureSize : monitorSize);
|
||||||
if (imageSize == monitorSize) {
|
if (imageSize != monitorSize) {
|
||||||
transform = {};
|
transform.Horizontal = (qreal(imageSize.width()) / qreal(monitorSize.width()));
|
||||||
} else {
|
transform.Vertical = (qreal(imageSize.height()) / qreal(monitorSize.height()));
|
||||||
transform.horizontal = (qreal(imageSize.width()) / qreal(monitorSize.width()));
|
|
||||||
transform.vertical = (qreal(imageSize.height()) / qreal(monitorSize.height()));
|
|
||||||
}
|
}
|
||||||
const QString wallpaperFilePath = Utils::getWallpaperFilePath();
|
const QString wallpaperFilePath = Utils::getWallpaperFilePath();
|
||||||
if (wallpaperFilePath.isEmpty()) {
|
if (wallpaperFilePath.isEmpty()) {
|
||||||
|
@ -625,10 +602,73 @@ void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force)
|
||||||
qt_blurImage(&painter, buffer, kDefaultBlurRadius, true, false);
|
qt_blurImage(&painter, buffer, kDefaultBlurRadius, true, false);
|
||||||
#endif // FRAMELESSHELPER_CORE_NO_PRIVATE
|
#endif // FRAMELESSHELPER_CORE_NO_PRIVATE
|
||||||
g_micaMaterialData()->mutex.unlock();
|
g_micaMaterialData()->mutex.unlock();
|
||||||
if (initialized) {
|
Q_EMIT imageUpdated(transform);
|
||||||
Q_Q(MicaMaterial);
|
|
||||||
Q_EMIT q->shouldRedraw();
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThreadData
|
||||||
|
{
|
||||||
|
std::unique_ptr<WallpaperThread> thread = nullptr;
|
||||||
|
QMutex mutex{};
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_GLOBAL_STATIC(ThreadData, g_threadData)
|
||||||
|
|
||||||
|
static inline void threadCleaner()
|
||||||
|
{
|
||||||
|
const QMutexLocker locker(&g_threadData()->mutex);
|
||||||
|
if (g_threadData()->thread && g_threadData()->thread->isRunning()) {
|
||||||
|
g_threadData()->thread->requestInterruption();
|
||||||
|
g_threadData()->thread->quit();
|
||||||
|
g_threadData()->thread->wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MicaMaterialPrivate::MicaMaterialPrivate(MicaMaterial *q) : QObject(q)
|
||||||
|
{
|
||||||
|
Q_ASSERT(q);
|
||||||
|
if (!q) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
q_ptr = q;
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
MicaMaterialPrivate::~MicaMaterialPrivate() = default;
|
||||||
|
|
||||||
|
MicaMaterialPrivate *MicaMaterialPrivate::get(MicaMaterial *q)
|
||||||
|
{
|
||||||
|
Q_ASSERT(q);
|
||||||
|
if (!q) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return q->d_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
const MicaMaterialPrivate *MicaMaterialPrivate::get(const MicaMaterial *q)
|
||||||
|
{
|
||||||
|
Q_ASSERT(q);
|
||||||
|
if (!q) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return q->d_func();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MicaMaterialPrivate::maybeGenerateBlurredWallpaper(const bool force)
|
||||||
|
{
|
||||||
|
g_micaMaterialData()->mutex.lock();
|
||||||
|
if (!g_micaMaterialData()->blurredWallpaper.isNull() && !force) {
|
||||||
|
g_micaMaterialData()->mutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
g_micaMaterialData()->mutex.unlock();
|
||||||
|
const QMutexLocker locker(&g_threadData()->mutex);
|
||||||
|
if (g_threadData()->thread->isRunning()) {
|
||||||
|
g_threadData()->thread->requestInterruption();
|
||||||
|
g_threadData()->thread->quit();
|
||||||
|
g_threadData()->thread->wait();
|
||||||
|
}
|
||||||
|
g_threadData()->thread->start(QThread::LowPriority);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MicaMaterialPrivate::updateMaterialBrush()
|
void MicaMaterialPrivate::updateMaterialBrush()
|
||||||
|
@ -669,13 +709,15 @@ void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoi
|
||||||
static constexpr const QPointF originPoint = {0, 0};
|
static constexpr const QPointF originPoint = {0, 0};
|
||||||
QPointF correctedPos = pos;
|
QPointF correctedPos = pos;
|
||||||
QSizeF correctedSize = size;
|
QSizeF correctedSize = size;
|
||||||
if (!qFuzzyIsNull(transform.horizontal) && (transform.horizontal > qreal(0)) && !qFuzzyCompare(transform.horizontal, qreal(1))) {
|
if (!qFuzzyIsNull(transform.Horizontal) && (transform.Horizontal > qreal(0))
|
||||||
correctedPos.setX(correctedPos.x() * transform.horizontal);
|
&& !qFuzzyCompare(transform.Horizontal, qreal(1))) {
|
||||||
correctedSize.setWidth(correctedSize.width() * transform.horizontal);
|
correctedPos.setX(correctedPos.x() * transform.Horizontal);
|
||||||
|
correctedSize.setWidth(correctedSize.width() * transform.Horizontal);
|
||||||
}
|
}
|
||||||
if (!qFuzzyIsNull(transform.vertical) && (transform.vertical > qreal(0)) && !qFuzzyCompare(transform.vertical, qreal(1))) {
|
if (!qFuzzyIsNull(transform.Vertical) && (transform.Vertical > qreal(0))
|
||||||
correctedPos.setY(correctedPos.y() * transform.vertical);
|
&& !qFuzzyCompare(transform.Vertical, qreal(1))) {
|
||||||
correctedSize.setHeight(correctedSize.height() * transform.vertical);
|
correctedPos.setY(correctedPos.y() * transform.Vertical);
|
||||||
|
correctedSize.setHeight(correctedSize.height() * transform.Vertical);
|
||||||
}
|
}
|
||||||
painter->save();
|
painter->save();
|
||||||
painter->setRenderHints(QPainter::Antialiasing |
|
painter->setRenderHints(QPainter::Antialiasing |
|
||||||
|
@ -700,6 +742,20 @@ void MicaMaterialPrivate::paint(QPainter *painter, const QSize &size, const QPoi
|
||||||
|
|
||||||
void MicaMaterialPrivate::initialize()
|
void MicaMaterialPrivate::initialize()
|
||||||
{
|
{
|
||||||
|
g_threadData()->mutex.lock();
|
||||||
|
if (!g_threadData()->thread) {
|
||||||
|
g_threadData()->thread = std::make_unique<WallpaperThread>();
|
||||||
|
qAddPostRoutine(threadCleaner);
|
||||||
|
}
|
||||||
|
connect(g_threadData()->thread.get(), &WallpaperThread::imageUpdated, this, [this](const Transform &t){
|
||||||
|
transform = t;
|
||||||
|
if (initialized) {
|
||||||
|
Q_Q(MicaMaterial);
|
||||||
|
Q_EMIT q->shouldRedraw();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
g_threadData()->mutex.unlock();
|
||||||
|
|
||||||
tintColor = kDefaultTransparentColor;
|
tintColor = kDefaultTransparentColor;
|
||||||
tintOpacity = kDefaultTintOpacity;
|
tintOpacity = kDefaultTintOpacity;
|
||||||
// Leave fallbackColor invalid, we need to use this state to judge
|
// Leave fallbackColor invalid, we need to use this state to judge
|
||||||
|
@ -851,3 +907,5 @@ void MicaMaterial::paint(QPainter *painter, const QSize &size, const QPoint &pos
|
||||||
}
|
}
|
||||||
|
|
||||||
FRAMELESSHELPER_END_NAMESPACE
|
FRAMELESSHELPER_END_NAMESPACE
|
||||||
|
|
||||||
|
#include "micamaterial.moc"
|
||||||
|
|
Loading…
Reference in New Issue