mirror of
https://github.com/tumic0/QtPBFImagePlugin.git
synced 2024-11-27 13:14:49 +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
|
||||
TEMPLATE = lib
|
||||
CONFIG += plugin
|
||||
QT += gui widgets
|
||||
QT += gui
|
||||
|
||||
PROTOS = protobuf/vector_tile.proto
|
||||
include(protobuf/vector_tile.pri)
|
||||
@ -17,8 +17,9 @@ HEADERS += src/pbfhandler.h \
|
||||
src/tile.h \
|
||||
src/function.h \
|
||||
src/textpathitem.h \
|
||||
src/textitem.h \
|
||||
src/font.h
|
||||
src/textpointitem.h \
|
||||
src/font.h \
|
||||
src/textitem.h
|
||||
SOURCES += src/pbfplugin.cpp \
|
||||
src/pbfhandler.cpp \
|
||||
src/gzip.cpp \
|
||||
@ -28,7 +29,7 @@ SOURCES += src/pbfplugin.cpp \
|
||||
src/text.cpp \
|
||||
src/function.cpp \
|
||||
src/textpathitem.cpp \
|
||||
src/textitem.cpp \
|
||||
src/textpointitem.cpp \
|
||||
src/font.cpp
|
||||
RESOURCES += pbfplugin.qrc
|
||||
|
||||
|
49
src/text.cpp
49
src/text.cpp
@ -1,8 +1,7 @@
|
||||
#include <QGraphicsPixmapItem>
|
||||
#include <QFontMetrics>
|
||||
#include <QPainter>
|
||||
#include "text.h"
|
||||
#include "textitem.h"
|
||||
#include "textpointitem.h"
|
||||
#include "textpathitem.h"
|
||||
|
||||
|
||||
@ -134,16 +133,32 @@ static bool reverse(const QPainterPath &path)
|
||||
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,
|
||||
const QPen &pen, qreal maxTextWidth)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
TextItem *ti = new TextItem(text, pos, font, maxTextWidth);
|
||||
TextPointItem *ti = new TextPointItem(text, pos, font, maxTextWidth);
|
||||
ti->setPen(pen);
|
||||
addItem(ti);
|
||||
QList<QGraphicsItem*> ci = collidingItems(ti);
|
||||
QList<TextItem*> ci = collidingItems(ti);
|
||||
for (int i = 0; i < ci.size(); i++)
|
||||
ci[i]->setVisible(false);
|
||||
}
|
||||
@ -163,21 +178,37 @@ void Text::addLabel(const QString &text, const QPainterPath &path,
|
||||
return;
|
||||
|
||||
QPainterPath tp(textPath(path, textWidth, maxAngle, fm.averageCharWidth(),
|
||||
sceneRect()));
|
||||
_sceneRect));
|
||||
if (tp.isEmpty())
|
||||
return;
|
||||
|
||||
TextPathItem *pi = new TextPathItem(text, reverse(tp) ? tp.toReversed()
|
||||
: tp, font);
|
||||
addItem(pi);
|
||||
if (!sceneRect().contains(pi->sceneBoundingRect())) {
|
||||
if (!_sceneRect.contains(pi->boundingRect())) {
|
||||
delete pi;
|
||||
return;
|
||||
}
|
||||
|
||||
pi->setPen(pen);
|
||||
|
||||
QList<QGraphicsItem*> ci = collidingItems(pi);
|
||||
addItem(pi);
|
||||
|
||||
QList<TextItem*> ci = collidingItems(pi);
|
||||
for (int j = 0; j < ci.size(); j++)
|
||||
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
|
||||
#define TEXT_H
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include "textitem.h"
|
||||
|
||||
class Text : public QGraphicsScene
|
||||
class Text
|
||||
{
|
||||
public:
|
||||
Text(int size, QObject *parent = 0) : QGraphicsScene(parent)
|
||||
{setSceneRect(0, 0, size, size);}
|
||||
Text(int size) : _sceneRect(QRectF(0, 0, size, size)) {}
|
||||
~Text();
|
||||
|
||||
void render(QPainter *painter);
|
||||
|
||||
void addLabel(const QString &text, const QPointF &pos, const QFont &font,
|
||||
const QPen &pen, qreal maxTextWidth);
|
||||
void addLabel(const QString &text, const QPainterPath &path,
|
||||
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
|
||||
|
@ -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
|
||||
#define TEXTITEM_H
|
||||
|
||||
#include <QGraphicsItem>
|
||||
#include <QPen>
|
||||
#include <QFont>
|
||||
#include <QString>
|
||||
#include <QPainterPath>
|
||||
|
||||
class TextItem : public QGraphicsItem
|
||||
#include <QDebug>
|
||||
|
||||
class TextItem
|
||||
{
|
||||
public:
|
||||
TextItem(const QString &text, const QPointF &pos, const QFont &font,
|
||||
int maxTextWidth, QGraphicsItem *parent = 0);
|
||||
TextItem() : _visible(true) {}
|
||||
virtual ~TextItem() {}
|
||||
|
||||
QRectF boundingRect() const {return _boundingRect;}
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget);
|
||||
virtual QPainterPath shape() const = 0;
|
||||
virtual QRectF boundingRect() const = 0;
|
||||
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:
|
||||
QString _text;
|
||||
QRectF _boundingRect;
|
||||
QFont _font;
|
||||
QPen _pen;
|
||||
bool _visible;
|
||||
};
|
||||
|
||||
#endif // TEXTITEM_H
|
||||
|
@ -4,22 +4,18 @@
|
||||
|
||||
|
||||
TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
|
||||
const QFont &font, QGraphicsItem *parent) : QGraphicsItem(parent),
|
||||
_text(text), _path(path), _font(font)
|
||||
const QFont &font) : _text(text), _path(path), _font(font)
|
||||
{
|
||||
QFontMetrics fm(font);
|
||||
QPainterPathStroker s;
|
||||
s.setWidth(fm.height());
|
||||
s.setCapStyle(Qt::FlatCap);
|
||||
_shape = s.createStroke(path).simplified();
|
||||
_boundingRect = _shape.boundingRect();
|
||||
}
|
||||
|
||||
void TextPathItem::paint(QPainter *painter,
|
||||
const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
void TextPathItem::paint(QPainter *painter) const
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_UNUSED(widget);
|
||||
|
||||
QFontMetrics fm(_font);
|
||||
int textWidth = fm.width(_text);
|
||||
|
||||
|
@ -1,21 +1,19 @@
|
||||
#ifndef TEXTPATHITEM_H
|
||||
#define TEXTPATHITEM_H
|
||||
|
||||
#include <QGraphicsItem>
|
||||
#include <QPainterPath>
|
||||
#include <QFont>
|
||||
#include <QString>
|
||||
#include "textitem.h"
|
||||
|
||||
class TextPathItem : public QGraphicsItem
|
||||
class TextPathItem : public TextItem
|
||||
{
|
||||
public:
|
||||
TextPathItem(const QString &text, const QPainterPath &path,
|
||||
const QFont &font, QGraphicsItem *parent = 0);
|
||||
const QFont &font);
|
||||
|
||||
QPainterPath shape() const {return _shape;}
|
||||
QRectF boundingRect() const {return _shape.boundingRect();}
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
|
||||
QWidget *widget);
|
||||
QRectF boundingRect() const {return _boundingRect;}
|
||||
void paint(QPainter *painter) const;
|
||||
|
||||
void setPen(const QPen &pen) {_pen = pen;}
|
||||
|
||||
@ -23,6 +21,7 @@ private:
|
||||
QString _text;
|
||||
QPainterPath _path;
|
||||
QPainterPath _shape;
|
||||
QRectF _boundingRect;
|
||||
QFont _font;
|
||||
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