FlightGear next
QmlColoredImageProvider.cxx
Go to the documentation of this file.
1// Copyright (C) 2021 James Turner <james@flightgear.org>
2//
3// This program is free software; you can redistribute it and/or
4// modify it under the terms of the GNU General Public License as
5// published by the Free Software Foundation; either version 2 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11// General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program; if not, write to the Free Software
15// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16
18
19#include <QDebug>
20#include <QQmlComponent>
21#include <QQmlEngine>
22
24
25QmlColoredImageProvider::QmlColoredImageProvider() : QQuickImageProvider(QQmlImageProviderBase::Image)
26{
27 static_instance = this;
28}
29
30void QmlColoredImageProvider::loadStyleColors(QQmlEngine* engine, int styleTypeId)
31{
32#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 0)
33 QJSValue styleValue = engine->singletonInstance<QJSValue>(styleTypeId);
34 if (styleValue.isNull() || !styleValue.isQObject()) {
35 qWarning() << "Unable to load Style object";
36 return;
37 }
38
39 QObject* styleObject = styleValue.toQObject();
40#else
41 // ugly version for Qt < 5.12 : parse and instantiate a dummy object to let
42 // us access the Style singleton
43 QQmlComponent comp(engine);
44 comp.setData(R"(
45 import QtQuick 2.0
46 import FlightGear 1.0
47 QtObject {
48 readonly property var styleObject: Style
49 }
50 )",
51 {});
52 if (comp.isError()) {
53 qWarning() << Q_FUNC_INFO << "Failed to create style accessor component" << comp.errors();
54 return;
55 }
56
57 auto item = comp.create();
58 if (!item) {
59 qWarning() << Q_FUNC_INFO << "Failed to create component instance";
60 return;
61 }
62
63 QObject* styleObject = item->property("styleObject").value<QObject*>();
64 item->deleteLater();
65#endif
66
67 _themeColor = QColor{styleObject->property("themeColor").toString()};
68 _textColor = QColor{styleObject->property("baseTextColor").toString()};
69 _themeContrastColor = QColor{styleObject->property("themeContrastTextColor").toString()};
70 _activeColor = QColor{styleObject->property("activeColor").toString()};
71 _destructiveColor = QColor{styleObject->property("destructiveActionColor").toString()};
72}
73
74QImage QmlColoredImageProvider::requestImage(const QString& id, QSize* size, const QSize& requestedSize)
75{
76 QString path = ":/icon/" + id;
77 QColor c = _themeColor;
78
79 auto queryPos = id.indexOf('?');
80 if (queryPos >= 0) {
81 path = ":/icon/" + id.left(queryPos); // without the query part
82 const QString q = id.mid(queryPos + 1);
83 if (q.startsWith('#')) {
84 c = QColor(q); // allow directly specifying a color
85 } else if (q == "text") {
86 c = _textColor;
87 } else if (q == "themeContrast") {
88 c = _themeContrastColor;
89 } else if (q == "active") {
90 c = _activeColor;
91 } else if (q == "destructive") {
92 c = _destructiveColor;
93 } else if (q == "theme") {
94 // default already
95 } else {
96 qWarning() << Q_FUNC_INFO << "Unrecognized color specification:" << id;
97 }
98 }
99
100 QImage originalImage = QImage{path};
101 if (originalImage.isNull()) {
102 qWarning() << Q_FUNC_INFO << "Failed to load image:" << path;
103 return {};
104 }
105
106 if (!originalImage.isGrayscale()) {
107 qWarning() << Q_FUNC_INFO << "Source image is not a greyscale mask:" << path;
108 }
109
110 const int baseRed = c.red();
111 const int baseGreen = c.green();
112 const int baseBlue = c.blue();
113 *size = originalImage.size();
114
115 // colorize it
116 QImage colored{originalImage.size(), QImage::Format_ARGB32_Premultiplied};
117 const int width = size->width();
118 const int height = size->height();
119
120 for (int y = 0; y < height; ++y) {
121 for (int x = 0; x < width; ++x) {
122 // this is 0..255 ranged alpha/intensity value
123 const int alpha = originalImage.pixel(x, y);
124 colored.setPixel(x, y, qPremultiply(qRgba(baseRed, baseGreen, baseBlue, alpha)));
125 }
126 }
127
128 return colored;
129}
130
131QPixmap QmlColoredImageProvider::requestPixmap(const QString& id, QSize* size, const QSize& requestedSize)
132{
133 QImage img = requestImage(id, size, requestedSize);
134 return QPixmap::fromImage(img);
135}
136
static std::unique_ptr< FavouriteAircraftData > static_instance
Heper image provider to allow re-colorizing images based on the active style.
void loadStyleColors(QQmlEngine *engine, int styleTypeId)
QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override
static QmlColoredImageProvider * instance()
QPixmap requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) override