Drawing thousands of rects quickly

久未见 提交于 2019-12-08 11:28:05

问题


What is the proper way to draw thousands of rects in QT (around 100,000 or more)?

I tried:

  • Simple with paintEvent() of QWidget.
  • Drawing objects to QImage then this image to QWidget.
  • Using QGraphicsScene (maybe I didn't use it properly, I just added rects to scene)

Every time drawing was really slow and I don't have more ideas on how to do this (maybe with opengl/directx but this doesn't sound like a good idea). I know that there exist applications that do that so there should be some way.

EDIT:

I wonder how drawRects() work? Is there a chance that filling some uchar* array and passing it to QImage will be better?


回答1:


The first trick is to do the drawing in a separate thread onto a QImage, then pass that into the main thread. This won't make it quicker, but it'll make it not block the GUI thread.

// https://github.com/KubaO/stackoverflown/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"

As a separate concern, you can then split the drawing across multiple parallel jobs, by clipping each job's painter to a sub-area of the shared image, and noting that fully clipped rectangle draws are no-ops, and partially clipped ones will only fill the pixels they affect.




回答2:


Solution which I found:

Create array of uint32_t which can contain all pixels of widget, fill it using memcpy(). Create QImage with this array and use drawImage() to show it. It can have some optimization like (for profiler) merging rects that are continues ( start time second is equal to end of first ). Don't draw rects that are out of time bounds. Maybe skip too small rects. For drawing things like text, tool tips you can still use Qt functions. For alpha blending in simplest case you can just take existing values, blend them in loop and write blended values or maybe use SIMD for this.

Of course for more complex shapes it will get harder to draw but still, I think, it will be faster than using Qt functions.



来源:https://stackoverflow.com/questions/36748972/drawing-thousands-of-rects-quickly

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!