diff --git a/core/document.cpp b/core/document.cpp --- a/core/document.cpp +++ b/core/document.cpp @@ -1426,6 +1426,8 @@ request->setNormalizedRect( TilesManager::fromRotatedRect( request->normalizedRect(), m_rotation ) ); + request->setPartialUpdatesWanted( request->asynchronous() && !request->page()->hasPixmap( request->observer() ) ); + // we always have to unlock _before_ the generatePixmap() because // a sync generation would end with requestDone() -> deadlock, and // we can not really know if the generator can do async requests diff --git a/core/generator.h b/core/generator.h --- a/core/generator.h +++ b/core/generator.h @@ -576,6 +576,13 @@ */ Okular::Generator::PrintError printError() const; + /** + * This method can be called to trigger a partial pixmap update for the given request + * Make sure you call it in a way it's executed in the main thread. + * @since 1.3 + */ + void signalPartialPixmapRequest( Okular::PixmapRequest * request, const QImage &image ); + protected: /// @cond PRIVATE Generator(GeneratorPrivate &dd, QObject *parent, const QVariantList &args); @@ -703,6 +710,20 @@ */ const NormalizedRect& normalizedRect() const; + /** + * Sets whether the request should report back updates if possible + * + * @since 1.3 + */ + void setPartialUpdatesWanted(bool partialUpdatesWanted); + + /** + * Should the request report back updates if possible? + * + * @since 1.3 + */ + bool partialUpdatesWanted() const; + private: Q_DISABLE_COPY( PixmapRequest ) @@ -713,6 +734,7 @@ } Q_DECLARE_METATYPE(Okular::Generator::PrintError) +Q_DECLARE_METATYPE(Okular::PixmapRequest*) #define OkularGeneratorInterface_iid "org.kde.okular.Generator" Q_DECLARE_INTERFACE(Okular::Generator, OkularGeneratorInterface_iid) diff --git a/core/generator.cpp b/core/generator.cpp --- a/core/generator.cpp +++ b/core/generator.cpp @@ -402,6 +402,14 @@ delete textPage; } +void Generator::signalPartialPixmapRequest( PixmapRequest * request, const QImage &image ) +{ + request->page()->setPixmap( request->observer(), new QPixmap( QPixmap::fromImage( image ) ), request->normalizedRect() ); + + const int pageNumber = request->page()->number(); + request->observer()->notifyPageChanged( pageNumber, Okular::DocumentObserver::Pixmap ); +} + const Document * Generator::document() const { Q_D( const Generator ); @@ -499,6 +507,7 @@ d->mForce = false; d->mTile = false; d->mNormalizedRect = NormalizedRect(); + d->mPartialUpdatesWanted = false; } PixmapRequest::~PixmapRequest() @@ -569,6 +578,16 @@ return d->mNormalizedRect; } +void PixmapRequest::setPartialUpdatesWanted(bool partialUpdatesWanted) +{ + d->mPartialUpdatesWanted = partialUpdatesWanted; +} + +bool PixmapRequest::partialUpdatesWanted() const +{ + return d->mPartialUpdatesWanted; +} + Okular::TilesManager* PixmapRequestPrivate::tilesManager() const { return mPage->d->tilesManager(mObserver); diff --git a/core/generator_p.h b/core/generator_p.h --- a/core/generator_p.h +++ b/core/generator_p.h @@ -85,6 +85,7 @@ int mFeatures; bool mForce : 1; bool mTile : 1; + bool mPartialUpdatesWanted : 1; Page *mPage; NormalizedRect mNormalizedRect; }; diff --git a/core/page.cpp b/core/page.cpp --- a/core/page.cpp +++ b/core/page.cpp @@ -210,7 +210,11 @@ { if ( width != tm->width() || height != tm->height() ) { - tm->setSize( width, height ); + // FIXME hasPixmap should not be calling setSize on the TilesManager this is not very "const" + // as this function claims to be + if ( width != -1 && height != -1 ) { + tm->setSize( width, height ); + } return false; } @@ -531,10 +535,14 @@ it.value().m_pixmap = pixmap; it.value().m_rotation = d->m_rotation; } else { - RotationJob *job = new RotationJob( pixmap->toImage(), Rotation0, d->m_rotation, observer ); - job->setPage( d ); - job->setRect( TilesManager::toRotatedRect( rect, d->m_rotation ) ); - d->m_doc->m_pageController->addRotationJob(job); + // it can happen that we get a setPixmap while closing and thus the page controller is gone + if ( d->m_doc->m_pageController ) + { + RotationJob *job = new RotationJob( pixmap->toImage(), Rotation0, d->m_rotation, observer ); + job->setPage( d ); + job->setRect( TilesManager::toRotatedRect( rect, d->m_rotation ) ); + d->m_doc->m_pageController->addRotationJob(job); + } delete pixmap; } diff --git a/generators/poppler/CMakeLists.txt b/generators/poppler/CMakeLists.txt --- a/generators/poppler/CMakeLists.txt +++ b/generators/poppler/CMakeLists.txt @@ -19,7 +19,7 @@ set (HAVE_POPPLER_0_37 1) endif() -set(CMAKE_REQUIRED_LIBRARIES Poppler::Qt5 Qt5::Core) +set(CMAKE_REQUIRED_LIBRARIES Poppler::Qt5 Qt5::Core Qt5::Gui) check_cxx_source_compiles(" #include @@ -50,6 +50,17 @@ } " HAVE_POPPLER_0_60) +check_cxx_source_compiles(" +#include +#include +int main() +{ + Poppler::Page *p; + p->renderToImage(0, 0, 0, 0, 0, 0, Poppler::Page::Rotate0, nullptr, nullptr, QVariant()); + return 0; +} +" HAVE_POPPLER_0_62) + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/config-okular-poppler.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-okular-poppler.h diff --git a/generators/poppler/config-okular-poppler.h.cmake b/generators/poppler/config-okular-poppler.h.cmake --- a/generators/poppler/config-okular-poppler.h.cmake +++ b/generators/poppler/config-okular-poppler.h.cmake @@ -18,3 +18,6 @@ /* Defined if we have the 0.60 version of the Poppler library */ #cmakedefine HAVE_POPPLER_0_60 1 + +/* Defined if we have the 0.62 version of the Poppler library */ +#cmakedefine HAVE_POPPLER_0_62 1 diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -894,6 +895,44 @@ return b; } +#ifdef HAVE_POPPLER_0_62 +struct PartialUpdatePayload +{ + PartialUpdatePayload(PDFGenerator *g, Okular::PixmapRequest *r) : + generator(g), request(r) + { + // Don't report partial updates for the first 500 ms + timer.setInterval(500); + timer.setSingleShot(true); + timer.start(); + } + + PDFGenerator *generator; + Okular::PixmapRequest *request; + QTimer timer; +}; +Q_DECLARE_METATYPE(PartialUpdatePayload*) + +static bool shouldDoPartialUpdateCallback(const QVariant &vPayload) +{ + auto payload = vPayload.value(); + + // Since the timer lives in a thread without even loop we need to stop it ourselves + // when the remaining time has reached 0 + if (payload->timer.isActive() && payload->timer.remainingTime() == 0) { + payload->timer.stop(); + } + + return !payload->timer.isActive(); +} + +static void partialUpdateCallback(const QImage &image, const QVariant &vPayload) +{ + auto payload = vPayload.value(); + QMetaObject::invokeMethod(payload->generator, "signalPartialPixmapRequest", Qt::QueuedConnection, Q_ARG(Okular::PixmapRequest*, payload->request), Q_ARG(QImage, image)); +} +#endif + QImage PDFGenerator::image( Okular::PixmapRequest * request ) { // debug requests to this (xpdf) generator @@ -928,11 +967,33 @@ if ( request->isTile() ) { QRect rect = request->normalizedRect().geometry( request->width(), request->height() ); - img = p->renderToImage( fakeDpiX, fakeDpiY, rect.x(), rect.y(), rect.width(), rect.height(), Poppler::Page::Rotate0 ); +#ifdef HAVE_POPPLER_0_62 + if ( request->partialUpdatesWanted() ) + { + PartialUpdatePayload payload( this, request ); + img = p->renderToImage( fakeDpiX, fakeDpiY, rect.x(), rect.y(), rect.width(), rect.height(), Poppler::Page::Rotate0, + partialUpdateCallback, shouldDoPartialUpdateCallback, QVariant::fromValue( &payload ) ); + } + else +#endif + { + img = p->renderToImage( fakeDpiX, fakeDpiY, rect.x(), rect.y(), rect.width(), rect.height(), Poppler::Page::Rotate0 ); + } } else { - img = p->renderToImage(fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0 ); +#ifdef HAVE_POPPLER_0_62 + if ( request->partialUpdatesWanted() ) + { + PartialUpdatePayload payload(this, request); + img = p->renderToImage( fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0, + partialUpdateCallback, shouldDoPartialUpdateCallback, QVariant::fromValue( &payload ) ); + } + else +#endif + { + img = p->renderToImage(fakeDpiX, fakeDpiY, -1, -1, -1, -1, Poppler::Page::Rotate0 ); + } } } else