File indexing completed on 2024-04-21 03:49:49
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2017 Sergey Popov <sergobot@protonmail.com> 0004 // 0005 0006 #include "OsmcSymbol.h" 0007 0008 #include <QDebug> 0009 #include <QDomDocument> 0010 #include <QFile> 0011 #include <QPainter> 0012 0013 OsmcSymbol::OsmcSymbol(const QString &tag, int size) 0014 : m_wayColor(Qt::white) 0015 , m_backgroundColor(Qt::black) 0016 , m_foreground(nullptr) 0017 , m_foreground2(nullptr) 0018 , m_textColor(Qt::black) 0019 , m_side(size) 0020 { 0021 m_backgroundTypes 0022 << "round" << "circle" << "frame"; 0023 0024 m_foregroundTypes 0025 << "dot" << "bowl" << "circle" << "bar" 0026 << "stripe" << "cross" << "x" << "slash" 0027 << "backslash" << "rectangle" << "rectangle_line" 0028 << "triangle" << "triangle_turned" << "triangle_line" 0029 << "diamond" << "pointer" << "fork" << "arch" 0030 << "turned_T" << "L" << "lower" << "corner" 0031 << "drop_line" << "horse" << "hiker"; 0032 0033 m_precoloredForegroundTypes 0034 << "wolfshook" << "shell" << "shell_modern" << "ammonit" 0035 << "mine" << "hiker" << "heart" << "tower" << "bridleway"; 0036 0037 if (parseTag(tag)) { 0038 render(); 0039 } 0040 } 0041 0042 OsmcSymbol::~OsmcSymbol() 0043 { 0044 delete m_foreground; 0045 delete m_foreground2; 0046 } 0047 0048 bool OsmcSymbol::parseTag(const QString &tag) 0049 { 0050 QStringList parts = tag.split(':'); 0051 0052 if (parts.size() < 2) { 0053 return false; 0054 } 0055 0056 if (m_foreground) { 0057 delete m_foreground; 0058 m_foreground = nullptr; 0059 } 0060 if (m_foreground2) { 0061 delete m_foreground2; 0062 m_foreground2 = nullptr; 0063 } 0064 0065 // Determine way color 0066 if (QColor::isValidColor(parts.at(0))) { 0067 m_wayColor.setNamedColor(parts.at(0)); 0068 } else { 0069 return false; 0070 } 0071 0072 if (!parseBackground(parts.at(1))) { 0073 return false; 0074 } 0075 0076 if (parts.size() == 3) { 0077 m_foreground = parseForeground(parts.at(2)); 0078 } else if (parts.size() == 4) { 0079 if (QColor::isValidColor(parts.at(3))) { 0080 m_text = parts.at(2); 0081 m_textColor = parts.at(3); 0082 } else { 0083 m_foreground = parseForeground(parts.at(2)); 0084 m_foreground2 = parseForeground(parts.at(3)); 0085 } 0086 } else if (parts.size() == 5) { 0087 m_foreground = parseForeground(parts.at(2)); 0088 if (QColor::isValidColor(parts.at(4))) { 0089 m_text = parts.at(3); 0090 m_textColor = parts.at(4); 0091 } else { 0092 return false; 0093 } 0094 } else if (parts.size() == 6) { 0095 m_foreground = parseForeground(parts.at(2)); 0096 m_foreground2 = parseForeground(parts.at(3)); 0097 if (QColor::isValidColor(parts.at(5))) { 0098 m_text = parts.at(4); 0099 m_textColor.setNamedColor(parts.at(5)); 0100 } else { 0101 return false; 0102 } 0103 } else { 0104 return false; 0105 } 0106 0107 return true; 0108 } 0109 0110 bool OsmcSymbol::parseBackground(const QString &bg) 0111 { 0112 QString color = bg.section("_", 0, 0); 0113 QString type = bg.section("_", 1, -1); 0114 0115 if (!QColor::isValidColor(color)) { 0116 return false; 0117 } 0118 0119 // Plain color was provided 0120 if (type.isEmpty()) { 0121 m_backgroundColor.setNamedColor(color); 0122 m_backgroundType = type; 0123 } else if (m_backgroundTypes.contains(type)) { 0124 m_backgroundColor.setNamedColor(color); 0125 m_backgroundType = type; 0126 } else { 0127 return false; 0128 } 0129 0130 return true; 0131 } 0132 0133 void setXMLAttribute(QDomElement &elem, const QString& tag, const QString& attr, const QString& attrValue); 0134 0135 QSvgRenderer* OsmcSymbol::parseForeground(const QString &fg) 0136 { 0137 if (m_precoloredForegroundTypes.contains(fg)) { 0138 return new QSvgRenderer(QString(":/osmc-symbols/%1.svg").arg(fg)); 0139 } 0140 0141 QString color = fg.section('_', 0, 0); 0142 QString type = fg.section('_', 1, -1); 0143 if (QColor::isValidColor(color) && m_foregroundTypes.contains(type)) { 0144 // Open svg resource and load contents to QByteArray 0145 QFile file(QString(":/osmc-symbols/%1.svg").arg(type)); 0146 file.open(QIODevice::ReadOnly); 0147 QByteArray baData = file.readAll(); 0148 0149 // Load svg contents to xml document 0150 QDomDocument doc; 0151 doc.setContent(baData); 0152 0153 // Recursively change color 0154 QDomElement rootElement = doc.documentElement(); 0155 setXMLAttribute(rootElement, "path", "fill", color); 0156 0157 // Create and return svg renderer with edited contents 0158 return new QSvgRenderer(doc.toByteArray()); 0159 } 0160 0161 return nullptr; 0162 } 0163 0164 void OsmcSymbol::render() 0165 { 0166 m_image = QImage(m_side, m_side, QImage::Format_ARGB32); 0167 m_image.fill(Qt::transparent); 0168 0169 QPainter painter(&m_image); 0170 painter.setRenderHint(QPainter::Antialiasing); 0171 0172 // Default size of background 0173 int w = m_side, h = m_side; 0174 0175 // If there is some text, our background size must be recalculated 0176 if (!m_text.isEmpty()) { 0177 QFont font = painter.font(); 0178 font.setPixelSize(int(m_side * 0.8)); 0179 font.setBold(true); 0180 painter.setFont(font); 0181 QFontMetrics fm = QFontMetrics(font); 0182 0183 h = fm.height(); 0184 w = qMax(h, fm.horizontalAdvance(m_text)); 0185 } 0186 0187 const QRect bgRect = QRect((m_side - w) / 2, (m_side - h) / 2, w, h); 0188 0189 // Draw symbol's background 0190 if (m_backgroundType.isEmpty()) { 0191 painter.fillRect(bgRect, m_backgroundColor); 0192 } else if (m_backgroundType == "round") { 0193 painter.setBrush(m_backgroundColor); 0194 painter.setPen(m_backgroundColor); 0195 painter.drawEllipse(bgRect); 0196 } else if (m_backgroundType == "circle") { 0197 painter.setBrush(Qt::white); 0198 painter.setPen(QPen(m_backgroundColor, m_side / 10)); 0199 painter.drawEllipse(bgRect); 0200 } else if (m_backgroundType == "frame") { 0201 painter.setPen(QPen(m_backgroundColor, m_side / 10)); 0202 painter.fillRect(bgRect, Qt::white); 0203 painter.drawRect(bgRect); 0204 } 0205 0206 QPixmap foregrounds(bgRect.size()); 0207 foregrounds.fill(Qt::transparent); 0208 QPainter fgPainter(&foregrounds); 0209 m_foreground ? m_foreground->render(&fgPainter) : void(); 0210 m_foreground2 ? m_foreground2->render(&fgPainter) : void(); 0211 painter.drawPixmap(bgRect, foregrounds); 0212 0213 if (!m_text.isEmpty()) { 0214 // Draw text with provided color 0215 painter.setPen(m_textColor); 0216 painter.drawText(bgRect, Qt::AlignCenter, m_text); 0217 } 0218 0219 painter.end(); 0220 } 0221 0222 QImage OsmcSymbol::icon() const 0223 { 0224 return m_image; 0225 } 0226 0227 QColor OsmcSymbol::wayColor() const 0228 { 0229 return m_wayColor; 0230 } 0231 0232 void setXMLAttribute(QDomElement &elem, const QString& tag, const QString& attr, const QString& attrValue) 0233 { 0234 // If elem's tag is equal to the provided one then overwrite desired attribute 0235 if (elem.tagName() == tag) { 0236 elem.setAttribute(attr, attrValue); 0237 } 0238 0239 // Do the same for all the child nodes 0240 for (int i = 0; i < elem.childNodes().count(); ++i) { 0241 if (!elem.childNodes().at(i).isElement()) { 0242 continue; 0243 } 0244 0245 QDomElement child = elem.childNodes().at(i).toElement(); 0246 setXMLAttribute(child, tag, attr, attrValue); 0247 } 0248 }