refactor: improve image loading and handling in ImageData and ImagesCarousel

This commit is contained in:
2026-01-14 23:52:02 +01:00
parent 9e732bdf2b
commit 1f4d5dcc1d
5 changed files with 74 additions and 31 deletions
+34 -11
View File
@@ -1,34 +1,56 @@
/*
* @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-11-30 20:32:27
* @LastEditTime: 2025-11-30 23:07:52
* @LastEditTime: 2026-01-14 23:31:41
* @Description: Image item widget for displaying an image.
*/
#include "image_item.h"
#include <QImageReader>
#include "logger.h"
using namespace GeneralLogger;
ImageData* ImageData::create(const QString& p, const int initWidth, const int initHeight) {
ImageData* data = new ImageData(p);
data->image = new QImage();
if (!data->image.load(p)) {
QImageReader reader(p);
if (!reader.canRead()) {
error(QString("Failed to load image from path: %1").arg(p));
delete data;
return nullptr;
}
const QSize targetSize(initWidth, initHeight);
data->image = data->image.scaled(targetSize, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
// Crop to center
int x = (data->image.width() - targetSize.width()) / 2;
int y = (data->image.height() - targetSize.height()) / 2;
data->image = data->image.copy(x, y, targetSize.width(), targetSize.height());
const QSize targetSize(initWidth, initHeight);
const QSize originalSize = reader.size();
if (originalSize.isValid()) {
double widthRatio = (double)targetSize.width() / originalSize.width();
double heightRatio = (double)targetSize.height() / originalSize.height();
double scaleFactor = std::max(widthRatio, heightRatio);
QSize scaledSize = originalSize * scaleFactor;
reader.setScaledSize(scaledSize);
}
if (!reader.read(data->image)) {
error(QString("Failed to load image from path: %1").arg(p));
delete data;
return nullptr;
}
if (data->image->size() != targetSize) {
int x = (data->image->width() - targetSize.width()) / 2;
int y = (data->image->height() - targetSize.height()) / 2;
*data->image = data->image->copy(x, y, targetSize.width(), targetSize.height());
}
return data;
}
ImageItem::ImageItem(const ImageData* data,
ImageItem::ImageItem(ImageData* data,
const int itemWidth,
const int itemHeight,
const int itemFocusWidth,
@@ -40,11 +62,12 @@ ImageItem::ImageItem(const ImageData* data,
m_itemFocusSize(itemFocusWidth, itemFocusHeight) {
assert(data != nullptr);
setScaledContents(true);
if (data->image.isNull()) {
if (!data->isValid()) {
setText(":(");
setAlignment(Qt::AlignCenter);
} else {
setPixmap(QPixmap::fromImage(data->image));
setPixmap(QPixmap::fromImage(data->getImage()));
data->releaseImage();
}
setFixedSize(itemWidth, itemHeight);
}
+19 -9
View File
@@ -1,7 +1,7 @@
/*
* @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-11-30 20:31:15
* @LastEditTime: 2025-11-30 23:08:22
* @LastEditTime: 2026-01-14 23:32:58
* @Description: Image item widget for displaying an image.
*/
#ifndef IMAGE_ITEM_H
@@ -17,13 +17,23 @@
* @brief Data structure to hold image information
* and can be safely created and passed between threads.
*/
struct ImageData {
class ImageData {
QFileInfo file;
QImage image;
QImage* image;
public:
static ImageData* create(const QString& p, const int initWidth, const int initHeight);
~ImageData() { releaseImage(); }
void releaseImage() { delete image, image = nullptr; }
[[nodiscard]] const QImage& getImage() const { return *image; }
[[nodiscard]] bool isValid() const { return !image->isNull(); }
[[nodiscard]] const QFileInfo& getFileInfo() const { return file; }
private:
ImageData(const QString& path) : file(path), image() {}
};
@@ -38,7 +48,7 @@ class ImageItem : public QLabel {
public:
static constexpr int s_animationDuration = 300;
explicit ImageItem(const ImageData* data,
explicit ImageItem(ImageData* data,
const int itemWidth,
const int itemHeight,
const int itemFocusWidth,
@@ -47,15 +57,15 @@ class ImageItem : public QLabel {
~ImageItem() override;
[[nodiscard]] QString getFileFullPath() const { return m_data->file.absoluteFilePath(); }
[[nodiscard]] QString getFileFullPath() const { return m_data->getFileInfo().absoluteFilePath(); }
[[nodiscard]] QString getFileName() const { return m_data->file.fileName(); }
[[nodiscard]] QString getFileName() const { return m_data->getFileInfo().fileName(); }
[[nodiscard]] QDateTime getFileDate() const { return m_data->file.lastModified(); }
[[nodiscard]] QDateTime getFileDate() const { return m_data->getFileInfo().lastModified(); }
[[nodiscard]] const QImage& getThumbnail() const { return m_data->image; }
[[nodiscard]] const QImage& getThumbnail() const { return m_data->getImage(); }
[[nodiscard]] qint64 getFileSize() const { return m_data->file.size(); }
[[nodiscard]] qint64 getFileSize() const { return m_data->getFileInfo().size(); }
void setFocus(bool focus = true, bool animate = true);
+8 -5
View File
@@ -1,7 +1,7 @@
/*
* @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:22:53
* @LastEditTime: 2025-12-01 01:43:00
* @LastEditTime: 2026-01-14 23:50:49
* @Description: Animated carousel widget for displaying and selecting images.
*/
#include "images_carousel.h"
@@ -93,7 +93,10 @@ void ImagesCarousel::_onImagesLoaded() {
// Focus the first image
if (m_currentIndex < 0) {
m_currentIndex = 0;
// Ensure the layout events are processed
QTimer::singleShot(0, this, [this]() {
focusCurrImage();
});
}
}
@@ -145,7 +148,7 @@ ImageLoader::ImageLoader(const QString& path, ImagesCarousel* carousel)
setAutoDelete(true);
}
void ImagesCarousel::_insertImageQueue(const ImageData* data) {
void ImagesCarousel::_insertImageQueue(ImageData* data) {
if (!m_noLoadingScreen) {
_insertImage(data);
return;
@@ -156,7 +159,7 @@ void ImagesCarousel::_insertImageQueue(const ImageData* data) {
}
}
int ImagesCarousel::_insertImage(const ImageData* data) {
int ImagesCarousel::_insertImage(ImageData* data) {
// Increase loaded count regardless of success or failure
Defer defer([this]() {
emit imageLoaded(getLoadedImagesCount());
@@ -270,7 +273,7 @@ void ImageLoader::run() {
QMetaObject::invokeMethod(m_carousel,
"_insertImageQueue",
Qt::QueuedConnection,
Q_ARG(const ImageData*, data));
Q_ARG(ImageData*, data));
});
{
QMutexLocker stopSignLocker(&m_carousel->m_stopSignMutex);
@@ -340,7 +343,7 @@ void ImagesCarousel::focusCurrImage() {
}
auto item = getImageItemAt(m_currentIndex);
if (!item) {
warn(QString("Failed to get item at index: %1").arg(m_currentIndex));
error(QString("Failed to get item at index: %1").arg(m_currentIndex));
return;
}
item->setFocus(true, m_animationEnabled);
+3 -3
View File
@@ -1,7 +1,7 @@
/*
* @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-08-05 01:22:53
* @LastEditTime: 2025-12-01 01:36:04
* @LastEditTime: 2026-01-14 23:26:32
* @Description: Animated carousel widget for displaying and selecting images.
*/
#ifndef IMAGES_CAROUSEL_H
@@ -121,8 +121,8 @@ class ImagesCarousel : public QWidget {
void appendImages(const QStringList& paths);
private:
int _insertImage(const ImageData* item);
Q_INVOKABLE void _insertImageQueue(const ImageData* item);
int _insertImage(ImageData* item);
Q_INVOKABLE void _insertImageQueue(ImageData* item);
void _enableUIUpdates(bool enable);
int _focusingLeftOffset(int index);
+7
View File
@@ -1,3 +1,10 @@
/*
* @Author: Uyanide pywang0608@foxmail.com
* @Date: 2025-11-30 20:59:57
* @LastEditTime: 2025-12-07 06:08:18
* @Description: THE utils header that every project needs :)
*/
#ifndef UTILS_H
#define UTILS_H