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 channelWatermarkTest::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)!