Development/Tutorials/Plasma4/WallpaperDoubleBuffer

From KDE TechBase
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Plasma Wallpaper Tutorial 3 - Double Buffer

Introduction

So far we have ignored the exposedRect argument of our paint function. Whenever the paint function was called we redrew the entire wallpaper even when only a small region needed repainting. As you can imagine this is not very efficient so let's improve our wallpaper by using a so-called 'double buffer' technique.

Adding the Buffer

The way this double buffer technique works is by rendering our wallpaper to a pixmap in memory, the buffer, instead of rendering it directly to the screen. Whenever a region on the screen needs repainting we can just copy the corresponding region from the buffer to the screen.

First we'll need to add the buffer to our private variables and write a new function for rendering the buffer. Add the following lines to our tutorial.h:

#ifndef PLASMA_WALLPAPER_TUTORIAL
#define PLASMA_WALLPAPER_TUTORIAL

#include <Plasma/Wallpaper>
#include "ui_config.h"

class Tutorial : public Plasma::Wallpaper
{
    Q_OBJECT
    public:
        Tutorial(QObject* parent, const QVariantList& args);

        virtual void save(KConfigGroup &config);
        void paint(QPainter* painter, const QRectF& exposedRect);
        virtual QWidget* createConfigurationInterface(QWidget* parent);

    Q_SIGNALS:
        void settingsChanged(bool modified);

    protected:
        virtual void init(const KConfigGroup &config);

    protected slots:
        void settingsModified();

    private:
        Ui::Config configWidget;
        QColor backgroundColor;
        QColor textColor;
        QFont textFont;
        QString textString;
        QPixmap buffer;                         // New
        void render();                          // New
};

K_EXPORT_PLASMA_WALLPAPER(tutorial, Tutorial)

#endif

Now we will implement render() in our tutorial.cpp. This function is largely identical to the old paint function, except it will draw to our newly created buffer.

void Tutorial::render()
{
    if(buffer.size() != boundingRect().size())
    {
        buffer = QPixmap(boundingRect().size().toSize());
    }

    QPainter painter;
    painter.begin(&buffer);
	
    painter.setPen(backgroundColor);
    painter.setBrush(backgroundColor);
    painter.drawRect(boundingRect());
 
    painter.setPen(textColor);
    painter.setFont(textFont);
    painter.drawText(boundingRect(), Qt::AlignCenter, textString);
}

If the buffer has the wrong size or if the buffer doesn't exist yet (size = 0px*0px) we create a new QPixmap with the same size as our wallpaper. We then associate a QPainter object with this buffer and draw with the QPainter as before.

Using the Buffer

We will now need to change the paint function to make use of our buffer.

void Tutorial::paint(QPainter *painter, const QRectF& exposedRect)
{
    if(buffer.size() != boundingRect().size())
    {
        render();
    }
	
    painter->drawPixmap(exposedRect, buffer, exposedRect);
}

If the buffer has the wrong size or if the buffer doesn't exist yet (size = 0px*0px) we call our render function to fill the buffer with the correct content. When our buffer is up-to-date we copy the exposedRect region from the buffer to the painter.

We'll also need to update the buffer, by calling our render function, whenever the content of the wallpaper changes. Add the following lines to the init and settingsModified functions in our tutorial.cpp:

void Tutorial::init(const KConfigGroup &config)
{
    backgroundColor = config.readEntry("backgroundColor", QColor(Qt::white));
    textColor = config.readEntry("textColor", QColor(Qt::black));
    textFont = config.readEntry("textFont", QFont("Arial", 50));
    textString = config.readEntry("textString", QString("Hello World!"));
 
    render();                                             // New
    emit update(boundingRect());
}


void Tutorial::settingsModified()
{
    textString = configWidget.textString->text();
    textFont = configWidget.textFont->font();
    textColor = configWidget.textColor->color();
    backgroundColor = configWidget.backgroundColor->color();
 
    render();                                             // New
    emit settingsChanged(true);
    emit update(boundingRect());
}

Compiling

To compile we follow the same steps as before:

mkdir -p build
cd build
cmake .. -DCMAKE_INSTALL_PREFIX=`kde4-config --prefix`
make
sudo make install

Testing

Again we can test using the Plasma wallpaper viewer:

plasmawallpaperviewer -p tutorial

Or we can test it directly on our desktop. Restart plasma using:

kquitapp plasma-desktop; sleep 1; plasma-desktop

Then right click on your desktop, go to Desktop Settings>Wallpaper>Type and select 'Tutorial'.

Conclusion

By adding a buffer to our wallpaper plugin we can make it redraw only the parts that need to be redrawn.