Ich glaube nicht, dass es möglich ist, den Zwischenpixmap zu überspringen, ohne den Text mehrmals zeichnen zu müssen. Ich hoffe, dass ein Pixmap viel effizienter ist als das zweifache Rendern des Textes.
Das Einzige, was ich tun würde, ist die Semantik von drawText
beizubehalten. Die folgende Implementierung versucht dies zu tun und zu optimieren, indem das Pixmap an ganzzahligen Koordinaten innerhalb des Ziel-Painter gezeichnet wird. Ob dies die Qualität der Ausgabe verbessert, bleibt abzuwarten, aber schaden kann es nicht.
Wenn Sie eine nicht-portable Lösung in den Code einbauen möchten, dann ist es erwähnenswert, dass QBackingStore::paintDevice()
möglicherweise ein QImage
zurückgeben kann. Sie könnten dann einen spezialisierten Pfad in Ihrem Code hinzufügen, um darauf zuzugreifen, falls es sich um ein QImage
handelt. Beachten Sie, dass Sie dynamic_cast(backingStore()->paintDevice())
verwenden sollten und auf den Code zurückgreifen sollten, der ein Pixmap verwendet, falls dies fehlschlägt.
Ein anderer Umgang damit wäre, das gesamte Widget auf ein Pixmap zu zeichnen und erst am Ende dieses PixMap auf das Widget zu zeichnen. Dies kann weiter optimiert werden, wenn der Backing Store zufällig ein Bild ist. Das würde Ihnen zusätzliche Flexibilität geben, wenn Sie mehrfachen Zugriff auf den bisher gezeichneten Inhalt haben müssen. Für Fälle, in denen das Paint Device des Backing Stores kein QImage
ist, möchten Sie wahrscheinlich das Double Buffering für das QWindow
deaktivieren und weitere Vorkehrungen treffen, damit die Widgets trotz fehlendem Double Buffering nicht flackern.
#include
#include
#include
#include
#include
//! Der Bruchteil des Arguments, mit dem gleichen Vorzeichen wie das Argument.
template inline T fract(const T & x) { return x-trunc(x); }
//! Ein Rechteck, das in den Bruchteil des ursprünglichen topLeft() verschoben wurde
template <> inline QRectF fract(const QRectF & r) { return QRectF(fract(r.x()), fract(r.y()), r.width(), r.height()); }
//! Eine ganze Zahl, die die Größe eines gegebenen Rechtecks enthält.
static QSize ceil(const QRectF & r) { return QSize(ceil(r.width()), ceil(r.height())); }
//! Ein ganzzahliger Punkt, der durch Runden von `p` in Richtung Null erhalten wird.
static QPoint truncint(const QPointF & p) { return QPoint(trunc(p.x()), trunc(p.y())); }
class Widget : public QWidget {
void paintEvent(QPaintEvent *) {
static auto const text(QString(300, 'm'));
QPainter p(this);
p.setBrush(Qt::black);
p.drawRect(rect());
p.setPen(Qt::white);
drawFadedLineText(&p, rect(), text);
}
// Die Semantik von `rect` und `s` sind die gleichen wie beim Aufruf von `drawText(rect, s)`;
void drawFadedLineText(QPainter* const painter, const QRectF & rect, const QString & s)
{
auto const fontMetrics(painter->fontMetrics());
if (fontMetrics.width(s) <= rect.width()) {
painter->drawText(rect, s);
return;
}
static QLinearGradient lg;
static bool init;
if (!init) {
init = true;
lg.setStops(QGradientStops{
qMakePair(qreal(0), QColor(0, 0, 0, 238)),
qMakePair(qreal(1), QColor(0, 0, 0, 17))});
}
auto const margin(qreal(20.0));
auto pixRect(fract(rect));
auto const right(pixRect.right());
lg.setStart(right - margin, 0.0);
lg.setFinalStop(right, 0.0);
QPixmap pixmap(ceil(rect));
pixmap.fill(Qt::transparent);
QPainter p(&pixmap);
p.setPen(painter->pen());
p.setFont(painter->font());
p.drawText(pixRect, s);
p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
p.fillRect(QRectF(right - margin, 0, margin, pixmap.rect().height()), lg);
p.end();
painter->drawPixmap(truncint(rect.topLeft()), pixmap);
}
public:
Widget() { setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_StaticContents); }
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}