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