mirror of
				https://invent.kde.org/frameworks/kimageformats.git
				synced 2025-10-30 13:24:27 -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:
		
				
					committed by
					
						 Albert Astals Cid
						Albert Astals Cid
					
				
			
			
				
	
			
			
			
						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); | ||||
| }; | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user