diff options
author | Nicos Gollan <gtdev@spearhead.de> | 2010-07-23 18:40:56 +0400 |
---|---|---|
committer | Thorvald Natvig <slicer@users.sourceforge.net> | 2010-07-27 05:04:11 +0400 |
commit | 022772edd5e7b3281099e708288eb3fa82cf3215 (patch) | |
tree | 04d1b828baa441ac29313f8e0c9005ee61dc9020 /src | |
parent | 385f25684a23cc255fe3ac23074dc7a6088de446 (diff) |
More uniform font rendering in overlay
Fixes an issue where overlay text would be always scaled to fit a
given height, leading to uneven sizes depending on content
As long as text is within font metrics, it will not be scaled. Some
extremely high characters (accents on capitals, Zalgo, etc.) will
however be scaled to fit a bounding height, while preserving the
baseline to avoid breaking the regular layout.
Diffstat (limited to 'src')
-rw-r--r-- | src/mumble/Overlay.cpp | 112 | ||||
-rw-r--r-- | src/mumble/Overlay.h | 9 |
2 files changed, 73 insertions, 48 deletions
diff --git a/src/mumble/Overlay.cpp b/src/mumble/Overlay.cpp index 41fe9d103..5b05ebe7c 100644 --- a/src/mumble/Overlay.cpp +++ b/src/mumble/Overlay.cpp @@ -42,6 +42,12 @@ #define toReal toDouble #endif +OverlayTextLine::OverlayTextLine() { +} + +OverlayTextLine::OverlayTextLine(const QPainterPath &qpp) : QPainterPath(qpp), fAscent(0.0f), fDescent(0.0f) { +} + OverlayAppInfo::OverlayAppInfo(QString name, QIcon icon) { qsDisplayName = name; qiIcon = icon; @@ -478,8 +484,8 @@ void OverlayEditorScene::updateUserName() { break; } - QPainterPath pp; - const QPixmap &pm = OverlayUser::createPixmap(qsName, SCALESIZE(UserName), os.qcUserName[tsColor], os.qfUserName, pp); + OverlayTextLine tl; + const QPixmap &pm = OverlayUser::createPixmap(qsName, SCALESIZE(UserName), os.qcUserName[tsColor], os.qfUserName, tl); qgpiName->setPixmap(pm); moveUserName(); @@ -492,8 +498,8 @@ void OverlayEditorScene::moveUserName() { } void OverlayEditorScene::updateChannel() { - QPainterPath pp; - const QPixmap &pm = OverlayUser::createPixmap(Overlay::tr("Channel"), SCALESIZE(Channel), os.qcChannel, os.qfChannel, pp); + OverlayTextLine tl; + const QPixmap &pm = OverlayUser::createPixmap(Overlay::tr("Channel"), SCALESIZE(Channel), os.qcChannel, os.qfChannel, tl); qgpiChannel->setPixmap(pm); moveChannel(); @@ -1556,57 +1562,69 @@ void OverlayUser::updateLayout() { } } -QPixmap OverlayUser::createPixmap(const QString &string, unsigned int maxwidth, unsigned int height, QColor col, const QFont &font, QPainterPath &pp) { - float edge = height * 0.05f; +QPixmap OverlayUser::createPixmap(const QString &string, unsigned int maxwidth, unsigned int height, QColor col, const QFont &font, OverlayTextLine &tl) { + const float edge = static_cast<float>(height) * 0.05f; if (! height || ! maxwidth) return QPixmap(); - if (pp.isEmpty()) { + if (tl.isEmpty()) { QFont f = font; - - QRectF r; - { - QPainterPath qp; - qp.addText(0.0f, 0.0f, f, string); - r = qp.controlPointRect(); + QFontMetrics fm(f); + + // fit the font into a bounding box with padding + float pointsize = static_cast<float>(fm.descent()) + static_cast<float>(f.pointSizeF()); + float edge_correction = ((static_cast<float>(height) - 2.0f * edge) / pointsize); + + f.setPointSizeF(f.pointSizeF() * edge_correction); + fm = QFontMetrics(f); + + // strictly speaking cheating by using point size == ascent + float ascent = static_cast<float>(f.pointSizeF()); + float descent = static_cast<float>(fm.descent()); + + // calculate text metrics for eliding and scaling + QRectF bb; + tl.addText(0.0f, 0.0f, f, string); + bb = tl.controlPointRect(); + + float text_height = static_cast<float>(bb.height()) + 2*edge; + float scale = 1.0f; + if (bb.bottom() < 0.0f || text_height > static_cast<float>(height)) { + qreal scale_ascent = bb.top() < 0.0f ? ascent / (-bb.top() + edge) : 1.0f; + qreal scale_descent = bb.bottom() > 0.0f ? descent / (bb.bottom() + edge) : 1.0f; + scale = static_cast<float>(qMin(scale_ascent, scale_descent)); } - float fs = f.pointSizeF(); - float ds = fs * ((height - 2.0f * edge) / r.height()); - - f.setPointSizeF(ds); - - pp.addText(0.0f, 0.0f, f, string); - r = pp.controlPointRect(); - - QString str; - - if (maxwidth < 0 || r.width() < maxwidth) { - str = string; - } else { - QFontMetrics qfm(f); - str = qfm.elidedText(string, Qt::ElideRight, iroundf(maxwidth - 2 * edge)); + // eliding by previously calculated width + if ((bb.width()*scale) + 2*edge > maxwidth) { + int eliding_width = iroundf((static_cast<float>(maxwidth) / scale) - 2 * edge); + QString str = fm.elidedText(string, Qt::ElideRight, eliding_width); if (str.trimmed().isEmpty()) str = QLatin1String("..."); - pp = QPainterPath(); - pp.addText(0.0f, 0.0f, f, str); + tl = OverlayTextLine(); + tl.addText(0.0f, 0.0f, f, str); + bb = tl.controlPointRect(); } - QRectF qr = pp.controlPointRect(); + // translation to "pixmap space": + QMatrix correction; + // * adjust left edge + correction.translate(-bb.x() + edge, 0.0f); + // * scale overly high text (still on baseline) + correction.scale(scale, scale); + // * translate down to baseline + correction.translate(0.0f, (ascent + edge) / scale); -#if QT_VERSION >= 0x040600 - pp.translate(- qr.x() + edge, - qr.y() + edge); -#else - pp = QPainterPath(); - pp.addText(- qr.x() + edge, - qr.y() + edge, f, str); -#endif + tl = correction.map(tl); + tl.fAscent = ascent; + tl.fDescent = descent; } - QRectF qr = pp.controlPointRect(); + QRectF qr = tl.controlPointRect(); int w = iroundf(qr.width() + 2 * edge + 0.5f); - int h = iroundf(qr.height() + 2 * edge + 0.5f); + int h = iroundf(tl.fAscent + tl.fDescent + 2 * edge + 0.5f); QPixmap img(w, h); img.fill(Qt::transparent); @@ -1622,10 +1640,10 @@ QPixmap OverlayUser::createPixmap(const QString &string, unsigned int maxwidth, imgp.setBrush(qc); imgp.setPen(QPen(Qt::black, edge, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin)); - imgp.drawPath(pp); + imgp.drawPath(tl); imgp.setPen(Qt::NoPen); - imgp.drawPath(pp); + imgp.drawPath(tl); return img; } @@ -1635,9 +1653,9 @@ void OverlayUser::updateUser() { if (cuUser) qsName = cuUser->qsName; - QPainterPath pp; + OverlayTextLine tl; for (int i=0; i<4; ++i) { - const QPixmap &pm = createPixmap(qsName, SCALESIZE(UserName), os->qcUserName[i], os->qfUserName, pp); + const QPixmap &pm = createPixmap(qsName, SCALESIZE(UserName), os->qcUserName[i], os->qfUserName, tl); qgpiName[i]->setPixmap(pm); if (i == 0) @@ -1651,8 +1669,8 @@ void OverlayUser::updateUser() { if (cuUser) qsChannelName = cuUser->cChannel->qsName; - QPainterPath pp; - const QPixmap &pm = createPixmap(qsChannelName, SCALESIZE(Channel), os->qcChannel, os->qfChannel, pp); + OverlayTextLine tl; + const QPixmap &pm = createPixmap(qsChannelName, SCALESIZE(Channel), os->qcChannel, os->qfChannel, tl); qgpiChannel->setPixmap(pm); qgpiChannel->setPos(alignedPosition(scaledRect(os->qrfChannel, uiSize * os->fZoom), qgpiChannel->boundingRect(), os->qaChannel)); } @@ -2149,9 +2167,9 @@ bool OverlayClient::eventFilter(QObject *o, QEvent *e) { void OverlayClient::updateFPS() { if (g.s.os.bFps) { - QPainterPath pp; + OverlayTextLine tl; unsigned int uiSize = iroundf(qgs.sceneRect().height()); - const QPixmap &pm = OverlayUser::createPixmap(tr("FPS: %1").arg(static_cast<int>(fFps)), -1, iroundf(uiSize * g.s.os.fZoom * g.s.os.qrfFps.height()), g.s.os.qcFps, g.s.os.qfFps, pp); + const QPixmap &pm = OverlayUser::createPixmap(tr("FPS: %1").arg(static_cast<int>(fFps)), -1, iroundf(uiSize * g.s.os.fZoom * g.s.os.qrfFps.height()), g.s.os.qcFps, g.s.os.qfFps, tl); qgpiFPS->setPixmap(pm); } else { qgpiFPS->setPixmap(QPixmap()); diff --git a/src/mumble/Overlay.h b/src/mumble/Overlay.h index 13b09cdf8..2273b932c 100644 --- a/src/mumble/Overlay.h +++ b/src/mumble/Overlay.h @@ -66,6 +66,13 @@ class OverlayGroup : public QGraphicsItem { int type() const; }; +class OverlayTextLine : public QPainterPath { + public: + float fAscent, fDescent; + OverlayTextLine(); + OverlayTextLine(const QPainterPath&); +}; + class OverlayUser : public OverlayGroup { private: Q_DISABLE_COPY(OverlayUser) @@ -98,7 +105,7 @@ class OverlayUser : public OverlayGroup { int type() const; - static QPixmap createPixmap(const QString &str, unsigned int maxwidth, unsigned int height, QColor col, const QFont &font, QPainterPath &); + static QPixmap createPixmap(const QString &str, unsigned int maxwidth, unsigned int height, QColor col, const QFont &font, OverlayTextLine &); static QRectF scaledRect(const QRectF &qr, qreal scale); static QPointF alignedPosition(const QRectF &box, const QRectF &item, Qt::Alignment a); }; |