File indexing completed on 2024-05-05 04:46:59
0001 /* 0002 * Copyright 2020 Marco Martin <mart@kde.org> 0003 * 0004 * This program is free software; you can redistribute it and/or modify 0005 * it under the terms of the GNU General Public License as published by 0006 * the Free Software Foundation; either version 2 of the License, or 0007 * (at your option) any later version. 0008 * 0009 * This program is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU General Public License 0015 * along with this program; if not, write to the Free Software 0016 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA. 0017 */ 0018 0019 #pragma once 0020 0021 #include "colorutils.h" 0022 0023 #include <QColor> 0024 #include <QFuture> 0025 #include <QImage> 0026 #include <QObject> 0027 #include <QPointer> 0028 #include <QQuickItem> 0029 #include <QQuickItemGrabResult> 0030 #include <QQuickWindow> 0031 0032 class QTimer; 0033 0034 struct ImageData { 0035 struct colorStat { 0036 QList<QRgb> colors; 0037 QRgb centroid = 0; 0038 qreal ratio = 0; 0039 }; 0040 0041 struct colorSet { 0042 QColor average; 0043 QColor text; 0044 QColor background; 0045 QColor highlight; 0046 }; 0047 0048 QList<QRgb> m_samples; 0049 QList<colorStat> m_clusters; 0050 QVariantList m_palette; 0051 0052 bool m_darkPalette = true; 0053 QColor m_dominant; 0054 QColor m_dominantContrast; 0055 QColor m_average; 0056 QColor m_highlight; 0057 0058 QColor m_closestToBlack; 0059 QColor m_closestToWhite; 0060 }; 0061 0062 /** 0063 * Extracts the dominant colors from an element or an image and exports it to a color palette. 0064 */ 0065 class ImageColors : public QObject 0066 { 0067 Q_OBJECT 0068 QML_ELEMENT 0069 /** 0070 * The source from which colors should be extracted from. 0071 * 0072 * `source` can be one of the following: 0073 * * Item 0074 * * QImage 0075 * * QIcon 0076 * * Icon name 0077 * 0078 * Note that an Item's color palette will only be extracted once unless you * call `update()`, regardless of how the item hanges. 0079 */ 0080 Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged) 0081 0082 /** 0083 * A list of colors and related information about then. 0084 * 0085 * Each list item has the following properties: 0086 * * `color`: The color of the list item. 0087 * * `ratio`: How dominant the color is in the source image. 0088 * * `contrastingColor`: The color from the source image that's closest to the inverse of `color`. 0089 * 0090 * The list is sorted by `ratio`; the first element is the most 0091 * dominant color in the source image and the last element is the 0092 * least dominant color of the image. 0093 * 0094 * \note K-means clustering is used to extract these colors; see https://en.wikipedia.org/wiki/K-means_clustering. 0095 */ 0096 Q_PROPERTY(QVariantList palette READ palette NOTIFY paletteChanged) 0097 0098 /** 0099 * Information whether the palette is towards a light or dark color 0100 * scheme, possible values are: 0101 * * ColorUtils.Light 0102 * * ColorUtils.Dark 0103 */ 0104 Q_PROPERTY(ColorUtils::Brightness paletteBrightness READ paletteBrightness NOTIFY paletteChanged) 0105 0106 /** 0107 * The average color of the source image. 0108 */ 0109 Q_PROPERTY(QColor average READ average NOTIFY paletteChanged) 0110 0111 /** 0112 * The dominant color of the source image. 0113 * 0114 * The dominant color of the image is the color of the largest 0115 * cluster in the image. 0116 * 0117 * \sa https://en.wikipedia.org/wiki/K-means_clustering 0118 */ 0119 Q_PROPERTY(QColor dominant READ dominant NOTIFY paletteChanged) 0120 0121 /** 0122 * Suggested "contrasting" color to the dominant one. It's the color in the palette nearest to the negative of the dominant 0123 */ 0124 Q_PROPERTY(QColor dominantContrast READ dominantContrast NOTIFY paletteChanged) 0125 0126 /** 0127 * An accent color extracted from the source image. 0128 * 0129 * The accent color is the color cluster with the highest CIELAB 0130 * chroma in the source image. 0131 * 0132 * \sa https://en.wikipedia.org/wiki/Colorfulness#Chroma 0133 */ 0134 Q_PROPERTY(QColor highlight READ highlight NOTIFY paletteChanged) 0135 0136 /** 0137 * A color suitable for rendering text and other foreground 0138 * over the source image. 0139 * 0140 * On dark items, this will be the color closest to white in 0141 * the image if it's light enough, or a bright gray otherwise. 0142 * On light items, this will be the color closest to black in 0143 * the image if it's dark enough, or a dark gray otherwise. 0144 */ 0145 Q_PROPERTY(QColor foreground READ foreground NOTIFY paletteChanged) 0146 0147 /** 0148 * A color suitable for rendering a background behind the 0149 * source image. 0150 * 0151 * On dark items, this will be the color closest to black in the 0152 * image if it's dark enough, or a dark gray otherwise. 0153 * On light items, this will be the color closest to white 0154 * in the image if it's light enough, or a bright gray otherwise. 0155 */ 0156 Q_PROPERTY(QColor background READ background NOTIFY paletteChanged) 0157 0158 /** 0159 * The lightest color of the source image. 0160 */ 0161 Q_PROPERTY(QColor closestToWhite READ closestToWhite NOTIFY paletteChanged) 0162 0163 /** 0164 * The darkest color of the source image. 0165 */ 0166 Q_PROPERTY(QColor closestToBlack READ closestToBlack NOTIFY paletteChanged) 0167 0168 /** 0169 * The value to return when palette is not available, e.g. when 0170 * ImageColors is still computing it or the source is invalid. 0171 */ 0172 Q_PROPERTY(QVariantList fallbackPalette MEMBER m_fallbackPalette NOTIFY fallbackPaletteChanged) 0173 0174 /** 0175 * The value to return when paletteBrightness is not available, e.g. when 0176 * ImageColors is still computing it or the source is invalid. 0177 */ 0178 Q_PROPERTY(ColorUtils::Brightness fallbackPaletteBrightness MEMBER m_fallbackPaletteBrightness NOTIFY fallbackPaletteBrightnessChanged) 0179 0180 /** 0181 * The value to return when average is not available, e.g. when 0182 * ImageColors is still computing it or the source is invalid. 0183 */ 0184 Q_PROPERTY(QColor fallbackAverage MEMBER m_fallbackAverage NOTIFY fallbackAverageChanged) 0185 0186 /** 0187 * The value to return when dominant is not available, e.g. when 0188 * ImageColors is still computing it or the source is invalid. 0189 */ 0190 Q_PROPERTY(QColor fallbackDominant MEMBER m_fallbackDominant NOTIFY fallbackDominantChanged) 0191 0192 /** 0193 * The value to return when dominantContrasting is not available, e.g. when 0194 * ImageColors is still computing it or the source is invalid. 0195 */ 0196 Q_PROPERTY(QColor fallbackDominantContrasting MEMBER m_fallbackDominantContrasting NOTIFY fallbackDominantContrastingChanged) 0197 0198 /** 0199 * The value to return when highlight is not available, e.g. when 0200 * ImageColors is still computing it or the source is invalid. 0201 */ 0202 Q_PROPERTY(QColor fallbackHighlight MEMBER m_fallbackHighlight NOTIFY fallbackHighlightChanged) 0203 0204 /** 0205 * The value to return when foreground is not available, e.g. when 0206 * ImageColors is still computing it or the source is invalid. 0207 */ 0208 Q_PROPERTY(QColor fallbackForeground MEMBER m_fallbackForeground NOTIFY fallbackForegroundChanged) 0209 0210 /** 0211 * The value to return when background is not available, e.g. when 0212 * ImageColors is still computing it or the source is invalid. 0213 */ 0214 Q_PROPERTY(QColor fallbackBackground MEMBER m_fallbackBackground NOTIFY fallbackBackgroundChanged) 0215 0216 public: 0217 explicit ImageColors(QObject *parent = nullptr); 0218 ~ImageColors() override; 0219 0220 void setSource(const QVariant &source); 0221 QVariant source() const; 0222 0223 void setSourceImage(const QImage &image); 0224 QImage sourceImage() const; 0225 0226 void setSourceItem(QQuickItem *source); 0227 QQuickItem *sourceItem() const; 0228 0229 Q_INVOKABLE void update(); 0230 0231 QVariantList palette() const; 0232 ColorUtils::Brightness paletteBrightness() const; 0233 QColor average() const; 0234 QColor dominant() const; 0235 QColor dominantContrast() const; 0236 QColor highlight() const; 0237 QColor foreground() const; 0238 QColor background() const; 0239 QColor closestToWhite() const; 0240 QColor closestToBlack() const; 0241 0242 Q_SIGNALS: 0243 void sourceChanged(); 0244 void paletteChanged(); 0245 void fallbackPaletteChanged(); 0246 void fallbackPaletteBrightnessChanged(); 0247 void fallbackAverageChanged(); 0248 void fallbackDominantChanged(); 0249 void fallbackDominantContrastingChanged(); 0250 void fallbackHighlightChanged(); 0251 void fallbackForegroundChanged(); 0252 void fallbackBackgroundChanged(); 0253 0254 private: 0255 static inline void positionColor(QRgb rgb, QList<ImageData::colorStat> &clusters); 0256 static ImageData generatePalette(const QImage &sourceImage); 0257 0258 // Arbitrary number that seems to work well 0259 static const int s_minimumSquareDistance = 32000; 0260 QPointer<QQuickWindow> m_window; 0261 QVariant m_source; 0262 QPointer<QQuickItem> m_sourceItem; 0263 QSharedPointer<QQuickItemGrabResult> m_grabResult; 0264 QImage m_sourceImage; 0265 0266 QTimer *m_imageSyncTimer; 0267 0268 QFutureWatcher<ImageData> *m_futureImageData = nullptr; 0269 ImageData m_imageData; 0270 0271 QVariantList m_fallbackPalette; 0272 ColorUtils::Brightness m_fallbackPaletteBrightness; 0273 QColor m_fallbackAverage; 0274 QColor m_fallbackDominant; 0275 QColor m_fallbackDominantContrasting; 0276 QColor m_fallbackHighlight; 0277 QColor m_fallbackForeground; 0278 QColor m_fallbackBackground; 0279 };