diff --git a/src/map/IMG/rastertile.cpp b/src/map/IMG/rastertile.cpp index 42896aa4..a382c28d 100644 --- a/src/map/IMG/rastertile.cpp +++ b/src/map/IMG/rastertile.cpp @@ -490,5 +490,5 @@ void RasterTile::render() //painter.setPen(Qt::red); //painter.setRenderHint(QPainter::Antialiasing, false); - //painter.drawRect(QRect(_xy, _pixmap.size())); + //painter.drawRect(_rect); } diff --git a/src/map/mapsforge/rastertile.cpp b/src/map/mapsforge/rastertile.cpp index 7b0f0b22..38d60803 100644 --- a/src/map/mapsforge/rastertile.cpp +++ b/src/map/mapsforge/rastertile.cpp @@ -207,33 +207,58 @@ void RasterTile::processAreaLabels(const QVector &paths, void RasterTile::processLineLabels(const QVector &paths, QList &textItems) const { - QList instructions(_style->pathLabels(_zoom)); + QList labels(_style->pathLabels(_zoom)); + QList symbols(_style->lineSymbols(_zoom)); QSet set; - for (int i = 0; i < instructions.size(); i++) { - const Style::TextRender *ri = instructions.at(i); + for (int i = 0; i < paths.size(); i++) { + const PainterPath &path = paths.at(i); + const Style::TextRender *ti = 0; + const Style::Symbol *si = 0; + const QByteArray *lbl = 0; + bool limit = false; - for (int i = 0; i < paths.size(); i++) { - const PainterPath &path = paths.at(i); - const QByteArray *lbl = label(ri->key(), path.path->tags); + if (path.path->closed) + continue; - if (!lbl) - continue; - if (!ri->rule().match(path.path->closed, path.path->tags)) - continue; - bool limit = (ri->key() == ID_ELE || ri->key() == ID_REF); + for (int j = 0; j < labels.size(); j++) { + const Style::TextRender *ri = labels.at(j); + if (ri->rule().match(path.path->closed, path.path->tags)) { + if ((lbl = label(ri->key(), path.path->tags))) + ti = ri; + break; + } + } + + for (int j = 0; j < symbols.size(); j++) { + const Style::Symbol *ri = symbols.at(j); + if (ri->rule().match(path.path->tags)) { + si = ri; + break; + } + } + + if (!ti && !si) + continue; + if (ti) { + limit = (ti->key() == ID_ELE || ti->key() == ID_REF); if (limit && set.contains(*lbl)) continue; - - PathItem *item = new PathItem(path.pp, lbl, _rect, &ri->font(), - &ri->fillColor(), haloColor(ri)); - if (item->isValid() && !item->collides(textItems)) { - textItems.append(item); - if (limit) - set.insert(*lbl); - } else - delete item; } + + 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; + + PathItem *item = new PathItem(path.pp, lbl, img, _rect, font, color, + hColor); + if (item->isValid() && !item->collides(textItems)) { + textItems.append(item); + if (limit) + set.insert(*lbl); + } else + delete item; } } @@ -409,6 +434,7 @@ void RasterTile::render() QPainter painter(&_pixmap); painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.translate(-_rect.x(), -_rect.y()); drawPaths(&painter, paths, points, renderPaths); @@ -420,7 +446,8 @@ void RasterTile::render() //painter.setPen(Qt::red); //painter.setBrush(Qt::NoBrush); - //painter.drawRect(QRect(_rect.topLeft(), _pixmap.size())); + //painter.setRenderHint(QPainter::Antialiasing, false); + //painter.drawRect(_rect); qDeleteAll(textItems); diff --git a/src/map/mapsforge/rastertile.h b/src/map/mapsforge/rastertile.h index e3f7e0d5..d8136731 100644 --- a/src/map/mapsforge/rastertile.h +++ b/src/map/mapsforge/rastertile.h @@ -137,9 +137,10 @@ private: { public: PathItem(const QPainterPath &line, const QByteArray *label, - const QRect &tileRect, const QFont *font, const QColor *color, - const QColor *haloColor) : TextPathItem(line, - label ? new QString(*label) : 0, tileRect, font, color, haloColor) {} + const QImage *img, const QRect &tileRect, const QFont *font, + const QColor *color, const QColor *haloColor) : TextPathItem(line, + label ? new QString(*label) : 0, tileRect, font, color, haloColor, + img) {} ~PathItem() {delete _text;} }; diff --git a/src/map/mapsforge/style.cpp b/src/map/mapsforge/style.cpp index 807fc740..a159ec2a 100644 --- a/src/map/mapsforge/style.cpp +++ b/src/map/mapsforge/style.cpp @@ -419,7 +419,7 @@ void Style::text(QXmlStreamReader &reader, const MapData &data, const Rule &rule } void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio, - const Rule &rule) + const Rule &rule, QList &list) { Symbol ri(rule); const QXmlStreamAttributes &attr = reader.attributes(); @@ -457,7 +457,7 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio, ri._img = image(file, width, height, ratio); - _symbols.append(ri); + list.append(ri); reader.skipCurrentElement(); } @@ -529,7 +529,9 @@ void Style::rule(QXmlStreamReader &reader, const QString &dir, text(reader, data, r, list); } else if (reader.name() == QLatin1String("symbol")) - symbol(reader, dir, ratio, r); + symbol(reader, dir, ratio, r, _symbols); + else if (reader.name() == QLatin1String("lineSymbol")) + symbol(reader, dir, ratio, r, _lineSymbols); else reader.skipCurrentElement(); } @@ -716,7 +718,7 @@ QList Style::pointSymbols(int zoom) const for (int i = 0; i < _symbols.size(); i++) { const Symbol &symbol = _symbols.at(i); - const Rule & rule = symbol.rule(); + const Rule &rule = symbol.rule(); if (rule._zooms.contains(zoom) && (rule._type == Rule::AnyType || rule._type == Rule::NodeType)) list.append(&symbol); @@ -725,6 +727,19 @@ QList Style::pointSymbols(int zoom) const return list; } +QList Style::lineSymbols(int zoom) const +{ + QList list; + + for (int i = 0; i < _lineSymbols.size(); i++) { + const Symbol &symbol = _lineSymbols.at(i); + if (symbol.rule()._zooms.contains(zoom)) + list.append(&symbol); + } + + return list; +} + QList Style::areaSymbols(int zoom) const { QList list; diff --git a/src/map/mapsforge/style.h b/src/map/mapsforge/style.h index e2507e94..e004fa49 100644 --- a/src/map/mapsforge/style.h +++ b/src/map/mapsforge/style.h @@ -236,6 +236,7 @@ public: QList areaLabels(int zoom) const; QList pointSymbols(int zoom) const; QList areaSymbols(int zoom) const; + QList lineSymbols(int zoom) const; private: class Menu { @@ -282,6 +283,7 @@ private: QList _circles; QList _pathLabels, _pointLabels, _areaLabels; QList _symbols; + QList _lineSymbols; bool loadXml(const QString &path, const MapData &data, qreal ratio); void rendertheme(QXmlStreamReader &reader, const QString &dir, @@ -298,7 +300,7 @@ private: void text(QXmlStreamReader &reader, const MapData &data, const Rule &rule, QList *> &lists); void symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio, - const Rule &rule); + const Rule &rule, QList &list); }; } diff --git a/src/map/textpathitem.cpp b/src/map/textpathitem.cpp index ed65ec69..f377bd4e 100644 --- a/src/map/textpathitem.cpp +++ b/src/map/textpathitem.cpp @@ -2,8 +2,9 @@ #include #include "textpathitem.h" - +#define CHAR_RATIO 0.55 #define MAX_TEXT_ANGLE 30 +#define PADDING 2 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) #define INTERSECTS intersect @@ -12,6 +13,22 @@ #endif // QT 5.15 +static void swap(const QLineF &line, QPointF *p1, QPointF *p2) +{ + + QPointF lp1(line.p1()); + QPointF lp2(line.p2()); + + if ((lp1.rx() < lp2.rx() && p1->rx() > p2->rx()) + || (lp1.ry() < lp2.ry() && p1->ry() > p2->ry()) + || (lp1.rx() > lp2.rx() && p1->rx() < p2->rx()) + || (lp1.ry() > lp2.ry() && p1->ry() < p2->ry())) { + QPointF tmp(*p2); + *p2 = *p1; + *p1 = tmp; + } +} + static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p) { if (line.INTERSECTS(QLineF(rect.topLeft(), rect.topRight()), p) @@ -40,20 +57,26 @@ static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1, p = p2; if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p) == QLineF::BoundedIntersection) { - if (p == p2) + if (p == p2) { + swap(line, p1, p2); return true; + } p = p2; } if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p) == QLineF::BoundedIntersection) { - if (p == p2) + if (p == p2) { + swap(line, p1, p2); return true; + } p = p2; } if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p) == QLineF::BoundedIntersection) { - if (p == p2) + if (p == p2) { + swap(line, p1, p2); return true; + } } Q_ASSERT(p != p2); @@ -197,6 +220,13 @@ static QList lineString(const QPainterPath &path, return lines; } +static bool reverse(const QPainterPath &path) +{ + QLineF l(path.elementAt(0), path.elementAt(1)); + qreal angle = l.angle(); + return (angle > 90 && angle < 270) ? true : false; +} + template static QPainterPath textPath(const T &path, qreal textWidth, qreal charWidth, const QRectF &tileRect) @@ -229,109 +259,128 @@ static QPainterPath textPath(const T &path, qreal textWidth, : QPainterPath(); } -static bool reverse(const QPainterPath &path) +template +void TextPathItem::init(const T &line, const QRect &tileRect) { - QLineF l(path.elementAt(0), path.elementAt(1)); - qreal angle = l.angle(); - return (angle > 90 && angle < 270) ? true : false; + qreal cw, mw, textWidth; + + if (_text && _img) { + cw = _font->pixelSize() * CHAR_RATIO; + mw = _font->pixelSize() / 2; + textWidth = _text->size() * cw + _img->width() + PADDING; + } else if (_text) { + cw = _font->pixelSize() * CHAR_RATIO; + mw = _font->pixelSize() / 2; + textWidth = _text->size() * cw; + } else { + cw = _img->width(); + mw = _img->height() / 2; + textWidth = _img->width(); + } + + _path = textPath(line, textWidth, cw, tileRect.adjusted(mw, mw, -mw, -mw)); + if (_path.isEmpty()) + return; + + if (reverse(_path)) { + _path = _path.toReversed(); + _reverse = true; + } + + QPainterPathStroker s; + s.setWidth(mw * 2); + s.setCapStyle(Qt::FlatCap); + _shape = s.createStroke(_path).simplified(); + _rect = _shape.boundingRect(); } TextPathItem::TextPathItem(const QPolygonF &line, const QString *label, const QRect &tileRect, const QFont *font, const QColor *color, - const QColor *haloColor) : TextItem(label), _font(font), _color(color), - _haloColor(haloColor) + const QColor *haloColor, const QImage *img) : TextItem(label), _font(font), + _color(color), _haloColor(haloColor), _img(img), _reverse(false) { - qreal cw = font->pixelSize() * 0.6; - qreal textWidth = _text->size() * cw; - qreal mw = font->pixelSize() / 2; - _path = textPath(line, textWidth, cw, tileRect.adjusted(mw, mw, -mw, -mw)); - if (_path.isEmpty()) - return; - - if (reverse(_path)) - _path = _path.toReversed(); - - QPainterPathStroker s; - s.setWidth(font->pixelSize()); - s.setCapStyle(Qt::FlatCap); - _shape = s.createStroke(_path).simplified(); - _rect = _shape.boundingRect(); + init(line, tileRect); } TextPathItem::TextPathItem(const QPainterPath &line, const QString *label, const QRect &tileRect, const QFont *font, const QColor *color, - const QColor *haloColor) : TextItem(label), _font(font), _color(color), - _haloColor(haloColor) + const QColor *haloColor, const QImage *img) : TextItem(label), _font(font), + _color(color), _haloColor(haloColor), _img(img), _reverse(false) { - qreal cw = font->pixelSize() * 0.6; - qreal textWidth = _text->size() * cw; - qreal mw = font->pixelSize() / 2; - _path = textPath(line, textWidth, cw, tileRect.adjusted(mw, mw, -mw, -mw)); - if (_path.isEmpty()) - return; - - if (reverse(_path)) - _path = _path.toReversed(); - - QPainterPathStroker s; - s.setWidth(font->pixelSize()); - s.setCapStyle(Qt::FlatCap); - _shape = s.createStroke(_path).simplified(); - _rect = _shape.boundingRect(); + init(line, tileRect); } void TextPathItem::paint(QPainter *painter) const { - QFontMetrics fm(*_font); - int textWidth = fm.boundingRect(*_text).width(); + if (_img) { + QSizeF s(_img->size() / _img->devicePixelRatioF()); - qreal factor = (textWidth) / qMax(_path.length(), (qreal)textWidth); - qreal percent = (1.0 - factor) / 2.0; + painter->save(); + painter->translate(QPointF(_path.elementAt(0).x, _path.elementAt(0).y)); + painter->rotate(360 - _path.angleAtPercent(0)); - QTransform t = painter->transform(); + if (_reverse) + painter->drawImage(QPointF(0, -s.height()/2), + _img->transformed(QTransform().rotate(180.0))); + else + painter->drawImage(QPointF(0, -s.height()/2), *_img); - painter->setFont(*_font); + painter->restore(); + } - if (_haloColor) { - painter->setPen(*_haloColor); + if (_text) { + QFontMetrics fm(*_font); + int textWidth = fm.boundingRect(*_text).width(); + int imgWidth = _img ? _img->width() + PADDING : 0; + qreal imgPercent = imgWidth / _path.length(); + qreal factor = textWidth / qMax(_path.length(), (qreal)(textWidth)); + qreal percent = ((1.0 - factor) + imgPercent) / 2.0; + QTransform t = painter->transform(); + painter->setFont(*_font); + + if (_haloColor) { + painter->setPen(*_haloColor); + + 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), 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)); + percent += ((qreal)width / (qreal)textWidth) * factor; + } + percent = ((1.0 - factor) + imgPercent) / 2.0; + } + + painter->setPen(_color ? *_color : Qt::black); 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), 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->drawText(QPoint(0, fm.descent()), _text->at(i)); painter->setTransform(t); int width = fm.horizontalAdvance(_text->at(i)); percent += ((qreal)width / (qreal)textWidth) * factor; } - percent = (1.0 - factor) / 2.0; - } - - painter->setPen(_color ? *_color : Qt::black); - for (int i = 0; i < _text->size(); i++) { - QPointF point = _path.pointAtPercent(percent); - qreal angle = _path.angleAtPercent(percent); - - painter->translate(point); - painter->rotate(-angle); - painter->drawText(QPoint(0, fm.descent()), _text->at(i)); - painter->setTransform(t); - - int width = fm.horizontalAdvance(_text->at(i)); - percent += ((qreal)width / (qreal)textWidth) * factor; } + //painter->setBrush(Qt::NoBrush); //painter->setPen(Qt::red); //painter->drawPath(_shape); } diff --git a/src/map/textpathitem.h b/src/map/textpathitem.h index 2c0c0bc2..edf9b02b 100644 --- a/src/map/textpathitem.h +++ b/src/map/textpathitem.h @@ -11,10 +11,10 @@ public: TextPathItem() : TextItem(0), _font(0), _color(0) {} TextPathItem(const QPolygonF &line, const QString *label, const QRect &tileRect, const QFont *font, const QColor *color, - const QColor *haloColor); + const QColor *haloColor, const QImage *img = 0); TextPathItem(const QPainterPath &line, const QString *label, const QRect &tileRect, const QFont *font, const QColor *color, - const QColor *haloColor); + const QColor *haloColor, const QImage *img = 0); bool isValid() const {return !_path.isEmpty();} @@ -23,12 +23,16 @@ public: void paint(QPainter *painter) const; private: + template void init(const T &line, const QRect &tileRect); + const QFont *_font; const QColor *_color; const QColor *_haloColor; + const QImage *_img; QPainterPath _path; QRectF _rect; QPainterPath _shape; + bool _reverse; }; #endif // TEXTPATHITEM_H