diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,6 +27,7 @@ shadowedrectangle.cpp shadowedtexture.cpp colorutils.cpp + colorinfo.cpp scenegraph/shadowedrectanglenode.cpp scenegraph/shadowedrectanglematerial.cpp scenegraph/shadowedborderrectanglematerial.cpp diff --git a/src/colorinfo.h b/src/colorinfo.h new file mode 100644 --- /dev/null +++ b/src/colorinfo.h @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#pragma once + +#include +#include +#include "colorutils.h" + +/** + * Class providing information about a given item. + */ +class ColorInfo : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged) + + Q_PROPERTY(QColor averageColor READ averageColor NOTIFY averageColorComputed) + Q_PROPERTY(ColorUtils::Brightness brightness READ brightness NOTIFY brightnessComputed) + +private: + + QVariant m_source; + + QFuture> m_imageData; + QFuture m_averageColor; + QFuture m_brightness; + + QFutureWatcher> m_imageDataWatcher; + QFutureWatcher m_averageColorWatcher; + QFutureWatcher m_brightnessWatcher; + +public: + + explicit ColorInfo(QObject *parent = nullptr); + + Q_INVOKABLE void compute(); + + QVariant source(); + void setSource(QVariant source); + Q_SIGNAL void sourceChanged(); + + QColor averageColor(); + Q_INVOKABLE QColor awaitAverageColor() { return m_averageColor.result(); }; + Q_SIGNAL void averageColorComputed(); + + ColorUtils::Brightness brightness(); + Q_INVOKABLE ColorUtils::Brightness awaitBrightness() { return m_brightness.result(); }; + Q_SIGNAL void brightnessComputed(); +}; \ No newline at end of file diff --git a/src/colorinfo.cpp b/src/colorinfo.cpp new file mode 100644 --- /dev/null +++ b/src/colorinfo.cpp @@ -0,0 +1,109 @@ +/* + * SPDX-FileCopyrightText: 2020 Carson Black + * + * SPDX-License-Identifier: LGPL-2.0-or-later + */ + +#include +#include +#include +#include +#include "colorinfo.h" + +ColorInfo::ColorInfo(QObject *parent) : QObject(parent), m_imageDataWatcher(this), m_averageColorWatcher(this), m_brightnessWatcher(this) +{ + connect(&m_averageColorWatcher, &QFutureWatcher::finished, this, &ColorInfo::averageColorComputed); + connect(&m_brightnessWatcher, &QFutureWatcher::finished, this, &ColorInfo::brightnessComputed); +} + +QVariant ColorInfo::source() +{ + return m_source; +} + +void ColorInfo::compute() +{ + m_imageData = QtConcurrent::run([this]() { + auto source = this->source(); + auto dataFromImage = [](const QImage &image) { + QList ret; + for (int widthIndex = 0; widthIndex < image.width(); widthIndex++) { + for (int heightIndex = 0; heightIndex < image.height(); heightIndex++) { + ret << image.pixel(widthIndex, heightIndex); + } + } + return ret; + }; + if (source.canConvert()) { + auto casted = source.value(); + const auto response = casted->grabToImage(); + if (!response) { + return QList(); + } + QEventLoop loop; + QObject::connect(response.data(), &QQuickItemGrabResult::ready, &loop, &QEventLoop::quit); + loop.exec(); + return dataFromImage(response->image()); + } else if (source.canConvert()) { + return dataFromImage(source.value()); + } else if (source.canConvert()) { + return dataFromImage(source.value().pixmap(256, 256).toImage()); + } else if (source.canConvert()) { + return dataFromImage(QIcon::fromTheme(source.toString()).pixmap(256,256).toImage()); + } + qCritical() << "Item not in supported types, is of type" << source.typeName(); + return QList(); + }); + + m_averageColor = QtConcurrent::run([this]() { + auto imageData = m_imageData.result(); + + int red = 0, green = 0, blue = 0, alpha = 0, count = 0; + for (auto pixel : imageData) { + if (pixel == 0) { + continue; + } + red += qRed(pixel); + green += qGreen(pixel); + blue += qBlue(pixel); + alpha += qAlpha(pixel); + count++; + } + + if (count == 0) return QColor(); + + return QColor::fromRgb(red/count, green/count, blue/count, alpha/count); + }); + m_averageColorWatcher.setFuture(m_averageColor); + + m_brightness = QtConcurrent::run([this]() { + auto averageColor = m_averageColor.result(); + auto luma = [](QColor color) { + return (0.299*color.red() + 0.587*color.green() + 0.114*color.blue())/255; + }; + return luma(averageColor) > 0.5 ? ColorUtils::Light : ColorUtils::Dark; + }); + m_brightnessWatcher.setFuture(m_brightness); +} + +void ColorInfo::setSource(QVariant source) +{ + m_source = source; + compute(); +} + +QColor ColorInfo::averageColor() +{ + if (m_averageColor.isFinished()) { + return m_averageColor.result(); + } + return QColor(); +} + +ColorUtils::Brightness ColorInfo::brightness() +{ + if (m_brightness.isFinished()) { + return m_brightness.result(); + } + return ColorUtils::Light; +} diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp --- a/src/kirigamiplugin.cpp +++ b/src/kirigamiplugin.cpp @@ -20,6 +20,7 @@ #include "shadowedrectangle.h" #include "shadowedtexture.h" #include "colorutils.h" +#include "colorinfo.h" #include #include @@ -248,6 +249,7 @@ qmlRegisterSingletonType(uri, 2, 12, "ColorUtils", [](QQmlEngine*, QJSEngine*) { return new ColorUtils; }); qmlRegisterUncreatableType(uri, 2, 12, "CornersGroup", QStringLiteral("Used as grouped property")); + qmlRegisterType(uri, 2, 12, "ColorInfo"); qmlProtectModule(uri, 2); }