mirror of
https://github.com/YACReader/yacreader
synced 2026-03-01 10:22:58 -05:00
Implement a small cache to avoid scaling continuously while painting
This commit is contained in:
committed by
Luis Ángel San Martín
parent
0bd291ba98
commit
edbbc3c577
@ -42,6 +42,7 @@ void ContinuousPageWidget::setViewModel(ContinuousViewModel *viewModel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
|
scaledPageCache.invalidateAll();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ void ContinuousPageWidget::reset()
|
|||||||
{
|
{
|
||||||
setMinimumHeight(0);
|
setMinimumHeight(0);
|
||||||
setMaximumHeight(QWIDGETSIZE_MAX);
|
setMaximumHeight(QWIDGETSIZE_MAX);
|
||||||
|
scaledPageCache.invalidateAll();
|
||||||
updateGeometry();
|
updateGeometry();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
@ -86,6 +88,8 @@ void ContinuousPageWidget::onPageAvailable(int absolutePageIndex)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scaledPageCache.invalidatePage(absolutePageIndex);
|
||||||
|
|
||||||
// repaint the region where this page lives
|
// repaint the region where this page lives
|
||||||
if (absolutePageIndex < continuousViewModel->numPages()) {
|
if (absolutePageIndex < continuousViewModel->numPages()) {
|
||||||
QSize scaled = continuousViewModel->scaledPageSize(absolutePageIndex);
|
QSize scaled = continuousViewModel->scaledPageSize(absolutePageIndex);
|
||||||
@ -106,10 +110,17 @@ void ContinuousPageWidget::paintEvent(QPaintEvent *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
QPainter painter(this);
|
QPainter painter(this);
|
||||||
|
scaledPageCache.invalidateForWidth(width());
|
||||||
|
|
||||||
QRect visibleRect = event->rect();
|
QRect visibleRect = event->rect();
|
||||||
int firstPage = continuousViewModel->pageAtY(visibleRect.top());
|
int firstPage = continuousViewModel->pageAtY(visibleRect.top());
|
||||||
int lastPage = continuousViewModel->pageAtY(visibleRect.bottom());
|
int lastPage = continuousViewModel->pageAtY(visibleRect.bottom());
|
||||||
|
firstPage = qBound(0, firstPage, continuousViewModel->numPages() - 1);
|
||||||
|
lastPage = qBound(0, lastPage, continuousViewModel->numPages() - 1);
|
||||||
|
|
||||||
|
const int cacheMin = std::max(0, firstPage - 1);
|
||||||
|
const int cacheMax = std::min(continuousViewModel->numPages() - 1, lastPage + 1);
|
||||||
|
scaledPageCache.keepOnlyRange(cacheMin, cacheMax);
|
||||||
|
|
||||||
int w = width();
|
int w = width();
|
||||||
for (int i = firstPage; i <= lastPage && i < continuousViewModel->numPages(); ++i) {
|
for (int i = firstPage; i <= lastPage && i < continuousViewModel->numPages(); ++i) {
|
||||||
@ -124,10 +135,9 @@ void ContinuousPageWidget::paintEvent(QPaintEvent *event)
|
|||||||
|
|
||||||
const QImage *img = render->bufferedImage(i);
|
const QImage *img = render->bufferedImage(i);
|
||||||
if (img && !img->isNull()) {
|
if (img && !img->isNull()) {
|
||||||
if (img->size() != scaled) {
|
const QImage *drawable = scaledImageForPaint(i, img, scaled, width());
|
||||||
painter.drawImage(pageRect, img->scaled(scaled, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
|
if (drawable) {
|
||||||
} else {
|
painter.drawImage(pageRect, *drawable);
|
||||||
painter.drawImage(pageRect, *img);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// placeholder
|
// placeholder
|
||||||
@ -145,3 +155,39 @@ void ContinuousPageWidget::resizeEvent(QResizeEvent *event)
|
|||||||
continuousViewModel->setViewportSize(width(), continuousViewModel->viewportHeight());
|
continuousViewModel->setViewportSize(width(), continuousViewModel->viewportHeight());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QImage *ContinuousPageWidget::scaledImageForPaint(int pageIndex, const QImage *source, const QSize &targetSize, int effectiveWidth)
|
||||||
|
{
|
||||||
|
if (!source || source->isNull() || targetSize.isEmpty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source->size() == targetSize) {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
scaledPageCache.invalidateForWidth(effectiveWidth);
|
||||||
|
|
||||||
|
auto it = scaledPageCache.pages.find(pageIndex);
|
||||||
|
const qint64 sourceKey = source->cacheKey();
|
||||||
|
|
||||||
|
if (it != scaledPageCache.pages.end()) {
|
||||||
|
const ScaledPageCacheEntry &entry = it.value();
|
||||||
|
const bool validEntry = entry.sourceCacheKey == sourceKey
|
||||||
|
&& entry.sourceSize == source->size()
|
||||||
|
&& entry.targetSize == targetSize
|
||||||
|
&& !entry.scaledImage.isNull();
|
||||||
|
if (validEntry) {
|
||||||
|
return &it.value().scaledImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScaledPageCacheEntry entry;
|
||||||
|
entry.sourceCacheKey = sourceKey;
|
||||||
|
entry.sourceSize = source->size();
|
||||||
|
entry.targetSize = targetSize;
|
||||||
|
entry.scaledImage = source->scaled(targetSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
|
||||||
|
scaledPageCache.pages.insert(pageIndex, std::move(entry));
|
||||||
|
|
||||||
|
return &scaledPageCache.pages[pageIndex].scaledImage;
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@
|
|||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <QSize>
|
#include <QSize>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
|
|
||||||
#include "themable.h"
|
#include "themable.h"
|
||||||
|
|
||||||
@ -33,8 +35,61 @@ protected:
|
|||||||
void applyTheme(const Theme &theme) override;
|
void applyTheme(const Theme &theme) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct ScaledPageCacheEntry {
|
||||||
|
qint64 sourceCacheKey = 0;
|
||||||
|
QSize sourceSize;
|
||||||
|
QSize targetSize;
|
||||||
|
QImage scaledImage;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScaledPageCache {
|
||||||
|
int effectiveWidth = -1;
|
||||||
|
QHash<int, ScaledPageCacheEntry> pages;
|
||||||
|
|
||||||
|
void invalidateAll()
|
||||||
|
{
|
||||||
|
effectiveWidth = -1;
|
||||||
|
pages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void invalidateForWidth(int width)
|
||||||
|
{
|
||||||
|
if (effectiveWidth != width) {
|
||||||
|
effectiveWidth = width;
|
||||||
|
pages.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void invalidatePage(int pageIndex)
|
||||||
|
{
|
||||||
|
pages.remove(pageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void keepOnlyRange(int minPageIndex, int maxPageIndex)
|
||||||
|
{
|
||||||
|
if (pages.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<int> keysToRemove;
|
||||||
|
keysToRemove.reserve(pages.size());
|
||||||
|
for (auto it = pages.constBegin(); it != pages.constEnd(); ++it) {
|
||||||
|
if (it.key() < minPageIndex || it.key() > maxPageIndex) {
|
||||||
|
keysToRemove.append(it.key());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int key : keysToRemove) {
|
||||||
|
pages.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const QImage *scaledImageForPaint(int pageIndex, const QImage *source, const QSize &targetSize, int effectiveWidth);
|
||||||
|
|
||||||
Render *render = nullptr;
|
Render *render = nullptr;
|
||||||
ContinuousViewModel *continuousViewModel = nullptr;
|
ContinuousViewModel *continuousViewModel = nullptr;
|
||||||
|
ScaledPageCache scaledPageCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // CONTINUOUS_PAGE_WIDGET_H
|
#endif // CONTINUOUS_PAGE_WIDGET_H
|
||||||
|
|||||||
Reference in New Issue
Block a user