Improved fuzzy text bounding rect computation

This commit is contained in:
Martin Tůma 2018-12-11 18:48:12 +01:00
parent a559b5237b
commit 6d02dafeb6
5 changed files with 30 additions and 64 deletions

View File

@ -32,7 +32,8 @@ SOURCES += src/pbfplugin.cpp \
src/textpathitem.cpp \ src/textpathitem.cpp \
src/textpointitem.cpp \ src/textpointitem.cpp \
src/font.cpp \ src/font.cpp \
src/sprites.cpp src/sprites.cpp \
src/textitem.cpp
RESOURCES += pbfplugin.qrc RESOURCES += pbfplugin.qrc
unix:!macx{ unix:!macx{

View File

@ -12,12 +12,12 @@ class QPainter;
class TextItem class TextItem
{ {
public: public:
TextItem(const QString &text) : _text(text), _visible(true) {} TextItem(const QString &text, const QFont &font)
: _text(text), _font(font), _visible(true) {}
virtual ~TextItem() {} virtual ~TextItem() {}
const QString &text() const {return _text;} const QString &text() const {return _text;}
const QFont &font() const {return _font;} const QFont &font() const {return _font;}
void setFont(const QFont &font) {_font = font;}
const QPen &pen() const {return _pen;} const QPen &pen() const {return _pen;}
void setPen(const QPen &pen) {_pen = pen;} void setPen(const QPen &pen) {_pen = pen;}
@ -28,33 +28,10 @@ public:
bool isVisible() const {return _visible;} bool isVisible() const {return _visible;}
void setVisible(bool visible) {_visible = visible;} void setVisible(bool visible) {_visible = visible;}
bool collidesWithItem(const TextItem *other) const 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());
}
protected: protected:
static int avgCharWidth(const QString &str, const QFont &font) int avgCharWidth() const;
{
qreal ratio;
if (str.at(0).unicode() >= 0x2E80)
ratio = 1.0;
else {
ratio = (font.capitalization() == QFont::AllUppercase)
? 0.66 : 0.55;
if (font.bold())
ratio *= 1.1;
}
return ratio * font.pixelSize();
}
private: private:
QString _text; QString _text;

View File

@ -134,9 +134,9 @@ static bool reverse(const QPainterPath &path)
TextPathItem::TextPathItem(const QString &text, const QPainterPath &path, TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
const QFont &font, int maxAngle, const QRectF &tileRect) const QFont &font, int maxAngle, const QRectF &tileRect)
: TextItem(text) : TextItem(text, font)
{ {
int cw = avgCharWidth(text, font); int cw = avgCharWidth();
int textWidth = text.size() * cw; int textWidth = text.size() * cw;
if (textWidth > path.length()) if (textWidth > path.length())
return; return;
@ -146,7 +146,6 @@ TextPathItem::TextPathItem(const QString &text, const QPainterPath &path,
return; return;
_path = reverse(tp) ? tp.toReversed() : tp; _path = reverse(tp) ? tp.toReversed() : tp;
setFont(font);
QPainterPathStroker s; QPainterPathStroker s;
s.setWidth(font.pixelSize()); s.setWidth(font.pixelSize());

View File

@ -5,37 +5,32 @@
#define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip) #define FLAGS (Qt::AlignCenter | Qt::TextWordWrap | Qt::TextDontClip)
QRectF TextPointItem::exactBoundingRect(const QString &str, QRectF TextPointItem::exactBoundingRect() const
const QFont &font, int maxWidth)
{ {
QFontMetrics fm(font); QFontMetrics fm(font());
int limit = font.pixelSize() * maxWidth; int limit = font().pixelSize() * _maxWidth;
// Italic fonts overflow the computed bounding rect, so reduce it // Italic fonts overflow the computed bounding rect, so reduce it
// a little bit. // a little bit.
if (font.italic()) if (font().italic())
limit -= font.pixelSize() / 2.0; limit -= font().pixelSize() / 2.0;
QRect br = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, str); QRect br = fm.boundingRect(QRect(0, 0, limit, 0), FLAGS, text());
Q_ASSERT(br.isValid()); Q_ASSERT(br.isValid());
// Expand the bounding rect back to the real content size // Expand the bounding rect back to the real content size
if (font.italic()) if (font().italic())
br.adjust(-font.pixelSize() / 4.0, 0, font.pixelSize() / 4.0, 0); br.adjust(-font().pixelSize() / 4.0, 0, font().pixelSize() / 4.0, 0);
return br; return br;
} }
QRectF TextPointItem::fuzzyBoundingRect(const QString &str, QRectF TextPointItem::fuzzyBoundingRect() const
const QFont &font, int maxWidth)
{ {
int limit = font.pixelSize() * maxWidth; int limit = font().pixelSize() * _maxWidth;
qreal cw = avgCharWidth(str, font); qreal cw = avgCharWidth();
qreal lh = font.pixelSize() * 1.25; qreal lh = font().pixelSize() * 1.25;
int width = 0, lines = 0; int width = 0, lines = 0;
if (font.italic()) QStringList l(text().split('\n'));
limit -= font.pixelSize() / 2.0;
QStringList l(str.split('\n'));
for (int i = 0; i < l.size(); i++) { for (int i = 0; i < l.size(); i++) {
int lw = (int)(l.at(i).length() * cw); int lw = (int)(l.at(i).length() * cw);
if (lw > limit) { if (lw > limit) {
@ -69,11 +64,11 @@ QRectF TextPointItem::fuzzyBoundingRect(const QString &str,
} }
QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const QRectF TextPointItem::computeTextRect(bool exact) const
{ {
QRectF iconRect = _icon.isNull() ? QRectF() QRectF iconRect = _icon.isNull() ? QRectF()
: QRectF(QPointF(0, 0), QSizeF(_icon.size()) / _icon.devicePixelRatioF()); : QRectF(QPointF(0, 0), QSizeF(_icon.size()) / _icon.devicePixelRatioF());
QRectF textRect = brf(text(), font(), _maxWidth); QRectF textRect = exact ? exactBoundingRect() : fuzzyBoundingRect();
switch (_anchor) { switch (_anchor) {
case Text::Center: case Text::Center:
@ -102,11 +97,10 @@ QRectF TextPointItem::computeTextRect(BoundingRectFunction brf) const
TextPointItem::TextPointItem(const QString &text, const QPointF &pos, TextPointItem::TextPointItem(const QString &text, const QPointF &pos,
const QFont &font, int maxWidth, Text::Anchor anchor, const QImage &icon) const QFont &font, int maxWidth, Text::Anchor anchor, const QImage &icon)
: TextItem(text), _pos(pos), _icon(icon), _maxWidth(maxWidth), : TextItem(text, font), _pos(pos), _icon(icon), _maxWidth(maxWidth),
_anchor(anchor) _anchor(anchor)
{ {
setFont(font); _boundingRect = computeTextRect(false);
_boundingRect = computeTextRect(fuzzyBoundingRect);
if (!_icon.isNull()) { if (!_icon.isNull()) {
QRectF iconRect(QPointF(0, 0), QSizeF(_icon.size()) QRectF iconRect(QPointF(0, 0), QSizeF(_icon.size())
@ -126,12 +120,12 @@ void TextPointItem::paint(QPainter *painter) const
QRectF textRect; QRectF textRect;
if (!_icon.isNull()) { if (!_icon.isNull()) {
textRect = computeTextRect(exactBoundingRect); textRect = computeTextRect(true);
painter->drawImage(_pos - QPointF(_icon.width() painter->drawImage(_pos - QPointF(_icon.width()
/ _icon.devicePixelRatioF() / 2, _icon.height() / _icon.devicePixelRatioF() / 2, _icon.height()
/ _icon.devicePixelRatioF() / 2), _icon); / _icon.devicePixelRatioF() / 2), _icon);
} else } else
textRect = computeTextRect(fuzzyBoundingRect); textRect = _boundingRect;
painter->setFont(font()); painter->setFont(font());
painter->setPen(pen()); painter->setPen(pen());

View File

@ -16,14 +16,9 @@ public:
void paint(QPainter *painter) const; void paint(QPainter *painter) const;
private: private:
typedef QRectF (*BoundingRectFunction)(const QString &, const QFont &, int); QRectF exactBoundingRect() const;
QRectF fuzzyBoundingRect() const;
static QRectF exactBoundingRect(const QString &str, const QFont &font, QRectF computeTextRect(bool exact) const;
int maxWidth);
static QRectF fuzzyBoundingRect(const QString &str, const QFont &font,
int maxWidth);
QRectF computeTextRect(BoundingRectFunction brf) const;
QPointF _pos; QPointF _pos;
QPainterPath _shape; QPainterPath _shape;