mirror of
https://github.com/tumic0/QtPBFImagePlugin.git
synced 2024-11-27 21:24:48 +01:00
Use fuzzy text bounding rect computation to improve performance
Do not use QGraphicsScene as it is not thread-safe
This commit is contained in:
parent
078fc93783
commit
d6f6a0eb3d
@ -1,7 +1,7 @@
|
|||||||
TARGET = pbf
|
TARGET = pbf
|
||||||
TEMPLATE = lib
|
TEMPLATE = lib
|
||||||
CONFIG += plugin
|
CONFIG += plugin
|
||||||
QT += gui widgets
|
QT += gui
|
||||||
|
|
||||||
PROTOS = protobuf/vector_tile.proto
|
PROTOS = protobuf/vector_tile.proto
|
||||||
include(protobuf/vector_tile.pri)
|
include(protobuf/vector_tile.pri)
|
||||||
@ -17,8 +17,9 @@ HEADERS += src/pbfhandler.h \
|
|||||||
src/tile.h \
|
src/tile.h \
|
||||||
src/function.h \
|
src/function.h \
|
||||||
src/textpathitem.h \
|
src/textpathitem.h \
|
||||||
src/textitem.h \
|
src/textpointitem.h \
|
||||||
src/font.h
|
src/font.h \
|
||||||
|
src/textitem.h
|
||||||
SOURCES += src/pbfplugin.cpp \
|
SOURCES += src/pbfplugin.cpp \
|
||||||
src/pbfhandler.cpp \
|
src/pbfhandler.cpp \
|
||||||
src/gzip.cpp \
|
src/gzip.cpp \
|
||||||
@ -28,7 +29,7 @@ SOURCES += src/pbfplugin.cpp \
|
|||||||
src/text.cpp \
|
src/text.cpp \
|
||||||
src/function.cpp \
|
src/function.cpp \
|
||||||
src/textpathitem.cpp \
|
src/textpathitem.cpp \
|
||||||
src/textitem.cpp \
|
src/textpointitem.cpp \
|
||||||
src/font.cpp
|
src/font.cpp
|
||||||
RESOURCES += pbfplugin.qrc
|
RESOURCES += pbfplugin.qrc
|
||||||
|
|
||||||
|
49
src/text.cpp
49
src/text.cpp
@ -1,8 +1,7 @@
|
|||||||
#include <QGraphicsPixmapItem>
|
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include "text.h"
|
#include "text.h"
|
||||||
#include "textitem.h"
|
#include "textpointitem.h"
|
||||||
#include "textpathitem.h"
|
#include "textpathitem.h"
|
||||||
|
|
||||||
|
|
||||||
@ -134,16 +133,32 @@ static bool reverse(const QPainterPath &path)
|
|||||||
return (angle > 90 && angle < 270) ? true : false;
|
return (angle > 90 && angle < 270) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Text::~Text()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _items.size(); i++)
|
||||||
|
delete _items[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Text::render(QPainter *painter)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _items.size(); i++) {
|
||||||
|
const TextItem *ti = _items.at(i);
|
||||||
|
if (ti->isVisible() && _sceneRect.intersects(ti->boundingRect()))
|
||||||
|
ti->paint(painter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Text::addLabel(const QString &text, const QPointF &pos, const QFont &font,
|
void Text::addLabel(const QString &text, const QPointF &pos, const QFont &font,
|
||||||
const QPen &pen, qreal maxTextWidth)
|
const QPen &pen, qreal maxTextWidth)
|
||||||
{
|
{
|
||||||
if (text.isEmpty())
|
if (text.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TextItem *ti = new TextItem(text, pos, font, maxTextWidth);
|
TextPointItem *ti = new TextPointItem(text, pos, font, maxTextWidth);
|
||||||
ti->setPen(pen);
|
ti->setPen(pen);
|
||||||
addItem(ti);
|
addItem(ti);
|
||||||
QList<QGraphicsItem*> ci = collidingItems(ti);
|
QList<TextItem*> ci = collidingItems(ti);
|
||||||
for (int i = 0; i < ci.size(); i++)
|
for (int i = 0; i < ci.size(); i++)
|
||||||
ci[i]->setVisible(false);
|
ci[i]->setVisible(false);
|
||||||
}
|
}
|
||||||
@ -163,21 +178,37 @@ void Text::addLabel(const QString &text, const QPainterPath &path,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
QPainterPath tp(textPath(path, textWidth, maxAngle, fm.averageCharWidth(),
|
QPainterPath tp(textPath(path, textWidth, maxAngle, fm.averageCharWidth(),
|
||||||
sceneRect()));
|
_sceneRect));
|
||||||
if (tp.isEmpty())
|
if (tp.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
TextPathItem *pi = new TextPathItem(text, reverse(tp) ? tp.toReversed()
|
TextPathItem *pi = new TextPathItem(text, reverse(tp) ? tp.toReversed()
|
||||||
: tp, font);
|
: tp, font);
|
||||||
addItem(pi);
|
if (!_sceneRect.contains(pi->boundingRect())) {
|
||||||
if (!sceneRect().contains(pi->sceneBoundingRect())) {
|
|
||||||
delete pi;
|
delete pi;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pi->setPen(pen);
|
pi->setPen(pen);
|
||||||
|
|
||||||
QList<QGraphicsItem*> ci = collidingItems(pi);
|
addItem(pi);
|
||||||
|
|
||||||
|
QList<TextItem*> ci = collidingItems(pi);
|
||||||
for (int j = 0; j < ci.size(); j++)
|
for (int j = 0; j < ci.size(); j++)
|
||||||
ci[j]->setVisible(false);
|
ci[j]->setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<TextItem*> Text::collidingItems(const TextItem *item) const
|
||||||
|
{
|
||||||
|
QList<TextItem*> list;
|
||||||
|
|
||||||
|
if (!item->isVisible())
|
||||||
|
return list;
|
||||||
|
|
||||||
|
for (int i = 0; i < _items.size();i ++) {
|
||||||
|
const TextItem *ti = _items.at(i);
|
||||||
|
if (ti != item && ti->isVisible() && ti->collidesWithItem(item))
|
||||||
|
list.append(const_cast<TextItem*>(ti));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
17
src/text.h
17
src/text.h
@ -1,18 +1,27 @@
|
|||||||
#ifndef TEXT_H
|
#ifndef TEXT_H
|
||||||
#define TEXT_H
|
#define TEXT_H
|
||||||
|
|
||||||
#include <QGraphicsScene>
|
#include "textitem.h"
|
||||||
|
|
||||||
class Text : public QGraphicsScene
|
class Text
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Text(int size, QObject *parent = 0) : QGraphicsScene(parent)
|
Text(int size) : _sceneRect(QRectF(0, 0, size, size)) {}
|
||||||
{setSceneRect(0, 0, size, size);}
|
~Text();
|
||||||
|
|
||||||
|
void render(QPainter *painter);
|
||||||
|
|
||||||
void addLabel(const QString &text, const QPointF &pos, const QFont &font,
|
void addLabel(const QString &text, const QPointF &pos, const QFont &font,
|
||||||
const QPen &pen, qreal maxTextWidth);
|
const QPen &pen, qreal maxTextWidth);
|
||||||
void addLabel(const QString &text, const QPainterPath &path,
|
void addLabel(const QString &text, const QPainterPath &path,
|
||||||
const QFont &font, const QPen &pen, qreal maxAngle);
|
const QFont &font, const QPen &pen, qreal maxAngle);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addItem(TextItem *item) {_items.append(item);}
|
||||||
|
QList<TextItem *> collidingItems(const TextItem *item) const;
|
||||||
|
|
||||||
|
QRectF _sceneRect;
|
||||||
|
QList<TextItem *> _items;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TEXT_H
|
#endif // TEXT_H
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
#include <QPainter>
|
|
||||||
#include "textitem.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
|
|
||||||
|
|
||||||
TextItem::TextItem(const QString &text, const QPointF &pos, const QFont &font,
|
|
||||||
int maxTextWidth, QGraphicsItem *parent) : QGraphicsItem(parent), _text(text),
|
|
||||||
_font(font)
|
|
||||||
{
|
|
||||||
QFontMetrics fm(font);
|
|
||||||
int limit = font.pixelSize() * maxTextWidth;
|
|
||||||
// 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);
|
|
||||||
setPos((pos - QPointF(br.width() / 2.0, br.height() / 2.0)).toPoint());
|
|
||||||
_boundingRect = QRectF(0, 0, br.width(), br.height());
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
|
||||||
QWidget *widget)
|
|
||||||
{
|
|
||||||
Q_UNUSED(option);
|
|
||||||
Q_UNUSED(widget);
|
|
||||||
|
|
||||||
painter->setFont(_font);
|
|
||||||
painter->setPen(_pen);
|
|
||||||
painter->drawText(_boundingRect, FLAGS, _text);
|
|
||||||
|
|
||||||
//painter->setPen(Qt::red);
|
|
||||||
//painter->drawRect(_boundingRect);
|
|
||||||
}
|
|
@ -1,28 +1,36 @@
|
|||||||
#ifndef TEXTITEM_H
|
#ifndef TEXTITEM_H
|
||||||
#define TEXTITEM_H
|
#define TEXTITEM_H
|
||||||
|
|
||||||
#include <QGraphicsItem>
|
#include <QPainterPath>
|
||||||
#include <QPen>
|
|
||||||
#include <QFont>
|
|
||||||
#include <QString>
|
|
||||||
|
|
||||||
class TextItem : public QGraphicsItem
|
#include <QDebug>
|
||||||
|
|
||||||
|
class TextItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TextItem(const QString &text, const QPointF &pos, const QFont &font,
|
TextItem() : _visible(true) {}
|
||||||
int maxTextWidth, QGraphicsItem *parent = 0);
|
virtual ~TextItem() {}
|
||||||
|
|
||||||
QRectF boundingRect() const {return _boundingRect;}
|
virtual QPainterPath shape() const = 0;
|
||||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
virtual QRectF boundingRect() const = 0;
|
||||||
QWidget *widget);
|
virtual void paint(QPainter *painter) const = 0;
|
||||||
|
|
||||||
void setPen(const QPen &pen) {_pen = pen;}
|
bool isVisible() const {return _visible;}
|
||||||
|
void setVisible(bool visible) {_visible = visible;}
|
||||||
|
|
||||||
|
bool collidesWithItem(const TextItem *other) const
|
||||||
|
{
|
||||||
|
QRectF r1(boundingRect());
|
||||||
|
QRectF r2(other->boundingRect());
|
||||||
|
|
||||||
|
if (r1.isEmpty() || r2.isEmpty() || !r1.intersects(r2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return other->shape().intersects(shape());
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString _text;
|
bool _visible;
|
||||||
QRectF _boundingRect;
|
|
||||||
QFont _font;
|
|
||||||
QPen _pen;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TEXTITEM_H
|
#endif // TEXTITEM_H
|
||||||
|
@ -4,22 +4,18 @@
|
|||||||
|
|
||||||
|
|
||||||
TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
|
TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
|
||||||
const QFont &font, QGraphicsItem *parent) : QGraphicsItem(parent),
|
const QFont &font) : _text(text), _path(path), _font(font)
|
||||||
_text(text), _path(path), _font(font)
|
|
||||||
{
|
{
|
||||||
QFontMetrics fm(font);
|
QFontMetrics fm(font);
|
||||||
QPainterPathStroker s;
|
QPainterPathStroker s;
|
||||||
s.setWidth(fm.height());
|
s.setWidth(fm.height());
|
||||||
s.setCapStyle(Qt::FlatCap);
|
s.setCapStyle(Qt::FlatCap);
|
||||||
_shape = s.createStroke(path).simplified();
|
_shape = s.createStroke(path).simplified();
|
||||||
|
_boundingRect = _shape.boundingRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextPathItem::paint(QPainter *painter,
|
void TextPathItem::paint(QPainter *painter) const
|
||||||
const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
||||||
{
|
{
|
||||||
Q_UNUSED(option);
|
|
||||||
Q_UNUSED(widget);
|
|
||||||
|
|
||||||
QFontMetrics fm(_font);
|
QFontMetrics fm(_font);
|
||||||
int textWidth = fm.width(_text);
|
int textWidth = fm.width(_text);
|
||||||
|
|
||||||
|
@ -1,21 +1,19 @@
|
|||||||
#ifndef TEXTPATHITEM_H
|
#ifndef TEXTPATHITEM_H
|
||||||
#define TEXTPATHITEM_H
|
#define TEXTPATHITEM_H
|
||||||
|
|
||||||
#include <QGraphicsItem>
|
|
||||||
#include <QPainterPath>
|
|
||||||
#include <QFont>
|
#include <QFont>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include "textitem.h"
|
||||||
|
|
||||||
class TextPathItem : public QGraphicsItem
|
class TextPathItem : public TextItem
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TextPathItem(const QString &text, const QPainterPath &path,
|
TextPathItem(const QString &text, const QPainterPath &path,
|
||||||
const QFont &font, QGraphicsItem *parent = 0);
|
const QFont &font);
|
||||||
|
|
||||||
QPainterPath shape() const {return _shape;}
|
QPainterPath shape() const {return _shape;}
|
||||||
QRectF boundingRect() const {return _shape.boundingRect();}
|
QRectF boundingRect() const {return _boundingRect;}
|
||||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
void paint(QPainter *painter) const;
|
||||||
QWidget *widget);
|
|
||||||
|
|
||||||
void setPen(const QPen &pen) {_pen = pen;}
|
void setPen(const QPen &pen) {_pen = pen;}
|
||||||
|
|
||||||
@ -23,6 +21,7 @@ private:
|
|||||||
QString _text;
|
QString _text;
|
||||||
QPainterPath _path;
|
QPainterPath _path;
|
||||||
QPainterPath _shape;
|
QPainterPath _shape;
|
||||||
|
QRectF _boundingRect;
|
||||||
QFont _font;
|
QFont _font;
|
||||||
QPen _pen;
|
QPen _pen;
|
||||||
};
|
};
|
||||||
|
88
src/textpointitem.cpp
Normal file
88
src/textpointitem.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#include <QPainter>
|
||||||
|
#include <QtMath>
|
||||||
|
#include "textpointitem.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
|
||||||
|
|
||||||
|
static QRectF fuzzyBoundingRect(const QString &str, const QFont &font,
|
||||||
|
int maxTextWidth)
|
||||||
|
{
|
||||||
|
int limit = font.pixelSize() * maxTextWidth;
|
||||||
|
qreal cw = font.pixelSize() * 0.6;
|
||||||
|
qreal lh = font.pixelSize() * 1.25;
|
||||||
|
int width = 0, lines = 0;
|
||||||
|
|
||||||
|
QStringList l(str.split('\n'));
|
||||||
|
for (int i = 0; i < l.size(); i++) {
|
||||||
|
int lw = (int)(l.at(i).length() * cw);
|
||||||
|
if (lw > limit) {
|
||||||
|
l[i].replace('-', ' ');
|
||||||
|
l[i].replace('/', ' ');
|
||||||
|
QStringList words(l.at(i).split(' '));
|
||||||
|
int pl = 0;
|
||||||
|
for (int j = 0; j < words.size(); j++) {
|
||||||
|
int wl = (int)(words.at(j).length() * cw);
|
||||||
|
if (wl + pl < limit) {
|
||||||
|
pl += wl + cw;
|
||||||
|
} else {
|
||||||
|
if (wl > limit) {
|
||||||
|
if (pl > 0)
|
||||||
|
lines++;
|
||||||
|
} else
|
||||||
|
lines++;
|
||||||
|
width = qMax(width, qMax(pl, wl));
|
||||||
|
pl = wl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width = qMax(width, pl);
|
||||||
|
lines++;
|
||||||
|
} else {
|
||||||
|
width = qMax(width, lw);
|
||||||
|
lines++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QRectF(0, 0, width, lines * lh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
static QRectF exactBoundingRect(const QString &str, const QFont &font,
|
||||||
|
int maxTextWidth)
|
||||||
|
{
|
||||||
|
QFontMetrics fm(font);
|
||||||
|
int limit = font.pixelSize() * maxTextWidth;
|
||||||
|
// 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, str);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
TextPointItem::TextPointItem(const QString &text, const QPointF &pos,
|
||||||
|
const QFont &font, int maxTextWidth) :_text(text), _font(font)
|
||||||
|
{
|
||||||
|
_boundingRect = fuzzyBoundingRect(text, font, maxTextWidth);
|
||||||
|
//_boundingRect = exactBoundingRect(text, font, maxTextWidth);
|
||||||
|
|
||||||
|
_boundingRect.moveCenter(pos);
|
||||||
|
_shape.addRect(_boundingRect);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextPointItem::paint(QPainter *painter) const
|
||||||
|
{
|
||||||
|
painter->setFont(_font);
|
||||||
|
painter->setPen(_pen);
|
||||||
|
painter->drawText(_boundingRect, FLAGS, _text);
|
||||||
|
|
||||||
|
//painter->setPen(Qt::red);
|
||||||
|
//painter->drawRect(_boundingRect);
|
||||||
|
}
|
29
src/textpointitem.h
Normal file
29
src/textpointitem.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef TEXTPOINTITEM_H
|
||||||
|
#define TEXTPOINTITEM_H
|
||||||
|
|
||||||
|
#include <QPen>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QString>
|
||||||
|
#include "textitem.h"
|
||||||
|
|
||||||
|
class TextPointItem : public TextItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextPointItem(const QString &text, const QPointF &pos, const QFont &font,
|
||||||
|
int maxTextWidth);
|
||||||
|
|
||||||
|
QRectF boundingRect() const {return _boundingRect;}
|
||||||
|
QPainterPath shape() const {return _shape;}
|
||||||
|
void paint(QPainter *painter) const;
|
||||||
|
|
||||||
|
void setPen(const QPen &pen) {_pen = pen;}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString _text;
|
||||||
|
QPainterPath _shape;
|
||||||
|
QRectF _boundingRect;
|
||||||
|
QFont _font;
|
||||||
|
QPen _pen;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // TEXTPOINTITEM_H
|
Loading…
Reference in New Issue
Block a user