30 Commits
1.2 ... 2.1

Author SHA1 Message Date
d8df5cb595 Added style expressions not supported info 2019-10-22 09:14:06 +02:00
543bf63017 Merge branch 'master' of https://github.com/tumic0/QtPBFImagePlugin 2019-10-06 13:27:10 +02:00
7ea19d260c Fixed broken interpolation of grayscale colors 2019-10-06 13:26:30 +02:00
0185cd904f Added change log link 2019-10-05 22:06:37 +02:00
ec6302d524 Updated scaling sample to fit v2.0 2019-10-05 18:49:34 +02:00
0916d6330c Added tile size info 2019-10-05 18:47:39 +02:00
cc0c16cfd6 Explicitly set the text format (optimization) 2019-10-05 18:38:24 +02:00
8da172c0ff Adjusted icon text alignment 2019-10-05 13:21:36 +02:00
148f79eaef Some more bounding rect computation tweaking 2019-10-04 20:23:34 +02:00
1c42f104d1 Merge branch 'master' of https://github.com/tumic0/QtPBFImagePlugin 2019-10-04 18:57:19 +02:00
536cad2d61 Use QStaticText instead of own text caching 2019-10-04 18:56:45 +02:00
ff1c29e078 Use QStaticText instead of own text caching 2019-10-04 18:54:59 +02:00
e8e9139740 Some more text layout tweeking 2019-10-04 09:26:15 +02:00
a58962ab93 Yet another text rendering improvement/optimization 2019-10-03 22:39:05 +02:00
aacc42c25d Fixed broken down-scaling 2019-10-03 08:38:48 +02:00
3f6b5faa2c Switched to 512x512px tile size 2019-10-03 01:01:40 +02:00
5b6a222999 Added correct maxzoom default value 2019-10-02 23:19:49 +02:00
e00c8feab6 Use the "official" min/max zoom defaults in zoom check 2019-10-02 23:14:20 +02:00
82308857ba Added missing include 2019-10-02 23:12:53 +02:00
044e95e061 Some more text layout fiddeling 2019-10-02 22:57:11 +02:00
f5c15ece52 Improved/optimized text rendering 2019-10-01 22:11:45 +02:00
df4134c03a Alittle bit more sane API... 2019-05-04 12:50:16 +02:00
c28d10eafc Proper path-text halo drawing 2019-04-28 09:10:19 +02:00
76fb129651 Halo drawing performance improvement 2019-04-28 08:25:57 +02:00
3dfcf35e53 Added support for text halo 2019-04-27 22:52:18 +02:00
5ccb93bd59 Use the official IANA MIME-type and extension 2019-02-26 22:20:12 +01:00
c3df78a455 Added support for named (HTML) colors 2019-02-26 22:18:07 +01:00
f96b530fa3 Merge branch 'master' of https://github.com/tumic0/QtPBFImagePlugin 2019-02-22 08:47:29 +01:00
9fdfd35752 Removed obsolete code 2019-02-22 08:47:01 +01:00
20f2e7c7d2 Refer to PBF also as MVT 2019-01-03 01:44:57 +01:00
17 changed files with 209 additions and 104 deletions

View File

@ -3,8 +3,8 @@ Qt image plugin for displaying Mapbox vector tiles
## Description
QtPBFImagePlugin is a Qt image plugin that enables applications capable of
displaying raster MBTiles maps or raster XYZ online maps to also display PBF
vector tiles without (almost, see usage) any application modifications.
displaying raster MBTiles maps or raster XYZ online maps to also display
PBF(MVT) vector tiles without (almost, see usage) any application modifications.
Standard Mapbox GL Styles are used for styling the maps. Most relevant style
features used by [Maputnik](http://editor.openmaptiles.org) are supported.
@ -16,7 +16,8 @@ placed in the same directory as the style itself. A default fallback style
(OSM-Liberty) for OpenMapTiles is part of the plugin.
"Plain" PBF files as well as gzip compressed files (as used in MBTiles) are
supported by the plugin.
supported by the plugin. The tile size is (since version 2.0 of the plugin) 512px
to fit the styles and available data (OpenMapTiles, Mapbox tiles).
## Usage
Due to a major design flaw in the Mapbox vector tiles specification - the zoom
@ -33,10 +34,10 @@ The plugin supports vector scaling using QImageReader's setScaledSize() method,
so when used like in the following example:
```cpp
QImageReader reader(file, QString::number(zoom).toLatin1());
reader.setScaledSize(QSize(512, 512));
reader.setScaledSize(QSize(1024, 1024));
reader.read(&image);
```
you will get 512x512px tiles with a pixel ratio of 2 (= HiDPI tiles).
you will get 1024x1024px tiles with a pixel ratio of 2 (= HiDPI tiles).
## Build
### Requirements
@ -72,6 +73,10 @@ for most common distros available on OBS.
style are ignored.
* Text PBF features must have a unique id (OpenMapTiles >= v3.7) for the text layout
algorithm to work properly.
* Expressions not supported in the styles, only property functions are implemented.
## Changelog
[Changelog](https://build.opensuse.org/package/view_file/home:tumic:QtPBFImagePlugin/QtPBFImagePlugin/libqt5-qtpbfimageformat.changes)
## Status
A picture is worth a thousand words. Data and styles from https://openmaptiles.org.

View File

@ -1,4 +1,4 @@
{
"Keys": [ "pbf" ],
"MimeTypes": [ "image/pbf" ]
"Keys": [ "mvt" ],
"MimeTypes": [ "application/vnd.mapbox-vector-tile" ]
}

9
pkg/pbfplugin.xml Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/vnd.mapbox-vector-tile">
<comment>Mapbox Vector Tile</comment>
<sub-class-of type="application/octet-stream"/>
<generic-icon name="application/octet-stream"/>
<glob pattern="*.mvt"/>
</mime-type>
</mime-info>

View File

@ -13,9 +13,7 @@ QColor Color::fromJsonString(const QString &str)
{
QColor ret;
if (str.startsWith('#'))
return QColor(str);
else if (str.startsWith("rgb(")) {
if (str.startsWith("rgb(")) {
QStringList comp(str.mid(4, str.size() - 5).split(','));
if (comp.size() != 3)
return QColor();
@ -39,7 +37,8 @@ QColor Color::fromJsonString(const QString &str)
return QColor();
ret = QColor::fromHslF(comp.at(0).toFloat() / 360.0, pval(comp.at(1)),
pval(comp.at(2)), comp.at(3).toFloat());
}
} else
ret = QColor(str);
if (!ret.isValid())
qWarning() << str << ": invalid color";

View File

@ -40,7 +40,9 @@ static QColor interpolate(const QPair<qreal, QColor> &p0,
qreal p1h, p1s, p1l, p1a;
p1.second.getHslF(&p1h, &p1s, &p1l, &p1a);
return QColor::fromHslF(f(p0h, p1h, ratio), f(p0s, p1s, ratio),
/* Qt returns a hue of -1 for achromatic colors. We convert it to a hue of 1
using qAbs() to enable interpolation of grayscale colors */
return QColor::fromHslF(f(qAbs(p0h), qAbs(p1h), ratio), f(p0s, p1s, ratio),
f(p0l, p1l, ratio), f(p0a, p1a, ratio));
}

View File

@ -1,9 +1,5 @@
#include "pbf.h"
#define POLYGON vector_tile::Tile_GeomType_POLYGON
#define LINESTRING vector_tile::Tile_GeomType_LINESTRING
#define POINT vector_tile::Tile_GeomType_POINT
#define MOVE_TO 1
#define LINE_TO 2
#define CLOSE_PATH 7

View File

@ -8,7 +8,7 @@
#include "pbfhandler.h"
#define TILE_SIZE 256
#define TILE_SIZE 512
#define GZIP_MAGIC 0x1F8B0800
#define GZIP_MAGIC_MASK 0xFFFFFF00
@ -75,8 +75,8 @@ bool PBFHandler::read(QImage *image)
QSize size = _scaledSize.isValid()
? _scaledSize : QSize(TILE_SIZE, TILE_SIZE);
QPointF scale = _scaledSize.isValid()
? QPointF(_scaledSize.width() / TILE_SIZE, _scaledSize.height() / TILE_SIZE)
: QPointF(1.0, 1.0);
? QPointF((qreal)_scaledSize.width() / TILE_SIZE,
(qreal)_scaledSize.height() / TILE_SIZE) : QPointF(1.0, 1.0);
*image = QImage(size, QImage::Format_ARGB32_Premultiplied);
Tile tile(image, ok ? zoom : -1, scale);

View File

@ -21,7 +21,7 @@ QImageIOPlugin::Capabilities PBFPlugin::capabilities(QIODevice *device,
const QByteArray &format) const
{
if (device == 0)
return (format == "pbf") ? Capabilities(CanRead) : Capabilities();
return (format == "mvt") ? Capabilities(CanRead) : Capabilities();
else
return (device->isReadable() && PBFHandler::canRead(device))
? Capabilities(CanRead) : Capabilities();

View File

@ -230,6 +230,9 @@ Style::Layer::Paint::Paint(const QJsonObject &json)
// text
_textColor = FunctionC(json["text-color"]);
_textHaloColor = FunctionC(json["text-halo-color"], QColor());
_textHaloWidth = FunctionF(json["text-halo-width"]);
_textHaloBlur = FunctionF(json["text-halo-blur"]);
}
QPen Style::Layer::Paint::pen(Type type, int zoom) const
@ -434,7 +437,7 @@ Text::RotationAlignment Style::Layer::Layout::textRotationAlignment(int zoom)
}
Style::Layer::Layer(const QJsonObject &json)
: _type(Unknown), _minZoom(-1), _maxZoom(-1)
: _type(Unknown), _minZoom(0), _maxZoom(24)
{
// type
QString type(json["type"].toString());
@ -472,12 +475,8 @@ Style::Layer::Layer(const QJsonObject &json)
bool Style::Layer::match(int zoom, const PBF::Feature &feature) const
{
if (zoom >= 0) {
if (_minZoom > 0 && zoom < _minZoom)
return false;
if (_maxZoom > 0 && zoom > _maxZoom)
return false;
}
if (zoom >= 0 && (zoom < _minZoom || zoom > _maxZoom))
return false;
return _filter.match(feature);
}
@ -511,6 +510,7 @@ void Style::Layer::setTextProperties(Tile &tile) const
t.setFont(_layout.font(zoom));
t.setSymbolPlacement(_layout.symbolPlacement(zoom));
t.setRotationAlignment(_layout.textRotationAlignment(zoom));
t.setHalo(_paint.halo(zoom));
}
void Style::Layer::addSymbol(Tile &tile, const QPainterPath &path,
@ -597,7 +597,8 @@ void Style::setupLayer(Tile &tile, const Layer &layer) const
void Style::drawBackground(Tile &tile) const
{
QRectF rect(QPointF(0, 0), tile.size());
QRectF rect(QPointF(0, 0), QSizeF(tile.size().width() / tile.scale().x(),
tile.size().height() / tile.scale().y()));
QPainterPath path;
path.addRect(rect);

View File

@ -35,7 +35,7 @@ public:
private:
class Layer {
public:
Layer() : _type(Unknown), _minZoom(-1), _maxZoom(-1) {}
Layer() : _type(Unknown), _minZoom(0), _maxZoom(24) {}
Layer(const QJsonObject &json);
const QString &sourceLayer() const {return _sourceLayer;}
@ -137,9 +137,13 @@ private:
const;
qreal opacity(Layer::Type type, int zoom) const;
bool antialias(Layer::Type type, int zoom) const;
Text::Halo halo(int zoom) const
{return Text::Halo(_textHaloColor.value(zoom),
_textHaloWidth.value(zoom), _textHaloBlur.value(zoom));}
private:
FunctionC _textColor;
FunctionC _textHaloColor;
FunctionC _lineColor;
FunctionC _fillColor;
FunctionC _fillOutlineColor;
@ -147,6 +151,8 @@ private:
FunctionF _fillOpacity;
FunctionF _lineOpacity;
FunctionF _lineWidth;
FunctionF _textHaloWidth;
FunctionF _textHaloBlur;
FunctionB _fillAntialias;
QVector<qreal> _lineDasharray;
FunctionS _fillPattern;

View File

@ -1,6 +1,7 @@
#include <QFontMetrics>
#include <QPainter>
#include <QSet>
#include <QMap>
#include "text.h"
#include "textpointitem.h"
#include "textpathitem.h"
@ -31,22 +32,36 @@ void Text::addLabel(const QString &text, const QImage &icon,
{
TextItem *ti;
switch (_placement) {
case Line:
if (_alignment == Viewport)
if (_alignment == Viewport) {
QMap<qreal, int> map;
for (int j = 0; j < path.elementCount(); j++) {
QLineF l(path.elementAt(j), _sceneRect.center());
map.insert(l.length(), j);
}
QMap<qreal, int>::const_iterator jt = map.constBegin();
ti = new TextPointItem(text, path.elementAt(jt.value()), _font,
_maxWidth, _anchor, icon);
while (true) {
if (_sceneRect.contains(ti->boundingRect()))
break;
if (++jt == map.constEnd())
break;
static_cast<TextPointItem*>(ti)->setPos(path.elementAt(
jt.value()));
}
} else {
switch (_placement) {
case Line:
ti = new TextPathItem(text, path, _font, _maxAngle, _sceneRect);
break;
case LineCenter:
ti = new TextPointItem(text, path.pointAtPercent(0.5), _font,
_maxWidth, _anchor, icon);
break;
default:
ti = new TextPointItem(text, path.elementAt(0), _font,
_maxWidth, _anchor, icon);
else
ti = new TextPathItem(text, path, _font, _maxAngle, _sceneRect);
break;
case LineCenter:
ti = new TextPointItem(text, path.pointAtPercent(0.5), _font,
_maxWidth, _anchor, icon);
break;
default:
ti = new TextPointItem(text, path.elementAt(0), _font, _maxWidth,
_anchor, icon);
break;
}
}
// Note: empty path == point geometry (single move)
@ -56,6 +71,7 @@ void Text::addLabel(const QString &text, const QImage &icon,
}
ti->setPen(_pen);
ti->setHalo(_halo);
addItem(ti);
QList<TextItem*> ci = collidingItems(ti);

View File

@ -34,6 +34,22 @@ public:
Auto
};
class Halo {
public:
Halo() : _width(0), _blur(0) {}
Halo(const QColor &color, qreal width, qreal blur)
: _color(color), _width(width), _blur(blur) {}
const QColor &color() const {return _color;}
qreal width() const {return _width;}
qreal blur() const {return _blur;}
private:
QColor _color;
qreal _width;
qreal _blur;
};
Text(const QSize &size) : _sceneRect(QRectF(QPointF(0, 0), size)) {}
~Text();
@ -45,6 +61,7 @@ public:
void setSymbolPlacement(SymbolPlacement placement);
void setRotationAlignment(RotationAlignment alignment)
{_alignment = alignment;}
void setHalo(const Halo &halo) {_halo = halo;}
void addLabel(const QString &text, const QImage &icon,
const QPainterPath &path);
@ -65,6 +82,7 @@ private:
RotationAlignment _alignment;
QFont _font;
QPen _pen;
Halo _halo;
};
#endif // TEXT_H

View File

@ -11,7 +11,7 @@ bool TextItem::collidesWithItem(const TextItem *other) const
return other->shape().intersects(shape());
}
int TextItem::avgCharWidth() const
qreal TextItem::avgCharWidth() const
{
qreal ratio;
ushort cp = _text.at(0).unicode();
@ -21,14 +21,18 @@ int TextItem::avgCharWidth() const
ratio = 1.0;
// Greek & Cyrilic
else if (cp >= 0x03FF && cp <= 0x04FF) {
ratio = (_font.capitalization() == QFont::AllUppercase) ? 0.75 : 0.67;
ratio = (_font.capitalization() == QFont::AllUppercase) ? 0.80 : 0.73;
if (_font.bold())
ratio *= 1.1;
if (_font.italic())
ratio *= 0.9;
// The rest (Latin scripts, Arabic, ...)
} else {
ratio = (_font.capitalization() == QFont::AllUppercase) ? 0.7 : 0.58;
ratio = (_font.capitalization() == QFont::AllUppercase) ? 0.75 : 0.63;
if (_font.bold())
ratio *= 1.1;
if (_font.italic())
ratio *= 0.9;
}
return ratio * _font.pixelSize();

View File

@ -6,6 +6,7 @@
#include <QPen>
#include <QFont>
#include <QRectF>
#include "text.h"
class QPainter;
@ -19,7 +20,9 @@ public:
const QString &text() const {return _text;}
const QFont &font() const {return _font;}
const QPen &pen() const {return _pen;}
const Text::Halo &halo() const {return _halo;}
void setPen(const QPen &pen) {_pen = pen;}
void setHalo(const Text::Halo &halo) {_halo = halo;}
virtual QPainterPath shape() const = 0;
virtual QRectF boundingRect() const = 0;
@ -31,12 +34,13 @@ public:
bool collidesWithItem(const TextItem *other) const;
protected:
int avgCharWidth() const;
qreal avgCharWidth() const;
private:
QString _text;
QFont _font;
QPen _pen;
Text::Halo _halo;
bool _visible;
};

View File

@ -3,23 +3,23 @@
#include "textpathitem.h"
static QPointF intersection(const QLineF &line, const QRectF &rect)
static bool intersection(const QLineF &line, const QRectF &rect,
QPointF *p)
{
QPointF p;
if (line.intersect(QLineF(rect.topLeft(), rect.topRight()), &p)
if (line.intersect(QLineF(rect.topLeft(), rect.topRight()), p)
== QLineF::BoundedIntersection)
return p;
if (line.intersect(QLineF(rect.topLeft(), rect.bottomLeft()), &p)
return true;
if (line.intersect(QLineF(rect.topLeft(), rect.bottomLeft()), p)
== QLineF::BoundedIntersection)
return p;
if (line.intersect(QLineF(rect.bottomRight(), rect.bottomLeft()), &p)
return true;
if (line.intersect(QLineF(rect.bottomRight(), rect.bottomLeft()), p)
== QLineF::BoundedIntersection)
return p;
if (line.intersect(QLineF(rect.bottomRight(), rect.topRight()), &p)
return true;
if (line.intersect(QLineF(rect.bottomRight(), rect.topRight()), p)
== QLineF::BoundedIntersection)
return p;
return true;
return rect.center();
return false;
}
static QPainterPath subpath(const QList<QLineF> &lines, int start, int end,
@ -63,6 +63,7 @@ static QList<QLineF> lineString(const QPainterPath &path,
{
QList<QLineF> lines;
int start = 0, end = path.elementCount() - 1;
QPointF p;
for (int i = 0; i < path.elementCount(); i++) {
if (boundingRect.contains(path.elementAt(i))) {
@ -79,16 +80,14 @@ static QList<QLineF> lineString(const QPainterPath &path,
if (start > 0) {
QLineF l(path.elementAt(start-1), path.elementAt(start));
QPointF p(intersection(l, boundingRect));
if (p != boundingRect.center())
if (intersection(l, boundingRect, &p))
lines.append(QLineF(p, path.elementAt(start)));
}
for (int i = start + 1; i <= end; i++)
lines.append(QLineF(path.elementAt(i-1), path.elementAt(i)));
if (end < path.elementCount() - 1) {
QLineF l(path.elementAt(end), path.elementAt(end+1));
QPointF p(intersection(l, boundingRect));
if (p != boundingRect.center())
if (intersection(l, boundingRect, &p))
lines.append(QLineF(path.elementAt(end), p));
}
@ -136,7 +135,7 @@ TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
const QFont &font, int maxAngle, const QRectF &tileRect)
: TextItem(text, font)
{
int cw = avgCharWidth();
qreal cw = avgCharWidth();
int textWidth = text.size() * cw;
if (textWidth > path.length())
return;
@ -166,10 +165,35 @@ void TextPathItem::paint(QPainter *painter) const
qreal percent = (1.0 - factor) / 2.0;
painter->setFont(font());
painter->setPen(pen());
QTransform t = painter->transform();
if (halo().color().isValid() && halo().width() > 0) {
painter->setPen(halo().color());
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(-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->setTransform(t);
int width = fm.charWidth(text(), i);
percent += ((qreal)width / (qreal)textWidth) * factor;
}
percent = (1.0 - factor) / 2.0;
}
painter->setPen(pen());
for (int i = 0; i < text().size(); i++) {
QPointF point = _path.pointAtPercent(percent);
qreal angle = _path.angleAtPercent(percent);

View File

@ -1,34 +1,20 @@
#include <QPainter>
#include <QtMath>
#include <QStaticText>
#include "config.h"
#include "textpointitem.h"
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
QRectF TextPointItem::exactBoundingRect() const
{
QFontMetrics fm(font());
int limit = font().pixelSize() * _maxWidth;
// Italic fonts overflow the computed bounding rect, so reduce it
// a little bit.
if (font().italic())
limit -= font().pixelSize() / 2.0;
QRect br = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, text());
Q_ASSERT(br.isValid());
// Expand the bounding rect back to the real content size
if (font().italic())
br.adjust(-font().pixelSize() / 4.0, 0, font().pixelSize() / 4.0, 0);
return br;
}
QRectF TextPointItem::fuzzyBoundingRect() const
{
int limit = font().pixelSize() * _maxWidth;
int fs = font().pixelSize();
if (text().size() <= 3)
return QRectF(0, 0, text().size() * fs, fs * 1.6);
int limit = fs * _maxWidth;
qreal cw = avgCharWidth();
qreal lh = font().pixelSize() * 1.25;
int width = 0, lines = 0;
QStringList l(text().split('\n'));
@ -44,7 +30,7 @@ QRectF TextPointItem::fuzzyBoundingRect() const
if (wl + pl < limit) {
pl += wl + cw;
} else {
if (wl > limit) {
if (wl >= limit) {
if (pl > 0)
lines++;
} else
@ -61,11 +47,11 @@ QRectF TextPointItem::fuzzyBoundingRect() const
}
}
return QRectF(0, 0, width, lines * lh);
return QRectF(0, 0, width, lines * fs * 1.6);
}
QRectF TextPointItem::computeTextRect(bool exact) const
QRectF TextPointItem::moveTextRect(const QRectF &rect) const
{
#ifdef ENABLE_HIDPI
QRectF iconRect = _icon.isNull() ? QRectF()
@ -74,19 +60,19 @@ QRectF TextPointItem::computeTextRect(bool exact) const
QRectF iconRect = _icon.isNull() ? QRectF() : QRectF(QPointF(0, 0),
QSizeF(_icon.size()));
#endif // ENABLE_HIDPI
QRectF textRect = exact ? exactBoundingRect() : fuzzyBoundingRect();
QRectF textRect(rect);
switch (_anchor) {
case Text::Center:
textRect.moveCenter(_pos);
break;
case Text::Left:
textRect.moveTopLeft(_pos - QPointF(-iconRect.width() / 2,
textRect.height() / 2));
textRect.moveTopLeft(_pos - QPointF(-iconRect.width() / 2
- font().pixelSize()/4.0, textRect.height() / 2));
break;
case Text::Right:
textRect.moveTopRight(_pos - QPointF(iconRect.width() / 2,
textRect.height() / 2));
textRect.moveTopRight(_pos - QPointF(iconRect.width() / 2
+ font().pixelSize()/4.0, textRect.height() / 2));
break;
case Text::Bottom:
textRect.moveTopLeft(_pos - QPointF(textRect.width() / 2,
@ -106,7 +92,8 @@ TextPointItem::TextPointItem(const QString &text, const QPointF &pos,
: TextItem(text, font), _pos(pos), _icon(icon), _maxWidth(maxWidth),
_anchor(anchor)
{
_boundingRect = computeTextRect(false);
_textRect = fuzzyBoundingRect();
_boundingRect = moveTextRect(_textRect);
if (!_icon.isNull()) {
#ifdef ENABLE_HIDPI
@ -122,16 +109,24 @@ TextPointItem::TextPointItem(const QString &text, const QPointF &pos,
_shape.addRect(_boundingRect);
}
void TextPointItem::setPos(const QPointF &pos)
{
QPointF d(_boundingRect.left() - _pos.x(), _boundingRect.top() - _pos.y());
_boundingRect.moveTopLeft(pos + d);
_shape = QPainterPath();
_shape.addRect(_boundingRect);
_pos = pos;
}
void TextPointItem::paint(QPainter *painter) const
{
//painter->setPen(Qt::red);
//painter->drawRect(_boundingRect);
QRectF textRect;
painter->setFont(font());
painter->setPen(pen());
if (!_icon.isNull()) {
textRect = (_anchor != Text::Center)
? computeTextRect(true) : _boundingRect;
textRect = moveTextRect(painter->boundingRect(_textRect, FLAGS, text()));
#ifdef ENABLE_HIDPI
painter->drawImage(_pos - QPointF(_icon.width()
/ _icon.devicePixelRatioF() / 2, _icon.height()
@ -143,7 +138,30 @@ void TextPointItem::paint(QPainter *painter) const
} else
textRect = _boundingRect;
painter->setFont(font());
painter->setPen(pen());
painter->drawText(textRect, FLAGS, text());
if (hasHalo()) {
QStaticText st(text());
st.setTextFormat(Qt::PlainText);
st.setTextWidth(textRect.width());
st.setTextOption(QTextOption(Qt::AlignHCenter));
st.setPerformanceHint(QStaticText::AggressiveCaching);
painter->setPen(halo().color());
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(pen());
painter->drawStaticText(textRect.topLeft(), st);
} else
painter->drawText(textRect, FLAGS, text());
//painter->setBrush(Qt::NoBrush);
//painter->setPen(Qt::red);
//painter->drawRect(_boundingRect);
//painter->setPen(Qt::blue);
//painter->drawRect(textRect);
}

View File

@ -15,14 +15,17 @@ public:
QPainterPath shape() const {return _shape;}
void paint(QPainter *painter) const;
void setPos(const QPointF &pos);
private:
QRectF exactBoundingRect() const;
QRectF fuzzyBoundingRect() const;
QRectF computeTextRect(bool exact) const;
QRectF moveTextRect(const QRectF &rect) const;
bool hasHalo() const
{return halo().color().isValid() && halo().width() > 0;}
QPointF _pos;
QPainterPath _shape;
QRectF _boundingRect;
QRectF _textRect, _boundingRect;
QImage _icon;
int _maxWidth;
Text::Anchor _anchor;