Commit b56fc72c authored by Marcel Wiesweg's avatar Marcel Wiesweg
Browse files

Adapt image preview loading to use the new settings.

PreviewTask is restructured and much cleaner now.

CCBUG: 205776
parent 04220ad4
......@@ -94,7 +94,8 @@ LoadingDescription::LoadingDescription(const QString& filePath, const DRawDecodi
postProcessingParameters.colorManagement = cm;
}
LoadingDescription::LoadingDescription(const QString& filePath, int size,
LoadingDescription::LoadingDescription(const QString& filePath,
const PreviewSettings& previewSettings, int size,
ColorManagementSettings cm,
LoadingDescription::PreviewParameters::PreviewType type)
: filePath(filePath)
......@@ -103,6 +104,7 @@ LoadingDescription::LoadingDescription(const QString& filePath, int size,
rawDecodingHint = RawDecodingDefaultSettings;
previewParameters.type = type;
previewParameters.size = size;
previewParameters.previewSettings = previewSettings;
postProcessingParameters.colorManagement = cm;
}
......
......@@ -32,6 +32,7 @@
#include "dimg.h"
#include "digikam_export.h"
#include "previewsettings.h"
namespace Digikam
{
......@@ -83,10 +84,6 @@ public:
{
NoFlags = 0,
OnlyPregenerate = 1 << 0,
/// This prefers large images, but if loading a larger
/// image is very much slower, it will give a smaller image.
/// Size serves as a lower bound.
FastButLarge = 1 << 1
};
Q_DECLARE_FLAGS(PreviewFlags, PreviewFlag)
......@@ -104,11 +101,6 @@ public:
return flags & OnlyPregenerate;
}
bool fastButLarge() const
{
return flags & FastButLarge;
}
bool operator==(const PreviewParameters& other) const;
public:
......@@ -116,6 +108,7 @@ public:
PreviewType type;
int size;
PreviewFlags flags;
PreviewSettings previewSettings;
QVariant extraParameter;
};
......@@ -178,7 +171,8 @@ public:
* If size is 0, DImg based loading will be used with default raw decoding settings.
* You can also adjust raw decoding settings and hint in this case.
*/
LoadingDescription(const QString& filePath, int size,
LoadingDescription(const QString& filePath,
const PreviewSettings& settings, int size,
ColorManagementSettings = NoColorConversion,
PreviewParameters::PreviewType = PreviewParameters::PreviewImage);
......
......@@ -39,14 +39,14 @@ PreviewLoadThread::PreviewLoadThread(QObject* const parent)
m_loadingPolicy = LoadingPolicyFirstRemovePrevious;
}
LoadingDescription PreviewLoadThread::createLoadingDescription(const QString& filePath, int size)
LoadingDescription PreviewLoadThread::createLoadingDescription(const QString& filePath, const PreviewSettings& settings, int size)
{
return createLoadingDescription(filePath, size, IccManager::displayProfile(m_displayingWidget));
return createLoadingDescription(filePath, settings, size, IccManager::displayProfile(m_displayingWidget));
}
LoadingDescription PreviewLoadThread::createLoadingDescription(const QString& filePath, int size, const IccProfile& displayProfile)
LoadingDescription PreviewLoadThread::createLoadingDescription(const QString& filePath, const PreviewSettings& previewSettings, int size, const IccProfile& displayProfile)
{
LoadingDescription description(filePath, size);
LoadingDescription description(filePath, previewSettings, size);
if (DImg::fileFormat(filePath) == DImg::RAW)
{
......@@ -74,21 +74,27 @@ LoadingDescription PreviewLoadThread::createLoadingDescription(const QString& fi
return description;
}
void PreviewLoadThread::load(const QString& filePath, int size)
void PreviewLoadThread::loadFast(const QString& filePath, int size)
{
load(createLoadingDescription(filePath, size));
PreviewSettings settings(PreviewSettings::FastPreview);
load(createLoadingDescription(filePath, settings, size));
}
void PreviewLoadThread::loadFastButLarge(const QString& filePath, int size)
{
LoadingDescription description = createLoadingDescription(filePath, size);
description.previewParameters.flags |= LoadingDescription::PreviewParameters::FastButLarge;
load(description);
PreviewSettings settings(PreviewSettings::FastButLargePreview);
load(createLoadingDescription(filePath, settings, size));
}
void PreviewLoadThread::loadHighQuality(const QString& filePath)
void PreviewLoadThread::loadHighQuality(const QString& filePath, PreviewSettings::RawLoading rawLoadingMode)
{
load(filePath, 0);
PreviewSettings settings(PreviewSettings::HighQualityPreview, rawLoadingMode);
load(createLoadingDescription(filePath, settings, 0));
}
void PreviewLoadThread::load(const QString& filePath, const PreviewSettings& settings, int size)
{
load(createLoadingDescription(filePath, settings, size));
}
void PreviewLoadThread::load(const LoadingDescription& description)
......@@ -102,10 +108,27 @@ void PreviewLoadThread::setDisplayingWidget(QWidget* const widget)
m_displayingWidget = widget;
}
DImg PreviewLoadThread::loadSynchronously(const QString& filePath, int size, const IccProfile& profile)
DImg PreviewLoadThread::loadFastSynchronously(const QString& filePath, int size, const IccProfile& profile)
{
PreviewSettings settings(PreviewSettings::FastPreview);
return loadSynchronously(createLoadingDescription(filePath, settings, size, profile));
}
DImg PreviewLoadThread::loadFastButLargeSynchronously(const QString& filePath, int minimumSize, const IccProfile& profile)
{
PreviewSettings settings(PreviewSettings::FastButLargePreview);
return loadSynchronously(createLoadingDescription(filePath, settings, minimumSize, profile));
}
DImg PreviewLoadThread::loadHighQualitySynchronously(const QString& filePath, PreviewSettings::RawLoading rawLoadingMode, const IccProfile& profile)
{
PreviewSettings settings(PreviewSettings::HighQualityPreview, rawLoadingMode);
return loadSynchronously(createLoadingDescription(filePath, settings, 0, profile));
}
DImg PreviewLoadThread::loadSynchronously(const QString& filePath, const PreviewSettings& previewSettings, int size, const IccProfile& profile)
{
LoadingDescription description = createLoadingDescription(filePath, size, profile);
return loadSynchronously(description);
return loadSynchronously(createLoadingDescription(filePath, previewSettings, size, profile));
}
DImg PreviewLoadThread::loadSynchronously(const LoadingDescription& description)
......
......@@ -27,6 +27,7 @@
// Local includes
#include "managedloadsavethread.h"
#include "previewsettings.h"
namespace Digikam
{
......@@ -49,7 +50,7 @@ public:
* Load a preview that is optimized for fast loading.
* Raw decoding and color management settings will be adjusted.
*/
void load(const QString& filePath, int size);
void loadFast(const QString& filePath, int size);
/**
* Load a preview that is as large as possible without sacrificing speed
......@@ -63,7 +64,16 @@ public:
* for less speed.
* Raw decoding and color management settings will be adjusted.
*/
void loadHighQuality(const QString& filePath);
void loadHighQuality(const QString& filePath, PreviewSettings::RawLoading rawLoadingMode = PreviewSettings::RawPreviewAutomatic);
/**
* Load a preview.
* Settings determine the loading mode.
* For fast loading, size is preview area size.
* For fast-but-large loading, it serves as a minimum size.
* For high quality loading, it is ignored
*/
void load(const QString& filePath, const PreviewSettings& settings, int size = 0);
/**
* Load a preview. Loading description will not be touched.
......@@ -73,13 +83,20 @@ public:
/// Optionally, set the displaying widget for color management
void setDisplayingWidget(QWidget* const widget);
static DImg loadSynchronously(const QString& filePath, int size, const IccProfile& profile = IccProfile());
/**
* Synchronous versions of the above methods.
* These are safe to call from the non-UI thread, as the IccProfile either passed or deduced independent from a displaying widget
*/
static DImg loadFastSynchronously(const QString& filePath, int size, const IccProfile& profile = IccProfile());
static DImg loadFastButLargeSynchronously(const QString& filePath, int minimumSize, const IccProfile& profile = IccProfile());
static DImg loadHighQualitySynchronously(const QString& filePath, PreviewSettings::RawLoading rawLoadingMode = PreviewSettings::RawPreviewAutomatic, const IccProfile& profile = IccProfile());
static DImg loadSynchronously(const QString& filePath, const PreviewSettings& previewSettings, int size, const IccProfile& profile = IccProfile());
static DImg loadSynchronously(const LoadingDescription& description);
protected:
static LoadingDescription createLoadingDescription(const QString& filePath, int size, const IccProfile& profile);
LoadingDescription createLoadingDescription(const QString& filePath, int size);
static LoadingDescription createLoadingDescription(const QString& filePath, const PreviewSettings& settings, int size, const IccProfile& profile);
LoadingDescription createLoadingDescription(const QString& filePath, const PreviewSettings& settings, int size);
protected:
......
......@@ -210,254 +210,147 @@ void PreviewLoadingTask::execute()
// Preview is not in cache, we will load image from file.
int size = m_loadingDescription.previewParameters.size;
bool fromEmbeddedPreview = false;
QImage qimage;
KExiv2Iface::KExiv2Previews previews(m_loadingDescription.filePath);
// Check original image size using Exiv2.
QSize originalSize = previews.originalSize();
DImg::FORMAT format = DImg::fileFormat(m_loadingDescription.filePath);
m_fromRawEmbeddedPreview = false;
// If RAW file, promote LibRaw method to get original image size.
if (!originalSize.isValid() && format == DImg::RAW)
if (format == DImg::RAW)
{
DcrawInfoContainer container;
if (KDcrawIface::KDcraw::rawFileIdentify(container, m_loadingDescription.filePath))
{
originalSize = container.imageSize;
}
}
// If minimum image size is passed (not null), we will try to find relevant embedded preview size from file.
if (size)
{
int sizeLimit = -1;
int bestSize = qMax(originalSize.width(), originalSize.height());
// for RAWs, the alternative is the half preview, so best size is already originalSize / 2
if (format == DImg::RAW)
KExiv2Iface::KExiv2Previews previews(m_loadingDescription.filePath);
// Check original image size using Exiv2.
QSize originalSize = previews.originalSize();
// If not valid, get original size from LibRaw
if (!originalSize.isValid())
{
bestSize /= 2;
}
if (m_loadingDescription.previewParameters.fastButLarge())
{
if (format == DImg::RAW)
DcrawInfoContainer container;
if (KDcrawIface::KDcraw::rawFileIdentify(container, m_loadingDescription.filePath))
{
sizeLimit = qMin(size, bestSize);
originalSize = container.imageSize;
}
}
else
{
int aBitSmallerThanSize = (int)lround(double(size) * 0.8);
sizeLimit = qMin(aBitSmallerThanSize, bestSize);
}
// -- STAGE 1: Try to extract previews with Exiv2 ----------
// Only check the first and largest preview
if (sizeLimit != -1 && !previews.isEmpty() && continueQuery())
switch (m_loadingDescription.previewParameters.previewSettings.quality)
{
// Require at least half preview size
if (qMax(previews.width(), previews.height()) >= sizeLimit)
case PreviewSettings::FastPreview:
case PreviewSettings::FastButLargePreview:
{
// See bug #339144 : only handle preview if right libkexiv2 version is used.
#if KEXIV2_VERSION >= 0x020302
qimage = previews.image();
#endif
if (!qimage.isNull())
// Size calculations
int sizeLimit = -1;
int bestSize = qMax(originalSize.width(), originalSize.height());
// for RAWs, the alternative is the half preview, so best size is already originalSize / 2
bestSize /= 2;
if (m_loadingDescription.previewParameters.previewSettings.quality == PreviewSettings::FastButLargePreview)
{
fromEmbeddedPreview = true;
sizeLimit = qMin(m_loadingDescription.previewParameters.size, bestSize);
}
}
}
// -- STAGE 2: Try to extract previews with LibRaw ----------
if (format == DImg::RAW && qimage.isNull() && continueQuery())
{
// Try LibRaw preview loading, it may support some more raws
QImage kdcrawPreview;
KDcrawIface::KDcraw::loadEmbeddedPreview(kdcrawPreview, m_loadingDescription.filePath);
if (!kdcrawPreview.isNull() &&
qMax(kdcrawPreview.width(), kdcrawPreview.height()) >= sizeLimit)
{
qimage = kdcrawPreview;
fromEmbeddedPreview = true;
}
else
{
// Fall back to non-demosaiced loading from raw data
KDcrawIface::KDcraw::loadHalfPreview(qimage, m_loadingDescription.filePath);
}
}
// -- STAGE 3: Try to extract Exif/IPTC preview -------------
if (qimage.isNull() && continueQuery())
{
loadImagePreview(qimage, m_loadingDescription.filePath);
}
if (!qimage.isNull() && continueQuery())
{
// convert from QImage
m_img = DImg(qimage);
DImg::FORMAT format = DImg::fileFormat(m_loadingDescription.filePath);
m_img.setAttribute("detectedFileFormat", format);
m_img.setAttribute("originalFilePath", m_loadingDescription.filePath);
DMetadata metadata(m_loadingDescription.filePath);
m_img.setAttribute("originalSize", metadata.getPixelSize());
m_img.setMetadata(metadata.data());
// mark as embedded preview (for Exif rotation)
if (fromEmbeddedPreview)
{
m_img.setAttribute("fromRawEmbeddedPreview", true);
// If we loaded the embedded preview, the Exif of the RAW indicates
// the color space of the preview (see bug 195950 for NEF files)
m_img.setIccProfile(metadata.getIccProfile());
if (loadExiv2Preview(previews, sizeLimit))
{
break;
}
if (loadLibRawPreview(sizeLimit))
{
break;
}
loadHalfSizeRaw();
}
// free memory
qimage = QImage();
}
// -- STAGE 4: Try to use DImg-dependent loading methods -------------
if (m_img.isNull() && continueQuery())
{
// Set a hint to try to load a JPEG or PGF with the fast scale-before-decoding method
if (!m_loadingDescription.previewParameters.fastButLarge())
case PreviewSettings::HighQualityPreview:
{
m_img.setAttribute("scaledLoadingSize", size);
switch (m_loadingDescription.previewParameters.previewSettings.rawLoading)
{
case PreviewSettings::RawPreviewAutomatic:
{
// If we find a preview that is larger than half size (which is what we get from half-size original data), we take it
int acceptableSize = qMax(lround(originalSize.width() * 0.48), lround(originalSize.height() * 0.48));
if (loadExiv2Preview(previews, acceptableSize))
{
break;
}
if (loadLibRawPreview(acceptableSize))
{
break;
}
loadHalfSizeRaw();
break;
}
case PreviewSettings::RawPreviewFromEmbeddedPreview:
{
if (loadExiv2Preview(previews))
{
break;
}
if (loadLibRawPreview())
{
break;
}
loadHalfSizeRaw();
break;
}
case PreviewSettings::RawPreviewFromRawHalfSize:
{
loadHalfSizeRaw();
break;
}
}
}
m_img.load(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings);
}
// -- END: error to load preview -------------------------------------
if (m_img.isNull() && continueQuery())
{
kWarning() << "Cannot extract preview for " << m_loadingDescription.filePath;
}
// So far, everything loaded QImage. Convert to DImg.
convertQImageToDImg();
}
else // No minimum image size is passed, we will try to load full image as preview.
else // Non-RAW images
{
// discard if smaller than half preview
int acceptableWidth = lround(originalSize.width() * 0.48);
int acceptableHeight = lround(originalSize.height() * 0.48);
// -- STAGE 1: Try to extract previews with Exiv2 ----------
if (qimage.isNull() && !previews.isEmpty() && continueQuery())
bool isFast = m_loadingDescription.previewParameters.previewSettings.quality == PreviewSettings::FastPreview;
switch (m_loadingDescription.previewParameters.previewSettings.quality)
{
if (previews.width() >= acceptableWidth && previews.height() >= acceptableHeight)
case PreviewSettings::FastPreview:
case PreviewSettings::FastButLargePreview:
{
qimage = previews.image();
if (!qimage.isNull())
if (isFast && loadImagePreview(m_loadingDescription.previewParameters.size))
{
fromEmbeddedPreview = true;
convertQImageToDImg();
break;
}
}
}
// -- STAGE 2: Try to extract previews with LibRaw ----------
if (format == DImg::RAW && qimage.isNull() && continueQuery())
{
// Try libraw preview loading, it may support some more raws
QImage kdcrawPreview;
KDcrawIface::KDcraw::loadEmbeddedPreview(kdcrawPreview, m_loadingDescription.filePath);
if (continueQuery())
{
// Set a hint to try to load a JPEG or PGF with the fast scale-before-decoding method
if (isFast)
{
m_img.setAttribute("scaledLoadingSize", m_loadingDescription.previewParameters.size);
}
if (!kdcrawPreview.isNull() &&
kdcrawPreview.width() >= acceptableWidth &&
kdcrawPreview.height() >= acceptableHeight)
{
qimage = kdcrawPreview;
fromEmbeddedPreview = true;
m_img.load(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings);
}
break;
}
else
case PreviewSettings::HighQualityPreview:
{
// Fall back to non-demosaiced loading or the raw data
KDcrawIface::KDcraw::loadHalfPreview(qimage, m_loadingDescription.filePath);
}
}
if (!qimage.isNull() && continueQuery())
{
// convert from QImage
m_img = DImg(qimage);
DImg::FORMAT format = DImg::fileFormat(m_loadingDescription.filePath);
m_img.setAttribute("detectedFileFormat", format);
m_img.setAttribute("originalFilePath", m_loadingDescription.filePath);
DMetadata metadata(m_loadingDescription.filePath);
m_img.setAttribute("originalSize", metadata.getPixelSize());
m_img.setMetadata(metadata.data());
// mark as embedded preview (for Exif rotation)
if (fromEmbeddedPreview)
{
m_img.setAttribute("fromRawEmbeddedPreview", true);
// If we loaded the embedded preview, the Exif of the RAW indicates
// the color space of the preview (see bug 195950 for NEF files)
m_img.setIccProfile(metadata.getIccProfile());
}
// free memory
qimage = QImage();
}
// -- STAGE 3: Try to use DImg-dependent loading methods -------------
if (m_img.isNull() && continueQuery())
{
m_img.load(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings);
if (continueQuery())
{
m_img.load(m_loadingDescription.filePath, this, m_loadingDescription.rawDecodingSettings);
// Now that we did a full load of the image, consider putting it in the cache
// but not for RAWs, there are so many cases to consider
if (!m_img.isNull() && m_img.detectedFormat() != DImg::RAW)
{
LoadingCache::CacheLock lock(cache);
LoadingDescription fullDescription(m_loadingDescription.filePath);
cache->putImage(fullDescription.cacheKey(), new DImg(m_img.copy()), m_loadingDescription.filePath);
// Now that we did a full load of the image, consider putting it in the cache
// but not for RAWs, there are so many cases to consider
if (!m_img.isNull())
{
LoadingCache::CacheLock lock(cache);
LoadingDescription fullDescription(m_loadingDescription.filePath);
cache->putImage(fullDescription.cacheKey(), new DImg(m_img.copy()), m_loadingDescription.filePath);
}
}
}
}
// -- END: error to load preview -------------------------------------
if (m_img.isNull() && continueQuery())
{
kWarning() << "Cannot extract preview for " << m_loadingDescription.filePath;
}
}
// Post rocessing
if (m_img.isNull() && continueQuery())
{
kWarning() << "Cannot extract preview for " << m_loadingDescription.filePath;
}
// Post processing
if (continueQuery())
{
......@@ -469,9 +362,9 @@ void PreviewLoadingTask::execute()
QSize scaledSize = m_img.size();
if (needToScale(scaledSize, size))
if (needToScale())
{
scaledSize.scale(size, size, Qt::KeepAspectRatio);
scaledSize.scale(m_loadingDescription.previewParameters.size, m_loadingDescription.previewParameters.size, Qt::KeepAspectRatio);
m_img = m_img.smoothScale(scaledSize.width(), scaledSize.height());
}
......@@ -567,34 +460,117 @@ void PreviewLoadingTask::execute()
}
}
bool PreviewLoadingTask::needToScale(const QSize& imageSize, int previewSize)
bool PreviewLoadingTask::needToScale()
{
switch (m_loadingDescription.previewParameters.previewSettings.quality)
{
case PreviewSettings::FastPreview:
if (m_loadingDescription.previewParameters.size > 0)
{
int maxSize = qMax(m_img.width(), m_img.height());
int acceptableUpperSize = lround(1.25 * (double)m_loadingDescription.previewParameters.size);
return (maxSize >= acceptableUpperSize);
}
break;
case PreviewSettings::FastButLargePreview:
case PreviewSettings::HighQualityPreview:
break;
}
return false;
}