From 9448d193c914f0d1398e8618f85790645fdefb66 Mon Sep 17 00:00:00 2001 From: John Papale Date: Fri, 16 Oct 2020 11:43:51 +0200 Subject: [PATCH 1/8] Optimized performance while zooming complex pdf. --- src/pdf/XPDFRenderer.cpp | 110 +++++++++++++++++++++++++++++++++++++-- src/pdf/XPDFRenderer.h | 33 +++++++++++- 2 files changed, 136 insertions(+), 7 deletions(-) diff --git a/src/pdf/XPDFRenderer.cpp b/src/pdf/XPDFRenderer.cpp index f7769f4b..b2a3fbe8 100644 --- a/src/pdf/XPDFRenderer.cpp +++ b/src/pdf/XPDFRenderer.cpp @@ -38,13 +38,22 @@ #include "core/memcheck.h" +#ifdef XPDFRENDERER_CACHE_ZOOM_WITH_LOSS +double const XPDFRenderer::sRatioZoomRendering[] = { 3.0 }; +#else //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS +double const XPDFRenderer::sRatioZoomRendering[] = { 2.5, 5, 10.0 }; +#endif //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS + QAtomicInt XPDFRenderer::sInstancesCount = 0; XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : mDocument(0) - , mpSplashBitmap(0) - , mSplash(0) { + for (int i = 0; i < NbrZoomCache; i++) + { + m_cache.push_back(TypeCacheData(sRatioZoomRendering[i])); + } + Q_UNUSED(importingFile); if (!globalParams) { @@ -67,9 +76,14 @@ XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) XPDFRenderer::~XPDFRenderer() { - if(mSplash){ - delete mSplash; - mSplash = NULL; + for(int i = 0; i < m_cache.size(); i++) + { + TypeCacheData &cacheData = m_cache[i]; + if(cacheData.splash != nullptr){ + cacheData.cachedImage = QImage(); // The 'cachedImage' uses a buffer from 'splash'. + delete cacheData.splash; + cacheData.splash = nullptr; + } } if (mDocument) @@ -175,6 +189,7 @@ int XPDFRenderer::pageRotation(int pageNumber) const return 0; } +#ifndef XPDFRENDERER_CACHE_ZOOM_IMAGE void XPDFRenderer::render(QPainter *p, int pageNumber, const QRectF &bounds) { if (isValid()) @@ -231,3 +246,88 @@ QImage* XPDFRenderer::createPDFImage(int pageNumber, qreal xscale, qreal yscale, } return new QImage(mpSplashBitmap->getDataPtr(), mpSplashBitmap->getWidth(), mpSplashBitmap->getHeight(), mpSplashBitmap->getWidth() * 3, QImage::Format_RGB888); } +#else + + +void XPDFRenderer::render(QPainter *p, int pageNumber, const QRectF &bounds) +{ + Q_UNUSED(bounds); + if (isValid()) + { + qreal xscale = p->worldTransform().m11(); + qreal yscale = p->worldTransform().m22(); + Q_ASSERT(qFuzzyCompare(xscale, yscale)); // Zoom equal in all axes expected. + Q_ASSERT(xscale > 0.0); // Potential Div0 later if this assert fail. + + int zoomIndex = 0; + bool foundIndex = false; + for (; zoomIndex < NbrZoomCache && !foundIndex;) + { + if (xscale <= m_cache[zoomIndex].ratio) { + foundIndex = true; + } else { + zoomIndex++; + } + } + + if (!foundIndex) // Use the previous one. + zoomIndex--; + + QImage const &pdfImage = createPDFImage(pageNumber, m_cache[zoomIndex]); + QTransform savedTransform = p->worldTransform(); + + double const ratioDifferenceBetweenWorldAndImage = 1.0/m_cache[zoomIndex].ratio; + // The 'pdfImage' is rendered with a quality equal or superior. We adjust the 'transform' to zoom it + // out the required ratio. + QTransform newTransform = savedTransform.scale(ratioDifferenceBetweenWorldAndImage, ratioDifferenceBetweenWorldAndImage); + p->setWorldTransform(newTransform); + p->drawImage(QPointF( mSliceX, mSliceY), pdfImage); + + p->setWorldTransform(savedTransform); + } +} + +QImage& XPDFRenderer::createPDFImage(int pageNumber, TypeCacheData &cacheData) +{ + if (isValid()) + { + SplashColor paperColor = {0xFF, 0xFF, 0xFF}; // white + bool const requireUpdateImage = (pageNumber != cacheData.cachedPageNumber) || (cacheData.splash == nullptr); + if (requireUpdateImage) + { + if(cacheData.splash != nullptr) + { + cacheData.cachedImage = QImage(); // The 'm_cachedImage' uses a buffer from 'mSplash'. + delete cacheData.splash; + } + cacheData.splash = new SplashOutputDev(splashModeRGB8, 1, false, paperColor); + cacheData.cachedPageNumber = pageNumber; + +#ifdef USE_XPDF + cacheData.splash->startDoc(mDocument->getXRef()); +#else + cacheData.splash->startDoc(mDocument); +#endif + int rotation = 0; // in degrees (get it from the worldTransform if we want to support rotation) + bool useMediaBox = false; + bool crop = true; + bool printing = false; + mSliceX = 0.; + mSliceY = 0.; + + mDocument->displayPage(cacheData.splash, pageNumber, this->dpiForRendering * cacheData.ratio, this->dpiForRendering * cacheData.ratio, + rotation, useMediaBox, crop, printing); + cacheData.splashBitmap = cacheData.splash->getBitmap(); + } + + // Note this uses the 'mSplash->getBitmap()->getDataPtr()' as data buffer. + cacheData.cachedImage = QImage(cacheData.splashBitmap->getDataPtr(), cacheData.splashBitmap->getWidth(), cacheData.splashBitmap->getHeight(), + cacheData.splashBitmap->getWidth() * 3 /* bytesPerLine, 24 bits for RGB888, = 3 bytes */, + QImage::Format_RGB888); + } else { + cacheData.cachedImage = QImage(); + } + + return cacheData.cachedImage; +} +#endif diff --git a/src/pdf/XPDFRenderer.h b/src/pdf/XPDFRenderer.h index c668515b..37387dc6 100644 --- a/src/pdf/XPDFRenderer.h +++ b/src/pdf/XPDFRenderer.h @@ -51,6 +51,9 @@ class PDFDoc; +#define XPDFRENDERER_CACHE_ZOOM_IMAGE +//#define XPDFRENDERER_CACHE_ZOOM_WITH_LOSS + class XPDFRenderer : public PDFRenderer { Q_OBJECT @@ -74,15 +77,41 @@ class XPDFRenderer : public PDFRenderer private: void init(); +#ifdef XPDFRENDERER_CACHE_ZOOM_IMAGE + + enum { +#ifndef XPDFRENDERER_CACHE_ZOOM_WITH_LOSS + NbrZoomCache = 3 +#else //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS + NbrZoomCache = 1 +#endif //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS + }; + + class TypeCacheData { + public: + TypeCacheData(double const a_ratio) : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(a_ratio) {}; + ~TypeCacheData() {}; + SplashBitmap* splashBitmap; + QImage cachedImage; + int cachedPageNumber; + SplashOutputDev* splash; + double const ratio; + }; + + QImage &createPDFImage(int pageNumber, TypeCacheData &cacheData); +#else QImage* createPDFImage(int pageNumber, qreal xscale = 0.5, qreal yscale = 0.5, const QRectF &bounds = QRectF()); +#endif PDFDoc *mDocument; static QAtomicInt sInstancesCount; + //! The image is rendered with a quality above normal, so we can use that same + //! image while zooming. + static const double sRatioZoomRendering[NbrZoomCache]; qreal mSliceX; qreal mSliceY; - SplashBitmap* mpSplashBitmap; - SplashOutputDev* mSplash; + QVector m_cache; }; #endif // XPDFRENDERER_H From c860e0b0b765f4ead74cd532fc3347836bb84cbc Mon Sep 17 00:00:00 2001 From: John Papale Date: Fri, 16 Oct 2020 12:07:14 +0200 Subject: [PATCH 2/8] Made work the (inactive) case !defined XPDFRENDERER_CACHE_ZOOM_IMAGE. --- src/pdf/XPDFRenderer.cpp | 13 +++++++++++-- src/pdf/XPDFRenderer.h | 9 ++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/pdf/XPDFRenderer.cpp b/src/pdf/XPDFRenderer.cpp index b2a3fbe8..e0ee4ac3 100644 --- a/src/pdf/XPDFRenderer.cpp +++ b/src/pdf/XPDFRenderer.cpp @@ -38,21 +38,28 @@ #include "core/memcheck.h" +#ifdef XPDFRENDERER_CACHE_ZOOM_IMAGE #ifdef XPDFRENDERER_CACHE_ZOOM_WITH_LOSS double const XPDFRenderer::sRatioZoomRendering[] = { 3.0 }; #else //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS double const XPDFRenderer::sRatioZoomRendering[] = { 2.5, 5, 10.0 }; #endif //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS +#endif //XPDFRENDERER_CACHE_ZOOM_IMAGE QAtomicInt XPDFRenderer::sInstancesCount = 0; -XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) - : mDocument(0) +XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : +#ifndef XPDFRENDERER_CACHE_ZOOM_IMAGE + mSplash(nullptr), +#endif //XPDFRENDERER_CACHE_ZOOM_IMAGE + mDocument(0) { +#ifdef XPDFRENDERER_CACHE_ZOOM_IMAGE for (int i = 0; i < NbrZoomCache; i++) { m_cache.push_back(TypeCacheData(sRatioZoomRendering[i])); } +#endif //XPDFRENDERER_CACHE_ZOOM_IMAGE Q_UNUSED(importingFile); if (!globalParams) @@ -76,6 +83,7 @@ XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) XPDFRenderer::~XPDFRenderer() { +#ifdef XPDFRENDERER_CACHE_ZOOM_IMAGE for(int i = 0; i < m_cache.size(); i++) { TypeCacheData &cacheData = m_cache[i]; @@ -85,6 +93,7 @@ XPDFRenderer::~XPDFRenderer() cacheData.splash = nullptr; } } +#endif //XPDFRENDERER_CACHE_ZOOM_IMAGE if (mDocument) { diff --git a/src/pdf/XPDFRenderer.h b/src/pdf/XPDFRenderer.h index 37387dc6..fef8b411 100644 --- a/src/pdf/XPDFRenderer.h +++ b/src/pdf/XPDFRenderer.h @@ -99,19 +99,22 @@ class XPDFRenderer : public PDFRenderer }; QImage &createPDFImage(int pageNumber, TypeCacheData &cacheData); + + static const double sRatioZoomRendering[NbrZoomCache]; + QVector m_cache; #else QImage* createPDFImage(int pageNumber, qreal xscale = 0.5, qreal yscale = 0.5, const QRectF &bounds = QRectF()); + + SplashBitmap* mpSplashBitmap; + SplashOutputDev* mSplash; #endif PDFDoc *mDocument; static QAtomicInt sInstancesCount; //! The image is rendered with a quality above normal, so we can use that same //! image while zooming. - static const double sRatioZoomRendering[NbrZoomCache]; qreal mSliceX; qreal mSliceY; - - QVector m_cache; }; #endif // XPDFRENDERER_H From 8ba6d1b7b21496793e035472bc961855fae740bb Mon Sep 17 00:00:00 2001 From: John Papale Date: Tue, 27 Oct 2020 19:05:40 +0100 Subject: [PATCH 3/8] Made zoom cache behavior configurable with var 'ZoomBehavior' (0 =no cache, 1 =x3 zoom cache, 2 = 2.5/5/10 cache). Cleanup. --- src/core/UBSettings.cpp | 1 + src/core/UBSettings.h | 1 + src/pdf/XPDFRenderer.cpp | 142 +++++++++++++++++++-------------------- src/pdf/XPDFRenderer.h | 37 ++++------ 4 files changed, 83 insertions(+), 98 deletions(-) diff --git a/src/core/UBSettings.cpp b/src/core/UBSettings.cpp index 80e14677..b83d5f88 100644 --- a/src/core/UBSettings.cpp +++ b/src/core/UBSettings.cpp @@ -408,6 +408,7 @@ void UBSettings::init() pdfMargin = new UBSetting(this, "PDF", "Margin", "20"); pdfPageFormat = new UBSetting(this, "PDF", "PageFormat", "A4"); pdfResolution = new UBSetting(this, "PDF", "Resolution", "300"); + pdfZoomBehavior = new UBSetting(this, "PDF", "ZoomBehavior", "1"); podcastFramesPerSecond = new UBSetting(this, "Podcast", "FramesPerSecond", 10); podcastVideoSize = new UBSetting(this, "Podcast", "VideoSize", "Medium"); diff --git a/src/core/UBSettings.h b/src/core/UBSettings.h index c79e4f95..5b287354 100644 --- a/src/core/UBSettings.h +++ b/src/core/UBSettings.h @@ -359,6 +359,7 @@ class UBSettings : public QObject UBSetting* pdfMargin; UBSetting* pdfPageFormat; UBSetting* pdfResolution; + UBSetting* pdfZoomBehavior; UBSetting* podcastFramesPerSecond; UBSetting* podcastVideoSize; diff --git a/src/pdf/XPDFRenderer.cpp b/src/pdf/XPDFRenderer.cpp index e0ee4ac3..410739fc 100644 --- a/src/pdf/XPDFRenderer.cpp +++ b/src/pdf/XPDFRenderer.cpp @@ -37,29 +37,27 @@ #endif #include "core/memcheck.h" +#include "core/UBSettings.h" -#ifdef XPDFRENDERER_CACHE_ZOOM_IMAGE -#ifdef XPDFRENDERER_CACHE_ZOOM_WITH_LOSS -double const XPDFRenderer::sRatioZoomRendering[] = { 3.0 }; -#else //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS -double const XPDFRenderer::sRatioZoomRendering[] = { 2.5, 5, 10.0 }; -#endif //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS -#endif //XPDFRENDERER_CACHE_ZOOM_IMAGE QAtomicInt XPDFRenderer::sInstancesCount = 0; XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : -#ifndef XPDFRENDERER_CACHE_ZOOM_IMAGE - mSplash(nullptr), -#endif //XPDFRENDERER_CACHE_ZOOM_IMAGE - mDocument(0) + mpSplashBitmapHistorical(nullptr), mSplashHistorical(nullptr), mDocument(nullptr) { -#ifdef XPDFRENDERER_CACHE_ZOOM_IMAGE - for (int i = 0; i < NbrZoomCache; i++) - { - m_cache.push_back(TypeCacheData(sRatioZoomRendering[i])); + switch (UBSettings::settings()->pdfZoomBehavior->get().toUInt()) { + case 0: // Render each time (historical initial implementation). + break; + case 1: // Render a single image, degradated quality when zoomed big. + default: + m_pdfZoomCache.push_back(3.0); + break; + case 2: // Render three images, optimal quality all the time. + m_pdfZoomCache.push_back(2.5); + m_pdfZoomCache.push_back(5.0); + m_pdfZoomCache.push_back(10.0); + break; } -#endif //XPDFRENDERER_CACHE_ZOOM_IMAGE Q_UNUSED(importingFile); if (!globalParams) @@ -83,17 +81,18 @@ XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : XPDFRenderer::~XPDFRenderer() { -#ifdef XPDFRENDERER_CACHE_ZOOM_IMAGE - for(int i = 0; i < m_cache.size(); i++) + for(int i = 0; i < m_pdfZoomCache.size(); i++) { - TypeCacheData &cacheData = m_cache[i]; + TypePdfZoomCacheData &cacheData = m_pdfZoomCache[i]; if(cacheData.splash != nullptr){ cacheData.cachedImage = QImage(); // The 'cachedImage' uses a buffer from 'splash'. delete cacheData.splash; cacheData.splash = nullptr; } } -#endif //XPDFRENDERER_CACHE_ZOOM_IMAGE + + if(mSplashHistorical) + delete mSplashHistorical; if (mDocument) { @@ -198,33 +197,17 @@ int XPDFRenderer::pageRotation(int pageNumber) const return 0; } -#ifndef XPDFRENDERER_CACHE_ZOOM_IMAGE -void XPDFRenderer::render(QPainter *p, int pageNumber, const QRectF &bounds) -{ - if (isValid()) - { - qreal xscale = p->worldTransform().m11(); - qreal yscale = p->worldTransform().m22(); - - QImage *pdfImage = createPDFImage(pageNumber, xscale, yscale, bounds); - QTransform savedTransform = p->worldTransform(); - p->resetTransform(); - p->drawImage(QPointF(savedTransform.dx() + mSliceX, savedTransform.dy() + mSliceY), *pdfImage); - p->setWorldTransform(savedTransform); - delete pdfImage; - } -} -QImage* XPDFRenderer::createPDFImage(int pageNumber, qreal xscale, qreal yscale, const QRectF &bounds) +QImage* XPDFRenderer::createPDFImageHistorical(int pageNumber, qreal xscale, qreal yscale, const QRectF &bounds) { if (isValid()) { SplashColor paperColor = {0xFF, 0xFF, 0xFF}; // white - if(mSplash) - delete mSplash; - mSplash = new SplashOutputDev(splashModeRGB8, 1, false, paperColor); + if(mSplashHistorical) + delete mSplashHistorical; + mSplashHistorical = new SplashOutputDev(splashModeRGB8, 1, false, paperColor); #ifdef USE_XPDF - mSplash->startDoc(mDocument->getXRef()); + mSplashHistorical->startDoc(mDocument->getXRef()); #else mSplash->startDoc(mDocument); #endif @@ -237,7 +220,7 @@ QImage* XPDFRenderer::createPDFImage(int pageNumber, qreal xscale, qreal yscale, if (bounds.isNull()) { - mDocument->displayPage(mSplash, pageNumber, this->dpiForRendering * xscale, this->dpiForRendering *yscale, + mDocument->displayPage(mSplashHistorical, pageNumber, this->dpiForRendering * xscale, this->dpiForRendering *yscale, rotation, useMediaBox, crop, printing); } else @@ -247,56 +230,68 @@ QImage* XPDFRenderer::createPDFImage(int pageNumber, qreal xscale, qreal yscale, qreal sliceW = bounds.width() * xscale; qreal sliceH = bounds.height() * yscale; - mDocument->displayPageSlice(mSplash, pageNumber, this->dpiForRendering * xscale, this->dpiForRendering * yscale, + mDocument->displayPageSlice(mSplashHistorical, pageNumber, this->dpiForRendering * xscale, this->dpiForRendering * yscale, rotation, useMediaBox, crop, printing, mSliceX, mSliceY, sliceW, sliceH); } - mpSplashBitmap = mSplash->getBitmap(); + mpSplashBitmapHistorical = mSplashHistorical->getBitmap(); } - return new QImage(mpSplashBitmap->getDataPtr(), mpSplashBitmap->getWidth(), mpSplashBitmap->getHeight(), mpSplashBitmap->getWidth() * 3, QImage::Format_RGB888); + return new QImage(mpSplashBitmapHistorical->getDataPtr(), mpSplashBitmapHistorical->getWidth(), mpSplashBitmapHistorical->getHeight(), mpSplashBitmapHistorical->getWidth() * 3, QImage::Format_RGB888); } -#else - void XPDFRenderer::render(QPainter *p, int pageNumber, const QRectF &bounds) { Q_UNUSED(bounds); if (isValid()) { - qreal xscale = p->worldTransform().m11(); - qreal yscale = p->worldTransform().m22(); - Q_ASSERT(qFuzzyCompare(xscale, yscale)); // Zoom equal in all axes expected. - Q_ASSERT(xscale > 0.0); // Potential Div0 later if this assert fail. - - int zoomIndex = 0; - bool foundIndex = false; - for (; zoomIndex < NbrZoomCache && !foundIndex;) + if (m_pdfZoomCache.size() > 0) { - if (xscale <= m_cache[zoomIndex].ratio) { - foundIndex = true; - } else { - zoomIndex++; + qreal xscale = p->worldTransform().m11(); + qreal yscale = p->worldTransform().m22(); + Q_ASSERT(qFuzzyCompare(xscale, yscale)); // Zoom equal in all axes expected. + Q_ASSERT(xscale > 0.0); // Potential Div0 later if this assert fail. + + int zoomIndex = 0; + bool foundIndex = false; + for (; zoomIndex < m_pdfZoomCache.size() && !foundIndex;) + { + if (xscale <= m_pdfZoomCache[zoomIndex].ratio) { + foundIndex = true; + } else { + zoomIndex++; + } } - } - if (!foundIndex) // Use the previous one. - zoomIndex--; + if (!foundIndex) // Use the previous one. + zoomIndex--; - QImage const &pdfImage = createPDFImage(pageNumber, m_cache[zoomIndex]); - QTransform savedTransform = p->worldTransform(); - double const ratioDifferenceBetweenWorldAndImage = 1.0/m_cache[zoomIndex].ratio; - // The 'pdfImage' is rendered with a quality equal or superior. We adjust the 'transform' to zoom it - // out the required ratio. - QTransform newTransform = savedTransform.scale(ratioDifferenceBetweenWorldAndImage, ratioDifferenceBetweenWorldAndImage); - p->setWorldTransform(newTransform); - p->drawImage(QPointF( mSliceX, mSliceY), pdfImage); + QImage const &pdfImage = createPDFImageCached(pageNumber, m_pdfZoomCache[zoomIndex]); + QTransform savedTransform = p->worldTransform(); - p->setWorldTransform(savedTransform); + double const ratioDifferenceBetweenWorldAndImage = 1.0/m_pdfZoomCache[zoomIndex].ratio; + // The 'pdfImage' is rendered with a quality equal or superior. We adjust the 'transform' to zoom it + // out the required ratio. + QTransform newTransform = savedTransform.scale(ratioDifferenceBetweenWorldAndImage, ratioDifferenceBetweenWorldAndImage); + p->setWorldTransform(newTransform); + p->drawImage(QPointF( mSliceX, mSliceY), pdfImage); + + p->setWorldTransform(savedTransform); + } else { + qreal xscale = p->worldTransform().m11(); + qreal yscale = p->worldTransform().m22(); + + QImage *pdfImage = createPDFImageHistorical(pageNumber, xscale, yscale, bounds); + QTransform savedTransform = p->worldTransform(); + p->resetTransform(); + p->drawImage(QPointF(savedTransform.dx() + mSliceX, savedTransform.dy() + mSliceY), *pdfImage); + p->setWorldTransform(savedTransform); + delete pdfImage; + } } } -QImage& XPDFRenderer::createPDFImage(int pageNumber, TypeCacheData &cacheData) +QImage& XPDFRenderer::createPDFImageCached(int pageNumber, TypePdfZoomCacheData &cacheData) { if (isValid()) { @@ -306,7 +301,7 @@ QImage& XPDFRenderer::createPDFImage(int pageNumber, TypeCacheData &cacheData) { if(cacheData.splash != nullptr) { - cacheData.cachedImage = QImage(); // The 'm_cachedImage' uses a buffer from 'mSplash'. + cacheData.cachedImage = QImage(); // The 'm_pdfZoomCachedImage' uses a buffer from 'mSplash'. delete cacheData.splash; } cacheData.splash = new SplashOutputDev(splashModeRGB8, 1, false, paperColor); @@ -339,4 +334,3 @@ QImage& XPDFRenderer::createPDFImage(int pageNumber, TypeCacheData &cacheData) return cacheData.cachedImage; } -#endif diff --git a/src/pdf/XPDFRenderer.h b/src/pdf/XPDFRenderer.h index fef8b411..61dc323a 100644 --- a/src/pdf/XPDFRenderer.h +++ b/src/pdf/XPDFRenderer.h @@ -51,9 +51,6 @@ class PDFDoc; -#define XPDFRENDERER_CACHE_ZOOM_IMAGE -//#define XPDFRENDERER_CACHE_ZOOM_WITH_LOSS - class XPDFRenderer : public PDFRenderer { Q_OBJECT @@ -77,20 +74,10 @@ class XPDFRenderer : public PDFRenderer private: void init(); -#ifdef XPDFRENDERER_CACHE_ZOOM_IMAGE - - enum { -#ifndef XPDFRENDERER_CACHE_ZOOM_WITH_LOSS - NbrZoomCache = 3 -#else //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS - NbrZoomCache = 1 -#endif //XPDFRENDERER_CACHE_ZOOM_WITH_LOSS - }; - class TypeCacheData { - public: - TypeCacheData(double const a_ratio) : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(a_ratio) {}; - ~TypeCacheData() {}; + struct TypePdfZoomCacheData { + TypePdfZoomCacheData(double const a_ratio) : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(a_ratio) {}; + ~TypePdfZoomCacheData() {}; SplashBitmap* splashBitmap; QImage cachedImage; int cachedPageNumber; @@ -98,16 +85,18 @@ class XPDFRenderer : public PDFRenderer double const ratio; }; - QImage &createPDFImage(int pageNumber, TypeCacheData &cacheData); + QImage &createPDFImageCached(int pageNumber, TypePdfZoomCacheData &cacheData); + QImage* createPDFImageHistorical(int pageNumber, qreal xscale, qreal yscale, const QRectF &bounds); - static const double sRatioZoomRendering[NbrZoomCache]; - QVector m_cache; -#else - QImage* createPDFImage(int pageNumber, qreal xscale = 0.5, qreal yscale = 0.5, const QRectF &bounds = QRectF()); + // Used when 'ZoomBehavior == 1 or 2'. + // =1 has only x3 zoom in cache (= loss if user zoom > 3.0). + // =2, has 2.5, 5 and 10 (= no loss, but a bit slower). + QVector m_pdfZoomCache; - SplashBitmap* mpSplashBitmap; - SplashOutputDev* mSplash; -#endif + // Used when 'ZoomBehavior == 0' (no cache). + SplashBitmap* mpSplashBitmapHistorical; + // Used when 'ZoomBehavior == 0' (no cache). + SplashOutputDev* mSplashHistorical; PDFDoc *mDocument; static QAtomicInt sInstancesCount; From e5377891edeb6124109c8df61f88af7d7685bfe6 Mon Sep 17 00:00:00 2001 From: John Papale Date: Tue, 27 Oct 2020 19:05:53 +0100 Subject: [PATCH 4/8] Removed MSVC debug warning. --- OpenBoard.pro | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/OpenBoard.pro b/OpenBoard.pro index da285a9b..f36bf9fe 100644 --- a/OpenBoard.pro +++ b/OpenBoard.pro @@ -126,7 +126,11 @@ win32 { CONFIG += axcontainer exists(console):CONFIG += console QMAKE_CXXFLAGS += /MP - QMAKE_CXXFLAGS += /MD + CONFIG( debug, debug|release ) { + QMAKE_CXXFLAGS += /MDd + } else { + QMAKE_CXXFLAGS += /MD + } QMAKE_CXXFLAGS_RELEASE += /Od /Zi QMAKE_LFLAGS += /VERBOSE:LIB UB_LIBRARY.path = $$DESTDIR From 0cf3bdde1aab882c59b12589a9df056146422cec Mon Sep 17 00:00:00 2001 From: John Papale Date: Tue, 27 Oct 2020 19:13:52 +0100 Subject: [PATCH 5/8] Cleanup comments. --- src/pdf/XPDFRenderer.cpp | 2 +- src/pdf/XPDFRenderer.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pdf/XPDFRenderer.cpp b/src/pdf/XPDFRenderer.cpp index 410739fc..ef6ede30 100644 --- a/src/pdf/XPDFRenderer.cpp +++ b/src/pdf/XPDFRenderer.cpp @@ -301,7 +301,7 @@ QImage& XPDFRenderer::createPDFImageCached(int pageNumber, TypePdfZoomCacheData { if(cacheData.splash != nullptr) { - cacheData.cachedImage = QImage(); // The 'm_pdfZoomCachedImage' uses a buffer from 'mSplash'. + cacheData.cachedImage = QImage(); delete cacheData.splash; } cacheData.splash = new SplashOutputDev(splashModeRGB8, 1, false, paperColor); diff --git a/src/pdf/XPDFRenderer.h b/src/pdf/XPDFRenderer.h index 61dc323a..105c574e 100644 --- a/src/pdf/XPDFRenderer.h +++ b/src/pdf/XPDFRenderer.h @@ -100,8 +100,6 @@ class XPDFRenderer : public PDFRenderer PDFDoc *mDocument; static QAtomicInt sInstancesCount; - //! The image is rendered with a quality above normal, so we can use that same - //! image while zooming. qreal mSliceX; qreal mSliceY; }; From d5988f979ca8a873696a11fc38670d739980e889 Mon Sep 17 00:00:00 2001 From: John Papale Date: Wed, 28 Oct 2020 10:56:19 +0100 Subject: [PATCH 6/8] Cleanup. Fixed 'Poppler' compile error, since previous commit. --- src/pdf/XPDFRenderer.cpp | 34 +++++++++++++++++----------------- src/pdf/XPDFRenderer.h | 25 ++++++++++++++++++++----- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src/pdf/XPDFRenderer.cpp b/src/pdf/XPDFRenderer.cpp index ef6ede30..92a6e1e6 100644 --- a/src/pdf/XPDFRenderer.cpp +++ b/src/pdf/XPDFRenderer.cpp @@ -42,6 +42,13 @@ QAtomicInt XPDFRenderer::sInstancesCount = 0; +namespace constants{ + const double mode1_zoomFactor = 3.0; + const double mode2_zoomFactorStage1 = 2.5; + const double mode2_zoomFactorStage2 = 5.0; + const double mode2_zoomFactorStage3 = 10.0; +} + XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : mpSplashBitmapHistorical(nullptr), mSplashHistorical(nullptr), mDocument(nullptr) { @@ -50,12 +57,12 @@ XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : break; case 1: // Render a single image, degradated quality when zoomed big. default: - m_pdfZoomCache.push_back(3.0); + m_pdfZoomCache.push_back(constants::mode1_zoomFactor); break; case 2: // Render three images, optimal quality all the time. - m_pdfZoomCache.push_back(2.5); - m_pdfZoomCache.push_back(5.0); - m_pdfZoomCache.push_back(10.0); + m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage1); + m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage2); + m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage3); break; } @@ -83,7 +90,7 @@ XPDFRenderer::~XPDFRenderer() { for(int i = 0; i < m_pdfZoomCache.size(); i++) { - TypePdfZoomCacheData &cacheData = m_pdfZoomCache[i]; + PdfZoomCacheData &cacheData = m_pdfZoomCache[i]; if(cacheData.splash != nullptr){ cacheData.cachedImage = QImage(); // The 'cachedImage' uses a buffer from 'splash'. delete cacheData.splash; @@ -209,7 +216,7 @@ QImage* XPDFRenderer::createPDFImageHistorical(int pageNumber, qreal xscale, qre #ifdef USE_XPDF mSplashHistorical->startDoc(mDocument->getXRef()); #else - mSplash->startDoc(mDocument); + mSplashHistorical->startDoc(mDocument); #endif int rotation = 0; // in degrees (get it from the worldTransform if we want to support rotation) bool useMediaBox = false; @@ -291,21 +298,14 @@ void XPDFRenderer::render(QPainter *p, int pageNumber, const QRectF &bounds) } } -QImage& XPDFRenderer::createPDFImageCached(int pageNumber, TypePdfZoomCacheData &cacheData) +QImage& XPDFRenderer::createPDFImageCached(int pageNumber, PdfZoomCacheData &cacheData) { if (isValid()) { SplashColor paperColor = {0xFF, 0xFF, 0xFF}; // white - bool const requireUpdateImage = (pageNumber != cacheData.cachedPageNumber) || (cacheData.splash == nullptr); - if (requireUpdateImage) + if (cacheData.requireUpdateImage(pageNumber)) { - if(cacheData.splash != nullptr) - { - cacheData.cachedImage = QImage(); - delete cacheData.splash; - } - cacheData.splash = new SplashOutputDev(splashModeRGB8, 1, false, paperColor); - cacheData.cachedPageNumber = pageNumber; + cacheData.prepareNewSplash(pageNumber, paperColor); #ifdef USE_XPDF cacheData.splash->startDoc(mDocument->getXRef()); @@ -324,7 +324,7 @@ QImage& XPDFRenderer::createPDFImageCached(int pageNumber, TypePdfZoomCacheData cacheData.splashBitmap = cacheData.splash->getBitmap(); } - // Note this uses the 'mSplash->getBitmap()->getDataPtr()' as data buffer. + // Note this uses the 'cacheData.splash->getBitmap()->getDataPtr()' as data buffer. cacheData.cachedImage = QImage(cacheData.splashBitmap->getDataPtr(), cacheData.splashBitmap->getWidth(), cacheData.splashBitmap->getHeight(), cacheData.splashBitmap->getWidth() * 3 /* bytesPerLine, 24 bits for RGB888, = 3 bytes */, QImage::Format_RGB888); diff --git a/src/pdf/XPDFRenderer.h b/src/pdf/XPDFRenderer.h index 105c574e..43357b01 100644 --- a/src/pdf/XPDFRenderer.h +++ b/src/pdf/XPDFRenderer.h @@ -75,23 +75,38 @@ class XPDFRenderer : public PDFRenderer private: void init(); - struct TypePdfZoomCacheData { - TypePdfZoomCacheData(double const a_ratio) : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(a_ratio) {}; - ~TypePdfZoomCacheData() {}; + struct PdfZoomCacheData { + PdfZoomCacheData(double const a_ratio) : splashBitmap(nullptr), cachedPageNumber(-1), splash(nullptr), ratio(a_ratio) {}; + ~PdfZoomCacheData() {}; SplashBitmap* splashBitmap; QImage cachedImage; int cachedPageNumber; SplashOutputDev* splash; double const ratio; + + bool requireUpdateImage(int const pageNumber) const { + return (pageNumber != cachedPageNumber) || (splash == nullptr); + } + + void prepareNewSplash(int const pageNumber, SplashColor &paperColor) + { + if(splash != nullptr) + { + cachedImage = QImage(); + delete splash; + } + splash = new SplashOutputDev(splashModeRGB8, 1, false, paperColor); + cachedPageNumber = pageNumber; + } }; - QImage &createPDFImageCached(int pageNumber, TypePdfZoomCacheData &cacheData); + QImage &createPDFImageCached(int pageNumber, PdfZoomCacheData &cacheData); QImage* createPDFImageHistorical(int pageNumber, qreal xscale, qreal yscale, const QRectF &bounds); // Used when 'ZoomBehavior == 1 or 2'. // =1 has only x3 zoom in cache (= loss if user zoom > 3.0). // =2, has 2.5, 5 and 10 (= no loss, but a bit slower). - QVector m_pdfZoomCache; + QVector m_pdfZoomCache; // Used when 'ZoomBehavior == 0' (no cache). SplashBitmap* mpSplashBitmapHistorical; From 505ca347afe1aca13e68823df05322364150f81e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fauconnier?= Date: Mon, 2 Nov 2020 11:05:10 +0100 Subject: [PATCH 7/8] renamed and moved XPDF zoom constants to make them more readable and open to further development --- src/pdf/XPDFRenderer.cpp | 15 ++++----------- src/pdf/XPDFRenderer.h | 9 +++++++++ 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/pdf/XPDFRenderer.cpp b/src/pdf/XPDFRenderer.cpp index 92a6e1e6..e128b33b 100644 --- a/src/pdf/XPDFRenderer.cpp +++ b/src/pdf/XPDFRenderer.cpp @@ -42,13 +42,6 @@ QAtomicInt XPDFRenderer::sInstancesCount = 0; -namespace constants{ - const double mode1_zoomFactor = 3.0; - const double mode2_zoomFactorStage1 = 2.5; - const double mode2_zoomFactorStage2 = 5.0; - const double mode2_zoomFactorStage3 = 10.0; -} - XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : mpSplashBitmapHistorical(nullptr), mSplashHistorical(nullptr), mDocument(nullptr) { @@ -57,12 +50,12 @@ XPDFRenderer::XPDFRenderer(const QString &filename, bool importingFile) : break; case 1: // Render a single image, degradated quality when zoomed big. default: - m_pdfZoomCache.push_back(constants::mode1_zoomFactor); + m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode1_zoomFactor); break; case 2: // Render three images, optimal quality all the time. - m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage1); - m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage2); - m_pdfZoomCache.push_back(constants::mode2_zoomFactorStage3); + m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode2_zoomFactorStage1); + m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode2_zoomFactorStage2); + m_pdfZoomCache.push_back(XPDFRendererZoomFactor::mode2_zoomFactorStage3); break; } diff --git a/src/pdf/XPDFRenderer.h b/src/pdf/XPDFRenderer.h index 43357b01..4ac3f9aa 100644 --- a/src/pdf/XPDFRenderer.h +++ b/src/pdf/XPDFRenderer.h @@ -51,6 +51,15 @@ class PDFDoc; + +namespace XPDFRendererZoomFactor +{ + const double mode1_zoomFactor = 3.0; + const double mode2_zoomFactorStage1 = 2.5; + const double mode2_zoomFactorStage2 = 5.0; + const double mode2_zoomFactorStage3 = 10.0; +} + class XPDFRenderer : public PDFRenderer { Q_OBJECT From 6716e031fd3ad1b568ef6c61b1a2c106bf019343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fauconnier?= Date: Mon, 2 Nov 2020 11:08:55 +0100 Subject: [PATCH 8/8] added pdf zoom behavior default value to config file --- resources/etc/OpenBoard.config | 1 + 1 file changed, 1 insertion(+) diff --git a/resources/etc/OpenBoard.config b/resources/etc/OpenBoard.config index 06c81ef6..c85c025e 100644 --- a/resources/etc/OpenBoard.config +++ b/resources/etc/OpenBoard.config @@ -123,6 +123,7 @@ RefreshRateInFramePerSecond=2 Margin=20 PageFormat=A4 Resolution=300 +ZoomBehavior=1 [Podcast] AudioRecordingDevice=Default