You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QPainter绘制线宽大于1的曲线时耗时骤增如何优化

问题现象

我以绘制正弦波作为测试场景,分别使用QPainter::drawPolyline()与QPainter::drawPath()接口统计曲线绘制耗时。测试发现无论采用哪种绘制接口,只要将画笔宽度设置为大于1,正弦波的绘制耗时就会增长10倍以上。

测试代码如下:

Widget::Widget(QWidget* parent)
    : QWidget(parent)
{
    setAutoFillBackground(true);
    setAttribute(Qt::WA_NoSystemBackground, true);
    
    // 生成正弦波数据
    for(qsizetype ii = 0; ii < 1000000; ++ii)
        sineWave << (1.0 + sin(2 * M_PI * 3 * ii / 1000000));
}

void Widget::paintEvent(QPaintEvent* evt)
{
    QPainter painter(this);
    painter.fillRect(rect(), QColor(Qt::white));
    painter.setRenderHint(QPainter::Antialiasing);

    QPen pen;
    pen.setWidthF(2); // 设置画笔宽度
    painter.setPen(pen);

    QElapsedTimer timer;
    timer.start();

    qDebug() << "-> Paint Event\tPen Width:" << pen.widthF() << "\tWidget WxH:" << width() << "x" << height();

    QList<QPointF> pixPoints;
    for(qsizetype ii = 0; ii < sineWave.length(); ++ii) {
        double yPos = height() * (sineWave[ii] / 2.0);
        double xPos = width() * (ii / 1000000.0);
        pixPoints << QPointF(xPos, yPos);
    }
    qDebug() << "--> 1: Time [ms]:" << (timer.nsecsElapsed() / 1000000.0);
    painter.drawPolyline(pixPoints);
    qDebug() << "--> 2: Time [ms]:" << (timer.nsecsElapsed() / 1000000.0);
}
  • 笔宽设置为2时的运行输出:
-> Paint Event  Pen Width: 2    Widget WxH: 722 x 494
--> 1: Time [ms]: 109.352
--> 2: Time [ms]: 5887.08
  • 笔宽设置为1时的运行输出:
-> Paint Event  Pen Width: 1    Widget WxH: 741 x 535
--> 1: Time [ms]: 105.882
--> 2: Time [ms]: 233.165

两种测试场景下,创建QList<QPointF> pixPoints的耗时基本相近(日志标记为1:的阶段),但调用QPainter::drawPolyline()执行曲线绘制的阶段(日志标记为2:的阶段),笔宽为2时的绘制耗时远高于笔宽为1的场景。

原因说明

这个性能差距是Qt默认光栅绘制引擎的固有机制导致的,和调用哪个绘制接口无关:

  • 笔宽为1时,引擎走专门优化的单像素线段快速绘制路径,只需要沿路径逐像素填色,不需要额外计算线条轮廓
  • 笔宽大于1时,引擎会把每一段折线转换成对应宽度的闭合多边形,再做光栅化填充;开启抗锯齿后还要额外计算边缘的透明度混合。测试用例中用了100万个点生成折线,相当于要处理近100万个多边形的生成与填充,耗时自然会暴涨十几倍。
优化方案

按优化收益从高到低排序:

  • 点抽稀(收益最高):测试中控件宽度仅700余像素,100万个点意味着平均每个X像素位置堆叠1400多个点,绝大多数点对最终显示效果没有任何贡献。按照控件实际像素宽度抽稀,每像素保留1-2个点即可,700宽度的控件最多留1500个点,绘制耗时会直接降到几毫秒级别,肉眼完全看不出和100万点绘制效果的差异。
  • 静态内容缓存:正弦波属于固定不变的静态内容,不需要每次触发paintEvent都重新绘制。首次绘制时将整个曲线渲染到和控件等尺寸的QPixmap缓存上,后续paintEvent直接将缓存的pixmap绘制到界面即可,无论线宽多少,后续刷新耗时都在毫秒级。
  • 移出paintEvent中的重复计算:当前代码每次paintEvent都循环100万次计算点坐标,这部分本身就占了100ms以上耗时。将坐标计算逻辑移到初始化函数、或者resizeEvent中,控件尺寸不变时直接复用预计算好的点列表,避免无意义的重复计算。
  • 细节调优:
    • 若对绘制精度要求不高,关闭QPainter::Antialiasing抗锯齿选项,宽线绘制速度可提升3倍以上
    • 将画笔端点样式设为Qt::FlatCap、连接样式设为Qt::MiterJoin,相比圆角端点、圆角连接样式能减少不少轮廓生成的计算量
    • 若需要绘制高频刷新的动态曲线,可切换到QOpenGLWidget使用GPU绘制,线宽带来的性能差距会被GPU并行计算能力大幅缩小。

内容的提问来源于stack exchange,提问作者Chaitanya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 22:15:48