Leveraging Qt For Smooth Transition Effects

I hate the days close to exams. I am pretty annoyed with learning by then, and the times I decide to call “creative breaks” get longer and longer. Yesterday, I was looking for something that might be fun, touching an area I haven’t been touching before. Now I don’t consider myself a graphics expert. As such, I am a total n00b when it comes to nice-looking effects.

However what has always bothered me is that despite virtually anyone having a bare minimum of OpenGL support nowadays, we still don’t have OpenGL-accelerated blending or transitioning effects in KDE. Back when I was at Trolltech, Matthias voiced the same opinion. And so I decided to program something cool “when I would find some spare time”. Now, as we all know, there is no such thing as spare time. It’s merely a matter of feeling The Itch or, like in my case, feel like you need to do something funny for a change.

I think our users deserve some eye candy, so I hope a lot of people who write photo albums, etc, watch this. So I wrote a demo that leverages Qt’s OpenGL widget, good old QTimer and the rather new QTimeLine class to transiate smoothly between two images. I’d love to see something like this in Gwenview or KPhotoAlbum for instance.
(I also feel that I owe a nice QtOpenGL example to the readers of my Qt 4 book, which I am linking here purely for commercial reasons, but that’s a different story).

Anyway, highlights of the demo I came up with include:

  • extra smooth transition using EaseInOutCurve
  • toggling between fullscreen mode and window using Alt+Tab (although implemented quick and dirty)

As you dive into the source code, you will realize that there is nothing OpenGL specific about it. All it does is using plain Qt APIs. In fact, the source code allows for an easy replacement of QGLWidget with QWidget. After a recompile, the blending effect still works, although by far not as smooth on X11. This is because if you use QGLWidget, it will redirect all your Painter calls to a special paint device using a special paint engine, which translates your QPainter calls into OpenGL commands. You can, but don’t have to bother with native OpenGL calls at all.

I wish it was possible to detect a software renderer in a generic way, but I’m afraid that will only be possible with GLX specific code. That way the code would simply not use a transition on non OpenGL-accelerated hardware. Hints welcome in the comments, please prove me wrong.

4 Replies to “Leveraging Qt For Smooth Transition Effects”

  1. glGetString(GL_RENDERER). If it contains “Mesa” on Linux, you know it’s software-rendered. I’m not sure what the string is for the Windows and MacOSX software renderers, though.

  2. Pretty amazing how fast it can be using OpenGL while changing nearly nothing to the source code!

    I played a little with the code.
    Using QWidget, I get 3 to 5 frames on key_right animations.

    And then I have seen you used drawImage().
    I’m used to Qt 3, so I tryed converting images to pixmaps after each scale, and simply use drawPixmap().
    I was surprised to see no speed improvement!

    If I’m right, on Qt3, QImages are stored in aplication-memory RAM, while QPixmaps are stored in X server memory.
    So drawImage() need Qt to transform the QImage to a QPixmap and transfer it to the X server before being able to paint it.
    In Qt3, I see huge differences when painting only pixmaps when possible.

    Are Qt4 internals changed a lot?
    Is this application-side/server-side image/pixmap distinction not valid anymore?

    Anyway, thanks for this demo, I will play more with it when it’s time for me to port Kirocker Music Display.
    I’m already excied about all wonderful animations I could add to my program just by using Qt4!

  3. I can’t refrain myself from playing more on this demo!

    I tryed some transformation, especially rotation and translation.
    I came with this code:

    void TransitionWidget::paintEvent(QPaintEvent * event)
    QPainter p(this);
    // p.setRenderHint(QPainter::Antialiasing, true);
    p.setRenderHint(QPainter::TextAntialiasing, true);
    // p.setRenderHint(QPainter::SmoothPixmapTransform, true);
    // p.setRenderHint(QPainter::HighQualityAntialiasing, true);
    p.drawImage(QPoint(0,0), mLastPic);

    QTransform transform;
    //transform.translate(50, 50);
    transform.rotate(90 – mBlendFactor * 90);
    //transform.scale(mBlendFactor, mBlendFactor);

    p.drawImage(QPoint(height() – (int) (height() * mBlendFactor),0), mCurrentPic);

    Quite nice (while not very perfect) looking.
    I post this code here, because I was obliged to comment the setRenderHint() calls.
    It was too slow with them enabled.

    Is there any reason for them to be slow, even using OpenGL?
    IMHO, that’s pretty basic, considered OpenGL can render millions of polygons per second, why not anti-aliased four-polygons (2 per pixmap rectangle) 😉

    My goal was trying to get cool animations ala Keynote.
    Perhapse I’m not using the good methods.
    After all, I’m still begining with Qt 4.

  4. Please don’t forget us non-ASCII people in demos 🙂 (escpecially in demos). Here’s a patch to make the demo work with non-ASCII directory names.

    ps. I’m not a C++ nor Qt programmer but this was easy :).

    — main.cpp 2007-10-19 03:16:00.000000000 +0200
    +++ main.cpp~ 2007-10-19 03:14:33.000000000 +0200
    @@ -29,7 +29,7 @@
    return -1;
    QApplication app(argc, argv);
    – TransitionWidget w(argv[1]);
    + TransitionWidget w(QString::fromLocal8Bit(argv[1]));
    return app.exec();