KOSMIndoorMap

iconloader.cpp
1 /*
2  SPDX-FileCopyrightText: 2020 Volker Krause <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "iconloader_p.h"
8 
9 #include <QBuffer>
10 #include <QByteArray>
11 #include <QDebug>
12 #include <QFile>
13 #include <QGuiApplication>
14 #include <QIconEngine>
15 #include <QImageReader>
16 #include <QPainter>
17 #include <QXmlStreamReader>
18 #include <QXmlStreamWriter>
19 
20 using namespace KOSMIndoorMap;
21 
22 /** Device pixel ratio preserving simple icon engine for our SVG assets. */
23 class IconEngine : public QIconEngine
24 {
25 public:
26  explicit IconEngine(QImage &&img)
27  : m_image(std::move(img))
28  {
29  }
30 
31  ~IconEngine() = default;
32 
33  QList<QSize> availableSizes(QIcon::Mode mode, QIcon::State state) const override
34  {
35  Q_UNUSED(mode);
36  Q_UNUSED(state);
37  return { m_image.size() / m_image.devicePixelRatio() };
38  }
39 
40  QIconEngine* clone() const override
41  {
42  auto engine = new IconEngine;
43  engine->m_image = m_image;
44  return engine;
45  }
46 
47  void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override
48  {
49  Q_UNUSED(mode);
50  Q_UNUSED(state);
51  painter->drawImage(rect, m_image);
52  }
53 
54 private:
55  explicit IconEngine() = default;
56  QImage m_image;
57 };
58 
59 static bool operator<(const IconData &lhs, const IconData &rhs)
60 {
61  if (lhs.name == rhs.name) {
62  if (lhs.color.rgb() == rhs.color.rgb()) {
63  return lhs.size.width() < rhs.size.width();
64  }
65  return lhs.color.rgb() < rhs.color.rgb();
66  }
67  return lhs.name < rhs.name;
68 }
69 
70 static bool operator==(const IconData &lhs, const IconData &rhs)
71 {
72  return lhs.name == rhs.name && lhs.color == rhs.color && lhs.size == rhs.size;
73 }
74 
75 QIcon IconLoader::loadIcon(const IconData &iconData) const
76 {
77  // check our cache
78  auto it = std::lower_bound(m_cache.begin(), m_cache.end(), iconData, [](const auto &lhs, const auto &rhs) { return lhs.data < rhs; });
79  if (it != m_cache.end() && (*it).data == iconData) {
80  return (*it).icon;
81  }
82 
83  // check if it's one of our bundled assets
84  const QString path = QLatin1String(":/org.kde.kosmindoormap/assets/icons/") + iconData.name + QLatin1String(".svg");
85  QFile f(path);
86  if (f.open(QFile::ReadOnly)) {
87  CacheEntry entry;
88  entry.data = iconData;
89  entry.icon = loadSvgAsset(&f, iconData);
90  it = m_cache.insert(it, std::move(entry));
91  return (*it).icon;
92  }
93 
94  // TODO file system URLs
95 
96  // XDG icons
97  const auto icon = QIcon::fromTheme(iconData.name);
98  if (icon.isNull()) {
99  qWarning() << "Failed to find icon:" << iconData.name;
100  }
101  return icon;
102 }
103 
104 QIcon IconLoader::loadSvgAsset(QIODevice *svgFile, const IconData &iconData) const
105 {
106  // prepare CSS
107  const QString css = QLatin1String(".ColorScheme-Text { color:") + iconData.color.name(QColor::HexArgb) + QLatin1String("; }");
108 
109  // inject CSS (inspired by KIconLoader)
110  QByteArray processedContents;
111  QXmlStreamReader reader(svgFile);
112  QBuffer buffer(&processedContents);
113  buffer.open(QIODevice::WriteOnly);
114  QXmlStreamWriter writer(&buffer);
115  while (!reader.atEnd()) {
116  if (reader.readNext() == QXmlStreamReader::StartElement &&
117  reader.qualifiedName() == QLatin1String("style") &&
118  reader.attributes().value(QLatin1String("id")) == QLatin1String("current-color-scheme")) {
119  writer.writeStartElement(QStringLiteral("style"));
120  writer.writeAttributes(reader.attributes());
121  writer.writeCharacters(css);
122  writer.writeEndElement();
123  while (reader.tokenType() != QXmlStreamReader::EndElement) {
124  reader.readNext();
125  }
126  } else if (reader.tokenType() != QXmlStreamReader::Invalid) {
127  writer.writeCurrentToken(reader);
128  }
129  }
130  buffer.close();
131 
132  // render SVG
133  buffer.open(QIODevice::ReadOnly);
134  buffer.seek(0);
135  QImageReader imgReader(&buffer, "svg");
136  imgReader.setScaledSize((iconData.size.isValid() ? iconData.size.toSize() : imgReader.size()) * qGuiApp->devicePixelRatio());
137  auto img = imgReader.read();
138  img.setDevicePixelRatio(qGuiApp->devicePixelRatio());
139  return QIcon(new IconEngine(std::move(img)));
140 }
OSM-based multi-floor indoor maps for buildings.
bool operator==(const Qt3DRender::QGraphicsApiFilter &reference, const Qt3DRender::QGraphicsApiFilter &sample)
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags)
QIcon fromTheme(const QString &name)
KIOCORE_EXPORT CopyJob * move(const QUrl &src, const QUrl &dest, JobFlags flags=DefaultFlags)
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 23:04:00 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.