16#include <QLoggingCategory>
17#include <QRegularExpressionMatch>
26typedef unsigned char uchar;
28Q_LOGGING_CATEGORY(HDRPLUGIN,
"kf.imageformats.plugins.hdr", QtWarningMsg)
42 Header(
const Header&) =
default;
43 Header& operator=(
const Header&) =
default;
45 bool isValid()
const {
return width() > 0 && height() > 0; }
46 qint32 width()
const {
return(m_size.width()); }
47 qint32 height()
const {
return(m_size.height()); }
48 QString software()
const {
return(m_software); }
65 QColorSpace colorSpace()
const {
return(m_colorSpace); }
78 float exposure()
const {
80 for (
auto&& v : m_exposure)
86 QColorSpace m_colorSpace;
89 QList<float> m_exposure;
92class HDRHandlerPrivate
102 const Header& header(QIODevice *device)
108 h = readHeader(device);
112 static Header readHeader(QIODevice *device)
117 QByteArray line(MAXLINE + 1, Qt::Uninitialized);
122 len = device->
readLine(line.data(), MAXLINE);
124 if (line.startsWith(
"FORMAT=")) {
127 if (line.startsWith(
"SOFTWARE=")) {
130 if (line.startsWith(
"EXPOSURE=")) {
136 if (line.startsWith(
"PRIMARIES=")) {
137 auto list = line.
mid(10, len - 10).trimmed().split(
' ');
138 QList<double> primaries;
139 for (
auto&& v : list) {
145 if (primaries.
size() == 8) {
146 auto cs = QColorSpace(QPointF(primaries.
at(6), primaries.
at(7)),
147 QPointF(primaries.
at(0), primaries.
at(1)),
148 QPointF(primaries.
at(2), primaries.
at(3)),
149 QPointF(primaries.
at(4), primaries.
at(5)),
150 QColorSpace::TransferFunction::Linear);
151 cs.setDescription(QStringLiteral(
"Embedded RGB"));
157 }
while ((len > 0) && (line[0] !=
'\n'));
159 if (format !=
"32-bit_rle_rgbe") {
160 qCDebug(HDRPLUGIN) <<
"Unknown HDR format:" << format;
164 len = device->
readLine(line.data(), MAXLINE);
180 QRegularExpression resolutionRegExp(QStringLiteral(
"([+\\-][XY])\\s+([0-9]+)\\s+([+\\-][XY])\\s+([0-9]+)\n"));
182 if (!
match.hasMatch()) {
183 qCDebug(HDRPLUGIN) <<
"Invalid HDR file, the first line after the header didn't have the expected format:" << line;
187 auto c0 =
match.captured(1);
188 auto c1 =
match.captured(3);
189 if (c0.at(1) == u
'Y') {
190 if (c0.at(0) == u
'-' && c1.at(0) == u
'+')
192 if (c0.at(0) == u
'-' && c1.at(0) == u
'-')
194 if (c0.at(0) == u
'+' && c1.at(0) == u
'+')
196 if (c0.at(0) == u
'+' && c1.at(0) == u
'-')
200 if (c0.at(0) == u
'-' && c1.at(0) == u
'+')
202 if (c0.at(0) == u
'-' && c1.at(0) == u
'-')
204 if (c0.at(0) == u
'+' && c1.at(0) == u
'+')
206 if (c0.at(0) == u
'+' && c1.at(0) == u
'-')
210 h.m_size = QSize(
match.captured(4).toInt(),
match.captured(2).toInt());
220static bool Read_Old_Line(uchar *image,
int width,
QDataStream &s)
225 uchar *
start = image;
236 if ((image[0] == 1) && (image[1] == 1) && (image[2] == 1)) {
241 for (i = image[3] << rshift; i > 0 && width > 0; i--) {
242 if (image ==
start) {
246 (uint &)image[0] = (uint &)image[0 - 4];
260template<
class float_T>
261void RGBE_To_QRgbLine(uchar *image, float_T *scanline,
const Header& h)
263 auto exposure = h.exposure();
264 for (
int j = 0, width = h.width(); j < width; j++) {
267 int e = qBound(-31,
int(image[3]) - 128, 31);
271 v = 1.0f / float(1 << -e);
275 auto vn = v / 255.0f;
280 scanline[j4] = float_T(
float(image[0]) * vn);
281 scanline[j4 + 1] = float_T(
float(image[1]) * vn);
282 scanline[j4 + 2] = float_T(
float(image[2]) * vn);
283 scanline[j4 + 3] = float_T(1.0f);
290#ifdef HDR_HALF_QUALITY
303 const int width = h.width();
304 const int height = h.height();
307 img = imageAlloc(width, height, imageFormat());
309 qCDebug(HDRPLUGIN) <<
"Couldn't create image with size" << width << height <<
"and format RGB32";
314 lineArray.
resize(4 * width);
315 uchar *image =
reinterpret_cast<uchar *
>(lineArray.
data());
317 for (
int cline = 0; cline < height; cline++) {
318#ifdef HDR_HALF_QUALITY
321 auto scanline =
reinterpret_cast<float *
>(img.
scanLine(cline));
325 if ((width < MINELEN) || (MAXELEN < width)) {
326 Read_Old_Line(image, width, s);
327 RGBE_To_QRgbLine(image, scanline, h);
339 Read_Old_Line(image, width, s);
340 RGBE_To_QRgbLine(image, scanline, h);
352 if ((image[1] != 2) || (image[2] & 128)) {
354 Read_Old_Line(image + 4, width - 1, s);
355 RGBE_To_QRgbLine(image, scanline, h);
359 if ((image[2] << 8 | image[3]) != width) {
360 qCDebug(HDRPLUGIN) <<
"Line of pixels had width" << (image[2] << 8 | image[3]) <<
"instead of" << width;
365 for (
int i = 0, len =
int(lineArray.
size()); i < 4; i++) {
366 for (
int j = 0; j < width;) {
369 qCDebug(HDRPLUGIN) <<
"Truncated HDR file";
377 auto idx = i + j * 4;
387 auto idx = i + j * 4;
397 RGBE_To_QRgbLine(image, scanline, h);
403bool HDRHandler::read(
QImage *outImage)
407 const Header& h = d->header(s.
device());
413 if (!LoadHDR(s, h, img)) {
423 img.
setText(QStringLiteral(META_KEY_SOFTWARE), h.software());
430bool HDRHandler::supportsOption(ImageOption option)
const
444QVariant HDRHandler::option(ImageOption option)
const
449 if (
auto dev = device()) {
450 auto&& h = d->header(dev);
462 if (
auto dev = device()) {
463 auto&& h = d->header(dev);
473HDRHandler::HDRHandler()
475 , d(new HDRHandlerPrivate)
479bool HDRHandler::canRead()
const
481 if (canRead(device())) {
488bool HDRHandler::canRead(
QIODevice *device)
491 qWarning(
"HDRHandler::canRead() called with no device");
496 if(device->
peek(11) ==
"#?RADIANCE\n" || device->
peek(7) ==
"#?RGBE\n") {
502 auto h = HDRHandlerPrivate::readHeader(device);
513 if (format ==
"hdr") {
524 if (device->
isReadable() && HDRHandler::canRead(device)) {
538#include "moc_hdr_p.cpp"
Q_SCRIPTABLE QString start(QString train="")
AKONADI_MIME_EXPORT const char Header[]
KCOREADDONS_EXPORT Result match(QStringView pattern, QStringView str)
QFlags< Capability > Capabilities
KIOCORE_EXPORT QStringList list(const QString &fileClass)
bool isEmpty() const const
QByteArray mid(qsizetype pos, qsizetype len) const const
void resize(qsizetype newSize, char c)
qsizetype size() const const
QByteArray trimmed() const const
QIODevice * device() const const
bool isNull() const const
void setColorSpace(const QColorSpace &colorSpace)
void setText(const QString &key, const QString &text)
void setDevice(QIODevice *device)
bool isOpen() const const
bool isReadable() const const
QByteArray peek(qint64 maxSize)
QByteArray readLine(qint64 maxSize)
void rollbackTransaction()
const_reference at(qsizetype i) const const
QList< T > mid(qsizetype pos, qsizetype length) const const
qsizetype size() const const
double toDouble(QStringView s, bool *ok) const const
float toFloat(QStringView s, bool *ok) const const
QString fromLatin1(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString trimmed() const const
QVariant fromValue(T &&value)