Implicit sharing between QImage and QPixmap objects



  • I knew that QImage and QPixmap both implement implicit data sharing, but I thought that this only applied between

    multiple copies of objects of the same class. I had a big surprise this week when I managed to fix a long-standing

    bug in my code with the realisation that, apparently, a QImage and a QPixmap can implicitly share the same buffer.

    In an application performing live video streaming, I had the following function being called from the camera

    thread each time a frame was ready:

    void ImageDisplayArea::updatePixmap(int nx, int ny, const uchar &imagedata)
    {
        // Check that the incoming image has the right dimensions; if not then we ignore it, but 
        // remind the camera what dimension we actually want. As resizeEvent() takes care of this 
        // in any case, this is only likely to happen when streaming is started for the first time:
        if (width() != nx || height() != ny) 
        {
            emit displayAreaResized(width(), height());
            return;
        }
    
        // Create a temporary QImage using the form of constructor which takes the data and leaves
        // it in-place without copying or modifying it:
        QImage theTempImage(imagedata, width(), height(), QImage::Format_RGB32);
    
        // Now update the pixmap, deep-copying the data into the pixmap object: we wrap this step 
        // in a mutex to prevent it from happening at the same time as paintEvent() is drawing the 
        // pixmap on the screen
        QMutexLocker ml(&itsMutex);
        itsPixmap = QPixmap::fromImage(theTempImage);
    }
    

    After calling this routine to update the pixmap, the camera thread would then use QMetaObject::invokeMethod to

    fire a 'repaint' event in the GUI thread, causing the image to be updated with the new pixmap:

    void ImageDisplayArea::paintEvent(QPaintEvent *event) 
    {
        if (!itsPixmap.isNull())
        {
            // Set up the QPainter object
            QPainter painter(this);
    
            // Draw in the pixmap: we wrap this in a mutex in case the camera driver thread tries to 
            // write a new image into the pixmap at the same time:
            QMutexLocker ml(&itsMutex);
            painter.drawPixmap(0, 0, itsPixmap);
        }
    }
    

    I kept on getting access violations which I couldn't understand. Even deep-copying the 'imagedata' into a local

    QImage object within updatePixmap (using a different form of the QImage constructor from the one shown above), and

    initialising the QPixmap from this local QImage object, failed to solve them. To cut a long story short,

    eventually it dawned on me that perhaps my call to "QPixmap::fromImage(theTempImage)" hadn't caused a deep copy

    after all. I tried adding "itsPixmap.detach()" at the end of updatePixmap:

    void ImageDisplayArea::updatePixmap(int nx, int ny, const uchar &imagedata)
    {
        // Check that the incoming image has the right dimensions; if not then we ignore it, but 
        // remind the camera what dimension we actually want. As resizeEvent() takes care of this 
        // in any case, this is only likely to happen when streaming is started for the first time:
        if (width() != nx || height() != ny) 
        {
            emit displayAreaResized(width(), height());
            return;
        }
    
        // Create a temporary QImage using the form of constructor which takes the data and leaves
        // it in-place without copying or modifying it:
        QImage theTempImage(imagedata, width(), height(), QImage::Format_RGB32);
    
        // Now update the pixmap, deep-copying the data into the pixmap object: we wrap this step 
        // in a mutex to prevent it from happening at the same time as paintEvent() is drawing the 
        // pixmap on the screen
        QMutexLocker ml(&itsMutex);
        itsPixmap = QPixmap::fromImage(theTempImage);
        itsPixmap.detach();  // Necessary to decouple the QPixmap from the QImage
    }
    

    With this, my access violations are a thing of the past.

    I don't really have a question, therefore, as my problem is solved, but I record this here as a 'cautionary tale'.

    If I were to ask a question, it would be this: are there any other examples in Qt of data buffers being implicitly

    shared between two obects of different classes?


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.