From 01fba7b74262a1c25d45733b8589d8c5c120be65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Fri, 8 Nov 2024 16:39:01 +0100 Subject: [PATCH] Render repeating "always-display" lineSymbols as bitmap lines --- src/map/bitmapline.cpp | 21 ++++++ src/map/bitmapline.h | 2 + src/map/mapsforge/rastertile.cpp | 21 ++++-- src/map/mapsforge/style.cpp | 107 +++++++++++++++++++++---------- src/map/mapsforge/style.h | 6 +- 5 files changed, 115 insertions(+), 42 deletions(-) diff --git a/src/map/bitmapline.cpp b/src/map/bitmapline.cpp index 8eeb9018..49420510 100644 --- a/src/map/bitmapline.cpp +++ b/src/map/bitmapline.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "bitmapline.h" @@ -58,3 +59,23 @@ void BitmapLine::draw(QPainter *painter, const QVector &lines, for (int i = 0; i < lines.size(); i++) draw(painter, lines.at(i), img); } + +void BitmapLine::draw(QPainter *painter, const QPainterPath &line, + const QImage &img) +{ + int offset = 0; + + for (int i = 1; i < line.elementCount(); i++) { + QLineF segment(line.elementAt(i-1).x, line.elementAt(i-1).y, + line.elementAt(i).x, line.elementAt(i).y); + int len = qCeil(segment.length() * img.devicePixelRatio()); + + painter->save(); + painter->translate(segment.p1()); + painter->rotate(-segment.angle()); + painter->drawImage(0.0, -img.height()/2.0, img2line(img, len, offset)); + painter->restore(); + + offset = (len + offset) % img.width(); + } +} diff --git a/src/map/bitmapline.h b/src/map/bitmapline.h index dc11f0c4..fcaef143 100644 --- a/src/map/bitmapline.h +++ b/src/map/bitmapline.h @@ -6,12 +6,14 @@ class QPainter; class QImage; class QPolygonF; +class QPainterPath; namespace BitmapLine { void draw(QPainter *painter, const QPolygonF &line, const QImage &img); void draw(QPainter *painter, const QVector &lines, const QImage &img); + void draw(QPainter *painter, const QPainterPath &line, const QImage &img); } #endif // BITMAPLINE_H diff --git a/src/map/mapsforge/rastertile.cpp b/src/map/mapsforge/rastertile.cpp index 8883ace1..859d0f27 100644 --- a/src/map/mapsforge/rastertile.cpp +++ b/src/map/mapsforge/rastertile.cpp @@ -5,6 +5,7 @@ #include "map/rectd.h" #include "map/hillshading.h" #include "map/filter.h" +#include "map/bitmapline.h" #include "rastertile.h" using namespace Mapsforge; @@ -421,13 +422,21 @@ void RasterTile::drawPaths(QPainter *painter, const QList &paths, if (!path->pp.elementCount()) path->pp = painterPath(path->path->poly, ri->curve()); - painter->setPen(ri->pen(_zoom)); - painter->setBrush(ri->brush()); + if (ri->bitmapLine()) { + if (dy != 0) + BitmapLine::draw(painter, parallelPath(path->pp, dy), + ri->img()); + else + BitmapLine::draw(painter, path->pp, ri->img()); + } else { + painter->setPen(ri->pen(_zoom)); + painter->setBrush(ri->brush()); - if (dy != 0) - painter->drawPath(parallelPath(path->pp, dy)); - else - painter->drawPath(path->pp); + if (dy != 0) + painter->drawPath(parallelPath(path->pp, dy)); + else + painter->drawPath(path->pp); + } } else if (point) { const Style::CircleRender *ri = is.circleRender(); qreal radius = ri->radius(_zoom); diff --git a/src/map/mapsforge/style.cpp b/src/map/mapsforge/style.cpp index 178d172f..2485ae04 100644 --- a/src/map/mapsforge/style.cpp +++ b/src/map/mapsforge/style.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "common/programpaths.h" #include "style.h" @@ -388,8 +389,8 @@ void Style::circle(QXmlStreamReader &reader, qreal baseStrokeWidth, reader.skipCurrentElement(); } -void Style::text(QXmlStreamReader &reader, const MapData &data, const Rule &rule, - QList*> &lists) +void Style::text(QXmlStreamReader &reader, const MapData &data, + const Rule &rule, bool line) { TextRender ri(rule); const QXmlStreamAttributes &attr = reader.attributes(); @@ -462,21 +463,28 @@ void Style::text(QXmlStreamReader &reader, const MapData &data, const Rule &rule ri._font.setItalic(italic); ri._font.setCapitalization(capitalization); - if (fontSize) - for (int i = 0; i < lists.size(); i++) - lists[i]->append(ri); + if (fontSize) { + if (line) + _pathLabels.append(ri); + else { + if (rule._type == Rule::WayType || rule._type == Rule::AnyType) + _areaLabels.append(ri); + if (rule._type == Rule::NodeType || rule._type == Rule::AnyType) + _pointLabels.append(ri); + } + } reader.skipCurrentElement(); } void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio, - const Rule &rule, QList &list) + const Rule &rule, bool line) { Symbol ri(rule); const QXmlStreamAttributes &attr = reader.attributes(); QString file; int height = 0, width = 0, percent = 100; - bool ok; + bool ok, bitmapLine = false; if (attr.hasAttribute("src")) file = resourcePath(attr.value("src").toString(), dir); @@ -505,23 +513,62 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio, return; } } - if (attr.hasAttribute("priority")) { - ri._priority = attr.value("priority").toInt(&ok); - if (!ok) { - reader.raiseError("invalid priority value"); + + // Convert repeating "always-display" lineSymbols to bitmap lines + if (line && (rule._type == Rule::AnyType || rule._type == Rule::WayType)) { + bool repeat = (attr.value("repeat").toString() == "true"); + bool always = (attr.value("display").toString() == "always"); + double start = attr.hasAttribute("repeat-start") + ? attr.value("repeat-start").toDouble(&ok) : 30; + + if (always && repeat && ok && start == 0) + bitmapLine = true; + } + + if (bitmapLine) { + PathRender pr(rule, _paths.size() + _circles.size() + + _hillShading.isValid()); + + double gap = attr.hasAttribute("repeat-gap") + ? attr.value("repeat-gap").toDouble(&ok) : 200; + if (!ok || gap < 0) { + reader.raiseError("invalid repeat-gap value"); return; } - } - if (attr.hasAttribute("rotate")) { - if (attr.value("rotate").toString() == "false") - ri._rotate = false; - } - if (attr.hasAttribute("id")) - ri._id = attr.value("id").toString(); - ri._img = image(file, width, height, percent, ratio); + QImage s(image(file, width, height, percent, ratio)); + pr._img = QImage(qCeil(gap) + s.width(), s.height(), + QImage::Format_ARGB32_Premultiplied); + pr._img.setDevicePixelRatio(s.devicePixelRatio()); + pr._img.fill(Qt::transparent); + QPainter painter(&pr._img); + painter.drawImage(QPoint(0, 0), s); - list.append(ri); + pr._brush = Qt::NoBrush; + + _paths.append(pr); + } else { + ri._img = image(file, width, height, percent, ratio); + + if (attr.hasAttribute("priority")) { + ri._priority = attr.value("priority").toInt(&ok); + if (!ok) { + reader.raiseError("invalid priority value"); + return; + } + } + if (attr.hasAttribute("rotate")) { + if (attr.value("rotate").toString() == "false") + ri._rotate = false; + } + if (attr.hasAttribute("id")) + ri._id = attr.value("id").toString(); + + if (line) + _lineSymbols.append(ri); + else + _symbols.append(ri); + } reader.skipCurrentElement(); } @@ -580,22 +627,14 @@ void Style::rule(QXmlStreamReader &reader, const QString &dir, line(reader, dir, ratio, baseStrokeWidth, r); else if (reader.name() == QLatin1String("circle")) circle(reader, baseStrokeWidth, r); - else if (reader.name() == QLatin1String("pathText")) { - QList*> list; - list.append(&_pathLabels); - text(reader, data, r, list); - } else if (reader.name() == QLatin1String("caption")) { - QList*> list; - if (r._type == Rule::WayType || r._type == Rule::AnyType) - list.append(&_areaLabels); - if (r._type == Rule::NodeType || r._type == Rule::AnyType) - list.append(&_pointLabels); - text(reader, data, r, list); - } + else if (reader.name() == QLatin1String("pathText")) + text(reader, data, r, true); + else if (reader.name() == QLatin1String("caption")) + text(reader, data, r, false); else if (reader.name() == QLatin1String("symbol")) - symbol(reader, dir, ratio, r, _symbols); + symbol(reader, dir, ratio, r, false); else if (reader.name() == QLatin1String("lineSymbol")) - symbol(reader, dir, ratio, r, _lineSymbols); + symbol(reader, dir, ratio, r, true); else reader.skipCurrentElement(); } diff --git a/src/map/mapsforge/style.h b/src/map/mapsforge/style.h index bde3a7db..6270e397 100644 --- a/src/map/mapsforge/style.h +++ b/src/map/mapsforge/style.h @@ -161,6 +161,8 @@ public: bool area() const {return _area;} bool curve() const {return _curve;} qreal dy(int zoom) const; + const QImage &img() const {return _img;} + bool bitmapLine() const {return !_img.isNull() && _strokeWidth == 0;} private: friend class Style; @@ -333,9 +335,9 @@ private: const Rule &rule); void hillshading(QXmlStreamReader &reader, const QSet &cats); void text(QXmlStreamReader &reader, const MapData &data, const Rule &rule, - QList *> &lists); + bool line); void symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio, - const Rule &rule, QList &list); + const Rule &rule, bool line); }; }