From 3cd2dd51043397a5024248d9767c7d7bae6ccdb1 Mon Sep 17 00:00:00 2001 From: Jakub Melka Date: Sun, 15 Dec 2019 17:46:58 +0100 Subject: [PATCH] Page prefetching, bugfixing --- PdfForQtLib/sources/pdfcompiler.cpp | 5 +-- PdfForQtLib/sources/pdfcompiler.h | 2 +- .../sources/pdfdrawspacecontroller.cpp | 39 ++++++++++++++++++- PdfForQtLib/sources/pdfdrawspacecontroller.h | 4 ++ PdfForQtLib/sources/pdfrenderer.cpp | 14 ++++--- PdfForQtViewer/pdfviewermainwindow.cpp | 6 +++ PdfForQtViewer/pdfviewersettings.cpp | 2 + PdfForQtViewer/pdfviewersettings.h | 3 ++ PdfForQtViewer/pdfviewersettingsdialog.cpp | 5 +++ PdfForQtViewer/pdfviewersettingsdialog.ui | 38 ++++++++++++------ 10 files changed, 94 insertions(+), 24 deletions(-) diff --git a/PdfForQtLib/sources/pdfcompiler.cpp b/PdfForQtLib/sources/pdfcompiler.cpp index 4161941..439640b 100644 --- a/PdfForQtLib/sources/pdfcompiler.cpp +++ b/PdfForQtLib/sources/pdfcompiler.cpp @@ -92,7 +92,7 @@ void PDFAsynchronousPageCompiler::reset() start(); } -const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getPrecompiledCache(PDFInteger pageIndex, bool compile) +const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getCompiledPage(PDFInteger pageIndex, bool compile) { if (m_state != State::Active || !m_proxy->getDocument()) { @@ -115,9 +115,8 @@ const PDFPrecompiledPage* PDFAsynchronousPageCompiler::getPrecompiledCache(PDFIn CompileTask& task = m_tasks[pageIndex]; task.taskFuture = QtConcurrent::run(compilePage); task.taskWatcher = new QFutureWatcher(this); - task.taskWatcher->setFuture(task.taskFuture); connect(task.taskWatcher, &QFutureWatcher::finished, this, &PDFAsynchronousPageCompiler::onPageCompiled); - onPageCompiled(); + task.taskWatcher->setFuture(task.taskFuture); } return page; diff --git a/PdfForQtLib/sources/pdfcompiler.h b/PdfForQtLib/sources/pdfcompiler.h index ee999ed..0e3f97a 100644 --- a/PdfForQtLib/sources/pdfcompiler.h +++ b/PdfForQtLib/sources/pdfcompiler.h @@ -66,7 +66,7 @@ public: /// task is performed. /// \param pageIndex Index of page /// \param compile Compile the page, if it is not found in the cache - const PDFPrecompiledPage* getPrecompiledCache(PDFInteger pageIndex, bool compile); + const PDFPrecompiledPage* getCompiledPage(PDFInteger pageIndex, bool compile); signals: void pageImageChanged(bool all, const std::vector& pages); diff --git a/PdfForQtLib/sources/pdfdrawspacecontroller.cpp b/PdfForQtLib/sources/pdfdrawspacecontroller.cpp index f1e539b..bf2e5e0 100644 --- a/PdfForQtLib/sources/pdfdrawspacecontroller.cpp +++ b/PdfForQtLib/sources/pdfdrawspacecontroller.cpp @@ -606,7 +606,7 @@ void PDFDrawWidgetProxy::draw(QPainter* painter, QRect rect) // Clear the page space by white color painter->fillRect(placedRect, Qt::white); - const PDFPrecompiledPage* compiledPage = m_compiler->getPrecompiledCache(item.pageIndex, true); + const PDFPrecompiledPage* compiledPage = m_compiler->getCompiledPage(item.pageIndex, true); if (compiledPage && compiledPage->isValid()) { QElapsedTimer timer; @@ -676,7 +676,7 @@ QImage PDFDrawWidgetProxy::drawThumbnailImage(PDFInteger pageIndex, int pixelSiz if (imageSize.isValid()) { - const PDFPrecompiledPage* compiledPage = m_compiler->getPrecompiledCache(pageIndex, true); + const PDFPrecompiledPage* compiledPage = m_compiler->getCompiledPage(pageIndex, true); if (compiledPage && compiledPage->isValid()) { // Rasterize the image. @@ -957,6 +957,41 @@ void PDFDrawWidgetProxy::updateRenderer(bool useOpenGL, const QSurfaceFormat& su m_rasterizer->reset(useOpenGL, surfaceFormat); } +void PDFDrawWidgetProxy::prefetchPages(PDFInteger pageIndex) +{ + // Determine number of pages, which should be prefetched. In case of two or more pages, + // we need to prefetch more pages (for example, two for two columns/two pages display mode). + int prefetchCount = 0; + switch (m_controller->getPageLayout()) + { + case PageLayout::OneColumn: + case PageLayout::SinglePage: + prefetchCount = 1; + break; + + case PageLayout::TwoPagesLeft: + case PageLayout::TwoPagesRight: + case PageLayout::TwoColumnLeft: + case PageLayout::TwoColumnRight: + prefetchCount = 2; + break; + + default: + Q_ASSERT(false); + break; + } + + if (const PDFDocument* document = getDocument()) + { + const PDFInteger pageCount = document->getCatalog()->getPageCount(); + const PDFInteger pageEnd = qMin(pageCount, pageIndex + prefetchCount + 1); + for (PDFInteger i = pageIndex + 1; i < pageEnd; ++i) + { + m_compiler->getCompiledPage(i, true); + } + } +} + void PDFDrawWidgetProxy::onHorizontalScrollbarValueChanged(int value) { if (!m_updateDisabled && !m_horizontalScrollbar->isHidden()) diff --git a/PdfForQtLib/sources/pdfdrawspacecontroller.h b/PdfForQtLib/sources/pdfdrawspacecontroller.h index 081119c..3f345dd 100644 --- a/PdfForQtLib/sources/pdfdrawspacecontroller.h +++ b/PdfForQtLib/sources/pdfdrawspacecontroller.h @@ -260,6 +260,10 @@ public: /// \param surfaceFormat Surface format for OpenGL rendering void updateRenderer(bool useOpenGL, const QSurfaceFormat& surfaceFormat); + /// Prefetches (prerenders) pages after page with pageIndex, i.e., prepares + /// for non-flickering scroll operation. + void prefetchPages(PDFInteger pageIndex); + static constexpr PDFReal ZOOM_STEP = 1.2; const PDFDocument* getDocument() const { return m_controller->getDocument(); } diff --git a/PdfForQtLib/sources/pdfrenderer.cpp b/PdfForQtLib/sources/pdfrenderer.cpp index b9a5039..f95888b 100644 --- a/PdfForQtLib/sources/pdfrenderer.cpp +++ b/PdfForQtLib/sources/pdfrenderer.cpp @@ -241,7 +241,7 @@ QImage PDFRasterizer::render(const PDFPage* page, const PDFPrecompiledPage* comp compiledPage->draw(&painter, page->getCropBox(), matrix, features); } - // Convert the image into format Format_ARGB32_Premultiplied for fast drawing. + // Jakub Melka: Convert the image into format Format_ARGB32_Premultiplied for fast drawing. // If this format is used, then no image conversion is performed while drawing. if (image.format() != QImage::Format_ARGB32_Premultiplied) { @@ -304,13 +304,15 @@ void PDFRasterizer::releaseOpenGL() if (m_surface) { Q_ASSERT(m_context); - Q_ASSERT(m_fbo); // Delete framebuffer - m_context->makeCurrent(m_surface); - delete m_fbo; - m_fbo = nullptr; - m_context->doneCurrent(); + if (m_fbo) + { + m_context->makeCurrent(m_surface); + delete m_fbo; + m_fbo = nullptr; + m_context->doneCurrent(); + } // Delete OpenGL context delete m_context; diff --git a/PdfForQtViewer/pdfviewermainwindow.cpp b/PdfForQtViewer/pdfviewermainwindow.cpp index 47a94d9..9bd94d3 100644 --- a/PdfForQtViewer/pdfviewermainwindow.cpp +++ b/PdfForQtViewer/pdfviewermainwindow.cpp @@ -633,6 +633,12 @@ void PDFViewerMainWindow::updateUI(bool fullUpdate) if (!currentPages.empty()) { m_pageNumberSpinBox->setValue(currentPages.front() + 1); + + // Prefetch pages, if it is enabled + if (m_settings->isPagePrefetchingEnabled()) + { + m_pdfWidget->getDrawWidgetProxy()->prefetchPages(currentPages.back()); + } } m_sidebarWidget->setCurrentPages(currentPages); diff --git a/PdfForQtViewer/pdfviewersettings.cpp b/PdfForQtViewer/pdfviewersettings.cpp index 11107e2..47d5104 100644 --- a/PdfForQtViewer/pdfviewersettings.cpp +++ b/PdfForQtViewer/pdfviewersettings.cpp @@ -36,6 +36,7 @@ void PDFViewerSettings::readSettings(QSettings& settings) m_settings.m_rendererEngine = static_cast(settings.value("renderingEngine", static_cast(pdf::RendererEngine::OpenGL)).toInt()); m_settings.m_multisampleAntialiasing = settings.value("msaa", defaultSettings.m_multisampleAntialiasing).toBool(); m_settings.m_rendererSamples = settings.value("rendererSamples", defaultSettings.m_rendererSamples).toInt(); + m_settings.m_prefetchPages = settings.value("prefetchPages", defaultSettings.m_prefetchPages).toBool(); m_settings.m_preferredMeshResolutionRatio = settings.value("preferredMeshResolutionRatio", defaultSettings.m_preferredMeshResolutionRatio).toDouble(); m_settings.m_minimalMeshResolutionRatio = settings.value("minimalMeshResolutionRatio", defaultSettings.m_minimalMeshResolutionRatio).toDouble(); m_settings.m_colorTolerance = settings.value("colorTolerance", defaultSettings.m_colorTolerance).toDouble(); @@ -54,6 +55,7 @@ void PDFViewerSettings::writeSettings(QSettings& settings) settings.setValue("renderingEngine", static_cast(m_settings.m_rendererEngine)); settings.setValue("msaa", m_settings.m_multisampleAntialiasing); settings.setValue("rendererSamples", m_settings.m_rendererSamples); + settings.setValue("prefetchPages", m_settings.m_prefetchPages); settings.setValue("preferredMeshResolutionRatio", m_settings.m_preferredMeshResolutionRatio); settings.setValue("minimalMeshResolutionRatio", m_settings.m_minimalMeshResolutionRatio); settings.setValue("colorTolerance", m_settings.m_colorTolerance); diff --git a/PdfForQtViewer/pdfviewersettings.h b/PdfForQtViewer/pdfviewersettings.h index b98c7b1..08149b3 100644 --- a/PdfForQtViewer/pdfviewersettings.h +++ b/PdfForQtViewer/pdfviewersettings.h @@ -43,6 +43,7 @@ public: m_rendererEngine(pdf::RendererEngine::OpenGL), m_multisampleAntialiasing(true), m_rendererSamples(16), + m_prefetchPages(true), m_preferredMeshResolutionRatio(0.02), m_minimalMeshResolutionRatio(0.005), m_colorTolerance(0.01), @@ -57,6 +58,7 @@ public: pdf::RendererEngine m_rendererEngine; bool m_multisampleAntialiasing; int m_rendererSamples; + bool m_prefetchPages; pdf::PDFReal m_preferredMeshResolutionRatio; pdf::PDFReal m_minimalMeshResolutionRatio; pdf::PDFReal m_colorTolerance; @@ -82,6 +84,7 @@ public: int getRendererSamples() const; void setRendererSamples(int rendererSamples); + bool isPagePrefetchingEnabled() const { return m_settings.m_prefetchPages; } bool isMultisampleAntialiasingEnabled() const { return m_settings.m_multisampleAntialiasing; } pdf::PDFReal getPreferredMeshResolutionRatio() const { return m_settings.m_preferredMeshResolutionRatio; } diff --git a/PdfForQtViewer/pdfviewersettingsdialog.cpp b/PdfForQtViewer/pdfviewersettingsdialog.cpp index 511d48b..34445e8 100644 --- a/PdfForQtViewer/pdfviewersettingsdialog.cpp +++ b/PdfForQtViewer/pdfviewersettingsdialog.cpp @@ -133,6 +133,7 @@ void PDFViewerSettingsDialog::loadData() ui->multisampleAntialiasingSamplesCountComboBox->setEnabled(false); ui->multisampleAntialiasingSamplesCountComboBox->setCurrentIndex(-1); } + ui->prefetchPagesCheckBox->setChecked(m_settings.m_prefetchPages); // Rendering ui->antialiasingCheckBox->setChecked(m_settings.m_features.testFlag(pdf::PDFRenderer::Antialiasing)); @@ -173,6 +174,10 @@ void PDFViewerSettingsDialog::saveData() { m_settings.m_rendererSamples = ui->multisampleAntialiasingSamplesCountComboBox->currentData().toInt(); } + else if (sender == ui->prefetchPagesCheckBox) + { + m_settings.m_prefetchPages = ui->prefetchPagesCheckBox->isChecked(); + } else if (sender == ui->antialiasingCheckBox) { m_settings.m_features.setFlag(pdf::PDFRenderer::Antialiasing, ui->antialiasingCheckBox->isChecked()); diff --git a/PdfForQtViewer/pdfviewersettingsdialog.ui b/PdfForQtViewer/pdfviewersettingsdialog.ui index 5f676b9..8164bbe 100644 --- a/PdfForQtViewer/pdfviewersettingsdialog.ui +++ b/PdfForQtViewer/pdfviewersettingsdialog.ui @@ -26,7 +26,7 @@ - 1 + 0 @@ -50,16 +50,6 @@ - - - - Rendering engine - - - - - - @@ -84,12 +74,36 @@ + + + + + + + Rendering engine + + + + + + + Prefetch pages + + + + + + + Enable + + + - <html><head/><body><p>Select rendering method according to your needs. <span style=" font-weight:600;">Software rendering</span> is much slower than hardware accelerated rendering using <span style=" font-weight:600;">OpenGL rendering</span>, but it works when OpenGL is not available at your platform. OpenGL rendering is selected as default and is recommended.</p><p>OpenGL rendering uses<span style=" font-weight:600;"> multisample antialiasing (MSAA)</span>, which provides good quality antialiasing. You can turn this feature on or off, but without antialiasing, bad quality image can occur. Samples count affect how much samples per pixel are considered to determine pixel color. It can be a value 1, 2, 4, 8, and 16. Most modern GPUs support at least value 8. Lower this value, if your GPU doesn't support the desired sample count.</p></body></html> + <html><head/><body><p>Select rendering method according to your needs. <span style=" font-weight:600;">Software rendering</span> is much slower than hardware accelerated rendering using <span style=" font-weight:600;">OpenGL rendering</span>, but it works when OpenGL is not available at your platform. OpenGL rendering is selected as default and is recommended.</p><p>OpenGL rendering uses<span style=" font-weight:600;"> multisample antialiasing (MSAA)</span>, which provides good quality antialiasing. You can turn this feature on or off, but without antialiasing, bad quality image can occur. Samples count affect how much samples per pixel are considered to determine pixel color. It can be a value 1, 2, 4, 8, and 16. Most modern GPUs support at least value 8. Lower this value, if your GPU doesn't support the desired sample count.</p><p><span style=" font-weight:600;">Prefetch pages </span>prefetches (pre-renders) pages next to currently viewed pages, to avoid flickering during scrolling. Prefetched pages are stored in the page cache.</p></body></html> true