Added support for HiDPI sprites

This commit is contained in:
Martin Tůma 2018-12-04 00:09:23 +01:00
parent 0d868f2f2a
commit 08509f0930
12 changed files with 1779 additions and 39 deletions

View File

@ -3,5 +3,7 @@
<file>style/style.json</file>
<file>style/sprite.json</file>
<file>style/sprite.png</file>
<file>style/sprite@2x.json</file>
<file>style/sprite@2x.png</file>
</qresource>
</RCC>

View File

@ -156,18 +156,20 @@ static void drawFeature(const Feature &feature, Style *style, int styleLayer,
}
static void drawLayer(const Layer &layer, Style *style, int styleLayer,
Tile &tile, const QPointF &scale)
Tile &tile)
{
if (layer.data()->version() > 2)
return;
QSizeF factor(tile.size().width() / scale.x() / (qreal)layer.data()->extent(),
tile.size().height() / scale.y() / (qreal)layer.data()->extent());
QSizeF factor(tile.size().width() / tile.scale().x() /
(qreal)layer.data()->extent(), tile.size().height() / tile.scale().y()
/ (qreal)layer.data()->extent());
tile.painter().save();
style->setupLayer(tile, styleLayer);
for (int i = 0; i < layer.features().size(); i++)
drawFeature(layer.features().at(i), style, styleLayer, factor, tile);
tile.painter().restore();
}
bool PBF::render(const QByteArray &data, int zoom, Style *style,
@ -192,8 +194,6 @@ bool PBF::render(const QByteArray &data, int zoom, Style *style,
layers.insert(name, Layer(&layer));
}
t.painter().save();
// Process source layers in order of style layers
for (int i = 0; i < style->sourceLayers().size(); i++) {
QMap<QString, Layer>::const_iterator it = layers.find(
@ -201,10 +201,9 @@ bool PBF::render(const QByteArray &data, int zoom, Style *style,
if (it == layers.constEnd())
continue;
drawLayer(*it, style, i, t, scale);
drawLayer(*it, style, i, t);
}
t.painter().restore();
t.text().render(&t.painter());
return true;

View File

@ -9,12 +9,19 @@
Loading the sprites atlas image must be deferred until all image plugins
are loaded, otherwise reading the image will cause a deadlock!
*/
static QImage &atlas(const QString &fileName)
static const QImage *atlas(const QString &fileName)
{
static QImage *img = new QImage(fileName);
return *img;
return img;
}
static const QImage *atlas2x(const QString &fileName)
{
static QImage *img = new QImage(fileName);
return img;
}
Sprites::Sprite::Sprite(const QJsonObject &json)
{
int x, y, width, height;
@ -42,7 +49,17 @@ Sprites::Sprite::Sprite(const QJsonObject &json)
bool Sprites::load(const QString &jsonFile, const QString &imageFile)
{
_imageFile = imageFile;
return load(jsonFile, _sprites);
}
bool Sprites::load2x(const QString &jsonFile, const QString &imageFile)
{
_image2xFile = imageFile;
return load(jsonFile, _sprites2x);
}
bool Sprites::load(const QString &jsonFile, QMap<QString, Sprite> &map)
{
QFile file(jsonFile);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qCritical() << jsonFile << ": error opening file";
@ -64,7 +81,7 @@ bool Sprites::load(const QString &jsonFile, const QString &imageFile)
if (val.isObject()) {
Sprite s(val.toObject());
if (s.rect().isValid())
_sprites.insert(it.key(), s);
map.insert(it.key(), s);
else
qWarning() << it.key() << ": invalid sprite definition";
} else
@ -74,20 +91,36 @@ bool Sprites::load(const QString &jsonFile, const QString &imageFile)
return true;
}
QImage Sprites::icon(const QString &name) const
QImage Sprites::icon(const QString &name, bool hidpi) const
{
if (_imageFile.isNull())
return QImage();
const QImage &img = atlas(_imageFile);
if (img.isNull())
qreal ratio;
const QImage *img;
const QMap<QString, Sprite> *map;
if (hidpi && !_image2xFile.isNull()) {
img = atlas2x(_image2xFile);
map = &_sprites2x;
ratio = 2;
} else if (!_imageFile.isNull()) {
img = atlas(_imageFile);
map = &_sprites;
ratio = 1;
} else
return QImage();
QMap<QString, Sprite>::const_iterator it = _sprites.find(name);
if (it == _sprites.constEnd())
if (img->isNull())
return QImage();
if (!img.rect().contains(it->rect()))
QMap<QString, Sprite>::const_iterator it = map->find(name);
if (it == map->constEnd())
return QImage();
return img.copy(it->rect());
if (!img->rect().contains(it->rect()))
return QImage();
QImage ret(img->copy(it->rect()));
ret.setDevicePixelRatio(ratio);
return ret;
}

View File

@ -9,8 +9,9 @@ class Sprites
{
public:
bool load(const QString &jsonFile, const QString &imageFile);
bool load2x(const QString &jsonFile, const QString &imageFile);
QImage icon(const QString &name) const;
QImage icon(const QString &name, bool hidpi) const;
private:
class Sprite {
@ -22,8 +23,10 @@ private:
QRect _rect;
};
QMap<QString, Sprite> _sprites;
QString _imageFile;
bool load(const QString &jsonFile, QMap<QString, Sprite> &map);
QMap<QString, Sprite> _sprites, _sprites2x;
QString _imageFile, _image2xFile;
};
#endif // SPRITES_H

View File

@ -223,7 +223,8 @@ QPen Style::Layer::Paint::pen(Type type, int zoom) const
return pen;
}
QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites) const
QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites,
bool hidpi) const
{
QColor color;
QBrush brush(Qt::NoBrush);
@ -236,7 +237,7 @@ QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites) c
brush = QBrush(color);
pattern = _fillPattern.value(zoom);
if (!pattern.isNull())
brush.setTextureImage(sprites.icon(pattern));
brush.setTextureImage(sprites.icon(pattern, hidpi));
break;
case Background:
color = _backgroundColor.value(zoom);
@ -244,7 +245,7 @@ QBrush Style::Layer::Paint::brush(Type type, int zoom, const Sprites &sprites) c
brush = QBrush(color);
pattern = _fillPattern.value(zoom);
if (!pattern.isNull())
brush.setTextureImage(sprites.icon(pattern));
brush.setTextureImage(sprites.icon(pattern, hidpi));
break;
default:
break;
@ -448,7 +449,8 @@ void Style::Layer::setPathPainter(Tile &tile, const Sprites &sprites) const
pen.setJoinStyle(_layout.lineJoin(zoom));
pen.setCapStyle(_layout.lineCap(zoom));
QBrush brush(_paint.brush(_type, zoom, sprites));
bool hidpi = qMax(tile.scale().x(), tile.scale().y()) > 1.0 ? true : false;
QBrush brush(_paint.brush(_type, zoom, sprites, hidpi));
p.setRenderHint(QPainter::Antialiasing, _paint.antialias(_type, zoom));
p.setPen(pen);
@ -478,7 +480,8 @@ void Style::Layer::addSymbol(Tile &tile, const QPainterPath &path,
return;
QString icon = _layout.icon(tile.zoom(), tags);
tile.text().addLabel(text, sprites.icon(icon), path);
bool hidpi = qMax(tile.scale().x(), tile.scale().y()) > 1.0 ? true : false;
tile.text().addLabel(text, sprites.icon(icon, hidpi), path);
}
bool Style::load(const QString &fileName)
@ -509,7 +512,9 @@ bool Style::load(const QString &fileName)
for (int i = 0; i < _layers.size(); i++)
_sourceLayers.append(_layers.at(i).sourceLayer());
QDir styleDir = QFileInfo(fileName).absoluteDir();
QString spritesJSON(styleDir.filePath("sprite.json"));
if (QFileInfo::exists(spritesJSON)) {
QString spritesImg(styleDir.filePath("sprite.png"));
@ -519,6 +524,15 @@ bool Style::load(const QString &fileName)
qCritical() << spritesImg << ": no such file";
}
QString sprites2xJSON(styleDir.filePath("sprite@2x.json"));
if (QFileInfo::exists(sprites2xJSON)) {
QString sprites2xImg(styleDir.filePath("sprite@2x.png"));
if (QFileInfo::exists(sprites2xImg))
_sprites.load2x(sprites2xJSON, sprites2xImg);
else
qCritical() << sprites2xImg << ": no such file";
}
return true;
}

View File

@ -136,7 +136,8 @@ private:
Paint(const QJsonObject &json);
QPen pen(Layer::Type type, int zoom) const;
QBrush brush(Layer::Type type, int zoom, const Sprites &sprites) const;
QBrush brush(Layer::Type type, int zoom, const Sprites &sprites,
bool hidpi) const;
qreal opacity(Layer::Type type, int zoom) const;
bool antialias(Layer::Type type, int zoom) const;
QString fillPattern(int zoom) const

View File

@ -1,4 +1,4 @@
#include <QFontMetrics>
#include <QFontMetrics>
#include <QPainter>
#include "text.h"
#include "textpointitem.h"

View File

@ -34,8 +34,7 @@ public:
Auto
};
Text(const QSize &size)
: _sceneRect(QRectF(QPointF(0, 0), size)) {}
Text(const QSize &size) : _sceneRect(QRectF(QPointF(0, 0), size)) {}
~Text();
void setFont(const QFont &font) {_font = font;}

View File

@ -71,7 +71,8 @@ QRectF TextPointItem::fuzzyBoundingRect(const QString &str,
QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const
{
QRectF iconRect = _icon.isNull() ? QRectF() : _icon.rect();
QRectF iconRect = _icon.isNull() ? QRectF()
: QRectF(QPointF(0, 0), QSizeF(_icon.size()) / _icon.devicePixelRatioF());
QRectF textRect = brf(text(), font(), _maxWidth);
switch (_anchor) {
@ -108,7 +109,8 @@ TextPointItem::TextPointItem(const QString &text, const QPointF &pos,
_boundingRect = computeTextRect(fuzzyBoundingRect);
if (!_icon.isNull()) {
QRectF iconRect(_icon.rect());
QRectF iconRect(QPointF(0, 0), QSizeF(_icon.size())
/ _icon.devicePixelRatioF());
iconRect.moveCenter(pos);
_boundingRect |= iconRect;
}
@ -125,8 +127,9 @@ void TextPointItem::paint(QPainter *painter) const
if (!_icon.isNull()) {
textRect = computeTextRect(exactBoundingRect);
painter->drawImage(_pos - QPointF(_icon.width() / 2,
_icon.height() / 2), _icon);
painter->drawImage(_pos - QPointF(_icon.width()
/ _icon.devicePixelRatioF() / 2, _icon.height()
/ _icon.devicePixelRatioF() / 2), _icon);
} else
textRect = computeTextRect(fuzzyBoundingRect);

View File

@ -8,18 +8,22 @@
class Tile {
public:
Tile(QImage *img, int zoom, const QPointF &scale)
: _zoom(zoom), _size(img->size()), _text(QSize(img->size().width()
/ scale.x(), img->size().height() / scale.y())), _painter(img)
: _zoom(zoom), _size(img->size()), _scale(scale),
_text(QSize(img->size().width() / scale.x(),
img->size().height() / scale.y())), _painter(img)
{_painter.scale(scale.x(), scale.y());}
int zoom() const {return _zoom;}
QSize size() const {return _size;}
const QSize &size() const {return _size;}
const QPointF &scale() const {return _scale;}
Text &text() {return _text;}
QPainter &painter() {return _painter;}
private:
int _zoom;
QSize _size;
QPointF _scale;
Text _text;
QPainter _painter;
};

1682
style/sprite@2x.json Normal file

File diff suppressed because it is too large Load Diff

BIN
style/sprite@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB