Development/Tutorials/Plasma4/WallpaperDoubleBuffer

    From KDE TechBase

    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.