From 816d5bfe9a635da7a3fda2d217d1ed8064a9e1fb Mon Sep 17 00:00:00 2001 From: luisangelsm Date: Sun, 18 Jan 2026 10:06:44 +0100 Subject: [PATCH] Extract the comic and page flow classes to their own files --- YACReader/YACReader.pro | 4 + YACReader/goto_flow_gl.h | 2 +- YACReaderLibrary/YACReaderLibrary.pro | 4 + YACReaderLibrary/comic_flow_widget.h | 2 +- common/rhi/yacreader_comic_flow_rhi.cpp | 246 +++++++++++++ common/rhi/yacreader_comic_flow_rhi.h | 59 ++++ common/rhi/yacreader_flow_rhi.cpp | 444 +----------------------- common/rhi/yacreader_flow_rhi.h | 105 +----- common/rhi/yacreader_page_flow_rhi.cpp | 204 +++++++++++ common/rhi/yacreader_page_flow_rhi.h | 55 +++ 10 files changed, 582 insertions(+), 543 deletions(-) create mode 100644 common/rhi/yacreader_comic_flow_rhi.cpp create mode 100644 common/rhi/yacreader_comic_flow_rhi.h create mode 100644 common/rhi/yacreader_page_flow_rhi.cpp create mode 100644 common/rhi/yacreader_page_flow_rhi.h diff --git a/YACReader/YACReader.pro b/YACReader/YACReader.pro index 3cc1aa76..940ea723 100644 --- a/YACReader/YACReader.pro +++ b/YACReader/YACReader.pro @@ -110,6 +110,8 @@ HEADERS += ../common/comic.h \ greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) { HEADERS += ../common/rhi/yacreader_flow_rhi.h + HEADERS += ../common/rhi/yacreader_comic_flow_rhi.h \ + ../common/rhi/yacreader_page_flow_rhi.h } } @@ -153,6 +155,8 @@ SOURCES += ../common/comic.cpp \ greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) { SOURCES += ../common/rhi/yacreader_flow_rhi.cpp + SOURCES += ../common/rhi/yacreader_comic_flow_rhi.cpp \ + ../common/rhi/yacreader_page_flow_rhi.cpp RESOURCES += ../common/rhi/shaders/shaders.qrc } } diff --git a/YACReader/goto_flow_gl.h b/YACReader/goto_flow_gl.h index d2278641..8afc0ce3 100644 --- a/YACReader/goto_flow_gl.h +++ b/YACReader/goto_flow_gl.h @@ -6,7 +6,7 @@ // Conditional include based on Qt version and RHI availability #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI) -#include "yacreader_flow_rhi.h" +#include "yacreader_page_flow_rhi.h" using YACReaderPageFlowImpl = YACReaderPageFlow3D; #else #include "yacreader_flow_gl.h" diff --git a/YACReaderLibrary/YACReaderLibrary.pro b/YACReaderLibrary/YACReaderLibrary.pro index 79d1ed17..11952c35 100644 --- a/YACReaderLibrary/YACReaderLibrary.pro +++ b/YACReaderLibrary/YACReaderLibrary.pro @@ -157,6 +157,8 @@ HEADERS += comic_flow.h \ greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) { HEADERS += ../common/rhi/yacreader_flow_rhi.h + HEADERS += ../common/rhi/yacreader_comic_flow_rhi.h \ + ../common/rhi/yacreader_page_flow_rhi.h } } @@ -249,6 +251,8 @@ SOURCES += comic_flow.cpp \ greaterThan(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 6) { SOURCES += ../common/rhi/yacreader_flow_rhi.cpp + SOURCES += ../common/rhi/yacreader_comic_flow_rhi.cpp \ + ../common/rhi/yacreader_page_flow_rhi.cpp RESOURCES += ../common/rhi/shaders/shaders.qrc } } diff --git a/YACReaderLibrary/comic_flow_widget.h b/YACReaderLibrary/comic_flow_widget.h index 98158592..51217cc4 100644 --- a/YACReaderLibrary/comic_flow_widget.h +++ b/YACReaderLibrary/comic_flow_widget.h @@ -8,7 +8,7 @@ #ifndef NO_OPENGL // Conditional include based on Qt version and RHI availability #if QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) && defined(YACREADER_USE_RHI) -#include "yacreader_flow_rhi.h" +#include "yacreader_comic_flow_rhi.h" using YACReaderComicFlowImpl = YACReaderComicFlow3D; #else #include "yacreader_flow_gl.h" diff --git a/common/rhi/yacreader_comic_flow_rhi.cpp b/common/rhi/yacreader_comic_flow_rhi.cpp new file mode 100644 index 00000000..bf7bafaa --- /dev/null +++ b/common/rhi/yacreader_comic_flow_rhi.cpp @@ -0,0 +1,246 @@ +#include "yacreader_comic_flow_rhi.h" +#include + +// YACReaderComicFlow3D implementation +YACReaderComicFlow3D::YACReaderComicFlow3D(QWidget *parent, struct Preset p) + : YACReaderFlow3D(parent, p) +{ + worker = new ImageLoader3D(this); + worker->flow = this; +} + +void YACReaderComicFlow3D::setImagePaths(QStringList paths) +{ + worker->reset(); + reset(); + numObjects = 0; + + if (hasBeenInitialized) { + YACReaderFlow3D::populate(paths.size()); + } else { + lazyPopulateObjects = paths.size(); + } + + this->paths = paths; +} + +void YACReaderComicFlow3D::updateImageData() +{ + if (worker->busy()) + return; + + int idx = worker->index(); + if (idx >= 0 && !worker->result().isNull()) { + if (!loaded[idx]) { + float x = 1; + QImage img = worker->result(); + + // Create QRhiTexture from the loaded image + if (m_rhi) { + QRhiTexture *texture = m_rhi->newTexture(QRhiTexture::BGRA8, img.size(), 1, + (performance == high || performance == ultraHigh) ? QRhiTexture::MipMapped : QRhiTexture::UsedAsTransferSource); + + if (texture->create()) { + PendingTextureUpload upload; + upload.index = idx; + upload.image = img; + upload.x = x; + upload.y = 1 * (float(img.height()) / img.width()); + pendingTextureUploads.append(upload); + + QString s = "cover"; + replace(s.toLocal8Bit().data(), texture, upload.x, upload.y, idx); + } + } + } + } + + int count = 8; + switch (performance) { + case low: + count = 8; + break; + case medium: + count = 10; + break; + case high: + count = 12; + break; + case ultraHigh: + count = 16; + break; + } + + int *indexes = new int[2 * count + 1]; + int center = currentSelected; + indexes[0] = center; + for (int j = 0; j < count; j++) { + indexes[j * 2 + 1] = center + j + 1; + indexes[j * 2 + 2] = center - j - 1; + } + + for (int c = 0; c < 2 * count + 1; c++) { + int i = indexes[c]; + if ((i >= 0) && (i < numObjects)) + if (!loaded[i]) { + if (paths.size() > 0) { + QString fname = paths.at(i); + worker->generate(i, fname); + } + delete[] indexes; + return; + } + } + + delete[] indexes; +} + +void YACReaderComicFlow3D::remove(int item) +{ + worker->lock(); + worker->reset(); + YACReaderFlow3D::remove(item); + if (item >= 0 && item < paths.size()) { + paths.removeAt(item); + } + worker->unlock(); +} + +void YACReaderComicFlow3D::add(const QString &path, int index) +{ + worker->lock(); + worker->reset(); + paths.insert(index, path); + YACReaderFlow3D::add(index); + worker->unlock(); +} + +void YACReaderComicFlow3D::resortCovers(QList newOrder) +{ + worker->lock(); + worker->reset(); + startAnimationTimer(); + + QList pathsNew; + QVector loadedNew; + QVector marksNew; + QVector imagesNew; + + int index = 0; + foreach (int i, newOrder) { + if (i < 0 || i >= images.size()) { + continue; + } + + pathsNew << paths.at(i); + loadedNew << loaded.at(i); + marksNew << marks.at(i); + imagesNew << images.at(i); + imagesNew.last().index = index++; + } + + paths = pathsNew; + loaded = loadedNew; + marks = marksNew; + images = imagesNew; + + worker->unlock(); +} + +// ImageLoader3D implementation +QImage ImageLoader3D::loadImage(const QString &fileName) +{ + QImage image; + + if (!image.load(fileName)) { + return QImage(); + } + + switch (flow->performance) { + case low: + image = image.scaledToWidth(200, Qt::SmoothTransformation); + break; + case medium: + image = image.scaledToWidth(256, Qt::SmoothTransformation); + break; + case high: + image = image.scaledToWidth(320, Qt::SmoothTransformation); + break; + case ultraHigh: + break; + } + + return image; +} + +ImageLoader3D::ImageLoader3D(YACReaderFlow3D *flow) + : QThread(), flow(flow), restart(false), working(false), idx(-1) +{ +} + +ImageLoader3D::~ImageLoader3D() +{ + mutex.lock(); + condition.wakeOne(); + mutex.unlock(); + wait(); +} + +bool ImageLoader3D::busy() const +{ + return isRunning() ? working : false; +} + +void ImageLoader3D::generate(int index, const QString &fileName) +{ + mutex.lock(); + this->idx = index; + this->fileName = fileName; + this->img = QImage(); + mutex.unlock(); + + if (!isRunning()) + start(); + else { + restart = true; + condition.wakeOne(); + } +} + +void ImageLoader3D::lock() +{ + mutex.lock(); +} + +void ImageLoader3D::unlock() +{ + mutex.unlock(); +} + +void ImageLoader3D::run() +{ + for (;;) { + mutex.lock(); + this->working = true; + QString fileName = this->fileName; + mutex.unlock(); + + QImage image = loadImage(fileName); + + mutex.lock(); + this->working = false; + this->img = image; + mutex.unlock(); + + mutex.lock(); + if (!this->restart) + condition.wait(&mutex); + restart = false; + mutex.unlock(); + } +} + +QImage ImageLoader3D::result() +{ + return img; +} diff --git a/common/rhi/yacreader_comic_flow_rhi.h b/common/rhi/yacreader_comic_flow_rhi.h new file mode 100644 index 00000000..c64db0e8 --- /dev/null +++ b/common/rhi/yacreader_comic_flow_rhi.h @@ -0,0 +1,59 @@ +#ifndef __YACREADER_COMIC_FLOW_RHI_H +#define __YACREADER_COMIC_FLOW_RHI_H + +#include "yacreader_flow_rhi.h" + +class ImageLoader3D; + +class YACReaderComicFlow3D : public YACReaderFlow3D +{ +public: + YACReaderComicFlow3D(QWidget *parent = nullptr, struct Preset p = defaultYACReaderFlowConfig); + void setImagePaths(QStringList paths); + void updateImageData() override; + void remove(int item) override; + void add(const QString &path, int index); + void resortCovers(QList newOrder); + friend class ImageLoader3D; + +private: + ImageLoader3D *worker; + +protected: + QList paths; +}; + +class ImageLoader3D : public QThread +{ +public: + ImageLoader3D(YACReaderFlow3D *flow); + ~ImageLoader3D(); + bool busy() const; + void generate(int index, const QString &fileName); + void reset() + { + idx = -1; + fileName = ""; + } + int index() const { return idx; } + void lock(); + void unlock(); + QImage result(); + YACReaderFlow3D *flow; + QImage loadImage(const QString &fileName); + +protected: + void run() override; + +private: + QMutex mutex; + QWaitCondition condition; + + bool restart; + bool working; + int idx; + QString fileName; + QImage img; +}; + +#endif // __YACREADER_COMIC_FLOW_RHI_H diff --git a/common/rhi/yacreader_flow_rhi.cpp b/common/rhi/yacreader_flow_rhi.cpp index 93395927..b8487477 100644 --- a/common/rhi/yacreader_flow_rhi.cpp +++ b/common/rhi/yacreader_flow_rhi.cpp @@ -1259,446 +1259,6 @@ QSize YACReaderFlow3D::minimumSizeHint() const } // YACReaderComicFlow3D implementation -YACReaderComicFlow3D::YACReaderComicFlow3D(QWidget *parent, struct Preset p) - : YACReaderFlow3D(parent, p) -{ - worker = new ImageLoader3D(this); - worker->flow = this; -} +// YACReaderComicFlow3D implementation moved to common/rhi/yacreader_comic_flow_rhi.* -void YACReaderComicFlow3D::setImagePaths(QStringList paths) -{ - worker->reset(); - reset(); - numObjects = 0; - - if (hasBeenInitialized) { - YACReaderFlow3D::populate(paths.size()); - } else { - lazyPopulateObjects = paths.size(); - } - - this->paths = paths; -} - -void YACReaderComicFlow3D::updateImageData() -{ - if (worker->busy()) - return; - - int idx = worker->index(); - if (idx >= 0 && !worker->result().isNull()) { - if (!loaded[idx]) { - float x = 1; - QImage img = worker->result(); - - // // Ensure the loaded image is in RGBA8888 layout so QRhi interprets channels correctly - // if (img.format() != QImage::Format_RGBA8888) - // img = img.convertToFormat(QImage::Format_RGBA8888); - - // Create QRhiTexture from the loaded image - if (m_rhi) { - QRhiTexture *texture = m_rhi->newTexture(QRhiTexture::BGRA8, img.size(), 1, - (performance == high || performance == ultraHigh) ? QRhiTexture::MipMapped : QRhiTexture::UsedAsTransferSource); - - if (texture->create()) { - // Queue texture upload (image already converted to RGBA8888) - PendingTextureUpload upload; - upload.index = idx; - upload.image = img; - upload.x = x; - upload.y = 1 * (float(img.height()) / img.width()); - pendingTextureUploads.append(upload); - - QString s = "cover"; - replace(s.toLocal8Bit().data(), texture, upload.x, upload.y, idx); - } - } - } - } - - int count = 8; - switch (performance) { - case low: - count = 8; - break; - case medium: - count = 10; - break; - case high: - count = 12; - break; - case ultraHigh: - count = 16; - break; - } - - int *indexes = new int[2 * count + 1]; - int center = currentSelected; - indexes[0] = center; - for (int j = 0; j < count; j++) { - indexes[j * 2 + 1] = center + j + 1; - indexes[j * 2 + 2] = center - j - 1; - } - - for (int c = 0; c < 2 * count + 1; c++) { - int i = indexes[c]; - if ((i >= 0) && (i < numObjects)) - if (!loaded[i]) { - if (paths.size() > 0) { - QString fname = paths.at(i); - worker->generate(i, fname); - } - delete[] indexes; - return; - } - } - - delete[] indexes; -} - -void YACReaderComicFlow3D::remove(int item) -{ - worker->lock(); - worker->reset(); - YACReaderFlow3D::remove(item); - if (item >= 0 && item < paths.size()) { - paths.removeAt(item); - } - worker->unlock(); -} - -void YACReaderComicFlow3D::add(const QString &path, int index) -{ - worker->lock(); - worker->reset(); - paths.insert(index, path); - YACReaderFlow3D::add(index); - worker->unlock(); -} - -void YACReaderComicFlow3D::resortCovers(QList newOrder) -{ - worker->lock(); - worker->reset(); - startAnimationTimer(); - - QList pathsNew; - QVector loadedNew; - QVector marksNew; - QVector imagesNew; - - int index = 0; - foreach (int i, newOrder) { - if (i < 0 || i >= images.size()) { - continue; - } - - pathsNew << paths.at(i); - loadedNew << loaded.at(i); - marksNew << marks.at(i); - imagesNew << images.at(i); - imagesNew.last().index = index++; - } - - paths = pathsNew; - loaded = loadedNew; - marks = marksNew; - images = imagesNew; - - worker->unlock(); -} - -// YACReaderPageFlow3D implementation -YACReaderPageFlow3D::YACReaderPageFlow3D(QWidget *parent, struct Preset p) - : YACReaderFlow3D(parent, p) -{ - worker = new ImageLoaderByteArray3D(this); - worker->flow = this; -} - -YACReaderPageFlow3D::~YACReaderPageFlow3D() -{ - if (timerId != -1) { - this->killTimer(timerId); - timerId = -1; - } - rawImages.clear(); - - // Clean up textures and clear images to prevent double-delete in base destructor - for (auto &image : images) { - if (image.texture != scene.defaultTexture.get()) { - delete image.texture; - } - } - images.clear(); - numObjects = 0; -} - -void YACReaderPageFlow3D::updateImageData() -{ - if (worker->busy()) - return; - - int idx = worker->index(); - if (idx >= 0 && !worker->result().isNull()) { - if (!loaded[idx]) { - float x = 1; - QImage img = worker->result(); - - // Create QRhiTexture from the loaded image - if (m_rhi) { - QRhiTexture *texture = m_rhi->newTexture(QRhiTexture::BGRA8, img.size(), 1, - (performance == high || performance == ultraHigh) ? QRhiTexture::MipMapped : QRhiTexture::UsedAsTransferSource); - - if (texture->create()) { - float y = 1 * (float(img.height()) / img.width()); - QString s = "cover"; - replace(s.toLocal8Bit().data(), texture, x, y, idx); - loaded[idx] = true; - } - } - } - } - - int count = 8; - switch (performance) { - case low: - count = 8; - break; - case medium: - count = 10; - break; - case high: - count = 12; - break; - case ultraHigh: - count = 14; - break; - } - - int *indexes = new int[2 * count + 1]; - int center = currentSelected; - indexes[0] = center; - for (int j = 0; j < count; j++) { - indexes[j * 2 + 1] = center + j + 1; - indexes[j * 2 + 2] = center - j - 1; - } - - for (int c = 0; c < 2 * count + 1; c++) { - int i = indexes[c]; - if ((i >= 0) && (i < numObjects)) - if (rawImages.size() > 0) - if (!loaded[i] && imagesReady[i]) { - worker->generate(i, rawImages.at(i)); - delete[] indexes; - return; - } - } - - delete[] indexes; -} - -void YACReaderPageFlow3D::populate(int n) -{ - worker->reset(); - - if (hasBeenInitialized) { - YACReaderFlow3D::populate(n); - } else { - lazyPopulateObjects = n; - } - - imagesReady = QVector(n, false); - rawImages = QVector(n); - imagesSetted = QVector(n, false); -} - -// ImageLoader3D implementation -QImage ImageLoader3D::loadImage(const QString &fileName) -{ - QImage image; - - if (!image.load(fileName)) { - return QImage(); - } - - switch (flow->performance) { - case low: - image = image.scaledToWidth(200, Qt::SmoothTransformation); - break; - case medium: - image = image.scaledToWidth(256, Qt::SmoothTransformation); - break; - case high: - image = image.scaledToWidth(320, Qt::SmoothTransformation); - break; - case ultraHigh: - break; - } - - return image; -} - -ImageLoader3D::ImageLoader3D(YACReaderFlow3D *flow) - : QThread(), flow(flow), restart(false), working(false), idx(-1) -{ -} - -ImageLoader3D::~ImageLoader3D() -{ - mutex.lock(); - condition.wakeOne(); - mutex.unlock(); - wait(); -} - -bool ImageLoader3D::busy() const -{ - return isRunning() ? working : false; -} - -void ImageLoader3D::generate(int index, const QString &fileName) -{ - mutex.lock(); - this->idx = index; - this->fileName = fileName; - this->size = size; - this->img = QImage(); - mutex.unlock(); - - if (!isRunning()) - start(); - else { - restart = true; - condition.wakeOne(); - } -} - -void ImageLoader3D::lock() -{ - mutex.lock(); -} - -void ImageLoader3D::unlock() -{ - mutex.unlock(); -} - -void ImageLoader3D::run() -{ - for (;;) { - mutex.lock(); - this->working = true; - QString fileName = this->fileName; - mutex.unlock(); - - QImage image = loadImage(fileName); - - mutex.lock(); - this->working = false; - this->img = image; - mutex.unlock(); - - mutex.lock(); - if (!this->restart) - condition.wait(&mutex); - restart = false; - mutex.unlock(); - } -} - -QImage ImageLoader3D::result() -{ - return img; -} - -// ImageLoaderByteArray3D implementation -QImage ImageLoaderByteArray3D::loadImage(const QByteArray &raw) -{ - QImage image; - - if (!image.loadFromData(raw)) { - return QImage(); - } - - switch (flow->performance) { - case low: - image = image.scaledToWidth(128, Qt::SmoothTransformation); - break; - case medium: - image = image.scaledToWidth(196, Qt::SmoothTransformation); - break; - case high: - image = image.scaledToWidth(256, Qt::SmoothTransformation); - break; - case ultraHigh: - image = image.scaledToWidth(320, Qt::SmoothTransformation); - break; - } - - return image; -} - -ImageLoaderByteArray3D::ImageLoaderByteArray3D(YACReaderFlow3D *flow) - : QThread(), flow(flow), restart(false), working(false), idx(-1) -{ -} - -ImageLoaderByteArray3D::~ImageLoaderByteArray3D() -{ - mutex.lock(); - condition.wakeOne(); - mutex.unlock(); - wait(); -} - -bool ImageLoaderByteArray3D::busy() const -{ - return isRunning() ? working : false; -} - -void ImageLoaderByteArray3D::generate(int index, const QByteArray &raw) -{ - mutex.lock(); - this->idx = index; - this->rawData = raw; - this->size = size; - this->img = QImage(); - mutex.unlock(); - - if (!isRunning()) - start(); - else { - restart = true; - condition.wakeOne(); - } -} - -void ImageLoaderByteArray3D::run() -{ - for (;;) { - mutex.lock(); - this->working = true; - QByteArray raw = this->rawData; - mutex.unlock(); - - QImage image = loadImage(raw); - - mutex.lock(); - this->working = false; - this->img = image; - mutex.unlock(); - - mutex.lock(); - if (!this->restart) - condition.wait(&mutex); - restart = false; - mutex.unlock(); - } -} - -QImage ImageLoaderByteArray3D::result() -{ - return img; -} +// Implementations for derived flows and loaders live in their own files. diff --git a/common/rhi/yacreader_flow_rhi.h b/common/rhi/yacreader_flow_rhi.h index 6d778f5d..f22f173f 100644 --- a/common/rhi/yacreader_flow_rhi.h +++ b/common/rhi/yacreader_flow_rhi.h @@ -81,6 +81,8 @@ extern struct Preset pressetYACReaderFlowDownConfig; class ImageLoader3D; class ImageLoaderByteArray3D; +class YACReaderComicFlow3D; +class YACReaderPageFlow3D; class YACReaderFlow3D : public QRhiWidget, public ScrollManagement { @@ -312,105 +314,10 @@ signals: void selected(unsigned int); }; -class YACReaderComicFlow3D : public YACReaderFlow3D -{ -public: - YACReaderComicFlow3D(QWidget *parent = nullptr, struct Preset p = defaultYACReaderFlowConfig); - void setImagePaths(QStringList paths); - void updateImageData() override; - void remove(int item) override; - void add(const QString &path, int index); - void resortCovers(QList newOrder); - friend class ImageLoader3D; - -private: - ImageLoader3D *worker; - -protected: - QList paths; -}; - -class YACReaderPageFlow3D : public YACReaderFlow3D -{ -public: - YACReaderPageFlow3D(QWidget *parent = nullptr, struct Preset p = defaultYACReaderFlowConfig); - ~YACReaderPageFlow3D(); - void updateImageData() override; - void populate(int n); - QVector imagesReady; - QVector rawImages; - QVector imagesSetted; - friend class ImageLoaderByteArray3D; - -private: - ImageLoaderByteArray3D *worker; -}; - -class ImageLoader3D : public QThread -{ -public: - ImageLoader3D(YACReaderFlow3D *flow); - ~ImageLoader3D(); - bool busy() const; - void generate(int index, const QString &fileName); - void reset() - { - idx = -1; - fileName = ""; - } - int index() const { return idx; } - void lock(); - void unlock(); - QImage result(); - YACReaderFlow3D *flow; - QImage loadImage(const QString &fileName); - -protected: - void run() override; - -private: - QMutex mutex; - QWaitCondition condition; - - bool restart; - bool working; - int idx; - QString fileName; - QSize size; - QImage img; -}; - -class ImageLoaderByteArray3D : public QThread -{ -public: - ImageLoaderByteArray3D(YACReaderFlow3D *flow); - ~ImageLoaderByteArray3D(); - bool busy() const; - void generate(int index, const QByteArray &raw); - void reset() - { - idx = -1; - rawData.clear(); - } - int index() const { return idx; } - QImage result(); - YACReaderFlow3D *flow; - QImage loadImage(const QByteArray &rawData); - -protected: - void run() override; - -private: - QMutex mutex; - QWaitCondition condition; - - bool restart; - bool working; - int idx; - QByteArray rawData; - QSize size; - QImage img; -}; +/* Derived flow & loader classes moved to dedicated files: + - common/rhi/yacreader_comic_flow_rhi.h/.cpp + - common/rhi/yacreader_page_flow_rhi.h/.cpp +*/ #endif // QT_VERSION >= QT_VERSION_CHECK(6, 7, 0) diff --git a/common/rhi/yacreader_page_flow_rhi.cpp b/common/rhi/yacreader_page_flow_rhi.cpp new file mode 100644 index 00000000..87da6e6a --- /dev/null +++ b/common/rhi/yacreader_page_flow_rhi.cpp @@ -0,0 +1,204 @@ +#include "yacreader_page_flow_rhi.h" +#include + +// YACReaderPageFlow3D implementation +YACReaderPageFlow3D::YACReaderPageFlow3D(QWidget *parent, struct Preset p) + : YACReaderFlow3D(parent, p) +{ + worker = new ImageLoaderByteArray3D(this); + worker->flow = this; +} + +YACReaderPageFlow3D::~YACReaderPageFlow3D() +{ + if (timerId != -1) { + this->killTimer(timerId); + timerId = -1; + } + rawImages.clear(); + + // Clean up textures and clear images to prevent double-delete in base destructor + for (auto &image : images) { + if (image.texture != scene.defaultTexture.get()) { + delete image.texture; + } + } + images.clear(); + numObjects = 0; +} + +void YACReaderPageFlow3D::updateImageData() +{ + if (worker->busy()) + return; + + int idx = worker->index(); + if (idx >= 0 && !worker->result().isNull()) { + if (!loaded[idx]) { + float x = 1; + QImage img = worker->result(); + + // Create QRhiTexture from the loaded image and queue the pixel upload + if (m_rhi) { + QRhiTexture *texture = m_rhi->newTexture(QRhiTexture::BGRA8, img.size(), 1, + (performance == high || performance == ultraHigh) ? QRhiTexture::MipMapped : QRhiTexture::UsedAsTransferSource); + + if (texture->create()) { + // Queue the image upload so it happens together with other resource updates + PendingTextureUpload upload; + upload.index = idx; + upload.image = img; + upload.x = x; + upload.y = 1 * (float(img.height()) / img.width()); + pendingTextureUploads.append(upload); + + QString s = "cover"; + replace(s.toLocal8Bit().data(), texture, upload.x, upload.y, idx); + } + } + } + } + + int count = 8; + switch (performance) { + case low: + count = 8; + break; + case medium: + count = 10; + break; + case high: + count = 12; + break; + case ultraHigh: + count = 14; + break; + } + + int *indexes = new int[2 * count + 1]; + int center = currentSelected; + indexes[0] = center; + for (int j = 0; j < count; j++) { + indexes[j * 2 + 1] = center + j + 1; + indexes[j * 2 + 2] = center - j - 1; + } + + for (int c = 0; c < 2 * count + 1; c++) { + int i = indexes[c]; + if ((i >= 0) && (i < numObjects)) + if (rawImages.size() > 0) + if (!loaded[i] && imagesReady[i]) { + worker->generate(i, rawImages.at(i)); + delete[] indexes; + return; + } + } + + delete[] indexes; +} + +void YACReaderPageFlow3D::populate(int n) +{ + worker->reset(); + reset(); + numObjects = 0; + + if (hasBeenInitialized) { + YACReaderFlow3D::populate(n); + } else { + lazyPopulateObjects = n; + } + + imagesReady = QVector(n, false); + rawImages = QVector(n); + imagesSetted = QVector(n, false); +} + +// ImageLoaderByteArray3D implementation +QImage ImageLoaderByteArray3D::loadImage(const QByteArray &raw) +{ + QImage image; + + if (!image.loadFromData(raw)) { + return QImage(); + } + + switch (flow->performance) { + case low: + image = image.scaledToWidth(128, Qt::SmoothTransformation); + break; + case medium: + image = image.scaledToWidth(196, Qt::SmoothTransformation); + break; + case high: + image = image.scaledToWidth(256, Qt::SmoothTransformation); + break; + case ultraHigh: + image = image.scaledToWidth(320, Qt::SmoothTransformation); + break; + } + + return image; +} + +ImageLoaderByteArray3D::ImageLoaderByteArray3D(YACReaderFlow3D *flow) + : QThread(), flow(flow), restart(false), working(false), idx(-1) +{ +} + +ImageLoaderByteArray3D::~ImageLoaderByteArray3D() +{ + mutex.lock(); + condition.wakeOne(); + mutex.unlock(); + wait(); +} + +bool ImageLoaderByteArray3D::busy() const +{ + return isRunning() ? working : false; +} + +void ImageLoaderByteArray3D::generate(int index, const QByteArray &raw) +{ + mutex.lock(); + this->idx = index; + this->rawData = raw; + this->img = QImage(); + mutex.unlock(); + + if (!isRunning()) + start(); + else { + restart = true; + condition.wakeOne(); + } +} + +void ImageLoaderByteArray3D::run() +{ + for (;;) { + mutex.lock(); + this->working = true; + QByteArray raw = this->rawData; + mutex.unlock(); + + QImage image = loadImage(raw); + + mutex.lock(); + this->working = false; + this->img = image; + mutex.unlock(); + + mutex.lock(); + if (!this->restart) + condition.wait(&mutex); + restart = false; + mutex.unlock(); + } +} + +QImage ImageLoaderByteArray3D::result() +{ + return img; +} diff --git a/common/rhi/yacreader_page_flow_rhi.h b/common/rhi/yacreader_page_flow_rhi.h new file mode 100644 index 00000000..b188b6ff --- /dev/null +++ b/common/rhi/yacreader_page_flow_rhi.h @@ -0,0 +1,55 @@ +#ifndef __YACREADER_PAGE_FLOW_RHI_H +#define __YACREADER_PAGE_FLOW_RHI_H + +#include "yacreader_flow_rhi.h" + +class ImageLoaderByteArray3D; + +class YACReaderPageFlow3D : public YACReaderFlow3D +{ +public: + YACReaderPageFlow3D(QWidget *parent = nullptr, struct Preset p = defaultYACReaderFlowConfig); + ~YACReaderPageFlow3D(); + void updateImageData() override; + void populate(int n); + QVector imagesReady; + QVector rawImages; + QVector imagesSetted; + friend class ImageLoaderByteArray3D; + +private: + ImageLoaderByteArray3D *worker; +}; + +class ImageLoaderByteArray3D : public QThread +{ +public: + ImageLoaderByteArray3D(YACReaderFlow3D *flow); + ~ImageLoaderByteArray3D(); + bool busy() const; + void generate(int index, const QByteArray &raw); + void reset() + { + idx = -1; + rawData.clear(); + } + int index() const { return idx; } + QImage result(); + YACReaderFlow3D *flow; + QImage loadImage(const QByteArray &rawData); + +protected: + void run() override; + +private: + QMutex mutex; + QWaitCondition condition; + + bool restart; + bool working; + int idx; + QByteArray rawData; + QImage img; +}; + +#endif // __YACREADER_PAGE_FLOW_RHI_H