Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Segmentation fault when using QImage/QPainter functions (e.g. QImage::scaledToWidth, QPainter::drawImage) in certain situations



  • I'm working on the migration of a large Qt 4.8/mingw 4.4 application from Windows 7 to Windows 8. When watermarking images, I get a segmentation fault. It is possible to work around the bug (I've been able to get a half-decent looking result using clipping in place of the alpha channel). However I'm interested in figuring out if this is a bug in the Qt libraries - especially since we could get similar issues in the future if we do not have a good understanding of the bug.

    Configuration:

    • Qt 4.8.4, mingw 4.4. Qt 4.8.5 also crashed.
    • Windows 8.0 or 8.1
    • The crash occurs on debug builds only, independently of whether or not a debugger is attached

    Crash scenario:

    • Have a GUI thread running
    • Create a QImage from a png file that has an alpha channel (=transparency)
    • Attempt to resize the image with QImage::scaledToWidth(width), but not in the GUI thread

    No crash if either is true:

    • OS is windows 7
    • There is no alpha channel
    • The scaledToWidth call is made from the GUI thread
    • There is no concurrent GUI thread

    Stacktrace:
    0 _mm_andnot_si128 emmintrin.h 1239 0x8a6572
    1 comp_func_SourceOver_sse2 qdrawhelper_sse2.cpp 152 0x8a6572
    2 BlendSrcGeneric<(SpanMethod)0>::process qdrawhelper.cpp 3581 0xf4cf7b
    3 handleSpans<BlendSrcGeneric<(SpanMethod)0> > qdrawhelper.cpp 3527 0xf020eb
    4 blend_src_generic<(SpanMethod)0> qdrawhelper.cpp 3599 0xa764a1
    5 qBlendTexture qdrawhelper.cpp 6832 0xa67159
    6 fillRect_normalized qpaintengine_raster.cpp 1494 0xa55b4b
    7 QRasterPaintEngine::drawImage qpaintengine_raster.cpp 2407 0xa5a4db
    8 QRasterPaintEngine::drawImage qpaintengine_raster.cpp 2169 0xa58ef8
    9 QPainter::drawImage qpainter.cpp 5636 0x9eae0a
    10 QPainter::drawImage qpainter.h 928 0x1047ca1
    11 QImage::transformed qimage.cpp 6672 0x95861e
    12 QImage::scaledToWidth qimage.cpp 4498 0x951c4b
    13 WatermarkTest::watermark watermarktest.cpp 83 0x4023dd
    14 QtConcurrent::StoredFunctorCall0<void, void (*)()>::runFunctor qtconcurrentstoredfunctioncall.h 74 0x407a96
    15 QtConcurrent::RunFunctionTask<void>::run qtconcurrentrunbase.h 134 0x4078e3
    16 QThreadPoolThread::run qthreadpool.cpp 107 0x69ccd310
    17 QThreadPrivate::start qthread_win.cpp 346 0x69cd7eb5
    18 wtoi64 C:\WINDOWS\SysWOW64\msvcrt.dll 0x76fb0bc4
    19 msvcrt!_beginthreadex C:\WINDOWS\SysWOW64\msvcrt.dll 0x76fb0cec
    20 KERNEL32!GetNumberOfConsoleFonts C:\WINDOWS\SysWOW64\kernel32.dll 0x74fc495d
    21 ?? 0x773d98ee
    22 ?? 0x773d98c4
    23 ??

    Notes about the stacktrace:
    emmintrin.h seems to be a MinGW file (not completely sure). The function that is macro-ed consists of a single line thanks to multiple "", but it seems (?) that the crash occurs on line 176 of emmintrin.h (based on the status of initialized variables at the time of the segmentation fault). I think this is very low level code for processing pixel value changes.

    I'm including code for a small application that reproduces the crash in a second post (due to the character limit).



  • Code:
    In the process of figuring out the cause, I made a small application (still roughly 150 lines...) to reproduce the crash and show scenarios in which it does versus does not occur:

    File: watermarktest.h
    @
    #ifndef WATERMARKTEST_H
    #define WATERMARKTEST_H

    #include <QMainWindow>
    #include <QLabel>
    #include <QVBoxLayout>
    #include <QPushButton>

    class WatermarkTest : public QMainWindow
    {
    Q_OBJECT
    public:
    explicit WatermarkTest(QWidget *parent = 0);

    signals:

    public slots:
    void addRemoveWatermark();
    void addWatermarkAsync();
    void addWatermarkSync();

    private: //functions
    static void watermark();

    private: //variables
    QPixmap m_pix;
    QLabel *m_imageViewer;
    QPushButton *m_buttonApplyWatermark;
    QPushButton *m_buttonAsyncWatermark;
    QPushButton *m_buttonSyncWatermark;
    QVBoxLayout *m_layout;
    QWidget *m_central;
    bool m_watermarkOn;

    static const QString BACKGROUND;
    static const QString WATERMARK;
    

    };

    #endif // WATERMARKTEST_H
    @

    File: watermarktest.cpp
    @
    #include "watermarktest.h"
    #include <QPainter>
    #include <QtConcurrentRun>

    const QString WatermarkTest::BACKGROUND = "C:/Development/baseImage.png";
    const QString WatermarkTest::WATERMARK = "C:/Development/alpha.png"; //This image has an alpha channel

    WatermarkTest::WatermarkTest(QWidget *parent) :
    QMainWindow(parent),
    m_watermarkOn(false)
    {
    setWindowTitle("Windows 8 watermark crash replication");

    m_imageViewer = new QLabel();
    m_buttonApplyWatermark = new QPushButton("add/remove watermark");
    m_buttonSyncWatermark = new QPushButton("main thread watermark");
    m_buttonAsyncWatermark = new QPushButton("asynchronous watermark");
    m_layout = new QVBoxLayout();--
    m_pix = QPixmap(BACKGROUND);
    m_central = new QWidget();
    
    m_imageViewer->setPixmap(m_pix);
    
    connect(m_buttonApplyWatermark, SIGNAL(clicked()), this, SLOT(addRemoveWatermark()));
    connect(m_buttonAsyncWatermark, SIGNAL(clicked()), this, SLOT(addWatermarkAsync()));
    connect(m_buttonSyncWatermark, SIGNAL(clicked()), this, SLOT(addWatermarkSync()));
    
    
    m_layout->addWidget(m_imageViewer);
    m_layout->addWidget(m_buttonApplyWatermark);
    m_layout->addWidget(m_buttonAsyncWatermark);
    m_layout->addWidget(m_buttonSyncWatermark);
    
    m_central->setLayout(m_layout);
    setCentralWidget(m_central);
    

    }

    void WatermarkTest::addRemoveWatermark() //works
    {
    if(!m_watermarkOn)
    {
    QImage bg(BACKGROUND);
    QPainter painter(&bg);
    painter.setRenderHint(QPainter::Antialiasing, true);
    int width = 160;
    QImage watermark(WATERMARK);

        QImage scaledImg = watermark.scaledToWidth(width);
        painter.drawImage(0, 0, scaledImg);
    
        painter.end();
        m_imageViewer->setPixmap(QPixmap::fromImage(bg));
        m_watermarkOn = true;
    }
    else
    {
        QImage bg(BACKGROUND);
        m_imageViewer->setPixmap(QPixmap::fromImage(bg));
        m_watermarkOn = false;
    }
    

    }

    void WatermarkTest::addWatermarkSync()
    {
    watermark(); //works
    }

    void WatermarkTest::addWatermarkAsync()
    {
    QtConcurrent::run(watermark); //seg fault
    }

    void WatermarkTest::watermark()
    {
    QImage bg(BACKGROUND);
    QPainter painter(&bg);
    painter.setRenderHint(QPainter::Antialiasing, true);
    QImage watermark(WATERMARK);

    //scenario 1
    int width = 160;
    QImage scaledImg = watermark.scaledToWidth(width); //causes crash, iff called via addWatermarkAsync
    painter.drawImage(0, 0, scaledImg);
    
    //scenario 2
    //painter.drawImage(0, 0, watermark); //causes crash if scenario 1 is commented out
    
    painter.end();
    bg.save("C:/Development/savedfile.png");
    

    }
    @

    File: main.cpp
    @
    #include <watermarktest.h>
    #include <QApplication>

    int main(int argc, char *argv[])
    {
    QApplication app(argc, argv);

    WatermarkTest test;------
    test.show();
    
    return app.exec();
    

    }
    @

    Sample image file with alpha channel so you don't have to make your own
    !http://i42.tinypic.com/30xkke1.png(Alpha image for watermark)!


Log in to reply