From 663859ef1cb12e7aaea59d086cdaab018881dba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Sun, 18 Apr 2021 12:20:07 +0200 Subject: [PATCH] Improved mapsforge hi-dpi rendering --- src/map/IMG/rastertile.cpp | 12 ++++--- src/map/mapsforge/rastertile.cpp | 45 +++++++++++++++----------- src/map/mapsforge/rastertile.h | 18 ++++++----- src/map/mapsforge/style.cpp | 2 ++ src/map/mapsforge/style.h | 5 ++- src/map/mapsforgemap.cpp | 22 +++++++++---- src/map/mapsforgemap.h | 2 ++ src/map/textpathitem.cpp | 25 ++++++++------- src/map/textpathitem.h | 4 +-- src/map/textpointitem.cpp | 54 +++++++++++++++++--------------- src/map/textpointitem.h | 6 ++-- 11 files changed, 114 insertions(+), 81 deletions(-) diff --git a/src/map/IMG/rastertile.cpp b/src/map/IMG/rastertile.cpp index d1b808f2..82b3227a 100644 --- a/src/map/IMG/rastertile.cpp +++ b/src/map/IMG/rastertile.cpp @@ -13,6 +13,8 @@ using namespace IMG; #define AREA(rect) \ (rect.size().width() * rect.size().height()) +static const QColor textColor(Qt::black); +static const QColor haloColor(Qt::white); static const QColor shieldColor(Qt::white); static const QColor shieldBgColor1("#dd3e3e"); static const QColor shieldBgColor2("#379947"); @@ -316,8 +318,8 @@ void RasterTile::processPolygons(QList &textItems) || Style::isNatureReserve(poly.type))) { const Style::Polygon &style = _style->polygon(poly.type); TextPointItem *item = new TextPointItem( - centroid(poly.points).toPoint(), &poly.label.text(), - poiFont(), 0, &style.brush().color()); + centroid(poly.points).toPoint(), &poly.label.text(), poiFont(), + 0, &style.brush().color(), &haloColor); if (item->isValid() && !item->collides(textItems) && !item->collides(labels) && !(exists && tileRect.contains(item->boundingRect())) @@ -417,7 +419,7 @@ void RasterTile::processShields(const QRect &tileRect, TextPointItem *item = new TextPointItem( p.at(jt.value()).toPoint(), &(sp.value(it.key())->text()), - poiFont(), 0, &shieldColor, shieldBgColor(it.key().type())); + poiFont(), 0, &shieldColor, 0, shieldBgColor(it.key().type())); bool valid = false; while (true) { @@ -457,7 +459,7 @@ void RasterTile::processPoints(QList &textItems) const QFont *fnt = poi ? poiFont(style.textFontSize()) : font(style.textFontSize()); const QColor *color = style.textColor().isValid() - ? &style.textColor() : 0; + ? &style.textColor() : &textColor; if ((!label || !fnt) && !img) continue; @@ -471,7 +473,7 @@ void RasterTile::processPoints(QList &textItems) } TextPointItem *item = new TextPointItem(QPoint(point.coordinates.lon(), - point.coordinates.lat()), label, fnt, img, color); + point.coordinates.lat()), label, fnt, img, color, &haloColor); if (item->isValid() && !item->collides(textItems)) textItems.append(item); else diff --git a/src/map/mapsforge/rastertile.cpp b/src/map/mapsforge/rastertile.cpp index 58fa02d2..578c9c08 100644 --- a/src/map/mapsforge/rastertile.cpp +++ b/src/map/mapsforge/rastertile.cpp @@ -79,6 +79,13 @@ static QString *pathLabel(const Style::TextRender *ri, MapData::Path &path, return 0; } +static const QColor *haloColor(const Style::TextRender *ti) +{ + return (ti->strokeColor() != ti->fillColor() && ti->strokeWidth() > 0) + ? &ti->strokeColor() : 0; +} + + void RasterTile::processPointLabels(QList &textItems) { const Style &s = style(); @@ -115,9 +122,11 @@ void RasterTile::processPointLabels(QList &textItems) const QImage *img = si ? &si->img() : 0; const QFont *font = ti ? &ti->font() : 0; const QColor *color = ti ? &ti->fillColor() : 0; + const QColor *hColor = ti ? haloColor(ti) : 0; TextPointItem *item = new TextPointItem( - ll2xy(point.coordinates).toPoint(), label, font, img, color, 0, false); + ll2xy(point.coordinates).toPoint(), label, font, img, color, + hColor, 0, false); if (item->isValid() && !item->collides(textItems)) textItems.append(item); else @@ -125,8 +134,7 @@ void RasterTile::processPointLabels(QList &textItems) } } -void RasterTile::processAreaLabels(const QRect &tileRect, - QList &textItems) +void RasterTile::processAreaLabels(QList &textItems) { const Style &s = style(); QList labels(s.areaLabels(_zoom)); @@ -165,12 +173,13 @@ void RasterTile::processAreaLabels(const QRect &tileRect, const QImage *img = si ? &si->img() : 0; const QFont *font = ti ? &ti->font() : 0; const QColor *color = ti ? &ti->fillColor() : 0; + const QColor *hColor = ti ? haloColor(ti) : 0; QPointF pos = path.labelPos.isNull() ? centroid(path.path) : ll2xy(path.labelPos); TextPointItem *item = new TextPointItem(pos.toPoint(), label, font, img, - color, 0, false); - if (item->isValid() && tileRect.contains(item->boundingRect().toRect()) + color, hColor, 0, false); + if (item->isValid() && _rect.contains(item->boundingRect().toRect()) && !item->collides(textItems)) textItems.append(item); else @@ -178,8 +187,7 @@ void RasterTile::processAreaLabels(const QRect &tileRect, } } -void RasterTile::processLineLabels(const QRect &tileRect, - QList &textItems) +void RasterTile::processLineLabels(QList &textItems) { const Style &s = style(); QList instructions(s.pathLabels(_zoom)); @@ -202,8 +210,8 @@ void RasterTile::processLineLabels(const QRect &tileRect, if (limit && set.contains(path.label)) continue; - TextPathItem *item = new TextPathItem(path.path, label, tileRect, - &ri->font(), &ri->fillColor(), &ri->strokeColor()); + TextPathItem *item = new TextPathItem(path.path, label, _rect, + &ri->font(), &ri->fillColor(), haloColor(ri)); if (item->isValid() && !item->collides(textItems)) { textItems.append(item); if (limit) @@ -271,11 +279,12 @@ void RasterTile::drawPaths(QPainter *painter) const Style::PathRender *lri = 0; QPixmap layer(_pixmap.size()); + layer.setDevicePixelRatio(_ratio); layer.fill(Qt::transparent); QPainter lp(&layer); lp.setRenderHint(QPainter::Antialiasing); - lp.translate(-_xy.x(), -_xy.y()); + lp.translate(-_rect.x(), -_rect.y()); lp.setCompositionMode(QPainter::CompositionMode_Source); for (int i = 0; i < instructions.size(); i++) { @@ -283,8 +292,8 @@ void RasterTile::drawPaths(QPainter *painter) const Style::PathRender *ri = is.render(); if (lri && lri != ri) { - painter->drawPixmap(_xy, layer); - lp.fillRect(QRect(_xy, _pixmap.size()), Qt::transparent); + painter->drawPixmap(_rect.topLeft(), layer); + lp.fillRect(QRect(_rect.topLeft(), _pixmap.size()), Qt::transparent); } if (!is.path()->path.elementCount()) @@ -304,7 +313,7 @@ void RasterTile::drawPaths(QPainter *painter) } if (lri) - painter->drawPixmap(_xy, layer); + painter->drawPixmap(_rect.topLeft(), layer); } void RasterTile::render() @@ -312,24 +321,24 @@ void RasterTile::render() std::sort(_points.begin(), _points.end()); QList textItems; - QRect tileRect(_xy, _pixmap.size()); _pixmap.fill(Qt::transparent); QPainter painter(&_pixmap); painter.setRenderHint(QPainter::Antialiasing); - painter.translate(-_xy.x(), -_xy.y()); + painter.scale(_ratio, _ratio); + painter.translate(-_rect.x(), -_rect.y()); drawPaths(&painter); processPointLabels(textItems); - processAreaLabels(tileRect, textItems); - processLineLabels(tileRect, textItems); + processAreaLabels(textItems); + processLineLabels(textItems); drawTextItems(&painter, textItems); //painter.setPen(Qt::red); //painter.setBrush(Qt::NoBrush); - //painter.drawRect(QRect(_xy, _pixmap.size())); + //painter.drawRect(QRect(_rect.topLeft(), _pixmap.size())); qDeleteAll(textItems); } diff --git a/src/map/mapsforge/rastertile.h b/src/map/mapsforge/rastertile.h index e7feba84..b41603a0 100644 --- a/src/map/mapsforge/rastertile.h +++ b/src/map/mapsforge/rastertile.h @@ -16,13 +16,14 @@ class RasterTile { public: RasterTile(const Projection &proj, const Transform &transform, int zoom, - const QRect &rect, const QString &key, const QList &paths, - const QList &points) - : _proj(proj), _transform(transform), _zoom(zoom), _xy(rect.topLeft()), - _key(key), _pixmap(rect.size()), _paths(paths), _points(points) {} + const QRect &rect, qreal ratio, const QString &key, + const QList &paths, const QList &points) + : _proj(proj), _transform(transform), _zoom(zoom), _rect(rect), + _ratio(ratio), _key(key), _pixmap(rect.width() * ratio, + rect.height() * ratio), _paths(paths), _points(points) {} const QString &key() const {return _key;} - const QPoint &xy() const {return _xy;} + QPoint xy() const {return _rect.topLeft();} const QPixmap &pixmap() const {return _pixmap;} void render(); @@ -72,8 +73,8 @@ private: QPointF ll2xy(const Coordinates &c) const {return _transform.proj2img(_proj.ll2xy(c));} void processPointLabels(QList &textItems); - void processAreaLabels(const QRect &tileRect, QList &textItems); - void processLineLabels(const QRect &tileRect, QList &textItems); + void processAreaLabels(QList &textItems); + void processLineLabels(QList &textItems); QPainterPath painterPath(const Polygon &polygon) const; void drawTextItems(QPainter *painter, const QList &textItems); void drawPaths(QPainter *painter); @@ -81,7 +82,8 @@ private: Projection _proj; Transform _transform; int _zoom; - QPoint _xy; + QRect _rect; + qreal _ratio; QString _key; QPixmap _pixmap; QList _paths; diff --git a/src/map/mapsforge/style.cpp b/src/map/mapsforge/style.cpp index 345e0a83..e1134cdc 100644 --- a/src/map/mapsforge/style.cpp +++ b/src/map/mapsforge/style.cpp @@ -159,6 +159,8 @@ void Style::text(QXmlStreamReader &reader, const Rule &rule, ri._fillColor = QColor(attr.value("fill").toString()); if (attr.hasAttribute("stroke")) ri._strokeColor = QColor(attr.value("stroke").toString()); + if (attr.hasAttribute("stroke-width")) + ri._strokeWidth = attr.value("stroke-width").toFloat(); if (attr.hasAttribute("font-size")) fontSize = attr.value("font-size").toInt(); if (attr.hasAttribute("font-style")) { diff --git a/src/map/mapsforge/style.h b/src/map/mapsforge/style.h index 9fe05146..e9608d4a 100644 --- a/src/map/mapsforge/style.h +++ b/src/map/mapsforge/style.h @@ -187,17 +187,20 @@ public: { public: TextRender(const Rule &rule) - : Render(rule), _fillColor(Qt::black), _strokeColor(Qt::white) {} + : Render(rule), _fillColor(Qt::black), _strokeColor(Qt::black), + _strokeWidth(0) {} const QFont &font() const {return _font;} const QColor &fillColor() const {return _fillColor;} const QColor &strokeColor() const {return _strokeColor;} + qreal strokeWidth() const {return _strokeWidth;} const QByteArray &key() const {return _key;} private: friend class Style; QColor _fillColor, _strokeColor; + qreal _strokeWidth; QFont _font; QByteArray _key; }; diff --git a/src/map/mapsforgemap.cpp b/src/map/mapsforgemap.cpp index 1c3209da..a7c0e20b 100644 --- a/src/map/mapsforgemap.cpp +++ b/src/map/mapsforgemap.cpp @@ -21,7 +21,8 @@ static int log2i(unsigned val) } MapsforgeMap::MapsforgeMap(const QString &fileName, QObject *parent) - : Map(fileName, parent), _data(fileName), _zoom(0), _projection(PCS::pcs(3857)) + : Map(fileName, parent), _data(fileName), _zoom(0), + _projection(PCS::pcs(3857)), _tileRatio(1.0) { _zoom = _data.zooms().min(); updateTransform(); @@ -147,9 +148,10 @@ void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags) if (isRunning(key)) continue; - if (QPixmapCache::find(key, &pm)) + if (QPixmapCache::find(key, &pm)) { + pm.setDevicePixelRatio(_tileRatio); painter->drawPixmap(ttl, pm); - else { + } else { QList paths; QList points; @@ -170,11 +172,12 @@ void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags) pointRect &= _bounds; RectD pointRectD(_transform.img2proj(pointRect.topLeft()), _transform.img2proj(pointRect.bottomRight())); - _data.points(pointRectD.toRectC(_projection, 20), _zoom, &points); + _data.points(pointRectD.toRectC(_projection, 20), _zoom, + &points); tiles.append(RasterTile(_projection, _transform, _zoom, - QRect(ttl, QSize(_data.tileSize(), _data.tileSize())), key, - paths, points)); + QRect(ttl, QSize(_data.tileSize(), _data.tileSize())), + _tileRatio, key, paths, points)); } } } @@ -188,6 +191,13 @@ void MapsforgeMap::draw(QPainter *painter, const QRectF &rect, Flags flags) } } +void MapsforgeMap::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) +{ + Q_UNUSED(mapRatio); + + _tileRatio = deviceRatio; +} + void MapsforgeMap::setOutputProjection(const Projection &projection) { if (projection == _projection) diff --git a/src/map/mapsforgemap.h b/src/map/mapsforgemap.h index 8b77587e..62fa037b 100644 --- a/src/map/mapsforgemap.h +++ b/src/map/mapsforgemap.h @@ -72,6 +72,7 @@ public: void load(); void unload(); void setOutputProjection(const Projection &projection); + void setDevicePixelRatio(qreal deviceRatio, qreal mapRatio); QPointF ll2xy(const Coordinates &c) {return _transform.proj2img(_projection.ll2xy(c));} @@ -99,6 +100,7 @@ private: Projection _projection; Transform _transform; QRectF _bounds; + qreal _tileRatio; QSet _running; }; diff --git a/src/map/textpathitem.cpp b/src/map/textpathitem.cpp index 805a78c6..b6708106 100644 --- a/src/map/textpathitem.cpp +++ b/src/map/textpathitem.cpp @@ -238,7 +238,7 @@ static bool reverse(const QPainterPath &path) TextPathItem::TextPathItem(const QPolygonF &line, const QString *label, const QRect &tileRect, const QFont *font, const QColor *color) - : TextItem(label), _font(font), _color(color), _outlineColor(0) + : TextItem(label), _font(font), _color(color), _haloColor(0) { qreal cw = font->pixelSize() * 0.6; qreal textWidth = _text->size() * cw; @@ -259,8 +259,8 @@ TextPathItem::TextPathItem(const QPolygonF &line, const QString *label, TextPathItem::TextPathItem(const QPainterPath &line, const QString *label, const QRect &tileRect, const QFont *font, const QColor *color, - const QColor *outlineColor) : TextItem(label), _font(font), _color(color), - _outlineColor(outlineColor) + const QColor *haloColor) : TextItem(label), _font(font), _color(color), + _haloColor(haloColor) { qreal cw = font->pixelSize() * 0.6; qreal textWidth = _text->size() * cw; @@ -290,22 +290,23 @@ void TextPathItem::paint(QPainter *painter) const QTransform t = painter->transform(); painter->setFont(*_font); - painter->setPen(_outlineColor ? *_outlineColor : Qt::white); + painter->setPen(_haloColor ? *_haloColor : Qt::white); for (int i = 0; i < _text->size(); i++) { QPointF point = _path.pointAtPercent(percent); qreal angle = _path.angleAtPercent(percent); + QChar c = _text->at(i); painter->translate(point); painter->rotate(-angle); - painter->drawText(QPoint(-1, fm.descent() - 1), _text->at(i)); - painter->drawText(QPoint(1, fm.descent() + 1), _text->at(i)); - painter->drawText(QPoint(-1, fm.descent() + 1), _text->at(i)); - painter->drawText(QPoint(1, fm.descent() -1), _text->at(i)); - painter->drawText(QPoint(0, fm.descent() - 1), _text->at(i)); - painter->drawText(QPoint(0, fm.descent() + 1), _text->at(i)); - painter->drawText(QPoint(-1, fm.descent()), _text->at(i)); - painter->drawText(QPoint(1, fm.descent()), _text->at(i)); + painter->drawText(QPoint(-1, fm.descent() - 1), c); + painter->drawText(QPoint(1, fm.descent() + 1), c); + painter->drawText(QPoint(-1, fm.descent() + 1), c); + painter->drawText(QPoint(1, fm.descent() -1), c); + painter->drawText(QPoint(0, fm.descent() - 1), c); + painter->drawText(QPoint(0, fm.descent() + 1), c); + painter->drawText(QPoint(-1, fm.descent()), c); + painter->drawText(QPoint(1, fm.descent()), c); painter->setTransform(t); int width = fm.horizontalAdvance(_text->at(i)); diff --git a/src/map/textpathitem.h b/src/map/textpathitem.h index 82bb155b..277f7179 100644 --- a/src/map/textpathitem.h +++ b/src/map/textpathitem.h @@ -13,7 +13,7 @@ public: const QRect &tileRect, const QFont *font, const QColor *color); TextPathItem(const QPainterPath &line, const QString *label, const QRect &tileRect, const QFont *font, const QColor *color, - const QColor *outlineColor); + const QColor *haloColor); bool isValid() const {return !_path.isEmpty();} @@ -24,7 +24,7 @@ public: private: const QFont *_font; const QColor *_color; - const QColor *_outlineColor; + const QColor *_haloColor; QPainterPath _path; QRectF _rect; QPainterPath _shape; diff --git a/src/map/textpointitem.cpp b/src/map/textpointitem.cpp index 15137105..08a7f4ec 100644 --- a/src/map/textpointitem.cpp +++ b/src/map/textpointitem.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "textpointitem.h" @@ -17,8 +18,9 @@ static void expand(QRect &rect, int width) TextPointItem::TextPointItem(const QPoint &point, const QString *text, const QFont *font, const QImage *img, const QColor *color, - const QColor *bgColor, bool padding) : TextItem(font ? text : 0), - _font(font), _img(img), _color(color), _bgColor(bgColor) + const QColor *haloColor, const QColor *bgColor, bool padding) + : TextItem(font ? text : 0), _font(font), _img(img), _color(color), + _haloColor(haloColor), _bgColor(bgColor) { if (_text) { QFontMetrics fm(*_font); @@ -66,31 +68,31 @@ void TextPointItem::paint(QPainter *painter) const painter->setBrush(Qt::NoBrush); painter->setFont(*_font); painter->drawText(_textRect, FLAGS, *_text); + } else if (_haloColor) { + QStaticText st(*_text); + st.setTextFormat(Qt::PlainText); + st.setTextWidth(_textRect.width()); + st.setTextOption(QTextOption(Qt::AlignHCenter)); + st.setPerformanceHint(QStaticText::AggressiveCaching); + + painter->setPen(*_haloColor); + painter->setFont(*_font); + + painter->drawStaticText(_textRect.topLeft() + QPointF(-1, -1), st); + painter->drawStaticText(_textRect.topLeft() + QPointF(+1, +1), st); + painter->drawStaticText(_textRect.topLeft() + QPointF(-1, +1), st); + painter->drawStaticText(_textRect.topLeft() + QPointF(+1, -1), st); + painter->drawStaticText(_textRect.topLeft() + QPointF(0, -1), st); + painter->drawStaticText(_textRect.topLeft() + QPointF(0, +1), st); + painter->drawStaticText(_textRect.topLeft() + QPointF(-1, 0), st); + painter->drawStaticText(_textRect.topLeft() + QPointF(+1, 0), st); + + painter->setPen(*_color); + painter->drawStaticText(_textRect.topLeft(), st); } else { - QImage img(_textRect.size(), QImage::Format_ARGB32_Premultiplied); - img.fill(Qt::transparent); - QPainter ip(&img); - ip.setPen(Qt::white); - ip.setFont(*_font); - ip.drawText(img.rect(), FLAGS, *_text); - - painter->drawImage(_textRect.x() - 1, _textRect.y() - 1, img); - painter->drawImage(_textRect.x() + 1, _textRect.y() + 1, img); - painter->drawImage(_textRect.x() - 1, _textRect.y() + 1, img); - painter->drawImage(_textRect.x() + 1, _textRect.y() - 1, img); - painter->drawImage(_textRect.x(), _textRect.y() - 1, img); - painter->drawImage(_textRect.x(), _textRect.y() + 1, img); - painter->drawImage(_textRect.x() - 1, _textRect.y(), img); - painter->drawImage(_textRect.x() + 1, _textRect.y(), img); - - if (_color) { - painter->setFont(*_font); - painter->setPen(*_color); - painter->drawText(_textRect, FLAGS, *_text); - } else { - img.invertPixels(); - painter->drawImage(_textRect, img); - } + painter->setPen(*_color); + painter->setFont(*_font); + painter->drawText(_textRect, FLAGS, *_text); } } diff --git a/src/map/textpointitem.h b/src/map/textpointitem.h index 226a8150..eff6800a 100644 --- a/src/map/textpointitem.h +++ b/src/map/textpointitem.h @@ -16,8 +16,8 @@ class TextPointItem : public TextItem public: TextPointItem() : TextItem(0), _font(0), _img(0) {} TextPointItem(const QPoint &point, const QString *text, const QFont *font, - const QImage *img, const QColor *color, const QColor *bgColor = 0, - bool padding = true); + const QImage *img, const QColor *color, const QColor *haloColor, + const QColor *bgColor = 0, bool padding = true); bool isValid() const {return !_rect.isEmpty();} @@ -30,7 +30,7 @@ public: private: const QFont *_font; const QImage *_img; - const QColor *_color, *_bgColor; + const QColor *_color, *_haloColor, *_bgColor; QRect _rect, _textRect; QPainterPath _shape; };