我正在寻找绘图库(目前使用Qt作为UI),但我的问题如下:我需要在画布上绘制数百万的非常小的矩形,并且能够缩放画布和出。要求:它必须非常快,最好是跨平台。随着"快速"我的意思是在不到一秒的时间内绘制几百万(或更多)矩形。
到目前为止我尝试过的所有东西都太慢了。我一直在使用Qt + OpenGL,但它有点太低级了,反正很慢(可能是由于缺乏OpenGL经验)。还有qcustomplot,不幸的是太迟钝了。甚至尝试过HTML + Canvas作为实验。
对于那些想知道为什么我有这样的要求并且更好地了解我需要的人,请参阅此演示文稿:Heap Visualisation Tools(参见幻灯片10到17和28-29)。
你有什么建议?什么工具/库/框架能够绘制数百万个矩形而没有明显的性能问题?如果有足够快的JavaScript库,我甚至可以使用web。也许在引擎盖下使用LOD的东西。
答案 0 :(得分:0)
TL; DR:你应该能够在4核系统上一秒钟内绘制大约1000万个小矩形,只使用QPainter::drawRect
上的QImage
。
绘制一百万个矩形并不是什么大问题,典型的1920x1080屏幕有大约200万像素,所以绘制"小"矩形就像是单独写每对像素,或者平均每个矩形写8个字节。
在我的特定系统上,运行Qt 5.6的i5 iMac,需要约1/3秒来绘制100万个小矩形:
#include <QtWidgets>
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QImage image(1920, 1080, QImage::Format_ARGB32_Premultiplied);
QPainter p(&image);
QElapsedTimer timer;
timer.start();
int n = 0;
for (int y = 0; y < image.height(); ++y)
for (int x = 0; x < image.width(); x+=2) {
++ n;
p.drawRect(x, y, 2, 1);
}
p.end();
qDebug() << n << timer.elapsed();
}
如果您愿意,可以跨多个线程并行化绘图。
// https://github.com/KubaO/stackoverflown/tree/master/questions/qimage-rectangles-37510435
#include <QtWidgets>
#include <QtConcurrent>
QVector<QRect> rects(const QSize & size) {
QVector<QRect> rs;
for (int y = 0; y < size.height(); ++y)
for (int x = 0; x < size.width(); x+=2)
rs.append(QRect(x, y, 2, 1));
return rs;
}
QImage render(const QVector<QRect> & rects, const QSize & size, const QPair<int,int> range)
{
QImage image(size, QImage::Format_ARGB32_Premultiplied);
image.fill(Qt::transparent);
QPainter p(&image);
QElapsedTimer timer;
timer.start();
const int n = range.second-range.first;
for (int i = range.first; i < range.second; ++i)
p.drawRect(rects[i]);
p.end();
qDebug() << n << timer.elapsed();
return image;
}
struct Render {
const QVector<QRect> & rects;
const QSize & size;
typedef QImage result_type;
QImage operator()(const QPair<int,int> range) { return render(rects, size, range); }
Render(QVector<QRect>& rects, const QSize& size) : rects(rects), size(size) {}
};
template <typename Seq>
QVector<QPair<int,int>> partition(const Seq & s, int n)
{
QVector<QPair<int,int>> ps;
ps.reserve(n);
int begin = 0;
for (int i = 0; i < n; ++i) {
int end = (s.count() * (i+1))/n;
ps.append(qMakePair(begin, end));
begin = end;
}
return ps;
}
void combine(QImage & result, const QImage & source)
{
if (result.isNull()) {
result = source;
return;
}
QPainter p(&result);
p.drawImage(0, 0, source);
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
QSize size{1920, 1080};
auto rs = rects(size);
auto ranges = partition(rs, QThread::idealThreadCount());
QElapsedTimer t;
t.start();
QtConcurrent::blockingMappedReduced(ranges, Render(rs, size), combine);
qDebug() << "parallel time" << t.elapsed() << "ms";
t.restart();
render(rs, size, qMakePair(0, rs.count()));
qDebug() << "serial time" << t.elapsed() << "ms";
}
输出:
259200 94
259200 97
259200 102
259200 102
parallel time 112 ms
1036800 360
serial time 362 ms
如果你可以将矩形分组到具有相同笔/笔的组中,那么请利用drawRects
比重复调用drawRect
更快的事实(在我的机器上快约20%)。
您也可以自己实现一个未经转化的drawRect
,并使其更快。