Improve support for QImages in QDataStream read transactions
QImage's operator>>(QDataStream&) did not set an error mode on the stream on read failures. That would break QDataStream transactions. Since the current QImage serialization cannot differentiate between truncated and corrupted data, we set the ReadPastEnd error as expected by the transaction system. Also specify the expected file format on decoding QImage from stream, to avoid all the format handlers' canRead() being invoked. This is necessary since some of them may call ungetChar(), which fails when the stream is in a transaction. Also add testing of this feature to the QDataStram transaction autotest. That required a slight rewrite of the fake sequential QIODevice subclass. The previous implementation had incorrect behavior of peek(), which is required by QImage decoders. Task-number: QTBUG-70875 Change-Id: If3f1ca7186ad1e6ca0e6e8ea81d2b2fbece6ea01 Reviewed-by: Alex Trotsenko <alex1973tr@gmail.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
0479379d24
commit
f2f040ae1c
@ -3750,7 +3750,9 @@ QDataStream &operator>>(QDataStream &s, QImage &image)
|
||||
return s;
|
||||
}
|
||||
}
|
||||
image = QImageReader(s.device(), 0).read();
|
||||
image = QImageReader(s.device(), s.version() == 1 ? "bmp" : "png").read();
|
||||
if (image.isNull() && s.version() >= 5)
|
||||
s.setStatus(QDataStream::ReadPastEnd);
|
||||
return s;
|
||||
}
|
||||
#endif // QT_NO_DATASTREAM
|
||||
|
@ -2211,25 +2211,22 @@ void tst_QDataStream::setVersion()
|
||||
}
|
||||
}
|
||||
|
||||
class SequentialBuffer : public QBuffer
|
||||
class SequentialBuffer : public QIODevice
|
||||
{
|
||||
public:
|
||||
SequentialBuffer(QByteArray *data) : QBuffer(data) { offset = 0; }
|
||||
SequentialBuffer(QByteArray *data) : QIODevice() { buf.setBuffer(data); }
|
||||
|
||||
bool isSequential() const { return true; }
|
||||
bool seek(qint64 pos) { offset = pos; return QBuffer::seek(pos); }
|
||||
qint64 pos() const { return qint64(offset); }
|
||||
bool isSequential() const override { return true; }
|
||||
bool open(OpenMode mode) override { return buf.open(mode) && QIODevice::open(mode | QIODevice::Unbuffered); }
|
||||
void close() override { buf.close(); QIODevice::close(); }
|
||||
qint64 bytesAvailable() const override { return QIODevice::bytesAvailable() + buf.bytesAvailable(); }
|
||||
|
||||
protected:
|
||||
qint64 readData(char *data, qint64 maxSize)
|
||||
{
|
||||
qint64 ret = QBuffer::readData(data, maxSize);
|
||||
offset += ret;
|
||||
return ret;
|
||||
}
|
||||
qint64 readData(char *data, qint64 maxSize) override { return buf.read(data, maxSize); }
|
||||
qint64 writeData(const char *data, qint64 maxSize) override { return buf.write(data, maxSize); }
|
||||
|
||||
private:
|
||||
int offset;
|
||||
QBuffer buf;
|
||||
};
|
||||
|
||||
void tst_QDataStream::skipRawData_data()
|
||||
@ -3329,15 +3326,21 @@ void tst_QDataStream::transaction_data()
|
||||
QTest::addColumn<bool>("bData");
|
||||
QTest::addColumn<float>("fData");
|
||||
QTest::addColumn<double>("dData");
|
||||
QTest::addColumn<QImage>("imgData");
|
||||
QTest::addColumn<QByteArray>("strData");
|
||||
QTest::addColumn<QByteArray>("rawData");
|
||||
|
||||
QImage img1(open_xpm);
|
||||
QImage img2;
|
||||
QImage img3(50, 50, QImage::Format_ARGB32);
|
||||
img3.fill(qRgba(12, 34, 56, 78));
|
||||
|
||||
QTest::newRow("1") << qint8(1) << qint16(2) << qint32(3) << qint64(4) << true << 5.0f
|
||||
<< double(6.0) << QByteArray("Hello world!") << QByteArray("Qt rocks!");
|
||||
<< double(6.0) << img1 << QByteArray("Hello world!") << QByteArray("Qt rocks!");
|
||||
QTest::newRow("2") << qint8(1 << 6) << qint16(1 << 14) << qint32(1 << 30) << qint64Data(3) << false << 123.0f
|
||||
<< double(234.0) << stringData(5).toUtf8() << stringData(6).toUtf8();
|
||||
<< double(234.0) << img2 << stringData(5).toUtf8() << stringData(6).toUtf8();
|
||||
QTest::newRow("3") << qint8(-1) << qint16(-2) << qint32(-3) << qint64(-4) << true << -123.0f
|
||||
<< double(-234.0) << stringData(3).toUtf8() << stringData(4).toUtf8();
|
||||
<< double(-234.0) << img3 << stringData(3).toUtf8() << stringData(4).toUtf8();
|
||||
}
|
||||
|
||||
void tst_QDataStream::transaction()
|
||||
@ -3351,6 +3354,7 @@ void tst_QDataStream::transaction()
|
||||
QFETCH(bool, bData);
|
||||
QFETCH(float, fData);
|
||||
QFETCH(double, dData);
|
||||
QFETCH(QImage, imgData);
|
||||
QFETCH(QByteArray, strData);
|
||||
QFETCH(QByteArray, rawData);
|
||||
|
||||
@ -3358,12 +3362,13 @@ void tst_QDataStream::transaction()
|
||||
QDataStream stream(&testBuffer, QIODevice::WriteOnly);
|
||||
|
||||
stream << i8Data << i16Data << i32Data << i64Data
|
||||
<< bData << fData << dData << strData.constData();
|
||||
<< bData << fData << dData << imgData << strData.constData();
|
||||
stream.writeRawData(rawData.constData(), rawData.size());
|
||||
}
|
||||
|
||||
for (int splitPos = 0; splitPos <= testBuffer.size(); ++splitPos) {
|
||||
QByteArray readBuffer(testBuffer.left(splitPos));
|
||||
|
||||
SequentialBuffer dev(&readBuffer);
|
||||
dev.open(QIODevice::ReadOnly);
|
||||
QDataStream stream(&dev);
|
||||
@ -3375,12 +3380,13 @@ void tst_QDataStream::transaction()
|
||||
bool b;
|
||||
float f;
|
||||
double d;
|
||||
QImage img;
|
||||
char *str;
|
||||
QByteArray raw(rawData.size(), 0);
|
||||
|
||||
forever {
|
||||
stream.startTransaction();
|
||||
stream >> i8 >> i16 >> i32 >> i64 >> b >> f >> d >> str;
|
||||
stream >> i8 >> i16 >> i32 >> i64 >> b >> f >> d >> img >> str;
|
||||
stream.readRawData(raw.data(), raw.size());
|
||||
|
||||
if (stream.commitTransaction())
|
||||
@ -3402,6 +3408,7 @@ void tst_QDataStream::transaction()
|
||||
QCOMPARE(b, bData);
|
||||
QCOMPARE(f, fData);
|
||||
QCOMPARE(d, dData);
|
||||
QCOMPARE(img, imgData);
|
||||
QVERIFY(strData == str);
|
||||
delete [] str;
|
||||
QCOMPARE(raw, rawData);
|
||||
|
Loading…
Reference in New Issue
Block a user