summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@theqtcompany.com>2014-12-15 21:13:10 +0100
committerShawn Rutledge <shawn.rutledge@theqtcompany.com>2014-12-15 21:48:07 +0100
commite9ca7cf67c0c1b45aa2e2b4faa3c8ab2cac415e7 (patch)
tree27bc0c03a94c5ea5a8d76921b97b52e50b6198db
parenta74902cff874a6be9887cda54a9af3b1b2c44624 (diff)
pdfviewer example: add threaded PageCache
thread fixes
-rw-r--r--examples/widgets/pdfviewer/images/busy.pngbin0 -> 172 bytes
-rw-r--r--examples/widgets/pdfviewer/pagecache.cpp106
-rw-r--r--examples/widgets/pdfviewer/pagecache.h46
-rw-r--r--examples/widgets/pdfviewer/pdfviewer.pro6
-rw-r--r--examples/widgets/pdfviewer/resources.qrc1
-rw-r--r--examples/widgets/pdfviewer/sequentialpagewidget.cpp39
-rw-r--r--examples/widgets/pdfviewer/sequentialpagewidget.h9
7 files changed, 172 insertions, 35 deletions
diff --git a/examples/widgets/pdfviewer/images/busy.png b/examples/widgets/pdfviewer/images/busy.png
new file mode 100644
index 0000000..69056c4
--- /dev/null
+++ b/examples/widgets/pdfviewer/images/busy.png
Binary files differ
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