src/thumbnailworker.cpp
browsing at commit = 4db3041aa5124ab367c9f70be0c3a9ed62a38294
#include "thumbnailworker.h"
#include "thumbnailcache.h"
#include <QFile>
#include <QImageReader>
#include <fcntl.h>
#include <unistd.h>
namespace
{
constexpr int kDisplaySize = 128;
QImage downscaleForDisplay(const QImage &src)
{
if (src.isNull())
return src;
if (src.width() <= kDisplaySize && src.height() <= kDisplaySize)
return src;
return src.scaled(kDisplaySize, kDisplaySize,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
}
} // namespace
ThumbnailWorker::ThumbnailWorker(const QByteArray &path,
int row,
ThumbnailCache::Size size,
std::atomic_bool *cancelFlag)
: m_path(path),
m_row(row),
m_size(size),
m_cancelFlag(cancelFlag)
{
setAutoDelete(true);
}
void ThumbnailWorker::run()
{
if (*m_cancelFlag)
return;
// Per the spec, never thumbnail files that live inside the cache itself.
if (ThumbnailCache::isInsideCache(m_path))
return;
const int target = static_cast<int>(m_size);
QImage cached = ThumbnailCache::load(m_path, m_size);
if (*m_cancelFlag)
return;
if (!cached.isNull())
{
emit finished(m_row, downscaleForDisplay(cached));
return;
}
// Open the file via POSIX open() so that paths with non-UTF-8 bytes
// (e.g. Latin-1 encoded filenames) are handled correctly.
int fd = ::open(m_path.constData(), O_RDONLY | O_CLOEXEC);
if (fd < 0)
return;
QFile file;
if (!file.open(fd, QIODevice::ReadOnly, QFileDevice::AutoCloseHandle))
{
::close(fd);
return;
}
QImageReader reader(&file);
reader.setAutoTransform(true);
// Scale during decoding to avoid hitting the allocation limit
// on very large images (Qt 6 defaults to 128 MB).
QSize fullSize = reader.size();
if (fullSize.isValid())
{
QSize scaled = fullSize.scaled(target, target, Qt::KeepAspectRatio);
reader.setScaledSize(scaled);
}
QImage image = reader.read();
if (*m_cancelFlag)
return;
if (image.isNull())
return;
// Ensure the longest side is exactly the target (the decoder hint may have
// been ignored for some formats). Aspect ratio preserved, no padding canvas.
QImage thumb;
if (image.width() > target || image.height() > target)
thumb = image.scaled(target, target,
Qt::KeepAspectRatio,
Qt::SmoothTransformation);
else
thumb = image;
ThumbnailCache::save(m_path, m_size, thumb);
if (*m_cancelFlag)
return;
emit finished(m_row, downscaleForDisplay(thumb));
}