PDF: Support the poppler 0.62 renderToImage with update callback

Summary:
This way pages that take more than 500ms to render get updated every so often so that the
user can see that the program didn't hang, it's just that it's taking long to render

Tags:
incremental rendering, partial updates

BUGS: 344081

Subscribers: #okular

Tags: #okular

Differential Revision: https://phabricator.kde.org/D8379
This commit is contained in:
Albert Astals Cid 2017-10-03 09:29:18 +02:00 committed by Albert Astals Cid
parent 01ce40b955
commit a2f5560c00
8 changed files with 154 additions and 8 deletions

View file

@ -1,6 +1,9 @@
/***************************************************************************
* Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it> *
* Copyright (C) 2004-2008 by Albert Astals Cid <aacid@kde.org> *
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
* company, info@kdab.com. Work sponsored by the *
* LiMux project of the city of Munich *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -1426,6 +1429,8 @@ void DocumentPrivate::sendGeneratorPixmapRequest()
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

View file

@ -1,6 +1,9 @@
/***************************************************************************
* Copyright (C) 2005 by Piotr Szymanski <niedakh@gmail.com> *
* Copyright (C) 2008 by Albert Astals Cid <aacid@kde.org> *
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
* company, info@kdab.com. Work sponsored by the *
* LiMux project of the city of Munich *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -403,6 +406,14 @@ void Generator::signalTextGenerationDone( Page *page, TextPage *textPage )
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 );
@ -500,6 +511,7 @@ PixmapRequest::PixmapRequest( DocumentObserver *observer, int pageNumber, int wi
d->mForce = false;
d->mTile = false;
d->mNormalizedRect = NormalizedRect();
d->mPartialUpdatesWanted = false;
}
PixmapRequest::~PixmapRequest()
@ -570,6 +582,16 @@ const NormalizedRect& PixmapRequest::normalizedRect() const
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);

View file

@ -2,6 +2,9 @@
* Copyright (C) 2004-5 by Enrico Ros <eros.kde@email.it> *
* Copyright (C) 2005 by Piotr Szymanski <niedakh@gmail.com> *
* Copyright (C) 2008 by Albert Astals Cid <aacid@kde.org> *
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
* company, info@kdab.com. Work sponsored by the *
* LiMux project of the city of Munich *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -576,6 +579,13 @@ class OKULARCORE_EXPORT Generator : public QObject
*/
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 +713,20 @@ class OKULARCORE_EXPORT PixmapRequest
*/
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 +737,7 @@ class OKULARCORE_EXPORT PixmapRequest
}
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)

View file

@ -1,5 +1,8 @@
/***************************************************************************
* Copyright (C) 2007 Tobias Koenig <tokoe@kde.org> *
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
* company, info@kdab.com. Work sponsored by the *
* LiMux project of the city of Munich *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -85,6 +88,7 @@ class PixmapRequestPrivate
int mFeatures;
bool mForce : 1;
bool mTile : 1;
bool mPartialUpdatesWanted : 1;
Page *mPage;
NormalizedRect mNormalizedRect;
};

View file

@ -1,5 +1,9 @@
/***************************************************************************
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
* company, info@kdab.com. Work sponsored by the *
* LiMux project of the city of Munich *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
@ -210,7 +214,11 @@ bool Page::hasPixmap( DocumentObserver *observer, int width, int height, const N
{
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 +539,14 @@ void Page::setPixmap( DocumentObserver *observer, QPixmap *pixmap, const Normali
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;
}

View file

@ -19,7 +19,7 @@ if (Poppler_VERSION VERSION_GREATER "0.36.99")
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 <poppler-qt5.h>
@ -50,6 +50,17 @@ int main()
}
" HAVE_POPPLER_0_60)
check_cxx_source_compiles("
#include <poppler-qt5.h>
#include <QImage>
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

View file

@ -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

View file

@ -2,6 +2,9 @@
* Copyright (C) 2004-2008 by Albert Astals Cid <aacid@kde.org> *
* Copyright (C) 2004 by Enrico Ros <eros.kde@email.it> *
* Copyright (C) 2012 by Guillermo A. Amaral B. <gamaral@kde.org> *
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group *
* company, info@kdab.com. Work sponsored by the *
* LiMux project of the city of Munich *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
@ -25,6 +28,7 @@
#include <qtextstream.h>
#include <QPrinter>
#include <QPainter>
#include <QTimer>
#include <QtCore/QDebug>
#include <KAboutData>
@ -894,6 +898,44 @@ bool PDFGenerator::isAllowed( Okular::Permission permission ) const
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<PartialUpdatePayload *>();
// Since the timer lives in a thread without an event 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<PartialUpdatePayload *>();
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 +970,33 @@ QImage PDFGenerator::image( Okular::PixmapRequest * request )
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