From a38dcd522e05514a8caeeee7d01b05ec6af23fe1 Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Thu, 15 Oct 2009 16:19:42 +0200 Subject: Fixed QPainter::begin() so that it fails gracefully. Fixed QPainter::begin() so that if it fails, it can still be used on other paint devices without causing an assert. Autotest included. I also made begin() fail on images with the QImage::Format_Indexed8 format and added warnings in the documentation since painting on such images is not supported. Reviewed-by: Trond --- src/gui/image/qimage.cpp | 3 ++ src/gui/painting/qpainter.cpp | 61 +++++++++++++++++++----------------- tests/auto/qpainter/tst_qpainter.cpp | 40 +++++++++++++++++++++++ 3 files changed, 75 insertions(+), 29 deletions(-) diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 9048387..21ab40c 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -377,6 +377,9 @@ bool QImageData::checkForAlphaPixels() const \note If you would like to load QImage objects in a static build of Qt, refer to the \l{How To Create Qt Plugins#Static Plugins}{Plugin HowTo}. + \warning Painting on a QImage with the format + QImage::Format_Indexed8 is not supported. + \tableofcontents \section1 Reading and Writing Image Files diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 155eefe..cddad7d 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -1608,9 +1608,21 @@ void QPainter::restore() \warning A paint device can only be painted by one painter at a time. + \warning Painting on a QImage with the format + QImage::Format_Indexed8 is not supported. + \sa end(), QPainter() */ +static inline void qt_cleanup_painter_state(QPainterPrivate *d) +{ + d->states.clear(); + delete d->state; + d->state = 0; + d->engine = 0; + d->device = 0; +} + bool QPainter::begin(QPaintDevice *pd) { Q_ASSERT(pd); @@ -1656,15 +1668,21 @@ bool QPainter::begin(QPaintDevice *pd) printf("QPainter::begin(), device=%p, type=%d\n", pd, pd->devType()); #endif - - d->device = pd; if (pd->devType() == QInternal::Pixmap) static_cast(pd)->detach(); else if (pd->devType() == QInternal::Image) static_cast(pd)->detach(); d->engine = pd->paintEngine(); - d->extended = d->engine && d->engine->isExtended() ? static_cast(d->engine) : 0; + + if (!d->engine) { + qWarning("QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType()); + return false; + } + + d->device = pd; + + d->extended = d->engine->isExtended() ? static_cast(d->engine) : 0; if (d->emulationEngine) d->emulationEngine->real_engine = d->extended; @@ -1677,11 +1695,6 @@ bool QPainter::begin(QPaintDevice *pd) d->state->redirectionMatrix.translate(-redirectionOffset.x(), -redirectionOffset.y()); d->state->brushOrigin = QPointF(); - if (!d->engine) { - qWarning("QPainter::begin: Paint device returned engine == 0, type: %d", pd->devType()); - return false; - } - // Slip a painter state into the engine before we do any other operations if (d->extended) d->extended->setState(d->state); @@ -1700,8 +1713,7 @@ bool QPainter::begin(QPaintDevice *pd) && !paintOutsidePaintEvent && !inPaintEvent) { qWarning("QPainter::begin: Widget painting can only begin as a " "result of a paintEvent"); - d->engine = 0; - d->device = 0; + qt_cleanup_painter_state(d); return false; } @@ -1719,8 +1731,7 @@ bool QPainter::begin(QPaintDevice *pd) Q_ASSERT(pm); if (pm->isNull()) { qWarning("QPainter::begin: Cannot paint on a null pixmap"); - d->engine = 0; - d->device = 0; + qt_cleanup_painter_state(d); return false; } @@ -1736,8 +1747,12 @@ bool QPainter::begin(QPaintDevice *pd) Q_ASSERT(img); if (img->isNull()) { qWarning("QPainter::begin: Cannot paint on a null image"); - d->engine = 0; - d->device = 0; + qt_cleanup_painter_state(d); + return false; + } else if (img->format() == QImage::Format_Indexed8) { + // Painting on indexed8 images is not supported. + qWarning("QPainter::begin: Cannot paint on an image with the QImage::Format_Indexed8 format"); + qt_cleanup_painter_state(d); return false; } if (img->depth() == 1) { @@ -1760,12 +1775,8 @@ bool QPainter::begin(QPaintDevice *pd) if (d->engine->isActive()) { end(); } else { - d->states.clear(); - delete d->state; - d->state = 0; + qt_cleanup_painter_state(d); } - d->engine = 0; - d->device = 0; return false; } else { d->engine->setActive(begun); @@ -1828,10 +1839,7 @@ bool QPainter::end() if (!d->engine) { qWarning("QPainter::end: Painter not active, aborted"); - d->states.clear(); - delete d->state; - d->state = 0; - d->device = 0; + qt_cleanup_painter_state(d); return false; } @@ -1862,8 +1870,6 @@ bool QPainter::end() delete d->engine; } - d->engine = 0; - if (d->emulationEngine) { delete d->emulationEngine; d->emulationEngine = 0; @@ -1873,11 +1879,8 @@ bool QPainter::end() d->extended = 0; } - d->states.clear(); - delete d->state; - d->state = 0; + qt_cleanup_painter_state(d); - d->device = 0; return ended; } diff --git a/tests/auto/qpainter/tst_qpainter.cpp b/tests/auto/qpainter/tst_qpainter.cpp index 9515d87..2231287 100644 --- a/tests/auto/qpainter/tst_qpainter.cpp +++ b/tests/auto/qpainter/tst_qpainter.cpp @@ -238,6 +238,8 @@ private slots: void taskQT4444_dontOverflowDashOffset(); + void painterBegin(); + private: void fillData(); QColor baseColor( int k, int intensity=255 ); @@ -4312,5 +4314,43 @@ void tst_QPainter::taskQT4444_dontOverflowDashOffset() QVERIFY(true); // Don't crash } +void tst_QPainter::painterBegin() +{ + QImage nullImage; + QImage indexed8Image(16, 16, QImage::Format_Indexed8); + QImage rgb32Image(16, 16, QImage::Format_RGB32); + QImage argb32Image(16, 16, QImage::Format_ARGB32_Premultiplied); + + QPainter p; + + // Painting on null image should fail. + QVERIFY(!p.begin(&nullImage)); + + // Check that the painter is not messed up by using it on another image. + QVERIFY(p.begin(&rgb32Image)); + QVERIFY(p.end()); + + // If painting on indexed8 image fails, the painter state should still be OK. + if (p.begin(&indexed8Image)) + QVERIFY(p.end()); + QVERIFY(p.begin(&rgb32Image)); + QVERIFY(p.end()); + + // Try opening a painter on the two different images. + QVERIFY(p.begin(&rgb32Image)); + QVERIFY(!p.begin(&argb32Image)); + QVERIFY(p.end()); + + // Try opening two painters on the same image. + QVERIFY(p.begin(&rgb32Image)); + QPainter q; + QVERIFY(!q.begin(&rgb32Image)); + QVERIFY(!q.end()); + QVERIFY(p.end()); + + // Try ending an inactive painter. + QVERIFY(!p.end()); +} + QTEST_MAIN(tst_QPainter) #include "tst_qpainter.moc" -- cgit v0.12