3

如果渲染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);
  }
}

这种方法的问题是,需要一个额外的层(a QPixmap),但我不能DestinationIn直接将合成模式与文本一起使用,因为画家可能已经用一些背景绘制了,然后淡化文本会淡化那个以及。有没有更好的方法?

4

1 回答 1

1

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

关于我唯一要做的就是保留drawText语义。下面的实现试图做到这一点,并通过在目标画家内的整数坐标处绘制像素图来进行优化。这是否会提高输出质量还有待观察,但不会有坏处。

如果您想将不可移植的解决方案特例化到代码中,那么值得注意的是QBackingStore::paintDevice()可能会返回一个QImage. 然后,您可以在代码中添加一个专门的路径来访问它,如果它恰好是QImage. 请注意,如果dynamic_cast<QImage*>(backingStore()->paintDevice())失败,您应该使用并恢复到使用像素图的代码。

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