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.
This commit is contained in:
Mirco Miranda 2024-06-19 22:18:45 +00:00 committed by Albert Astals Cid
parent 81b7263d73
commit b849e48ef4
18 changed files with 368 additions and 87 deletions

View File

@ -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) int main(int argc, char **argv)
{ {
QCoreApplication app(argc, argv); QCoreApplication app(argc, argv);
@ -208,11 +309,32 @@ int main(int argc, char **argv)
} }
continue; continue;
} }
OptionTest optionTest;
if (!optionTest.store(&inputReader)) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": error while reading options\n";
++failed;
continue;
}
if (!inputReader.read(&inputImage)) { if (!inputReader.read(&inputImage)) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed to load: " << inputReader.errorString() << "\n"; QTextStream(stdout) << "FAIL : " << fi.fileName() << ": failed to load: " << inputReader.errorString() << "\n";
++failed; ++failed;
continue; 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()) { if (expImage.width() != inputImage.width()) {
QTextStream(stdout) << "FAIL : " << fi.fileName() << ": width was " << inputImage.width() << " but " << expfilename << " width was " QTextStream(stdout) << "FAIL : " << fi.fileName() << ": width was " << inputImage.width() << " but " << expfilename << " width was "
<< expImage.width() << "\n"; << expImage.width() << "\n";

View File

@ -734,10 +734,12 @@ void EXRHandler::setOption(ImageOption option, const QVariant &value)
bool EXRHandler::supportsOption(ImageOption option) const bool EXRHandler::supportsOption(ImageOption option) const
{ {
if (option == QImageIOHandler::Size) { if (option == QImageIOHandler::Size) {
return true; if (auto d = device())
return !d->isSequential();
} }
if (option == QImageIOHandler::ImageFormat) { if (option == QImageIOHandler::ImageFormat) {
return true; if (auto d = device())
return !d->isSequential();
} }
if (option == QImageIOHandler::CompressionRatio) { if (option == QImageIOHandler::CompressionRatio) {
return true; return true;
@ -756,6 +758,9 @@ QVariant EXRHandler::option(ImageOption option) const
if (auto d = device()) { if (auto d = device()) {
// transactions works on both random and sequential devices // transactions works on both random and sequential devices
d->startTransaction(); d->startTransaction();
if (m_startPos > -1) {
d->seek(m_startPos);
}
try { try {
K_IStream istr(d, QByteArray()); K_IStream istr(d, QByteArray());
Imf::RgbaInputFile file(istr); Imf::RgbaInputFile file(istr);
@ -778,6 +783,9 @@ QVariant EXRHandler::option(ImageOption option) const
if (auto d = device()) { if (auto d = device()) {
// transactions works on both random and sequential devices // transactions works on both random and sequential devices
d->startTransaction(); d->startTransaction();
if (m_startPos > -1) {
d->seek(m_startPos);
}
try { try {
K_IStream istr(d, QByteArray()); K_IStream istr(d, QByteArray());
Imf::RgbaInputFile file(istr); Imf::RgbaInputFile file(istr);

View File

@ -269,13 +269,13 @@ bool HDRHandler::read(QImage *outImage)
{ {
QDataStream s(device()); QDataStream s(device());
QSize size = readHeaderSize(s.device()); m_imageSize = readHeaderSize(s.device());
if (!size.isValid()) { if (!m_imageSize.isValid()) {
return false; return false;
} }
QImage img; 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."; // qDebug() << "Error loading HDR file.";
return false; return false;
} }
@ -303,7 +303,9 @@ QVariant HDRHandler::option(ImageOption option) const
QVariant v; QVariant v;
if (option == QImageIOHandler::Size) { 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 // transactions works on both random and sequential devices
d->startTransaction(); d->startTransaction();
auto size = readHeaderSize(d); auto size = readHeaderSize(d);

View File

@ -22,6 +22,13 @@ public:
QVariant option(QImageIOHandler::ImageOption option) const override; QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private:
/*!
* \brief m_imageSize
* Image size cache used by option()
*/
QSize m_imageSize;
}; };
class HDRPlugin : public QImageIOPlugin class HDRPlugin : public QImageIOPlugin

View File

@ -21,7 +21,7 @@
Q_DECLARE_LOGGING_CATEGORY(LOG_PFMPLUGIN) Q_DECLARE_LOGGING_CATEGORY(LOG_PFMPLUGIN)
Q_LOGGING_CATEGORY(LOG_PFMPLUGIN, "kf.imageformats.plugins.pfm", QtWarningMsg) Q_LOGGING_CATEGORY(LOG_PFMPLUGIN, "kf.imageformats.plugins.pfm", QtWarningMsg)
class PfmHeader class PFMHeader
{ {
private: private:
/*! /*!
@ -61,7 +61,7 @@ private:
QDataStream::ByteOrder m_byteOrder; QDataStream::ByteOrder m_byteOrder;
public: public:
PfmHeader() : PFMHeader() :
m_bw(false), m_bw(false),
m_ps(false), m_ps(false),
m_width(0), m_width(0),
@ -157,7 +157,18 @@ public:
} }
} ; } ;
class PFMHandlerPrivate
{
public:
PFMHandlerPrivate() {}
~PFMHandlerPrivate() {}
PFMHeader m_header;
};
PFMHandler::PFMHandler() PFMHandler::PFMHandler()
: QImageIOHandler()
, d(new PFMHandlerPrivate)
{ {
} }
@ -177,7 +188,7 @@ bool PFMHandler::canRead(QIODevice *device)
return false; return false;
} }
PfmHeader h; PFMHeader h;
if (!h.peek(device)) { if (!h.peek(device)) {
return false; return false;
} }
@ -187,8 +198,7 @@ bool PFMHandler::canRead(QIODevice *device)
bool PFMHandler::read(QImage *image) bool PFMHandler::read(QImage *image)
{ {
PfmHeader header; auto&& header = d->m_header;
if (!header.read(device())) { if (!header.read(device())) {
qCWarning(LOG_PFMPLUGIN) << "PFMHandler::read() invalid header"; qCWarning(LOG_PFMPLUGIN) << "PFMHandler::read() invalid header";
return false; return false;
@ -265,27 +275,33 @@ QVariant PFMHandler::option(ImageOption option) const
QVariant v; QVariant v;
if (option == QImageIOHandler::Size) { if (option == QImageIOHandler::Size) {
if (auto d = device()) { auto&& h = d->m_header;
PfmHeader h; if (h.isValid()) {
if (h.peek(d)) { v = QVariant::fromValue(h.size());
} else if (auto dev = device()) {
if (h.peek(dev)) {
v = QVariant::fromValue(h.size()); v = QVariant::fromValue(h.size());
} }
} }
} }
if (option == QImageIOHandler::ImageFormat) { if (option == QImageIOHandler::ImageFormat) {
if (auto d = device()) { auto&& h = d->m_header;
PfmHeader h; if (h.isValid()) {
if (h.peek(d)) { v = QVariant::fromValue(h.format());
} else if (auto dev = device()) {
if (h.peek(dev)) {
v = QVariant::fromValue(h.format()); v = QVariant::fromValue(h.format());
} }
} }
} }
if (option == QImageIOHandler::Endianness) { if (option == QImageIOHandler::Endianness) {
if (auto d = device()) { auto&& h = d->m_header;
PfmHeader h; if (h.isValid()) {
if (h.peek(d)) { v = QVariant::fromValue(h.byteOrder());
} else if (auto dev = device()) {
if (h.peek(dev)) {
v = QVariant::fromValue(h.byteOrder()); v = QVariant::fromValue(h.byteOrder());
} }
} }

View File

@ -9,7 +9,9 @@
#define KIMG_PFM_P_H #define KIMG_PFM_P_H
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QScopedPointer>
class PFMHandlerPrivate;
class PFMHandler : public QImageIOHandler class PFMHandler : public QImageIOHandler
{ {
public: public:
@ -22,6 +24,9 @@ public:
QVariant option(QImageIOHandler::ImageOption option) const override; QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private:
const QScopedPointer<PFMHandlerPrivate> d;
}; };
class PFMPlugin : public QImageIOPlugin class PFMPlugin : public QImageIOPlugin

View File

@ -112,6 +112,10 @@ enum LayerId : quint32 {
}; };
struct PSDHeader { struct PSDHeader {
PSDHeader() {
memset(this, 0, sizeof(PSDHeader));
}
uint signature; uint signature;
ushort version; ushort version;
uchar reserved[6]; uchar reserved[6];
@ -1375,7 +1379,17 @@ static bool LoadPSD(QDataStream &stream, const PSDHeader &header, QImage &img)
} // Private } // Private
class PSDHandlerPrivate
{
public:
PSDHandlerPrivate() {}
~PSDHandlerPrivate() {}
PSDHeader m_header;
};
PSDHandler::PSDHandler() PSDHandler::PSDHandler()
: QImageIOHandler()
, d(new PSDHandlerPrivate)
{ {
} }
@ -1393,7 +1407,7 @@ bool PSDHandler::read(QImage *image)
QDataStream s(device()); QDataStream s(device());
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
PSDHeader header; auto&& header = d->m_header;
s >> header; s >> header;
// Check image file format. // Check image file format.
@ -1430,18 +1444,20 @@ QVariant PSDHandler::option(ImageOption option) const
QVariant v; QVariant v;
if (option == QImageIOHandler::Size) { 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 // transactions works on both random and sequential devices
d->startTransaction(); dev->startTransaction();
auto ba = d->read(sizeof(PSDHeader)); auto ba = dev->read(sizeof(PSDHeader));
d->rollbackTransaction(); dev->rollbackTransaction();
QDataStream s(ba); QDataStream s(ba);
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
PSDHeader header;
s >> header; s >> header;
if (s.status() == QDataStream::Ok && IsValid(header)) if (s.status() == QDataStream::Ok && IsValid(header))
v = QVariant::fromValue(QSize(header.width, header.height)); v = QVariant::fromValue(QSize(header.width, header.height));
} }

View File

@ -9,7 +9,9 @@
#define KIMG_PSD_P_H #define KIMG_PSD_P_H
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QScopedPointer>
class PSDHandlerPrivate;
class PSDHandler : public QImageIOHandler class PSDHandler : public QImageIOHandler
{ {
public: public:
@ -22,6 +24,9 @@ public:
QVariant option(QImageIOHandler::ImageOption option) const override; QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private:
const QScopedPointer<PSDHandlerPrivate> d;
}; };
class PSDPlugin : public QImageIOPlugin class PSDPlugin : public QImageIOPlugin

View File

@ -15,7 +15,7 @@
Q_DECLARE_LOGGING_CATEGORY(LOG_PXRPLUGIN) Q_DECLARE_LOGGING_CATEGORY(LOG_PXRPLUGIN)
Q_LOGGING_CATEGORY(LOG_PXRPLUGIN, "kf.imageformats.plugins.pxr", QtWarningMsg) Q_LOGGING_CATEGORY(LOG_PXRPLUGIN, "kf.imageformats.plugins.pxr", QtWarningMsg)
class PxrHeader class PXRHeader
{ {
private: private:
QByteArray m_rawHeader; QByteArray m_rawHeader;
@ -29,7 +29,7 @@ private:
} }
public: public:
PxrHeader() PXRHeader()
{ {
} }
@ -139,7 +139,18 @@ public:
} }
}; };
class PXRHandlerPrivate
{
public:
PXRHandlerPrivate() {}
~PXRHandlerPrivate() {}
PXRHeader m_header;
};
PXRHandler::PXRHandler() PXRHandler::PXRHandler()
: QImageIOHandler()
, d(new PXRHandlerPrivate)
{ {
} }
@ -159,7 +170,7 @@ bool PXRHandler::canRead(QIODevice *device)
return false; return false;
} }
PxrHeader h; PXRHeader h;
if (!h.peek(device)) { if (!h.peek(device)) {
return false; return false;
} }
@ -169,7 +180,7 @@ bool PXRHandler::canRead(QIODevice *device)
bool PXRHandler::read(QImage *image) bool PXRHandler::read(QImage *image)
{ {
PxrHeader header; auto&& header = d->m_header;
if (!header.read(device())) { if (!header.read(device())) {
qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() invalid header"; qCWarning(LOG_PXRPLUGIN) << "PXRHandler::read() invalid header";
@ -217,8 +228,10 @@ QVariant PXRHandler::option(ImageOption option) const
QVariant v; QVariant v;
if (option == QImageIOHandler::Size) { if (option == QImageIOHandler::Size) {
if (auto d = device()) { auto&& h = d->m_header;
PxrHeader h; if (h.isValid()) {
v = QVariant::fromValue(h.size());
} else if (auto d = device()) {
if (h.peek(d)) { if (h.peek(d)) {
v = QVariant::fromValue(h.size()); v = QVariant::fromValue(h.size());
} }
@ -226,8 +239,10 @@ QVariant PXRHandler::option(ImageOption option) const
} }
if (option == QImageIOHandler::ImageFormat) { if (option == QImageIOHandler::ImageFormat) {
if (auto d = device()) { auto&& h = d->m_header;
PxrHeader h; if (h.isValid()) {
v = QVariant::fromValue(h.format());
} else if (auto d = device()) {
if (h.peek(d)) { if (h.peek(d)) {
v = QVariant::fromValue(h.format()); v = QVariant::fromValue(h.format());
} }

View File

@ -9,7 +9,9 @@
#define KIMG_PXR_P_H #define KIMG_PXR_P_H
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QScopedPointer>
class PXRHandlerPrivate;
class PXRHandler : public QImageIOHandler class PXRHandler : public QImageIOHandler
{ {
public: public:
@ -22,6 +24,9 @@ public:
QVariant option(QImageIOHandler::ImageOption option) const override; QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private:
const QScopedPointer<PXRHandlerPrivate> d;
}; };
class PXRPlugin : public QImageIOPlugin class PXRPlugin : public QImageIOPlugin

View File

@ -31,6 +31,18 @@ namespace // Private
#define QOI_END_STREAM_PAD 8 #define QOI_END_STREAM_PAD 8
struct QoiHeader { 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 MagicNumber;
quint32 Width; quint32 Width;
quint32 Height; quint32 Height;
@ -297,7 +309,19 @@ static bool SaveQOI(QIODevice *device, const QoiHeader &qoi, const QImage &img)
} // namespace } // namespace
class QOIHandlerPrivate
{
public:
QOIHandlerPrivate() {}
~QOIHandlerPrivate() {}
QoiHeader m_header;
};
QOIHandler::QOIHandler() QOIHandler::QOIHandler()
: QImageIOHandler()
, d(new QOIHandlerPrivate)
{ {
} }
@ -328,7 +352,7 @@ bool QOIHandler::canRead(QIODevice *device)
QDataStream stream(head); QDataStream stream(head);
stream.setByteOrder(QDataStream::BigEndian); stream.setByteOrder(QDataStream::BigEndian);
QoiHeader qoi = {0, 0, 0, 0, 2}; QoiHeader qoi;
stream >> qoi; stream >> qoi;
return IsSupported(qoi); return IsSupported(qoi);
@ -340,7 +364,7 @@ bool QOIHandler::read(QImage *image)
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
// Read image header // Read image header
QoiHeader qoi = {0, 0, 0, 0, 2}; auto&& qoi = d->m_header;
s >> qoi; s >> qoi;
// Check if file is supported // Check if file is supported
@ -402,7 +426,10 @@ QVariant QOIHandler::option(ImageOption option) const
QVariant v; QVariant v;
if (option == QImageIOHandler::Size) { 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 // transactions works on both random and sequential devices
d->startTransaction(); d->startTransaction();
auto ba = d->read(sizeof(QoiHeader)); auto ba = d->read(sizeof(QoiHeader));
@ -410,10 +437,7 @@ QVariant QOIHandler::option(ImageOption option) const
QDataStream s(ba); QDataStream s(ba);
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
QoiHeader header = {0, 0, 0, 0, 2};
s >> header; s >> header;
if (s.status() == QDataStream::Ok && IsSupported(header)) { if (s.status() == QDataStream::Ok && IsSupported(header)) {
v = QVariant::fromValue(QSize(header.Width, header.Height)); v = QVariant::fromValue(QSize(header.Width, header.Height));
} }
@ -421,7 +445,10 @@ QVariant QOIHandler::option(ImageOption option) const
} }
if (option == QImageIOHandler::ImageFormat) { 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 // transactions works on both random and sequential devices
d->startTransaction(); d->startTransaction();
auto ba = d->read(sizeof(QoiHeader)); auto ba = d->read(sizeof(QoiHeader));
@ -429,10 +456,7 @@ QVariant QOIHandler::option(ImageOption option) const
QDataStream s(ba); QDataStream s(ba);
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
QoiHeader header = {0, 0, 0, 0, 2};
s >> header; s >> header;
if (s.status() == QDataStream::Ok && IsSupported(header)) { if (s.status() == QDataStream::Ok && IsSupported(header)) {
v = QVariant::fromValue(imageFormat(header)); v = QVariant::fromValue(imageFormat(header));
} }

View File

@ -9,7 +9,9 @@
#define KIMG_QOI_P_H #define KIMG_QOI_P_H
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QScopedPointer>
class QOIHandlerPrivate;
class QOIHandler : public QImageIOHandler class QOIHandler : public QImageIOHandler
{ {
public: public:
@ -23,6 +25,9 @@ public:
QVariant option(QImageIOHandler::ImageOption option) const override; QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private:
const QScopedPointer<QOIHandlerPrivate> d;
}; };
class QOIPlugin : public QImageIOPlugin class QOIPlugin : public QImageIOPlugin

View File

@ -336,7 +336,19 @@ static bool LoadRAS(QDataStream &s, const RasHeader &ras, QImage &img)
} }
} // namespace } // namespace
class RASHandlerPrivate
{
public:
RASHandlerPrivate() {}
~RASHandlerPrivate() {}
RasHeader m_header;
};
RASHandler::RASHandler() RASHandler::RASHandler()
: QImageIOHandler()
, d(new RASHandlerPrivate)
{ {
} }
@ -384,7 +396,7 @@ bool RASHandler::read(QImage *outImage)
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
// Read image header. // Read image header.
RasHeader ras; auto&& ras = d->m_header;
s >> ras; s >> ras;
if (ras.ColorMapLength > kMaxQVectorSize) { if (ras.ColorMapLength > kMaxQVectorSize) {
@ -426,18 +438,19 @@ QVariant RASHandler::option(ImageOption option) const
QVariant v; QVariant v;
if (option == QImageIOHandler::Size) { 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 // transactions works on both random and sequential devices
d->startTransaction(); dev->startTransaction();
auto ba = d->read(RasHeader::SIZE); auto ba = dev->read(RasHeader::SIZE);
d->rollbackTransaction(); dev->rollbackTransaction();
QDataStream s(ba); QDataStream s(ba);
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
RasHeader header;
s >> header; s >> header;
if (s.status() == QDataStream::Ok && IsSupported(header)) { if (s.status() == QDataStream::Ok && IsSupported(header)) {
v = QVariant::fromValue(QSize(header.Width, header.Height)); v = QVariant::fromValue(QSize(header.Width, header.Height));
} }
@ -445,18 +458,19 @@ QVariant RASHandler::option(ImageOption option) const
} }
if (option == QImageIOHandler::ImageFormat) { 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 // transactions works on both random and sequential devices
d->startTransaction(); dev->startTransaction();
auto ba = d->read(RasHeader::SIZE); auto ba = dev->read(RasHeader::SIZE);
d->rollbackTransaction(); dev->rollbackTransaction();
QDataStream s(ba); QDataStream s(ba);
s.setByteOrder(QDataStream::BigEndian); s.setByteOrder(QDataStream::BigEndian);
RasHeader header;
s >> header; s >> header;
if (s.status() == QDataStream::Ok && IsSupported(header)) { if (s.status() == QDataStream::Ok && IsSupported(header)) {
v = QVariant::fromValue(imageFormat(header)); v = QVariant::fromValue(imageFormat(header));
} }

View File

@ -10,7 +10,9 @@
#define KIMG_RAS_P_H #define KIMG_RAS_P_H
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QScopedPointer>
class RASHandlerPrivate;
class RASHandler : public QImageIOHandler class RASHandler : public QImageIOHandler
{ {
public: public:
@ -23,6 +25,9 @@ public:
QVariant option(QImageIOHandler::ImageOption option) const override; QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private:
const QScopedPointer<RASHandlerPrivate> d;
}; };
class RASPlugin : public QImageIOPlugin class RASPlugin : public QImageIOPlugin

View File

@ -56,18 +56,18 @@ enum TGAType {
/** Tga Header. */ /** Tga Header. */
struct TgaHeader { struct TgaHeader {
uchar id_length; uchar id_length = 0;
uchar colormap_type; uchar colormap_type = 0;
uchar image_type; uchar image_type = 0;
ushort colormap_index; ushort colormap_index = 0;
ushort colormap_length; ushort colormap_length = 0;
uchar colormap_size; uchar colormap_size = 0;
ushort x_origin; ushort x_origin = 0;
ushort y_origin; ushort y_origin = 0;
ushort width; ushort width = 0;
ushort height; ushort height = 0;
uchar pixel_size; uchar pixel_size = 0;
uchar flags; uchar flags = 0;
enum { enum {
SIZE = 18, SIZE = 18,
@ -407,7 +407,18 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img)
} // namespace } // namespace
class TGAHandlerPrivate
{
public:
TGAHandlerPrivate() {}
~TGAHandlerPrivate() {}
TgaHeader m_header;
};
TGAHandler::TGAHandler() TGAHandler::TGAHandler()
: QImageIOHandler()
, d(new TGAHandlerPrivate)
{ {
} }
@ -424,20 +435,20 @@ bool TGAHandler::read(QImage *outImage)
{ {
// qDebug() << "Loading TGA file!"; // qDebug() << "Loading TGA file!";
auto d = device(); auto dev = device();
TgaHeader tga; auto&& tga = d->m_header;
if (!peekHeader(d, tga) || !IsSupported(tga)) { if (!peekHeader(dev, tga) || !IsSupported(tga)) {
// qDebug() << "This TGA file is not valid."; // qDebug() << "This TGA file is not valid.";
return false; return false;
} }
if (d->isSequential()) { if (dev->isSequential()) {
d->read(TgaHeader::SIZE + tga.id_length); dev->read(TgaHeader::SIZE + tga.id_length);
} else { } 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); s.setByteOrder(QDataStream::LittleEndian);
// Check image file format. // Check image file format.
@ -519,18 +530,22 @@ QVariant TGAHandler::option(ImageOption option) const
QVariant v; QVariant v;
if (option == QImageIOHandler::Size) { if (option == QImageIOHandler::Size) {
if (auto d = device()) { auto&& header = d->m_header;
TgaHeader header; if (IsSupported(header)) {
if (peekHeader(d, header) && 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)); v = QVariant::fromValue(QSize(header.width, header.height));
} }
} }
} }
if (option == QImageIOHandler::ImageFormat) { if (option == QImageIOHandler::ImageFormat) {
if (auto d = device()) { auto&& header = d->m_header;
TgaHeader header; if (IsSupported(header)) {
if (peekHeader(d, header) && IsSupported(header)) { v = QVariant::fromValue(imageFormat(header));
} else if (auto dev = device()) {
if (peekHeader(dev, header) && IsSupported(header)) {
v = QVariant::fromValue(imageFormat(header)); v = QVariant::fromValue(imageFormat(header));
} }
} }

View File

@ -9,7 +9,9 @@
#define KIMG_TGA_P_H #define KIMG_TGA_P_H
#include <QImageIOPlugin> #include <QImageIOPlugin>
#include <QScopedPointer>
class TGAHandlerPrivate;
class TGAHandler : public QImageIOHandler class TGAHandler : public QImageIOHandler
{ {
public: public:
@ -23,6 +25,9 @@ public:
QVariant option(QImageIOHandler::ImageOption option) const override; QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private:
const QScopedPointer<TGAHandlerPrivate> d;
}; };
class TGAPlugin : public QImageIOPlugin class TGAPlugin : public QImageIOPlugin

View File

@ -4149,7 +4149,9 @@ bool XCFHandler::canRead() const
bool XCFHandler::read(QImage *image) bool XCFHandler::read(QImage *image)
{ {
XCFImageFormat xcfif; XCFImageFormat xcfif;
return xcfif.readXCF(device(), image); auto ok = xcfif.readXCF(device(), image);
m_imageSize = image->size();
return ok;
} }
bool XCFHandler::write(const QImage &) bool XCFHandler::write(const QImage &)
@ -4169,6 +4171,9 @@ QVariant XCFHandler::option(ImageOption option) const
QVariant v; QVariant v;
if (option == QImageIOHandler::Size) { if (option == QImageIOHandler::Size) {
if (!m_imageSize.isEmpty()) {
return m_imageSize;
}
/* /*
* The image structure always starts at offset 0 in the XCF file. * The image structure always starts at offset 0 in the XCF file.
* byte[9] "gimp xcf " File type identification * byte[9] "gimp xcf " File type identification
@ -4181,7 +4186,7 @@ QVariant XCFHandler::option(ImageOption option) const
* uint32 width Width of canvas * uint32 width Width of canvas
* uint32 height Height of canvas * uint32 height Height of canvas
*/ */
if (auto d = device()) { else if (auto d = device()) {
// transactions works on both random and sequential devices // transactions works on both random and sequential devices
d->startTransaction(); d->startTransaction();
auto ba9 = d->read(9); // "gimp xcf " auto ba9 = d->read(9); // "gimp xcf "

View File

@ -24,6 +24,13 @@ public:
QVariant option(QImageIOHandler::ImageOption option) const override; QVariant option(QImageIOHandler::ImageOption option) const override;
static bool canRead(QIODevice *device); static bool canRead(QIODevice *device);
private:
/*!
* \brief m_imageSize
* Image size cache used by option()
*/
QSize m_imageSize;
}; };
class XCFPlugin : public QImageIOPlugin class XCFPlugin : public QImageIOPlugin