QPainterPaths的高效离屏渲染(需要OpenGL和非OpenGL解决方案)

45
  1. 我的应用程序使用QPainter在一个widget上绘制街道地图.
    • 这个widget是由QPainterPaths创建的,其中包含了要绘制的预计算路径
    • 当前的widget是一个QWidget,而不是QGLWidget,但这可能会改变。
  2. 我试图将绘画移出屏幕并分成分块作业。
    • 我想将每个块绘制到QImage上,最后将所有图像绘制到widget
    • QPainterPaths已经被分块了,所以这不是问题
    • 问题是,在QImages上绘制比在QWidget上绘制慢5倍
  3. 一些基准测试
    • 时间值是多次运行的平均值四舍五入而来
    • 测试块包含100个QPainterPaths,每个路径大约有150个线性线段
    • 大约15k条路径使用QPainter::Antialiasing渲染提示进行绘制,QPen使用圆形端点和圆形连接。
  4. 请记住,我的源是QPainterPaths(以及线宽和颜色;一些绘制,一些填充)
    • 我不需要QPainter支持的所有其他类型的绘图
    • 是否可以将QPainterPaths转换为其他可在OpenGL buffer上绘制的东西,这将是一个好的解决方案。
    • 我不熟悉OpenGL离屏渲染,我知道有不同类型的OpenGL buffers,其中大多数不是用于2D图像渲染而是用于顶点数据。
Paint Device for chunk | Rendering the chunk itself | Painting chunk on QWidget
-----------------------+----------------------------+--------------------------
QImage                 |                    2000 ms |                   < 10 ms
QPixmap (*)            |                     250 ms |                   < 10 ms
QGLFramebufferObj. (*) |                      50 ms |                   < 10 ms
QPicture               |                      50 ms |                    400 ms
-----------------------+----------------------------+--------------------------
none (directly on a QWidget in paintEvent)          |                    400 ms
----------------------------------------------------+--------------------------

(*) 这两行是后来添加的,是解决问题的解决方案!

如果可以的话,最好能告诉我一个非基于OpenGL的解决方案,因为我想编译出两个版本的应用程序:OpenGL非OpenGL版本。
此外,我希望这个解决方案能够在非GUI线程中进行渲染。


有没有一种有效的方法可以将区块高效地绘制到屏幕之外?
是否存在与QGLWidget(一个OpenGL屏幕外缓冲区)对应的屏幕外版本,可以作为QPainter的绘制设备使用?


1
QPicture 不是一个好的选择吗? - pearcoding
1
我将测试QPicture的性能,并通过一些基准测试扩展我的问题:QImage vs QPicture vs QWidget :: paintEvent。 - leemes
8
如果您不需要像素级别的访问,那么QPixmap是优选的图像类,而QPicture则不是您想要的。然而,如果您希望在非GUI线程中进行渲染,则需要使用QImage。对于OpenGL,您可以尝试使用QGLFrameBufferObject。 - Dan Milburn
2
太好了!我已经将它运作起来了。我建议你,@DanMilburn,写一个回答,建议使用QGLFramebufferObject来解决OpenGL问题,并使用QPixmap来解决非OpenGL问题。由于你的评论引导我朝着正确的方向前进,我会接受它的。 - leemes
@DanMilburn,你不想回答这个问题吗?我会接受它的 ;) - leemes
显示剩余7条评论
1个回答

5
Qt-interest Archive, August 2008 QGLContext::create()文件中提到:
一个 QGLContext 只能在有效的 GL paint device 的情况下创建,这意味着当您创建它时,它需要绑定到 QGLWidget、QGLPixelBuffer 或 QPixmap 中的一个。如果您使用 QPixmap,则会给出软件渲染,而您不希望出现这种情况。 QGLFramebufferObject 本身并不是一个有效的 GL paint device,它只能在 QGLWidget 或 QGLPixelBuffer 上下文中创建。这意味着您需要一个 QGLWidget 或 QGLPixelBuffer 作为基础来创建 QGLFramebufferObject。
正如文档所述,如果您想要使用OpenGL在离屏缓冲区中进行渲染,您需要使用 QGLPixelBuffer。以下代码是一个非常简单的示例,演示了如何使用 QGLPixelBuffer 和 OpenGL:
#include <QtGui/QApplication>
#include <Windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#include <QtOpenGL/QGLFormat>
#include <QtOpenGL/QGLPixelBuffer>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // Construct an OpenGL pixel buffer.
    QGLPixelBuffer glPixBuf(100, 100); 
    // Make the QGLContext object bound to pixel buffer the current context
    glPixBuf.makeCurrent();
    // The opengl commands 
    glClearColor(1.0, 1.0, 1.0, 0.0);
    glViewport(0, 0, 100, 100);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, 100, 0, 100);
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 0.0, 0.0);
    glPointSize(4.0);
    glBegin(GL_TRIANGLES);
    glVertex2i(10, 10);
    glVertex2i(50, 50);
    glVertex2i(25, 75);
    glEnd();
    // At last, the pixel buffer was saved as an image
    QImage &pImage = glPixBuf.toImage();
    pImage.save(QString::fromLocal8Bit("gl.png"));

    return a.exec();
}

该程序的结果是一个png图像文件,如下所示:

enter image description here

如果使用QPixmap的非OpenGL版本,则代码可能如下所示:
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QPixmap pixmap(100, 100);
    QPainter painter;
    painter.begin(&pixmap);
    painter.drawText(10, 45, QString::fromLocal8Bit("I love American."));
    painter.end();
    pixmap.save(QString::fromLocal8Bit("pixmap.png"));

    return a.exec();
}

以上程序的结果是一个PNG文件,看起来像这样:enter image description here 虽然代码很简单,但它起作用了,也许你可以做一些改变使其适合你的需求。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接