Search code examples
c++qtdereferenceqpixmap

Does dereferencing a pointer create a copy in this example?


I'm attempting to optimize some code because I must draw the same QPixmap onto a larger one many many times. Since passing a QPixmap by value in my own methods would create copies with each call, I thought I could shave some time off by working with pointers to QPixmaps. However, it seems my work has been in vein. I think it's because calling QPainter::drawPixmap(..., const QPixmap&, ...) creates a copy of it.

QPixmap *pixmap = new QPixmap(10,10);
painter.drawPixmap(0,0, *pixmap);

Is a copy being created in this example?

If so, how might I go about optimizing the drawing many images onto another?

I have already read this Q/A here: Does dereferencing a pointer make a copy of it? but a definite answer for my specific case eludes me.


Solution

  • According to the QPixmap class reference:

    QPixmap objects can be passed around by value since the QPixmap class uses implicit data sharing. For more information, see the Implicit Data Sharing documentation.

    QPixmap implementation:

    QPixmap::QPixmap(const QPixmap &pixmap)
        : QPaintDevice()
    {
        if (!qt_pixmap_thread_test()) {
            init(0, 0, QPixmapData::PixmapType);
            return;
        }
        if (pixmap.paintingActive()) {                // make a deep copy
            operator=(pixmap.copy());
        } else {
            data = pixmap.data;
        }
    }
    

    Only when the pixmap is painting active you'll need a deep copy otherwise the new pixmap would only need to copy the orignal data pointer.

    For difference of const reference and pointer:

    QPixmap largeMap(1000, 1000);
    QPainter p(&largeMap);
    
    int count = 100000;
    qint64 time1, time2;
    QPixmap *pSmallMap = new QPixmap("e:/test.png");
    QPixmap smallMap = QPixmap("e:/test.png");
    
    time1 =  QDateTime::currentMSecsSinceEpoch();
    for (int i = 0; i < count; ++i) {
        p.drawPixmap(0, 0, *pSmallMap);
    }
    time2 =  QDateTime::currentMSecsSinceEpoch();;
    qDebug("def time = %d\n", time2 - time1);
    
    time1 =  QDateTime::currentMSecsSinceEpoch();
    for (int i = 0; i < count; ++i) {
        p.drawPixmap(0, 0, smallMap);
    }
    time2 =  QDateTime::currentMSecsSinceEpoch();;
    qDebug("normal time = %d\n", time2 - time1);
    

    compile under visual studio 2010 Debug configuration would produce following assembly :

        28:         p.drawPixmap(0, 0, *pSmallMap);
    003B1647 8B 55 C4             mov         edx,dword ptr [ebp-3Ch]  //the pixmap pointer
    003B164A 52                   push        edx  
    003B164B 6A 00                push        0  //x
    003B164D 6A 00                push        0  //y
    003B164F 8D 4D F0             lea         ecx,[ebp-10h]  //the qpainter pointer
    003B1652 FF 15 9C D7 3B 00    call        dword ptr [__imp_QPainter::drawPixmap (3BD79Ch)]
    
        35:         p.drawPixmap(0, 0, smallMap);
    003B16A8 8D 4D E0             lea         ecx,[ebp-20h]  //the pixmap pointer
    003B16AB 51                   push        ecx            
    003B16AC 6A 00                push        0              //x
    003B16AE 6A 00                push        0              //y
    003B16B0 8D 4D F0             lea         ecx,[ebp-10h]  //the qpainter pointer
    003B16B3 FF 15 9C D7 3B 00    call        dword ptr [__imp_QPainter::drawPixmap (3BD79Ch)]  
    

    There should be no differece between this two, because the compiler will produce the same assembly code: pass the pointer to the drawPixmap function.

    And QDateTime::currentMSecsSinceEpoch() nearly show the same result on my box.