Efficient off-screen rendering of QPainterPaths (OpenGL and non-OpenGL solution required)

  1. In my application, I paint a street map using QPainter on a widget
    • made by QPainterPaths that contain precalculated paths to be drawn
    • the widget is currently a QWidget, not a QGLWidget, but this might change.
  2. I'm trying to move the painting off-screen and split it into chunked jobs
    • I want to paint each chunk onto a QImage and finally draw all images onto the widget
    • QPainterPaths are already chunked, so this is not the problem
    • problem is, that drawing on QImages is about 5 times slower than drawing on QWidget
  3. Some benchmark testing I've done
    • time values are rounded averages over multiple runs
    • test chunk contains 100 QPainterPaths that have about 150 linear line segments each
    • the roughly 15k paths are drawn with QPainter::Antialiasing render hint, QPen uses round cap and round join
  4. Remember that my source are QPainterPaths (and line width + color; some drawn, some filled)
    • I don't need all the other types of drawing QPainter supports
    • Can QPainterPaths be converted to something else which can be drawn on a OpenGL buffer, this would be a good solution.
    • I'm not familiar with OpenGL off-screen rendering and I know that there are different types of OpenGL buffers, of which most of them aren't for 2D image rendering but for vertex data.
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
----------------------------------------------------+--------------------------

(*) These 2 lines have been added afterwards and are solutions to the problem!

It would be nice if you can tell me a non-OpenGL-based solution, too, as I want to compile my application in two versions: OpenGL and non-OpenGL version. Also, I want the solution to be able to render in a non-GUI thread.


Is there a good way to efficiently draw the chunks off-screen? Is there an off-screen counter part of QGLWidget (an OpenGL off-screen buffer) which can be used as a paint device for QPainter?

Answers


The document of Qt-interest Archive, August 2008 QGLContext::create()

says:

A QGLContext can only be created with a valid GL paint device, which means it needs to be bound to either a QGLWidget, QGLPixelBuffer or QPixmap when you create it. If you use a QPixmap it will give you software-only rendering, and you don't want that. A QGLFramebufferObject is not in itself a valid GL paint device, it can only be created within the context of a QGLWidget or a QGLPixelBuffer. What this means is that you need a QGLWidget or QGLPixelBuffer as the base for your QGLFramebufferObject.

As the document indicated, if you want to render in an off-screen buffer using opengl, you need QGLPixelBuffer. The code below is a very simple example which demonstrates how to use QGLPixelBuffer with 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();
}

The result of the program is a png image file as:

For non-opengl version using QPixmap, the code maybe in forms of below:

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();
}

The result of the program above is a png file looks like:

Though the code is simple, but it works, maybe you can do some changes to make it suitable for you.


Need Your Help

Naming conventions for threads?

c# java multithreading

It's helpful to name threads so one can sort out which threads are doing what for diagnostic and debugging purposes.

How to improve the use of Delphi Frames

delphi components tframe

I've used frames in Delphi for years, and they are one of the most powerful features of the VCL, but standard use of them seems to have some risk such as: