diff --git a/.gitattributes b/.gitattributes index 4ddaa1c..d13bdd5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -11,3 +11,5 @@ autotests/read/hdr/fake_earth.hdr binary autotests/read/hdr/rgb.hdr binary autotests/read/hdr/rgb-landscape.hdr binary autotests/read/hdr/rgb-portrait.hdr binary +autotests/read/pfm/testcard_gray_half.phm binary +autotests/read/pfm/testcard_rgb_half.phm binary diff --git a/README.md b/README.md index 201d2ae..552e72d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The following image formats have read-only support: - Krita (kra) - OpenRaster (ora) - Pixar raster (pxr) -- Portable FloatMap (pfm) +- Portable FloatMap/HalfMap (pfm, phm) - Photoshop documents (psd, psb, pdd, psdt) - Radiance HDR (hdr) - Scitex CT (sct) diff --git a/autotests/read/pfm/testcard_gray_half.phm b/autotests/read/pfm/testcard_gray_half.phm new file mode 100644 index 0000000..7dea091 Binary files /dev/null and b/autotests/read/pfm/testcard_gray_half.phm differ diff --git a/autotests/read/pfm/testcard_gray_half.png b/autotests/read/pfm/testcard_gray_half.png new file mode 100644 index 0000000..8997b93 Binary files /dev/null and b/autotests/read/pfm/testcard_gray_half.png differ diff --git a/autotests/read/pfm/testcard_rgb_half.phm b/autotests/read/pfm/testcard_rgb_half.phm new file mode 100644 index 0000000..4771e94 Binary files /dev/null and b/autotests/read/pfm/testcard_rgb_half.phm differ diff --git a/autotests/read/pfm/testcard_rgb_half.png b/autotests/read/pfm/testcard_rgb_half.png new file mode 100644 index 0000000..58263b0 Binary files /dev/null and b/autotests/read/pfm/testcard_rgb_half.png differ diff --git a/src/imageformats/pfm.cpp b/src/imageformats/pfm.cpp index 0a5111d..a04f9ed 100644 --- a/src/imageformats/pfm.cpp +++ b/src/imageformats/pfm.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,11 @@ private: */ bool m_bw; + /*! + * \brief m_half True if half float. + */ + bool m_half; + /*! * \brief m_ps True if saved by Photoshop (Photoshop variant). * @@ -61,12 +67,13 @@ private: QDataStream::ByteOrder m_byteOrder; public: - PFMHeader() : - m_bw(false), - m_ps(false), - m_width(0), - m_height(0), - m_byteOrder(QDataStream::BigEndian) + PFMHeader() + : m_bw(false) + , m_half(false) + , m_ps(false) + , m_width(0) + , m_height(0) + , m_byteOrder(QDataStream::BigEndian) { } @@ -81,6 +88,11 @@ public: return m_bw; } + bool isHalfFloat() const + { + return m_half; + } + bool isPhotoshop() const { return m_ps; @@ -109,7 +121,7 @@ public: QImage::Format format() const { if (isValid()) { - return QImage::Format_RGBX32FPx4; + return m_half ? QImage::Format_RGBX16FPx4 : QImage::Format_RGBX32FPx4; } return QImage::Format_Invalid; } @@ -118,8 +130,16 @@ public: { auto pf = d->read(3); if (pf == QByteArray("PF\n")) { + m_half = false; m_bw = false; } else if (pf == QByteArray("Pf\n")) { + m_half = false; + m_bw = true; + } else if (pf == QByteArray("PH\n")) { + m_half = true; + m_bw = false; + } else if (pf == QByteArray("Ph\n")) { + m_half = true; m_bw = true; } else { return false; @@ -196,6 +216,28 @@ bool PFMHandler::canRead(QIODevice *device) return h.isValid(); } +template +bool readScanLine(qint32 y, QDataStream &s, QImage &img, const PFMHeader &header) +{ + auto bw = header.isBlackAndWhite(); + auto line = reinterpret_cast(img.scanLine(header.isPhotoshop() ? y : img.height() - y - 1)); + for (auto x = 0, n = img.width() * 4; x < n; x += 4) { + line[x + 3] = T(1); + s >> line[x]; + if (bw) { + line[x + 1] = line[x]; + line[x + 2] = line[x]; + } else { + s >> line[x + 1]; + s >> line[x + 2]; + } + if (s.status() != QDataStream::Ok) { + return false; + } + } + return true; +} + bool PFMHandler::read(QImage *image) { auto&& header = d->m_header; @@ -215,22 +257,15 @@ bool PFMHandler::read(QImage *image) } for (auto y = 0, h = img.height(); y < h; ++y) { - auto bw = header.isBlackAndWhite(); - auto line = reinterpret_cast(img.scanLine(header.isPhotoshop() ? y : h - y - 1)); - for (auto x = 0, n = img.width() * 4; x < n; x += 4) { - line[x + 3] = float(1); - s >> line[x]; - if (bw) { - line[x + 1] = line[x]; - line[x + 2] = line[x]; - } else { - s >> line[x + 1]; - s >> line[x + 2]; - } - if (s.status() != QDataStream::Ok) { - qCWarning(LOG_PFMPLUGIN) << "PFMHandler::read() detected corrupted data"; - return false; - } + auto ok = false; + if (header.isHalfFloat()) { + ok = readScanLine(y, s, img, header); + } else { + ok = readScanLine(y, s, img, header); + } + if (!ok) { + qCWarning(LOG_PFMPLUGIN) << "PFMHandler::read() detected corrupted data"; + return false; } } @@ -296,7 +331,7 @@ QVariant PFMHandler::option(ImageOption option) const QImageIOPlugin::Capabilities PFMPlugin::capabilities(QIODevice *device, const QByteArray &format) const { - if (format == "pfm") { + if (format == "pfm" || format == "phm") { return Capabilities(CanRead); } if (!format.isEmpty()) { diff --git a/src/imageformats/pfm.json b/src/imageformats/pfm.json index 4c4d855..d8ba603 100644 --- a/src/imageformats/pfm.json +++ b/src/imageformats/pfm.json @@ -1,4 +1,4 @@ { - "Keys": [ "pfm" ], - "MimeTypes": [ "image/x-pfm" ] + "Keys": [ "pfm", "phm" ], + "MimeTypes": [ "image/x-pfm", "image/x-phm" ] }