diff --git a/src/imageformats/psd.cpp b/src/imageformats/psd.cpp index d35ed40..33f3298 100644 --- a/src/imageformats/psd.cpp +++ b/src/imageformats/psd.cpp @@ -384,9 +384,7 @@ static PSDImageResourceSection readImageResourceSection(QDataStream &s, bool *ok *ok = false; break; } - // NOTE: Qt device::read() and QDataStream::readRawData() could read less data than specified. - // The read code should be improved. - irb.data = dev->read(dataSize); + irb.data = deviceRead(dev, dataSize); } auto read = irb.data.size(); if (read > 0) { diff --git a/src/imageformats/util_p.h b/src/imageformats/util_p.h index 2a6d6b3..980f793 100644 --- a/src/imageformats/util_p.h +++ b/src/imageformats/util_p.h @@ -12,6 +12,7 @@ #include #include +#include // Default maximum width and height for the large image plugins. #ifndef KIF_LARGE_IMAGE_PIXEL_LIMIT @@ -132,4 +133,48 @@ inline float fppm2dpi(qint32 ppm, bool *ok = nullptr) return ppm2dpi_T(ppm, ok); } +/*! + * \brief deviceRead + * A function for reading from devices. + * + * Similar to QIODevice::read(qint64) but limits the initial memory allocation. Useful for reading corrupted streams. + * \param d The device. + * \param maxSize The maximum size to read. + * \return The byte array read. + */ +static QByteArray deviceRead(QIODevice *d, qint64 maxSize) +{ + if (d == nullptr) { + return{}; + } + + const qint64 blockSize = 32 * 1024 * 1024; + auto devSize = d->isSequential() ? qint64() : d->size(); + + if (devSize > 0) { + // random access device + maxSize = std::min(maxSize, devSize - d->pos()); + return d->read(maxSize); + } else if (maxSize < blockSize) { + // small read + return d->read(maxSize); + } + + // sequential device + QByteArray ba; + while (ba.size() < maxSize) { + auto toRead = std::min(blockSize, maxSize - ba.size()); + if (toRead + ba.size() > QByteArray::maxSize()) { + break; + } + auto tmp = d->read(toRead); + if (tmp.isEmpty()) { + break; + } + ba.append(tmp); + } + + return ba; +} + #endif // UTIL_P_H