mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-05-25 07:50:23 -04:00
PFM: extended to half float format
The Portable HalfMap is a format supported by ImageMagick. The test cases was generated by converting pfm to phm using ImageMagick: `convert image.pfm image.phm`.
This commit is contained in:
parent
2ea724c241
commit
c38a1a0248
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -11,3 +11,5 @@ autotests/read/hdr/fake_earth.hdr binary
|
|||||||
autotests/read/hdr/rgb.hdr binary
|
autotests/read/hdr/rgb.hdr binary
|
||||||
autotests/read/hdr/rgb-landscape.hdr binary
|
autotests/read/hdr/rgb-landscape.hdr binary
|
||||||
autotests/read/hdr/rgb-portrait.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
|
||||||
|
@ -19,7 +19,7 @@ The following image formats have read-only support:
|
|||||||
- Krita (kra)
|
- Krita (kra)
|
||||||
- OpenRaster (ora)
|
- OpenRaster (ora)
|
||||||
- Pixar raster (pxr)
|
- Pixar raster (pxr)
|
||||||
- Portable FloatMap (pfm)
|
- Portable FloatMap/HalfMap (pfm, phm)
|
||||||
- Photoshop documents (psd, psb, pdd, psdt)
|
- Photoshop documents (psd, psb, pdd, psdt)
|
||||||
- Radiance HDR (hdr)
|
- Radiance HDR (hdr)
|
||||||
- Scitex CT (sct)
|
- Scitex CT (sct)
|
||||||
|
BIN
autotests/read/pfm/testcard_gray_half.phm
Normal file
BIN
autotests/read/pfm/testcard_gray_half.phm
Normal file
Binary file not shown.
BIN
autotests/read/pfm/testcard_gray_half.png
Normal file
BIN
autotests/read/pfm/testcard_gray_half.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
BIN
autotests/read/pfm/testcard_rgb_half.phm
Normal file
BIN
autotests/read/pfm/testcard_rgb_half.phm
Normal file
Binary file not shown.
BIN
autotests/read/pfm/testcard_rgb_half.png
Normal file
BIN
autotests/read/pfm/testcard_rgb_half.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include <QColorSpace>
|
#include <QColorSpace>
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
|
#include <QFloat16>
|
||||||
#include <QIODevice>
|
#include <QIODevice>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
@ -29,6 +30,11 @@ private:
|
|||||||
*/
|
*/
|
||||||
bool m_bw;
|
bool m_bw;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief m_half True if half float.
|
||||||
|
*/
|
||||||
|
bool m_half;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* \brief m_ps True if saved by Photoshop (Photoshop variant).
|
* \brief m_ps True if saved by Photoshop (Photoshop variant).
|
||||||
*
|
*
|
||||||
@ -61,12 +67,13 @@ private:
|
|||||||
QDataStream::ByteOrder m_byteOrder;
|
QDataStream::ByteOrder m_byteOrder;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PFMHeader() :
|
PFMHeader()
|
||||||
m_bw(false),
|
: m_bw(false)
|
||||||
m_ps(false),
|
, m_half(false)
|
||||||
m_width(0),
|
, m_ps(false)
|
||||||
m_height(0),
|
, m_width(0)
|
||||||
m_byteOrder(QDataStream::BigEndian)
|
, m_height(0)
|
||||||
|
, m_byteOrder(QDataStream::BigEndian)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -81,6 +88,11 @@ public:
|
|||||||
return m_bw;
|
return m_bw;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isHalfFloat() const
|
||||||
|
{
|
||||||
|
return m_half;
|
||||||
|
}
|
||||||
|
|
||||||
bool isPhotoshop() const
|
bool isPhotoshop() const
|
||||||
{
|
{
|
||||||
return m_ps;
|
return m_ps;
|
||||||
@ -109,7 +121,7 @@ public:
|
|||||||
QImage::Format format() const
|
QImage::Format format() const
|
||||||
{
|
{
|
||||||
if (isValid()) {
|
if (isValid()) {
|
||||||
return QImage::Format_RGBX32FPx4;
|
return m_half ? QImage::Format_RGBX16FPx4 : QImage::Format_RGBX32FPx4;
|
||||||
}
|
}
|
||||||
return QImage::Format_Invalid;
|
return QImage::Format_Invalid;
|
||||||
}
|
}
|
||||||
@ -118,8 +130,16 @@ public:
|
|||||||
{
|
{
|
||||||
auto pf = d->read(3);
|
auto pf = d->read(3);
|
||||||
if (pf == QByteArray("PF\n")) {
|
if (pf == QByteArray("PF\n")) {
|
||||||
|
m_half = false;
|
||||||
m_bw = false;
|
m_bw = false;
|
||||||
} else if (pf == QByteArray("Pf\n")) {
|
} 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;
|
m_bw = true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -196,6 +216,28 @@ bool PFMHandler::canRead(QIODevice *device)
|
|||||||
return h.isValid();
|
return h.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
bool readScanLine(qint32 y, QDataStream &s, QImage &img, const PFMHeader &header)
|
||||||
|
{
|
||||||
|
auto bw = header.isBlackAndWhite();
|
||||||
|
auto line = reinterpret_cast<T *>(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)
|
bool PFMHandler::read(QImage *image)
|
||||||
{
|
{
|
||||||
auto&& header = d->m_header;
|
auto&& header = d->m_header;
|
||||||
@ -215,24 +257,17 @@ bool PFMHandler::read(QImage *image)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto y = 0, h = img.height(); y < h; ++y) {
|
for (auto y = 0, h = img.height(); y < h; ++y) {
|
||||||
auto bw = header.isBlackAndWhite();
|
auto ok = false;
|
||||||
auto line = reinterpret_cast<float *>(img.scanLine(header.isPhotoshop() ? y : h - y - 1));
|
if (header.isHalfFloat()) {
|
||||||
for (auto x = 0, n = img.width() * 4; x < n; x += 4) {
|
ok = readScanLine<qfloat16>(y, s, img, header);
|
||||||
line[x + 3] = float(1);
|
|
||||||
s >> line[x];
|
|
||||||
if (bw) {
|
|
||||||
line[x + 1] = line[x];
|
|
||||||
line[x + 2] = line[x];
|
|
||||||
} else {
|
} else {
|
||||||
s >> line[x + 1];
|
ok = readScanLine<float>(y, s, img, header);
|
||||||
s >> line[x + 2];
|
|
||||||
}
|
}
|
||||||
if (s.status() != QDataStream::Ok) {
|
if (!ok) {
|
||||||
qCWarning(LOG_PFMPLUGIN) << "PFMHandler::read() detected corrupted data";
|
qCWarning(LOG_PFMPLUGIN) << "PFMHandler::read() detected corrupted data";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
img.setColorSpace(QColorSpace(QColorSpace::SRgbLinear));
|
||||||
|
|
||||||
@ -296,7 +331,7 @@ QVariant PFMHandler::option(ImageOption option) const
|
|||||||
|
|
||||||
QImageIOPlugin::Capabilities PFMPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
QImageIOPlugin::Capabilities PFMPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||||
{
|
{
|
||||||
if (format == "pfm") {
|
if (format == "pfm" || format == "phm") {
|
||||||
return Capabilities(CanRead);
|
return Capabilities(CanRead);
|
||||||
}
|
}
|
||||||
if (!format.isEmpty()) {
|
if (!format.isEmpty()) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"Keys": [ "pfm" ],
|
"Keys": [ "pfm", "phm" ],
|
||||||
"MimeTypes": [ "image/x-pfm" ]
|
"MimeTypes": [ "image/x-pfm", "image/x-phm" ]
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user