Marble

OsmcSymbol.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2017 Sergey Popov <sergobot@protonmail.com>
4//
5
6#include "OsmcSymbol.h"
7
8#include <QDebug>
9#include <QDomDocument>
10#include <QFile>
11#include <QPainter>
12
13OsmcSymbol::OsmcSymbol(const QString &tag, int size)
14 : m_wayColor(Qt::white)
15 , m_backgroundColor(Qt::black)
16 , m_foreground(nullptr)
17 , m_foreground2(nullptr)
18 , m_textColor(Qt::black)
19 , m_side(size)
20{
21 m_backgroundTypes << "round"
22 << "circle"
23 << "frame";
24
25 m_foregroundTypes << "dot"
26 << "bowl"
27 << "circle"
28 << "bar"
29 << "stripe"
30 << "cross"
31 << "x"
32 << "slash"
33 << "backslash"
34 << "rectangle"
35 << "rectangle_line"
36 << "triangle"
37 << "triangle_turned"
38 << "triangle_line"
39 << "diamond"
40 << "pointer"
41 << "fork"
42 << "arch"
43 << "turned_T"
44 << "L"
45 << "lower"
46 << "corner"
47 << "drop_line"
48 << "horse"
49 << "hiker";
50
51 m_precoloredForegroundTypes << "wolfshook"
52 << "shell"
53 << "shell_modern"
54 << "ammonit"
55 << "mine"
56 << "hiker"
57 << "heart"
58 << "tower"
59 << "bridleway";
60
61 if (parseTag(tag)) {
62 render();
63 }
64}
65
66OsmcSymbol::~OsmcSymbol()
67{
68 delete m_foreground;
69 delete m_foreground2;
70}
71
72bool OsmcSymbol::parseTag(const QString &tag)
73{
74 QStringList parts = tag.split(':');
75
76 if (parts.size() < 2) {
77 return false;
78 }
79
80 if (m_foreground) {
81 delete m_foreground;
82 m_foreground = nullptr;
83 }
84 if (m_foreground2) {
85 delete m_foreground2;
86 m_foreground2 = nullptr;
87 }
88
89 // Determine way color
90 if (QColor::isValidColorName(parts.at(0))) {
91 m_wayColor.fromString(parts.at(0));
92 } else {
93 return false;
94 }
95
96 if (!parseBackground(parts.at(1))) {
97 return false;
98 }
99
100 if (parts.size() == 3) {
101 m_foreground = parseForeground(parts.at(2));
102 } else if (parts.size() == 4) {
103 if (QColor::isValidColorName(parts.at(3))) {
104 m_text = parts.at(2);
105 m_textColor = parts.at(3);
106 } else {
107 m_foreground = parseForeground(parts.at(2));
108 m_foreground2 = parseForeground(parts.at(3));
109 }
110 } else if (parts.size() == 5) {
111 m_foreground = parseForeground(parts.at(2));
112 if (QColor::isValidColorName(parts.at(4))) {
113 m_text = parts.at(3);
114 m_textColor = parts.at(4);
115 } else {
116 return false;
117 }
118 } else if (parts.size() == 6) {
119 m_foreground = parseForeground(parts.at(2));
120 m_foreground2 = parseForeground(parts.at(3));
121 if (QColor::isValidColorName(parts.at(5))) {
122 m_text = parts.at(4);
123 m_textColor.fromString(parts.at(5));
124 } else {
125 return false;
126 }
127 } else {
128 return false;
129 }
130
131 return true;
132}
133
134bool OsmcSymbol::parseBackground(const QString &bg)
135{
136 QString color = bg.section("_", 0, 0);
137 QString type = bg.section("_", 1, -1);
138
139 if (!QColor::isValidColorName(color)) {
140 return false;
141 }
142
143 // Plain color was provided
144 if (type.isEmpty()) {
145 m_backgroundColor.fromString(color);
146 m_backgroundType = type;
147 } else if (m_backgroundTypes.contains(type)) {
148 m_backgroundColor.fromString(color);
149 m_backgroundType = type;
150 } else {
151 return false;
152 }
153
154 return true;
155}
156
157void setXMLAttribute(QDomElement &elem, const QString &tag, const QString &attr, const QString &attrValue);
158
159QSvgRenderer *OsmcSymbol::parseForeground(const QString &fg)
160{
161 if (m_precoloredForegroundTypes.contains(fg)) {
162 return new QSvgRenderer(QStringLiteral(":/osmc-symbols/%1.svg").arg(fg));
163 }
164
165 QString color = fg.section('_', 0, 0);
166 QString type = fg.section('_', 1, -1);
167 if (QColor::isValidColorName(color) && m_foregroundTypes.contains(type)) {
168 // Open svg resource and load contents to QByteArray
169 QFile file(QStringLiteral(":/osmc-symbols/%1.svg").arg(type));
170 file.open(QIODevice::ReadOnly);
171 QByteArray baData = file.readAll();
172
173 // Load svg contents to xml document
174 QDomDocument doc;
175 doc.setContent(baData);
176
177 // Recursively change color
178 QDomElement rootElement = doc.documentElement();
179 setXMLAttribute(rootElement, "path", "fill", color);
180
181 // Create and return svg renderer with edited contents
182 return new QSvgRenderer(doc.toByteArray());
183 }
184
185 return nullptr;
186}
187
188void OsmcSymbol::render()
189{
190 m_image = QImage(m_side, m_side, QImage::Format_ARGB32);
191 m_image.fill(Qt::transparent);
192
193 QPainter painter(&m_image);
194 painter.setRenderHint(QPainter::Antialiasing);
195
196 // Default size of background
197 int w = m_side, h = m_side;
198
199 // If there is some text, our background size must be recalculated
200 if (!m_text.isEmpty()) {
201 QFont font = painter.font();
202 font.setPixelSize(int(m_side * 0.8));
203 font.setBold(true);
204 painter.setFont(font);
205 QFontMetrics fm = QFontMetrics(font);
206
207 h = fm.height();
208 w = qMax(h, fm.horizontalAdvance(m_text));
209 }
210
211 const QRect bgRect = QRect((m_side - w) / 2, (m_side - h) / 2, w, h);
212
213 // Draw symbol's background
214 if (m_backgroundType.isEmpty()) {
215 painter.fillRect(bgRect, m_backgroundColor);
216 } else if (m_backgroundType == "round") {
217 painter.setBrush(m_backgroundColor);
218 painter.setPen(m_backgroundColor);
219 painter.drawEllipse(bgRect);
220 } else if (m_backgroundType == "circle") {
221 painter.setBrush(Qt::white);
222 painter.setPen(QPen(m_backgroundColor, m_side / 10));
223 painter.drawEllipse(bgRect);
224 } else if (m_backgroundType == "frame") {
225 painter.setPen(QPen(m_backgroundColor, m_side / 10));
226 painter.fillRect(bgRect, Qt::white);
227 painter.drawRect(bgRect);
228 }
229
230 QPixmap foregrounds(bgRect.size());
231 foregrounds.fill(Qt::transparent);
232 QPainter fgPainter(&foregrounds);
233 m_foreground ? m_foreground->render(&fgPainter) : void();
234 m_foreground2 ? m_foreground2->render(&fgPainter) : void();
235 painter.drawPixmap(bgRect, foregrounds);
236
237 if (!m_text.isEmpty()) {
238 // Draw text with provided color
239 painter.setPen(m_textColor);
240 painter.drawText(bgRect, Qt::AlignCenter, m_text);
241 }
242
243 painter.end();
244}
245
246QImage OsmcSymbol::icon() const
247{
248 return m_image;
249}
250
251QColor OsmcSymbol::wayColor() const
252{
253 return m_wayColor;
254}
255
256void setXMLAttribute(QDomElement &elem, const QString &tag, const QString &attr, const QString &attrValue)
257{
258 // If elem's tag is equal to the provided one then overwrite desired attribute
259 if (elem.tagName() == tag) {
260 elem.setAttribute(attr, attrValue);
261 }
262
263 // Do the same for all the child nodes
264 for (int i = 0; i < elem.childNodes().count(); ++i) {
265 if (!elem.childNodes().at(i).isElement()) {
266 continue;
267 }
268
269 QDomElement child = elem.childNodes().at(i).toElement();
270 setXMLAttribute(child, tag, attr, attrValue);
271 }
272}
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
QColor fromString(QAnyStringView name)
bool isValidColorName(QAnyStringView name)
QDomElement documentElement() const const
ParseResult setContent(QAnyStringView text, ParseOptions options)
QByteArray toByteArray(int indent) const const
void setAttribute(const QString &name, const QString &value)
QString tagName() const const
QDomNodeList childNodes() const const
bool isElement() const const
QDomElement toElement() const const
QDomNode at(int index) const const
void setBold(bool enable)
void setPixelSize(int pixelSize)
int height() const const
int horizontalAdvance(QChar ch) const const
void fill(Qt::GlobalColor color)
const_reference at(qsizetype i) const const
qsizetype size() const const
QSize size() const const
bool isEmpty() const const
QString section(QChar sep, qsizetype start, qsizetype end, SectionFlags flags) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
void render(QPainter *painter)
AlignCenter
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 20 2024 11:52:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.