1
0
mirror of https://github.com/tumic0/GPXSee.git synced 2024-11-24 03:35:53 +01:00

Render repeating "always-display" lineSymbols as bitmap lines

This commit is contained in:
Martin Tůma 2024-11-08 16:39:01 +01:00
parent 70ddd83154
commit 01fba7b742
5 changed files with 115 additions and 42 deletions

View File

@ -1,6 +1,7 @@
#include <QPainter>
#include <QImage>
#include <QtMath>
#include <QPainterPath>
#include "bitmapline.h"
@ -58,3 +59,23 @@ void BitmapLine::draw(QPainter *painter, const QVector<QPolygonF> &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();
}
}

View File

@ -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<QPolygonF> &lines,
const QImage &img);
void draw(QPainter *painter, const QPainterPath &line, const QImage &img);
}
#endif // BITMAPLINE_H

View File

@ -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,6 +422,13 @@ void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
if (!path->pp.elementCount())
path->pp = painterPath(path->path->poly, ri->curve());
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());
@ -428,6 +436,7 @@ void RasterTile::drawPaths(QPainter *painter, const QList<MapData::Path> &paths,
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);

View File

@ -3,6 +3,7 @@
#include <QUrl>
#include <QFileInfo>
#include <QImageReader>
#include <QPainter>
#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<QList<TextRender>*> &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<Symbol> &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,6 +513,43 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
return;
}
}
// 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;
}
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);
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) {
@ -519,9 +564,11 @@ void Style::symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
if (attr.hasAttribute("id"))
ri._id = attr.value("id").toString();
ri._img = image(file, width, height, percent, ratio);
list.append(ri);
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<QList<TextRender>*> list;
list.append(&_pathLabels);
text(reader, data, r, list);
} else if (reader.name() == QLatin1String("caption")) {
QList<QList<TextRender>*> 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();
}

View File

@ -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<QString> &cats);
void text(QXmlStreamReader &reader, const MapData &data, const Rule &rule,
QList<QList<TextRender> *> &lists);
bool line);
void symbol(QXmlStreamReader &reader, const QString &dir, qreal ratio,
const Rule &rule, QList<Symbol> &list);
const Rule &rule, bool line);
};
}