【问题标题】:right-faded text optimization右淡化文本优化
【发布时间】:2013-06-05 07:33:09
【问题描述】:

如果渲染的QString s 超过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 与文本,因为画家可能已经用一些背景和淡化文本也会随之淡化。有没有更好的办法?

【问题讨论】:

  • inline这个方法毫无意义。与函数调用开销相比,您在其中进行的调用将花费相当长的时间。除非您有测量结果表明它可以改善事情,否则不要内联。

标签: c++ qt optimization


【解决方案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();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多