From 1982557a55fb57ccd07e40194b53693d2b0ab40b Mon Sep 17 00:00:00 2001 From: Mirco Miranda Date: Wed, 8 Jan 2025 07:01:57 +0000 Subject: [PATCH] RAW: Allow preview loading This patch addresses reports of performance issues on large raw collections. Programs that generate previews must use the plugin correctly. **Setting quality to 0 may return a different image than in the past** (Nothing changes for all other quality values): the plugin loads the embedded thumbnail and, in case of error, decodes the image with quality 1. When compiled with libRAW 0.21+, the plugin automatically select the largest preview from the ones in the file. --- src/imageformats/raw.cpp | 76 +++++++++++++++++++++++++++++++++++++++- src/imageformats/raw_p.h | 8 +++-- 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/src/imageformats/raw.cpp b/src/imageformats/raw.cpp index b56d1ca..9746c7e 100644 --- a/src/imageformats/raw.cpp +++ b/src/imageformats/raw.cpp @@ -581,6 +581,66 @@ void setParams(QImageIOHandler *handler, LibRaw *rawProcessor) params.use_fuji_rotate = T_SR(quality) ? 0 : 1; } +bool LoadTHUMB(QImageIOHandler *handler, QImage &img) +{ + std::unique_ptr rawProcessor(new LibRaw); + + // *** Open the stream + auto device = handler->device(); +#ifndef EXCLUDE_LibRaw_QIODevice + LibRaw_QIODevice stream(device); + if (rawProcessor->open_datastream(&stream) != LIBRAW_SUCCESS) { + return false; + } +#else + auto ba = device->readAll(); + if (rawProcessor->open_buffer(ba.data(), ba.size()) != LIBRAW_SUCCESS) { + return false; + } +#endif + +#if (LIBRAW_VERSION < LIBRAW_MAKE_VERSION(0, 21, 0)) + // *** Unpacking selected thumbnail + if (rawProcessor->unpack_thumb() != LIBRAW_SUCCESS) { + return false; + } +#else + // *** Search for the bigger thumbnail + auto &&tlist = rawProcessor->imgdata.thumbs_list; + auto idx = 0; + for (auto n = 1; n < std::min(tlist.thumbcount, LIBRAW_THUMBNAIL_MAXCOUNT); ++n) { + if (tlist.thumblist[n].twidth > tlist.thumblist[idx].twidth) + idx = n; + } + + // *** Unpacking selected thumbnail + if (rawProcessor->unpack_thumb_ex(idx) != LIBRAW_SUCCESS) { + return false; + } +#endif + + // *** Convert to QImage + pi_unique_ptr processedImage(rawProcessor->dcraw_make_mem_thumb(), LibRaw::dcraw_clear_mem); + if (processedImage == nullptr) { + return false; + } + auto ba = QByteArray(reinterpret_cast(processedImage->data), qsizetype(processedImage->data_size)); + if (ba.isEmpty()) { + return false; + } + if (processedImage->type == LIBRAW_IMAGE_BITMAP) { + // clang-format off + auto header = QString::fromUtf8("P%1\n%2 %3\n%4\n") // taken from KDcraw + .arg(processedImage->colors == 3 ? QLatin1String("6") : QLatin1String("5")) + .arg(processedImage->width) + .arg(processedImage->height) + .arg((1 << processedImage->bits)-1); + // clang-format on + ba.prepend(header.toLatin1()); + } + return img.loadFromData(ba); +} + bool LoadRAW(QImageIOHandler *handler, QImage &img) { std::unique_ptr rawProcessor(new LibRaw); @@ -679,6 +739,11 @@ bool LoadRAW(QImageIOHandler *handler, QImage &img) if (params.output_color == 7) { img.setColorSpace(QColorSpace(QColorSpace::DisplayP3)); } +#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) + if (params.output_color == 8) { + img.setColorSpace(QColorSpace(QColorSpace::Bt2020)); + } +#endif } // *** Set the metadata @@ -756,7 +821,16 @@ bool RAWHandler::read(QImage *image) } QImage img; - if (!LoadRAW(this, img)) { + auto ok = false; + if (m_quality == 0) { + ok = LoadTHUMB(this, img); + if (!ok && !dev->isSequential()) + dev->seek(m_startPos); + } + if (!ok) { + ok = LoadRAW(this, img); + } + if (!ok) { return false; } diff --git a/src/imageformats/raw_p.h b/src/imageformats/raw_p.h index bf62058..b14f344 100644 --- a/src/imageformats/raw_p.h +++ b/src/imageformats/raw_p.h @@ -60,7 +60,8 @@ private: * @note It is safe to set both W and A: W is used if camera white balance is found, otherwise A is used. * * When quality is a positive value, a value between 0 and 100 is expected. The values are interpreted as follows: - * - 00-09: I = 0, C = 1, B = 0, W = 1, A = 1, H = 1 (Linear, sRGB, 8-bits, Camera White, Auto White, Half-size) + * - 00 : Embedded preview, if fails same as Half-Size (01-09) + * - 01-09: I = 0, C = 1, B = 0, W = 1, A = 1, H = 1 (Linear, sRGB, 8-bits, Camera White, Auto White, Half-size) * - 10-19: I = 0, C = 1, B = 0, W = 1, A = 1, H = 0 (Linear, sRGB, 8-bits, Camera White, Auto White) * - 20-29: I = 3, C = 1, B = 0, W = 1, A = 1, H = 0 (AHD, sRGB, 8-bits, Camera White, Auto White) * - 30-39: I = 3, C = 1, B = 1, W = 1, A = 1, H = 0 (AHD, sRGB, 16-bits, Camera White, Auto White) [Default] @@ -72,11 +73,12 @@ private: * - >= 90: I = 11, C = 4, B = 1, W = 1, A = 1, H = 0 (DHT, ProPhoto, 16-bits, Camera White, Auto White) * * When the quality is -1, default quality is used. + * @sa m_subType */ qint32 m_quality; - /*! - * \brief m_startPos + /** + * @brief m_startPos * The initial device position to allow multi image load (cache value). */ qint64 m_startPos;