From 9b3b29997f91892266fc34083eef269d7d7cc006 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 3 Nov 2011 10:22:32 +0100 Subject: qmlplugindump: Add flush to fix output redirection on windows. Task-number: QTCREATORBUG-5825 --- tools/qmlplugindump/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index 6bef8d4..e9547af 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -641,7 +641,7 @@ int main(int argc, char *argv[]) qml.writeEndObject(); qml.writeEndDocument(); - std::cout << bytes.constData(); + std::cout << bytes.constData() << std::flush; // workaround to avoid crashes on exit QTimer timer; -- cgit v0.12 From b029874615f0da72a44dab5b6d1a398a71f13100 Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Mon, 14 Nov 2011 11:57:21 +1000 Subject: Properly protect access to pixmap reader thread with mutex Previously, access to the data from the reader thread wasn't guarded properly, causing a crash when the reader thread was deleted prior to QDeclarativePixmapData (which then attempted to dereference the thread pointer to cancel the request), or in the case where a QDeclarativePixmapData was deleted after its QDeclarativePixmapReply was removed from the jobs queue but prior to processing. Reviewed-by: Martin Jones Task-number: QTBUG-22125 --- src/declarative/util/qdeclarativepixmapcache.cpp | 57 ++++++++++++++++------ .../qdeclarativeimage/data/qtbug_22125.qml | 44 +++++++++++++++++ .../qdeclarativeimage/tst_qdeclarativeimage.cpp | 41 ++++++++++++++++ 3 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 tests/auto/declarative/qdeclarativeimage/data/qtbug_22125.qml diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index 3557425..b23ac73 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -90,8 +90,9 @@ public: ~QDeclarativePixmapReply(); QDeclarativePixmapData *data; - QDeclarativePixmapReader *reader; + QDeclarativeEngine *engineForReader; // always access reader inside readerMutex. QSize requestSize; + QUrl url; bool loading; int redirectCount; @@ -147,6 +148,7 @@ public: void cancel(QDeclarativePixmapReply *rep); static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine); + static QDeclarativePixmapReader *existingInstance(QDeclarativeEngine *engine); protected: void run(); @@ -176,6 +178,7 @@ private: static int downloadProgress; static int threadNetworkRequestDone; static QHash readers; +public: static QMutex readerMutex; }; @@ -326,6 +329,22 @@ QDeclarativePixmapReader::~QDeclarativePixmapReader() readers.remove(engine); readerMutex.unlock(); + mutex.lock(); + // manually cancel all outstanding jobs. + foreach (QDeclarativePixmapReply *reply, jobs) { + delete reply; + } + jobs.clear(); + QList activeJobs = replies.values(); + foreach (QDeclarativePixmapReply *reply, activeJobs) { + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + } + } + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + eventLoopQuitHack->deleteLater(); wait(); } @@ -433,9 +452,8 @@ void QDeclarativePixmapReader::processJobs() if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) { QDeclarativePixmapReply *runningJob = jobs.takeLast(); runningJob->loading = true; - - QUrl url = runningJob->data->url; - QSize requestSize = runningJob->data->requestSize; + QUrl url = runningJob->url; + QSize requestSize = runningJob->requestSize; locker.unlock(); processJob(runningJob, url, requestSize); locker.relock(); @@ -459,7 +477,6 @@ void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, c errorCode = QDeclarativePixmapReply::Loading; errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()); } - mutex.lock(); if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image); mutex.unlock(); @@ -487,10 +504,8 @@ void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, c QNetworkRequest req(url); req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); QNetworkReply *reply = networkAccessManager()->get(req); - QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); - replies.insert(reply, runningJob); } } @@ -498,22 +513,27 @@ void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, c QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine) { - readerMutex.lock(); + // XXX NOTE: must be called within readerMutex locking. QDeclarativePixmapReader *reader = readers.value(engine); if (!reader) { reader = new QDeclarativePixmapReader(engine); readers.insert(engine, reader); } - readerMutex.unlock(); return reader; } +QDeclarativePixmapReader *QDeclarativePixmapReader::existingInstance(QDeclarativeEngine *engine) +{ + // XXX NOTE: must be called within readerMutex locking. + return readers.value(engine, 0); +} + QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data) { mutex.lock(); QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data); - reply->reader = this; + reply->engineForReader = engine; jobs.append(reply); // XXX if (threadObject) threadObject->processJobs(); @@ -692,7 +712,7 @@ void QDeclarativePixmapStore::flushCache() } QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d) -: data(d), reader(0), requestSize(d->requestSize), loading(false), redirectCount(0) +: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0) { if (finishedIndex == -1) { finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()"); @@ -750,8 +770,14 @@ void QDeclarativePixmapData::release() if (refCount == 0) { if (reply) { - reply->reader->cancel(reply); + QDeclarativePixmapReply *cancelReply = reply; + reply->data = 0; reply = 0; + QDeclarativePixmapReader::readerMutex.lock(); + QDeclarativePixmapReader *reader = QDeclarativePixmapReader::existingInstance(cancelReply->engineForReader); + if (reader) + reader->cancel(cancelReply); + QDeclarativePixmapReader::readerMutex.unlock(); } if (pixmapStatus == QDeclarativePixmap::Ready) { @@ -1013,13 +1039,12 @@ void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const if (!engine) return; - QDeclarativePixmapReader *reader = QDeclarativePixmapReader::instance(engine); - d = new QDeclarativePixmapData(url, requestSize); if (options & QDeclarativePixmap::Cache) d->addToCache(); - - d->reply = reader->getImage(d); + QDeclarativePixmapReader::readerMutex.lock(); + d->reply = QDeclarativePixmapReader::instance(engine)->getImage(d); + QDeclarativePixmapReader::readerMutex.unlock(); } else { d = *iter; d->addref(); diff --git a/tests/auto/declarative/qdeclarativeimage/data/qtbug_22125.qml b/tests/auto/declarative/qdeclarativeimage/data/qtbug_22125.qml new file mode 100644 index 0000000..8588028 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeimage/data/qtbug_22125.qml @@ -0,0 +1,44 @@ +import QtQuick 1.1 + +Item { + id: root + width: 800 + height: 800 + + GridView { + anchors.fill: parent + delegate: Image { + source: imagePath; + asynchronous: true + smooth: true + width: 200 + height: 200 + } + model: ListModel { + ListElement { + imagePath: "http://127.0.0.1:14451/big256.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/big256.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/big256.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/colors.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/colors1.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/big.jpeg" + } + ListElement { + imagePath: "http://127.0.0.1:14451/heart.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/green.png" + } + } + } +} diff --git a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp index a35d69a..f67c5b5 100644 --- a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp +++ b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp @@ -94,6 +94,7 @@ private slots: void resetSourceSize(); void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); + void readerCrash_QTBUG_22125(); private: template @@ -762,6 +763,46 @@ void tst_qdeclarativeimage::testQtQuick11Attributes_data() << ":1 \"Image.cache\" is not available in QtQuick 1.0.\n"; } +void tst_qdeclarativeimage::readerCrash_QTBUG_22125() +{ + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/data/", TestHTTPServer::Delay); + + { + QDeclarativeView view(QUrl::fromLocalFile(SRCDIR "/data/qtbug_22125.qml")); + view.show(); + qApp->processEvents(); + qApp->processEvents(); + // shouldn't crash when the view drops out of scope due to + // QDeclarativePixmapData attempting to dereference a pointer to + // the destroyed reader. + } + + // shouldn't crash when deleting cancelled QDeclarativePixmapReplys. + QTest::qWait(1000); + qApp->processEvents(QEventLoop::DeferredDeletion); + } + + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/data/"); + + { + QDeclarativeView view(QUrl::fromLocalFile(SRCDIR "/data/qtbug_22125.qml")); + view.show(); + qApp->processEvents(); + QTest::qWait(1000); + qApp->processEvents(); + // shouldn't crash when the view drops out of scope due to + // the reader thread accessing self-deleted QDeclarativePixmapReplys. + } + qApp->processEvents(); + } +} + /* Find an item with the specified objectName. If index is supplied then the item must also evaluate the {index} expression equal to index -- cgit v0.12 From 951a997893407b0a26f21fe0acec4701fa3d4279 Mon Sep 17 00:00:00 2001 From: Sarah Smith Date: Mon, 14 Nov 2011 13:51:50 +1000 Subject: Move tga support from Qt3d to Qt. Rev-By: Samuel Rodal Fix-for: QTBUG-21955 Part of fix for https://bugreports.qt.nokia.com/browse/QTBUG-21955 Having tga plugin inside a Qt add-on causes packaging problems. There have been many queries over the years for tga support even tho it is a niche image format. It is particularly useful for OpenGL work as a texture format since it is a blittable bitmap, but it is very innefficient compared to png or jpg. For this reason only read support is added, and this is documented. Also add some unit tests. The unit tests are in the qimagereader suite, and this is the right place for them as the other formats are tested there. Noticed that there was an annoying bug with tiff files and chased it down, in the hope of getting a clean test case PASS. Looks as tho there is a bit of work for someone to fix the other FAILs, but the ones for tga are all passing. --- src/gui/image/qimagereader.cpp | 5 + src/gui/image/qtiffhandler.cpp | 6 + src/plugins/imageformats/imageformats.pro | 1 + src/plugins/imageformats/tga/main.cpp | 95 +++++++++ src/plugins/imageformats/tga/qtgafile.cpp | 239 +++++++++++++++++++++++ src/plugins/imageformats/tga/qtgafile.h | 146 ++++++++++++++ src/plugins/imageformats/tga/qtgahandler.cpp | 123 ++++++++++++ src/plugins/imageformats/tga/qtgahandler.h | 74 +++++++ src/plugins/imageformats/tga/tga.pro | 16 ++ tests/auto/qimagereader/images/test-flag-rle.tga | Bin 0 -> 7971 bytes tests/auto/qimagereader/images/test-flag.tga | Bin 0 -> 480044 bytes tests/auto/qimagereader/qimagereader.pro | 1 + tests/auto/qimagereader/qimagereader.qrc | 2 + tests/auto/qimagereader/tst_qimagereader.cpp | 51 ++++- 14 files changed, 755 insertions(+), 4 deletions(-) create mode 100644 src/plugins/imageformats/tga/main.cpp create mode 100644 src/plugins/imageformats/tga/qtgafile.cpp create mode 100644 src/plugins/imageformats/tga/qtgafile.h create mode 100644 src/plugins/imageformats/tga/qtgahandler.cpp create mode 100644 src/plugins/imageformats/tga/qtgahandler.h create mode 100644 src/plugins/imageformats/tga/tga.pro create mode 100644 tests/auto/qimagereader/images/test-flag-rle.tga create mode 100644 tests/auto/qimagereader/images/test-flag.tga diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index 411e5e9..2b56e49 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -1473,11 +1473,16 @@ QByteArray QImageReader::imageFormat(QIODevice *device) \row \o XBM \o X11 Bitmap \row \o XPM \o X11 Pixmap \row \o SVG \o Scalable Vector Graphics + \row \o TGA \o Targa Image Format \endtable Reading and writing SVG files is supported through Qt's \l{QtSvg Module}{SVG Module}. + TGA support only extends to reading non-RLE compressed files. In particular + calls to \l{http://doc.qt.nokia.com/4.7-snapshot/qimageioplugin.html#capabilities}{capabilities} + for the tga plugin returns only QImageIOPlugin::CanRead, not QImageIOPlugin::CanWrite. + To configure Qt with GIF support, pass \c -qt-gif to the \c configure script or check the appropriate option in the graphical installer. diff --git a/src/gui/image/qtiffhandler.cpp b/src/gui/image/qtiffhandler.cpp index 4dc9775..51a500d 100644 --- a/src/gui/image/qtiffhandler.cpp +++ b/src/gui/image/qtiffhandler.cpp @@ -158,7 +158,13 @@ bool QTiffHandler::canRead(QIODevice *device) // current implementation uses TIFFClientOpen which needs to be // able to seek, so sequential devices are not supported + int pos = device->pos(); + if (pos != 0) + device->seek(0); // need the magic from the beginning QByteArray header = device->peek(4); + if (pos != 0) + device->seek(pos); // put it back where we found it + return header == QByteArray::fromRawData("\x49\x49\x2A\x00", 4) || header == QByteArray::fromRawData("\x4D\x4D\x00\x2A", 4); } diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro index 5fff2de..54a2795 100644 --- a/src/plugins/imageformats/imageformats.pro +++ b/src/plugins/imageformats/imageformats.pro @@ -6,3 +6,4 @@ TEMPLATE = subdirs contains(QT_CONFIG, svg):SUBDIRS += svg !contains(QT_CONFIG, no-tiff):!contains(QT_CONFIG, tiff):SUBDIRS += tiff !contains(QT_CONFIG, no-ico):SUBDIRS += ico +!contains(QT_CONFIG, no-tga):SUBDIRS += tga diff --git a/src/plugins/imageformats/tga/main.cpp b/src/plugins/imageformats/tga/main.cpp new file mode 100644 index 0000000..8b1e070 --- /dev/null +++ b/src/plugins/imageformats/tga/main.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#ifdef QT_NO_IMAGEFORMAT_TGA +#undef QT_NO_IMAGEFORMAT_TGA +#endif + +#include "qtgahandler.h" + +QT_BEGIN_NAMESPACE + +class QTgaPlugin : public QImageIOPlugin +{ +public: + Capabilities capabilities(QIODevice * device, const QByteArray & format) const; + QImageIOHandler * create(QIODevice * device, const QByteArray & format = QByteArray()) const; + QStringList keys() const; +}; + +QImageIOPlugin::Capabilities QTgaPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "tga") + return Capabilities(CanRead); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && QTgaHandler::canRead(device)) + cap |= CanRead; + return cap; +} + +QImageIOHandler* QTgaPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *tgaHandler = new QTgaHandler(); + tgaHandler->setDevice(device); + tgaHandler->setFormat(format); + return tgaHandler; +} + +QStringList QTgaPlugin::keys() const +{ + return QStringList() << QLatin1String("tga"); +} + +Q_EXPORT_PLUGIN2(qtga, QTgaPlugin) + +QT_END_NAMESPACE + +#endif /* QT_NO_IMAGEFORMATPLUGIN */ diff --git a/src/plugins/imageformats/tga/qtgafile.cpp b/src/plugins/imageformats/tga/qtgafile.cpp new file mode 100644 index 0000000..af7c876 --- /dev/null +++ b/src/plugins/imageformats/tga/qtgafile.cpp @@ -0,0 +1,239 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgafile.h" + +#include +#include +#include + +struct TgaReader +{ + virtual ~TgaReader() {} + virtual QRgb operator()(QIODevice *s) const = 0; +}; + +struct Tga16Reader : public TgaReader +{ + ~Tga16Reader() {} + QRgb operator()(QIODevice *s) const + { + char ch1, ch2; + if (s->getChar(&ch1) && s->getChar(&ch2)) { + quint16 d = (int(ch1) & 0xFF) | ((int(ch2) & 0xFF) << 8); + QRgb result = (d & 0x8000) ? 0xFF000000 : 0x00000000; + result |= (d & 0x7C00 << 6) | (d & 0x03E0 << 3) | (d & 0x001F); + return result; + } else { + return 0; + } + } +}; + +struct Tga24Reader : public TgaReader +{ + QRgb operator()(QIODevice *s) const + { + char r, g, b; + if (s->getChar(&b) && s->getChar(&g) && s->getChar(&r)) + return qRgb(uchar(r), uchar(g), uchar(b)); + else + return 0; + } +}; + +struct Tga32Reader : public TgaReader +{ + QRgb operator()(QIODevice *s) const + { + char r, g, b, a; + if (s->getChar(&b) && s->getChar(&g) && s->getChar(&r) && s->getChar(&a)) + return qRgba(uchar(r), uchar(g), uchar(b), uchar(a)); + else + return 0; + } +}; + +/*! + \class QTgaFile + \since 4.8 + \internal + + File data container for a TrueVision Graphics format file. + + Format is as described here: + http://local.wasp.uwa.edu.au/~pbourke/dataformats/tga/ + http://netghost.narod.ru/gff2/graphics/summary/tga.htm + + Usage is: + \code + QTgaFile tga(myFile); + QImage tgaImage; + if (tga.isValid()) + tgaImage = tga.readImage(); + \endcode + + The class is designed to handle sequential and non-sequential + sources, so during construction the mHeader is read. Then during + the readImage() call the rest of the data is read. + + After passing myFile to the constructor, if the QIODevice *myFile + is read, or has seek() called, the results are undefined - so don't + do that. +*/ + +/*! + Construct a new QTgaFile object getting data from \a device. + + The object does not take ownership of the \a device, but until the + object is destroyed do not do any non-const operations, eg seek or + read on the device. +*/ +QTgaFile::QTgaFile(QIODevice *device) + : mDevice(device) +{ + ::memset(mHeader, 0, HeaderSize); + if (!mDevice->isReadable()) + { + mErrorMessage = QObject::tr("Could not read image data"); + return; + } + if (mDevice->isSequential()) + { + mErrorMessage = QObject::tr("Sequential device (eg socket) for image read not supported"); + return; + } + if (!mDevice->seek(0)) + { + mErrorMessage = QObject::tr("Seek file/device for image read failed"); + return; + } + int bytes = device->read((char*)mHeader, HeaderSize); + if (bytes != HeaderSize) + { + mErrorMessage = QObject::tr("Image mHeader read failed"); + return; + } + if (mHeader[ImageType] != 2) + { + // TODO: should support other image types + mErrorMessage = QObject::tr("Image type not supported"); + return; + } +} + +/*! + \internal + Destroy the device, recovering any resources. +*/ +QTgaFile::~QTgaFile() +{ +} + +/*! + \internal + Reads an image file from the QTgaFile's device, and returns it. + + This method seeks to the absolute position of the image data in the file, + so no assumptions are made about where the devices read pointer is when this + method is called. For this reason only random access devices are supported. + + If the constructor completed successfully, such that isValid() returns true, + then this method is likely to succeed, unless the file is somehow corrupted. + + In the case that the read fails, the QImage returned will be null, such that + QImage::isNull() will be true. +*/ +QImage QTgaFile::readImage() +{ + if (!isValid()) + return QImage(); + + int offset = mHeader[IdLength]; // Mostly always zero + + // Even in TrueColor files a color pallette may be present + if (mHeader[ColorMapType] == 1) + offset += littleEndianInt(&mHeader[CMapLength]) * littleEndianInt(&mHeader[CMapDepth]); + + mDevice->seek(HeaderSize + offset); + + char dummy; + for (int i = 0; i < offset; ++i) + mDevice->getChar(&dummy); + + int bitsPerPixel = mHeader[PixelDepth]; + int imageWidth = width(); + int imageHeight = height(); + + unsigned char desc = mHeader[ImageDescriptor]; + //unsigned char xCorner = desc & 0x10; // 0 = left, 1 = right + unsigned char yCorner = desc & 0x20; // 0 = lower, 1 = upper + + QImage im(imageWidth, imageHeight, QImage::Format_ARGB32); + TgaReader *reader = 0; + if (bitsPerPixel == 16) + reader = new Tga16Reader(); + else if (bitsPerPixel == 24) + reader = new Tga24Reader(); + else if (bitsPerPixel == 32) + reader = new Tga32Reader(); + TgaReader &read = *reader; + + // For now only deal with yCorner, since no one uses xCorner == 1 + // Also this is upside down, since Qt has the origin flipped + if (yCorner) + { + for (int y = 0; y < imageHeight; ++y) + for (int x = 0; x < imageWidth; ++x) + im.setPixel(x, y, read(mDevice)); + } + else + { + for (int y = imageHeight - 1; y >= 0; --y) + for (int x = 0; x < imageWidth; ++x) + im.setPixel(x, y, read(mDevice)); + } + + delete reader; + + // TODO: add processing of TGA extension information - ie TGA 2.0 files + return im; +} diff --git a/src/plugins/imageformats/tga/qtgafile.h b/src/plugins/imageformats/tga/qtgafile.h new file mode 100644 index 0000000..034200e --- /dev/null +++ b/src/plugins/imageformats/tga/qtgafile.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGAFILE_H +#define QTGAFILE_H + +#include +#include + +class QIODevice; + +class QTgaFile +{ +public: + enum Compression { + NoCompression = 0, + RleCompression = 1 + }; + + enum HeaderOffset { + IdLength = 0, /* 00h Size of Image ID field */ + ColorMapType = 1, /* 01h Color map type */ + ImageType = 2, /* 02h Image type code */ + CMapStart = 3, /* 03h Color map origin */ + CMapLength = 5, /* 05h Color map length */ + CMapDepth = 7, /* 07h Depth of color map entries */ + XOffset = 8, /* 08h X origin of image */ + YOffset = 10, /* 0Ah Y origin of image */ + Width = 12, /* 0Ch Width of image */ + Height = 14, /* 0Eh Height of image */ + PixelDepth = 16, /* 10h Image pixel size */ + ImageDescriptor = 17, /* 11h Image descriptor byte */ + HeaderSize = 18 + }; + + QTgaFile(QIODevice *); + ~QTgaFile(); + + inline bool isValid() const; + inline QString errorMessage() const; + QImage readImage(); + inline int xOffset() const; + inline int yOffset() const; + inline int width() const; + inline int height() const; + inline QSize size() const; + inline Compression compression() const; + +private: + static inline quint16 littleEndianInt(const unsigned char *d); + + QString mErrorMessage; + unsigned char mHeader[HeaderSize]; + QIODevice *mDevice; +}; + +inline bool QTgaFile::isValid() const +{ + return mErrorMessage.isEmpty(); +} + +inline QString QTgaFile::errorMessage() const +{ + return mErrorMessage; +} + +/*! + \internal + Returns the integer encoded in the two little endian bytes at \a d. +*/ +inline quint16 QTgaFile::littleEndianInt(const unsigned char *d) +{ + return d[0] + d[1] * 256; +} + +inline int QTgaFile::xOffset() const +{ + return littleEndianInt(&mHeader[XOffset]); +} + +inline int QTgaFile::yOffset() const +{ + return littleEndianInt(&mHeader[YOffset]); +} + +inline int QTgaFile::width() const +{ + return littleEndianInt(&mHeader[Width]); +} + +inline int QTgaFile::height() const +{ + return littleEndianInt(&mHeader[Height]); +} + +inline QSize QTgaFile::size() const +{ + return QSize(width(), height()); +} + +inline QTgaFile::Compression QTgaFile::compression() const +{ + // TODO: for now, only handle type 2 files, with no color table + // and no compression + return NoCompression; +} + +#endif // QTGAFILE_H diff --git a/src/plugins/imageformats/tga/qtgahandler.cpp b/src/plugins/imageformats/tga/qtgahandler.cpp new file mode 100644 index 0000000..2464daa --- /dev/null +++ b/src/plugins/imageformats/tga/qtgahandler.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgahandler.h" +#include "qtgafile.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QTgaHandler::QTgaHandler() + : QImageIOHandler() + , tga(0) +{ +} + +QTgaHandler::~QTgaHandler() +{ + delete tga; +} + +bool QTgaHandler::canRead() const +{ + if (!tga) + tga = new QTgaFile(device()); + if (tga->isValid()) + { + setFormat("tga"); + return true; + } + return false; +} + +bool QTgaHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning("QTgaHandler::canRead() called with no device"); + return false; + } + QTgaFile tga(device); + return tga.isValid(); +} + +bool QTgaHandler::read(QImage *image) +{ + if (!canRead()) + return false; + *image = tga->readImage(); + return !image->isNull(); +} + +QByteArray QTgaHandler::name() const +{ + return "tga"; +} + +QVariant QTgaHandler::option(ImageOption option) const +{ + if (option == Size && canRead()) { + return tga->size(); + } else if (option == CompressionRatio) { + return tga->compression(); + } else if (option == ImageFormat) { + return QImage::Format_ARGB32; + } + return QVariant(); +} + +void QTgaHandler::setOption(ImageOption option, const QVariant &value) +{ + Q_UNUSED(option); + Q_UNUSED(value); +} + +bool QTgaHandler::supportsOption(ImageOption option) const +{ + return option == CompressionRatio + || option == Size + || option == ImageFormat; +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/tga/qtgahandler.h b/src/plugins/imageformats/tga/qtgahandler.h new file mode 100644 index 0000000..88e2b94 --- /dev/null +++ b/src/plugins/imageformats/tga/qtgahandler.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtQuick3D module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGAHANDLER_H +#define QTGAHANDLER_H + +#include + +QT_BEGIN_NAMESPACE + +class QTgaFile; + +class QTgaHandler : public QImageIOHandler +{ +public: + QTgaHandler(); + ~QTgaHandler(); + + bool canRead() const; + bool read(QImage *image); + + QByteArray name() const; + + static bool canRead(QIODevice *device); + + QVariant option(ImageOption option) const; + void setOption(ImageOption option, const QVariant &value); + bool supportsOption(ImageOption option) const; + +private: + mutable QTgaFile *tga; +}; + +QT_END_NAMESPACE + +#endif // QTGAHANDLER_H diff --git a/src/plugins/imageformats/tga/tga.pro b/src/plugins/imageformats/tga/tga.pro new file mode 100644 index 0000000..292d06e --- /dev/null +++ b/src/plugins/imageformats/tga/tga.pro @@ -0,0 +1,16 @@ +TARGET = qtga +include(../../qpluginbase.pri) + +QTDIR_build:REQUIRES = "!contains(QT_CONFIG, no-tga)" + +HEADERS += qtgahandler.h \ + qtgafile.h +SOURCES += main.cpp \ + qtgahandler.cpp \ + qtgafile.cpp + +QTDIR_build:DESTDIR = $$QT_BUILD_TREE/plugins/imageformats +target.path += $$[QT_INSTALL_PLUGINS]/imageformats +INSTALLS += target + +symbian: TARGET.UID3=0x20031E99 diff --git a/tests/auto/qimagereader/images/test-flag-rle.tga b/tests/auto/qimagereader/images/test-flag-rle.tga new file mode 100644 index 0000000..c14c712 Binary files /dev/null and b/tests/auto/qimagereader/images/test-flag-rle.tga differ diff --git a/tests/auto/qimagereader/images/test-flag.tga b/tests/auto/qimagereader/images/test-flag.tga new file mode 100644 index 0000000..c6d0c89 Binary files /dev/null and b/tests/auto/qimagereader/images/test-flag.tga differ diff --git a/tests/auto/qimagereader/qimagereader.pro b/tests/auto/qimagereader/qimagereader.pro index 827819d..a9bfa1d 100644 --- a/tests/auto/qimagereader/qimagereader.pro +++ b/tests/auto/qimagereader/qimagereader.pro @@ -10,6 +10,7 @@ RESOURCES += qimagereader.qrc !contains(QT_CONFIG, no-mng):DEFINES += QTEST_HAVE_MNG !contains(QT_CONFIG, no-tiff):DEFINES += QTEST_HAVE_TIFF !contains(QT_CONFIG, no-svg):DEFINES += QTEST_HAVE_SVG +!contains(QT_CONFIG, no-tga):DEFINES += QTEST_HAVE_TGA win32-msvc:QMAKE_CXXFLAGS -= -Zm200 win32-msvc:QMAKE_CXXFLAGS += -Zm800 diff --git a/tests/auto/qimagereader/qimagereader.qrc b/tests/auto/qimagereader/qimagereader.qrc index 2c70652..03c03d6 100644 --- a/tests/auto/qimagereader/qimagereader.qrc +++ b/tests/auto/qimagereader/qimagereader.qrc @@ -67,5 +67,7 @@ images/corrupt.svgz images/qtbug13653-no_eoi.jpg images/txts.png + images/test-flag.tga + images/test-flag-rle.tga diff --git a/tests/auto/qimagereader/tst_qimagereader.cpp b/tests/auto/qimagereader/tst_qimagereader.cpp index 5d958d7..5bf9d19 100644 --- a/tests/auto/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/qimagereader/tst_qimagereader.cpp @@ -168,6 +168,10 @@ private slots: void tiffGrayscale(); #endif +#if defined QTEST_HAVE_TGA + void tgaTestImages(); +#endif + void autoDetectImageFormat(); void fileNameProbing(); @@ -569,6 +573,10 @@ void tst_QImageReader::imageFormat_data() QTest::newRow("mng-2") << QString("fire.mng") << QByteArray("mng") << QImage::Format_Invalid; QTest::newRow("svg") << QString("rect.svg") << QByteArray("svg") << QImage::Format_ARGB32_Premultiplied; QTest::newRow("svgz") << QString("rect.svgz") << QByteArray("svgz") << QImage::Format_ARGB32_Premultiplied; +#if defined QTEST_HAVE_TGA + QTest::newRow("tga") << QString("test-flag.tga") << QByteArray("tga") << QImage::Format_ARGB32; + QTest::newRow("tga-rle") << QString("test-flag-rle.tga") << QByteArray("tga") << QImage::Format_ARGB32; +#endif } void tst_QImageReader::imageFormat() @@ -578,22 +586,31 @@ void tst_QImageReader::imageFormat() QFETCH(QImage::Format, imageFormat); if (QImageReader::imageFormat(prefix + fileName).isEmpty()) { - if (QByteArray("jpeg") == format) + if (QByteArray("jpeg") == format) { #ifndef QTEST_HAVE_JPEG return; #endif // !QTEST_HAVE_JPEG - if (QByteArray("gif") == format) + } + if (QByteArray("gif") == format) { #ifndef QTEST_HAVE_GIF return; #endif // !QTEST_HAVE_GIF - if (QByteArray("mng") == format) + } + if (QByteArray("mng") == format) { #ifndef QTEST_HAVE_MNG return; #endif // !QTEST_HAVE_MNG - if (QByteArray("svg") == format || QByteArray("svgz") == format) + } + if (QByteArray("svg") == format || QByteArray("svgz") == format) { #ifndef QTEST_HAVE_SVG return; #endif // !QTEST_HAVE_SVG + } + if (QByteArray("tga") == format) { +#ifndef QTEST_HAVE_TGA + return; +#endif // !QTEST_HAVE_TGA + } QSKIP(("Qt does not support the " + format + " format.").constData(), SkipSingle); } else { QCOMPARE(QImageReader::imageFormat(prefix + fileName), format); @@ -734,6 +751,11 @@ void tst_QImageReader::sizeBeforeRead() { QFETCH(QString, fileName); QFETCH(QByteArray, format); + + if (fileName == QLatin1String("test-flag-rle.tga")) { + QSKIP("Qt does not support reading RLE compressed TGA files", SkipSingle); + } + QImageReader reader(prefix + fileName); QVERIFY(reader.canRead()); if (format == "mng") { @@ -1711,6 +1733,27 @@ void tst_QImageReader::tiffGrayscale() } #endif +#if defined QTEST_HAVE_TGA +void tst_QImageReader::tgaTestImages() +{ + QImage tgaTest(prefix + "test-flag.tga"); + + // Test image is 400x400 of a "flag" with a blue circle in the middle + // and a green square top left, and red rectangle on the bottom so + // so we test for pixels in those areas to ensure the image is not + // inverted or the palette is not messed up. + QVERIFY(!tgaTest.isNull()); + QCOMPARE(tgaTest.size().width(), 400); + QCOMPARE(tgaTest.size().height(), 400); + QRgb pixel = tgaTest.pixel(200,200); + QCOMPARE(qRgb(0, 0, 255), pixel); + pixel = tgaTest.pixel(0, 0); + QCOMPARE(qRgb(0, 255, 0), pixel); + pixel = tgaTest.pixel(0, 399); + QCOMPARE(qRgb(255, 0, 0), pixel); +} +#endif + void tst_QImageReader::dotsPerMeter_data() { QTest::addColumn("fileName"); -- cgit v0.12 From b8cc85402bf0992657ed527996532d6020ac9acb Mon Sep 17 00:00:00 2001 From: Sarah Smith Date: Mon, 14 Nov 2011 13:51:50 +1000 Subject: Move tga support from Qt3d to Qt. Rev-By: Samuel Rodal Fix-for: QTBUG-21955 Part of fix for https://bugreports.qt.nokia.com/browse/QTBUG-21955 Having tga plugin inside a Qt add-on causes packaging problems. There have been many queries over the years for tga support even tho it is a niche image format. It is particularly useful for OpenGL work as a texture format since it is a blittable bitmap, but it is very innefficient compared to png or jpg. For this reason only read support is added, and this is documented. Also add some unit tests. The unit tests are in the qimagereader suite, and this is the right place for them as the other formats are tested there. Noticed that there was an annoying bug with tiff files and chased it down, in the hope of getting a clean test case PASS. Looks as tho there is a bit of work for someone to fix the other FAILs, but the ones for tga are all passing. --- tests/auto/qimagereader/tst_qimagereader.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/auto/qimagereader/tst_qimagereader.cpp b/tests/auto/qimagereader/tst_qimagereader.cpp index 5bf9d19..2b867c3 100644 --- a/tests/auto/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/qimagereader/tst_qimagereader.cpp @@ -590,17 +590,17 @@ void tst_QImageReader::imageFormat() #ifndef QTEST_HAVE_JPEG return; #endif // !QTEST_HAVE_JPEG - } + } if (QByteArray("gif") == format) { #ifndef QTEST_HAVE_GIF return; #endif // !QTEST_HAVE_GIF - } + } if (QByteArray("mng") == format) { #ifndef QTEST_HAVE_MNG return; #endif // !QTEST_HAVE_MNG - } + } if (QByteArray("svg") == format || QByteArray("svgz") == format) { #ifndef QTEST_HAVE_SVG return; @@ -1736,8 +1736,8 @@ void tst_QImageReader::tiffGrayscale() #if defined QTEST_HAVE_TGA void tst_QImageReader::tgaTestImages() { - QImage tgaTest(prefix + "test-flag.tga"); - + QImage tgaTest(prefix + "test-flag.tga"); + // Test image is 400x400 of a "flag" with a blue circle in the middle // and a green square top left, and red rectangle on the bottom so // so we test for pixels in those areas to ensure the image is not @@ -1745,8 +1745,8 @@ void tst_QImageReader::tgaTestImages() QVERIFY(!tgaTest.isNull()); QCOMPARE(tgaTest.size().width(), 400); QCOMPARE(tgaTest.size().height(), 400); - QRgb pixel = tgaTest.pixel(200,200); - QCOMPARE(qRgb(0, 0, 255), pixel); + QRgb pixel = tgaTest.pixel(200,200); + QCOMPARE(qRgb(0, 0, 255), pixel); pixel = tgaTest.pixel(0, 0); QCOMPARE(qRgb(0, 255, 0), pixel); pixel = tgaTest.pixel(0, 399); -- cgit v0.12 From dfb518b8ff1de2423c7f8d3d9a3a63c258acb06d Mon Sep 17 00:00:00 2001 From: Chris Adams Date: Mon, 14 Nov 2011 11:57:21 +1000 Subject: Properly protect access to pixmap reader thread with mutex Previously, access to the data from the reader thread wasn't guarded properly, causing a crash when the reader thread was deleted prior to QDeclarativePixmapData (which then attempted to dereference the thread pointer to cancel the request), or in the case where a QDeclarativePixmapData was deleted after its QDeclarativePixmapReply was removed from the jobs queue but prior to processing. Reviewed-by: Martin Jones Task-number: QTBUG-22125 --- src/declarative/util/qdeclarativepixmapcache.cpp | 57 ++++++++++++++++------ .../qdeclarativeimage/data/qtbug_22125.qml | 44 +++++++++++++++++ .../qdeclarativeimage/tst_qdeclarativeimage.cpp | 41 ++++++++++++++++ 3 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 tests/auto/declarative/qdeclarativeimage/data/qtbug_22125.qml diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index 3557425..b23ac73 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -90,8 +90,9 @@ public: ~QDeclarativePixmapReply(); QDeclarativePixmapData *data; - QDeclarativePixmapReader *reader; + QDeclarativeEngine *engineForReader; // always access reader inside readerMutex. QSize requestSize; + QUrl url; bool loading; int redirectCount; @@ -147,6 +148,7 @@ public: void cancel(QDeclarativePixmapReply *rep); static QDeclarativePixmapReader *instance(QDeclarativeEngine *engine); + static QDeclarativePixmapReader *existingInstance(QDeclarativeEngine *engine); protected: void run(); @@ -176,6 +178,7 @@ private: static int downloadProgress; static int threadNetworkRequestDone; static QHash readers; +public: static QMutex readerMutex; }; @@ -326,6 +329,22 @@ QDeclarativePixmapReader::~QDeclarativePixmapReader() readers.remove(engine); readerMutex.unlock(); + mutex.lock(); + // manually cancel all outstanding jobs. + foreach (QDeclarativePixmapReply *reply, jobs) { + delete reply; + } + jobs.clear(); + QList activeJobs = replies.values(); + foreach (QDeclarativePixmapReply *reply, activeJobs) { + if (reply->loading) { + cancelled.append(reply); + reply->data = 0; + } + } + if (threadObject) threadObject->processJobs(); + mutex.unlock(); + eventLoopQuitHack->deleteLater(); wait(); } @@ -433,9 +452,8 @@ void QDeclarativePixmapReader::processJobs() if (!jobs.isEmpty() && replies.count() < IMAGEREQUEST_MAX_REQUEST_COUNT) { QDeclarativePixmapReply *runningJob = jobs.takeLast(); runningJob->loading = true; - - QUrl url = runningJob->data->url; - QSize requestSize = runningJob->data->requestSize; + QUrl url = runningJob->url; + QSize requestSize = runningJob->requestSize; locker.unlock(); processJob(runningJob, url, requestSize); locker.relock(); @@ -459,7 +477,6 @@ void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, c errorCode = QDeclarativePixmapReply::Loading; errorStr = QDeclarativePixmap::tr("Failed to get image from provider: %1").arg(url.toString()); } - mutex.lock(); if (!cancelled.contains(runningJob)) runningJob->postReply(errorCode, errorStr, readSize, image); mutex.unlock(); @@ -487,10 +504,8 @@ void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, c QNetworkRequest req(url); req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); QNetworkReply *reply = networkAccessManager()->get(req); - QMetaObject::connect(reply, replyDownloadProgress, runningJob, downloadProgress); QMetaObject::connect(reply, replyFinished, threadObject, threadNetworkRequestDone); - replies.insert(reply, runningJob); } } @@ -498,22 +513,27 @@ void QDeclarativePixmapReader::processJob(QDeclarativePixmapReply *runningJob, c QDeclarativePixmapReader *QDeclarativePixmapReader::instance(QDeclarativeEngine *engine) { - readerMutex.lock(); + // XXX NOTE: must be called within readerMutex locking. QDeclarativePixmapReader *reader = readers.value(engine); if (!reader) { reader = new QDeclarativePixmapReader(engine); readers.insert(engine, reader); } - readerMutex.unlock(); return reader; } +QDeclarativePixmapReader *QDeclarativePixmapReader::existingInstance(QDeclarativeEngine *engine) +{ + // XXX NOTE: must be called within readerMutex locking. + return readers.value(engine, 0); +} + QDeclarativePixmapReply *QDeclarativePixmapReader::getImage(QDeclarativePixmapData *data) { mutex.lock(); QDeclarativePixmapReply *reply = new QDeclarativePixmapReply(data); - reply->reader = this; + reply->engineForReader = engine; jobs.append(reply); // XXX if (threadObject) threadObject->processJobs(); @@ -692,7 +712,7 @@ void QDeclarativePixmapStore::flushCache() } QDeclarativePixmapReply::QDeclarativePixmapReply(QDeclarativePixmapData *d) -: data(d), reader(0), requestSize(d->requestSize), loading(false), redirectCount(0) +: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0) { if (finishedIndex == -1) { finishedIndex = QDeclarativePixmapReply::staticMetaObject.indexOfSignal("finished()"); @@ -750,8 +770,14 @@ void QDeclarativePixmapData::release() if (refCount == 0) { if (reply) { - reply->reader->cancel(reply); + QDeclarativePixmapReply *cancelReply = reply; + reply->data = 0; reply = 0; + QDeclarativePixmapReader::readerMutex.lock(); + QDeclarativePixmapReader *reader = QDeclarativePixmapReader::existingInstance(cancelReply->engineForReader); + if (reader) + reader->cancel(cancelReply); + QDeclarativePixmapReader::readerMutex.unlock(); } if (pixmapStatus == QDeclarativePixmap::Ready) { @@ -1013,13 +1039,12 @@ void QDeclarativePixmap::load(QDeclarativeEngine *engine, const QUrl &url, const if (!engine) return; - QDeclarativePixmapReader *reader = QDeclarativePixmapReader::instance(engine); - d = new QDeclarativePixmapData(url, requestSize); if (options & QDeclarativePixmap::Cache) d->addToCache(); - - d->reply = reader->getImage(d); + QDeclarativePixmapReader::readerMutex.lock(); + d->reply = QDeclarativePixmapReader::instance(engine)->getImage(d); + QDeclarativePixmapReader::readerMutex.unlock(); } else { d = *iter; d->addref(); diff --git a/tests/auto/declarative/qdeclarativeimage/data/qtbug_22125.qml b/tests/auto/declarative/qdeclarativeimage/data/qtbug_22125.qml new file mode 100644 index 0000000..8588028 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeimage/data/qtbug_22125.qml @@ -0,0 +1,44 @@ +import QtQuick 1.1 + +Item { + id: root + width: 800 + height: 800 + + GridView { + anchors.fill: parent + delegate: Image { + source: imagePath; + asynchronous: true + smooth: true + width: 200 + height: 200 + } + model: ListModel { + ListElement { + imagePath: "http://127.0.0.1:14451/big256.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/big256.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/big256.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/colors.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/colors1.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/big.jpeg" + } + ListElement { + imagePath: "http://127.0.0.1:14451/heart.png" + } + ListElement { + imagePath: "http://127.0.0.1:14451/green.png" + } + } + } +} diff --git a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp index a35d69a..f67c5b5 100644 --- a/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp +++ b/tests/auto/declarative/qdeclarativeimage/tst_qdeclarativeimage.cpp @@ -94,6 +94,7 @@ private slots: void resetSourceSize(); void testQtQuick11Attributes(); void testQtQuick11Attributes_data(); + void readerCrash_QTBUG_22125(); private: template @@ -762,6 +763,46 @@ void tst_qdeclarativeimage::testQtQuick11Attributes_data() << ":1 \"Image.cache\" is not available in QtQuick 1.0.\n"; } +void tst_qdeclarativeimage::readerCrash_QTBUG_22125() +{ + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/data/", TestHTTPServer::Delay); + + { + QDeclarativeView view(QUrl::fromLocalFile(SRCDIR "/data/qtbug_22125.qml")); + view.show(); + qApp->processEvents(); + qApp->processEvents(); + // shouldn't crash when the view drops out of scope due to + // QDeclarativePixmapData attempting to dereference a pointer to + // the destroyed reader. + } + + // shouldn't crash when deleting cancelled QDeclarativePixmapReplys. + QTest::qWait(1000); + qApp->processEvents(QEventLoop::DeferredDeletion); + } + + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.serveDirectory(SRCDIR "/data/"); + + { + QDeclarativeView view(QUrl::fromLocalFile(SRCDIR "/data/qtbug_22125.qml")); + view.show(); + qApp->processEvents(); + QTest::qWait(1000); + qApp->processEvents(); + // shouldn't crash when the view drops out of scope due to + // the reader thread accessing self-deleted QDeclarativePixmapReplys. + } + qApp->processEvents(); + } +} + /* Find an item with the specified objectName. If index is supplied then the item must also evaluate the {index} expression equal to index -- cgit v0.12 From d8aad8299125da82d95406577b281dfab5c12853 Mon Sep 17 00:00:00 2001 From: Sarah Smith Date: Wed, 16 Nov 2011 11:53:46 +1000 Subject: Fix compile with -qtnamespace Also fix header includes to match Qt core style. RevBy: Patrick Burke --- src/plugins/imageformats/tga/main.cpp | 4 ++-- src/plugins/imageformats/tga/qtgafile.cpp | 6 +++--- src/plugins/imageformats/tga/qtgafile.h | 8 ++++++-- src/plugins/imageformats/tga/qtgahandler.cpp | 7 +++---- src/plugins/imageformats/tga/qtgahandler.h | 2 +- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/plugins/imageformats/tga/main.cpp b/src/plugins/imageformats/tga/main.cpp index 8b1e070..5650be2 100644 --- a/src/plugins/imageformats/tga/main.cpp +++ b/src/plugins/imageformats/tga/main.cpp @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#include -#include +#include +#include #ifndef QT_NO_IMAGEFORMATPLUGIN diff --git a/src/plugins/imageformats/tga/qtgafile.cpp b/src/plugins/imageformats/tga/qtgafile.cpp index af7c876..dfb5a01 100644 --- a/src/plugins/imageformats/tga/qtgafile.cpp +++ b/src/plugins/imageformats/tga/qtgafile.cpp @@ -41,9 +41,9 @@ #include "qtgafile.h" -#include -#include -#include +#include +#include +#include struct TgaReader { diff --git a/src/plugins/imageformats/tga/qtgafile.h b/src/plugins/imageformats/tga/qtgafile.h index 034200e..68c5a0c 100644 --- a/src/plugins/imageformats/tga/qtgafile.h +++ b/src/plugins/imageformats/tga/qtgafile.h @@ -42,8 +42,10 @@ #ifndef QTGAFILE_H #define QTGAFILE_H -#include -#include +#include +#include + +QT_BEGIN_NAMESPACE class QIODevice; @@ -143,4 +145,6 @@ inline QTgaFile::Compression QTgaFile::compression() const return NoCompression; } +QT_END_NAMESPACE + #endif // QTGAFILE_H diff --git a/src/plugins/imageformats/tga/qtgahandler.cpp b/src/plugins/imageformats/tga/qtgahandler.cpp index 2464daa..ddea4f2 100644 --- a/src/plugins/imageformats/tga/qtgahandler.cpp +++ b/src/plugins/imageformats/tga/qtgahandler.cpp @@ -42,10 +42,9 @@ #include "qtgahandler.h" #include "qtgafile.h" -#include -#include -#include -#include +#include +#include +#include QT_BEGIN_NAMESPACE diff --git a/src/plugins/imageformats/tga/qtgahandler.h b/src/plugins/imageformats/tga/qtgahandler.h index 88e2b94..f0eb2b0 100644 --- a/src/plugins/imageformats/tga/qtgahandler.h +++ b/src/plugins/imageformats/tga/qtgahandler.h @@ -42,7 +42,7 @@ #ifndef QTGAHANDLER_H #define QTGAHANDLER_H -#include +#include QT_BEGIN_NAMESPACE -- cgit v0.12 From 7c5d13df7ec850196a6f9bb739deafbee32c77ee Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Wed, 16 Nov 2011 12:31:51 +0100 Subject: qmlplugindump: Fix dumping empty names for generated QMetaObjects. Task-number: QTCREATORBUG-6543 Reviewed-by: Roberto Raggi --- tools/qmlplugindump/main.cpp | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index e9547af..4f523b9 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -147,6 +147,31 @@ QByteArray convertToId(const QByteArray &cppName) return cppToId.value(cppName, cppName); } +QByteArray convertToId(const QMetaObject *mo) +{ + QByteArray className(mo->className()); + if (!className.isEmpty()) + return convertToId(className); + + // likely a metaobject generated for an extended qml object + if (mo->superClass()) { + className = convertToId(mo->superClass()); + className.append("_extended"); + return className; + } + + static QHash generatedNames; + className = generatedNames.value(mo); + if (!className.isEmpty()) + return className; + + qWarning() << "Found a QMetaObject without a className, generating a random name"; + className = QByteArray("error-unknown-name-"); + className.append(QByteArray::number(generatedNames.size())); + generatedNames.insert(mo, className); + return className; +} + QSet collectReachableMetaObjects(const QList &skip = QList()) { QSet metas; @@ -241,7 +266,7 @@ public: { qml->writeStartObject("Component"); - QByteArray id = convertToId(meta->className()); + QByteArray id = convertToId(meta); qml->writeScriptBinding(QLatin1String("name"), enquote(id)); for (int index = meta->classInfoCount() - 1 ; index >= 0 ; --index) { @@ -253,7 +278,7 @@ public: } if (meta->superClass()) - qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass()->className()))); + qml->writeScriptBinding(QLatin1String("prototype"), enquote(convertToId(meta->superClass()))); QSet qmlTypes = qmlTypesByCppName.value(meta->className()); if (!qmlTypes.isEmpty()) { @@ -284,7 +309,7 @@ public: if (const QMetaObject *attachedType = (*qmlTypes.begin())->attachedPropertiesType()) { qml->writeScriptBinding(QLatin1String("attachedType"), enquote( - convertToId(attachedType->className()))); + convertToId(attachedType))); } } @@ -624,7 +649,7 @@ int main(int argc, char *argv[]) // put the metaobjects into a map so they are always dumped in the same order QMap nameToMeta; foreach (const QMetaObject *meta, metas) - nameToMeta.insert(convertToId(meta->className()), meta); + nameToMeta.insert(convertToId(meta), meta); Dumper dumper(&qml); if (relocatable) -- cgit v0.12 From 665bc3951709f0d726cb82501a5bca684f3347a5 Mon Sep 17 00:00:00 2001 From: Sarah Smith Date: Thu, 17 Nov 2011 15:06:17 +1000 Subject: Fix failing unit tests. The test that was failing was the readFromDevice one - where the extension is not known. Looks as though image detection is required in a positive way, that is it is not enough to say I think I can read this file, and then fail if the format is "corrupt", you must be certain that the file was intended to be that format. In the case of TGA the original format has no magic byte header, and no consistent way to check if it really is a TGA file. With 2.0 the footer was added at the end, so that can be checked for confirming the file is TGA. However rejecting files which do not have this means that old TGA files will not be read. On a quick survey TGA files that have been used in applications so far all seem to be 2.0 TrueVision, so for now, lets just reject earlier files and see how it goes. Also add reading the tga test file to the readFromDevice test. --- src/plugins/imageformats/tga/qtgafile.cpp | 27 +++++++++++++++++++++++++++ src/plugins/imageformats/tga/qtgafile.h | 7 +++++++ tests/auto/qimagereader/tst_qimagereader.cpp | 3 +++ 3 files changed, 37 insertions(+) diff --git a/src/plugins/imageformats/tga/qtgafile.cpp b/src/plugins/imageformats/tga/qtgafile.cpp index dfb5a01..885a63c 100644 --- a/src/plugins/imageformats/tga/qtgafile.cpp +++ b/src/plugins/imageformats/tga/qtgafile.cpp @@ -158,6 +158,33 @@ QTgaFile::QTgaFile(QIODevice *device) mErrorMessage = QObject::tr("Image type not supported"); return; } + int bitsPerPixel = mHeader[PixelDepth]; + bool validDepth = (bitsPerPixel == 16 || bitsPerPixel == 24 || bitsPerPixel == 32); + if (!validDepth) + { + mErrorMessage = QObject::tr("Image dpeth not valid"); + } + int curPos = mDevice->pos(); + int fileBytes = mDevice->size(); + if (!mDevice->seek(fileBytes - FooterSize)) + { + mErrorMessage = QObject::tr("Could not seek to image read footer"); + return; + } + char footer[FooterSize]; + bytes = mDevice->read((char*)footer, FooterSize); + if (bytes != FooterSize) + { + mErrorMessage = QObject::tr("Could not read footer"); + } + if (qstrncmp(&footer[SignatureOffset], "TRUEVISION-XFILE", 16) != 0) + { + mErrorMessage = QObject::tr("Image type (non-TrueVision 2.0) not supported"); + } + if (!mDevice->seek(curPos)) + { + mErrorMessage = QObject::tr("Could not reset to read data"); + } } /*! diff --git a/src/plugins/imageformats/tga/qtgafile.h b/src/plugins/imageformats/tga/qtgafile.h index 68c5a0c..264c18b 100644 --- a/src/plugins/imageformats/tga/qtgafile.h +++ b/src/plugins/imageformats/tga/qtgafile.h @@ -73,6 +73,13 @@ public: HeaderSize = 18 }; + enum FooterOffset { + ExtensionOffset = 0, + DeveloperOffset = 4, + SignatureOffset = 8, + FooterSize = 26 + }; + QTgaFile(QIODevice *); ~QTgaFile(); diff --git a/tests/auto/qimagereader/tst_qimagereader.cpp b/tests/auto/qimagereader/tst_qimagereader.cpp index 2b867c3..54e8714 100644 --- a/tests/auto/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/qimagereader/tst_qimagereader.cpp @@ -1107,6 +1107,9 @@ void tst_QImageReader::readFromDevice_data() QTest::newRow("svg") << QString("rect.svg") << QByteArray("svg"); QTest::newRow("svgz") << QString("rect.svgz") << QByteArray("svgz"); #endif +#if defined QTEST_HAVE_TGA + QTest::newRow("tga") << QString("test-flag.tga") << QByteArray("tga"); +#endif } void tst_QImageReader::readFromDevice() -- cgit v0.12 From 8c898d681d12f3c67e396e7e2f2aa5522288aa63 Mon Sep 17 00:00:00 2001 From: Aurindam Jana Date: Thu, 17 Nov 2011 15:42:48 +0100 Subject: DeclarativeDebugServer: Instantiate QPluginLoader on heap The pluginloader is instantiated on heap with server as its parent so that the connection plugin instance remains loaded in memory. This prevents the plugin loader in QDeclarativeInspectorService to accidentally un-load the plugin while it's in use. Change-Id: I11c82ce595a336ba0fd1b243d23cb497fe355147 Reviewed-by: Kai Koehne --- src/declarative/debugger/qdeclarativedebugserver.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/declarative/debugger/qdeclarativedebugserver.cpp b/src/declarative/debugger/qdeclarativedebugserver.cpp index 12691b2..fc908f3 100644 --- a/src/declarative/debugger/qdeclarativedebugserver.cpp +++ b/src/declarative/debugger/qdeclarativedebugserver.cpp @@ -95,7 +95,7 @@ public: private: // private slot void _q_deliverMessage(const QString &serviceName, const QByteArray &message); - static QDeclarativeDebugServerConnection *loadConnectionPlugin(const QString &pluginName); + static QDeclarativeDebugServerConnection *loadConnectionPlugin(QPluginLoader *loader, const QString &pluginName); }; QDeclarativeDebugServerPrivate::QDeclarativeDebugServerPrivate() : @@ -118,7 +118,7 @@ void QDeclarativeDebugServerPrivate::advertisePlugins() } QDeclarativeDebugServerConnection *QDeclarativeDebugServerPrivate::loadConnectionPlugin( - const QString &pluginName) + QPluginLoader *loader, const QString &pluginName) { #ifndef QT_NO_LIBRARY QStringList pluginCandidates; @@ -135,17 +135,17 @@ QDeclarativeDebugServerConnection *QDeclarativeDebugServerPrivate::loadConnectio } foreach (const QString &pluginPath, pluginCandidates) { - QPluginLoader loader(pluginPath); - if (!loader.load()) { + loader->setFileName(pluginPath); + if (!loader->load()) { continue; } QDeclarativeDebugServerConnection *connection = 0; - if (QObject *instance = loader.instance()) + if (QObject *instance = loader->instance()) connection = qobject_cast(instance); if (connection) return connection; - loader.unload(); + loader->unload(); } #endif return 0; @@ -200,8 +200,9 @@ QDeclarativeDebugServer *QDeclarativeDebugServer::instance() if (ok) { server = new QDeclarativeDebugServer(); + QPluginLoader *loader = new QPluginLoader(server); QDeclarativeDebugServerConnection *connection - = QDeclarativeDebugServerPrivate::loadConnectionPlugin(pluginName); + = QDeclarativeDebugServerPrivate::loadConnectionPlugin(loader, pluginName); if (connection) { server->d_func()->connection = connection; -- cgit v0.12 From c0f782bc6c7eeebc5f6119c70248ac4be55dcc74 Mon Sep 17 00:00:00 2001 From: Sarah Smith Date: Fri, 18 Nov 2011 17:18:52 +1000 Subject: Sequential reading not supported. The read from device test should be better named "read from network device" because that is what is being tested here - so remove the tga test data, since sequential only (devices that cannot seek) are not supported (yet) for tga. --- tests/auto/qimagereader/tst_qimagereader.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/auto/qimagereader/tst_qimagereader.cpp b/tests/auto/qimagereader/tst_qimagereader.cpp index 54e8714..2b867c3 100644 --- a/tests/auto/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/qimagereader/tst_qimagereader.cpp @@ -1107,9 +1107,6 @@ void tst_QImageReader::readFromDevice_data() QTest::newRow("svg") << QString("rect.svg") << QByteArray("svg"); QTest::newRow("svgz") << QString("rect.svgz") << QByteArray("svgz"); #endif -#if defined QTEST_HAVE_TGA - QTest::newRow("tga") << QString("test-flag.tga") << QByteArray("tga"); -#endif } void tst_QImageReader::readFromDevice() -- cgit v0.12