diff options
| author | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-08-27 22:55:17 +1000 |
|---|---|---|
| committer | 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com> | 2025-08-27 22:55:17 +1000 |
| commit | 258feae3d7e3da2e2f7c59fe00cbb7d839dce904 (patch) | |
| tree | 7073dab2466dc791d851beca96a737914c4f0384 | |
| parent | plugin: make thread safe (diff) | |
| download | caelestia-shell-258feae3d7e3da2e2f7c59fe00cbb7d839dce904.tar.gz caelestia-shell-258feae3d7e3da2e2f7c59fe00cbb7d839dce904.tar.bz2 caelestia-shell-258feae3d7e3da2e2f7c59fe00cbb7d839dce904.zip | |
plugin: add getDominantColour
Replaces wack canvas stuff for ColouredIcon
| -rw-r--r-- | components/effects/ColouredIcon.qml | 63 | ||||
| -rw-r--r-- | plugin/src/Caelestia/cutils.cpp | 71 | ||||
| -rw-r--r-- | plugin/src/Caelestia/cutils.hpp | 3 |
3 files changed, 79 insertions, 58 deletions
diff --git a/components/effects/ColouredIcon.qml b/components/effects/ColouredIcon.qml index 8ba6c5d..a858c25 100644 --- a/components/effects/ColouredIcon.qml +++ b/components/effects/ColouredIcon.qml @@ -1,5 +1,6 @@ pragma ComponentBehavior: Bound +import Caelestia import Quickshell.Widgets import QtQuick @@ -7,77 +8,23 @@ IconImage { id: root required property color colour - property color sourceColour - property url lastSource + property color dominantColour asynchronous: true layer.enabled: true layer.effect: Colouriser { - sourceColor: root.sourceColour + sourceColor: root.dominantColour colorizationColor: root.colour } layer.onEnabledChanged: { if (layer.enabled) - canvas.requestPaint(); + CUtils.getDominantColour(this); } onStatusChanged: { if (layer.enabled && status === Image.Ready) - canvas.requestPaint(); - } - - Canvas { - id: canvas - - property int retryCount - - implicitWidth: 32 - implicitHeight: 32 - visible: false - - onPaint: { - if (!root.layer.enabled) - return; - - const ctx = getContext("2d"); - ctx.reset(); - ctx.drawImage(root.backer, 0, 0, width, height); - - const colours = {} as Object; - const data = ctx.getImageData(0, 0, width, height).data; - - for (let i = 0; i < data.length; i += 4) { - if (data[i + 3] === 0) - continue; - - const c = `${data[i]},${data[i + 1]},${data[i + 2]}`; - if (colours.hasOwnProperty(c)) - colours[c]++; - else - colours[c] = 1; - } - - // Canvas is empty, try again next frame - if (retryCount < 5 && !Object.keys(colours).length) { - retryCount++; - Qt.callLater(() => requestPaint()); - return; - } - - let max = 0; - let maxColour = "0,0,0"; - for (const [colour, occurences] of Object.entries(colours)) { - if (occurences > max) { - max = occurences; - maxColour = colour; - } - } - - const [r, g, b] = maxColour.split(","); - root.sourceColour = Qt.rgba(r / 255, g / 255, b / 255, 1); - retryCount = 0; - } + CUtils.getDominantColour(this); } } diff --git a/plugin/src/Caelestia/cutils.cpp b/plugin/src/Caelestia/cutils.cpp index dfe5ee8..30f38dd 100644 --- a/plugin/src/Caelestia/cutils.cpp +++ b/plugin/src/Caelestia/cutils.cpp @@ -90,3 +90,74 @@ bool CUtils::copyFile(const QUrl& source, const QUrl& target, bool overwrite) co return QFile::copy(source.toLocalFile(), target.toLocalFile()); } + +void CUtils::getDominantColour(QQuickItem* item) const { + this->getDominantColour(item, 128, 128); +} + +void CUtils::getDominantColour(QQuickItem* item, int width, int height) const { + if (!item) { + qWarning() << "CUtils::getDominantColour: an item is required"; + return; + } + + if (!item->window()) { + // Fail silently to avoid warning + return; + } + + QSharedPointer<QQuickItemGrabResult> grabResult = item->grabToImage(QSize(width, height)); + + QObject::connect(grabResult.data(), &QQuickItemGrabResult::ready, this, + [grabResult, item, this]() { + QImage image = grabResult->image(); + + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + + std::unordered_map<uint32_t, int> colours; + const uchar* data = image.bits(); + int width = image.width(); + int height = image.height(); + int bytesPerLine = image.bytesPerLine(); + + for (int y = 0; y < height; ++y) { + const uchar* line = data + y * bytesPerLine; + for (int x = 0; x < width; ++x) { + const uchar* pixel = line + x * 4; + uchar r = pixel[0]; + uchar g = pixel[1]; + uchar b = pixel[2]; + uchar a = pixel[3]; + + if (a == 0) { + continue; + } + + r &= 0xF8; + g &= 0xF8; + b &= 0xF8; + + uint32_t colour = (r << 16) | (g << 8) | b; + ++colours[colour]; + } + } + + uint32_t dominantColour = 0; + int maxCount = 0; + for (const auto& [colour, count] : colours) { + if (count > maxCount) { + dominantColour = colour; + maxCount = count; + } + } + + const QColor colour = QColor((0xFF << 24) | dominantColour); + + QMetaObject::invokeMethod(item, [item, colour]() { + item->setProperty("dominantColour", colour); + }, Qt::QueuedConnection); + } + ); +} diff --git a/plugin/src/Caelestia/cutils.hpp b/plugin/src/Caelestia/cutils.hpp index c796e8b..36120f4 100644 --- a/plugin/src/Caelestia/cutils.hpp +++ b/plugin/src/Caelestia/cutils.hpp @@ -19,4 +19,7 @@ public: Q_INVOKABLE bool copyFile(const QUrl& source, const QUrl& target) const; Q_INVOKABLE bool copyFile(const QUrl& source, const QUrl& target, bool overwrite) const; + + Q_INVOKABLE void getDominantColour(QQuickItem* item) const; + Q_INVOKABLE void getDominantColour(QQuickItem* item, int width, int height) const; }; |