右褪色文本优化

时间:2013-06-05 07:33:10

标签: c++ qt optimization

如果呈现的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和画面,因为画家可能已经用一些背景绘制并褪色文本然后会消失。有没有更好的方法?

1 个答案:

答案 0 :(得分:1)

我认为不必跳过中间像素图而不必多次绘制文本。我希望pixmap比文本的双重渲染更有效。

关于我唯一要做的就是保留drawText语义。下面的实现尝试通过在目标画家中的整体坐标处绘制像素图来进行优化并进行优化。这是否会改善输出质量,但它不会受到伤害。

如果你想在代码中使用非便携式解决方案,那么值得注意的是QBackingStore::paintDevice() may happen to return a QImage。然后,您可以在代码中添加专门的路径来访问它,如果它恰好是QImage。请注意,您应该使用dynamic_cast<QImage*>(backingStore()->paintDevice())并恢复使用pixmap的代码失败。

处理它的另一种方法是将整个小部件绘制成像素图,并且仅在最后在小部件上绘制像素图。如果后备存储恰好是图像,则可以进一步优化这一点。如果您必须多次访问绘制到目前为止的内容,那么这将为您提供额外的灵活性。对于后备商店的绘图设备不是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();
}