From 68bb1a0ee7f96a86d9d8499e323f6b22e5f2f532 Mon Sep 17 00:00:00 2001 From: Christoph Feck Date: Sat, 14 Sep 2019 14:05:30 +0200 Subject: [PATCH] Port HDR (Radiance RGBE) image loader to Qt5 Tested with HDR images from hdrihaven.com * Loading in KolourPaint works * Thumbnails in Dolphin work Reviewed by: aacid Differential Revision: https://phabricator.kde.org/D23811 --- autotests/CMakeLists.txt | 1 + autotests/read/hdr/rgb.hdr | Bin 0 -> 1789 bytes autotests/read/hdr/rgb.png | Bin 0 -> 1113 bytes src/imageformats/CMakeLists.txt | 5 ++ src/imageformats/hdr.cpp | 97 +++++++++++++++++++++++--------- src/imageformats/hdr.json | 4 ++ src/imageformats/hdr_p.h | 27 +++++++-- 7 files changed, 100 insertions(+), 34 deletions(-) create mode 100644 autotests/read/hdr/rgb.hdr create mode 100644 autotests/read/hdr/rgb.png create mode 100644 src/imageformats/hdr.json diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index ae28988..4d90f99 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -55,6 +55,7 @@ endmacro() # Loads each image in read//, and compares the # result against the data read from the corresponding png file kimageformats_read_tests( + hdr pcx psd ras diff --git a/autotests/read/hdr/rgb.hdr b/autotests/read/hdr/rgb.hdr new file mode 100644 index 0000000000000000000000000000000000000000..a0b29ecfc9a9aa0a9eeda990dc59ed5e43994f17 GIT binary patch literal 1789 zcmZuye@Gi=7_QNrE~P)*q;|t?q3x#3x%q3IQ!YCGldu zv+A6inbb)-kyyzp5(-76m52oYa7COI30XLTEQAn_BO^PGTUZD;_I=+~jomnMy!qbe zd!F}u-uL59p3ziP)mGG3Rx94GsH>|`6(~N^)YesKYOA%XeC57JQFB34SMjl`s4%a! zy{EamL)YAWwNWhYQwR5sbpQl=gqBJ!E`pu)@ z){b1SR+Uv=Yj`y&nP_VN&^B{5BR#oP)!+GInnsW1jOX8O=Rn-&jfEZ+*ISNFgr`0m zp8CV)S>85bIbrPB0a^L1x?~(!>)*SOUbbFU`e|~uGmPa@;F}r*17;a84}v7~z&`1h zP7aP$DpH6mXHqkV2Mgak_(4N;|Lcl|UFkMf6q#T=9>CDr*OrXyYj>be|7>!OU{Fw}VdTk_2w4i%WYp8a83lwlN0#w3ISODHwm-bR zW#Jf-bXr=8~`)erIkm<$EG6~Oc$jWZDLl|P*lH!DoJFG`Fw#WP6w`()vw@62>Pw$ z=O=piBZ=t!`H_GK7XcB{<`g`L^rtu|w|D zS1fkPH(}OdT|gl=7xs78+~BX>U+KdbPfyt&S*5L^pN<@DWF$PQWDn1ek>qUQwf zjB|Kvuu-MH6#74YrXek@0Wkq0P6q_% LuM(c^o$u4XBJy9} literal 0 HcmV?d00001 diff --git a/autotests/read/hdr/rgb.png b/autotests/read/hdr/rgb.png new file mode 100644 index 0000000000000000000000000000000000000000..503abd97ff0b2f569cdde5fd0ea0deae63d2874a GIT binary patch literal 1113 zcmV-f1g86mP)&}ae{ zwIQ($t$LwcXgs8*m)_{TCjJN2i^il?vBVl}iiQF`kYF_?-GUXeNT3I{OLwPxoSox( z0jVvC;j^L7)nxKM?=SE7ykGKpN~IE>q{tKP4uE~|a70&@+QLv^RG-`Xicso ztQgfzzBCnjcw+oAp(uk4@-Zox<<;Yim^8 zlFj-wsbL^)%z!V$o<2B?c*EPP*`A$v0$3awwI4yQi^7BvKvvf=YQb;2Tgxm0JgR9;~Tlt%q!hy2645T#zEz3``e= z%q;)qyV`|>wSy6k9ryPPNt^@+C|EE`uEg352n$Om&|N_$WMjwxI1U^K2?UVV{xARs z$lw6!M!+2XAU3Z?R~90Ny8JG`ULbRjK>&WOn*>OYZvMs${!6x+DSP2mtA1XNJQehR z!g3LSp)t~4wCa5B)l>1_qoKoJFSTNxflXk4B0rz*4xXN2 zCS9*5my-;mTjql_M%pb>d~QdFms0|Q6lk=G;JTDX+6nTiZ))hJt6~d)fYo|LQsk`# zfS}Tc$l?@$%BEKO_Y^HacC#<8o|Be;1+cHYT2@~Mk+u?GTZk_BN51I*P(I{8&`fKk z1sFy3?xlB9V{ZYt+r%6D)rX!YTLDnK{9ymn>03I0!G2Nk@|4DZpDmlpFpx?CfT}_e fHmiONz~kv3t0LP}xRS&y00000NkvXXu0mjfI$RGD literal 0 HcmV?d00001 diff --git a/src/imageformats/CMakeLists.txt b/src/imageformats/CMakeLists.txt index 0dc9707..8a53bba 100644 --- a/src/imageformats/CMakeLists.txt +++ b/src/imageformats/CMakeLists.txt @@ -53,6 +53,11 @@ endif() ################################## +kimageformats_add_plugin(kimg_hdr JSON "hdr.json" SOURCES hdr.cpp) +install(FILES hdr.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}/qimageioplugins/) + +################################## + kimageformats_add_plugin(kimg_pcx JSON "pcx.json" SOURCES pcx.cpp) install(FILES pcx.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}/qimageioplugins/) diff --git a/src/imageformats/hdr.cpp b/src/imageformats/hdr.cpp index 128699c..178aae8 100644 --- a/src/imageformats/hdr.cpp +++ b/src/imageformats/hdr.cpp @@ -15,7 +15,7 @@ #include -typedef Q_UINT8 uchar; +typedef unsigned char uchar; namespace // Private. { @@ -93,19 +93,22 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i uchar val, code; // Create dst image. - if (!img.create(width, height, 32)) { + img = QImage(width, height, QImage::Format_RGB32); + if (img.isNull()) { return false; } - QMemArray image(width * 4); + QByteArray lineArray; + lineArray.resize(4 * width); + uchar *image = (uchar *) lineArray.data(); for (int cline = 0; cline < height; cline++) { QRgb *scanline = (QRgb *) img.scanLine(cline); // determine scanline type if ((width < MINELEN) || (MAXELEN < width)) { - Read_Old_Line(image.data(), width, s); - RGBE_To_QRgbLine(image.data(), scanline, width); + Read_Old_Line(image, width, s); + RGBE_To_QRgbLine(image, scanline, width); continue; } @@ -116,9 +119,9 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i } if (val != 2) { - s.device()->at(s.device()->at() - 1); - Read_Old_Line(image.data(), width, s); - RGBE_To_QRgbLine(image.data(), scanline, width); + s.device()->ungetChar(val); + Read_Old_Line(image, width, s); + RGBE_To_QRgbLine(image, scanline, width); continue; } @@ -132,8 +135,8 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i if ((image[1] != 2) || (image[2] & 128)) { image[0] = 2; - Read_Old_Line(image.data() + 4, width - 1, s); - RGBE_To_QRgbLine(image.data(), scanline, width); + Read_Old_Line(image + 4, width - 1, s); + RGBE_To_QRgbLine(image, scanline, width); continue; } @@ -168,7 +171,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i } } - RGBE_To_QRgbLine(image.data(), scanline, width); + RGBE_To_QRgbLine(image, scanline, width); } return true; @@ -176,7 +179,7 @@ static bool LoadHDR(QDataStream &s, const int width, const int height, QImage &i } // namespace -Q_DECL_EXPORT void kimgio_hdr_read(QImageIO *io) +bool HDRHandler::read(QImage *outImage) { int len; char line[MAXLINE]; @@ -185,7 +188,7 @@ Q_DECL_EXPORT void kimgio_hdr_read(QImageIO *io) // Parse header do { - len = io->ioDevice()->readLine(line, MAXLINE); + len = device()->readLine(line, MAXLINE); /*if (strcmp(line, "#?RADIANCE\n") == 0 || strcmp(line, "#?RGBE\n") == 0) { @@ -199,12 +202,10 @@ Q_DECL_EXPORT void kimgio_hdr_read(QImageIO *io) if (/*!validHeader ||*/ !validFormat) { // qDebug() << "Unknown HDR format."; - io->setImage(0); - io->setStatus(-1); - return; + return false; } - io->ioDevice()->readLine(line, MAXLINE); + device()->readLine(line, MAXLINE); char s1[3], s2[3]; int width, height; @@ -212,27 +213,67 @@ Q_DECL_EXPORT void kimgio_hdr_read(QImageIO *io) //if( sscanf(line, "-Y %d +X %d", &height, &width) < 2 ) { // qDebug() << "Invalid HDR file."; - io->setImage(0); - io->setStatus(-1); - return; + return false; } - QDataStream s(io->ioDevice()); + QDataStream s(device()); QImage img; if (!LoadHDR(s, width, height, img)) { // qDebug() << "Error loading HDR file."; - io->setImage(0); - io->setStatus(-1); - return; + return false; } - io->setImage(img); - io->setStatus(0); + *outImage = img; + return true; } -Q_DECL_EXPORT void kimgio_hdr_write(QImageIO *) +HDRHandler::HDRHandler() { - // intentionally not implemented (since writing low dynamic range data to a HDR file is nonsense.) } +bool HDRHandler::canRead() const +{ + if (canRead(device())) { + setFormat("hdr"); + return true; + } + return false; +} + +bool HDRHandler::canRead(QIODevice *device) +{ + if (!device) { + qWarning("HDRHandler::canRead() called with no device"); + return false; + } + + return device->peek(11) == "#?RADIANCE\n" || device->peek(7) == "#?RGBE\n"; +} + +QImageIOPlugin::Capabilities HDRPlugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "hdr") { + return Capabilities(CanRead); + } + if (!format.isEmpty()) { + return {}; + } + if (!device->isOpen()) { + return {}; + } + + Capabilities cap; + if (device->isReadable() && HDRHandler::canRead(device)) { + cap |= CanRead; + } + return cap; +} + +QImageIOHandler *HDRPlugin::create(QIODevice *device, const QByteArray &format) const +{ + QImageIOHandler *handler = new HDRHandler; + handler->setDevice(device); + handler->setFormat(format); + return handler; +} diff --git a/src/imageformats/hdr.json b/src/imageformats/hdr.json new file mode 100644 index 0000000..000729e --- /dev/null +++ b/src/imageformats/hdr.json @@ -0,0 +1,4 @@ +{ + "Keys": [ "hdr" ], + "MimeTypes": [ "image/x-hdr", "image/vnd.radiance" ] +} diff --git a/src/imageformats/hdr_p.h b/src/imageformats/hdr_p.h index 50700c6..6ac1645 100644 --- a/src/imageformats/hdr_p.h +++ b/src/imageformats/hdr_p.h @@ -10,12 +10,27 @@ #ifndef KIMG_HDR_P_H #define KIMG_HDR_P_H -class QImageIO; +#include -extern "C" { - void kimgio_hdr_read(QImageIO *); - void kimgio_hdr_write(QImageIO *); -} +class HDRHandler : public QImageIOHandler +{ +public: + HDRHandler(); -#endif + bool canRead() const override; + bool read(QImage *outImage) override; + static bool canRead(QIODevice *device); +}; + +class HDRPlugin : public QImageIOPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "hdr.json") + +public: + Capabilities capabilities(QIODevice *device, const QByteArray &format) const override; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const override; +}; + +#endif // KIMG_HDR_P_H