From b849e48ef4da785d1ae2d2946f257047975ee496 Mon Sep 17 00:00:00 2001 From: Mirco Miranda Date: Wed, 19 Jun 2024 22:18:45 +0000 Subject: [PATCH] Fixed wrong plugin options behaviour While working on MR !230 I noticed that the options read I entered into several plugins could not be read after reading the image. **The patch fixes problems reading options in plugins and adds option checking in the readtest.cpp.** In particular, the reading test does the following additional actions: - reads options before reading the image; - compare the options read with the options returned by the reader after reading the image; - compares the format and size of the returned image with the format and size returned by the reader. --- autotests/readtest.cpp | 122 +++++++++++++++++++++++++++++++++++++++ src/imageformats/exr.cpp | 12 +++- src/imageformats/hdr.cpp | 10 ++-- src/imageformats/hdr_p.h | 7 +++ src/imageformats/pfm.cpp | 44 +++++++++----- src/imageformats/pfm_p.h | 5 ++ src/imageformats/psd.cpp | 30 +++++++--- src/imageformats/psd_p.h | 5 ++ src/imageformats/pxr.cpp | 31 +++++++--- src/imageformats/pxr_p.h | 5 ++ src/imageformats/qoi.cpp | 44 ++++++++++---- src/imageformats/qoi_p.h | 5 ++ src/imageformats/ras.cpp | 44 +++++++++----- src/imageformats/ras_p.h | 5 ++ src/imageformats/tga.cpp | 65 +++++++++++++-------- src/imageformats/tga_p.h | 5 ++ src/imageformats/xcf.cpp | 9 ++- src/imageformats/xcf_p.h | 7 +++ 18 files changed, 368 insertions(+), 87 deletions(-) diff --git a/autotests/readtest.cpp b/autotests/readtest.cpp index 2685c58..de62219 100644 --- a/autotests/readtest.cpp +++ b/autotests/readtest.cpp @@ -90,6 +90,107 @@ static QImage::Format preferredFormat(QImage::Format fmt) } } +/*! + * \brief The OptionTest class + * Class for testing image options. + * Supports the most common options: + * - Size + * - ImageFormat + * - ImageTransformation (rotations) + * \todo Add missing options if needed. + */ +class OptionTest +{ +public: + OptionTest() + : m_size(QSize()) + , m_format(QImage::Format_Invalid) + , m_transformations(QImageIOHandler::TransformationNone) + { + } + + OptionTest(const OptionTest&) = default; + OptionTest& operator =(const OptionTest&) = default; + + /*! + * \brief store + * Stores the supported options of the reader. + * \param reader + * \return True on success, otherwise false. + */ + bool store(const QImageReader *reader = nullptr) + { + if (reader == nullptr) { + return false; + } + bool ok = true; + if (reader->supportsOption(QImageIOHandler::Size)) { + m_size = reader->size(); + if (m_size.isEmpty()) + ok = false; + } + if (reader->supportsOption(QImageIOHandler::ImageFormat)) { + m_format = reader->imageFormat(); + if (m_format == QImage::Format_Invalid) + ok = false; + } + if (reader->supportsOption(QImageIOHandler::ImageTransformation)) { + m_transformations = reader->transformation(); + if (m_transformations < 0 || m_transformations > 7) + ok = false; + } + return ok; + } + + + /*! + * \brief compare + * Compare the stored values with the ones read from the image reader. + * \param reader + * \return True on success, otherwise false. + */ + bool compare(const QImageReader *reader) + { + if (reader == nullptr) { + return false; + } + bool ok = true; + if (reader->supportsOption(QImageIOHandler::Size)) { + ok = ok && (m_size == reader->size()); + } + if (reader->supportsOption(QImageIOHandler::ImageFormat)) { + ok = ok && (m_format == reader->imageFormat()); + } + if (reader->supportsOption(QImageIOHandler::ImageTransformation)) { + ok = ok && (m_transformations == reader->transformation()); + } + return ok; + } + + /*! + * \brief compare + * Compare the image properties with the ones stored. + * \param image + * \return True on success, otherwise false. + */ + bool compare(const QImage& image) + { + bool ok = true; + if (!m_size.isEmpty()) { + ok = ok && (m_size == image.size()); + } + if (m_format != QImage::Format_Invalid) { + ok = ok && (m_format == image.format()); + } + return ok; + } + +private: + QSize m_size; + QImage::Format m_format; + QImageIOHandler::Transformations m_transformations; +}; + int main(int argc, char **argv) { QCoreApplication app(argc, argv); @@ -208,11 +309,32 @@ int main(int argc, char **argv) } continue; } + + OptionTest optionTest; + if (!optionTest.store(&inputReader)) { + QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n"; + ++failed; + continue; + } + if (!inputReader.read(&inputImage)) { QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed to load: " << inputReader.errorString() << "\n"; ++failed; continue; } + + if (!optionTest.compare(&inputReader)) { + QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while comparing options\n"; + ++failed; + continue; + } + + if (!optionTest.compare(inputImage)) { + QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while comparing the image properties with options\n"; + ++failed; + continue; + } + if (expImage.width() != inputImage.width()) { QTextStream(stdout) << "FAIL : " << fi.fileName() << ": width was " << inputImage.width() << " but " << expfilename << " width was " << expImage.width() << "\n"; diff --git a/src/imageformats/exr.cpp b/src/imageformats/exr.cpp index 239c58f..f1ff045 100644 --- a/src/imageformats/exr.cpp +++ b/src/imageformats/exr.cpp @@ -734,10 +734,12 @@ void EXRHandler::setOption(ImageOption option, const QVariant &value) bool EXRHandler::supportsOption(ImageOption option) const { if (option == QImageIOHandler::Size) { - return true; + if (auto d = device()) + return !d->isSequential(); } if (option == QImageIOHandler::ImageFormat) { - return true; + if (auto d = device()) + return !d->isSequential(); } if (option == QImageIOHandler::CompressionRatio) { return true; @@ -756,6 +758,9 @@ QVariant EXRHandler::option(ImageOption option) const if (auto d = device()) { // transactions works on both random and sequential devices d->startTransaction(); + if (m_startPos > -1) { + d->seek(m_startPos); + } try { K_IStream istr(d, QByteArray()); Imf::RgbaInputFile file(istr); @@ -778,6 +783,9 @@ QVariant EXRHandler::option(ImageOption option) const if (auto d = device()) { // transactions works on both random and sequential devices d->startTransaction(); + if (m_startPos > -1) { + d->seek(m_startPos); + } try { K_IStream istr(d, QByteArray()); Imf::RgbaInputFile file(istr); diff --git a/src/imageformats/hdr.cpp b/src/imageformats/hdr.cpp index 3d4e384..0db702a 100644 --- a/src/imageformats/hdr.cpp +++ b/src/imageformats/hdr.cpp @@ -269,13 +269,13 @@ bool HDRHandler::read(QImage *outImage) { QDataStream s(device()); - QSize size = readHeaderSize(s.device()); - if (!size.isValid()) { + m_imageSize = readHeaderSize(s.device()); + if (!m_imageSize.isValid()) { return false; } QImage img; - if (!LoadHDR(s, size.width(), size.height(), img)) { + if (!LoadHDR(s, m_imageSize.width(), m_imageSize.height(), img)) { // qDebug() << "Error loading HDR file."; return false; } @@ -303,7 +303,9 @@ QVariant HDRHandler::option(ImageOption option) const QVariant v; if (option == QImageIOHandler::Size) { - if (auto d = device()) { + if (!m_imageSize.isEmpty()) { + v = QVariant::fromValue(m_imageSize); + } else if (auto d = device()) { // transactions works on both random and sequential devices d->startTransaction(); auto size = readHeaderSize(d); diff --git a/src/imageformats/hdr_p.h b/src/imageformats/hdr_p.h index e1450c6..f0ac600 100644 --- a/src/imageformats/hdr_p.h +++ b/src/imageformats/hdr_p.h @@ -22,6 +22,13 @@ public: QVariant option(QImageIOHandler::ImageOption option) const override; static bool canRead(QIODevice *device); + +private: + /*! + * \brief m_imageSize + * Image size cache used by option() + */ + QSize m_imageSize; }; class HDRPlugin : public QImageIOPlugin diff --git a/src/imageformats/pfm.cpp b/src/imageformats/pfm.cpp index 3fd764f..5c462f0 100644 --- a/src/imageformats/pfm.cpp +++ b/src/imageformats/pfm.cpp @@ -21,7 +21,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_PFMPLUGIN) Q_LOGGING_CATEGORY(LOG_PFMPLUGIN, "kf.imageformats.plugins.pfm", QtWarningMsg) -class PfmHeader +class PFMHeader { private: /*! @@ -61,7 +61,7 @@ private: QDataStream::ByteOrder m_byteOrder; public: - PfmHeader() : + PFMHeader() : m_bw(false), m_ps(false), m_width(0), @@ -157,7 +157,18 @@ public: } } ; +class PFMHandlerPrivate +{ +public: + PFMHandlerPrivate() {} + ~PFMHandlerPrivate() {} + + PFMHeader m_header; +}; + PFMHandler::PFMHandler() + : QImageIOHandler() + , d(new PFMHandlerPrivate) { } @@ -177,7 +188,7 @@ bool PFMHandler::canRead(QIODevice *device) return false; } - PfmHeader h; + PFMHeader h; if (!h.peek(device)) { return false; } @@ -187,8 +198,7 @@ bool PFMHandler::canRead(QIODevice *device) bool PFMHandler::read(QImage *image) { - PfmHeader header; - + auto&& header = d->m_header; if (!header.read(device())) { qCWarning(LOG_PFMPLUGIN) << "PFMHandler::read() invalid header"; return false; @@ -265,27 +275,33 @@ QVariant PFMHandler::option(ImageOption option) const QVariant v; if (option == QImageIOHandler::Size) { - if (auto d = device()) { - PfmHeader h; - if (h.peek(d)) { + auto&& h = d->m_header; + if (h.isValid()) { + v = QVariant::fromValue(h.size()); + } else if (auto dev = device()) { + if (h.peek(dev)) { v = QVariant::fromValue(h.size()); } } } if (option == QImageIOHandler::ImageFormat) { - if (auto d = device()) { - PfmHeader h; - if (h.peek(d)) { + auto&& h = d->m_header; + if (h.isValid()) { + v = QVariant::fromValue(h.format()); + } else if (auto dev = device()) { + if (h.peek(dev)) { v = QVariant::fromValue(h.format()); } } } if (option == QImageIOHandler::Endianness) { - if (auto d = device()) { - PfmHeader h; - if (h.peek(d)) { + auto&& h = d->m_header; + if (h.isValid()) { + v = QVariant::fromValue(h.byteOrder()); + } else if (auto dev = device()) { + if (h.peek(dev)) { v = QVariant::fromValue(h.byteOrder()); } } diff --git a/src/imageformats/pfm_p.h b/src/imageformats/pfm_p.h index b605d87..3ef73a8 100644 --- a/src/imageformats/pfm_p.h +++ b/src/imageformats/pfm_p.h @@ -9,7 +9,9 @@ #define KIMG_PFM_P_H #include +#include +class PFMHandlerPrivate; class PFMHandler : public QImageIOHandler { public: @@ -22,6 +24,9 @@ public: QVariant option(QImageIOHandler::ImageOption option) const override; static bool canRead(QIODevice *device); + +private: + const QScopedPointer d; }; class PFMPlugin : public QImageIOPlugin diff --git a/src/imageformats/psd.cpp b/src/imageformats/psd.cpp index ba7b54a..21bd30d 100644 --- a/src/imageformats/psd.cpp +++ b/src/imageformats/psd.cpp @@ -112,6 +112,10 @@ enum LayerId : quint32 { }; struct PSDHeader { + PSDHeader() { + memset(this, 0, sizeof(PSDHeader)); + } + uint signature; ushort version; uchar reserved[6]; @@ -1375,7 +1379,17 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img) } // Private +class PSDHandlerPrivate +{ +public: + PSDHandlerPrivate() {} + ~PSDHandlerPrivate() {} + PSDHeader m_header; +}; + PSDHandler::PSDHandler() + : QImageIOHandler() + , d(new PSDHandlerPrivate) { } @@ -1393,7 +1407,7 @@ bool PSDHandler::read(QImage *image) QDataStream s(device()); s.setByteOrder(QDataStream::BigEndian); - PSDHeader header; + auto&& header = d->m_header; s >> header; // Check image file format. @@ -1430,18 +1444,20 @@ QVariant PSDHandler::option(ImageOption option) const QVariant v; if (option == QImageIOHandler::Size) { - if (auto d = device()) { + auto&& header = d->m_header; + if (IsValid(header)) { + v = QVariant::fromValue(QSize(header.width, header.height)); + } + else if (auto dev = device()) { // transactions works on both random and sequential devices - d->startTransaction(); - auto ba = d->read(sizeof(PSDHeader)); - d->rollbackTransaction(); + dev->startTransaction(); + auto ba = dev->read(sizeof(PSDHeader)); + dev->rollbackTransaction(); QDataStream s(ba); s.setByteOrder(QDataStream::BigEndian); - PSDHeader header; s >> header; - if (s.status() == QDataStream::Ok && IsValid(header)) v = QVariant::fromValue(QSize(header.width, header.height)); } diff --git a/src/imageformats/psd_p.h b/src/imageformats/psd_p.h index f0014a0..b2be87a 100644 --- a/src/imageformats/psd_p.h +++ b/src/imageformats/psd_p.h @@ -9,7 +9,9 @@ #define KIMG_PSD_P_H #include +#include +class PSDHandlerPrivate; class PSDHandler : public QImageIOHandler { public: @@ -22,6 +24,9 @@ public: QVariant option(QImageIOHandler::ImageOption option) const override; static bool canRead(QIODevice *device); + +private: + const QScopedPointer d; }; class PSDPlugin : public QImageIOPlugin diff --git a/src/imageformats/pxr.cpp b/src/imageformats/pxr.cpp index a598dc3..181bddb 100644 --- a/src/imageformats/pxr.cpp +++ b/src/imageformats/pxr.cpp @@ -15,7 +15,7 @@ Q_DECLARE_LOGGING_CATEGORY(LOG_PXRPLUGIN) Q_LOGGING_CATEGORY(LOG_PXRPLUGIN, "kf.imageformats.plugins.pxr", QtWarningMsg) -class PxrHeader +class PXRHeader { private: QByteArray m_rawHeader; @@ -29,7 +29,7 @@ private: } public: - PxrHeader() + PXRHeader() { } @@ -139,7 +139,18 @@ public: } }; +class PXRHandlerPrivate +{ +public: + PXRHandlerPrivate() {} + ~PXRHandlerPrivate() {} + + PXRHeader m_header; +}; + PXRHandler::PXRHandler() + : QImageIOHandler() + , d(new PXRHandlerPrivate) { } @@ -159,7 +170,7 @@ bool PXRHandler::canRead(QIODevice *device) return false; } - PxrHeader h; + PXRHeader h; if (!h.peek(device)) { return false; } @@ -169,7 +180,7 @@ bool PXRHandler::canRead(QIODevice *device) bool PXRHandler::read(QImage *image) { - PxrHeader header; + auto&& header = d->m_header; if (!header.read(device())) { qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() invalid header"; @@ -217,8 +228,10 @@ QVariant PXRHandler::option(ImageOption option) const QVariant v; if (option == QImageIOHandler::Size) { - if (auto d = device()) { - PxrHeader h; + auto&& h = d->m_header; + if (h.isValid()) { + v = QVariant::fromValue(h.size()); + } else if (auto d = device()) { if (h.peek(d)) { v = QVariant::fromValue(h.size()); } @@ -226,8 +239,10 @@ QVariant PXRHandler::option(ImageOption option) const } if (option == QImageIOHandler::ImageFormat) { - if (auto d = device()) { - PxrHeader h; + auto&& h = d->m_header; + if (h.isValid()) { + v = QVariant::fromValue(h.format()); + } else if (auto d = device()) { if (h.peek(d)) { v = QVariant::fromValue(h.format()); } diff --git a/src/imageformats/pxr_p.h b/src/imageformats/pxr_p.h index ddf38bb..c3e1586 100644 --- a/src/imageformats/pxr_p.h +++ b/src/imageformats/pxr_p.h @@ -9,7 +9,9 @@ #define KIMG_PXR_P_H #include +#include +class PXRHandlerPrivate; class PXRHandler : public QImageIOHandler { public: @@ -22,6 +24,9 @@ public: QVariant option(QImageIOHandler::ImageOption option) const override; static bool canRead(QIODevice *device); + +private: + const QScopedPointer d; }; class PXRPlugin : public QImageIOPlugin diff --git a/src/imageformats/qoi.cpp b/src/imageformats/qoi.cpp index 985dadc..fdc7a5e 100644 --- a/src/imageformats/qoi.cpp +++ b/src/imageformats/qoi.cpp @@ -31,6 +31,18 @@ namespace // Private #define QOI_END_STREAM_PAD 8 struct QoiHeader { + QoiHeader() + : MagicNumber(0) + , Width(0) + , Height(0) + , Channels(0) + , Colorspace(2) + { + } + + QoiHeader(const QoiHeader&) = default; + QoiHeader& operator=(const QoiHeader&) = default; + quint32 MagicNumber; quint32 Width; quint32 Height; @@ -297,7 +309,19 @@ static bool SaveQOI(QIODevice *device, const QoiHeader &qoi, const QImage &img) } // namespace +class QOIHandlerPrivate +{ +public: + QOIHandlerPrivate() {} + ~QOIHandlerPrivate() {} + + QoiHeader m_header; +}; + + QOIHandler::QOIHandler() + : QImageIOHandler() + , d(new QOIHandlerPrivate) { } @@ -328,7 +352,7 @@ bool QOIHandler::canRead(QIODevice *device) QDataStream stream(head); stream.setByteOrder(QDataStream::BigEndian); - QoiHeader qoi = {0, 0, 0, 0, 2}; + QoiHeader qoi; stream >> qoi; return IsSupported(qoi); @@ -340,7 +364,7 @@ bool QOIHandler::read(QImage *image) s.setByteOrder(QDataStream::BigEndian); // Read image header - QoiHeader qoi = {0, 0, 0, 0, 2}; + auto&& qoi = d->m_header; s >> qoi; // Check if file is supported @@ -402,7 +426,10 @@ QVariant QOIHandler::option(ImageOption option) const QVariant v; if (option == QImageIOHandler::Size) { - if (auto d = device()) { + auto&& header = d->m_header; + if (IsSupported(header)) { + v = QVariant::fromValue(QSize(header.Width, header.Height)); + } else if (auto d = device()) { // transactions works on both random and sequential devices d->startTransaction(); auto ba = d->read(sizeof(QoiHeader)); @@ -410,10 +437,7 @@ QVariant QOIHandler::option(ImageOption option) const QDataStream s(ba); s.setByteOrder(QDataStream::BigEndian); - - QoiHeader header = {0, 0, 0, 0, 2}; s >> header; - if (s.status() == QDataStream::Ok && IsSupported(header)) { v = QVariant::fromValue(QSize(header.Width, header.Height)); } @@ -421,7 +445,10 @@ QVariant QOIHandler::option(ImageOption option) const } if (option == QImageIOHandler::ImageFormat) { - if (auto d = device()) { + auto&& header = d->m_header; + if (IsSupported(header)) { + v = QVariant::fromValue(imageFormat(header)); + } else if (auto d = device()) { // transactions works on both random and sequential devices d->startTransaction(); auto ba = d->read(sizeof(QoiHeader)); @@ -429,10 +456,7 @@ QVariant QOIHandler::option(ImageOption option) const QDataStream s(ba); s.setByteOrder(QDataStream::BigEndian); - - QoiHeader header = {0, 0, 0, 0, 2}; s >> header; - if (s.status() == QDataStream::Ok && IsSupported(header)) { v = QVariant::fromValue(imageFormat(header)); } diff --git a/src/imageformats/qoi_p.h b/src/imageformats/qoi_p.h index b125e9f..992623c 100644 --- a/src/imageformats/qoi_p.h +++ b/src/imageformats/qoi_p.h @@ -9,7 +9,9 @@ #define KIMG_QOI_P_H #include +#include +class QOIHandlerPrivate; class QOIHandler : public QImageIOHandler { public: @@ -23,6 +25,9 @@ public: QVariant option(QImageIOHandler::ImageOption option) const override; static bool canRead(QIODevice *device); + +private: + const QScopedPointer d; }; class QOIPlugin : public QImageIOPlugin diff --git a/src/imageformats/ras.cpp b/src/imageformats/ras.cpp index b347258..801dfa0 100644 --- a/src/imageformats/ras.cpp +++ b/src/imageformats/ras.cpp @@ -336,7 +336,19 @@ static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img) } } // namespace +class RASHandlerPrivate +{ +public: + RASHandlerPrivate() {} + ~RASHandlerPrivate() {} + + RasHeader m_header; +}; + + RASHandler::RASHandler() + : QImageIOHandler() + , d(new RASHandlerPrivate) { } @@ -384,7 +396,7 @@ bool RASHandler::read(QImage *outImage) s.setByteOrder(QDataStream::BigEndian); // Read image header. - RasHeader ras; + auto&& ras = d->m_header; s >> ras; if (ras.ColorMapLength > kMaxQVectorSize) { @@ -426,18 +438,19 @@ QVariant RASHandler::option(ImageOption option) const QVariant v; if (option == QImageIOHandler::Size) { - if (auto d = device()) { + auto&& header = d->m_header; + if (IsSupported(header)) { + v = QVariant::fromValue(QSize(header.Width, header.Height)); + } + else if (auto dev = device()) { // transactions works on both random and sequential devices - d->startTransaction(); - auto ba = d->read(RasHeader::SIZE); - d->rollbackTransaction(); + dev->startTransaction(); + auto ba = dev->read(RasHeader::SIZE); + dev->rollbackTransaction(); QDataStream s(ba); s.setByteOrder(QDataStream::BigEndian); - - RasHeader header; s >> header; - if (s.status() == QDataStream::Ok && IsSupported(header)) { v = QVariant::fromValue(QSize(header.Width, header.Height)); } @@ -445,18 +458,19 @@ QVariant RASHandler::option(ImageOption option) const } if (option == QImageIOHandler::ImageFormat) { - if (auto d = device()) { + auto&& header = d->m_header; + if (IsSupported(header)) { + v = QVariant::fromValue(imageFormat(header)); + } + else if (auto dev = device()) { // transactions works on both random and sequential devices - d->startTransaction(); - auto ba = d->read(RasHeader::SIZE); - d->rollbackTransaction(); + dev->startTransaction(); + auto ba = dev->read(RasHeader::SIZE); + dev->rollbackTransaction(); QDataStream s(ba); s.setByteOrder(QDataStream::BigEndian); - - RasHeader header; s >> header; - if (s.status() == QDataStream::Ok && IsSupported(header)) { v = QVariant::fromValue(imageFormat(header)); } diff --git a/src/imageformats/ras_p.h b/src/imageformats/ras_p.h index 99a82a0..702f8f1 100644 --- a/src/imageformats/ras_p.h +++ b/src/imageformats/ras_p.h @@ -10,7 +10,9 @@ #define KIMG_RAS_P_H #include +#include +class RASHandlerPrivate; class RASHandler : public QImageIOHandler { public: @@ -23,6 +25,9 @@ public: QVariant option(QImageIOHandler::ImageOption option) const override; static bool canRead(QIODevice *device); + +private: + const QScopedPointer d; }; class RASPlugin : public QImageIOPlugin diff --git a/src/imageformats/tga.cpp b/src/imageformats/tga.cpp index f78f216..3f816be 100644 --- a/src/imageformats/tga.cpp +++ b/src/imageformats/tga.cpp @@ -56,18 +56,18 @@ enum TGAType { /** Tga Header. */ struct TgaHeader { - uchar id_length; - uchar colormap_type; - uchar image_type; - ushort colormap_index; - ushort colormap_length; - uchar colormap_size; - ushort x_origin; - ushort y_origin; - ushort width; - ushort height; - uchar pixel_size; - uchar flags; + uchar id_length = 0; + uchar colormap_type = 0; + uchar image_type = 0; + ushort colormap_index = 0; + ushort colormap_length = 0; + uchar colormap_size = 0; + ushort x_origin = 0; + ushort y_origin = 0; + ushort width = 0; + ushort height = 0; + uchar pixel_size = 0; + uchar flags = 0; enum { SIZE = 18, @@ -407,7 +407,18 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img) } // namespace +class TGAHandlerPrivate +{ +public: + TGAHandlerPrivate() {} + ~TGAHandlerPrivate() {} + + TgaHeader m_header; +}; + TGAHandler::TGAHandler() + : QImageIOHandler() + , d(new TGAHandlerPrivate) { } @@ -424,20 +435,20 @@ bool TGAHandler::read(QImage *outImage) { // qDebug() << "Loading TGA file!"; - auto d = device(); - TgaHeader tga; - if (!peekHeader(d, tga) || !IsSupported(tga)) { + auto dev = device(); + auto&& tga = d->m_header; + if (!peekHeader(dev, tga) || !IsSupported(tga)) { // qDebug() << "This TGA file is not valid."; return false; } - if (d->isSequential()) { - d->read(TgaHeader::SIZE + tga.id_length); + if (dev->isSequential()) { + dev->read(TgaHeader::SIZE + tga.id_length); } else { - d->seek(TgaHeader::SIZE + tga.id_length); + dev->seek(TgaHeader::SIZE + tga.id_length); } - QDataStream s(d); + QDataStream s(dev); s.setByteOrder(QDataStream::LittleEndian); // Check image file format. @@ -519,18 +530,22 @@ QVariant TGAHandler::option(ImageOption option) const QVariant v; if (option == QImageIOHandler::Size) { - if (auto d = device()) { - TgaHeader header; - if (peekHeader(d, header) && IsSupported(header)) { + auto&& header = d->m_header; + if (IsSupported(header)) { + v = QVariant::fromValue(QSize(header.width, header.height)); + } else if (auto dev = device()) { + if (peekHeader(dev, header) && IsSupported(header)) { v = QVariant::fromValue(QSize(header.width, header.height)); } } } if (option == QImageIOHandler::ImageFormat) { - if (auto d = device()) { - TgaHeader header; - if (peekHeader(d, header) && IsSupported(header)) { + auto&& header = d->m_header; + if (IsSupported(header)) { + v = QVariant::fromValue(imageFormat(header)); + } else if (auto dev = device()) { + if (peekHeader(dev, header) && IsSupported(header)) { v = QVariant::fromValue(imageFormat(header)); } } diff --git a/src/imageformats/tga_p.h b/src/imageformats/tga_p.h index 9354227..64a451c 100644 --- a/src/imageformats/tga_p.h +++ b/src/imageformats/tga_p.h @@ -9,7 +9,9 @@ #define KIMG_TGA_P_H #include +#include +class TGAHandlerPrivate; class TGAHandler : public QImageIOHandler { public: @@ -23,6 +25,9 @@ public: QVariant option(QImageIOHandler::ImageOption option) const override; static bool canRead(QIODevice *device); + +private: + const QScopedPointer d; }; class TGAPlugin : public QImageIOPlugin diff --git a/src/imageformats/xcf.cpp b/src/imageformats/xcf.cpp index 4cfe565..b2fb773 100644 --- a/src/imageformats/xcf.cpp +++ b/src/imageformats/xcf.cpp @@ -4149,7 +4149,9 @@ bool XCFHandler::canRead() const bool XCFHandler::read(QImage *image) { XCFImageFormat xcfif; - return xcfif.readXCF(device(), image); + auto ok = xcfif.readXCF(device(), image); + m_imageSize = image->size(); + return ok; } bool XCFHandler::write(const QImage &) @@ -4169,6 +4171,9 @@ QVariant XCFHandler::option(ImageOption option) const QVariant v; if (option == QImageIOHandler::Size) { + if (!m_imageSize.isEmpty()) { + return m_imageSize; + } /* * The image structure always starts at offset 0 in the XCF file. * byte[9] "gimp xcf " File type identification @@ -4181,7 +4186,7 @@ QVariant XCFHandler::option(ImageOption option) const * uint32 width Width of canvas * uint32 height Height of canvas */ - if (auto d = device()) { + else if (auto d = device()) { // transactions works on both random and sequential devices d->startTransaction(); auto ba9 = d->read(9); // "gimp xcf " diff --git a/src/imageformats/xcf_p.h b/src/imageformats/xcf_p.h index 4c31d4f..0324797 100644 --- a/src/imageformats/xcf_p.h +++ b/src/imageformats/xcf_p.h @@ -24,6 +24,13 @@ public: QVariant option(QImageIOHandler::ImageOption option) const override; static bool canRead(QIODevice *device); + +private: + /*! + * \brief m_imageSize + * Image size cache used by option() + */ + QSize m_imageSize; }; class XCFPlugin : public QImageIOPlugin