2013-06-05 67 views
3

如果渲染的QStrings超過rect.width(),我想繪製褪色文字(向右)。目前我做的:右褪色文字優化

inline void drawFadedLineText(QPainter* const painter, QRectF const& rect, 
    QColor const& color, QString const& s) 
{ 
    painter->setPen(color); 

    auto const fontMetrics(painter->fontMetrics()); 

    if (fontMetrics.width(s) > rect.width()) 
    { 
    QPixmap pixmap(rect.size().toSize()); 

    pixmap.fill(Qt::transparent); 

    { 
     QPainter p(&pixmap); 

     p.setFont(painter->font()); 

     p.drawText(0, fontMetrics.ascent(), s); 

     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))}); 
     } 
     // else do nothing 

     static auto const margin(qreal(10)); 

     auto const right(rect.width()); 

     lg.setStart(right - margin, qreal(0)); 
     lg.setFinalStop(right, qreal(0)); 

     p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 

     p.fillRect(QRectF(right - margin, 0, margin, rect.height()), lg); 
    } 

    painter->drawPixmap(rect.topLeft(), pixmap); 
    } 
    else 
    { 
    painter->drawText(QPointF(rect.left(), 
     rect.top() + fontMetrics.ascent()), s); 
    } 
} 

這種方法的問題是,需要一個額外層(QPixmap),但我不能使用的組成模式DestinationIn與文本直接作爲畫家可能已經被一些背景所吸引,並且隨着文本的消逝,文本也會隨之消失。有沒有更好的方法?

+0

這種方法沒有意義。與函數調用開銷相比,您在內部調用需要相當長的時間。除非您有測量結果顯示它改善了事情,否則不要內聯。 –

回答

1

我不認爲有可能跳過中間像素圖而不必多次繪製文本。我希望像素圖比雙重渲染文本效率更高。

我要做的唯一事情就是保留drawText語義。下面的實現試圖做到這一點,並通過在目標畫家內的整體座標處繪製像素圖來進行優化。是否可以看到這是否提高了產出的質量,但它不會受到傷害。

如果您想在代碼中使用非便攜式解決方案,那麼值得注意的是QBackingStore::paintDevice() may happen to return a QImage。然後你可以在代碼中添加一個專門的路徑來訪問它,如果它碰巧是QImage。請注意,您應該使用dynamic_cast<QImage*>(backingStore()->paintDevice())並恢復使用失敗時使用像素映射的代碼。

處理它的另一種方法是將整個小部件繪製成一個像素圖,並且只在最後在小部件上繪製該像素圖。如果後備存儲恰好是映像,這可以進一步優化。如果您必須對已繪製的內容進行多次訪問,那麼這會給您額外的靈活性。對於後備商店的油漆設備不是QImage的情況,您可能需要關閉QWindow的雙緩衝,並進一步規定小部件不要閃爍,儘管沒有雙緩衝。

#include <QApplication> 
#include <QFontMetrics> 
#include <QPainter> 
#include <QWidget> 
#include <cmath> 

//! Fractional part of the argument, with the same sign as the argument. 
template <typename T> inline T fract(const T & x) { return x-trunc(x); } 
//! A rectangle moved to the fractional part of the original topLeft() 
template <> inline QRectF fract(const QRectF & r) { return QRectF(fract(r.x()), fract(r.y()), r.width(), r.height()); } 
//! An integral size that contains the size of a given rectangle. 
static QSize ceil(const QRectF & r) { return QSize(ceil(r.width()), ceil(r.height())); } 
//! An integral point obtained by rounding `p` towards zero. 
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); 
    } 
    // The semantics of `rect` and `s` are the same as in the call to `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(); 
}