diff --git a/src/image_item.cpp b/src/image_item.cpp index 32c5598..4013b47 100644 --- a/src/image_item.cpp +++ b/src/image_item.cpp @@ -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 + #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); } diff --git a/src/image_item.h b/src/image_item.h index 01731d1..894c293 100644 --- a/src/image_item.h +++ b/src/image_item.h @@ -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); @@ -75,4 +85,4 @@ class ImageItem : public QLabel { void clicked(const QString& path); }; -#endif // IMAGE_ITEM_H \ No newline at end of file +#endif // IMAGE_ITEM_H diff --git a/src/images_carousel.cpp b/src/images_carousel.cpp index 3dc7e3f..260e8f4 100644 --- a/src/images_carousel.cpp +++ b/src/images_carousel.cpp @@ -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; - focusCurrImage(); + // 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); @@ -428,4 +431,4 @@ void ImagesCarousel::_onItemClicked(const QString& path) { void ImagesCarousel::onStop() { QMutexLocker locker(&m_stopSignMutex); m_stopSign = true; -} \ No newline at end of file +} diff --git a/src/images_carousel.h b/src/images_carousel.h index 762ced6..a8ba6fe 100644 --- a/src/images_carousel.h +++ b/src/images_carousel.h @@ -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); diff --git a/src/utils.h b/src/utils.h index 595010b..3e7757a 100644 --- a/src/utils.h +++ b/src/utils.h @@ -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