From c8acbf13199d41e062c7fe075fbb16b11636994f Mon Sep 17 00:00:00 2001 From: Uyanide Date: Fri, 27 Feb 2026 16:24:21 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20cache=20loaded=20images=20a?= =?UTF-8?q?nd=20computed=20dominant=20colors=20using=20db?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 2 +- WallReel/Core/CMakeLists.txt | 2 + WallReel/Core/Cache/manager.cpp | 247 ++++++++++++++++++++++++++++++++ WallReel/Core/Cache/manager.hpp | 42 ++++++ WallReel/Core/Cache/types.hpp | 33 +++++ WallReel/Core/Image/data.cpp | 152 +++++++------------- WallReel/Core/Image/data.hpp | 20 +-- WallReel/Core/Image/model.cpp | 13 +- WallReel/Core/Image/model.hpp | 5 +- WallReel/main.cpp | 5 +- 10 files changed, 398 insertions(+), 123 deletions(-) create mode 100644 WallReel/Core/Cache/manager.cpp create mode 100644 WallReel/Core/Cache/manager.hpp create mode 100644 WallReel/Core/Cache/types.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 801fe78..1346594 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ endif() configure_file(WallReel/version.h.in ${CMAKE_BINARY_DIR}/generated/version.h) -find_package(Qt6 REQUIRED COMPONENTS Quick Widgets QuickControls2 Concurrent) +find_package(Qt6 REQUIRED COMPONENTS Quick Widgets QuickControls2 Concurrent Sql) qt_standard_project_setup(REQUIRES 6.5) diff --git a/WallReel/Core/CMakeLists.txt b/WallReel/Core/CMakeLists.txt index 7d97d17..3c3efc8 100644 --- a/WallReel/Core/CMakeLists.txt +++ b/WallReel/Core/CMakeLists.txt @@ -2,6 +2,7 @@ qt_add_qml_module(${CORELIB_NAME} URI ${COREMODULE_URI} VERSION ${MODULE_VERSION_MAJOR}.${MODULE_VERSION_MINOR} SOURCES + Cache/manager.hpp Cache/manager.cpp Image/data.hpp Image/data.cpp Image/model.hpp Image/model.cpp Palette/data.hpp @@ -21,6 +22,7 @@ target_link_libraries(${CORELIB_NAME} PUBLIC Qt6::Widgets Qt6::QuickControls2 Qt6::Concurrent + Qt6::Sql ) target_include_directories(${CORELIB_NAME} PUBLIC diff --git a/WallReel/Core/Cache/manager.cpp b/WallReel/Core/Cache/manager.cpp new file mode 100644 index 0000000..8bdb5bb --- /dev/null +++ b/WallReel/Core/Cache/manager.cpp @@ -0,0 +1,247 @@ +#include "manager.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "logger.hpp" + +using namespace Qt::StringLiterals; + +namespace WallReel::Core::Cache { + +QString Manager::cacheKey(const QFileInfo& fileInfo, const QSize& imageSize) { + const QString raw = fileInfo.absoluteFilePath() + QString::number(fileInfo.lastModified().toMSecsSinceEpoch()) + u'x' + QString::number(imageSize.width()) + u'x' + QString::number(imageSize.height()); + return QString::fromLatin1( + QCryptographicHash::hash(raw.toUtf8(), QCryptographicHash::Sha256).toHex()); +} + +Manager::Manager(const QDir& cacheDir) + : m_cacheDir(cacheDir), m_dbPath(cacheDir.filePath(u"cache.db"_s)), m_connectionPrefix(u"WallReelCache:"_s + QString::fromLatin1(QCryptographicHash::hash(m_dbPath.toUtf8(), QCryptographicHash::Md5).toHex())) { + Logger::debug(u"Initializing cache db: %1"_s.arg(m_dbPath)); + // Open a connection on the constructing thread so the schema is + // guaranteed to exist before any worker thread first calls _db(). + _db(); +} + +Manager::~Manager() { + QSet names; + { + QMutexLocker lock(&m_connectionsMutex); + names = std::move(m_connectionNames); + } + Logger::debug(u"Closing %1 cache db connection(s)"_s.arg(names.size())); + for (const QString& connName : std::as_const(names)) { + { + // Scope: release the QSqlDatabase copy before removeDatabase() + QSqlDatabase db = QSqlDatabase::database(connName, false); + if (db.isOpen()) + db.close(); + } + QSqlDatabase::removeDatabase(connName); + } +} + +void Manager::clearCache(Type type) { + QSqlDatabase db = _db(); + if (!db.isOpen()) + return; + + if ((type & Type::Image) != Type::None) { + int removed = 0; + QSqlQuery selectQuery(db); + if (selectQuery.exec(QStringLiteral("SELECT file_name FROM image_cache"))) { + while (selectQuery.next()) { + QFile::remove(m_cacheDir.filePath(selectQuery.value(0).toString())); + ++removed; + } + } + QSqlQuery(db).exec(QStringLiteral("DELETE FROM image_cache")); + Logger::info(u"Cleared %1 image cache file(s)"_s.arg(removed)); + } + + if ((type & Type::Color) != Type::None) { + QSqlQuery(db).exec(QStringLiteral("DELETE FROM color_cache")); + Logger::info(u"Cleared color cache"_s); + } +} + +QColor Manager::getColor(const QString& key, const std::function& computeFunc) { + QSqlDatabase db = _db(); + if (db.isOpen()) { + QSqlQuery query(db); + query.prepare(QStringLiteral( + "SELECT r, g, b, a FROM color_cache WHERE key = :key")); + query.bindValue(u":key"_s, key); + + if (query.exec() && query.next()) { + Logger::debug(u"Color cache hit [%1]"_s.arg(key)); + return QColor( + query.value(0).toInt(), + query.value(1).toInt(), + query.value(2).toInt(), + query.value(3).toInt()); + } + } + + Logger::debug(u"Color cache miss [%1], computing"_s.arg(key)); + const QColor color = computeFunc(); + + if (!color.isValid()) { + Logger::warn(u"ComputeFunc returned invalid color for key [%1]"_s.arg(key)); + return color; + } + + if (db.isOpen()) { + QSqlQuery insertQuery(db); + insertQuery.prepare(QStringLiteral( + "INSERT OR REPLACE INTO color_cache (key, r, g, b, a) " + "VALUES (:key, :r, :g, :b, :a)")); + insertQuery.bindValue(u":key"_s, key); + insertQuery.bindValue(u":r"_s, color.red()); + insertQuery.bindValue(u":g"_s, color.green()); + insertQuery.bindValue(u":b"_s, color.blue()); + insertQuery.bindValue(u":a"_s, color.alpha()); + if (!insertQuery.exec()) + Logger::warn(u"Failed to cache color [%1]: %2"_s + .arg(key, insertQuery.lastError().text())); + else + Logger::debug(u"Color cached [%1]"_s.arg(key)); + } + + return color; +} + +QFileInfo Manager::getImage(const QString& key, const std::function& computeFunc) { + QSqlDatabase db = _db(); + if (db.isOpen()) { + QSqlQuery query(db); + query.prepare(QStringLiteral( + "SELECT file_name FROM image_cache WHERE key = :key")); + query.bindValue(u":key"_s, key); + + if (query.exec() && query.next()) { + const QFileInfo cached(m_cacheDir.filePath(query.value(0).toString())); + if (cached.exists()) { + Logger::debug(u"Image cache hit [%1] -> %2"_s + .arg(key, cached.absoluteFilePath())); + return cached; + } + + // File was deleted externally — evict the stale DB record. + Logger::warn(u"Image cache stale, file missing [%1], evicting"_s.arg(key)); + QSqlQuery evict(db); + evict.prepare(QStringLiteral("DELETE FROM image_cache WHERE key = :key")); + evict.bindValue(u":key"_s, key); + evict.exec(); + } + } + + Logger::debug(u"Image cache miss [%1], computing"_s.arg(key)); + const QImage image = computeFunc(); + if (image.isNull()) { + Logger::warn(u"ComputeFunc returned null image for key [%1]"_s.arg(key)); + return QFileInfo{}; + } + + const QString fileName = key + u".png"_s; + const QString filePath = m_cacheDir.filePath(fileName); + + if (!image.save(filePath, "PNG")) { + Logger::warn(u"Failed to save image to %1"_s.arg(filePath)); + return QFileInfo{}; + } + Logger::debug(u"Image saved to %1"_s.arg(filePath)); + + if (db.isOpen()) { + QSqlQuery insertQuery(db); + insertQuery.prepare(QStringLiteral( + "INSERT OR REPLACE INTO image_cache (key, file_name) " + "VALUES (:key, :file_name)")); + insertQuery.bindValue(u":key"_s, key); + insertQuery.bindValue(u":file_name"_s, fileName); + if (!insertQuery.exec()) + Logger::warn(u"Failed to record image in db [%1]: %2"_s + .arg(key, insertQuery.lastError().text())); + } + + return QFileInfo(filePath); +} + +/// Returns an open QSqlDatabase for the calling thread, creating it on first use. +QSqlDatabase Manager::_db() const { + // thread_local: one slot per OS thread, initialized on first call in that thread. + // For QThreadPool workers the thread is reused across tasks, so the connection + // is opened once per worker thread for the lifetime of the Manager. + thread_local QHash tlsConns; + + auto it = tlsConns.find(m_connectionPrefix); + if (it != tlsConns.end()) { + QSqlDatabase db = QSqlDatabase::database(*it, false); + if (db.isOpen()) + return db; + // Reopen if closed externally. + Logger::debug(u"Reopening cache db connection [%1]"_s.arg(*it)); + if (!db.open()) { + Logger::warn(u"Cannot reopen cache database: %1"_s.arg(db.lastError().text())); + return QSqlDatabase{}; + } + QSqlQuery q(db); + q.exec(u"PRAGMA journal_mode=WAL"_s); + q.exec(u"PRAGMA synchronous=NORMAL"_s); + return db; + } + + // First use of this Manager in this thread. + const QString connName = m_connectionPrefix + u':' + + QString::number(reinterpret_cast(QThread::currentThreadId())); + + QSqlDatabase db = QSqlDatabase::addDatabase(u"QSQLITE"_s, connName); + db.setDatabaseName(m_dbPath); + + if (!db.open()) { + Logger::warn(u"Cannot open cache database %1: %2"_s + .arg(m_dbPath, db.lastError().text())); + db = QSqlDatabase{}; + QSqlDatabase::removeDatabase(connName); + return QSqlDatabase{}; + } + Logger::debug(u"Opened cache db connection [%1]"_s.arg(connName)); + + tlsConns.insert(m_connectionPrefix, connName); + { + QMutexLocker lock(&m_connectionsMutex); + m_connectionNames.insert(connName); + } + + QSqlQuery q(db); + q.exec(u"PRAGMA journal_mode=WAL"_s); + q.exec(u"PRAGMA synchronous=NORMAL"_s); + q.exec(u"PRAGMA foreign_keys=ON"_s); + _setupTables(db); + + return db; +} + +void Manager::_setupTables(QSqlDatabase& db) const { + QSqlQuery q(db); + q.exec(QStringLiteral( + "CREATE TABLE IF NOT EXISTS color_cache (" + " key TEXT PRIMARY KEY NOT NULL," + " r INTEGER NOT NULL," + " g INTEGER NOT NULL," + " b INTEGER NOT NULL," + " a INTEGER NOT NULL" + ")")); + q.exec(QStringLiteral( + "CREATE TABLE IF NOT EXISTS image_cache (" + " key TEXT PRIMARY KEY NOT NULL," + " file_name TEXT NOT NULL" + ")")); +} + +} // namespace WallReel::Core::Cache diff --git a/WallReel/Core/Cache/manager.hpp b/WallReel/Core/Cache/manager.hpp new file mode 100644 index 0000000..cfa7f05 --- /dev/null +++ b/WallReel/Core/Cache/manager.hpp @@ -0,0 +1,42 @@ +#ifndef WALLREEL_CACHE_MANAGER_HPP +#define WALLREEL_CACHE_MANAGER_HPP + +#include +#include +#include +#include +#include + +#include "types.hpp" + +namespace WallReel::Core::Cache { + +class Manager { + public: + static QString cacheKey(const QFileInfo& fileInfo, const QSize& imageSize); + + Manager(const QDir& cacheDir); + + ~Manager(); + + void clearCache(Type type = Type::Image | Type::Color); + + QColor getColor(const QString& key, const std::function& computeFunc); + + QFileInfo getImage(const QString& key, const std::function& computeFunc); + + private: + QDir m_cacheDir; + QString m_dbPath; + QString m_connectionPrefix; + + mutable QMutex m_connectionsMutex; + mutable QSet m_connectionNames; + + QSqlDatabase _db() const; + void _setupTables(QSqlDatabase& db) const; +}; + +} // namespace WallReel::Core::Cache + +#endif // WALLREEL_CACHE_MANAGER_HPP diff --git a/WallReel/Core/Cache/types.hpp b/WallReel/Core/Cache/types.hpp new file mode 100644 index 0000000..133e222 --- /dev/null +++ b/WallReel/Core/Cache/types.hpp @@ -0,0 +1,33 @@ +#ifndef WALLREEL_CACHE_TYPES_HPP +#define WALLREEL_CACHE_TYPES_HPP + +#include +#include +#include +#include +#include + +namespace WallReel::Core::Cache { + +enum class Type : uint32_t { + None = 0, + Image = 1, ///< Cache for processed images + Color = 1 << 1, ///< Cache for palette color matching results + All = ~0u +}; + +inline constexpr Type operator|(Type a, Type b) { + using T = std::underlying_type_t; + return static_cast(static_cast(a) | static_cast(b)); +} + +inline constexpr Type operator&(Type a, Type b) { + using T = std::underlying_type_t; + return static_cast(static_cast(a) & static_cast(b)); +} + +using Data = std::variant; + +} // namespace WallReel::Core::Cache + +#endif // WALLREEL_CACHE_TYPES_HPP diff --git a/WallReel/Core/Image/data.cpp b/WallReel/Core/Image/data.cpp index 1af3d4f..0e37b60 100644 --- a/WallReel/Core/Image/data.cpp +++ b/WallReel/Core/Image/data.cpp @@ -4,13 +4,12 @@ #include #include "Palette/domcolor.hpp" -#include "logger.hpp" WallReel::Core::Image::Data* WallReel::Core::Image::Data::create( const QString& path, const QSize& size, - const QDir& cacheDir) { - Data* ret = new Data(path, size, cacheDir); + Cache::Manager& cacheMgr) { + Data* ret = new Data(path, size, cacheMgr); if (!ret->isValid()) { delete ret; return nullptr; @@ -18,99 +17,66 @@ WallReel::Core::Image::Data* WallReel::Core::Image::Data::create( return ret; } -WallReel::Core::Image::Data::Data(const QString& path, const QSize& targetSize, const QDir& cacheDir) - : m_file(path), m_targetSize(targetSize) { - m_id = _generateId(path, targetSize); - const auto cachePath = cacheDir.absoluteFilePath(_generateCacheFileName(m_id)); - m_cachedFile = QFileInfo(cachePath); +WallReel::Core::Image::Data::Data(const QString& path, const QSize& targetSize, Cache::Manager& cacheMgr) + : m_cacheMgr(cacheMgr), m_file(path), m_targetSize(targetSize) { + m_id = cacheMgr.cacheKey(m_file, m_targetSize); + m_cachedFile = cacheMgr.getImage(m_id, [this]() { + QImageReader reader(m_file.absoluteFilePath()); + if (!reader.canRead()) { + return QImage(); + } - // If cached file exists, use it directly - if (m_cachedFile.exists()) { - Logger::debug(QString("Cache hit for image: %1").arg(m_file.absoluteFilePath())); - if (!_loadFromCache()) { - Logger::warn(QString("Failed to load cached image from path: %1").arg(m_cachedFile.absoluteFilePath())); - if (!_loadFresh()) { - Logger::warn(QString("Failed to load fresh image from path: %1").arg(m_file.absoluteFilePath())); + const QSize originalSize = reader.size(); + + // Scale the image to fit the target size while maintaining aspect ratio + QSize processSize = originalSize; + if (originalSize.isValid()) { + double widthRatio = (double)m_targetSize.width() / originalSize.width(); + double heightRatio = (double)m_targetSize.height() / originalSize.height(); + double scaleFactor = std::max(widthRatio, heightRatio); + processSize = originalSize * scaleFactor; + + // Use reader's built-in scaling if supported + if (reader.supportsOption(QImageIOHandler::ScaledSize)) { + reader.setScaledSize(processSize); } } - } else { - if (!_loadFresh()) { - Logger::warn(QString("Failed to load fresh image from path: %1").arg(m_file.absoluteFilePath())); + + QImage image; + if (!reader.read(&image)) { + return QImage(); } - } -} -bool WallReel::Core::Image::Data::_loadFresh() { - QImageReader reader(m_file.absoluteFilePath()); - if (!reader.canRead()) { - return false; - } - - const QSize originalSize = reader.size(); - - // Scale the image to fit the target size while maintaining aspect ratio - QSize processSize = originalSize; - if (originalSize.isValid()) { - double widthRatio = (double)m_targetSize.width() / originalSize.width(); - double heightRatio = (double)m_targetSize.height() / originalSize.height(); - double scaleFactor = std::max(widthRatio, heightRatio); - processSize = originalSize * scaleFactor; - - // Use reader's built-in scaling if supported - if (reader.supportsOption(QImageIOHandler::ScaledSize)) { - reader.setScaledSize(processSize); + // If reader doesn't support built-in scaling or the image still do not match the target size, do manual scaling + if (image.size() != processSize) { + image = image.scaled(processSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } - } - QImage image; - if (!reader.read(&image)) { - return false; - } + // Crop to target size if necessary + if (image.size() != m_targetSize) { + int x = (image.width() - m_targetSize.width()) / 2; + int y = (image.height() - m_targetSize.height()) / 2; + image = image.copy(x, y, m_targetSize.width(), m_targetSize.height()); + } - // If reader doesn't support built-in scaling or the image still do not match the target size, do manual scaling - if (image.size() != processSize) { - image = image.scaled(processSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - } + // Convert to GPU-friendly format + if (image.format() != QImage::Format_ARGB32_Premultiplied) { + image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } + return image; + }); + m_dominantColor = cacheMgr.getColor(m_id, [this]() { + QImageReader reader(m_cachedFile.absoluteFilePath()); + if (!reader.canRead()) { + return QColor(); + } - // Crop to target size if necessary - if (image.size() != m_targetSize) { - int x = (image.width() - m_targetSize.width()) / 2; - int y = (image.height() - m_targetSize.height()) / 2; - image = image.copy(x, y, m_targetSize.width(), m_targetSize.height()); - } - - // Convert to GPU-friendly format - if (image.format() != QImage::Format_ARGB32_Premultiplied) { - image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); - } - - // Get dominant color - m_dominantColor = Palette::getDominantColor(image); - - // Save to cache - if (!image.save(m_cachedFile.absoluteFilePath())) { - Logger::warn(QString("Failed to save cached image to path: %1").arg(m_cachedFile.absoluteFilePath())); - return false; - } else { - Logger::debug(QString("Cached image saved to path: %1").arg(m_cachedFile.absoluteFilePath())); - } - return true; -} - -bool WallReel::Core::Image::Data::_loadFromCache() { - QImageReader reader(m_cachedFile.absoluteFilePath()); - if (!reader.canRead()) { - return false; - } - - QImage image; - if (!reader.read(&image)) { - return false; - } - - // Get dominant color - m_dominantColor = Palette::getDominantColor(image); - return true; + QImage image; + if (!reader.read(&image)) { + return QColor(); + } + return Palette::getDominantColor(image); + }); } QImage WallReel::Core::Image::Data::loadImage() const { @@ -125,15 +91,3 @@ QImage WallReel::Core::Image::Data::loadImage() const { } return image; } - -QString WallReel::Core::Image::Data::_generateId(const QString& path, const QSize& size) { - auto info = QFileInfo(path); - auto key = QString("%1|%2|%3|%4x%5").arg(info.absoluteFilePath()).arg(info.lastModified().toSecsSinceEpoch()).arg(info.size()).arg(size.width()).arg(size.height()); - - QByteArray hash = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Md5); - return QString::fromLatin1(hash.toHex()); -} - -QString WallReel::Core::Image::Data::_generateCacheFileName(const QString& id) { - return id + ".png"; -} diff --git a/WallReel/Core/Image/data.hpp b/WallReel/Core/Image/data.hpp index 2e682bb..ab09434 100644 --- a/WallReel/Core/Image/data.hpp +++ b/WallReel/Core/Image/data.hpp @@ -6,6 +6,8 @@ #include #include +#include "Cache/manager.hpp" + // Development note /* Current implementation of image loading and caching: @@ -34,10 +36,6 @@ Why this approach - Main purposes - Resizing during loading fundamentally eliminates the possibility of the frontend storing large images in memory. (and not all image formats support `sourceSize` property in the right way) -Possible improvements: -- Cache other properties of the image (dominant color for example) to entirely avoid processing the - image in loading stage. A simple key-value store should be sufficient. - */ namespace WallReel::Core::Image { @@ -47,6 +45,8 @@ namespace WallReel::Core::Image { * */ class Data { + Cache::Manager& m_cacheMgr; + QString m_id; ///< Unique identifier for the image QFileInfo m_file; ///< File information of the image QFileInfo m_cachedFile; ///< Cached file information for the loaded image @@ -54,15 +54,7 @@ class Data { QColor m_dominantColor; ///< Dominant color of the image, used for palette matching QHash m_colorCache; ///< Cache for palette color matching results, key is palette name, value is matched color name - Data(const QString& path, const QSize& size, const QDir& cacheDir); - - bool _loadFromCache(); - - bool _loadFresh(); - - static QString _generateId(const QString& path, const QSize& size); - - static QString _generateCacheFileName(const QString& id); + Data(const QString& path, const QSize& size, Cache::Manager& cacheMgr); public: /** @@ -72,7 +64,7 @@ class Data { * @param size Target size for loaded image, the image will be scaled and cropped to this size and stored in memory * @return Data* */ - static Data* create(const QString& path, const QSize& size, const QDir& cacheDir); + static Data* create(const QString& path, const QSize& size, Cache::Manager& cacheMgr); QSize getTargetSize() const { return m_targetSize; } diff --git a/WallReel/Core/Image/model.cpp b/WallReel/Core/Image/model.cpp index 994b657..64b2921 100644 --- a/WallReel/Core/Image/model.cpp +++ b/WallReel/Core/Image/model.cpp @@ -8,14 +8,15 @@ WallReel::Core::Image::Model::Model( const Config::SortConfigItems& sortConfig, - const QDir& cacheDir, + Cache::Manager& cacheMgr, const QSize& thumbnailSize, QObject* parent) : QAbstractListModel(parent), m_sortConfig(sortConfig), - m_cacheDir(cacheDir), + m_cacheMgr(cacheMgr), m_thumbnailSize(thumbnailSize), - m_currentSortType(sortConfig.type) { + m_currentSortType(sortConfig.type), + m_currentSortReverse(sortConfig.reverse) { connect( &m_watcher, &QFutureWatcher::finished, @@ -184,10 +185,10 @@ void WallReel::Core::Image::Model::loadAndProcess(const QStringList& paths) { // These are all small objects so capturing by value should be fine const auto thumbnailSize = m_thumbnailSize; const auto counterPtr = &m_processedCount; - const auto cacheDir = m_cacheDir; + const auto cacheMgr = &m_cacheMgr; QFuture future = - QtConcurrent::mapped(paths, [thumbnailSize, counterPtr, cacheDir](const QString& path) { - auto data = Data::create(path, thumbnailSize, cacheDir); + QtConcurrent::mapped(paths, [thumbnailSize, counterPtr, cacheMgr](const QString& path) { + auto data = Data::create(path, thumbnailSize, *cacheMgr); counterPtr->fetch_add(1, std::memory_order_relaxed); return data; }); diff --git a/WallReel/Core/Image/model.hpp b/WallReel/Core/Image/model.hpp index 02f78e5..a34f494 100644 --- a/WallReel/Core/Image/model.hpp +++ b/WallReel/Core/Image/model.hpp @@ -7,6 +7,7 @@ #include #include +#include "Cache/manager.hpp" #include "Config/data.hpp" #include "data.hpp" @@ -83,7 +84,7 @@ class Model : public QAbstractListModel { Model( const Config::SortConfigItems& sortConfig, - const QDir& cacheDir, + Cache::Manager& cacheMgr, const QSize& thumbnailSize, QObject* parent = nullptr); @@ -161,7 +162,7 @@ class Model : public QAbstractListModel { private: const Config::SortConfigItems& m_sortConfig; - QDir m_cacheDir; + Cache::Manager& m_cacheMgr; QSize m_thumbnailSize; QList m_data; diff --git a/WallReel/main.cpp b/WallReel/main.cpp index df35fd0..33439cd 100644 --- a/WallReel/main.cpp +++ b/WallReel/main.cpp @@ -4,6 +4,7 @@ #include #include +#include "Cache/manager.hpp" #include "Core/Config/manager.hpp" #include "Core/Image/model.hpp" #include "Core/Palette/data.hpp" @@ -44,9 +45,11 @@ int main(int argc, char* argv[]) { "Config", config); + auto cacheMgr = new Cache::Manager(Utils::getCacheDir()); + auto imageModel = new Image::Model( config->getSortConfig(), - Utils::getCacheDir(), + *cacheMgr, config->getFocusImageSize(), config); qmlRegisterSingletonInstance(