From 15f20961c7ce5ab434874027e2eadfed8d4ae82a Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sun, 22 Aug 2021 19:48:49 +0200 Subject: [PATCH] profile: render DiveTextItem onto a pixmap The DiveTextItems were redrawn on every paint() call. This was a prohibitively expensive operation (converting the text into a path, drawing an outline, etc.), which was called numerous times. Instead, render the text only when changing into a QPixmap and blit that pixmap in the paint() call. This will make it possible to do absolutely positioned DiveTextItems. So far they were placed relatively in scene coordinates ranging from 0-100(!). Signed-off-by: Berthold Stoeger --- profile-widget/divetextitem.cpp | 86 ++++++++++++++++----------------- profile-widget/divetextitem.h | 11 ++--- 2 files changed, 48 insertions(+), 49 deletions(-) diff --git a/profile-widget/divetextitem.cpp b/profile-widget/divetextitem.cpp index b8f03d794..6e6f97e95 100644 --- a/profile-widget/divetextitem.cpp +++ b/profile-widget/divetextitem.cpp @@ -5,33 +5,60 @@ #include "core/errorhelper.h" #include -#include #include -DiveTextItem::DiveTextItem(double dpr, double scale, int alignFlags, QGraphicsItem *parent) : QGraphicsItemGroup(parent), +static const double outlineSize = 3.0; + +DiveTextItem::DiveTextItem(double dpr, double scale, int alignFlags, QGraphicsItem *parent) : QGraphicsPixmapItem(parent), internalAlignFlags(alignFlags), - textBackgroundItem(new QGraphicsPathItem(this)), - textItem(new QGraphicsPathItem(this)), dpr(dpr), scale(scale) { setFlag(ItemIgnoresTransformations); - textBackgroundItem->setBrush(QBrush(getColor(TEXT_BACKGROUND))); - textBackgroundItem->setPen(Qt::NoPen); - textItem->setPen(Qt::NoPen); -} - -void DiveTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) -{ - updateText(); - QGraphicsItemGroup::paint(painter, option, widget); } void DiveTextItem::set(const QString &t, const QBrush &b) { - textItem->setBrush(b); internalText = t; - updateText(); + if (internalText.isEmpty()) { + setPixmap(QPixmap()); + return; + } + + QFont fnt = getFont(dpr, scale); + + QPainterPath textPath; + textPath.addText(0.0, 0.0, fnt, internalText); + QPainterPathStroker stroker; + stroker.setWidth(outlineSize * dpr); + QPainterPath outlinePath = stroker.createStroke(textPath); + + QRectF outlineRect = outlinePath.boundingRect(); + textPath.translate(-outlineRect.topLeft()); + outlinePath.translate(-outlineRect.topLeft()); + + QPixmap pixmap(outlineRect.size().toSize()); + pixmap.fill(Qt::transparent); + { + QPainter painter(&pixmap); + painter.setRenderHints(QPainter::Antialiasing); + painter.setBrush(QBrush(getColor(TEXT_BACKGROUND))); + painter.setPen(Qt::NoPen); + painter.drawPath(outlinePath); + painter.setBrush(b); + painter.setPen(Qt::NoPen); + painter.drawPath(textPath); + } + setPixmap(pixmap); + + double yOffset = (internalAlignFlags & Qt::AlignTop) ? 0.0 : + (internalAlignFlags & Qt::AlignBottom) ? -outlineRect.height() : + /*(internalAlignFlags & Qt::AlignVCenter ? */ -outlineRect.height() / 2.0; + + double xOffset = (internalAlignFlags & Qt::AlignLeft) ? -outlineRect.width() : + (internalAlignFlags & Qt::AlignHCenter) ? -outlineRect.width() / 2.0 : + /* (internalAlignFlags & Qt::AlignRight) */ 0.0; + setOffset(xOffset, yOffset); } const QString &DiveTextItem::text() @@ -64,32 +91,5 @@ double DiveTextItem::fontHeight(double dpr, double scale) double DiveTextItem::height() const { - return fontHeight(dpr, scale); -} - -void DiveTextItem::updateText() -{ - if (internalText.isEmpty()) - return; - - QFont fnt = getFont(dpr, scale); - QFontMetrics fm(fnt); - - QPainterPath textPath; - qreal xPos = 0, yPos = 0; - - QRectF rect = fm.boundingRect(internalText); - yPos = (internalAlignFlags & Qt::AlignTop) ? 0 : - (internalAlignFlags & Qt::AlignBottom) ? +rect.height() : - /*(internalAlignFlags & Qt::AlignVCenter ? */ +rect.height() / 4; - - xPos = (internalAlignFlags & Qt::AlignLeft) ? -rect.width() : - (internalAlignFlags & Qt::AlignHCenter) ? -rect.width() / 2 : - /* (internalAlignFlags & Qt::AlignRight) */ 0; - - textPath.addText(xPos, yPos, fnt, internalText); - QPainterPathStroker stroker; - stroker.setWidth(3); - textBackgroundItem->setPath(stroker.createStroke(textPath)); - textItem->setPath(textPath); + return fontHeight(dpr, scale) + outlineSize * dpr; } diff --git a/profile-widget/divetextitem.h b/profile-widget/divetextitem.h index 28bafe257..cb7d054b1 100644 --- a/profile-widget/divetextitem.h +++ b/profile-widget/divetextitem.h @@ -4,29 +4,28 @@ #include #include -#include +#include class QBrush; /* A Line Item that has animated-properties. */ -class DiveTextItem : public QObject, public QGraphicsItemGroup { +class DiveTextItem : public QObject, public QGraphicsPixmapItem { Q_OBJECT Q_PROPERTY(QPointF pos READ pos WRITE setPos) Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) public: + // Note: vertical centring is based on the actual rendered text, not on the font metrics. + // This is fine for placing text in the "tankbar", but it will look disastrous when + // placing text items next to each other. This may have to be fixed. DiveTextItem(double dpr, double scale, int alignFlags, QGraphicsItem *parent); void set(const QString &text, const QBrush &brush); const QString &text(); - void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); static QFont getFont(double dpr, double scale); static double fontHeight(double dpr, double scale); double height() const; private: - void updateText(); int internalAlignFlags; - QGraphicsPathItem *textBackgroundItem; - QGraphicsPathItem *textItem; QString internalText; double dpr; double scale;