diff options
author | Shawn Rutledge <shawn.rutledge@theqtcompany.com> | 2014-12-15 21:13:10 +0100 |
---|---|---|
committer | Shawn Rutledge <shawn.rutledge@theqtcompany.com> | 2014-12-15 21:48:07 +0100 |
commit | e9ca7cf67c0c1b45aa2e2b4faa3c8ab2cac415e7 (patch) | |
tree | 27bc0c03a94c5ea5a8d76921b97b52e50b6198db | |
parent | a74902cff874a6be9887cda54a9af3b1b2c44624 (diff) |
pdfviewer example: add threaded PageCache
thread fixes
-rw-r--r-- | examples/widgets/pdfviewer/images/busy.png | bin | 0 -> 172 bytes | |||
-rw-r--r-- | examples/widgets/pdfviewer/pagecache.cpp | 106 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/pagecache.h | 46 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/pdfviewer.pro | 6 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/resources.qrc | 1 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/sequentialpagewidget.cpp | 39 | ||||
-rw-r--r-- | examples/widgets/pdfviewer/sequentialpagewidget.h | 9 |
7 files changed, 172 insertions, 35 deletions
diff --git a/examples/widgets/pdfviewer/images/busy.png b/examples/widgets/pdfviewer/images/busy.png Binary files differnew file mode 100644 index 0000000..69056c4 --- /dev/null +++ b/examples/widgets/pdfviewer/images/busy.png diff --git a/examples/widgets/pdfviewer/pagecache.cpp b/examples/widgets/pdfviewer/pagecache.cpp new file mode 100644 index 0000000..5943b90 --- /dev/null +++ b/examples/widgets/pdfviewer/pagecache.cpp @@ -0,0 +1,106 @@ +#include "pagecache.h" +#include <QPainter> +#include <QPdfDocument> +#include <QLoggingCategory> +#include <QElapsedTimer> +#include <QThread> + +Q_DECLARE_LOGGING_CATEGORY(lcExample) + +PageCache::PageCache(QPdfDocument *doc, qreal zoom) + : QObject(Q_NULLPTR) + , m_thread(new QThread(this)) + , m_doc(doc) + , m_zoom(zoom) + , m_placeholderIcon(":icons/images/busy.png") + , m_placeholderBackground(Qt::white) + , m_minRenderTime(1000000000.) + , m_maxRenderTime(0.) + , m_totalRenderTime(0.) + , m_totalPagesRendered(0) +{ + moveToThread(m_thread); + connect(this, &PageCache::pagesNeeded, this, &PageCache::run, Qt::QueuedConnection); +} + +PageCache::~PageCache() +{ +} + +/*! + Get the result from the cache, + or a placeholder pixmap if unavailable. + */ +QPixmap PageCache::get(int page) +{ + m_lastPageRequested = page; + if (m_pageCache.contains(page)) + return m_pageCache[page]; + if (!m_thread->isRunning()) + m_thread->start(QThread::LowestPriority); + emit pagesNeeded(); + QSizeF sizeF = m_doc->pageSize(page); + if (!sizeF.isValid()) + return QPixmap(); + QSize size = QSizeF(sizeF * m_zoom).toSize(); + QPixmap placeholder(size); + { + QPainter painter(&placeholder); + painter.fillRect(QRect(QPoint(), size), m_placeholderBackground); + painter.drawPixmap((size.width() - m_placeholderIcon.width()) / 2, + (size.height() - m_placeholderIcon.height()) / 2, m_placeholderIcon); + } + return placeholder; +} + +void PageCache::run() +{ + int lastPageRequested = m_lastPageRequested; + int forward = lastPageRequested; + int backward = lastPageRequested - 1; + while (true) { + bool done = true; + if (lastPageRequested != m_lastPageRequested) { + lastPageRequested = m_lastPageRequested; + forward = lastPageRequested; + backward = lastPageRequested - 1; + } + if (forward < m_doc->pageCount()) { + if (!m_pageCache.contains(forward)) + insertPage(forward); + done = false; + } + if (backward >= 0) { + if (!m_pageCache.contains(backward)) + insertPage(backward); + done = false; + } + ++forward; + --backward; + if (done) + return; + } +} + +void PageCache::insertPage(int page) +{ + if (!m_pageCache.contains(page)) { + QSizeF size = m_doc->pageSize(page) * m_zoom; + QElapsedTimer timer; timer.start(); + const QImage &img = m_doc->render(page, size); + qreal secs = timer.nsecsElapsed() / 1000000000.0; + if (secs < m_minRenderTime) + m_minRenderTime = secs; + if (secs > m_maxRenderTime) + m_maxRenderTime = secs; + m_totalRenderTime += secs; + ++m_totalPagesRendered; + m_pageCache.insert(page, QPixmap::fromImage(img)); + emit pageReady(page); + + qCDebug(lcExample) << "page" << page << "zoom" << m_zoom << "size" << size << "in" << secs << + "secs; min" << m_minRenderTime << + "avg" << m_totalRenderTime / m_totalPagesRendered << + "max" << m_maxRenderTime; + } +} diff --git a/examples/widgets/pdfviewer/pagecache.h b/examples/widgets/pdfviewer/pagecache.h new file mode 100644 index 0000000..49d8bf8 --- /dev/null +++ b/examples/widgets/pdfviewer/pagecache.h @@ -0,0 +1,46 @@ +#ifndef PAGECACHE_H +#define PAGECACHE_H + +#include <QBrush> +#include <QHash> +#include <QPixmap> +#include <QRunnable> + +class QPdfDocument; + +class PageCache : public QObject +{ + Q_OBJECT +public: + PageCache(QPdfDocument *doc, qreal zoom); + ~PageCache(); + + QPixmap get(int page); + +public slots: + void run(); + +signals: + void pagesNeeded(); + void pageReady(int page); + +private: + void insertPage(int page); + +private: + QThread *m_thread; + QPdfDocument *m_doc; + QHash<int, QPixmap> m_pageCache; + qreal m_zoom; + int m_lastPageRequested; + QPixmap m_placeholderIcon; + QBrush m_placeholderBackground; + + // performance statistics + qreal m_minRenderTime; + qreal m_maxRenderTime; + qreal m_totalRenderTime; + int m_totalPagesRendered; +}; + +#endif // PAGECACHE_H diff --git a/examples/widgets/pdfviewer/pdfviewer.pro b/examples/widgets/pdfviewer/pdfviewer.pro index babcd85..4056915 100644 --- a/examples/widgets/pdfviewer/pdfviewer.pro +++ b/examples/widgets/pdfviewer/pdfviewer.pro @@ -4,10 +4,12 @@ TEMPLATE = app SOURCES += main.cpp\ mainwindow.cpp \ - sequentialpagewidget.cpp + sequentialpagewidget.cpp \ + pagecache.cpp HEADERS += mainwindow.h \ - sequentialpagewidget.h + sequentialpagewidget.h \ + pagecache.h FORMS += mainwindow.ui diff --git a/examples/widgets/pdfviewer/resources.qrc b/examples/widgets/pdfviewer/resources.qrc index 945c595..02d9655 100644 --- a/examples/widgets/pdfviewer/resources.qrc +++ b/examples/widgets/pdfviewer/resources.qrc @@ -7,5 +7,6 @@ <file>images/zoom-in-32.png</file> <file>images/zoom-out-24.png</file> <file>images/zoom-out-32.png</file> + <file>images/busy.png</file> </qresource> </RCC> diff --git a/examples/widgets/pdfviewer/sequentialpagewidget.cpp b/examples/widgets/pdfviewer/sequentialpagewidget.cpp index 5bac32c..6b84015 100644 --- a/examples/widgets/pdfviewer/sequentialpagewidget.cpp +++ b/examples/widgets/pdfviewer/sequentialpagewidget.cpp @@ -1,4 +1,5 @@ #include "sequentialpagewidget.h" +#include "pagecache.h" #include <QPaintEvent> #include <QPainter> #include <QPdfDocument> @@ -12,15 +13,12 @@ Q_DECLARE_LOGGING_CATEGORY(lcExample) SequentialPageWidget::SequentialPageWidget(QWidget *parent) : QWidget(parent) , m_doc(Q_NULLPTR) + , m_pageCache(Q_NULLPTR) , m_background(Qt::darkGray) , m_pageSpacing(3) , m_topPageShowing(0) , m_zoom(1.) , m_screenResolution(QGuiApplication::primaryScreen()->logicalDotsPerInch() / 72.0) - , m_minRenderTime(1000000000.) - , m_maxRenderTime(0.) - , m_totalRenderTime(0.) - , m_totalPagesRendered(0) { } @@ -32,6 +30,10 @@ void SequentialPageWidget::setDocument(QPdfDocument *doc) { m_doc = doc; m_topPageShowing = 0; + if (m_pageCache) + delete m_pageCache; + m_pageCache = new PageCache(doc, m_screenResolution * m_zoom); + connect(m_pageCache, SIGNAL(pageReady(int)), this, SLOT(update())); invalidate(); } @@ -49,7 +51,11 @@ QSizeF SequentialPageWidget::pageSize(int page) void SequentialPageWidget::invalidate() { - m_pageCache.clear(); + if (m_pageCache) + delete m_pageCache; + if (m_doc) + m_pageCache = new PageCache(m_doc, m_screenResolution * m_zoom); + connect(m_pageCache, SIGNAL(pageReady(int)), this, SLOT(update())); QSizeF totalSize(0, m_pageSpacing); for (int page = 0; page < m_doc->pageCount(); ++page) { QSizeF size = pageSize(page); @@ -88,36 +94,17 @@ void SequentialPageWidget::paintEvent(QPaintEvent * event) } y += m_pageSpacing; m_topPageShowing = page; - int previousRendered = m_totalPagesRendered; + if (!m_pageCache) return; // Actually render pages while (y < event->rect().bottom() && page < m_doc->pageCount()) { - if (!m_pageCache.contains(page)) { - QSizeF size = pageSize(page); - qCDebug(lcExample) << "rendering page" << page << "to size" << size; - QElapsedTimer timer; timer.start(); - const QImage &img = m_doc->render(page, size); - qreal secs = timer.nsecsElapsed() / 1000000000.0; - if (secs < m_minRenderTime) - m_minRenderTime = secs; - if (secs > m_maxRenderTime) - m_maxRenderTime = secs; - m_totalRenderTime += secs; - ++m_totalPagesRendered; - m_pageCache.insert(page, QPixmap::fromImage(img)); - } - const QPixmap &pm = m_pageCache[page]; + const QPixmap &pm = m_pageCache->get(page); painter.drawPixmap((width() - pm.width()) / 2, y, pm); y += pm.height() + m_pageSpacing; ++page; } m_bottomPageShowing = page - 1; emit showingPageRange(m_topPageShowing, m_bottomPageShowing); - - if (m_totalPagesRendered != previousRendered) - qCDebug(lcExample) << "rendering time: min" << m_minRenderTime << - "avg" << m_totalRenderTime / m_totalPagesRendered << - "max" << m_maxRenderTime; } qreal SequentialPageWidget::yForPage(int endPage) diff --git a/examples/widgets/pdfviewer/sequentialpagewidget.h b/examples/widgets/pdfviewer/sequentialpagewidget.h index 3d91279..23dfe7f 100644 --- a/examples/widgets/pdfviewer/sequentialpagewidget.h +++ b/examples/widgets/pdfviewer/sequentialpagewidget.h @@ -4,6 +4,7 @@ #include <QWidget> class QPdfDocument; +class PageCache; class SequentialPageWidget : public QWidget { @@ -33,7 +34,7 @@ private: private: QPdfDocument *m_doc; - QHash<int, QPixmap> m_pageCache; + PageCache *m_pageCache; QBrush m_background; int m_pageSpacing; int m_topPageShowing; @@ -41,12 +42,6 @@ private: QSize m_totalSize; qreal m_zoom; qreal m_screenResolution; // pixels per point - - // performance statistics - qreal m_minRenderTime; - qreal m_maxRenderTime; - qreal m_totalRenderTime; - int m_totalPagesRendered; }; #endif // SEQUENTIALPAGEWIDGET_H |