快速绘制数千个矩形

Drawing thousands of rects quickly

在QT中画几千rects的正确方法是什么(大约100,000或更多)?

我试过了:

每次绘图都非常慢,我没有更多关于如何做到这一点的想法(也许 opengl/directx 但这听起来不是个好主意)。我知道存在这样做的应用程序,所以应该有一些方法。

编辑:

我想知道 drawRects() 是如何工作的?是否有可能填充一些 uchar* 数组并将其传递给 QImage 会更好?

第一个技巧是在单独的线程中将绘图绘制到 QImage 上,然后将其传递到主线程中。这不会使其更快,但不会阻塞 GUI 线程。

// https://github.com/KubaO/Whosebugn/tree/master/questions/threaded-paint-36748972
#include <QtWidgets>
#include <QtConcurrent>

class Widget : public QWidget {
   Q_OBJECT
   QImage m_image;
   bool m_pendingRender { false };
   Q_SIGNAL void hasNewRender(const QImage &);
   // Must be thread-safe, we can't access the widget directly!
   void paint() {
      QImage image{2048, 2048, QImage::Format_ARGB32_Premultiplied};
      image.fill(Qt::white);
      QPainter p(&image);
      for (int i = 0; i < 100000; ++i) {
         QColor c{rand() % 256, rand() % 256, rand() % 256};
         p.setBrush(c);
         p.setPen(c);
         p.drawRect(rand() % 2048, rand() % 2048, rand() % 100, rand() % 100);
      }
      emit hasNewRender(image);
   }
   void paintEvent(QPaintEvent *) {
      QPainter p(this);
      p.drawImage(0, 0, m_image);
   }
public:
   Widget(QWidget * parent = 0) : QWidget(parent) {
      this->setAttribute(Qt::WA_OpaquePaintEvent);
      setMinimumSize(200, 200);
      connect(this, &Widget::hasNewRender, this, [this](const QImage & img) {
         m_image = img;
         m_pendingRender = false;
         update();
      });
      refresh();
   }
   Q_SLOT void refresh() {
      if (!m_pendingRender) {
         m_pendingRender = true;
         QtConcurrent::run([this] { paint(); });
      }
   }
};

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   Widget w;
   QPushButton button{"Refresh", &w};
   button.connect(&button, &QPushButton::clicked, &w, [&w]{ w.refresh(); });
   w.show();
   return app.exec();
}

#include "main.moc"

作为一个单独的问题,您可以将绘图拆分到多个并行作业中,方法是将每个作业的画家剪裁到共享图像的一个子区域,并注意完全剪裁的矩形绘图是空操作,部分剪裁的只会填充它们影响的像素。

我找到的解决方案:

创建 uint32_t 的数组,其中可以包含 widget 的所有像素,使用 memcpy() 填充它。使用此数组创建 QImage 并使用 drawImage() 显示它。 它可以有一些优化,比如(对于探查器)合并 rects 继续(开始时间第二等于第一结束)。不要绘制超出时间范围的 rects。可能跳过太小 rects。 对于绘制文本、工具提示等内容,您仍然可以使用 Qt 函数。 对于最简单情况下的 alpha 混合,您可以只获取现有值,将它们混合在循环中并写入混合值,或者为此使用 SIMD

当然,对于更复杂的形状,它会变得更难绘制,但我认为,它仍然比使用 Qt 函数更快。