mirror of
https://invent.kde.org/frameworks/kimageformats.git
synced 2025-05-28 00:30:23 -04:00
hdr: options support and bugfixes
- Support for Size and Format options - Added compile option to generate 16-bit instead of 32-bit images - Includes MR !189 and MR !192 fixes
This commit is contained in:
parent
0a6fbd88e9
commit
75e1280073
BIN
autotests/read/hdr/fake_earth.hdr
Normal file
BIN
autotests/read/hdr/fake_earth.hdr
Normal file
Binary file not shown.
BIN
autotests/read/hdr/fake_earth.png
Normal file
BIN
autotests/read/hdr/fake_earth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
@ -11,12 +11,18 @@
|
||||
|
||||
#include <QColorSpace>
|
||||
#include <QDataStream>
|
||||
#include <QFloat16>
|
||||
#include <QImage>
|
||||
#include <QLoggingCategory>
|
||||
#include <QRegularExpressionMatch>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
/* *** HDR_HALF_QUALITY ***
|
||||
* If defined, a 16-bits float image is created, otherwise a 32-bits float ones (default).
|
||||
*/
|
||||
//#define HDR_HALF_QUALITY // default commented -> you should define it in your cmake file
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
Q_LOGGING_CATEGORY(HDRPLUGIN, "kf.imageformats.plugins.hdr", QtWarningMsg)
|
||||
@ -34,6 +40,7 @@ static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
||||
int rshift = 0;
|
||||
int i;
|
||||
|
||||
uchar *start = image;
|
||||
while (width > 0) {
|
||||
s >> image[0];
|
||||
s >> image[1];
|
||||
@ -45,7 +52,14 @@ static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
||||
}
|
||||
|
||||
if ((image[0] == 1) && (image[1] == 1) && (image[2] == 1)) {
|
||||
for (i = image[3] << rshift; i > 0; i--) {
|
||||
// NOTE: we don't have an image sample that cover this code
|
||||
if (rshift > 31) {
|
||||
return false;
|
||||
}
|
||||
for (i = image[3] << rshift; i > 0 && width > 0; i--) {
|
||||
if (image == start) {
|
||||
return false; // you cannot be here at the first run
|
||||
}
|
||||
// memcpy(image, image-4, 4);
|
||||
(uint &)image[0] = (uint &)image[0 - 4];
|
||||
image += 4;
|
||||
@ -61,12 +75,13 @@ static bool Read_Old_Line(uchar *image, int width, QDataStream &s)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void RGBE_To_QRgbLine(uchar *image, float *scanline, int width)
|
||||
template<class float_T>
|
||||
void RGBE_To_QRgbLine(uchar *image, float_T *scanline, int width)
|
||||
{
|
||||
for (int j = 0; j < width; j++) {
|
||||
// v = ldexp(1.0, int(image[3]) - 128);
|
||||
float v;
|
||||
int e = int(image[3]) - 128;
|
||||
int e = qBound(-31, int(image[3]) - 128, 31);
|
||||
if (e > 0) {
|
||||
v = float(1 << e);
|
||||
} else {
|
||||
@ -75,14 +90,23 @@ static void RGBE_To_QRgbLine(uchar *image, float *scanline, int width)
|
||||
|
||||
auto j4 = j * 4;
|
||||
auto vn = v / 255.0f;
|
||||
scanline[j4] = std::min(float(image[0]) * vn, 1.0f);
|
||||
scanline[j4 + 1] = std::min(float(image[1]) * vn, 1.0f);
|
||||
scanline[j4 + 2] = std::min(float(image[2]) * vn, 1.0f);
|
||||
scanline[j4 + 3] = 1.0f;
|
||||
scanline[j4] = float_T(std::min(float(image[0]) * vn, 1.0f));
|
||||
scanline[j4 + 1] = float_T(std::min(float(image[1]) * vn, 1.0f));
|
||||
scanline[j4 + 2] = float_T(std::min(float(image[2]) * vn, 1.0f));
|
||||
scanline[j4 + 3] = float_T(1.0f);
|
||||
image += 4;
|
||||
}
|
||||
}
|
||||
|
||||
QImage::Format imageFormat()
|
||||
{
|
||||
#ifdef HDR_HALF_QUALITY
|
||||
return QImage::Format_RGBX16FPx4;
|
||||
#else
|
||||
return QImage::Format_RGBX32FPx4;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Load the HDR image.
|
||||
static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &img)
|
||||
{
|
||||
@ -90,7 +114,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
uchar code;
|
||||
|
||||
// Create dst image.
|
||||
img = imageAlloc(width, height, QImage::Format_RGBX32FPx4);
|
||||
img = imageAlloc(width, height, imageFormat());
|
||||
if (img.isNull()) {
|
||||
qCDebug(HDRPLUGIN) << "Couldn't create image with size" << width << height << "and format RGB32";
|
||||
return false;
|
||||
@ -98,10 +122,14 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
|
||||
QByteArray lineArray;
|
||||
lineArray.resize(4 * width);
|
||||
uchar *image = (uchar *)lineArray.data();
|
||||
uchar *image = reinterpret_cast<uchar *>(lineArray.data());
|
||||
|
||||
for (int cline = 0; cline < height; cline++) {
|
||||
auto scanline = (float *)img.scanLine(cline);
|
||||
#ifdef HDR_HALF_QUALITY
|
||||
auto scanline = reinterpret_cast<qfloat16 *>(img.scanLine(cline));
|
||||
#else
|
||||
auto scanline = reinterpret_cast<float *>(img.scanLine(cline));
|
||||
#endif
|
||||
|
||||
// determine scanline type
|
||||
if ((width < MINELEN) || (MAXELEN < width)) {
|
||||
@ -144,7 +172,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
}
|
||||
|
||||
// read each component
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int i = 0, len = int(lineArray.size()); i < 4; i++) {
|
||||
for (int j = 0; j < width;) {
|
||||
s >> code;
|
||||
if (s.atEnd()) {
|
||||
@ -156,14 +184,20 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
code &= 127;
|
||||
s >> val;
|
||||
while (code != 0) {
|
||||
image[i + j * 4] = val;
|
||||
auto idx = i + j * 4;
|
||||
if (idx < len) {
|
||||
image[idx] = val;
|
||||
}
|
||||
j++;
|
||||
code--;
|
||||
}
|
||||
} else {
|
||||
// non-run
|
||||
while (code != 0) {
|
||||
s >> image[i + j * 4];
|
||||
auto idx = i + j * 4;
|
||||
if (idx < len) {
|
||||
s >> image[idx];
|
||||
}
|
||||
j++;
|
||||
code--;
|
||||
}
|
||||
@ -177,9 +211,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool HDRHandler::read(QImage *outImage)
|
||||
static QSize readHeaderSize(QIODevice *device)
|
||||
{
|
||||
int len;
|
||||
QByteArray line(MAXLINE + 1, Qt::Uninitialized);
|
||||
@ -187,7 +219,7 @@ bool HDRHandler::read(QImage *outImage)
|
||||
|
||||
// Parse header
|
||||
do {
|
||||
len = device()->readLine(line.data(), MAXLINE);
|
||||
len = device->readLine(line.data(), MAXLINE);
|
||||
|
||||
if (line.startsWith("FORMAT=")) {
|
||||
format = line.mid(7, len - 7 - 1 /*\n*/);
|
||||
@ -197,10 +229,10 @@ bool HDRHandler::read(QImage *outImage)
|
||||
|
||||
if (format != "32-bit_rle_rgbe") {
|
||||
qCDebug(HDRPLUGIN) << "Unknown HDR format:" << format;
|
||||
return false;
|
||||
return QSize();
|
||||
}
|
||||
|
||||
len = device()->readLine(line.data(), MAXLINE);
|
||||
len = device->readLine(line.data(), MAXLINE);
|
||||
line.resize(len);
|
||||
|
||||
/*
|
||||
@ -220,21 +252,30 @@ bool HDRHandler::read(QImage *outImage)
|
||||
QRegularExpressionMatch match = resolutionRegExp.match(QString::fromLatin1(line));
|
||||
if (!match.hasMatch()) {
|
||||
qCDebug(HDRPLUGIN) << "Invalid HDR file, the first line after the header didn't have the expected format:" << line;
|
||||
return false;
|
||||
return QSize();
|
||||
}
|
||||
|
||||
if ((match.captured(1).at(1) != u'Y') || (match.captured(3).at(1) != u'X')) {
|
||||
qCDebug(HDRPLUGIN) << "Unsupported image orientation in HDR file.";
|
||||
return QSize();
|
||||
}
|
||||
|
||||
return QSize(match.captured(4).toInt(), match.captured(2).toInt());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool HDRHandler::read(QImage *outImage)
|
||||
{
|
||||
QDataStream s(device());
|
||||
|
||||
QSize size = readHeaderSize(s.device());
|
||||
if (!size.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int width = match.captured(4).toInt();
|
||||
const int height = match.captured(2).toInt();
|
||||
|
||||
QDataStream s(device());
|
||||
|
||||
QImage img;
|
||||
if (!LoadHDR(s, width, height, img)) {
|
||||
if (!LoadHDR(s, size.width(), size.height(), img)) {
|
||||
// qDebug() << "Error loading HDR file.";
|
||||
return false;
|
||||
}
|
||||
@ -246,6 +287,40 @@ bool HDRHandler::read(QImage *outImage)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HDRHandler::supportsOption(ImageOption option) const
|
||||
{
|
||||
if (option == QImageIOHandler::Size) {
|
||||
return true;
|
||||
}
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QVariant HDRHandler::option(ImageOption option) const
|
||||
{
|
||||
QVariant v;
|
||||
|
||||
if (option == QImageIOHandler::Size) {
|
||||
if (auto d = device()) {
|
||||
// transactions works on both random and sequential devices
|
||||
d->startTransaction();
|
||||
auto size = readHeaderSize(d);
|
||||
d->rollbackTransaction();
|
||||
if (size.isValid()) {
|
||||
v = QVariant::fromValue(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (option == QImageIOHandler::ImageFormat) {
|
||||
v = QVariant::fromValue(imageFormat());
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
HDRHandler::HDRHandler()
|
||||
{
|
||||
}
|
||||
@ -266,7 +341,20 @@ bool HDRHandler::canRead(QIODevice *device)
|
||||
return false;
|
||||
}
|
||||
|
||||
return device->peek(11) == "#?RADIANCE\n" || device->peek(7) == "#?RGBE\n";
|
||||
// the .pic taken from official test cases does not start with this string but can be loaded.
|
||||
if(device->peek(11) == "#?RADIANCE\n" || device->peek(7) == "#?RGBE\n") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// allow to load offical test cases: https://radsite.lbl.gov/radiance/framed.html
|
||||
device->startTransaction();
|
||||
QSize size = readHeaderSize(device);
|
||||
device->rollbackTransaction();
|
||||
if (size.isValid()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QImageIOPlugin::Capabilities HDRPlugin::capabilities(QIODevice *device, const QByteArray &format) const
|
||||
|
@ -18,6 +18,9 @@ public:
|
||||
bool canRead() const override;
|
||||
bool read(QImage *outImage) override;
|
||||
|
||||
bool supportsOption(QImageIOHandler::ImageOption option) const override;
|
||||
QVariant option(QImageIOHandler::ImageOption option) const override;
|
||||
|
||||
static bool canRead(QIODevice *device);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user